  *{box-sizing:border-box}
  /* Themed background on <html> itself (not just body) so any moment where body is dimmed/
     transparent during the leave-transition reveals the app's own background instead of the
     browser's stark-white default canvas. */
  html{ background:var(--bg); }
  html,body{height:100%; overscroll-behavior:none;}
  /* Native-app feel: fade+rise the page in on load (pure CSS, no JS dependency so the
     page is never left invisible), and fade it out just before an internal nav (see
     the "mm-leaving" script below) instead of the white MPA-reload flash that reads
     as "this is a website". This is the FALLBACK path: on Chromium ≥126 / Safari ≥18.2
     the html.mm-vt rules below switch it all off and real cross-document view transitions
     take over. (Speculation rules remain disabled — see the comment above.) */
  /* transform:none on the 'to' keyframe was NOT enough on its own: while a CSS animation
     targeting `transform` is in its fill-mode "after phase" (forwards/both), Chromium keeps
     reporting getComputedStyle().transform as a resolved matrix (confirmed via Playwright:
     matrix(1,0,0,1,0,0), NOT the string "none"), and any non-"none" computed transform value —
     even the identity matrix — still establishes a new containing block for position:fixed/
     sticky descendants. That's why .tabbar/.appbar kept drifting even after that first fix.
     The real fix: drop 'both' from the animation below entirely (see body{} rule) so once the
     .32s animation's active period ends, the effect stops applying altogether and the property
     reverts to its true cascaded value (initial: transform:none, opacity:1 — identical to what
     the 'to' keyframe already showed, so there's no visible jump). */
  @keyframes mmPageIn{ from{opacity:0; transform:translateY(6px);} to{opacity:1; transform:none;} }
  /* Was opacity:0 — fading the outgoing page fully invisible before the real (unavoidable)
     MPA navigation just added ~110ms of manufactured blank/white time on top of the browser's
     own load gap. Dimming instead of hiding keeps the old screen's content on-screen right up
     until the real navigation cuts over, so only the genuine network+render gap is ever blank. */
  html.mm-leaving body{ opacity:.55; transform:translateY(-4px) scale(.99); pointer-events:none; }

  /* ── View-transition mode (html.mm-vt — set by the head script above) ──────────────
     VT and the hand-rolled fade above are mutually exclusive, not additive:
      · mm-leaving's dim would be captured INTO the outgoing snapshot (cross-doc VT snapshots
        at navigation start, i.e. after the dim painted), so the transition would cross-fade
        FROM a dimmed, shrunk page — visibly worse than shipping nothing.
      · mmPageIn is mid-flight at opacity:0 when the incoming snapshot is taken around
        pagereveal, so VT would capture a blank frame and then double-animate.
     Hence: under mm-vt both are switched off and the browser owns the transition. */
  html.mm-vt body{ animation:none; }
  /* Neutralise the DIM (a view transition must not cross-fade from a faded page) but KEEP
     pointer-events:none — that is the double-tap guard on an uncached, always-round-tripping
     /checkout, not decoration. pointer-events has no effect on the transition itself. */
  html.mm-vt.mm-leaving body{ opacity:1; transform:none; pointer-events:none; }
  /* The payoff. Naming the fixed chrome captures it as its own snapshot pair instead of
     folding it into the root snapshot, so the appbar and tabbar PERSIST across the
     navigation and only the content cross-fades — which is exactly what a native app does.
     Without this, VT is just a whole-page cross-fade that makes the chrome flicker on every
     tap: strictly not worth the regression risk of turning VT back on. */
  html.mm-vt .appbar{ view-transition-name:mm-appbar; }
  html.mm-vt .tabbar{ view-transition-name:mm-tabbar; }
  @media (prefers-reduced-motion: reduce){ body{ animation:none !important; transition:none !important; } }
  body{
    margin:0;
    font-family:Inter,system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
    color:var(--text);
    background:
      radial-gradient(1200px 420px at 50% -220px, color-mix(in oklab, var(--mm-primary) 12%, transparent) 0%, transparent 60%),
      var(--bg);
    animation:mmPageIn .32s cubic-bezier(.22,.61,.36,1);
    transition:opacity .12s ease, transform .12s ease;
    -webkit-tap-highlight-color: transparent;
    /* Native feel: block long-press link preview / "open in browser" URL + text selection
       so the customer never sees the underlying webapp address. */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    /* Native feel: no pinch/double-tap zoom (iOS Safari ignores user-scalable; this covers it). */
    touch-action: pan-x pan-y;
  }
  /* Re-enable selection + callout only where typing/copy is genuinely needed. */
  input, textarea, select, [contenteditable], .selectable{
    -webkit-touch-callout: default;
    -webkit-user-select: text;
    user-select: text;
  }
  a, button, .tabbar a, .drawer-link{ -webkit-touch-callout: none; }
  .container{max-width:var(--container); margin:0 auto; padding:0 16px;}
  .page{ padding:16px; padding-bottom:110px; }
  .footer{ margin:26px 0 96px; text-align:center; color:var(--muted); font-size:12px; }

  /* App Bar — iFis-style: hamburger | logo+name | bell */
  .appbar{ position:sticky; top:0; z-index:40; background:var(--mm-header-bg); color:var(--mm-header-text); border-bottom:1px solid var(--ring);
           box-shadow:var(--shadow); backdrop-filter:saturate(120%) blur(6px); padding-top:var(--safe-top); }
  .appbar .row .left-icon, .appbar .row .right-icon{ color:var(--mm-header-text); }
  .appbar .title{ color:var(--mm-header-text); }
  .appbar .row{ display:flex; align-items:center; gap:4px; padding:8px 0; min-height:56px; }
  .appbar .row .left-icon, .appbar .row .right-icon{ min-width:48px; min-height:48px; width:48px; height:48px; display:flex; align-items:center; justify-content:center; flex-shrink:0; border:0; background:0; cursor:pointer; color:var(--text); padding:0; -webkit-tap-highlight-color:transparent; }
  .appbar .row .right-icon{ margin-left:auto; }
  .appbar .row .right-icon ~ .right-icon{ margin-left:0; }
  #appbarVoiceGuide .vg-icon-off{ display:none; }
  #appbarVoiceGuide.vg-muted .vg-icon-on{ display:none; }
  #appbarVoiceGuide.vg-muted .vg-icon-off{ display:block; }
  /* Speaking indicator: pulses the appbar voice-guide icon while a clip is
     actually playing, so a sighted-but-illiterate customer can tell "this is
     reading to me right now" without needing to read anything. */
  #appbarVoiceGuide.vg-speaking{ color:var(--accent, #2563eb); animation:vgSpeakPulse 1s ease-in-out infinite; }
  @keyframes vgSpeakPulse{ 0%,100%{ transform:scale(1); opacity:1; } 50%{ transform:scale(1.15); opacity:.75; } }
  @media (prefers-reduced-motion: reduce){ #appbarVoiceGuide.vg-speaking{ animation:none; opacity:.75; } }
  body.pf-page .appbar .row .left-icon, body.pf-page .appbar .row .right-icon,
  body.hist-page .appbar .row .left-icon, body.hist-page .appbar .row .right-icon,
  body.rukumo-page .appbar .row .left-icon, body.rukumo-page .appbar .row .right-icon{ color:#fff; }
  .brand{ display:flex; align-items:center; gap:10px; min-width:0; flex:1; justify-content:center; }
  .brand img{ width:36px; height:36px; object-fit:cover; border-radius:11px; background:#fff; border:1px solid var(--ring); }
  body.pf-page .appbar .brand img, body.hist-page .appbar .brand img, body.rukumo-page .appbar .brand img{ border-color:rgba(255,255,255,.5); }
  .title{ font-weight:800; letter-spacing:.2px; font-size:16px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

  /* Bottom Tab Bar — large touch targets (min 44px) */
  .tabbar{ position:fixed; left:0; right:0; bottom:0; z-index:50; background:var(--mm-nav-bg); border-top:1px solid var(--ring);
           box-shadow:0 -10px 30px rgba(2,6,23,.06); padding-top:8px; padding-bottom:calc(8px + var(--safe-bottom)); }
  .tabbar .wrap{ display:grid; grid-template-columns:1fr 1fr auto 1fr 1fr; max-width:var(--container); margin:0 auto; min-height:64px; align-items:stretch; gap:0; padding:0 4px; }
  .tabbar a{ position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;
             text-decoration:none; color:var(--mm-nav-inactive); font-size:11px; font-weight:800; padding:12px 8px; min-height:56px; min-width:44px;
             -webkit-tap-highlight-color:transparent; box-sizing:border-box; }
  .tabbar a .ico{ width:26px; height:26px; display:inline-flex; flex-shrink:0; }
  .tabbar a.active{ color:var(--mm-nav-active); }
  .tabbar a.active .dot{ position:absolute; top:10px; left:50%; transform:translateX(-50%); width:48px; height:36px; background:var(--tint); border-radius:12px; z-index:-1; }
  .tabbar a:active{ transform:scale(0.97); opacity:0.9; }
  .tabbar .tabbar-quicksend{ width:56px; height:56px; min-width:56px; min-height:56px; border-radius:50%; background:var(--mm-nav-active); color:#fff; display:flex; align-items:center; justify-content:center; box-shadow:0 4px 14px rgba(0,0,0,.25); text-decoration:none; flex-shrink:0; margin:-16px auto 0; padding:0; }
  @media (hover:hover){
    .tabbar .tabbar-quicksend:hover{ opacity:.95; color:#fff; }
  }
  .tabbar .tabbar-quicksend .ico{ width:28px; height:28px; }
  .tabbar .tabbar-quicksend.active-fav{ box-shadow:0 4px 18px color-mix(in oklab, #ef4444 45%, transparent); background:linear-gradient(180deg,#ef4444,#dc2626); }

  /* App bar drawer (hamburger menu) */
  .appbar-drawer{ position:fixed; inset:0; z-index:60; visibility:hidden; opacity:0; transition:visibility .25s, opacity .25s; }
  .appbar-drawer.open{ visibility:visible; opacity:1; }
  .appbar-drawer-back{ position:absolute; inset:0; background:rgba(0,0,0,.4); }
  .appbar-drawer-panel{ position:absolute; left:0; top:0; bottom:0; width:min(300px,85vw); background:var(--surface); box-shadow:4px 0 24px rgba(0,0,0,.15); display:flex; flex-direction:column; transform:translateX(-100%); transition:transform .25s ease; }
  .appbar-drawer.open .appbar-drawer-panel{ transform:translateX(0); }
  .appbar-drawer-panel .drawer-head{ padding:16px; border-bottom:1px solid var(--ring); font-weight:800; font-size:16px; }
  .appbar-drawer-panel a{ display:flex; align-items:center; gap:12px; padding:16px 20px; min-height:52px; color:var(--text); text-decoration:none; font-weight:600; border:0; border-bottom:1px solid var(--ring); background:0; width:100%; cursor:pointer; font-size:15px; -webkit-tap-highlight-color:transparent; box-sizing:border-box; }
  @media (hover:hover){
    .appbar-drawer-panel a:hover{ background:var(--surface-2); }
  }
  .appbar-drawer-panel a:active{ background:var(--surface-2); }
  .appbar-drawer-panel a .ico{ width:24px; height:24px; opacity:.8; flex-shrink:0; }
  .toast-msg{ position:fixed; bottom:90px; left:50%; transform:translateX(-50%) translateY(80px); background:var(--text); color:var(--surface); padding:12px 20px; border-radius:12px; font-size:14px; font-weight:600; z-index:70; opacity:0; transition:transform .25s, opacity .25s; box-shadow:0 8px 24px rgba(0,0,0,.3); }
  .toast-msg.show{ transform:translateX(-50%) translateY(0); opacity:1; }

  /* Tactile press feedback on chrome that didn't already have its own :active rule
     (tabbar/paychip/buttons elsewhere define their own). Native apps visibly "give"
     under a tap; a static icon reads as a web link. */
  .appbar .left-icon:active, .appbar .right-icon:active, .drawer-link:active{
    transform:scale(.92); transition:transform .08s ease;
  }

  /* ── Shared native bottom sheet (every dialog/modal app-wide should use this, not a
     plain centered popup or the native <dialog> element): slides up from the bottom
     edge with a drag handle on mobile, centered pop-in on desktop. Originally built for
     checkout.php's USSD/Rukumo modals; lifted here 2026-07-04 so every page gets it. ── */
  .modal-backdrop{
    position:fixed; inset:0; background:rgba(15,23,42,.5); backdrop-filter:blur(4px);
    display:none; align-items:center; justify-content:center; z-index:80; padding:16px;
  }
  @keyframes mmBackdropIn{ from{opacity:0} to{opacity:1} }
  @keyframes mmSheetIn{ from{transform:translateY(100%)} to{transform:translateY(0)} }
  @keyframes mmCardIn{ from{opacity:0; transform:scale(.94) translateY(10px);} to{opacity:1; transform:scale(1) translateY(0);} }
  .modal-backdrop.show{ display:flex; animation:mmBackdropIn .18s ease both; }
  .modal-card{
    background:var(--surface); color:var(--text); border-radius:20px;
    border:1px solid var(--ring); box-shadow:0 24px 60px rgba(0,0,0,.25);
    max-width:420px; width:100%; overflow:hidden;
  }
  .modal-head{
    padding:16px 18px; border-bottom:1px solid var(--ring);
    display:flex; align-items:center; justify-content:space-between; gap:10px;
  }
  .modal-title{ font-weight:900; font-size:1rem; margin:0; }
  .modal-close{ border:0; background:transparent; font-size:22px; line-height:1; cursor:pointer; color:var(--muted); }
  .modal-body{ padding:16px 18px; font-size:.875rem; line-height:1.55; }
  .modal-body .small{ font-size:.75rem; color:var(--muted); margin-top:8px; }
  .modal-foot{
    padding:14px 18px; border-top:1px solid var(--ring);
    display:flex; gap:10px; justify-content:flex-end; flex-wrap:wrap;
  }
  .modal-foot .btn{ margin:0; }
  @media (max-width: 859px){
    .modal-backdrop{ align-items:flex-end; padding:0; }
    .modal-backdrop .modal-card{
      max-width:100%; width:100%; border-radius:22px 22px 0 0;
      padding-bottom:env(safe-area-inset-bottom, 0px);
    }
    .modal-backdrop.show .modal-card{ animation:mmSheetIn .32s cubic-bezier(.22,.61,.36,1) both; }
    .modal-backdrop .modal-card::before{
      content:''; display:block; width:40px; height:5px; border-radius:999px;
      background:var(--ring); margin:10px auto 2px;
    }
  }
  @media (min-width: 860px){
    .modal-backdrop.show .modal-card{ animation:mmCardIn .22s cubic-bezier(.22,.61,.36,1) both; }
  }

  /* ── Shared staggered-entrance utility (generalized from checkout.php's coRise this
     session) — apply the class + an inline animation-delay per rendered item so card
     grids rise in instead of popping in flat. ── */
  @keyframes mmRise{ from{opacity:0; transform:translateY(14px);} to{opacity:1; transform:translateY(0);} }
  .mm-rise{ animation:mmRise .38s cubic-bezier(.22,.61,.36,1) both; }

  /* ── Shared loading-skeleton utility (generalized from favorites.php's pulse divs and
     complaints.php/referrals.php's shimmer this round) — drop a `.mm-skel` placeholder
     block in while a screen's data is still fetching instead of leaving it blank. Uses
     theme vars so it reads right in dark mode too, unlike the two prior ad-hoc versions. ── */
  @keyframes mmShimmer{ 0%{background-position:200% 0} 100%{background-position:-200% 0} }
  .mm-skel{
    background:linear-gradient(90deg, var(--surface-2), var(--ring), var(--surface-2));
    background-size:200% 100%; animation:mmShimmer 1.2s ease-in-out infinite;
    border-radius:12px; color:transparent!important;
  }

  /* ── Shared confirm sheet (window.mmConfirm) — replaces raw browser confirm()/alert()
     app-wide with an in-app bottom sheet, self-styled so it looks right even on pages
     that don't define their own .btn classes. ── */
  .mm-confirm-btn{ border:0; border-radius:12px; padding:12px 18px; font-weight:800; cursor:pointer; font-size:.875rem; font-family:inherit; }
  .mm-confirm-btn.cancel{ background:var(--surface-2); color:var(--text); border:1.5px solid var(--ring); }
  .mm-confirm-btn.ok{ background:var(--mm-primary); color:#fff; }
  .mm-confirm-btn.ok.danger{ background:#dc2626; }

  /* Keyboard open state (see the shared script below) intentionally does NOT move or hide
     .tabbar/.appbar — the bottom nav stays fixed in place; the on-screen keyboard is left to
     overlay it naturally instead of animating chrome around. Only the focused field itself
     gets scrolled into view. */

  /* ── Shared top loading bar — Android/Chrome-tab-style indeterminate progress shown on
     every fresh page load, so the moment right after a page's own bytes start arriving reads
     as "loading→settled" instead of just abruptly appearing. Faded out on a real readiness
     signal (DOMContentLoaded, see the script below), not a guessed fixed duration, so it can't
     visually mismatch a very fast or very slow load. Animates transform (GPU-accelerated),
     not width/left, same reasoning as the p-bar-fill/card-shine fixes elsewhere in the app. ── */
  .mm-loadbar{
    position:fixed; top:0; left:0; right:0; height:3px; z-index:100;
    background:var(--mm-primary); overflow:hidden; pointer-events:none;
  }
  .mm-loadbar::after{
    content:''; position:absolute; inset:0; width:40%;
    background:linear-gradient(90deg, transparent, color-mix(in oklab, var(--mm-primary) 40%, #fff), transparent);
    animation:mmLoadSweep 1s ease-in-out infinite;
  }
  @keyframes mmLoadSweep{ from{transform:translateX(-100%)} to{transform:translateX(350%)} }
  html.mm-loaded .mm-loadbar{ opacity:0; transition:opacity .25s ease; }
  @media (prefers-reduced-motion: reduce){
    .mm-loadbar::after{ animation:none; }
    .mm-loadbar{ opacity:0; }
  }

  /* ── Voice-guide splash: shown once per device (assets/js/voice-guide.js
     toggles it) while the ~600KB Somali audio set downloads into the Cache
     API, so playback never has to wait on the network afterward. Stays
     display:none until JS decides it's actually needed (first-ever visit
     with voice guide on) — never flashes for returning visitors. ── */
  .vg-splash{
    display:none; position:fixed; inset:0; z-index:200; background:var(--mm-splash);
    align-items:center; justify-content:center; flex-direction:column;
    opacity:1; transition:opacity .35s ease;
  }
  .vg-splash.vg-splash-hide{ opacity:0; pointer-events:none; }
  .vg-splash-inner{ display:flex; flex-direction:column; align-items:center; gap:16px; }
  .vg-splash-inner img{ width:88px; height:88px; border-radius:20px; object-fit:cover; box-shadow:0 8px 24px rgba(0,0,0,.2); }
  .vg-splash-spinner{ width:32px; height:32px; border-radius:50%; border:3px solid rgba(255,255,255,.35); border-top-color:#fff; animation:vgSpin .8s linear infinite; }
  .vg-splash-text{ color:#fff; font-size:.8125rem; font-weight:600; opacity:.9; text-align:center; padding:0 24px; }
  @keyframes vgSpin{ to{ transform:rotate(360deg); } }
  @media (prefers-reduced-motion: reduce){ .vg-splash-spinner{ animation:none; } }
