/**
 * fergi-presentation.css — Fergi AI Presentation Framework surfaces.
 * Born 2026-07-21 (issue 18): the z-cap for delegated native children and the
 * bespoke Fergi-managed modal chassis (the no-native-home case), palette-
 * coordinated with the Fergi vessel (FDC orchid/plum/azure family).
 */

/* ── Z-CAP: a native modal in Fergi's custody presents BEHIND Fergi ──────
   MOBILE-ONLY since directing-desktop-scenes S2 (S0 Decision 5): on the
   desktop canvas the static cap retired — FFVWindowManager's focus-order z
   band supersedes Fergi-always-on-top, and the orchestrator's _setCap never
   applies this class on desktop. On mobile the constitutional layering
   holds: Fergi AI is z 10350; while a delegated child carries this class it
   is pinned just under Fergi. !important is deliberate and IS the semantics:
   the cap must dominate whatever tier the native modal's own rules assign. */
.fergi-child-capped {
    z-index: 10330 !important;
}

/* ── Bespoke Fergi-managed modal chassis ───────────────────────────────── */

/* ⛔ SCOPED TO THE APP PANEL, NOT THE VIEWPORT — issue #6, certified by eye in a
   live browser at certifying-desktop-frame S4 (2026-08-13).

   This rule used to be `inset: 0`. With `backdrop-filter: blur(6px)` at z-index
   10330, that box spanned the whole viewport and therefore blurred the two
   surfaces the FFA Window Frame declares permanently legible: the Header
   (.top-nav) and the Control Panel rail (.side-nav). Opening the Dressing Room
   from the rail smeared the rail you opened it from.
   Confirmed live: box was 1458x772 = the full viewport, coversHeader and
   coversRail both true; setting backdrop-filter:none sharpened both instantly.
   The three layers the /listen walkthrough nominated were all innocent (S3 § 2)
   — the blur came from a fourth file that never made that promise.

   The blur itself is DESIGNED and is kept: it softens the content view behind a
   bespoke modal. Only the SURFACE THAT PAINTS IT is scoped, via ::before below.

   ⚠️ THE OBVIOUS FIX WAS WRONG, AND THE BROWSER SAID SO. The first attempt moved
   this element itself to the App-Panel inset that .browse-media-modal and seven
   other stylesheets use. #6 was cured — and the Dressing Room came back clipped
   off the right edge of the screen. The reason is that this container is not a
   plain centring box: it hosts room windows whose rects FFASpatial computes
   against the App Panel. With `inset: 0` those coordinates land correctly; moving
   the container's origin to the App Panel made every child pay the rail offset
   TWICE. The inset pattern is right for a modal that positions its own content
   and wrong for one that hosts pre-computed geometry.

   So the box keeps `inset: 0` — preserving the coordinate space every child
   depends on — and only the tinted, blurring surface is inset. That surface
   paints nothing structural, so nothing measures against it. */
.fergi-bespoke-modal {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4vh 4vw;
    /* The tint AND the blur moved to ::before — see the block below. This
       element keeps `inset: 0` deliberately. */
    background: transparent;
    z-index: 10330; /* below Fergi (10350), above Browse Media (9500) */
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.22s ease;
}
/* The scrim: the tint + blur, inset to the App Panel so the Header (.top-nav)
   and the Control Panel rail (.side-nav) stay legible behind an open bespoke
   modal, per the FFA Window Frame rule. Tokens, not literals, so it tracks the
   Control Panel's collapse and the v15 rail width (96px -> 172px) for free.
   `position: fixed` because the parent is fixed at inset:0 and this must be
   measured against the viewport, not against a flex-centred content box. */
.fergi-bespoke-modal::before {
    content: '';
    position: fixed;
    top: var(--nav-height, 60px);
    left: var(--side-nav-width, 60px);
    right: 0;
    bottom: 0;
    background: rgba(10, 8, 18, 0.55);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    pointer-events: none;   /* the parent already owns dismissal */
    z-index: 0;             /* .fergi-bespoke-shell is position:relative, so it
                               paints above this in DOM order */
}

.fergi-bespoke-modal.is-open {
    opacity: 1;
    pointer-events: auto;
}

/* Mobile: the scrim covers everything, exactly as .browse-media-modal goes full
   screen at the same breakpoint. The App-Panel inset is a DESKTOP correction —
   on mobile the Control Panel is a drawer rather than a standing rail, so there
   is no rail to keep legible and insetting by --side-nav-width would leave an
   unblurred phantom margin down the left edge. Without this carve-out the
   desktop fix would have shipped a mobile regression. */
@media (max-width: 767px) {
    .fergi-bespoke-modal::before {
        top: 0;
        left: 0;
    }
}

.fergi-bespoke-shell {
    position: relative;
    display: flex;
    flex-direction: column;
    width: min(720px, 92vw);
    max-height: 86vh;
    border-radius: 24px;
    /* Modal Renaissance S2: token-driven via .ffv-theme-satellite (default
       dress applied by openBespoke); fallback = the same literal, so the
       formalization is a visual no-op. */
    background: var(--ffv-glass-bg,
        linear-gradient(160deg, rgba(34, 22, 44, 0.97), rgba(16, 14, 34, 0.985)));
    border: 1px solid rgba(194, 102, 164, 0.35);
    box-shadow:
        0 0 0 1px rgba(125, 127, 181, 0.18),
        0 18px 60px rgba(6, 4, 14, 0.65),
        0 0 42px rgba(239, 63, 165, 0.12);
    /* clips content only — resize chrome sits at positive insets inside this
       edge by contract (spatial-manager.css), so it can never be clipped */
    overflow: hidden;
    transform: translateY(12px) scale(0.985);
    transition: transform 0.24s cubic-bezier(0.22, 0.9, 0.3, 1);
}
.fergi-bespoke-modal.is-open .fergi-bespoke-shell {
    transform: translateY(0) scale(1);
}

/* gradient rim echoing the vessel's magenta→violet→azure band */
.fergi-bespoke-shell::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 2px;
    background: var(--ffv-rim-gradient, linear-gradient(120deg,
        rgba(239, 63, 165, 0.55), rgba(157, 95, 224, 0.45),
        rgba(79, 155, 196, 0.5), rgba(64, 226, 210, 0.4)));
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    pointer-events: none;
    opacity: 0.75;
}

.fergi-bespoke-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 18px;
    background: linear-gradient(90deg,
        rgba(194, 102, 164, 0.22), rgba(125, 127, 181, 0.16), rgba(79, 155, 196, 0.18));
    border-bottom: 1px solid rgba(194, 102, 164, 0.22);
}
.fergi-bespoke-glyph { font-size: calc(1.1rem * var(--type-scale, 1)); }
.fergi-bespoke-title {
    flex: 1;
    margin: 0;
    font-size: calc(1.02rem * var(--type-scale, 1));
    font-weight: 600;
    color: #f3eefa;
    letter-spacing: 0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.fergi-bespoke-close,
.fergi-bespoke-minimize {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: none;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.08);
    color: #e8dff2;
    cursor: pointer;
    transition: background 0.15s ease;
}
.fergi-bespoke-close:hover { background: rgba(239, 63, 165, 0.3); }
.fergi-bespoke-minimize:hover { background: rgba(157, 95, 224, 0.3); }

.fergi-bespoke-body {
    flex: 1;
    overflow-y: auto;
    padding: 20px 22px 24px;
    color: #e9e4f2;
    line-height: 1.55;
    font-size: calc(0.95rem * var(--type-scale, 1));
}
.fergi-bespoke-body h1, .fergi-bespoke-body h2, .fergi-bespoke-body h3 {
    color: #f6f1fb;
    margin: 0.9em 0 0.45em;
    line-height: 1.25;
}
.fergi-bespoke-body h1:first-child, .fergi-bespoke-body h2:first-child,
.fergi-bespoke-body h3:first-child, .fergi-bespoke-body p:first-child { margin-top: 0; }
.fergi-bespoke-body a { color: #7fc4e8; }
.fergi-bespoke-body table {
    width: 100%;
    border-collapse: collapse;
    margin: 0.8em 0;
    font-size: calc(0.9rem * var(--type-scale, 1));
}
.fergi-bespoke-body th, .fergi-bespoke-body td {
    padding: 6px 10px;
    border: 1px solid rgba(194, 102, 164, 0.22);
    text-align: left;
}
.fergi-bespoke-body th { background: rgba(125, 127, 181, 0.18); }
.fergi-bespoke-body code {
    background: rgba(125, 127, 181, 0.2);
    padding: 0.1em 0.35em;
    border-radius: 5px;
    font-size: 0.88em;
}
.fergi-bespoke-body blockquote {
    margin: 0.8em 0;
    padding: 0.4em 1em;
    border-left: 3px solid rgba(239, 63, 165, 0.55);
    background: rgba(239, 63, 165, 0.07);
    border-radius: 0 8px 8px 0;
}
.fergi-bespoke-body .ai-media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 8px;
    margin-top: 10px;
}
.fergi-bespoke-body .ai-media-grid-item img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: 10px;
    cursor: pointer;
}
.fergi-bespoke-empty {
    padding: 30px 0;
    text-align: center;
    opacity: 0.6;
}

/* size variants */
.fergi-bespoke-modal.fergi-bespoke--compact .fergi-bespoke-shell { width: min(460px, 92vw); }
.fergi-bespoke-modal.fergi-bespoke--wide    .fergi-bespoke-shell { width: min(980px, 94vw); }
.fergi-bespoke-modal.fergi-bespoke--tall    .fergi-bespoke-shell { max-height: 94vh; }

/* ── BiographyModal production-requested badge (issue-18 bio integration) ── */
.bio-production-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    padding: 0.16em 0.62em;
    border-radius: 999px;
    font-size: calc(0.78rem * var(--type-scale, 1));
    font-weight: 600;
    color: #4a1a6b;
    background: linear-gradient(135deg, #f3d9ff, #d9a8f0);
    border: 1px solid #b06fd4;
}

/* mobile: full-bleed sheet feel */
@media (max-width: 640px) {
    .fergi-bespoke-modal { padding: 0; align-items: flex-end; }
    .fergi-bespoke-shell {
        width: 100vw;
        max-height: 92vh;
        border-radius: 20px 20px 0 0;
    }
}

/* ── FFASizeLadder pill (shared −/+ stepper, vessel-pattern) ─────────────── */
.fv-size-ladder {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
}
.fv-step-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: 1px solid rgba(125, 127, 181, 0.45);
    border-radius: 9px;
    background: rgba(30, 22, 46, 0.55);
    color: #e8dff2;
    font-size: calc(1.05rem * var(--type-scale, 1));
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease, opacity 0.15s ease;
}
.fv-step-btn:hover:not(:disabled) { background: rgba(194, 102, 164, 0.35); }
.fv-step-btn:disabled { opacity: 0.32; cursor: default; }

/* ── the fleet cycle control (truing-vessel-chrome S4, #6 standard) ────────
   ONE windowing-chrome contract across every FFV vessel: the cycle control
   is a state-REFLECTING window glyph placed upper-LEFT (order:-1 in these
   flex headers); the universal ✕ stays upper-RIGHT (order:9), far from it.
   The glyph DEPICTS the current state — minimized = thin bar, persistent =
   small window frame, maximized = full fill — via the data-ffw-state attr
   stamped by FFVWindowManager.syncCycleControl. With no state attr (mobile:
   syncCycleControl is desktop-only) only the thin bar shows, i.e. the plain
   minimize the control has always been there. */
/* the fleet STEP pair (Joe /listen 2026-07-23 — replaces the S4 one-way
   cycle carousel): − / + walk dock ↔ persistent ↔ maximized stepwise. The
   end button disables; on mobile (no data-ffw-state — sync is desktop-only)
   the + hides and the − reads as the plain dock park it always was. */
.ffw-steps { display: inline-flex; gap: 4px; align-items: center; }
.ffw-step[disabled] { opacity: .35; cursor: default; }
.ffw-step[data-ffw-step="up"]:not([data-ffw-state]) { display: none; }

/* ── the viewport takeover (squaring-chrome-maximize S1, Joe 2026-08-02) ──
   Maximized = the entire viewport at the fullscreen tier. The shell may carry
   no residual frame styling that would open a gap or clip the close target;
   geometry is inline (FFVWindowManager.applyViewportRect) — these rules kill
   the box-model leftovers a host stylesheet could still contribute. */
.ffw-maximized {
    border-radius: 0 !important;
    margin: 0 !important;
    max-width: none !important;
    max-height: none !important;
    transform: none !important;
    /* The takeover is OPAQUE: sculpted shells are transparent outside their
       drawn chassis path, so at the viewport rect the stage beneath bleeds
       through (Joe /listen 2026-08-02 — "the whole screen with no
       transparency to whatever is beneath"). The themes' translucent body
       panels composite over this plate, so each vessel keeps its hue. */
    background: #0b0f14 !important;
}
/* The WM steps the − / + pair down with the `hidden` attribute while
   maximized; any host display rule (.ffrw-controls .ffw-step et al.)
   outranks the UA [hidden] default, leaving dead-but-visible buttons.
   The attribute must always win: a locked control never presents as live. */
.ffw-step[hidden] { display: none !important; }
/* The size ladder is a resize authority, and resize cannot exit a takeover
   (029 §3): its pill stands down while the shell is maximized. */
.ffw-maximized .fv-size-ladder { display: none; }

/* The + slot's noninteractive status indicator while maximized — same chrome
   slot, no button semantics, never in the tab order. */
.ffw-max-indicator {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    color: currentColor;
    opacity: .8;
    cursor: default;
    pointer-events: none;
}

/* Screen-reader-only live region for takeover refusals (029 §5). */
.ffw-sr-live {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    overflow: hidden;
    white-space: nowrap;
}

/* flex-header mounts: the cycle glyph leads the header at the far LEFT; the
 * pill sits between the title and the ✕ at the far right (S6 + S4 #6) */
.bio-modal-header .bio-modal-close { order: 9; }
.bio-modal-header .bio-modal-minimize { order: -1; margin-right: 2px; }
.bio-modal-header .bio-modal-title-block { margin-right: auto; }
.bio-modal-header .fv-size-ladder { margin-left: auto; margin-right: 10px; }
.bio-modal-header .fv-step-btn {
    border-color: rgba(120, 100, 150, 0.35);
    background: rgba(120, 100, 150, 0.12);
    color: inherit;
}
.fergi-bespoke-header .fergi-bespoke-close { order: 9; }
.fergi-bespoke-header .fergi-bespoke-minimize { order: -1; }
/* ONE sizing control per header (Joe ruling, release-day /listen 2026-08-18):
   the ffw-steps pair upper-left is the control; the FFASizeLadder's own chrome
   was ALSO mounting here, putting a second −/+ pair beside the ✕ — two
   generations of sizing machinery in one header. The ladder's rung MECHANICS
   stay (stepUp/stepDown drive them); only its duplicate chrome is suppressed.
   Full 4-state cycle standard (default ↔ maximize ↔ default ↔ dock, factory
   first-open fills the App Panel) is the fleet audit's scope. */
.fergi-bespoke-header .fv-size-ladder { display: none; }

/* root mounts (whole-surface modals): float top-right, clear of the ✕ */
.story-detail-modal > .fv-size-ladder,
.browse-media-modal > .fv-size-ladder {
    position: absolute;
    top: 12px;
    right: 64px;
    z-index: 60;
    padding: 4px;
    border-radius: 12px;
    background: rgba(12, 10, 24, 0.55);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

/* biography Approve & Render chrome */
.bio-rendered-note {
    display: inline-flex;
    align-items: center;
    padding: 0.3em 0.7em;
    border-radius: 999px;
    font-size: calc(0.8rem * var(--type-scale, 1));
    font-weight: 600;
    color: #1c6b3a;
    background: #e2f6ea;
    border: 1px solid #9fd8b4;
}
.bio-spinner-inline {
    display: inline-block;
    width: 14px;
    height: 14px;
    border: 2px solid rgba(120, 100, 150, 0.35);
    border-top-color: #7d5fb0;
    border-radius: 50%;
    animation: fvspin 0.9s linear infinite;
}
@keyframes fvspin { to { transform: rotate(360deg); } }

/* ── bespoke gallery child modal (Joe ruling 2026-07-21, Mary walk) ─────── */
.fergi-gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 10px;
}
.fergi-gallery-item {
    position: relative;
    border-radius: 12px;
    overflow: hidden;
    cursor: pointer;
    background: rgba(125, 127, 181, 0.12);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.fergi-gallery-item:hover {
    transform: translateY(-2px) scale(1.02);
    box-shadow: 0 8px 26px rgba(239, 63, 165, 0.25);
}
.fergi-gallery-item img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    display: block;
}
.fergi-gallery-cap {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 4px 8px;
    font-size: calc(0.72rem * var(--type-scale, 1));
    color: #f3eefa;
    background: linear-gradient(transparent, rgba(10, 8, 20, 0.85));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── delegation reachability (Joe, Mary walk 2026-07-21) ──────────────────
   .unified-ai-modal is a full-App-Panel overlay (transparent but
   click-intercepting) — a delegated child behind Fergi was visible yet
   unreachable. With the vessel active, the overlay root passes pointer
   events through; only Fergi's real surfaces (the container/deck and any
   overlay children) accept clicks. Click-through lands on the child modal —
   the constitutional co-presence actually usable. */
body.fv-active .unified-ai-modal:is(.active, .is-open) {
    pointer-events: none;
}
body.fv-active .unified-ai-modal:is(.active, .is-open) > * {
    pointer-events: auto;
}

/* gallery thumbnail size rungs (Joe, Mary walk — grow/shrink in the child) */
.fergi-gallery-grid.fg-thumb-0 { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
.fergi-gallery-grid.fg-thumb-1 { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.fergi-gallery-grid.fg-thumb-2 { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.fergi-gallery-grid.fg-thumb-3 { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.fg-thumb-0 .fergi-gallery-cap { display: none; }
.fv-thumb-ladder .fv-step-btn { width: auto; padding: 0 8px; font-size: calc(0.82rem * var(--type-scale, 1)); }

/* What-We-Know hosted in a bespoke child (issue-18, Mary walk) — the WWK
   cards were authored on the panel's light substrate; keep them readable
   on the bespoke dark glass. */
.wwk-in-bespoke { padding: 14px 16px 20px; }
.wwk-in-bespoke .wwk-person-bio,
.wwk-in-bespoke p { color: inherit; }

/* ═══ The biography WORKSPACE (Modal Renaissance S3, Track A3/A4) ═════════
   open_biography flagged `workspace:true` composes the two-child scene:
   the Biographer's Study (deckled folio) open on the left of the desk, the
   Cartographer's star chart pinned upper-right, the Fergi vessel perched
   compact at the bottom-right (adopt() compacts it). Each child's
   fullscreen overlay would otherwise swallow clicks aimed at the other —
   in workspace mode both overlays go pointer-transparent (their shells
   stay live) and ONE shared scrim carries the night dim beneath both.
   S5 (Track B3): FFASpatial now owns the desk — the orchestrator hands it
   these same rects as workspace defaultRect overrides (persist keys
   'workspace:bio'/'workspace:kg'), so both children drag/resize freely on
   the desk. The calc() rules below stay ONLY as the no-FFASpatial fallback,
   sans !important so spatial's inline rects outrank them (composition
   clears any persisted rung first, so no inline rung fights the fallback). */

body.ffa-bio-workspace .ffa-ws-bio,
body.ffa-bio-workspace .ffa-ws-kg {
    background: transparent !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    pointer-events: none !important;
}

/* one shared scene FLOOR beneath the members (z 10320 < children 10330).
   truing-vessel-chrome S4 (#7, Joe /listen): fresh programmatic scenes wear
   the generic NEURAL-NET dress — the FFV desktop's night field at the same
   whisper volume (ffv-desktop.css palette, fv-neural-tree language) — instead
   of the old blur-of-last-view night dim. It covers ONLY the FFACanvas rect
   (right of the Control Panel, below the header), so the frame chrome stays
   fully visible as well as reachable.
   DECORATIVE ONLY — the 07-22 R11 rulings hold exactly (Joe /listen, the
   bio-trio "locked condition" blocker): pointer-transparent by law, no click
   handler; clicks pass through to the app beneath — the Control Panel stays
   reachable and background presses never close windows. Scene dismissal =
   primary ✕ (endScene), CP navigation (notifyNavReset), or ESC. */
.ffa-ws-scrim {
    position: fixed;
    left: var(--ffa-canvas-x, var(--side-nav-width, 60px));
    top: var(--ffa-canvas-y, var(--nav-height, 60px));
    width: var(--ffa-canvas-w, calc(100vw - var(--side-nav-width, 60px)));
    height: var(--ffa-canvas-h, calc(100vh - var(--nav-height, 60px)));
    background:
        radial-gradient(120% 90% at 18% 8%, rgba(226, 67, 159, 0.09), transparent 52%),
        radial-gradient(130% 100% at 88% 94%, rgba(56, 170, 232, 0.095), transparent 55%),
        linear-gradient(158deg, #0d0b1c 0%, #120e26 44%, #171130 78%, #131026 100%);
    /* A true FLOOR (Joe /listen 2026-07-23, v1.0.065 regression): OPAQUE — the
       underlying view never bleeds through — and BELOW the window band
       (FFVWindowManager Z_BASE 10300) so EVERY scene member paints above it.
       The S4 value (10320, translucent) sat inside the band: only the focused
       window + Fergi escaped it and everything else ghosted beneath.
       Guard: scene-manager.smoke.cjs asserts floor-z < Z_BASE. */
    z-index: 10290;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.28s ease;
}
/* the constellation — a SPINE layer over a finer CAPILLARY layer, at the same
   speaking volume as the FFV desktop field (S4/D4.2). The second layer is where
   the density Joe asked for comes from; thickening the spine alone would have
   bought loudness without depth. */
.ffa-ws-scrim::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image:
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1200' height='800' viewBox='0 0 1200 800'><g fill='none' stroke-linecap='round'><path d='M60 720 C 220 640, 340 700, 470 610 C 560 548, 640 580, 740 520' stroke='%23ff85cf' stroke-width='1.2' opacity='.35'/><path d='M1150 120 C 1000 200, 900 150, 780 240 C 700 300, 620 270, 540 330' stroke='%235fd8f0' stroke-width='1.2' opacity='.32'/><path d='M140 160 C 300 220, 430 180, 560 230 C 640 260, 700 240, 760 260' stroke='%239d5fe0' stroke-width='1' opacity='.3'/><path d='M1080 640 C 960 600, 880 650, 780 610' stroke='%23ef3fa5' stroke-width='1' opacity='.26'/><path d='M470 610 402 528M470 610 545 552M740 520 700 448M780 240 840 168M560 230 520 152' stroke='%23c9b8ea' stroke-width='.9' opacity='.28'/></g><g fill='%23ffffff'><circle cx='740' cy='520' r='3' opacity='.5'/><circle cx='540' cy='330' r='2.4' opacity='.42'/><circle cx='470' cy='610' r='2.2' opacity='.42'/><circle cx='780' cy='240' r='2.2' opacity='.42'/></g><g fill='%23ffd6ee'><circle cx='402' cy='528' r='1.6' opacity='.45'/><circle cx='840' cy='168' r='1.6' opacity='.45'/><circle cx='176' cy='430' r='1.2' opacity='.35'/><circle cx='1040' cy='390' r='1.2' opacity='.35'/></g><g fill='%2340e2d2'><circle cx='700' cy='448' r='1.4' opacity='.5'/><circle cx='545' cy='552' r='1.3' opacity='.45'/><circle cx='520' cy='152' r='1.4' opacity='.45'/><circle cx='930' cy='700' r='1.1' opacity='.35'/></g></svg>"),
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1200' height='800' viewBox='0 0 1200 800'><g fill='none' stroke-linecap='round' stroke-width='.7'><path d='M-20 448 C 130 496, 254 388, 380 448 C 502 506, 606 402, 740 472 C 866 538, 968 430, 1090 486 C 1152 514, 1184 470, 1220 486' stroke='%23ff85cf' opacity='.2'/><path d='M-20 284 C 128 356, 250 292, 372 340 C 500 390, 598 314, 728 360 C 852 404, 960 330, 1080 374 C 1148 398, 1186 360, 1220 366' stroke='%235fd8f0' opacity='.19'/><path d='M-20 596 C 136 500, 258 606, 376 532 C 504 454, 608 592, 736 512 C 860 434, 964 566, 1088 500 C 1152 466, 1188 512, 1220 480' stroke='%239d5fe0' opacity='.18'/><path d='M176 430 168 318M176 430 152 552M470 610 380 448M470 610 376 532M740 520 728 360M740 520 740 472M1040 390 1080 374M1040 390 1088 500M560 230 372 340M780 240 728 360' stroke='%23c9b8ea' opacity='.16'/></g><g fill='%23bfe9f7'><circle cx='380' cy='448' r='1.5' opacity='.3'/><circle cx='740' cy='472' r='1.5' opacity='.3'/><circle cx='1090' cy='486' r='1.3' opacity='.28'/><circle cx='372' cy='340' r='1.3' opacity='.28'/><circle cx='728' cy='360' r='1.5' opacity='.3'/><circle cx='1080' cy='374' r='1.3' opacity='.28'/><circle cx='376' cy='532' r='1.3' opacity='.26'/><circle cx='736' cy='512' r='1.5' opacity='.28'/><circle cx='1088' cy='500' r='1.3' opacity='.26'/><circle cx='168' cy='318' r='1.2' opacity='.24'/><circle cx='152' cy='552' r='1.2' opacity='.24'/></g></svg>");
    background-repeat: no-repeat, no-repeat;
    background-position: center, center;
    background-size: cover, cover;
    /* obeying-portrait-instructions S4/D4.2. This floor is documented above as
       "the FFV desktop's night field at the same whisper volume" — so when the
       desktop's volume rose (ffv-desktop.css, Joe: "more salient… denser"), this
       had to rise with it or the two surfaces a member moves between would
       disagree about how loud the vault's own design language is. Lifted to the
       same effective register; the traces inside the data-URI carry their own
       0.26–0.35 alphas, so 0.5 → 0.82 lands it beside the desktop's 0.62 × 0.34
       spine rather than past it. */
    opacity: 0.82;
}
/* soft vignette settles the floor's edges under the window shadows */
.ffa-ws-scrim::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(120% 105% at 50% 46%, transparent 62%, rgba(5, 4, 12, 0.4) 100%);
}
.ffa-ws-scrim.is-on { opacity: 1; }

/* the manuscript on the desk — left of center, full working height
   (fallback geometry; FFASpatial's inline rect governs when present) */
body.ffa-bio-workspace .ffa-ws-bio .bio-modal-shell {
    position: fixed;
    left: calc(var(--side-nav-width, 60px) + 22px);
    right: auto;
    top: calc(var(--nav-height, 60px) + 18px);
    width: calc((100vw - var(--side-nav-width, 60px)) * 0.52 - 34px);
    height: calc(100vh - var(--nav-height, 60px) - 36px);
    max-height: none;
    max-width: none;
    margin: 0;
    pointer-events: auto;
}

/* the star chart pinned to the right — top-aligned with the folio, its
   lower limb leaving the bottom-right corner of the desk for the
   compacted vessel (the vessel floats above; a grazing overlap reads as
   Fergi perched at the chart's edge, not as a collision) */
body.ffa-bio-workspace .ffa-ws-kg .fergi-bespoke-shell {
    position: fixed;
    left: auto;
    right: 22px;
    top: calc(var(--nav-height, 60px) + 18px);
    width: calc((100vw - var(--side-nav-width, 60px)) * 0.44 - 30px);
    height: calc((100vh - var(--nav-height, 60px)) * 0.58);
    max-height: none;
    margin: 0;
    pointer-events: auto;
}

/* While the workspace owns the layout, the size-ladder pills rest — a
   rung's inline styles would fight the pinned rects. FFASpatial (S5)
   restores freeform sizing with a proper rung hand-off. */
body.ffa-bio-workspace .ffa-ws-bio .fv-size-ladder,
body.ffa-bio-workspace .ffa-ws-kg .fv-size-ladder {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    .ffa-ws-scrim { transition: none; }
}

/* ═══ Touch floor for the text-size pair (Greg G-055, 2026-10-02) ═══
   The family-device matrix measured the biography header's − / + ("Smaller" / "Larger") at 30 × 30 on
   every phone. Each gets an invisible centred max(100%,44px) target (the G-027 pattern), and the pair's
   gap widens to 16 px so the two expanded targets cannot overlap (an overlap would let a tap on the edge
   of "Smaller" land on "Larger"; at 14 px the targets touched with zero margin and sub-pixel layout
   tipped the edge to the neighbour). Mouse desktops (pointer: fine) are untouched. */
@media (pointer: coarse) {
    html .fv-size-ladder { gap: 16px; }
    html .fv-step-btn { position: relative; touch-action: manipulation; }
    html .fv-step-btn::after {
        content: '';
        position: absolute;
        left: 50%;
        top: 50%;
        width: max(100%, 44px);
        height: max(100%, 44px);
        transform: translate(-50%, -50%);
    }
}
