/* ═══════════════════════════════════════════════════════════════════════════
   styles/mushaf.css — Mushaf v2 · Route D layered renderer
   ─────────────────────────────────────────────────────────────────────────
   OWNS:        all styling for modules/mushaf/* — the 3-layer page stack,
                word highlights, selection feedback, blur quiz modes,
                mutashābih accent classes.
   TOKENS ONLY: per CLAUDE.md rule A2 — no hex literals here. All colors
                resolve to --ms-* tokens defined in styles/tokens.css.
   LOAD ORDER:  after styles/tokens.css, before any module-specific CSS.

   LAYER STACK
   ───────────
   .mushaf-host                — runtime mount point (theme-dependent chrome)
     .mushaf-frame             — wraps one rendered page, themed border/padding
       .mushaf-page            — the printed page itself (paper-colored)
         .glyph-layer          — LAYER 1 · QCF glyph spans (publisher pixels)
         .text-layer           — LAYER 2 · invisible Tanzil text (color:transparent)
           .word.hl-*          — LAYER 3 · CSS highlight classes
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── HOST + FRAME ─────────────────────────────────────────────────────── */
/* The page sizing below depends on the host being a SIZE container so the
   page can scale to fit it. The host itself fills whatever room it's given. */
.mushaf-host {
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 100%;
  background: var(--ms-bg);
  color: var(--ms-ink);
  container-type: size;        /* page reads cqi + cqb from here */
  box-sizing: border-box;
  /* Padding scales DOWN to 0 on very narrow screens so the page can claim
     every available pixel without being clipped. Caps at 16px on roomy
     viewports for breathing room. */
  padding: clamp(0px, 2vw, 16px);
}
.mushaf-frame {
  display: grid;
  place-items: center;
  /* No padding here — it would compound on top of .mushaf-host's padding
     and start eating into the page's available width/height. */
  opacity: 1;
  transition: opacity 140ms ease-out;
}

/* ═══════════════════ v227g · horizontal-scroll window ════════════════
   When the host carries data-scroll="horizontal", it becomes a native
   scroll-snap container. Pages are mounted as flush sibling slots —
   each slot occupies exactly 100% of the host width with NO gap (per
   user: "have the mushaf right next to each other"). The browser's
   own momentum + snap-stop handles the flick feel; an
   IntersectionObserver (in renderer.js) promotes the centered slot to
   state.page when the snap settles.

   The classic single-page / spread render still mounts a `.mushaf-frame`
   directly into `.mushaf-host` — that path is unaffected and is used
   when the user opts out via localStorage `mithlayn.mushaf.scrollMode
   = "classic"`.
   ════════════════════════════════════════════════════════════════════ */
/* v227j · 2026-05-22 · selector bumped from `.mushaf-host[data-scroll]`
   to `.mushaf-shell .mushaf-page-host[data-scroll]` so this rule beats
   the centering + `overflow: hidden` block at line 918 below
   (`.mushaf-shell .mushaf-page-host`). Without the bump, specificity
   ties (both 0,2,0) and source-order made the wrong one win — the
   host stayed `display: flex; align-items: center; overflow: hidden`,
   which centered the 2048-wide scroller inside the 1024-wide host
   instead of clipping + scrolling. Result: one page landed on-screen
   by accident; the other(s) were pushed off-viewport left. */
.mushaf-shell .mushaf-page-host[data-scroll="horizontal"] {
  /* The host becomes the scroll container. Override the centering +
     `overflow: hidden` from the .mushaf-shell .mushaf-page-host rule;
     the scroller below handles positioning of its own slots. */
  display: block !important;
  align-items: initial !important;
  justify-content: initial !important;
  padding: 0 !important;
  overflow-x: auto !important;
  overflow-y: hidden !important;
  /* v227k5 · NATURAL SCROLL · per user "it's rigid, doesn't want to move,
     just wants to instantly jump." Fixes:
     - mandatory → proximity: pages snap into place AFTER the user lifts
       their finger, but the drag itself moves freely with the finger.
     - scroll-snap-stop: NORMAL on slots (removed `always`) so a strong
       flick can traverse multiple pages, like a real book flip.
     - -webkit-overflow-scrolling: touch keeps iOS momentum.
     - overscroll-behavior: contain prevents the gesture from bubbling
       into the page-level scroll (which would feel like a wall). */
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  /* v227k6 · scroll-behavior LEFT AS auto so user-initiated touch /
     trackpad scrolls feel native (browser's own momentum / snap kicks
     in immediately). chrome.js's chevron clicks use scrollTo({
     behavior: 'smooth' }) explicitly when smoothness is desired, so we
     don't lose that capability. Setting scroll-behavior: smooth here
     made every scroll re-snap with animation, which read as "rigid". */
  scroll-behavior: auto;
  -webkit-overflow-scrolling: touch;
  /* Hide the scrollbar · the snap behavior tells the user there are
     more pages, no need for visible chrome. */
  scrollbar-width: none;
  /* v229-fix-touch-action: pan-x alone blocked the browser from passing
     vertical gestures up to ancestors — on iPad that read as "the mushaf
     won't scroll." pan-x pan-y lets vertical pans bubble while still
     letting the browser drive horizontal page-flip natively. */
  touch-action: pan-x pan-y;
}
.mushaf-shell .mushaf-page-host[data-scroll="horizontal"]::-webkit-scrollbar { display: none; }
/* v227k6 · cursor hint for desktop drag-to-scroll. */
.mushaf-shell .mushaf-page-host[data-scroll="horizontal"] { cursor: grab; }
.mushaf-shell .mushaf-page-host[data-scroll="horizontal"].is-dragging { cursor: grabbing; user-select: none; }

.mushaf-host[data-scroll="horizontal"] .mushaf-scroller {
  display: flex;
  height: 100%;
  gap: 0;                    /* pages flush · no separator */
  width: max-content;        /* sum of all slot widths */
  /* Fade-in matches the classic .mushaf-frame opacity transition so
     the swap between modes feels consistent. */
  opacity: 1;
  transition: opacity 140ms ease-out;
}
.mushaf-host[data-scroll="horizontal"] .mushaf-scroller.is-entering {
  opacity: 0;
  transition: none;
}

.mushaf-host[data-scroll="horizontal"] .mushaf-page-slot {
  /* Each slot fills the host width exactly on narrow viewports
     (one page per snap, single-page reading). The @media block below
     switches to half-host slots on landscape iPad / desktop so the
     spread (two pages side-by-side) fits inside the host. */
  flex: 0 0 100cqi;
  width: 100cqi;
  height: 100%;
  scroll-snap-align: start;
  /* v228-fix-2C · skip layout + paint for slots scrolled off-screen.
     Reclaims most of the DOM cost of the wider 5-slot window introduced
     by v229-fix-window-radius. Pair with contain-intrinsic-size so the
     skipped slot still holds its space in the scroller. */
  content-visibility: auto;
  contain-intrinsic-size: 100cqi 100cqb;
  /* v227k5 · removed scroll-snap-stop: always · was forcing a stop at
     every single slot which felt rigid. Now a hard flick can traverse
     2-3 pages like flipping a real book. Snap still LANDS on a page. */
  /* Each slot is its own size container so the .mushaf-page inside
     fits to THIS slot, not to the host. Preserves the SACRED line
     layout per-page (every page independently fit-to-its-own-slot). */
  container-type: size;
  display: grid;
  place-items: center;
  padding: clamp(0px, 2vw, 16px);
  box-sizing: border-box;
}

/* ═══════════════════ v227j · 2-page spread mode ═══════════════════
   On wide viewports (landscape iPad + desktop), the host shows TWO
   pages side-by-side (like a printed muṣḥaf open at the binding):
   right page = lower number (the page the user is on), left page =
   next page in the book. Each slot is sized to match the page's
   own aspect ratio so the page fills the slot completely (no dead
   space). Two slots = the open spread; the rest of the host width
   is symmetric margin around the spread.

   `direction: rtl` on the scroller is what flips the visual order:
   the FIRST DOM child (lowest page) ends up on the RIGHT side of the
   spread, matching how a printed muṣḥaf is read. Scroll behavior
   stays correct because all modern browsers (Chrome 85+, Safari 14+)
   implement the CSSOM-View RTL spec consistently. */
/* v227k11-isSpread-refactor · split into TWO blocks.
   ── "Wide enough for sidebar" (≥1000 any orientation) — sidebar permanent,
      host reserves 300px on the left. Fires on iPad 11" landscape, iPad
      12.9" portrait + landscape, and any desktop ≥1000 px.
   ── "Spread layout" — slot widths halved. Keyed on data-spread="on"
      (set by renderer.js when ≥1000 AND landscape). NO @media wrapper, so
      12.9" iPad Pro portrait (1024 wide, has sidebar but is single-page)
      gets the default 100cqi slots from line 128 — not the half-width
      spread sizing. THIS is the fix for "portrait shows 2-3 pages." */
@media (min-width: 1000px) {
  /* v227j · 2-page spread mode. The renderer mounts pages in DESCENDING
     order (so DOM[0] = highest, DOM[last] = lowest) and we use normal
     flex direction — visual matches DOM, with lower page numbers on
     the right of the spread (matching a printed muṣḥaf open at the
     binding). scrollLeft math stays simple: slot.offsetLeft is the
     normal value, scroll-snap behaves natively.

     The host reserves --mu-sidebar-reserve px on the LEFT so the
     absolutely-positioned `.mu-mz-attach` sidebar (which sits at the
     host's left edge in horizontal mode) does NOT overlap the spread.
     The remaining width is the spread area; 2 slots fit it exactly. */
  /* v227k · 2-page spread mode with persistent sidebar (per user). The
     memorize sidebar stays visible on the LEFT (its normal position);
     the spread sits to its RIGHT, filling the remaining host width.
     - --mu-sidebar-reserve: the width to leave for the sidebar.
     - padding-left: pushes the spread area past the sidebar so the
       scroller and slots don't sit underneath it.
     - The .mu-mz-attach itself gets a compact width in spread mode so
       the pages have more breathing room. */
  .mushaf-shell .mushaf-page-host[data-scroll="horizontal"] {
    --mu-sidebar-reserve: 300px;
    padding-left: var(--mu-sidebar-reserve) !important;
    /* v227k14d · scroll-padding-left aligns scroll-snap stops with the
       sidebar's right edge. Without it, WebKit's proximity snap pulls
       the computed scrollLeft back to slot.offsetLeft (placing the
       slot at viewport x=0, UNDER the opaque sidebar). Now snap stops
       at the value that puts the slot at viewport x=300 (= padding-
       left edge), making the previous-page pre-mount sit off-screen
       behind the sidebar. Fixes the 3-page-bleed reported on real iPad. */
    scroll-padding-left: var(--mu-sidebar-reserve) !important;
  }
  /* Compact sidebar in spread mode so the spread is wider. The
     positionMemoAttach() JS sets inline top / height / left to glue
     the sidebar to a single page's geometry. In spread mode we want
     the sidebar to fill the FULL host height instead — otherwise the
     3rd-page slot (mounted at x=-140 to 300 for pre-fetch) peeks out
     above and below the sidebar. Pin to host edges with !important to
     beat positionMemoAttach's inline styles. */
  /* v229k-fix-bottom-clip · cap the shell itself to fit inside viewport
     (minus the topbar). Without this, .mushaf-shell can be taller than
     the viewport (when ancestor min-height includes the topbar), so the
     mushaf page and sidebar both extend past the visible area. */
  .mushaf-shell {
    /* v229s-dvh · 100vh in Safari = LARGEST viewport (toolbar hidden);
       with the toolbar visible / home indicator present the visible
       viewport is smaller, so everything sized by 100vh overflowed the
       bottom — the reported "page is cut off" bug. dvh tracks the
       visible viewport; the vh line stays as a pre-iOS-15.4 fallback. */
    max-height: calc(100vh - var(--mu-topbar-h, 68px));
    max-height: calc(100dvh - var(--mu-topbar-h, 68px));
    overflow: hidden;
    position: relative;
  }
  /* v229k-scroll-up · per user "sometimes im stuck and i cant move up" —
     ensure nothing locks the body from scrolling. The mushaf shell is
     overflow:hidden internally (correct, for the page-host clip), but
     the OUTER body must remain scrollable so the iPad address bar can
     show/hide and the user can pull-down to reveal context. */
  html, body { overflow-y: auto !important; -webkit-overflow-scrolling: touch; }
  /* And ensure the .sb-root inside the sidebar is freely scrollable in
     both directions (no scroll-snap, no momentum-lock). */
  .mushaf-shell:has(.mushaf-page-host[data-scroll="horizontal"]) .mu-mz-attach .sb-root {
    overscroll-behavior: contain;
    scroll-snap-type: none !important;
    touch-action: pan-y;
  }
  /* v229k-fix-sidebar-fade · visual border at the bottom of the sidebar
     so cards going off-screen look intentional, not broken. Per user:
     "make a border and the sidebar bottom end goes under that border."
     20px fade from sidebar-bg to transparent, positioned absolute at the
     sidebar's bottom edge, pointer-events:none so it doesn't catch taps. */
  .mushaf-shell:has(.mushaf-page-host[data-scroll="horizontal"]) .mu-mz-attach::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 28px;
    background: linear-gradient(180deg, transparent, var(--ms-bg-1, #0f1114) 70%);
    pointer-events: none;
    z-index: 2;
    border-bottom: 1px solid var(--ms-rim, rgba(232,228,216,.12));
  }

  .mushaf-shell:has(.mushaf-page-host[data-scroll="horizontal"]) .mu-mz-attach {
    width: 300px !important;
    max-width: 300px !important;
    min-width: 300px !important;
    left: 0 !important;
    /* v229d-fix-sidebar-overlap: push sidebar's TOP below chrome via
       --mu-chrome-h (JS-set, default 87 for flattened bar / 144 for
       legacy 2-row). v229k-fix-bottom-clip: derive height from the
       VIEWPORT, not the shell, because the shell's natural height can
       exceed viewport on iPad — making the sidebar bottom (settings
       card) get clipped off-screen. Using bottom: 0 alone would inherit
       from absolute positioning relative to the (oversized) shell;
       calc(100vh - chrome - topbar) anchors it correctly. Topbar height
       is read from --mu-topbar-h (set by JS in chrome.js syncChromeHeight). */
    /* v229s-align · single geometry source: --mu-page-top / --mu-page-h
       are the centered page's shell-relative rect, published by
       positionMemoAttach (chrome.js). The sidebar now aligns to the
       PAGE (top edges + heights match in both orientations); the dvh
       viewport calcs remain only as pre-first-measure fallbacks.
       !important: must beat positionMemoAttach's inline styles, which
       carry stale pre-spread values for one frame on mode flips. */
    top: var(--mu-page-top, var(--mu-chrome-h, 87px)) !important;
    bottom: auto !important;
    height: var(--mu-page-h, calc(100vh  - var(--mu-chrome-h, 87px) - var(--mu-topbar-h, 68px))) !important;
    height: var(--mu-page-h, calc(100dvh - var(--mu-chrome-h, 87px) - var(--mu-topbar-h, 68px))) !important;
    max-height: var(--mu-page-h, calc(100vh  - var(--mu-chrome-h, 87px) - var(--mu-topbar-h, 68px))) !important;
    max-height: var(--mu-page-h, calc(100dvh - var(--mu-chrome-h, 87px) - var(--mu-topbar-h, 68px))) !important;
    /* v227k7 · OPAQUE sidebar bg so the 3rd-page pre-mount slot doesn't
       bleed through. */
    background: var(--ms-bg-1, #0f1114) !important;
    border-right: 1px solid var(--ms-rim, rgba(232,228,216,.10)) !important;
    box-shadow: none !important;
    /* Allow vertical scrolling inside the sidebar when cards exceed
       host height. */
    overflow-y: auto !important;
  }
  .mushaf-shell:has(.mushaf-page-host[data-scroll="horizontal"]) .mu-mz-attach .mu-mz-body {
    background: var(--ms-bg-1, #0f1114) !important;
  }
  /* The tatweel-cap ornaments on top/bottom of the sidebar are part of
     the single-page-attach styling; hide in spread mode (where the
     sidebar is a full-height column, not a page tab). */
  .mushaf-shell:has(.mushaf-page-host[data-scroll="horizontal"]) .mu-mz-cap {
    display: none !important;
  }
}

/* v227k11-isSpread-refactor · Slot width for SPREAD layout only.
   Default slot rule at line 128 gives 100cqi (full host) for single-page.
   This override halves it for the 2-page spread, but only when JS has
   set data-spread="on" (= width ≥1000 AND landscape orientation). 12.9"
   iPad Pro portrait (1024 wide, data-spread="off") falls through to the
   100cqi default. */
.mushaf-shell .mushaf-page-host[data-spread="on"][data-scroll="horizontal"] .mushaf-page-slot {
  /* v229b-fix-peek: slot width was `min(50cqi, calc(100cqb*600/917))`.
     When the height-bound branch won (common: viewport wider than the
     page's 600:917 aspect ratio), slots were narrower than 50cqi, and
     2 spread slots summed to LESS than the content area — leaving a
     gap on the right where the next slot peeked through. Using 50cqi
     directly makes 2 spread slots fill the content area exactly, with
     no peek. The page inside the slot still caps at its natural width
     via the .mushaf-page max-width rule, so it never stretches. */
  flex: 0 0 50cqi;
  width: 50cqi;
  /* Zero padding so adjacent slots are flush — the right and left
     pages touch at the spine like a real open book. */
  padding: 0;
  /* v229b-fix-peek: update intrinsic size for content-visibility:auto
     to match spread mode (was inheriting 100cqi from default rule). */
  contain-intrinsic-size: 50cqi 100cqb;
}

/* While not the active page, dim the slot subtly so the user knows
   which one is "live." Engine-paint that targets the active page only
   (random-mode, reverse-blur, etc.) can also key off .is-center. */
.mushaf-host[data-scroll="horizontal"] .mushaf-page-slot:not(.is-center) {
  opacity: 0.85;
  transition: opacity 200ms ease;
}
.mushaf-host[data-scroll="horizontal"] .mushaf-page-slot.is-center {
  opacity: 1;
}

/* v226 · FOUC fix: hold the frame invisible until the per-page QCF font has
   loaded AND fillVerseEnds() has populated the verse-end glyphs. The renderer
   adds .is-entering at innerHTML-swap time and removes it AFTER awaiting
   document.fonts.load(...) + fillVerseEnds(). Without this, the first paint
   shows fallback-font glyphs at QCF positions (everything visibly out of
   place) and empty verse-end medallions that pop in 80–300 ms later. */
.mushaf-frame.is-entering {
  opacity: 0;
  transition: none;
}
.mushaf-frame.is-leaving {
  opacity: 0;
  transition: opacity 80ms ease-out;
}
.mushaf-loading,
.mushaf-error {
  padding: 48px;
  text-align: center;
  color: var(--ms-ink-2);
  font-family: "Manrope", system-ui, sans-serif;
}
.mushaf-error { color: var(--ms-ink); }
.mushaf-error small {
  display: block;
  margin-top: 8px;
  color: var(--ms-ink-3);
  font-size: 12px;
}

/* ── PAGE · the printed mushaf surface ────────────────────────────────── */
/* Width/height are intrinsic to the compiled fragment (defined inline as
   percentages). This rule sets the paper surface and visual frame. */
/* v223 · 2026-05-18 · per user · pages 1-2 used to scale 1.12× to fill
   the reserved-line padding. Now disabled so ALL pages render at the
   same size · consistent visual rhythm across the mushaf. */
.mushaf-page[data-page="1"],
.mushaf-page[data-page="2"] {
  transform: none !important;
}
.mushaf-page {
  position: relative;
  /* Paper background — Phase 4d: defaults to --mu-paper which itself
     defaults to --ms-paper (tokens.css) when no paper theme is active.
     A paper theme (e.g. data-paper-theme="M-night") on the .mushaf-shell
     ancestor swaps --mu-paper / --mu-ink / --mu-medallion / --mu-accent
     via styles/mushaf-papers.css. Hex literals stay in that file's
     per-theme blocks, per rule A2. */
  background: var(--mu-paper, var(--ms-paper));
  border: 1px solid var(--mu-accent, var(--ms-gold-rim));
  border-radius: 4px;
  box-shadow:
    0 1px 0 var(--ms-rim),
    0 24px 64px -16px rgba(0, 0, 0, 0.45);

  /* Print-margin adjustment · 2026-05-15
     ──────────────────────────────────────────────────────────────────
     The Phase-1 compile pipeline emitted glyph positions normalized
     against a stage with `inset: 24px` on a 720 px canvas (~3.3 %
     margin both ways), which is tighter than the 1405 H Madina print
     (which has ~6 % horizontal · ~3 % vertical white margin around
     the text block). Rather than recompile all 604 pages, scale the
     layer contents uniformly to push every glyph / word / banner
     inward.

     Tune via `--mu-page-scale`. 0.92 = 4 % effective margin all sides
     beyond the compile baseline. Set to 1 to see the raw compile. */
  --mu-page-scale: 0.92;

  /* ── RESPONSIVE SIZING — HARD RULES ─────────────────────────────────
     The mushaf page MUST:
       (A) Scale uniformly across iPhone / iPad / desktop
       (B) NEVER reflow text to a different line (sacred-line rule)
       (C) NEVER be cut off — top, bottom, left, right always visible

     Strategy: query the host container's dimensions (cqi/cqb), pick
     whichever sizing constraint is the binding one, preserve aspect.

       width = min(
         720px,                          ← compile reference cap (no upscale)
         100cqi,                         ← never exceed host width
         calc(100cqb * 600 / 917)        ← never exceed host height
       )

     `cqi` = container query inline-size (host width)
     `cqb` = container query block-size  (host height)
     Both query the nearest size-typed ancestor — that's .mushaf-host.

     If host is tall+narrow (phone portrait): width is bound by 100cqi.
     If host is short+wide (phone landscape): width is bound by cqb*ratio.
     If host is huge (desktop): width is bound by 720px (compile reference).
     In ALL cases the full page is visible — nothing is ever clipped. */
  width: min(720px, 100cqi, calc(100cqb * 600 / 917));
  aspect-ratio: 600 / 917;
  /* Extra backstops: if any ancestor uses overflow:hidden or fixes a
     dimension, these prevent the page from ever exceeding it. */
  max-width: 100cqi;
  max-height: 100cqb;
  overflow: hidden;             /* the page itself clips its OWN bleed */
  direction: rtl;
  user-select: text;

  /* Establishes this element as a container so descendants can use cqi
     (container inline-size) units. The compiled p{N}.html emits
     `font-size: <pct>cqi` for every glyph and word — this is what makes
     them scale correctly when the page is rendered at any width. */
  container-type: inline-size;
}

/* ── NO-REFLOW GUARDS ──────────────────────────────────────────────────
   These are belt-and-suspenders. Every word span MUST stay on its
   compiled line, no matter how narrow the page becomes. */
.mushaf-page .glyph-layer .pv-glyph,
.mushaf-page .glyph-layer .word-glyph,
.mushaf-page .glyph-layer .verse-end,
.mushaf-page .text-layer .word {
  white-space: nowrap;        /* never split a word across lines */
  word-break: keep-all;       /* never break inside a word */
  overflow-wrap: normal;      /* never use a fallback break opportunity */
}

/* Modifier: dual-page spread (Phase 2+). Two pages side-by-side,
   each at 50% of the compiled-reference width. Triggers automatically
   at ≥900 px viewport (per the v1 spec). */
@media (min-width: 900px) {
  .mushaf-spread { display: flex; flex-direction: row; gap: 18px; }
  .mushaf-spread .mushaf-page {
    width: min(540px, 45vw, calc(95dvh * 600 / 917));
  }
}

/* ── Chrome layer (banner + basmala) ──────────────────────────────────────
   The compiled fragment puts .chrome-layer as a plain block; banner /
   basmala children are absolutely positioned with `top/right/width` as
   percentages. Without explicit positioning + transform on the layer,
   those children would anchor to .mushaf-page (the nearest positioned
   ancestor) and miss the print-margin scale. Mirror Layer 1's transform
   so the banner shrinks inward in lockstep with the glyphs. */
.mushaf-page .chrome-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transform: scale(var(--mu-page-scale, 1));
  transform-origin: center center;
}

/* ── LAYER 1 · glyph layer (publisher pixels) ─────────────────────────── */
/* Glyph spans are absolute-positioned with top/right percentages baked into
   their inline style by the compile step. The QCF per-page @font-face is
   declared in the compiled fragment's inline <style>.
   The uniform `scale(var(--mu-page-scale))` is what gives us the print-
   matching margin without re-doing the compile. */
.mushaf-page .glyph-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Color: defaults to the active paper theme's ink, falls back to
     --ms-ink-4 if no paper theme is active. */
  color: var(--mu-ink, var(--ms-ink-4));
  transform: scale(var(--mu-page-scale, 1));
  transform-origin: center center;
}
.mushaf-page .glyph-layer .pv-glyph,
.mushaf-page .glyph-layer .word-glyph,
.mushaf-page .glyph-layer .verse-end {
  position: absolute;
  /* Plain 2D translate (NOT translate3d). translate3d promoted EACH glyph
     to its own GPU compositing layer whose backing store is sized to the
     ~1em box; on iOS Safari the ink that overflows that box (tall alif
     ascenders, hamza/madda toppers, hamza-below) was clipped to the box —
     the "cut-off alif" bug. The parent `.glyph-layer { contain: layout
     paint }` + the page-level `transform: scale()` already give one
     composited surface, so per-glyph promotion was redundant. Position is
     identical (same 50%/-50% offset), so the SACRED line layout is
     untouched. */
  transform: translate(50%, -50%);
  white-space: nowrap;
  /* v229-fix-verse-end-intrinsic: give the medallion an intrinsic size
     so it claims layout space before fillVerseEnds() populates
     textContent. Without this, the element computes to height:0 (empty
     inline + position:absolute) and pops into existence 80-300 ms after
     the page mounts. */
  display: inline-block;
  min-height: 1em;
}
/* Containment on the glyph-layer keeps its paint isolated so per-glyph
   filter changes don't ripple into surrounding layout. */
.mushaf-page .glyph-layer {
  contain: layout paint;
}
.mushaf-page .glyph-layer .verse-end {
  color: var(--mu-medallion, var(--ms-gold));
}

/* ── LAYER 2 · invisible text layer (Tanzil Arabic) ───────────────────── */
.mushaf-page .text-layer {
  position: absolute;
  inset: 0;
  color: transparent;
  user-select: text;
  font-family: "Amiri Quran", "Amiri", "UthmanicHafs", serif;
  cursor: text;
  /* Match Layer 1's transform so the invisible spans stay aligned to the
     visible glyphs after scaling. */
  transform: scale(var(--mu-page-scale, 1));
  transform-origin: center center;
}
.mushaf-page .text-layer .word {
  position: absolute;
  transform: translate(50%, -50%);
  white-space: nowrap;
  border-radius: 3px;
  line-height: 1;
  box-sizing: border-box;
  text-align: center;
  /* Important: even if text overflows the explicit width, the bbox stays
     as compiled. Hit-testing, lasso intersection, and highlight rects
     all use this box. */
  overflow: visible;
  transition: background 0.14s ease, color 0.14s ease;
}
/* The selection rect IS the visible feedback — text stays transparent so
   we don't see ghost Arabic stacked on the glyph layer. */
.mushaf-page .text-layer ::selection {
  background: var(--ms-gold-soft);
  color: transparent;
}
.mushaf-page .text-layer .word:hover {
  background: var(--ms-mint-soft);
}
/* When a blur quiz is active, suppress the text-layer's hover /
   selection rectangles — they paint visible boxes on top of the
   blurred glyphs and read as "the layer 2 is leaking through." The
   reveal feedback comes from the glyph filter clearing instead. */
.mushaf-page .text-layer[data-blur] .word:hover,
.mushaf-page .text-layer[data-blur] .word.is-sel,
.mushaf-page .text-layer[data-blur] ::selection {
  background: transparent;
  box-shadow: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   LAYER 3 · EFFECT CATALOG
   ─────────────────────────────────────────────────────────────────────
   All effects are CSS classes on the Layer-2 .word span. Because Layer-2
   text is `color: transparent` but its glyph metrics are real, paint
   primitives compose naturally:

     - `background` / box-shadow  → paint a rect over Layer-1 glyph below
     - `text-shadow`              → paint a halo at glyph positions
     - `text-decoration`          → paint underlines/strikethroughs
     - `outline` / border         → paint a frame at the word bbox
     - `filter`                   → blur the word's region of Layer-1
     - `mix-blend-mode`           → blend background with Layer-1 glyph

   Multiple classes COMPOSE on the same word. e.g. `.hl-mint.glow-gold.fx-pulse`
   gives a mint background + gold glow + animated pulse — all at once.

   Token-only colors (rule A2). Add new effects by ADDING new classes
   — never inline hex literals here.
   ─────────────────────────────────────────────────────────────────── */

/* ── BACKGROUND HIGHLIGHTS (.hl-*) ─────────────────────────────────────
   Solid color washes painted as backgrounds. Use for marking words
   that belong to a category, palette, or scholar layer. */
.mushaf-page .text-layer .word.hl-gold      { background: var(--ms-gold-soft); }
.mushaf-page .text-layer .word.hl-gold-deep { background: var(--ms-gold-rim); }
.mushaf-page .text-layer .word.hl-mint      { background: var(--ms-mint-soft); }
.mushaf-page .text-layer .word.hl-mint-deep { background: var(--ms-mint-rim); }
.mushaf-page .text-layer .word.hl-rose      { background: rgba(217, 90, 90, 0.12); }
.mushaf-page .text-layer .word.hl-amber     { background: rgba(232, 168, 60, 0.14); }
.mushaf-page .text-layer .word.hl-violet    { background: rgba(168, 90, 217, 0.12); }
.mushaf-page .text-layer .word.hl-blue      { background: rgba(90, 168, 217, 0.14); }
.mushaf-page .text-layer .word.hl-ink       { background: rgba(20, 18, 14, 0.18); }
.mushaf-page .text-layer .word.hl-paper     { background: rgba(247, 241, 222, 0.55); }

/* Search-match highlight (find-on-page, root-match, etc.) */
.mushaf-page .text-layer .word.hl-search {
  background: var(--ms-gold-rim);
  outline: 1px solid var(--ms-gold);
}

/* Diagonal-stripe wash — for "spans multiple meanings" / ambiguous */
.mushaf-page .text-layer .word.hl-stripe {
  background-image: repeating-linear-gradient(
    45deg,
    var(--ms-gold-soft) 0 4px,
    transparent 4px 8px
  );
}

/* ── GLOW HALOS (.glow-*) ──────────────────────────────────────────────
   text-shadow on transparent text paints a soft halo at the EXACT
   positions of the Layer-1 glyph below it. Hugs the letter shapes. */
.mushaf-page .text-layer .word.glow-gold {
  text-shadow:
    0 0 6px var(--ms-gold),
    0 0 14px var(--ms-gold-soft);
}
.mushaf-page .text-layer .word.glow-mint {
  text-shadow:
    0 0 6px var(--ms-mint),
    0 0 14px var(--ms-mint-soft);
}
.mushaf-page .text-layer .word.glow-rose {
  text-shadow:
    0 0 6px rgba(217, 90, 90, 0.6),
    0 0 14px rgba(217, 90, 90, 0.18);
}
.mushaf-page .text-layer .word.glow-violet {
  text-shadow:
    0 0 6px rgba(168, 90, 217, 0.6),
    0 0 14px rgba(168, 90, 217, 0.18);
}
.mushaf-page .text-layer .word.glow-blue {
  text-shadow:
    0 0 6px rgba(90, 168, 217, 0.6),
    0 0 14px rgba(90, 168, 217, 0.18);
}
.mushaf-page .text-layer .word.glow-amber {
  text-shadow:
    0 0 6px rgba(232, 168, 60, 0.7),
    0 0 14px rgba(232, 168, 60, 0.18);
}
/* Intensity modifiers: strong / soft */
.mushaf-page .text-layer .word.glow-strong {
  text-shadow: 0 0 8px currentColor, 0 0 22px currentColor;
}
.mushaf-page .text-layer .word.glow-soft {
  text-shadow: 0 0 4px currentColor;
}

/* ── BOX GLOW (.boxglow-*) ─────────────────────────────────────────────
   Halo on the WORD'S BOX (not glyph shape). Use for emphasis where
   you want a rounded-rect halo rather than letter-hugging shape. */
.mushaf-page .text-layer .word.boxglow-gold {
  box-shadow: var(--ms-gold-glow);
}
.mushaf-page .text-layer .word.boxglow-mint {
  box-shadow: var(--ms-mint-glow-ring);
}

/* ── UNDERLINES / STRIKES (.ul-*) ──────────────────────────────────────
   text-decoration on transparent text underlines the glyph below
   without occluding it. Multiple variants for semantic distinctions. */
.mushaf-page .text-layer .word.ul-solid {
  text-decoration: underline 1.5px solid var(--ms-gold);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.ul-double {
  text-decoration: underline 1px double var(--ms-gold);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.ul-dashed {
  text-decoration: underline 1.5px dashed var(--ms-mint);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.ul-dotted {
  text-decoration: underline 1.5px dotted var(--ms-ink-2);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.ul-wavy {
  text-decoration: underline 1.5px wavy var(--ms-mint);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.strike {
  text-decoration: line-through 1.5px solid rgba(217, 90, 90, 0.8);
}

/* ── DIFF MARKERS (.diff-*) ────────────────────────────────────────────
   Show how one āyah differs from a parallel one. Used by the
   match-by-range / mutashābihāt comparison views. Color-blind safe:
   each diff also carries a shape (underline / strike / stripe). */
.mushaf-page .text-layer .word.diff-add {
  /* present in this verse, absent in the parallel */
  background: rgba(95, 181, 148, 0.18);
  text-decoration: underline 2px solid var(--ms-mint);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.diff-del {
  /* present in the parallel, absent here — usually rendered as a
     ghost-word at the position the missing word would occupy */
  background: rgba(217, 90, 90, 0.10);
  text-decoration: line-through 1.5px solid rgba(217, 90, 90, 0.7);
  opacity: 0.6;
}
.mushaf-page .text-layer .word.diff-swap {
  /* same position, different word (the classic mutashābih slip target) */
  background: var(--ms-gold-soft);
  text-decoration: underline 2px solid var(--ms-gold);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.diff-reorder {
  /* same word, different position */
  background: rgba(168, 90, 217, 0.10);
  text-decoration: underline 1.5px wavy rgba(168, 90, 217, 0.7);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.diff-prefix {
  /* differs only in prefix (e.g. wa- prefix added) */
  background: linear-gradient(90deg, var(--ms-mint-soft) 0 30%, transparent 30%);
}
.mushaf-page .text-layer .word.diff-suffix {
  /* differs only in suffix (e.g. case ending swap) */
  background: linear-gradient(90deg, transparent 0 70%, var(--ms-mint-soft) 70%);
}

/* ── ATTENTION / ANIMATION (.fx-*) ─────────────────────────────────────
   Use sparingly — animation pulls focus away from reading flow. */
.mushaf-page .text-layer .word.fx-pulse {
  animation: mushaf-pulse 1.8s ease-in-out infinite;
}
.mushaf-page .text-layer .word.fx-shimmer {
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--ms-gold-soft) 50%,
    transparent 100%
  );
  background-size: 200% 100%;
  animation: mushaf-shimmer 2.4s ease-in-out infinite;
}
.mushaf-page .text-layer .word.fx-spotlight {
  /* When applied to ONE word, all OTHER words on the page fade.
     Combine with .text-layer.has-spotlight on the parent. */
  text-shadow: 0 0 8px var(--ms-gold), 0 0 22px var(--ms-gold-soft);
  z-index: 1;
}
.mushaf-page .text-layer.has-spotlight .word:not(.fx-spotlight) {
  opacity: 0.35;
  filter: blur(1px);
  transition: all 0.3s;
}
.mushaf-page .text-layer .word.fx-fade {
  opacity: 0.4;
}

@keyframes mushaf-pulse {
  0%, 100% { text-shadow: 0 0 4px var(--ms-mint), 0 0 8px var(--ms-mint-soft); }
  50%      { text-shadow: 0 0 10px var(--ms-mint), 0 0 24px var(--ms-mint); }
}
@keyframes mushaf-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .mushaf-page .text-layer .word.fx-pulse,
  .mushaf-page .text-layer .word.fx-shimmer { animation: none; }
  /* v227k · honor user's reduced-motion preference for page-flips too. */
  .mushaf-shell .mushaf-page-host[data-scroll="horizontal"] {
    scroll-behavior: auto !important;
  }
}

/* v227k3 · Mushaf loading skeleton ─────────────────────────────────────
   When the host is mounted but no pages exist yet (during the brief
   fetch + font wait), show a centered shimmer card so users see "loading
   something" instead of a dark void. Auto-hides as soon as a .mushaf-page
   lands in the DOM (using :has). Pages then fade in via .is-entering. */
.mushaf-page-host:empty::after,
.mushaf-page-host:not(:has(.mushaf-page))::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(220px, 30%, 360px);
  aspect-ratio: 600 / 917;
  max-height: 70%;
  background: var(--mu-paper, #f7f1de);
  border: 1px solid var(--mu-accent, #b48b2b);
  border-radius: 4px;
  opacity: .35;
  background-image:
    linear-gradient(90deg, transparent 0%, rgba(255,255,255,.18) 50%, transparent 100%);
  background-size: 200% 100%;
  animation: mu-skeleton-shimmer 1.4s ease-in-out infinite;
  pointer-events: none;
}
.mushaf-page-host { position: relative; }
@keyframes mu-skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .mushaf-page-host:empty::after,
  .mushaf-page-host:not(:has(.mushaf-page))::after { animation: none; }
}

/* ── INDICATORS (.has-*) ───────────────────────────────────────────────
   Tiny dots/sparkles at the corner of a word marking metadata. */
.mushaf-page .text-layer .word.has-note::after,
.mushaf-page .text-layer .word.is-pair::after {
  content: "";
  position: absolute;
  top: -2px; left: -2px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ms-gold);
  box-shadow: 0 0 6px var(--ms-gold);
  pointer-events: none;
}
.mushaf-page .text-layer .word.has-note::after  { background: var(--ms-mint); }
.mushaf-page .text-layer .word.is-pair::after   { background: var(--ms-gold); }
.mushaf-page .text-layer .word.is-pair          { cursor: help; }

/* ── SELECTION (.is-sel, ::selection) ──────────────────────────────────
   `.is-sel` = programmatic selection (set by mushaf.highlightWord with
   the 'sel' color). `::selection` = native drag-selection feedback. */
.mushaf-page .text-layer .word.is-sel {
  box-shadow: var(--ms-mint-glow-ring);
  background: var(--ms-mint-soft);
}

/* ── MUTASHĀBIHĀT (Phase 5) ────────────────────────────────────────────
   Per-scholar layers. Distinct hues so multi-scholar overlays compose
   visibly (the gold of Sakhāwī + mint of Kirmānī on the same word
   should look like a third blended color, not erase one another). */
.mushaf-page .text-layer .word.mutashabih              { background: var(--ms-gold-soft); }
.mushaf-page .text-layer .word.mutashabih-sakhawi      {
  background: var(--ms-gold-soft);
  text-decoration: underline 1px solid var(--ms-gold);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.mutashabih-kirmani      {
  background: var(--ms-mint-soft);
  text-decoration: underline 1px solid var(--ms-mint);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.mutashabih-iskafi       {
  background: rgba(168, 90, 217, 0.10);
  text-decoration: underline 1px solid rgba(168, 90, 217, 0.6);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.mutashabih-ibn-jamaah   {
  background: rgba(217, 90, 90, 0.10);
  text-decoration: underline 1px solid rgba(217, 90, 90, 0.6);
  text-underline-offset: 4px;
}
.mushaf-page .text-layer .word.mutashabih-mudhish      {
  background: rgba(90, 168, 217, 0.10);
  text-decoration: underline 1px solid rgba(90, 168, 217, 0.6);
  text-underline-offset: 4px;
}

/* ── ROOT COLORING (Phase 2 with corpus morphology) ────────────────────
   When the corpus morphology is loaded, every word gets a `data-root`
   attribute (e.g. data-root="ك-ت-ب"). CSS can colorize by root for
   "every form of this root" highlighting. Implementation via JS-set
   `--root-hue` custom property on each word. */
.mushaf-page .text-layer .word[data-root].show-roots {
  text-shadow: 0 0 6px hsl(var(--root-hue, 45) 70% 50% / 0.55);
}

/* ── BLUR QUIZ MODES · v1-classic-style (Variant B · heavy fog) ────────
   The visual blur lives on the GLYPH layer — the calligraphic stroke
   itself gets `filter: blur(14px)`. The text-layer stays invisible and
   continues to be the tap target (its .word spans receive clicks and
   route through blur.js to add `.revealed` to the matching .word-glyph).
   Reveals clear the filter; mode-specific selectors gate which words
   stay sharp.
   Tunable via --mu-blur-radius (default 14px). */
.mushaf-page .glyph-layer .word-glyph,
.mushaf-page .glyph-layer .verse-end {
  transition: filter 0.22s ease;
}

/* Individual word/verse-end blur — applied via JS class for word-swipe
   and other modes that paint individual words. */
.mushaf-page .glyph-layer .word-glyph.blur,
.mushaf-page .glyph-layer .verse-end.blur {
  filter: blur(var(--mu-blur-radius, 14px));
}
.mushaf-page .glyph-layer .word-glyph.revealed,
.mushaf-page .glyph-layer .verse-end.revealed {
  filter: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   Surah banner · variant A (Banner D refined · shipped 2026-05-17)
   ─────────────────────────────────────────────────────────────────────
   Refinement layer on top of the per-page banner CSS emitted by the
   batch-compile pipeline. The per-page emit hardcodes selectors like
   .mushaf-page[data-page="77"][data-edition="v1"] .surah-banner — that's
   (0,3,1) specificity, which beats anything we'd write here normally,
   so each refinement uses `!important` to win. This avoids having to
   recompile all 604 pages just to land a typography polish.

   Three refinements over the original Banner D:
   1. `letter-spacing: 0.04em` on the surah name → tighter, more display.
   2. Filigree glyph swap `❀` (U+2740) → `❋` (U+274B, heavy 8-teardrop
      asterisk) → reads as an Islamic 8-pointed star at all sizes.
   3. Inner gilt rim emphasised slightly · 4 px → 4.5 px inset stop and
      rgba alpha bumped 0.50 → 0.55.
   ═══════════════════════════════════════════════════════════════════════ */
.mushaf-page .surah-banner span.name,
.mushaf-page .chrome-layer .surah-banner .name {
  letter-spacing: 0.04em !important;
}
.mushaf-page .surah-banner::before,
.mushaf-page .surah-banner::after {
  content: "\274B" !important;
  font-size: 3.2cqi !important;
  opacity: 0.78 !important;
}
.mushaf-page .surah-banner {
  box-shadow:
    inset 0 0 0 3px #f7f1de,
    inset 0 0 0 4.5px rgba(180, 139, 43, 0.55) !important;
}

/* ─── Basmala · theme the color so it follows the active paper ──────────
   The per-page emit hardcodes color:#1b1812 (a deep ink that matches the
   default ivory paper but becomes near-invisible on night/cliff themes
   where --mu-paper is black). Re-route through --mu-ink so the basmala
   inherits whichever ink the body glyphs are using. !important needed
   because the per-page selector outranks .mushaf-page .basmala on
   specificity (0,3,1) vs (0,1,1). */
.mushaf-page .basmala {
  color: var(--mu-ink, var(--ms-ink-4, #1b1812)) !important;
}

/* ─── Halo · problem-word glow ────────────────────────────────────────────
   Mirror of blur's wordbank flow, but writes .is-haloed onto matching
   glyphs and paints a bright glow on the letter contours themselves. The
   color is overridable via halobank.js (user picks one hex; the module
   rewrites the four --halo-* variables in a <style> block). Defaults are
   amber-gold that reads on both light and dark paper.

   Two structural tweaks vs the first draft:
   1. `contain: none` overrides the .word-glyph `contain: paint` rule that
      was added to clip blur radii. Without this override the text-shadow
      aura clips to the span's rectangular bounding box, producing a faint
      rectangular cutoff that looked like a hidden box outline around each
      word. Halo paint can safely escape its own box — there is no hit-
      test impact for a non-interactive visual effect.
   2. No animation. The pulse was distracting on long sessions. */
.mushaf-page .glyph-layer .word-glyph.is-haloed {
  contain: none;
  color: var(--halo-core, #fff7c2);
  text-shadow:
    0 0 4px  var(--halo-stop-1, rgba(255, 220, 100, 1)),
    0 0 12px var(--halo-stop-2, rgba(255, 196, 55, 0.95)),
    0 0 22px var(--halo-stop-3, rgba(255, 160, 30, 0.75)),
    0 0 40px var(--halo-stop-4, rgba(255, 140, 20, 0.45));
}
/* Text-layer mirror (invisible to user, kept for symmetry/searchability) */
.mushaf-page .text-layer .word.is-haloed {
  /* intentionally no visual — text layer stays invisible; class is only
     here so audits that grep both layers stay symmetric */
}

/* Halo color-picker swatch in the modal head */
.wb-modal .hb-color-row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-right: 12px;
  font-size: 12px;
  color: rgba(255,255,255,0.7);
}
.wb-modal .hb-color-row .hb-color-swatch {
  position: relative;
  display: inline-block;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.25);
  cursor: pointer;
  background: var(--halo-core, #fff7c2);
  box-shadow:
    0 0 4px  var(--halo-stop-1, rgba(255, 220, 100, 1)),
    0 0 10px var(--halo-stop-2, rgba(255, 196, 55, 0.95));
}
.wb-modal .hb-color-row .hb-color-swatch input[type="color"] {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  cursor: pointer;
  padding: 0; border: 0;
}
.wb-modal .hb-color-row .hb-color-reset {
  background: none;
  border: 1px solid rgba(255,255,255,0.2);
  color: rgba(255,255,255,0.75);
  border-radius: 12px;
  font-size: 11px;
  padding: 2px 8px;
  cursor: pointer;
}
.wb-modal .hb-color-row .hb-color-reset:hover {
  background: rgba(255,255,255,0.06);
}

/* data-blur is now a space-separated mode list (multi-mode hub).
   We use [data-blur~="X"] (whitespace-token match) so each mode's rules
   fire independently when its token is present in the attribute. */
/* Mode · verse: blur all glyphs + verse-end marks; reveals are per-verse. */
.mushaf-page .glyph-layer[data-blur~="verse"] .word-glyph:not(.revealed),
.mushaf-page .glyph-layer[data-blur~="verse"] .verse-end:not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}
/* Mode · word-swipe: same as verse but reveals are per-word, set by
   pointer drag (blur.js swipe handlers). */
.mushaf-page .glyph-layer[data-blur~="word-swipe"] .word-glyph:not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}
/* Mode · first: blur all but the first word of each verse (data-p="1"). */
.mushaf-page .glyph-layer[data-blur~="first"] .word-glyph:not([data-p="1"]):not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}
/* Mode · last → "Ending of āyat":
   Blur every word AFTER the LAST waqf stop sign in its verse
   (the part the reader has to "recall toward the next pause").
   blur.js's applyEndingAfterWaqf() marks each such word .after-last-waqf
   based on pages.json's t:'p' entries. For verses with no waqf entry,
   only the closing word is marked .after-last-waqf (last-word fallback). */
.mushaf-page .glyph-layer[data-blur~="last"] .word-glyph.after-last-waqf:not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}

/* Mode · connect: blur the TOP line + BOTTOM line of each page — the
   page-turn "connection" (what links to the previous / next page). JS
   (zero.html markConnect) tags the words on the min and max data-line of
   each page .connect-edge; reveals are per-word. */
.mushaf-page .glyph-layer[data-blur~="connect"] .word-glyph.connect-edge:not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}

/* Mode · begin: blur the OPENING of each verse — the first 2 words, universally
   (no waqf logic). JS (zero.html markBeginning) tags them .begin-edge;
   reveals are per-word. */
.mushaf-page .glyph-layer[data-blur~="begin"] .word-glyph.begin-edge:not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}

/* Mode · kumhum: blur every word ending in a plural/dual pronoun suffix
   (…كم / …هم family) — the mutashābihāt ending trap. zero.html markKumHum
   tags them .kumhum-edge from the text-layer twin; reveals are per-word. */
.mushaf-page .glyph-layer[data-blur~="kumhum"] .word-glyph.kumhum-edge:not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}

/* Mode · letters (v741): blur every word containing a letter from the picked
   confusable groups (ط ت · ض د · ق غ خ · ذ ز ظ · ص س ث · ح ه). zero.html
   markLetters tags them .lt-edge from the text-layer twin; per-word reveals. */
.mushaf-page .glyph-layer[data-blur~="letters"] .word-glyph.lt-edge:not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}

/* Mode · find / Search (v744): type a phrase — ONLY the consecutive verbatim
   matches (.sr-hit, tagged by zero.html markFind) stay sharp; everything else
   blurs. An inference aid: type what you remember, the phrase reveals. */
.mushaf-page .glyph-layer[data-blur~="find"] .word-glyph:not(.sr-hit):not(.revealed) {
  filter: blur(var(--mu-blur-radius, 14px));
}

/* v744 INVERT (body.blur-invert): one toggle flips every MARKER mode — blur
   the complement set instead. Higher specificity via body class, so these
   override the base rules both ways. Ayah/Word/Reverse are whole-page modes
   with no marker set to complement; the toggle no-ops there by design. */
body.blur-invert .mushaf-page .glyph-layer[data-blur~="find"] .word-glyph:not(.sr-hit) { filter: blur(0px); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="find"] .word-glyph.sr-hit:not(.revealed) { filter: blur(var(--mu-blur-radius, 14px)); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="letters"] .word-glyph.lt-edge { filter: blur(0px); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="letters"] .word-glyph:not(.lt-edge):not(.revealed) { filter: blur(var(--mu-blur-radius, 14px)); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="kumhum"] .word-glyph.kumhum-edge { filter: blur(0px); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="kumhum"] .word-glyph:not(.kumhum-edge):not(.revealed) { filter: blur(var(--mu-blur-radius, 14px)); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="last"] .word-glyph.after-last-waqf { filter: blur(0px); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="last"] .word-glyph:not(.after-last-waqf):not(.revealed) { filter: blur(var(--mu-blur-radius, 14px)); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="begin"] .word-glyph.begin-edge { filter: blur(0px); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="begin"] .word-glyph:not(.begin-edge):not(.revealed) { filter: blur(var(--mu-blur-radius, 14px)); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="connect"] .word-glyph.connect-edge { filter: blur(0px); }
body.blur-invert .mushaf-page .glyph-layer[data-blur~="connect"] .word-glyph:not(.connect-edge):not(.revealed) { filter: blur(var(--mu-blur-radius, 14px)); }

/* Mode · verse-tap-blur (v226):
   Inverse of `verse`. Default everything sharp; only verses the user has
   tapped get .is-tap-blurred. Double-tap removes the class.
   Applies to BOTH layers so the visible glyph AND the invisible text-layer
   selection box go fuzzy together. */
.mushaf-page .glyph-layer[data-blur~="verse-tap-blur"] .word-glyph.is-tap-blurred,
.mushaf-page .glyph-layer[data-blur~="verse-tap-blur"] .verse-end.is-tap-blurred {
  filter: blur(var(--mu-blur-radius, 14px));
}

/* While the user is mid-swipe, the host wants a "wiping" cursor. */
.mushaf-host[data-mushaf-blur="word-swipe"] .mushaf-page .text-layer,
.mushaf-page-host[data-mushaf-blur="word-swipe"] .mushaf-page .text-layer {
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}

/* ── Cursor feedback ─────────────────────────────────────────────────── */
.mushaf-page .text-layer .word { cursor: pointer; }

/* touch-action: manipulation tells iOS Safari to skip the 300 ms double-tap-
   to-zoom delay on tappable elements. user-select: none prevents the
   browser's "double-click selects a word" highlight on fast taps.
   Applied to both word layers because we tap-detect on either. */
.mushaf-page .text-layer .word,
.mushaf-page .glyph-layer .word-glyph {
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}
/* Clip the blur halo to the glyph's own box. Without this, filter:blur
   bleeds outward 10–14 px and looks like the word extends past its
   hit-test bounds — leading to "I tapped what looked like word X but
   accidentally unblurred its neighbor".
   v232b-fix-ink-clip: scoped to .blur ONLY. Applied unconditionally it
   clipped real glyph ink — QCF rasters routinely paint ~1% past the
   coords-measured span box, and high waqf marks poke past the box top —
   which violates the SACRED print-fidelity rule. Unblurred words must
   never be clipped; .revealed (filter:none) needs no clip either. */
.mushaf-page .glyph-layer .word-glyph.blur:not(.revealed) {
  contain: paint;
}

/* ── Lasso / draw-to-select mode ───────────────────────────────────────
   Activated by Mithlayn.mushaf.setDrawMode(true). The host gets a
   `.draw-mode` class; cursor becomes crosshair and native text-selection
   is disabled inside the page so the drag draws a rect instead of
   selecting Arabic. */
.mushaf-host.draw-mode .mushaf-page,
.mushaf-host.draw-mode .mushaf-page .text-layer,
.mushaf-host.draw-mode .mushaf-page .text-layer .word {
  cursor: crosshair;
  user-select: none;
  -webkit-user-select: none;
}
/* The live-drawn rectangle the user is dragging out. */
.mushaf-page .mushaf-lasso-rect {
  border: 1.5px dashed var(--ms-mint);
  background: rgba(95, 181, 148, 0.10);
  border-radius: 3px;
  box-shadow: 0 0 0 1px rgba(95, 181, 148, 0.18);
}
/* Words inside the lasso (live preview before release) get a soft fill
   so the user sees what will be picked. Different from .is-sel so the
   commit state is visually distinct from the in-flight preview. */
.mushaf-page .text-layer .word.is-sel-preview {
  background: var(--ms-mint-soft);
  outline: 1px dashed var(--ms-mint-rim);
}
/* Subtract-lasso preview — words inside the rect that will be REMOVED
   from selection get a red wash to telegraph the action. */
.mushaf-page .text-layer .word.is-sel-preview-sub {
  background: rgba(217, 90, 90, 0.18);
  outline: 1px dashed rgba(217, 90, 90, 0.7);
}
/* Subtract-mode rect color */
.mushaf-page .mushaf-lasso-rect.lasso-subtract {
  border-color: rgba(217, 90, 90, 0.8);
  background: rgba(217, 90, 90, 0.10);
  box-shadow: 0 0 0 1px rgba(217, 90, 90, 0.24);
}


/* ── Fallback for engines without `filter: blur()` (very rare) ─────────
   Hides the glyph entirely by setting opacity:0. The shape hint that
   filter:blur preserves is lost, but the quiz still functions. */
@supports not (filter: blur(1px)) {
  .mushaf-page .glyph-layer[data-blur] .word-glyph:not(.revealed),
  .mushaf-page .glyph-layer[data-blur] .verse-end:not(.revealed) {
    opacity: 0;
  }
  .mushaf-page .glyph-layer[data-blur~="first"] .word-glyph[data-p="1"],
  .mushaf-page .glyph-layer[data-blur~="last"]  .word-glyph.after-last-waqf {
    opacity: 1;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   Phase 2 · Chrome shell · popover
   ─────────────────────────────────────────────────────────────────────
   Owned by:
     - modules/mushaf/chrome.js  (.mushaf-shell, .mushaf-chrome, .mu-*)
     - modules/mushaf/popover.js (.mu-pop-card)
   Token-only colors (rule A2). 44 px tap targets (rule H3).
   ══════════════════════════════════════════════════════════════════════ */

.mushaf-shell {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  /* Fill the parent regardless of how it sizes:
     - flex parent (spine's #ph1-stage with display:flex): flex:1 takes over
     - block parent with explicit height: height:100% wins
     - block parent with only min-height: min-height:100% gives us the floor
     Belt-and-suspenders so we work in spine, preview, and any future host. */
  flex: 1 1 auto;
  min-height: 100%;
  background: var(--ms-bg);
  color: var(--ms-ink);
  font-family: "Manrope", system-ui, -apple-system, sans-serif;
}
.mushaf-shell {
  /* Ensures the absolutely-positioned .mu-mz-attach positions relative
     to the shell, not the viewport. */
  position: relative;
}
.mushaf-shell .mushaf-page-host {
  flex: 1 1 auto;
  min-height: 0;
  /* v227j · 2026-05-22 · `container-type: size` on this element requires
     a DEFINITE block-size at query time. Flex-stretched height (705px
     in the layout) is not "definite" enough for Chrome's container-
     query engine — `cqb` resolves to 0, which collapses the descendant
     .mushaf-page's `width: min(720px, 100cqi, calc(100cqb * 600/917))`
     to ~2px. Using `dvh` (dynamic viewport) minus the topbar gives a
     truly definite block-size that cqb can resolve against. The
     --hdr-h custom prop is set by boot.js when the chrome topbar
     measures; fall back to 80px until then. */
  height: calc(100dvh - var(--hdr-h, 80px));
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Generous side margins so the paper page doesn't sit jammed against
     the dark frame. Vertical kept small so we don't steal cqb (which is
     what binds the page's width on tall-narrow viewports). */
  padding: clamp(4px, 1.5vh, 12px) clamp(8px, 4vw, 40px);
  container-type: size;
  /* Pages 1-2 use transform:scale(1.12) which can visually overflow
     the host on short viewports. Clip so the visual stays inside the
     reader frame; the page itself already has its own overflow:hidden
     for content. */
  overflow: hidden;
}

/* ═══════════════════════════════════════════════════════════════════════
   Memorize right-attachment · variant C · tatweel-cap minimal
   ─────────────────────────────────────────────────────────────────────
   1.5″ + 35 % (≈194 px) wide strip glued to the right edge of the
   mushaf page itself. Position is computed in JS (chrome.js
   positionMemoAttach()) on every page render + window resize so the
   attach tracks the page through theme/zoom changes. Paper background,
   faint bronze rim, two tatweel-curl ornaments (top + bottom).

   Visibility is driven by `.is-memo-on` on the shell root, set by the
   topbar Memorize pill in chrome.js.
   ═══════════════════════════════════════════════════════════════════════ */
.mushaf-shell .mu-mz-attach {
  position: absolute;
  /* JS positioner sets `top`, `height`, `left`, and `--mz-overlap` (the
     amount of horizontal overlap with the page's right gutter, ~75% of
     the gutter width). The attach sits as a "tab" — its LEFT side
     overlaps INTO the page's right margin (over dead gutter space, never
     touching text), and its RIGHT side extends past the page edge for
     usable sidebar width. */
  top: 100px;
  height: 400px;
  left: 50%;
  width: 223px;
  display: none;                       /* hidden by default */
  background: var(--mu-paper, var(--ms-paper, #f7f1de));
  /* Full bronze rim on all 4 sides now — the attach is a tab sitting
     ON TOP of the page, not a sibling sharing one edge. */
  border: 1px solid var(--mu-accent, var(--ms-gold-rim));
  border-radius: 3px 4px 4px 3px;
  /* Soft left-side shadow makes the attach read as floating above the
     page's gutter zone. Right side keeps a smaller drop shadow for depth
     against the dark shell frame. */
  box-shadow:
    -6px 0 18px -6px rgba(0, 0, 0, 0.28),
    6px 16px 36px -12px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  pointer-events: auto;
  z-index: 4;
}
.mushaf-shell.is-memo-on .mu-mz-attach { display: block; }

/* Inner body — placeholder while the memorize-tool UI gets ported in.
   Positioned to leave room for the top + bottom tatweel-cap ornaments. */
.mushaf-shell .mu-mz-attach .mu-mz-body {
  position: absolute;
  left: 0; right: 0;
  top: 26px; bottom: 26px;
}

/* Tatweel-curl caps · SVG drawn as background-image. Two paths fade in
   from each end toward a center curl-pair, with flanking ink dots —
   matches the surah-banner ❋ vocabulary in a horizontal hairline form. */
.mushaf-shell .mu-mz-attach .mu-mz-cap {
  position: absolute;
  left: 10px; right: 10px;
  height: 16px;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 144 16' fill='none' stroke='%237c5b14' stroke-width='0.7' stroke-linecap='round'><path d='M0 8 L54 8'/><path d='M90 8 L144 8'/><g stroke='%237c5b14'><path d='M72 8 q-3 -3 -6 0 q3 3 6 0 q3 -3 6 0 q-3 3 -6 0 z'/><circle cx='62' cy='8' r='0.8' fill='%237c5b14'/><circle cx='82' cy='8' r='0.8' fill='%237c5b14'/></g></svg>");
  background-repeat: no-repeat;
  background-position: center center;
  background-size: 100% 100%;
  opacity: 0.7;
}
.mushaf-shell .mu-mz-attach .mu-mz-cap-top    { top:    6px; }
.mushaf-shell .mu-mz-attach .mu-mz-cap-bottom { bottom: 6px; transform: scaleY(-1); }

/* Dark/night paper themes · tint the caps cream so they remain visible. */
html[data-ui*="night"]  .mushaf-shell .mu-mz-attach .mu-mz-cap,
html[data-ui*="dark"]   .mushaf-shell .mu-mz-attach .mu-mz-cap,
html[data-ui*="cosmic"] .mushaf-shell .mu-mz-attach .mu-mz-cap {
  filter: invert(1) hue-rotate(180deg);
  opacity: 0.5;
}

/* Variant B · single-row compact (segmented). The grid spans the full
   width; the surah inline button absorbs leftover space via grid 1fr,
   with the two segmented groups flanking it. */
.mushaf-chrome {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, var(--ms-bg-1), var(--ms-bg));
  border-bottom: 1px solid var(--ms-hair-2);
  position: relative;
  z-index: 5;
  /* Push the topbar below the iOS dynamic island / notch on phones that
     report a non-zero safe-area-inset-top. No-op on desktop. */
  padding-top: env(safe-area-inset-top, 0);
}
/* Bottom of the shell respects the home-indicator inset (peek strip + page
   navigation arrows sit at the bottom edge on mobile). */
.mushaf-shell {
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.mu-grp-surah { position: relative; min-width: 0; }

/* ── Editorial · Variant D1 · stamp row + main row · sūrah centered ──── */
.ed-stamp {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  padding: 10px 26px 8px;
  border-bottom: 1px solid var(--ms-hair);
  font: 500 10px/1 "JetBrains Mono", Menlo, monospace;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ms-ink-3);
}
.ed-stamp-left { display: flex; align-items: center; gap: 14px; }
.ed-stamp-dot {
  width: 5px; height: 5px;
  background: var(--ms-mint);
  border-radius: 50%;
}
.ed-stamp-right { color: var(--ms-ink-4); }

.ed-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 18px;
  padding: 14px 26px 16px;
  /* v227k7 · min-height so .ed-arrow's 44px height target isn't
     squashed by the row's content-driven auto-row sizing. iPad HIG min
     tap target = 44×44. */
  min-height: 60px;
}
.ed-row-left  { justify-self: start;  display: flex; align-items: center; gap: 14px; }
.ed-row-center{ justify-self: center; position: relative; }
.ed-row-right { justify-self: end;    display: flex; align-items: center; gap: 3px; position: relative; }

.ed-rule { width: 1px; height: 30px; background: var(--ms-hair-2); }

.ed-pg {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
}
.ed-pg-input {
  width: 60px;
  min-height: 44px;       /* tap target floor (rule H3) */
  padding: 0;
  background: transparent;
  color: var(--ms-ink);
  border: none;
  border-radius: 4px;
  /* v143-A · larger display-serif page number; ties to the manuscript-
     aesthetic of the modes rubric. */
  font: 300 32px/1 "Cormorant Garamond", "Manrope", serif;
  text-align: end;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.ed-pg-input::-webkit-outer-spin-button,
.ed-pg-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* v227k7 · page-number subtle up-fade on PAGE CHANGE only. Was broken
   in v227k3: animation was always applied with play-state: paused +
   keyframes starting at opacity:0, which kept the number invisible
   forever. Now we use a CSS transition triggered by adding .is-flipping
   from chrome.js's onPageChange. Default state stays opacity:1. */
.ed-pg-input {
  transition: opacity 240ms ease, transform 240ms cubic-bezier(.2,.9,.2,1), color 180ms ease;
  opacity: 1;
}
.ed-pg-input.is-flipping {
  opacity: 0;
  transform: translateY(-4px);
}
@media (prefers-reduced-motion: reduce) {
  .ed-pg-input { transition: none; }
  .ed-pg-input.is-flipping { opacity: 1; transform: none; }
}
.ed-pg-input:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 2px; }
.ed-pg-of {
  font: 500 11px/1 "JetBrains Mono", Menlo, monospace;
  color: var(--ms-ink-3);
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}

.ed-nav { direction: rtl; display: inline-flex; gap: 2px; }
.ed-arrow {
  /* 44 px hit target (rule H3). Visual icon stays small via font-size +
     the round border-radius — the extra padding is invisible. */
  width: 44px; height: 44px; min-width: 44px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 99px;
  color: var(--ms-ink-2);
  cursor: pointer;
  font: 500 18px/1 "Manrope", system-ui, sans-serif;
  direction: ltr;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.12s, background 0.12s;
}
.ed-arrow:hover { color: var(--ms-mint); background: var(--ms-bg-2); }
.ed-arrow:active { transform: scale(0.92); transition: transform 80ms ease; }
.ed-arrow:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 1px; }
/* v227k3 · disabled state at p1 (prev) / p604 (next) so taps aren't no-ops. */
.ed-arrow:disabled {
  opacity: 0.28;
  cursor: not-allowed;
  pointer-events: none;
}

/* v227k7 · Cards drawer button · only visible on narrow viewports
   (iPad portrait / phones) where the sidebar is otherwise hidden.
   v229-fix-drawer-hide: bumped to .ed-arrow.mu-drawer-btn with
   !important because the generic .ed-arrow rule at L2329 uses
   `display: grid !important` and was winning the cascade, leaving the
   drawer button visible on every wide viewport. */
.ed-arrow.mu-drawer-btn { display: none !important; }
@media (max-width: 999px) {
  .ed-arrow.mu-drawer-btn { display: grid !important; }
  /* Drawer overlay · slides the sidebar in from the LEFT when the
     shell has .is-drawer-open. The sidebar would normally be hidden by
     the existing @media rule; we un-hide it via .is-drawer-open. */
  .mushaf-shell.is-drawer-open .mu-mz-attach {
    display: block !important;
    position: fixed !important;
    left: 0 !important;
    top: 0 !important;
    bottom: 0 !important;
    height: 100dvh !important;
    width: min(380px, 88vw) !important;
    z-index: 1000 !important;
    background: var(--ms-bg-1, #0f1114) !important;
    border-right: 1px solid var(--ms-rim, rgba(232,228,216,.10)) !important;
    box-shadow: 12px 0 40px rgba(0,0,0,.4) !important;
    transform: translateX(0);
    transition: transform 280ms cubic-bezier(.2,.9,.2,1);
    overflow-y: auto !important;
  }
  .mushaf-shell.is-drawer-open::before {
    content: '';
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.5);
    z-index: 999;
    animation: drawer-scrim-fade-in 280ms ease-out;
  }
  @keyframes drawer-scrim-fade-in {
    from { opacity: 0; } to { opacity: 1; }
  }
}

.ed-surah {
  display: inline-flex;
  align-items: baseline;
  gap: 16px;
  padding: 6px 14px;
  border-radius: 8px;
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--ms-ink);
  transition: background 0.12s;
}
.ed-surah:hover { background: var(--ms-bg-2); }
.ed-surah:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 1px; }
.ed-surah-tr {
  font: italic 400 28px/1 "Cormorant Garamond", "Manrope", serif;
  color: var(--ms-ink);
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.ed-surah-ar {
  font: 30px/1 "Amiri Quran", "Amiri", serif;
  color: var(--ms-ink-2);
  direction: rtl;
  padding-left: 16px;
  border-left: 1px solid var(--ms-hair-2);
  white-space: nowrap;
}
.ed-surah:hover .ed-surah-tr { color: var(--ms-mint); }
.ed-surah:hover .ed-surah-ar { color: var(--ms-mint-2); }

/* Right-side tools (★ ≡) — repurpose .mu-bmk's on-state styling */
.ed-arrow.mu-bmk.is-on {
  color: var(--ms-gold);
  background: var(--ms-gold-soft);
}
.mu-bmk-icon { font-size: 17px; line-height: 1; }

/* Right segmented group still uses the .mu-seg pattern (kept tight).
   The page nav uses .mu-nav-hero (Variant D), separate styling. */
.mu-seg {
  display: inline-flex;
  align-items: stretch;
  background: var(--ms-bg-2);
  border: 1px solid var(--ms-hair-2);
  border-radius: 8px;
  overflow: hidden;
}
.mu-seg-btn {
  min-width: 44px;
  min-height: 44px;
  padding: 0 14px;
  background: transparent;
  color: var(--ms-ink);
  border: none;
  border-right: 1px solid var(--ms-hair);
  border-radius: 0;
  font: 500 15px/1 "Manrope", system-ui, sans-serif;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.12s, background 0.12s;
}
.mu-seg-btn:last-child { border-right: none; }
.mu-seg-btn:hover  { background: var(--ms-bg-3); color: var(--ms-mint); }
.mu-seg-btn:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: -2px; }

/* ── Page nav · Variant D · hero indicator (RTL) ────────────────────────
   ‹ <page-input> / 604 ›
   The page nav reads right-to-left because the mushaf is an RTL book:
   the rightmost button (‹) goes to the previous page (lower number,
   which sits to the right of an open mushaf), and the leftmost (›)
   goes to the next page. */
.mu-nav-hero {
  direction: rtl;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 4px 12px;
  background: var(--ms-bg-2);
  border: 1px solid var(--ms-hair-2);
  border-radius: 12px;
}
.mu-nav-arrow {
  width: 44px;
  height: 44px;
  min-width: 44px;
  background: transparent;
  border: none;
  border-radius: 99px;
  color: var(--ms-ink-2);
  cursor: pointer;
  font: 500 22px/1 "Manrope", system-ui, sans-serif;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  direction: ltr;
  transition: color 0.12s, background 0.12s;
}
.mu-nav-arrow:hover  { color: var(--ms-mint); background: var(--ms-bg-3); }
.mu-nav-arrow:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 1px; }
.mu-nav-hero-box {
  direction: ltr;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 12px;
  border-left: 1px solid var(--ms-hair-2);
  border-right: 1px solid var(--ms-hair-2);
}
.mu-nav-hero-input {
  width: 56px;
  min-height: 36px;
  padding: 0;
  background: transparent;
  color: var(--ms-ink);
  border: none;
  border-radius: 4px;
  font: 300 28px/1 "Cormorant Garamond", "Manrope", serif;
  text-align: center;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.mu-nav-hero-input::-webkit-outer-spin-button,
.mu-nav-hero-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.mu-nav-hero-input:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 2px; }
.mu-nav-hero-total {
  font: 500 11px/1 "JetBrains Mono", Menlo, monospace;
  color: var(--ms-ink-3);
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mu-nav-hero-total b { color: var(--ms-ink-2); font-weight: 600; }

/* Bookmark star · gold when on */
.mu-bmk-icon { font-size: 18px; line-height: 1; }
.mu-bmk.is-on {
  color: var(--ms-gold);
  background: var(--ms-gold-soft);
}
.mu-bmk.is-on:hover { color: var(--ms-gold); background: var(--ms-gold-soft); }

/* Surah inline button · the picker affordance */
.mu-surah-inline {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 44px;
  padding: 0 14px;
  background: var(--ms-bg-2);
  border: 1px solid var(--ms-hair-2);
  border-radius: 8px;
  color: var(--ms-ink);
  cursor: pointer;
  font: 500 14px/1 "Manrope", system-ui, sans-serif;
  text-align: start;
  transition: border-color 0.12s, background 0.12s;
}
.mu-surah-inline:hover { border-color: var(--ms-mint-rim); background: var(--ms-bg-3); }
.mu-surah-inline:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 1px; }
.mu-surah-lbl {
  font: 600 11px/1 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ms-ink-3);
}
.mu-surah-tr {
  color: var(--ms-ink);
  flex: 0 0 auto;            /* claim full intrinsic width — no ellipsis */
  white-space: nowrap;
}
.mu-surah-ar {
  font: 20px/1 "Amiri Quran", "Amiri", serif;
  color: var(--ms-mint);
  direction: rtl;
  flex: 0 0 auto;            /* same — Arabic name must show in full */
  white-space: nowrap;
}
.mu-surah-caret { margin-left: auto; color: var(--ms-ink-3); font-size: 12px; }

/* Legacy aliases — keep the surah/bookmark popovers working under the
   new segmented layout (they used to live inside .mu-grp-tools / .mu-bmk).
   Resolve their absolute positioning to the new segmented containers. */
.mu-seg-tools { position: relative; }

.mu-pop {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  width: min(380px, 92vw);
  max-height: min(70vh, 520px);
  overflow: hidden;
  background: var(--ms-bg-1);
  border: 1px solid var(--ms-hair-3);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  padding: 10px;
  z-index: 10;
}
.mu-pop.is-open { display: flex; flex-direction: column; }
.mu-surah-pop  { left: 50%; transform: translateX(-50%); }
.mu-bmk-pop    { right: 0; width: min(280px, 92vw); }

.mu-pop-filter {
  width: 100%;
  padding: 10px 12px;
  margin-bottom: 8px;
  background: var(--ms-bg-2);
  color: var(--ms-ink);
  border: 1px solid var(--ms-hair-2);
  border-radius: 6px;
  font: 500 14px/1 "Manrope", system-ui, sans-serif;
}
.mu-pop-filter:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 1px; border-color: var(--ms-mint); }

.mu-pop-head {
  font: 600 10px/1 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ms-ink-3);
  padding: 4px 8px 10px;
}
.mu-pop-empty {
  padding: 14px 10px;
  color: var(--ms-ink-3);
  font-size: 13px;
  text-align: center;
}

.mu-pop-list {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.mu-pop-item {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  min-height: 44px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--ms-ink);
  cursor: pointer;
  border-radius: 6px;
  text-align: start;
  font: 500 14px/1.2 "Manrope", system-ui, sans-serif;
}
.mu-pop-item:hover { background: var(--ms-bg-2); border-color: var(--ms-hair-2); }
.mu-pop-num {
  font: 500 11px/1 "JetBrains Mono", Menlo, monospace;
  color: var(--ms-ink-3);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.mu-pop-tr { color: var(--ms-ink); }
.mu-pop-ar {
  font-family: "Amiri Quran", "Amiri", serif;
  font-size: 18px;
  color: var(--ms-mint);
  direction: rtl;
}

.mu-bmk-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.mu-bmk-go {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  padding: 10px 12px;
  min-height: 44px;
  background: var(--ms-bg-2);
  border: 1px solid var(--ms-hair-2);
  border-radius: 6px;
  color: var(--ms-ink);
  cursor: pointer;
  font: 500 14px/1 "Manrope", system-ui, sans-serif;
}
.mu-bmk-go:hover { border-color: var(--ms-mint-rim); color: var(--ms-mint); }
.mu-bmk-pg { font-variant-numeric: tabular-nums; }
.mu-bmk-del {
  /* 44 px hit target (rule H3). */
  width: 44px;
  height: 44px;
  min-width: 44px;
  background: transparent;
  border: 1px solid var(--ms-hair-2);
  border-radius: 6px;
  color: var(--ms-ink-3);
  cursor: pointer;
  font: 500 18px/1 "Manrope", system-ui, sans-serif;
}
.mu-bmk-del:hover { color: var(--ms-rose); border-color: var(--ms-rose-rim); }

.mu-pop-card {
  position: fixed;
  z-index: 1000;
  width: min(360px, 92vw);
  padding: 18px 20px 16px;
  background: var(--ms-bg-1);
  color: var(--ms-ink);
  border: 1px solid var(--ms-hair-3);
  border-radius: 10px;
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.55);
  font: 14px/1.55 "Manrope", system-ui, sans-serif;
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
  transition: opacity 0.14s ease, transform 0.14s ease;
}
.mu-pop-card.is-open { opacity: 1; transform: none; pointer-events: auto; }
.mu-pop-x {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 28px;
  height: 28px;
  background: transparent;
  border: none;
  color: var(--ms-ink-3);
  font: 500 20px/1 "Manrope", system-ui, sans-serif;
  cursor: pointer;
  border-radius: 6px;
}
.mu-pop-x:hover { color: var(--ms-ink); background: var(--ms-bg-2); }
.mu-pop-ref {
  font: 600 10px/1 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ms-ink-3);
  margin-bottom: 10px;
  padding-right: 28px;
}
.mu-pop-trans {
  font: 400 15px/1.55 "Cormorant Garamond", "Manrope", serif;
  color: var(--ms-ink);
}

@media (max-width: 720px) {
  /* On narrow viewports the main row stacks: surah on its own row above
     the controls (which sit on a single row, left = nav+page, right = tools). */
  .ed-row {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto;
    gap: 10px;
    padding: 12px 14px 14px;
  }
  .ed-row-center { grid-row: 1; grid-column: 1 / -1; justify-self: center; }
  .ed-row-left   { grid-row: 2; grid-column: 1; }
  .ed-row-right  { grid-row: 2; grid-column: 2; }
  .ed-surah-tr   { font-size: 22px; }
  .ed-surah-ar   { font-size: 24px; }
  .ed-stamp      { padding: 8px 14px 6px; font-size: 9px; letter-spacing: 0.16em; }
  .mu-surah-pop  { left: 0; right: 0; transform: none; width: auto; }
}

/* ══════════════════════════════════════════════════════════════════════
   Phase 4 · Memorize · blur-mode picker + rhythm dock
   ─────────────────────────────────────────────────────────────────────
   Blur pill: ALWAYS visible in the top bar (default mode = none →
   reads "Off"). User opens it any time to start a self-quiz drill.
   Rhythm dock: still memo-only — it only makes sense for Memorize
   sessions.
   ══════════════════════════════════════════════════════════════════════ */

/* Blur pill: always on. Pop closed by default; open class flips display. */
.ed-blur-pill   { display: inline-flex; }
.ed-blur-pop    { display: none; }
.ed-blur-pop.is-open { display: flex; }

/* Rhythm dock: memo-only (no rhythm context outside Memorize). */
.mu-rhythm-dock { display: none; }
html[data-mushaf-mode="memo"] .mu-rhythm-dock { display: flex; }

/* Blur-mode pill — mirrors the surah inline pill aesthetic but smaller.
   Padding tightened in design-A topbar compression: 14px → 11px horizontal
   so the right rail (Blur · Tajwīd · Halo · Peek · Undo · Bookmark · List ·
   Settings) fits without wrapping. Height stays 44px (WCAG H3 tap floor). */
.ed-blur-pill {
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 11px;
  background: var(--ms-bg-2);
  border: 1px solid var(--ms-hair-2);
  border-radius: 99px;
  color: var(--ms-ink);
  cursor: pointer;
  font: 500 12px/1 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.04em;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.ed-blur-pill:hover { border-color: var(--ms-mint-rim); color: var(--ms-mint); }
.ed-blur-pill:focus-visible { outline: 2px solid var(--ms-mint); outline-offset: 1px; }
.ed-blur-lbl {
  font: 600 11px/1 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.10em;
  text-transform: uppercase;
}
.ed-blur-caret { color: var(--ms-ink-3); font-size: 11px; }
.ed-blur-pill[data-mode="none"]   { color: var(--ms-ink-3); }
.ed-blur-pill[data-mode="verse"],
.ed-blur-pill[data-mode="word-swipe"],
.ed-blur-pill[data-mode="first"],
.ed-blur-pill[data-mode="last"]   { color: var(--ms-mint); border-color: var(--ms-mint-rim); }

/* Blur-mode picker popover (sits below the pill) */
.ed-blur-pop {
  right: 0;
  width: min(280px, 92vw);
}
.ed-blur-item {
  grid-template-columns: 1fr;
  align-items: flex-start;
  gap: 2px;
  padding: 10px 12px;
}
.ed-blur-item.is-on { background: var(--ms-mint-soft); border-color: var(--ms-mint-rim); }
.ed-blur-name {
  font: 600 12px/1.3 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.04em;
  color: var(--ms-ink);
}
.ed-blur-item.is-on .ed-blur-name { color: var(--ms-mint); }
.ed-blur-hint {
  font: 400 11px/1.4 "Manrope", system-ui, sans-serif;
  color: var(--ms-ink-3);
}

/* Rhythm dock — slim panel below the page in memo mode */
.mu-rhythm-dock {
  flex: 0 0 auto;
  background: linear-gradient(0deg, var(--ms-bg-1), var(--ms-bg));
  border-top: 1px solid var(--ms-hair-2);
  padding: 12px 18px;
}
.mu-rhythm-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.mu-rhythm-counter {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 14px;
  background: var(--ms-bg-2);
  border: 1px solid var(--ms-hair-2);
  border-radius: 99px;
  min-height: 44px;
}
.mu-rhythm-count {
  font: 300 22px/1 "Cormorant Garamond", serif;
  color: var(--ms-ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.mu-rhythm-lbl {
  font: 500 10px/1 "JetBrains Mono", monospace;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ms-ink-3);
}
.mu-rhythm-tick {
  min-width: 56px;
  min-height: 44px;
  padding: 0 18px;
  background: var(--ms-mint-soft);
  border: 1px solid var(--ms-mint-rim);
  border-radius: 99px;
  color: var(--ms-mint-2);
  cursor: pointer;
  font: 700 14px/1 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.02em;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.mu-rhythm-tick:hover {
  background: rgba(95, 181, 148, 0.18);
  color: var(--ms-mint);
  border-color: var(--ms-mint);
}
.mu-rhythm-tick:disabled {
  opacity: 0.55;
  cursor: progress;
}
.mu-rhythm-tick.is-pulsing { animation: mu-rhythm-pulse 1.2s ease-in-out infinite; }
@keyframes mu-rhythm-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(95,181,148,0); }
  50%      { box-shadow: 0 0 0 6px rgba(95,181,148,0.15); }
}
.mu-rhythm-sep {
  width: 1px;
  height: 26px;
  background: var(--ms-hair-2);
  margin: 0 4px;
}
.mu-rhythm-hint {
  flex: 1 1 auto;
  font: 500 11px/1.4 "JetBrains Mono", monospace;
  color: var(--ms-ink-3);
  letter-spacing: 0.06em;
}
.mu-rhythm-link {
  font: 500 11px/1 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.06em;
  color: var(--ms-ink-2);
  text-decoration: none;
  padding: 8px 12px;
  border-radius: 6px;
  border: 1px solid var(--ms-hair-2);
  background: transparent;
  white-space: nowrap;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.mu-rhythm-link:hover {
  color: var(--ms-mint);
  border-color: var(--ms-mint-rim);
  background: var(--ms-bg-2);
}

/* ── Settings panel (wrench in the chrome's right column) ──────────────
   Wired in modules/mushaf/chrome.js · buildSettingsPanel(). Future
   slots (font/scale · paper tint · edition switcher) will appear here
   as ed-set-row entries. */
.ed-settings { color: var(--ms-ink-2); }
.ed-settings:hover { color: var(--ms-mint); }
.ed-settings svg { width: 18px; height: 18px; display: block; }

.ed-settings-pop {
  right: 0;
  width: min(320px, 92vw);
}
.ed-settings-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 4px 0;
}
.ed-set-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 12px;
  min-height: 56px;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.12s;
}
.ed-set-row:hover { background: var(--ms-bg-2); }
.ed-set-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1 1 auto; }
.ed-set-name {
  font: 600 12px/1.2 "Manrope", system-ui, sans-serif;
  color: var(--ms-ink);
  letter-spacing: 0.02em;
}
.ed-set-hint {
  font: 400 11px/1.4 "Manrope", system-ui, sans-serif;
  color: var(--ms-ink-3);
}

/* Toggle switch */
.ed-set-toggle {
  position: relative;
  display: inline-flex;
  width: 40px;
  height: 24px;
  flex-shrink: 0;
}
.ed-set-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  margin: 0;
  z-index: 2;
}
.ed-set-knob {
  position: absolute;
  inset: 0;
  background: var(--ms-bg-2);
  border: 1px solid var(--ms-hair-2);
  border-radius: 99px;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.ed-set-knob::after {
  content: '';
  position: absolute;
  left: 2px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  background: var(--ms-ink-3);
  border-radius: 50%;
  transition: left 0.18s ease, background 0.18s ease;
}
.ed-set-input:checked + .ed-set-knob {
  background: var(--ms-mint-soft);
  border-color: var(--ms-mint-rim);
}
.ed-set-input:checked + .ed-set-knob::after {
  left: 20px;
  background: var(--ms-mint);
}
.ed-set-input:focus-visible + .ed-set-knob {
  outline: 2px solid var(--ms-mint);
  outline-offset: 2px;
}

/* ── Phase 4d · Paper-theme picker (in settings panel) ─────────────── */
.ed-set-section-head {
  padding: 14px 12px 6px;
  font: 600 10px/1 "JetBrains Mono", "Manrope", monospace;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ms-ink-4);
}
.ed-paper-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
  padding: 0 8px 8px;
}
.ed-paper-swatch {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 9px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
  text-align: left;
  font: 500 11.5px/1.1 "Manrope", system-ui, sans-serif;
  color: var(--ms-ink-2);
  min-height: 44px;     /* Rule H3 — tap target */
}
.ed-paper-swatch:hover {
  background: var(--ms-bg-2);
  color: var(--ms-ink);
}
.ed-paper-swatch.is-on {
  background: var(--ms-mint-soft);
  border-color: var(--ms-mint-rim);
  color: var(--ms-ink);
}
.ed-paper-disc {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex: 0 0 22px;
  background: var(--mu-paper);
  border: 1.5px solid var(--mu-accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10),
              0 1px 2px rgba(0, 0, 0, 0.18);
  position: relative;
}
.ed-paper-disc::after {
  /* Tiny medallion dot in the center to hint at the verse-end color. */
  content: '';
  position: absolute;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--mu-medallion);
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.ed-paper-name {
  font: 500 11.5px/1.2 "Manrope", system-ui, sans-serif;
  letter-spacing: 0.01em;
}

/* Edge peek · the WHOLE shell is a press-and-hold zone except for
   words/buttons (handled in JS via isInteractiveTarget). A small visible
   pull-bar handle stays at the bottom as a discoverability cue. */
.mu-peek-handle-frame {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  display: flex;
  justify-content: center;
  pointer-events: none;     /* the FRAME doesn't catch events; .mushaf-shell does */
  padding-bottom: 8px;
  z-index: 6;
}
.mu-peek-handle {
  width: 46px;
  height: 4px;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.16);
  pointer-events: none;
  transition: width 0.15s ease, background 0.15s ease;
}
.mushaf-shell:hover .mu-peek-handle {
  background: rgba(255, 255, 255, 0.30);
  width: 60px;
}
.mu-peek-handle-frame.is-pressed .mu-peek-handle {
  background: var(--ms-mint);
  width: 80px;
}
/* Slight cursor hint when hovering non-word area of the shell — invites peek */
.mushaf-shell:not(.is-peek-on) {
  cursor: zoom-in;
}
.mushaf-shell.is-peek-on { cursor: zoom-out; }
.mushaf-shell .text-layer .word { cursor: pointer; }    /* override on words */
.mushaf-shell button { cursor: pointer; }                /* override on buttons */
/* On light-theme paper the bar needs a darker color to stay visible */
html[data-ui="mushaf"]   .mu-peek-handle,
html[data-ui="medina"]   .mu-peek-handle,
html[data-ui="notion"]   .mu-peek-handle,
html[data-ui="tarteel"]  .mu-peek-handle,
html[data-ui="qcom-light"] .mu-peek-handle,
html[data-ui="arc"]      .mu-peek-handle,
html[data-ui="material"] .mu-peek-handle,
html[data-ui="ia"]       .mu-peek-handle,
html[data-ui="madinah"]  .mu-peek-handle,
html[data-ui="ottoman"]  .mu-peek-handle,
html[data-ui="alhambra"] .mu-peek-handle,
html[data-ui="zamzam"]   .mu-peek-handle {
  background: rgba(0, 0, 0, 0.14);
}
html[data-ui="mushaf"]   .mushaf-shell:hover .mu-peek-handle,
html[data-ui="medina"]   .mushaf-shell:hover .mu-peek-handle,
html[data-ui="notion"]   .mushaf-shell:hover .mu-peek-handle,
html[data-ui="tarteel"]  .mushaf-shell:hover .mu-peek-handle,
html[data-ui="qcom-light"] .mushaf-shell:hover .mu-peek-handle,
html[data-ui="arc"]      .mushaf-shell:hover .mu-peek-handle,
html[data-ui="material"] .mushaf-shell:hover .mu-peek-handle,
html[data-ui="ia"]       .mushaf-shell:hover .mu-peek-handle,
html[data-ui="madinah"]  .mushaf-shell:hover .mu-peek-handle,
html[data-ui="ottoman"]  .mushaf-shell:hover .mu-peek-handle,
html[data-ui="alhambra"] .mushaf-shell:hover .mu-peek-handle,
html[data-ui="zamzam"]   .mushaf-shell:hover .mu-peek-handle {
  background: rgba(0, 0, 0, 0.28);
}

/* V3 Peek toolbar button · square icon, custom 2-line "veil-lift" glyph */
.ed-peek-v3 {
  width: 44px; height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* v143-A · manuscript-quiet · no box, no border, no fill. The 44 px tap
     target is preserved invisibly. */
  background: transparent;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  color: var(--ms-ink-3);
  user-select: none;
  touch-action: none;
  transition: background 0.12s ease, color 0.12s ease, transform 0.08s ease;
}
.ed-peek-v3:hover,
.ed-peek-v3:active { color: var(--ms-mint); background: transparent; }
.ed-peek-v3.is-pressed {
  background: transparent;
  color: var(--ms-mint);
  transform: scale(0.96);
}
.ed-peek-glyph {
  position: relative;
  width: 20px;
  height: 20px;
  pointer-events: none;
}
.ed-peek-glyph::before,
.ed-peek-glyph::after { height: 2px !important; }

/* Undo-reveal button · matches the peek button (44×44 frame). Sits
   to the LEFT of the blur pill. Disabled state (no reveals to undo) is
   visibly dimmed and non-interactive. */
.ed-undo-reveal {
  width: 44px; height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* v143-A · manuscript-quiet · matches .ed-peek-v3 */
  background: transparent;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  color: var(--ms-ink-3);
  user-select: none;
  touch-action: manipulation;
  transition: background 0.12s ease, color 0.12s ease, transform 0.08s ease;
}
.ed-undo-reveal:hover:not(:disabled),
.ed-undo-reveal:active:not(:disabled) {
  color: var(--ms-mint);
  background: transparent;
}
.ed-undo-reveal:active:not(:disabled) {
  background: transparent;
  color: var(--ms-mint);
  transform: scale(0.96);
}
.ed-undo-reveal:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.ed-undo-glyph {
  pointer-events: none;
  display: block;
}
.ed-peek-glyph::before,
.ed-peek-glyph::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 1.5px;
  background: currentColor;
  border-radius: 1px;
  transition: transform 0.12s ease;
}
.ed-peek-glyph::before { top: 3px; }
.ed-peek-glyph::after  { bottom: 3px; transform: scaleX(0.6); }
.ed-peek-v3.is-pressed .ed-peek-glyph::before { transform: translateY(-2px) scaleX(0.85); }
.ed-peek-v3.is-pressed .ed-peek-glyph::after  { transform: translateY(2px)  scaleX(0.85); }
/* Ensure the shell can host the absolute peek strip */
.mushaf-shell { position: relative; }

/* Peek override · while pressed, the shell gains .is-peek-on and every
   blurred glyph snaps back to crisp. Pure CSS = instant; no mode swap,
   no JS re-walk. !important needed because per-mode blur rules use
   specific [data-blur="..."] selectors. */
.mushaf-shell.is-peek-on .mushaf-page .glyph-layer .word-glyph,
.mushaf-shell.is-peek-on .mushaf-page .glyph-layer .verse-end,
.mushaf-shell.is-peek-on .mushaf-page .text-layer .word {
  filter: none !important;
  transition: filter 0.10s ease !important;
}

/* "Site default" reset row · half-light half-dark disc shows it follows
   whichever brightness the site theme is currently on. */
.ed-paper-disc--auto {
  background: linear-gradient(135deg, #f3e9ce 0%, #f3e9ce 50%, #0a0a0a 50%, #0a0a0a 100%);
  border: 1.5px solid var(--ms-hair-3);
}
.ed-paper-disc--auto::after {
  display: none;
}

/* ═══════════════════════════════════════════════════════════════════
   v218 · Mushaf top bar · variant 1 (Manrope · minimal page nav)
   ship 2026-05-18 · spec from the user:
     · surah picker → LEFT (was center) · Manrope, mixed case, gilt
       chevron (no italic Cormorant)
     · page nav   → CENTER (was left) · BARE arrows + page text · no
       pill background, no border
     · right-side: existing 3-mode cluster + Memorize stay where they
       are · bookmark + settings switch to gilt-tinted icon-pills
     · all type → Manrope (drop the Cormorant italic on the surah)
   ─────────────────────────────────────────────────────────────────
   These are layered OVERRIDES on top of the existing v208 ed-row /
   ed-pg / ed-nav / ed-surah selectors · scoped via specificity, NO
   DOM restructure required. The modes-collapse popover lands in a
   follow-up commit.
   ═══════════════════════════════════════════════════════════════════ */

/* Layout · swap left ↔ center via grid columns so the surah picker
   moves to position 1 and the page-nav moves to position 2. */
.ed-row {
  grid-template-columns: 1fr auto 1fr;
}
.ed-row .ed-row-left   { order: 2; justify-self: center;   grid-column: 2; gap: 6px; }
.ed-row .ed-row-center { order: 1; justify-self: flex-start; grid-column: 1; }
.ed-row .ed-row-right  { order: 3; justify-self: flex-end; grid-column: 3; }

/* Surah picker · V1 spec from preview-mushaf-bar-manrope-5 ·
   tracked-caps Manrope with a small ▾ chevron · the picker opens
   on tap (button behavior restored per the V1 implementation). */
.ed-surah {
  background: transparent !important;
  border: 0 !important;
  /* v229-fix-tap-ed-surah: was 6px 8px → 26px tall (HIG 44 fail).
     Bumped to 10px 14px so padding + content reach 44+. min-height
     belt-and-suspenders for short content. */
  padding: 10px 14px !important;
  min-height: 44px !important;
  border-radius: 8px !important;
  display: inline-flex !important;
  align-items: center !important;
  gap: 8px !important;
  cursor: pointer !important;
  pointer-events: auto !important;
  transition: background 0.14s !important;
}
.ed-surah:hover { background: rgba(255, 255, 255, 0.04) !important; }
/* Restored tracked-caps treatment per V1 spec · uppercase Manrope */
.ed-surah-tr {
  font-family: "Manrope", system-ui, sans-serif !important;
  font-weight: 700 !important;
  font-style: normal !important;
  font-size: 11.5px !important;
  letter-spacing: 0.22em !important;
  text-transform: uppercase !important;
  color: var(--ms-ink) !important;
}
.ed-surah-ar { display: none !important; }   /* V1 · transliterated only */
.ed-surah::after {
  content: "▾";
  font: 500 10px "Manrope", system-ui, sans-serif;
  color: var(--ms-ink-3, rgba(255,255,255,0.4));
  margin-left: 2px;
}

/* Page nav · drop the pill background on the page input + arrows */
.ed-pg {
  background: transparent !important;
  border: 0 !important;
  padding: 0 !important;
  gap: 4px !important;
}
.ed-pg-input {
  background: transparent !important;
  border: 0 !important;
  font: 700 15px "Manrope", system-ui, sans-serif !important;
  color: var(--ms-ink) !important;
  font-feature-settings: 'tnum' !important;
  min-width: 22px !important;
  display: inline-block !important;
  text-align: center !important;
  cursor: pointer !important;
  padding: 4px 6px !important;
  border-radius: 4px !important;
  transition: background 0.14s !important;
}
.ed-pg-input:hover { background: rgba(255, 255, 255, 0.06) !important; }
.ed-pg-of {
  font: 500 11px "Manrope", system-ui, sans-serif !important;
  color: var(--ms-ink-3, rgba(255,255,255,0.4)) !important;
  letter-spacing: 0.02em !important;
}
.ed-rule { display: none !important; }   /* drop the vertical divider after the page input */

.ed-nav { gap: 4px !important; }
.ed-arrow {
  background: transparent !important;
  border: 0 !important;
  color: var(--ms-ink-3, rgba(255,255,255,0.4)) !important;
  font: 200 24px "Manrope", system-ui, sans-serif !important;
  /* v227k8 · was 32×32 which broke iPad HIG min tap target (44×44). */
  width: 44px !important; height: 44px !important;
  display: grid !important; place-items: center !important;
  border-radius: 50% !important;
  cursor: pointer !important;
  transition: color 0.12s, background 0.12s !important;
  line-height: 1 !important;
}
.ed-arrow:hover {
  color: var(--ms-ink) !important;
  background: rgba(255, 255, 255, 0.05) !important;
}

/* Gilt-tint bookmark + settings (the two util icons in ed-row-right
   that sit after the Memorize pill). They get the same "primary-util"
   treatment as Memorize but as icon-only pills. */
.ed-row-right > button.mu-util,
.ed-row-right > .mu-bmk-btn,
.ed-row-right > .mu-settings-btn {
  width: 36px !important; height: 36px !important;
  background: rgba(213, 177, 87, 0.12) !important;
  border: 1px solid rgba(213, 177, 87, 0.38) !important;
  color: #d5b157 !important;
  border-radius: 99px !important;
  display: grid !important; place-items: center !important;
  margin-left: 4px !important;
  transition: background 0.14s, border-color 0.14s !important;
}
.ed-row-right > button.mu-util:hover,
.ed-row-right > .mu-bmk-btn:hover,
.ed-row-right > .mu-settings-btn:hover {
  background: rgba(213, 177, 87, 0.2) !important;
  border-color: rgba(213, 177, 87, 0.6) !important;
}

/* ── Undo-reveal + Peek · move OUT of the top bar onto the page sides ──
   v218 supplemental · 2026-05-18 · iPad thumb-zone optimization.
   Both buttons used to crowd the top bar (between Modes and bookmark).
   They now float as 52 px circles vertically-centered on the viewport
   edges, gilt-tinted glass. The DOM doesn't move — pure CSS pulls them
   out via position:fixed. */
.ed-undo-reveal {
  position: fixed !important;
  left: max(14px, env(safe-area-inset-left, 14px)) !important;
  top: 50vh !important;
  transform: translateY(-50%) !important;
  width: 52px !important; height: 52px !important;
  border-radius: 50% !important;
  background: rgba(213, 177, 87, 0.16) !important;
  border: 1px solid rgba(213, 177, 87, 0.5) !important;
  color: #d5b157 !important;
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  backdrop-filter: blur(10px) saturate(120%);
  z-index: 50;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.36);
  display: grid !important; place-items: center !important;
  cursor: pointer;
  transition: background 0.14s, transform 0.14s, opacity 0.14s;
  margin: 0 !important;
}
.ed-undo-reveal:hover:not(:disabled) {
  background: rgba(213, 177, 87, 0.26) !important;
  transform: translateY(-50%) scale(1.04) !important;
}
.ed-undo-reveal:disabled {
  opacity: 0;        /* hide entirely when nothing to undo */
  pointer-events: none;
}
.ed-undo-reveal svg { width: 24px !important; height: 24px !important; }

.ed-peek-v3 {
  position: fixed !important;
  right: max(14px, env(safe-area-inset-right, 14px)) !important;
  top: 50vh !important;
  transform: translateY(-50%) !important;
  width: 52px !important; height: 52px !important;
  border-radius: 50% !important;
  background: rgba(213, 177, 87, 0.16) !important;
  border: 1px solid rgba(213, 177, 87, 0.5) !important;
  color: #d5b157 !important;
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  backdrop-filter: blur(10px) saturate(120%);
  z-index: 50;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.36);
  display: grid !important; place-items: center !important;
  cursor: pointer;
  transition: background 0.14s, transform 0.14s;
  margin: 0 !important;
}
.ed-peek-v3:hover {
  background: rgba(213, 177, 87, 0.26) !important;
  transform: translateY(-50%) scale(1.04) !important;
}
.ed-peek-v3.is-pressed {
  background: rgba(213, 177, 87, 0.42) !important;
  border-color: rgba(213, 177, 87, 0.85) !important;
}
.ed-peek-v3 .ed-peek-glyph {
  /* peek glyph styled via existing rules; visible at 52 px */
}

/* Hide the Reader-mode "Jump to verse" FAB when the Mushaf view is active.
   .float-jump is body-level so we use :has() to scope by which view is on. */
body:has(#view-placehold1.is-on) .float-jump,
body:has(#view-placehold1.active) .float-jump {
  display: none !important;
}

/* v219 · Per user · 2026-05-18 · the peek FAB on the right edge is gone;
   the LEFT + RIGHT negative-space strips (outside the mushaf page) become
   the touch zones for unblur. The page itself no longer reveals on tap. */
.ed-peek-v3 { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════
   v222 · Modes pill collapse · ship 2026-05-18
   The original 3-pill cluster (Blur · Tajwīd · Halo) collapses into a
   single "Modes" pill with three dot indicators. Tapping opens a
   popover with rows that hand off to each mode's existing hub.
   ═══════════════════════════════════════════════════════════════════ */
.ed-modes-wrap { display: none !important; }      /* hide the original cluster */

.ed-modes-pill-v1 {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 14px;
  background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1);
  border-radius: 99px; cursor: pointer;
  font: 600 12px "Manrope", system-ui, sans-serif;
  color: rgba(255,255,255,0.65);
  letter-spacing: 0.04em;
  transition: background 0.14s, color 0.14s, border-color 0.14s;
  margin-left: 4px;
}
.ed-modes-pill-v1:hover { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.92); border-color: rgba(255,255,255,0.18); }
.ed-modes-pill-v1.is-open { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.92); border-color: rgba(255,255,255,0.2); }
.ed-mp-dots { display: inline-flex; gap: 3px; }
.ed-mp-dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(255,255,255,0.32);
}
.ed-mp-dot.on { background: #6acf94; box-shadow: 0 0 4px rgba(106,207,148,0.5); }
.ed-mp-lbl { letter-spacing: 0.04em; }
.ed-mp-chev { font-size: 10px; color: rgba(255,255,255,0.4); }

/* Popover · drops from the modes pill */
.ed-modes-pop {
  position: absolute;
  top: calc(100% + 8px);
  right: 8px;
  width: 280px;
  background: rgba(15, 20, 24, 0.96);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 14px;
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.5);
  padding: 8px;
  z-index: 100;
}
.ed-modes-pop[hidden] { display: none !important; }
.ed-modes-pop::before {
  content: ""; position: absolute; top: -5px; right: 24px;
  width: 10px; height: 10px;
  background: rgba(15, 20, 24, 0.96);
  border-left: 1px solid rgba(255,255,255,0.12);
  border-top:  1px solid rgba(255,255,255,0.12);
  transform: rotate(45deg);
}
.ed-mp-pop-head {
  font: 700 9.5px "Manrope", sans-serif;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: rgba(255,255,255,0.4);
  padding: 8px 10px 6px;
}
.ed-mp-pop-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 9px 10px;
  background: transparent; border: 0;
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  color: rgba(255,255,255,0.92);
  font-family: "Manrope", sans-serif;
  transition: background 0.12s;
}
.ed-mp-pop-row:hover { background: rgba(255,255,255,0.04); }
.ed-mp-pop-icn {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 8px;
  color: rgba(255,255,255,0.7);
  flex-shrink: 0;
}
.ed-mp-pop-icn svg { width: 16px; height: 16px; }
.ed-mp-pop-meta { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.ed-mp-pop-nm  { font: 600 12.5px "Manrope", sans-serif; }
.ed-mp-pop-sub { font: 500 10px "Manrope", sans-serif; color: rgba(255,255,255,0.45); letter-spacing: 0.02em; }
.ed-mp-pop-arr { font: 500 14px "Manrope", sans-serif; color: rgba(255,255,255,0.4); }

/* ═══════════════════════════════════════════════════════════════════
   v220 · iPad portrait · mushaf flush-LEFT, memorize fills the right
   ship 2026-05-18 · spec from the user:
     · mushaf rendered with NO shrinkage (keeps its natural 720 px)
     · memorize panel expands to fill the negative space on the right
     · landscape behavior (centered mushaf + 223 px sidebar) unchanged
   ─────────────────────────────────────────────────────────────────
   The JS positionMemoAttach only sets left/top/height inline. WIDTH
   comes from CSS — so overriding .mu-mz-attach width in the media
   query is enough; the JS still glues the attach to the page's right
   edge with the 75% gutter overlap.
   ═══════════════════════════════════════════════════════════════════ */
/* v227k11-isSpread-refactor · DELETED v191/v224 block (was @media
   (min-width: 1000px) setting .mushaf-page-host { padding-left: 0 } and
   .mu-mz-attach { width: calc(--vp-w - 720px); max-width: 460px }).

   That block competed with the v227j sidebar-permanent rules at the
   first @media block above (which set 300px reserve + 300px sidebar
   pinned to host left). On real 12.9" iPad Pro portrait (1024 wide),
   the two rule-sets won different properties via specificity vs source
   order: v227j won padding-left (higher specificity, attr selector),
   v191/v224 won sidebar width (same specificity, source order). Result:
   sidebar was 460 wide while reserve was only 300, so the spread area
   was wider than expected and the slot-width math gave ~336px slots,
   3 of which fit in the visible host. THIS WAS THE "2-3 pages in
   portrait" BUG.

   Removing this block makes v227j the sole authority on wide-viewport
   layout. */
/* v227k11-isSpread-refactor · the chrome .ed-bar adds ~75px on every
   viewport, but only single-page (data-spread="off") mode needs the
   host height to subtract it. Spread mode lays the 2-page spread in
   the full remaining cqb, so the original `100dvh - hdr-h` is fine
   there. Was @media (max-width:999px), which missed 12.9" iPad Pro
   portrait (1024 wide, single-page mode, also needs the -75 subtraction).

   The :not([data-spread]) branch covers the brief window between mount
   and the first render — host has data-scroll set but not yet
   data-spread. Treat as single-page during that window so layout isn't
   broken at first paint. */
.mushaf-shell .mushaf-page-host[data-spread="off"][data-scroll="horizontal"],
.mushaf-shell .mushaf-page-host[data-scroll="horizontal"]:not([data-spread]) {
  /* v229s-dvh · was a hardcoded -75px for the chrome bar, but the
     v229c medallion bar measures ~87px — the 12px difference pushed the
     host (and the page bottom with it) below the viewport in portrait.
     Use the measured chrome height (set by chrome.js syncChromeHeight on
     the shell) with the same 87px fallback the sidebar rules use.
     !important: overrides the base host height at L1180 because this
     single-page branch must also subtract the chrome bar. */
  height: calc(100dvh - var(--hdr-h, 80px) - var(--mu-chrome-h, 87px)) !important;
}

@media (max-width: 999px) {
  /* Phones + iPad 11" portrait · sidebar hides, mushaf centers.
     These rules stay width-gated because they're about "viewport too
     narrow to fit a sidebar at all," not about spread vs single. */
  .mu-mz-attach { display: none !important; }
  .mushaf-shell .mushaf-page-host {
    justify-content: center !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   Hide memorize sidebar when on Progress (or any non-mushaf view).
   The sidebar lives inside the mushaf shell · should disappear when
   the user navigates to a different tab. body :has() is widely
   supported in all modern Safari / Chrome / Firefox builds.
   ═══════════════════════════════════════════════════════════════════ */
body:has(#view-progress.is-on)   .mu-mz-attach,
body:has(#view-progress.active)  .mu-mz-attach,
body:has(#view-priorities.is-on) .mu-mz-attach,
body:has(#view-priorities.active).mu-mz-attach,
body:has(#view-browse.is-on)     .mu-mz-attach,
body:has(#view-browse.active)    .mu-mz-attach {
  display: none !important;
}

/* v223 · Hide the SŪRAH X · MAKKĪ · N ĀYĀT context strip · the bar
   already names the surah and the page nav names the page. The strip
   was redundant and crowded the chrome. */
.ed-stamp { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════
   v196 · 2026-05-18 · Dual-page mushaf in landscape
   ───────────────────────────────────────────────────────────────────
   At viewport ≥ 1300 px (iPad Pro 13" landscape) the renderer fetches
   two pages and wraps each in .mushaf-spread-half. The frame becomes
   a flex row; each page sizes to ~half the host width with the spread
   centered in the page-host.

   Right side (RTL: read first) = lower page number.
   Left side = higher page number.
   Page 1 and page 604 render solo (no pair).
   ═══════════════════════════════════════════════════════════════════ */
.mushaf-frame.is-dual {
  display: flex !important;
  flex-direction: row !important;
  align-items: center;
  justify-content: center;
  gap: 0;
  /* Spread gets one shared shadow rather than per-page. */
  filter: drop-shadow(0 24px 64px rgba(0,0,0,.55))
          drop-shadow(0 6px 18px rgba(0,0,0,.35));
}
.mushaf-frame.is-dual .mushaf-spread-half {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Spine groove between the two pages */
.mushaf-frame.is-dual .mushaf-spread-left::after,
.mushaf-frame.is-dual .mushaf-spread-right::before {
  content: "";
  position: absolute;
  top: 4%; bottom: 4%;
  width: 4px;
  background: linear-gradient(180deg, transparent, rgba(0,0,0,.40) 18%, rgba(0,0,0,.40) 82%, transparent);
  pointer-events: none;
  z-index: 2;
}
.mushaf-frame.is-dual .mushaf-spread-left::after  { right: -2px; }
.mushaf-frame.is-dual .mushaf-spread-right::before { left: -2px; }

/* Each page in dual mode sizes to whichever binds first:
   half the host inline-size minus a tiny gutter, or the host height
   converted to width via the 600:917 page aspect ratio. v198 · drop the
   former 520 px cap so pages can grow to fill the available vertical
   space when the host is tall enough. */
.mushaf-frame.is-dual .mushaf-page {
  width: min(calc((100cqi - 16px) / 2), calc(100cqb * 600 / 917)) !important;
  max-width: none !important;
  max-height: 100cqb !important;
}
/* Soften the page edges so they look like a single spread.
   Left page: round only its left + outer corners; inner edge butts the spine. */
.mushaf-frame.is-dual .mushaf-spread-left .mushaf-page {
  border-radius: 4px 0 0 4px;
  border-right-width: 0.5px;
}
.mushaf-frame.is-dual .mushaf-spread-right .mushaf-page {
  border-radius: 0 4px 4px 0;
  border-left-width: 0.5px;
}
/* Re-shape the V3 mode's box-shadow rule so the dual shadow above
   doesn't double up (filter drop-shadow + box-shadow stacking). */
.mushaf-shell .mushaf-frame.is-dual .mushaf-page {
  box-shadow: none !important;
}

/* v198 · 2026-05-18 · Dual-mode layout:
   · sidebar caps at 304 px (tighter than the 460 px landscape cap)
   · page-host reserves 304 px on the left so the spread starts flush
     against the sidebar (no dead gap)
   · spread sits centered in the remaining ~1062 px, pages fill the
     vertical space since the 520 cap is gone (see rule above) */
@media (min-width: 1300px) {
  .mu-mz-attach {
    max-width: 304px !important;
  }
  .mushaf-shell .mushaf-page-host:has(.mushaf-frame.is-dual) {
    justify-content: center !important;
    padding-left:  304px !important;
    padding-right: clamp(8px, 2vw, 24px) !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   v186 · V3 cards layout · 2026-05-18
   ───────────────────────────────────────────────────────────────────
   Permanent sidebar with stacked cards (Surah · Page · Modes · Utils)
   ABOVE the existing memorize body. Top bar is hidden; controls are
   delegated to via the cards in the sidebar.

   Built by modules/mushaf/v3-sidebar.js. When the shell carries
   `.is-v3`, this stylesheet:
     · hides .ed-row (the old top bar)
     · forces .mu-mz-attach visible (always-on sidebar)
     · forces the sidebar wide enough to host the cards
     · paints the cards
   ═══════════════════════════════════════════════════════════════════ */

/* Hide the top bar entirely in V3 mode */
.mushaf-shell.is-v3 .ed-row { display: none !important; }
/* Hide the legacy edition stamp + verse-jump FAB in V3 too */
.mushaf-shell.is-v3 .ed-stamp,
.mushaf-shell.is-v3 .ed-peek-v3,
.mushaf-shell.is-v3 .float-jump { display: none !important; }

/* Always-on sidebar in V3 mode · pinned to viewport-LEFT in every
   orientation (positionMemoAttach in chrome.js reads .is-v3 and uses
   its portrait-flip branch to set inline left/width). */
.mushaf-shell.is-v3 .mu-mz-attach {
  display: flex !important;
  flex-direction: column !important;
  min-width: 304px !important;
  z-index: 5;
}
/* v188 · 2026-05-18 · The chrome host class is .mushaf-page-host, not
   .mushaf-host (which is a different surface used elsewhere). Target
   the right element so flush-right + bordered-page actually applies. */
.mushaf-shell.is-v3 .mushaf-page-host {
  /* Push mushaf to the right edge so the V3 sidebar sits on the left. */
  justify-content: flex-end !important;
  /* Breathing room above + below the page so it reads as a discrete
     sheet of paper rather than bleeding to the viewport edges. The
     page itself uses 100cqb internally — reducing host height via
     padding shrinks the page proportionally so its bottom edge
     becomes clearly visible. */
  padding-top:    clamp(20px, 4vh, 56px) !important;
  padding-bottom: clamp(20px, 4vh, 56px) !important;
  /* Side padding · keep page off the dark frame on the right; the
     sidebar occupies the left side so no left padding is needed. */
  padding-left:  clamp(8px, 2vw, 40px) !important;
  padding-right: clamp(16px, 3vw, 48px) !important;
}
/* Make the page borders more visible so it reads as a bounded sheet. */
.mushaf-shell.is-v3 .mushaf-page {
  border-width: 1.5px;
  box-shadow:
    0 2px 0 var(--ms-rim, rgba(255,255,255,.04)),
    0 32px 80px -20px rgba(0, 0, 0, 0.55),
    0 8px 24px -8px rgba(0, 0, 0, 0.40);
}
/* Override the smaller portrait media query that hid the sidebar
   below 1000 px width — V3 needs it visible everywhere. */
@media (orientation: portrait) and (max-width: 999px) {
  .mushaf-shell.is-v3 .mu-mz-attach { display: flex !important; }
}

/* The cards container takes the top region of the sidebar; the memo
   body takes the remaining height. */
.mushaf-shell.is-v3 .mu-mz-attach .mu-mz-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.mu-v3-cards {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 12px 8px;
  flex-shrink: 0;
}
.mu-v3-card {
  background: var(--bg-2, #141a1f);
  border: 1px solid var(--hair, rgba(232,228,216,.10));
  border-radius: 10px;
  padding: 12px 14px;
  color: var(--ink-0, #e9e4d6);
  font-family: Manrope, sans-serif;
  -webkit-tap-highlight-color: transparent;
}
button.mu-v3-card {
  text-align: left;
  cursor: pointer;
  width: 100%;
  font: inherit;
  color: inherit;
}
button.mu-v3-card:hover {
  border-color: var(--hair-2, rgba(232,228,216,.18));
}

/* ── Surah card ── */
.mu-v3-surah {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 1px;
  padding: 12px;
}
.mu-v3-surah .mu-v3-surah-tr {
  font: 700 17px Manrope, sans-serif;
  letter-spacing: .01em;
  color: var(--gilt, #d5b157);
}
.mu-v3-surah .mu-v3-surah-ar {
  font: 500 15px "Amiri Quran", "Amiri", serif;
  color: var(--ink-1, #c6c0b0);
  margin-top: 1px;
}
.mu-v3-surah .mu-v3-surah-meta {
  font: 500 9.5px Manrope, sans-serif;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-3, #5a5447);
  margin-top: 5px;
}

/* ── Page card ── */
.mu-v3-page {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 8px;
}
.mu-v3-arrow {
  font: 200 22px Manrope, sans-serif;
  color: var(--ink-1, #c6c0b0);
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px 12px;
  border-radius: 6px;
  line-height: 1;
  min-width: 44px; min-height: 36px;
}
.mu-v3-arrow:hover { color: var(--gilt, #d5b157); background: var(--bg-3, #1a2128); }
.mu-v3-page-body {
  background: none; border: none; cursor: pointer;
  font: 600 20px Manrope, sans-serif;
  color: var(--ink-0, #e9e4d6);
  padding: 4px 8px; border-radius: 6px;
  flex: 1; text-align: center;
}
.mu-v3-page-body:hover { background: var(--bg-3, #1a2128); }
.mu-v3-page-body .mu-v3-pg-of {
  font: 400 11px Manrope, sans-serif;
  color: var(--ink-3, #5a5447);
  margin-left: 2px;
}

/* ── Modes card ── */
.mu-v3-modes {
  display: flex;
  gap: 5px;
  justify-content: space-between;
  padding: 8px 8px;
}
.mu-v3-pill {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 7px 6px;
  font: 600 10px Manrope, sans-serif;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-1, #c6c0b0);
  border: 1px solid var(--hair-2, rgba(232,228,216,.18));
  border-radius: 999px;
  background: var(--bg-3, #1a2128);
  cursor: pointer;
  min-height: 30px;
}
.mu-v3-pill .mu-v3-dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--ink-3, #5a5447);
  flex-shrink: 0;
}
.mu-v3-pill.is-on {
  color: var(--gilt, #d5b157);
  border-color: rgba(213, 177, 87, .45);
  background: rgba(213, 177, 87, .08);
}
.mu-v3-pill.is-on .mu-v3-dot { background: var(--gilt, #d5b157); }
.mu-v3-pill-num {
  background: var(--gilt, #d5b157);
  color: #0f1418;
  font: 800 8.5px Manrope, sans-serif;
  padding: 1px 5px;
  border-radius: 99px;
  margin-left: 2px;
}

/* ── Utils card ── */
.mu-v3-utils {
  display: flex;
  gap: 6px;
  padding: 8px 8px;
}
.mu-v3-icon-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  border: 1px solid var(--hair, rgba(232,228,216,.10));
  background: var(--bg-3, #1a2128);
  border-radius: 7px;
  color: var(--ink-1, #c6c0b0);
  font: 500 15px Manrope, sans-serif;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.mu-v3-icon-btn:hover { border-color: var(--gilt, #d5b157); color: var(--gilt, #d5b157); }
.mu-v3-icon-btn svg { width: 17px; height: 17px; }

/* ── Narrow viewport adjustments ── */
@media (max-width: 720px) {
  .mu-v3-cards { padding: 10px 10px 6px; gap: 6px; }
  .mu-v3-card { padding: 10px 12px; }
  .mu-v3-surah { padding: 10px; }
  .mu-v3-pill { font-size: 9.5px; padding: 6px 4px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   v229c-medallion · Calligraphic page-navigator
   ─────────────────────────────────────────────────────────────────────
   Replaces the original `‹ 47 / 604 ›` chevron+counter layout. Big serif
   page number flanked by gold filigree gradients, with the surah-line
   ("al-Aʿrāf · الأعراف · juz' 9") rendered below. Lives in the center
   column of .ed-row; the left column is now empty.
   ═══════════════════════════════════════════════════════════════════════ */
.ed-medallion {
  display: inline-flex !important;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
}
/* v229i-flatten · medallion now has only one child (medalRow) — the
   surah-line moved to the left column. Drop the extra padding so the
   chrome bar is as skinny as possible. */
.ed-medallion:not(:has(.ed-medal-surah-line)) {
  padding: 2px 12px;
  gap: 0;
}
/* Surah info in the LEFT column. Was centered + below the medallion.
   Now pinned left, single-line, no center alignment. */
.mushaf-chrome .ed-row-left .ed-medal-surah-line {
  display: flex !important;
  align-items: center;
  justify-self: flex-start;
}
.mushaf-chrome .ed-row-left .ed-medal-surah {
  padding: 8px 12px !important;
  border-radius: 8px !important;
}
.mushaf-chrome .ed-row-left .ed-medal-surah:hover {
  background: rgba(213,177,87,.10) !important;
}
/* v229c-medallion · override the earlier `.ed-row .ed-row-center{order:1;
   justify-self:flex-start; grid-column:1}` swap at line 2284 so the
   medallion sits properly centered in column 2 (the middle of the row).
   The empty .ed-row-left at column 2 collapses out of the way. */
.mushaf-chrome .ed-row .ed-row-center.ed-medallion {
  grid-column: 2 !important;
  order: 2 !important;
  justify-self: center !important;
}
.mushaf-chrome .ed-row .ed-row-left:empty {
  display: none !important;
}
/* v229i-flatten · ed-row-left now has the surah-line (no longer empty),
   so move it to column 1 (real left) and right column to column 3.
   Overrides the legacy column-swap at line 2284. */
.mushaf-chrome .ed-row .ed-row-left:not(:empty) {
  grid-column: 1 !important;
  order: 1 !important;
  justify-self: flex-start !important;
}
.mushaf-chrome .ed-row .ed-row-right {
  grid-column: 3 !important;
  order: 3 !important;
  justify-self: flex-end !important;
}

/* Row 1 · arrows + page-number centerpiece. */
.ed-medal-row {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* The arrows · big circular hit area with a radial gold glow on hover.
   .ed-arrow already gives them 44×44 + center-aligned glyph via the
   v229-tap-* fix; we just add the medallion's visual treatment. */
.ed-medal-arrow {
  width: 52px !important; height: 52px !important;
  font: 300 28px/1 "Amiri", "Manrope", serif !important;
  color: var(--ms-gold, #d5b157) !important;
  background: radial-gradient(circle at center, rgba(213,177,87,.16) 0%, transparent 70%) !important;
  border-radius: 50% !important;
  transition: transform .18s ease, color .14s !important;
  border: 0 !important;
}
.ed-medal-arrow:hover {
  transform: scale(1.1);
  color: var(--ms-ink, #fff) !important;
  background: radial-gradient(circle at center, rgba(213,177,87,.32) 0%, transparent 75%) !important;
}
.ed-medal-arrow:disabled {
  opacity: 0.28 !important;
  transform: none !important;
  background: transparent !important;
  cursor: not-allowed !important;
}

/* The page-number centerpiece · flanked by gold filigree gradient lines.
   Uses ::before / ::after to draw 30px gradient streaks on either side
   of the page number. Radial background tint pulls focus to the digits. */
.ed-medal-center {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  padding: 4px 18px;
  min-width: 130px;
  background: radial-gradient(ellipse at center,
              rgba(213,177,87,.16) 0%,
              transparent 70%);
}
.ed-medal-center::before,
.ed-medal-center::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 28px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--ms-gold, #d5b157), transparent);
}
.ed-medal-center::before { left: -10px; }
.ed-medal-center::after { right: -10px; }

/* The big serif page number — the visual hero of the medallion. */
.ed-medal-page {
  font: 300 36px/1 "Amiri", "Cormorant Garamond", serif !important;
  color: var(--ms-gold, #d5b157) !important;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  background: transparent !important;
  border: 0 !important;
  padding: 0 !important;
  min-height: 44px !important;
  display: inline-flex !important;
  align-items: center;
}
.ed-medal-page:hover {
  background: transparent !important;
  color: var(--ms-mint, #5fb594) !important;
  cursor: pointer;
}
.ed-medal-page.is-flipping {
  opacity: 0.4;
  transform: translateY(-2px);
  transition: opacity .12s, transform .12s;
}
.ed-medal-of {
  font: 600 11px/1 "Manrope", system-ui, sans-serif !important;
  color: var(--ms-ink-3, rgba(232,228,216,.42)) !important;
  letter-spacing: 0.06em;
  margin-left: 8px;
  text-transform: uppercase;
}

/* Row 2 · surah line · calligraphic dots separating tr / Arabic / juz. */
.ed-medal-surah-line {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.ed-medal-surah {
  /* Wider, looser version of .ed-surah; keeps it a tap target. */
  display: inline-flex !important;
  align-items: center;
  gap: 8px;
  padding: 6px 14px !important;
  /* v229s-h3 · was 36px; H3 44px tap minimum. !important matches the
     rest of this medallion block (it overrides the base .ed-surah set). */
  min-height: 44px !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: 99px !important;
  cursor: pointer;
  font: 400 13px/1.3 "Amiri", "Cormorant Garamond", serif !important;
  color: var(--ms-ink-2, rgba(232,228,216,.78)) !important;
  transition: background .14s, color .14s;
}
.ed-medal-surah:hover {
  background: rgba(213,177,87,.10) !important;
  color: var(--ms-gold, #d5b157) !important;
}
.ed-medal-surah .ed-surah-tr {
  font: 500 13px/1 "Manrope", system-ui, sans-serif !important;
  letter-spacing: 0.04em;
  color: inherit !important;
  text-transform: none !important;
}
.ed-medal-surah .ed-surah-ar {
  font: 400 17px/1 "Amiri", serif !important;
  color: var(--ms-gold, #d5b157) !important;
  direction: rtl;
}
.ed-medal-dot {
  width: 4px; height: 4px;
  background: var(--ms-gold, #d5b157);
  border-radius: 50%;
  opacity: 0.5;
  display: inline-block;
}
.ed-medal-juz {
  font: 500 10.5px/1 "Manrope", system-ui, sans-serif !important;
  color: var(--ms-ink-3, rgba(232,228,216,.42)) !important;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Hide the now-redundant top stamp row (medallion includes juz/ḥizb info). */
.mushaf-chrome .ed-stamp { display: none !important; }

/* The empty left column collapses so the medallion can center properly. */
.mushaf-chrome .ed-row-left:empty { width: 0; padding: 0; }

/* Narrow viewports — shrink the medallion so it fits the chrome bar. */
@media (max-width: 640px) {
  .ed-medal-page { font-size: 28px !important; }
  .ed-medal-arrow { width: 44px !important; height: 44px !important; font-size: 22px !important; }
  .ed-medal-center { min-width: 90px; padding: 4px 12px; }
  .ed-medal-center::before, .ed-medal-center::after { width: 16px; }
  .ed-medal-surah .ed-surah-ar { font-size: 15px !important; }
  .ed-medal-surah .ed-surah-tr { font-size: 11.5px !important; }
  .ed-medal-juz, .ed-medal-juz-sep { display: none; }
}



/* ═══ v230b · ABSOLUTE full-book ribbon ════════════════════════════════
   v230 sized slots as flex items, so sliding the window meant shifting
   every existing slot and compensating scrollLeft — and iOS ignores
   programmatic scrollLeft writes during native pan momentum, so forward
   reading (next page = LEFT end of the descending DOM) jumped a page-
   width whenever the window extended mid-gesture. Now the scroller is
   the full book wide and every slot sits at its absolute position
   (--mu-ribbon-i = 604 - page). Mounting/unmounting slots moves NOTHING
   and needs no compensation, on any engine, mid-gesture or not. */
.mushaf-host[data-scroll="horizontal"] {
  --mu-slot-w: min(720px, 100cqi, calc(100cqb * 600 / 917));
}
.mushaf-host[data-scroll="horizontal"][data-spread="on"] {
  --mu-slot-w: 50cqi;   /* matches the legacy spread slot width */
}
.mushaf-host[data-scroll="horizontal"] .mushaf-scroller {
  position: relative;
  width: calc(604 * var(--mu-slot-w)) !important; /* overrides max-content; the ribbon IS the book */
  height: 100%;
}
.mushaf-host[data-scroll="horizontal"] .mushaf-page-slot {
  position: absolute;
  top: 0; bottom: 0;
  width: var(--mu-slot-w);
  left: calc(var(--mu-ribbon-i, 0) * var(--mu-slot-w));
  flex: none;
  contain-intrinsic-size: auto 100cqb;
}
/* single mode: center snap + hairline divider (neighbor peek) */
.mushaf-host[data-scroll="horizontal"][data-spread="off"] .mushaf-page-slot,
.mushaf-host[data-scroll="horizontal"]:not([data-spread]) .mushaf-page-slot {
  scroll-snap-align: center;
  padding: clamp(4px, 1cqb, 10px) 0;
  border-inline-start: 1px solid var(--ms-rim, rgba(232,228,216,.14));
}
/* blur-quiz integrity: with any blur mode active, the neighbor peek
   would leak unblurred answers at the page edges — veil non-center
   slots while a blur mode is on. */
.mushaf-host[data-scroll="horizontal"][data-mushaf-blur]:not([data-mushaf-blur=""]) .mushaf-page-slot:not(.is-center) {
  opacity: .12;
}
