/* ============================================================================
   MobileSheet3Panel — shared mobile scaffold for UMI and Story presentation.
   Three vertical panels: top (focal media) / middle (scroll) / bottom (actions).
   ============================================================================ */

/* Design tokens — override via CSS var on specific mounts */
:root {
    --msp-z-base: 9000;
    --msp-z-over: 9500;
    --msp-bottom-height: 80px;
    --msp-top-flex: 5;       /* 50% of remaining space (after bottom) */
    --msp-middle-flex: 3.4;  /* 34% of remaining */
    --msp-radius: 18px;
    --msp-pill-radius: 999px;
    --msp-pill-bg: rgba(255, 255, 255, 0.10);
    --msp-pill-bg-active: rgba(255, 204, 0, 0.18);
    --msp-pill-border: rgba(255, 255, 255, 0.14);
    --msp-pill-border-active: rgba(255, 204, 0, 0.42);
    --msp-text: #f5f5f7;
    --msp-text-muted: rgba(255, 255, 255, 0.62);
    --msp-bg: #0a0a0c;
    --msp-bg-mid: #16161a;
    --msp-divider: rgba(255, 255, 255, 0.08);
    --msp-maximize-swap-duration: 180ms;
}

/* ── Root dialog ── */
.msp-root {
    position: fixed;
    inset: 0;
    z-index: var(--msp-z-base);
    background: transparent;
    display: flex;
    flex-direction: column;
    color: var(--msp-text);
    font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'SF Pro', system-ui, sans-serif;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
}
.msp-root.msp-over-story {
    z-index: var(--msp-z-over);
}

/* Backdrop — only visible when UMI is over Story */
.msp-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.62);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    pointer-events: none;
}
.msp-root.msp-over-story .msp-backdrop {
    pointer-events: auto;
}
.msp-root:not(.msp-over-story) .msp-backdrop {
    background: #000;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

/* ── Frame: holds the three panels ── */
.msp-frame {
    position: relative;
    z-index: 1;
    flex: 1;
    display: flex;
    flex-direction: column;
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    min-height: 0;
}

/* ── Top panel (focal media) ── */
.msp-top {
    position: relative;
    flex: var(--msp-top-flex) 1 0;
    min-height: 0;
    background: #000;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.msp-top-tappable {
    cursor: pointer;
}

/* UMI-over-Story gets rounded top corners to feel like a sheet.

   truing-pocket-stagecraft S2 (#2): this was `margin: 8vh 0 0` — a fixed 67px
   band on a 844px stage that nothing ever drew in, and which stacked ON TOP of
   `.msp-frame`'s own `padding-top: env(safe-area-inset-top)`, so the first pixel
   of content landed ~114px down. Two dead bands, one of them counted twice.

   Now the frame opens just below the status bar with a 10px reveal of the story
   behind it — enough to keep the sheet metaphor (this is a surface OVER a story,
   and it should read that way), and the safe-area inset is spent once, here,
   instead of twice. ~57px handed back to the media. Law I-1: unused area costs
   more on the small stage, not less. */
.msp-root.msp-over-story .msp-frame {
    margin: calc(env(safe-area-inset-top, 0px) + 10px) 0 0;
    padding-top: 0;
    background: var(--msp-bg-mid);
    border-top-left-radius: var(--msp-radius);
    border-top-right-radius: var(--msp-radius);
    overflow: hidden;
    box-shadow: 0 -18px 40px rgba(0, 0, 0, 0.45);
}
.msp-root.msp-over-story .msp-top {
    border-top-left-radius: var(--msp-radius);
    border-top-right-radius: var(--msp-radius);
}

/* Focal content: images, videos, canvases fill the panel */
.msp-focal-content {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.msp-focal-content > img,
.msp-focal-content > video,
.msp-focal-content > .mpl-focal img,
.msp-focal-content > .mpl-focal video {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
    display: block;
    transition: opacity var(--msp-maximize-swap-duration) ease;
}
.msp-focal-content iframe {
    width: 100%;
    height: 100%;
    border: none;
}

/* ── Floating top controls (Close + Maximize + Download) ── */
.msp-float-controls {
    position: absolute;
    top: calc(env(safe-area-inset-top) + 8px);
    left: 0;
    right: 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 12px;
    z-index: 5;
    pointer-events: none;
}
/* ⛔ THE INSET IS NOT OPTIONAL — A CLOSE BUTTON UNDER THE CLOCK IS A TRAP.
   (Joe, Mary walk 2026-08-29: "I can't get out of this screen because the x is
   over the time...")

   This rule overrode `top` with a bare 8px and threw the safe-area inset away.
   Measured on his iPhone while he was stuck: env(safe-area-inset-top) = 62px
   (Dynamic Island), so the close chip rendered at y=8 — beneath the status bar,
   where iOS owns the pixels and the tap. The viewport is `viewport-fit=cover`,
   which is exactly why every fixed surface MUST add the inset itself; the base
   rule above does, and this variant undid it.

   He opened his wife's portrait full-size from inside Fergi — over a story —
   so this variant applied, and the only way out of a full-screen photo became
   unreachable. Not cosmetic: it is a dead end on a family-facing surface.

   The over-story case still sits nearer the top than the docked case; it just
   no longer pretends the notch is not there. `max()` keeps it honest on a
   device with no inset at all (inset 0 → 8px, the original intent). */
.msp-root.msp-over-story .msp-float-controls {
    top: max(8px, env(safe-area-inset-top));
}
.msp-float-controls > * { pointer-events: auto; }
.msp-float-right {
    display: flex;
    gap: 8px;
    align-items: center;
}

/* ── Header row centre slot: the object's NAME ──
   truing-pocket-stagecraft S2 (#2). The row carried only controls; the media's
   generated label lived down in the middle panel, reading as body copy under the
   media instead of as the object's name.

   The stage-management law at chrome scale: this slot is positioned against the
   two button clusters, not against the stage. Both clusters are `flex: 0 0 auto`;
   this is `flex: 1 1 auto` with `min-width: 0`, so it can only ever occupy the
   space they do not want — at any viewport width, for any title length. It
   cannot step on them, which is why there is no hand-tuned max-width here. */
.msp-float-title {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 10px;
    /* The label is not a tap target — let taps fall through to the media beneath
       (the float row sets pointer-events:auto on every child by default). */
    pointer-events: none;
}

/* The title needs the same protection the buttons give themselves.
   (Joe, Mary walk 2026-08-30: "the header panel is stepping on my face, as it
   steps on all documents.")

   Every `.msp-float-btn` carries `rgba(0,0,0,0.55)` + backdrop blur, so the
   controls read against any image. The centre title slot arrived later
   (truing-pocket-stagecraft S2) with no backing at all — bare white text laid
   directly on the photograph, which on a portrait means laid across the
   subject's face, and on a light document page means laid across white.
   The same chip treatment, sized to the text rather than the slot so it never
   becomes a bar across the image. */
.msp-float-title > * {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 5px 12px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
}
.msp-float-btn {
    /* 44, not 40 (Greg G-156, FergiPhone 2026-10-24): Close / Maximize / Download over the photo measured 40 x 40,
       under the touch floor every other phone control now meets. */
    width: 44px;
    height: 44px;
    flex: 0 0 auto;   /* the row's centre title must not squeeze Close to 43 */
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    display: grid;
    place-items: center;
    border: none;
    cursor: pointer;
    font: inherit;
    text-decoration: none;
}
.msp-float-btn:active {
    transform: scale(0.94);
}
.msp-float-btn:focus-visible {
    outline: 2px solid #5ad1ff;
    outline-offset: 2px;
}
.msp-maximize-btn[aria-pressed="true"] {
    background: rgba(255, 204, 0, 0.85);
    color: #1a1a1f;
}

/* ── Middle panel (scrollable content) ── */
.msp-middle {
    flex: var(--msp-middle-flex) 1 0;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    background: var(--msp-bg-mid);
    padding: 14px 18px;
    border-top: 1px solid var(--msp-divider);
}
.msp-middle::-webkit-scrollbar { width: 3px; }
.msp-middle::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 3px; }

/* Story-mode middle panel hosts the carousel — no padding, carousel manages its own.
   Shrunk in nimble-harmonizing-compass S3 (v14.0.382) — the middle was being too
   tall (≈ 1/3.5 of sheet); shrunk to ~14vh in nimble-harmonizing-compass S3
   so the focal panel gets the lion's share. But with the bottom panel retired
   (display:none below), .msp-top's default `flex: 5 1 0` grew to ~86vh — far
   taller than most primary media's natural aspect. Landscape images
   letterboxed to ~220px inside an ~726px panel, leaving ~250px of pure black
   between the image and the 14vh carousel. That's the "large blank panel"
   handoff Issue 17 reported.

   Root cause fix (abundant-hopping-sifakis S2 D6, 2026-04-23): cap the top
   panel at a natural-aspect ceiling and let the middle panel reclaim the
   remainder. Plan default: supporting list rises to fill reclaimed space. */
.msp-root[data-msp-mode="story"] .msp-top {
    flex: 0 0 auto;
    height: min(55vh, calc(100vw * 1.1));  /* ceiling — supports up to ~10:11 portrait before it would need to grow */
    min-height: 280px;                     /* floor — preserves a usable focal area on tiny phones */
    max-height: 65vh;
}
.msp-root[data-msp-mode="story"] .msp-middle {
    padding: 0;
    background: #0e0e12;
    flex: 1 1 auto;       /* absorb whatever .msp-top does not claim */
    min-height: 140px;
    max-height: none;     /* the 140px ceiling was the carousel-clamp that forced the blank gap */
}

/* Story-mode bottom panel is retired — playback controls migrated to the
   Mobile Control Panel (tab bar) in nimble-harmonizing-compass S3. */
.msp-root[data-msp-mode="story"] .msp-bottom {
    display: none;
}

/* ── Bottom panel (actions / playback) ── */
.msp-bottom {
    flex: 0 0 var(--msp-bottom-height);
    min-height: var(--msp-bottom-height);
    background: var(--msp-bg);
    border-top: 1px solid var(--msp-divider);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0));
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.msp-bottom::-webkit-scrollbar { display: none; }

/* ── Pills (used in bottom panel for UMI actions) ── */
.msp-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 9px 14px;
    border-radius: var(--msp-pill-radius);
    background: var(--msp-pill-bg);
    color: var(--msp-text);
    font-size: 13px;
    font-weight: 500;
    border: 1px solid var(--msp-pill-border);
    white-space: nowrap;
    cursor: pointer;
    font-family: inherit;
    flex-shrink: 0;
    min-height: 38px;
}
.msp-pill:active {
    transform: scale(0.97);
    background: rgba(255, 255, 255, 0.16);
}
.msp-pill:focus-visible {
    outline: 2px solid #5ad1ff;
    outline-offset: 2px;
}
.msp-pill.msp-pill-active,
.msp-pill[aria-pressed="true"] {
    background: var(--msp-pill-bg-active);
    border-color: var(--msp-pill-border-active);
}
.msp-pill.msp-pill-primary {
    background: #ffcc00;
    color: #1a1a1f;
    border-color: #ffcc00;
}
.msp-pill.msp-pill-danger { color: #ff6b6b; }
.msp-pill .msp-pill-icon {
    width: 16px;
    height: 16px;
    display: inline-grid;
    place-items: center;
}
.msp-pill svg {
    width: 16px;
    height: 16px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Dead-code: .msp-story-control / .msp-story-scrub / .msp-story-play /
   .msp-story-scrub-fill styles lived here to skin the in-panel playback
   controls. Those controls migrated to the Mobile Control Panel in
   nimble-harmonizing-compass S3 (PresentationPlayer._wireMobileControlPanel),
   leaving these selectors orphaned. Removed in abundant-hopping-sifakis S2 D6
   (2026-04-23) as part of the Story Play blank-panel root-cause sweep.
   The only remaining reference is the scaffold preview mockup at
   prototype/tools/m3-mockups/scaffold-preview.html which doesn't load this
   stylesheet. */

/* ── Section labels in middle panel (UMI) ── */
.msp-section-label {
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55);
    margin: 0 0 6px;
    display: block;
}
.msp-section {
    margin-bottom: 18px;
    line-height: 1.45;
}
.msp-section p,
.msp-section li {
    font-size: 15px;
    color: rgba(255, 255, 255, 0.92);
    margin: 0 0 8px;
}
.msp-title {
    font-size: 18px;
    font-weight: 600;
    line-height: 1.25;
    color: #fff;
    margin-bottom: 2px;
}
.msp-subtitle {
    font-size: 13px;
    color: var(--msp-text-muted);
    margin-bottom: 12px;
}

/* ── Maximized state: top fills screen, middle + bottom hide ── */
.msp-root.msp-maximized .msp-middle,
.msp-root.msp-maximized .msp-bottom {
    display: none;
}
.msp-root.msp-maximized .msp-top {
    flex: 1 1 auto;
    /* Release the story-mode height cap so maximized genuinely fills the
       viewport (abundant-hopping-sifakis S2 D6). */
    height: auto;
    min-height: 0;
    max-height: none;
}
.msp-root.msp-maximized .msp-frame {
    margin: 0;
    border-radius: 0;
}

/* Floating-controls backdrop: subtle gradient ensures close chip remains
   discoverable against light focal content (PDF poster, bright photos). */
.msp-root .msp-float-controls {
    background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), transparent);
    padding-bottom: 18px;
}
.msp-root.msp-maximized .msp-float-controls {
    background: linear-gradient(180deg, rgba(0, 0, 0, 0.6), transparent);
    padding-bottom: 24px;
}

/* ── Viewport breakpoints ── */

/* Shorter viewports (iPhone SE, iPhone 12 mini: ~600–720px) — trim middle */
@media (max-height: 720px) {
    :root {
        --msp-top-flex: 5;
        --msp-middle-flex: 3.1;
        --msp-bottom-height: 72px;
    }
    .msp-middle { padding: 12px 16px; }
    .msp-section { margin-bottom: 14px; }
    .msp-pill { padding: 8px 12px; font-size: 12.5px; }
}

/* Taller devices (iPhone 15 Pro Max etc. ~930px+) — give middle a bit more */
@media (min-height: 900px) {
    :root {
        --msp-top-flex: 4.8;
        --msp-middle-flex: 3.6;
        --msp-bottom-height: 84px;
    }
}

/* Landscape: collapse middle, prioritize focal */
@media (orientation: landscape) and (max-height: 500px) {
    :root {
        --msp-top-flex: 7;
        --msp-middle-flex: 2;
        --msp-bottom-height: 60px;
    }
    .msp-middle { padding: 8px 14px; }
}

/* ── sweeping-unifying-canopy S3 D3 / Issue #11 ─────────────────────────
   UMI focal-pane full-vertical claim. Joe directive 2026-05-11 /listen
   walkthrough: "use the full vertical pane of the UMI for the media
   itself — no big black letterbox bars above and below the image". The
   default --msp-top-flex of 5 gave the focal area ~60% of remaining
   vertical space; for non-presentation UMI (single-media inspection),
   bump to 8 so the focal claims ~85% of the available vertical and the
   info pane shrinks to a scrollable footer-strip carrying the title +
   short description. Object-fit: contain keeps aspect-ratio honest (no
   crop of archival photos); the much larger focal pane makes any
   residual letterbox bars proportional to the image's natural aspect
   rather than dominating the screen.

   Story-mode UMI (.msp-over-story) keeps its own height-capped rule above
   so the Story carousel behind it remains visible — the issue is
   specifically the non-presentation UMI. */
.msp-root[data-msp-mode="umi"]:not(.msp-over-story):not(.msp-maximized) {
    --msp-top-flex: 8;
    --msp-middle-flex: 1.5;
}
.msp-root[data-msp-mode="umi"]:not(.msp-over-story):not(.msp-maximized) .msp-top {
    min-height: 50vh;
}
.msp-root[data-msp-mode="umi"]:not(.msp-over-story):not(.msp-maximized) .msp-middle {
    max-height: 30vh;
}

/* ── App shell tab bar stays visible over msp-root ──
   Per user directive (2026-04-20): every mobile screen, including UMI and
   Story presentation modals, must expose the persistent Control Panel
   (Home + Fergi AI) at the bottom. The scaffold reserves room for the tab
   bar below its bottom action row — see the padding-bottom rule on
   .msp-frame / .msp-root. During active PresentationPlayer playback the
   `body.presentation-active` class still hides the tab bar (see
   mobile-tab-bar.css) so full-screen chapter playback is uninterrupted. */
body:has(.msp-root) .mobile-tab-bar {
    z-index: 11000; /* ensure tab bar sits above msp-root panels */
}
body:has(.msp-root):not(.presentation-active) .msp-frame {
    padding-bottom: calc(
        env(safe-area-inset-bottom, 0px)
        + var(--mobile-tab-bar-height, 56px)
    );
}

/* Prevent body scroll when scaffold mounted */
body:has(.msp-root) {
    overflow: hidden;
    touch-action: none;
}
.msp-root,
.msp-middle {
    touch-action: pan-y;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
    .msp-focal-content > img,
    .msp-focal-content > video,
    .msp-story-scrub-fill {
        transition: none !important;
    }
}
