/* =============================================================================
   STORYBOOK / PAGE-TURN SYSTEM
   Full-viewport 3D page-turn engine with paper texture overlay
   ============================================================================= */

/* ─── Book Viewport (perspective container — fills .book-frame or .book-frame-right) ─── */
.book-viewport {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: visible; /* Allow 3D page flaps to extend during turns — .book-desk-scene contains overflow */
    clip-path: none;
    perspective: var(--book-perspective);
    perspective-origin: 0% 50%; /* Align with transform-origin: left center */
    background: var(--book-page-cream);
    border-radius: inherit;
}

.book-page-stack {
    position: relative;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
}

/* During page turns, raise the page-stack above the theme ribbon toggle.
   The ribbon sits at z-index: 200 (dark-mode.css) and is lowered to z-index: 0
   during turns (desk-scene.css). But with page-stack at z-index: auto, both land
   in the same stacking tier and DOM order wins (ribbon after stack → on top).
   Setting z-index: 201 ensures the stack renders above the lowered ribbon. */
.book-page-stack:has(.book-page--turning),
.book-page-stack:has(.book-page--dragging) {
    z-index: 201;
}

/* ─── Individual Book Page ─── */
.book-page {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    transform-origin: left center;
    /* Cream background prevents any white line from seeping between front/back faces */
    background-color: var(--book-page-cream, #f5f0e6);
    /* No transition — animation is driven by keyframes for natural fold effect */
    /* Force GPU compositing layer — eliminates z-fighting between front/back faces
       at exactly 90deg by ensuring each page gets its own render surface */
    will-change: transform;
}

/* V-shape curvature — unturned pages bow slightly away from spine for open-book realism.
   translateZ micro-offset (0.5px per page index) prevents coplanar depth-sorting ambiguity
   in preserve-3d — without it, the compositor may randomly let sibling pages bleed through.
   translateZ is applied BEFORE rotateY so the offset is in world space (hinge stays at spine).
   B7 fix: Increased offset from 0.1px to 0.5px per page to prevent mid-turn bleed-through.
   At 0.1px, the total gap between page 0 and page 5 was only 0.5px — not enough to prevent
   the turning page's angled far edge from intersecting stacked pages behind it. At 0.5px,
   the gap is 2.5px, which combined with the turning page's translateZ(2px) provides adequate
   separation across the full rotation arc. */
.book-page:not(.book-page--turned):not(.book-page--turning) {
    transform: translateZ(calc(var(--page-z-offset, 0) * -0.5px)) rotateY(var(--book-page-rest-angle, 0deg));
}

.book-page-front,
.book-page-back {
    position: absolute;
    inset: 0;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    overflow: hidden;
}

.book-page-front {
    /* Z-fighting fix: will-change:transform on parent .book-page forces a compositing
       layer, eliminating the 90deg flicker without fragile sub-pixel translateZ hacks.
       The old translateZ(0.1px) was GPU/browser-dependent and failed on some retina displays. */
    /* Aged paper gradient — darker at spine and free edge, lighter in center */
    background: linear-gradient(to right,
        #ede5d8 0%,
        var(--book-page-cream) 8%,
        var(--book-page-cream) 85%,
        #f0e8dc 100%);
}

.book-page-back {
    transform: rotateY(180deg);
    /* Solid fallback prevents content bleed-through in preserve-3d context when
       turned pages fan at similar angles. Without this, pages behind can ghost
       through if the image hasn't loaded or has any transparency. */
    background-color: var(--book-page-cream, #f5f0e6);
    background-image: url('/images/backgrounds/left-page-parchment.jpg');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

.book-page-back-texture {
    position: absolute;
    inset: 0;
    /* Paper fiber texture instead of grain for visible back-face difference */
    background: var(--env-paper-fiber, var(--env-paper-grain));
    opacity: 0.10;
}

/* Custom back-face content (left-page content on BookPage back faces).
   No rotateY needed — the parent .book-page-back already has rotateY(180deg)
   for the 3D flip. Content inherits the back face's local coordinate space
   and reads correctly when the page is turned. Adding another 180deg would
   double-mirror the text (same pattern as .book-flyleaf-back-content). */
.book-page-back-content {
    position: absolute;
    inset: 0;
    z-index: 2;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
}

.book-countdown-back {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100%;
}

/* Background image layer */
.book-page-bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

/* Paper edge vignette — fades BG image into paper at edges */
.book-page-paper-edge {
    position: absolute;
    inset: 0;
    box-shadow: inset 0 0 var(--book-vignette-blur) var(--book-vignette-spread) var(--book-page-cream);
    pointer-events: none;
    z-index: 1;
}

/* Paper grain texture overlay on front face */
.book-page-front::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--env-paper-grain);
    opacity: 0.04;
    pointer-events: none;
    z-index: 2;
}

/* Gold decorative line at top of page content */
.book-page-content::before {
    content: '';
    display: block;
    width: 60%;
    max-width: 300px;
    height: 1px;
    margin: 0 auto 0;
    background: linear-gradient(to right,
        transparent,
        var(--book-gold-detail) 20%,
        var(--book-gold-leaf) 50%,
        var(--book-gold-detail) 80%,
        transparent);
    opacity: 0.3;
}

/* Page content layer */
.book-page-content {
    position: relative;
    z-index: 3;
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    scrollbar-color: rgba(212, 175, 55, 0.2) transparent;
    /* B3 fix: isolate content layout from external changes (frame width transition,
       perspective snap during Opening→Book) to prevent content reflowing/shifting */
    contain: layout style;
}

.book-page-content::-webkit-scrollbar {
    width: 4px;
}

.book-page-content::-webkit-scrollbar-track {
    background: transparent;
}

.book-page-content::-webkit-scrollbar-thumb {
    background: rgba(212, 175, 55, 0.2);
    border-radius: 2px;
}

/* Helper class for section content within book pages */
.book-page-section-content {
    padding: 40px 30px;
    max-width: 900px;
    margin: 0 auto;
    position: relative;
    min-height: 100%;
}

@media (max-width: 768px) {
    .book-page-section-content {
        padding: 24px 16px;
    }
}

/* ═══════════════════════════════════════════════════════════════
   CINEMATIC STORYBOOK PAGE TURN
   A multi-layered page-turn system that mimics real paper physics:
   - S-curve page curl via oscillating rotateX across keyframes
   - Dynamic shadow that grows/shifts with page position
   - Page flutter/settle with dampened overshoot on landing
   - Visible paper edge thickness during the turn arc
   - Warm golden spine glow (magical storybook feel)
   - Floating golden dust motes (CSS-only particles)
   - Curved curl shadow on the front face
   - Ambient right-edge glow on unturned pages
   ═══════════════════════════════════════════════════════════════ */

/* ─── Page Turn States ─── */
.book-page--turned {
    /* Micro Z-offset: turned pages stack in reverse order on the left side.
       Higher page index = turned later = should be on top of earlier turned pages.
       So we use positive offset × page-index (Page 5 is frontmost when turned). */
    transform: translateZ(calc(var(--page-z-offset, 0) * 0.5px)) rotateY(var(--book-settle-angle, -180deg));
    /* Phase 2A: Variable duration — JS sets --book-settle-duration per page for cascading fan */
    transition: transform var(--book-settle-duration, 0.5s) cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.book-page--turning {
    /* z-index set per-page by JS (descending from 200) to prevent clipping during staggered turns */
    will-change: transform;
}

/* During turns/drags, push turned pages BEHIND the turning page in the preserve-3d context.
   At the spine (transform-origin: left center), both turned and turning pages share the
   same hinge point. translateZ alone on the turning page can't prevent the turned page's
   back face from peeking through the semi-transparent gutter shadow at the spine edge.
   Base pushback at -10px. The micro-offset (0.5px per page-index) preserves correct
   ordering among turned pages — without it, all turned pages sit at the same Z and
   browser rendering order becomes unpredictable, causing wrong left pages to show.
   The :has() selector scopes this to only when a turn/drag is active. */
.book-page-stack:has(.book-page--turning) .book-page--turned:not(.book-page--turning),
.book-page-stack:has(.book-page--dragging) .book-page--turned:not(.book-page--dragging) {
    /* translateZ BEFORE rotateY so it applies in world space (not rotated local space).
       rotateY(-178deg) would flip local-Z, making translateZ(-5px) appear as +5px in world. */
    transform: translateZ(calc(-10px + var(--page-z-offset, 0) * 0.5px)) rotateY(var(--book-settle-angle, -180deg));
}

/* During turns/drags, push the turned COVER PAGE behind the turning page.
   The cover uses .book-cover-page--turned (not .book-page--turned), so the
   rule above doesn't catch it. At -11px the cover is behind all turned pages
   (at -10px + micro-offset) but in front of flyleaves (-12px). */
.book-page-stack:has(.book-page--turning) .book-cover-page--turned,
.book-page-stack:has(.book-page--dragging) .book-cover-page--turned {
    transform: translateZ(-11px) rotateY(-180deg);
}

/* Decorative flyleaves 1-3 (endpaper textures only): push behind and hide
   during turns/drags. They have no user-facing content, and at extreme -Z
   they can Z-fight or create rendering artifacts. */
.book-page-stack:has(.book-page--turning) .book-flyleaf--turned:not(.book-flyleaf--4),
.book-page-stack:has(.book-page--dragging) .book-flyleaf--turned:not(.book-flyleaf--4) {
    transform: translateZ(-12px) rotateY(-180deg);
    visibility: hidden;
}

/* Flyleaf 4 carries the Table of Contents on its back face (Spread 0 left page).
   Push it just behind the lowest turned page (page 0 at -10px) so it doesn't
   clip through on later spreads, but keep it close enough to remain visible.
   On Spread 0, no .book-page elements are turned yet, so nothing occludes
   the TOC — it renders correctly as the left page. On Spreads 1+, turned
   pages' opaque back faces at Z >= -10px naturally occlude flyleaf 4 behind them. */
.book-page-stack:has(.book-page--turning) .book-flyleaf--4.book-flyleaf--turned,
.book-page-stack:has(.book-page--dragging) .book-flyleaf--4.book-flyleaf--turned {
    transform: translateZ(-10.1px) rotateY(-180deg);
}

/* During turns/drags, push NON-TURNING stacked pages further behind.
   At rest, stacked pages have translateZ(-0.5px * index), giving only 0.5–2.5px
   of total Z separation. During a turn, the turning page's angled face sweeps
   through the Z space, potentially intersecting with the stacked pages' faces.
   Pushing stacked pages to -10px provides a 12px gap from the turning page
   at translateZ(2px), eliminating mid-turn bleed-through. */
.book-page-stack:has(.book-page--turning) .book-page:not(.book-page--turned):not(.book-page--turning),
.book-page-stack:has(.book-page--dragging) .book-page:not(.book-page--turned):not(.book-page--dragging) {
    transform: translateZ(calc(var(--page-z-offset, 0) * -0.5px - 10px)) rotateY(var(--book-page-rest-angle, 0deg));
}

/* ─── Page Stack Thickness — visible paper edge when idle (Phase 1A) ─── */
/* Right-edge thickness on unturned stack — alternating paper/shadow layers */
.book-page:not(.book-page--turned):not(.book-page--turning) > .book-page-front {
    box-shadow:
        inset -2px 0 0 #d4cfc8,
        inset -3px 0 0 #eae5de,
        inset -4px 0 0 #f5f0e6,
        inset -5px 0 0 #d4cfc8,
        inset -6px 0 0 #f0e8dc,
        inset -7px 0 0 #f5f0e6,
        inset -8px 0 0 #d4cfc8,
        inset -9px 0 0 #eae5de,
        inset -10px 0 0 #f5f0e6,
        inset -11px 0 0 #d4cfc8;
}

/* Left-edge thickness on turned stack */
.book-page.book-page--turned:not(.book-page--turning) > .book-page-back {
    box-shadow:
        inset 2px 0 0 #d4cfc8,
        inset 3px 0 0 #eae5de,
        inset 4px 0 0 #f5f0e6,
        inset 5px 0 0 #d4cfc8,
        inset 6px 0 0 #f0e8dc,
        inset 7px 0 0 #f5f0e6,
        inset 8px 0 0 #d4cfc8,
        inset 9px 0 0 #eae5de;
}

/* ─── 3D Page Edge Geometry — real positioned edge elements with Z-depth ─── */
/* Right edge (visible on unturned pages): 3 paper sheet spans at staggered Z-depths */
.book-page-edge-right {
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: 0;
    transform-origin: right center;
    pointer-events: none;
    z-index: 5;
}

.book-page-edge-right span {
    position: absolute;
    right: 0;
    top: 0;
    /* Micro-overlap: width slightly larger than gap to eliminate hairline cracks
       caused by float rounding and antialiasing between adjacent 3D spans */
    width: calc(var(--book-edge-3d-thickness) + 0.5px);
    height: 100%;
    background: linear-gradient(to right, #e8e0d0, #f5f0e8, #e8e0d0);
    transform-origin: right center;
    /* Soft shadow fills any residual sub-pixel gap between spans */
    box-shadow: 1px 0 1px rgba(0,0,0,0.04), -0.5px 0 0.5px rgba(232, 224, 208, 0.8);
}

.book-page-edge-right span:nth-child(1) {
    transform: rotateY(90deg) translateZ(0px);
}

.book-page-edge-right span:nth-child(2) {
    transform: rotateY(90deg) translateZ(calc(var(--book-edge-3d-gap)));
    opacity: 0.85;
}

.book-page-edge-right span:nth-child(3) {
    transform: rotateY(90deg) translateZ(calc(var(--book-edge-3d-gap) * 2));
    opacity: 0.7;
}

.book-page-edge-right span:nth-child(4) {
    transform: rotateY(90deg) translateZ(calc(var(--book-edge-3d-gap) * 3));
    opacity: 0.55;
}

/* Only show edge on top 3 unturned pages (based on --stack-position set by JS) */
.book-page:not(.book-page--turned):not(.book-page--turning) .book-page-edge-right {
    display: block;
}

.book-page--turned .book-page-edge-right,
.book-page--turning .book-page-edge-right {
    display: none;
}

/* Hide edges deeper than position 2 in the stack */
.book-page:not(.book-page--turned):not(.book-page--turning) .book-page-edge-right {
    opacity: calc(1 - var(--stack-position, 0) * 0.35);
}

/* Left edge (visible on turned pages): paper thickness on the turned stack */
.book-page-edge-left {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 0;
    transform-origin: left center;
    pointer-events: none;
    z-index: 5;
}

.book-page-edge-left span {
    position: absolute;
    left: 0;
    top: 0;
    width: var(--book-edge-3d-thickness);
    height: 100%;
    background: linear-gradient(to left, #e8e0d0, #f5f0e8, #e8e0d0);
    transform-origin: left center;
    box-shadow: -1px 0 1px rgba(0,0,0,0.04);
}

.book-page-edge-left span:nth-child(1) {
    transform: rotateY(-90deg) translateZ(0px);
}

.book-page-edge-left span:nth-child(2) {
    transform: rotateY(-90deg) translateZ(calc(var(--book-edge-3d-gap)));
    opacity: 0.85;
}

.book-page-edge-left span:nth-child(3) {
    transform: rotateY(-90deg) translateZ(calc(var(--book-edge-3d-gap) * 2));
    opacity: 0.7;
}

.book-page-edge-left span:nth-child(4) {
    transform: rotateY(-90deg) translateZ(calc(var(--book-edge-3d-gap) * 3));
    opacity: 0.55;
}

/* Only show left edge on turned pages */
.book-page--turned:not(.book-page--turning) .book-page-edge-left {
    display: block;
}

.book-page:not(.book-page--turned) .book-page-edge-left,
.book-page--turning .book-page-edge-left {
    display: none;
}

.book-page--turned:not(.book-page--turning) .book-page-edge-left {
    opacity: calc(1 - var(--stack-position, 0) * 0.35);
}

/* Idle Z-depth stacking — use box-shadow only; actual translateZ on resting pages
   breaks the 3D compositing (pages become invisible within the perspective context).
   The box-shadow page-stack thickness (Phase 1A above) provides the visual depth. */

/* ─── Spine Crease — permanent left-edge shadow simulating binding (Phase 1C) ─── */
.book-page-spine-crease {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 20px;
    background: linear-gradient(to right,
        rgba(0, 0, 0, 0.12) 0%,
        rgba(0, 0, 0, 0.06) 30%,
        rgba(0, 0, 0, 0.02) 60%,
        transparent 100%);
    pointer-events: none;
    z-index: 4;
}

/* ─── Ambient right-edge glow on unturned pages ─── */
.book-page:not(.book-page--turned):not(.book-page--turning) .book-page-front::before {
    content: '';
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: var(--book-ambient-edge);
    background: linear-gradient(to left,
        rgba(212, 175, 55, 0.06) 0%,
        rgba(212, 175, 55, 0.02) 40%,
        transparent 100%);
    z-index: 4;
    pointer-events: none;
    transition: opacity 0.6s ease;
}

/* ─── Spine glow element — golden light at the binding ─── */
.book-page-spine-glow {
    position: absolute;
    /* Clamped to 0 minimum to prevent glow seeping beyond page bounds at mid-turn.
       The blur filter provides the soft falloff that previously relied on negative offset. */
    left: max(0px, var(--book-spine-offset));
    top: 5%;
    bottom: 5%;
    width: var(--book-spine-width);
    background: radial-gradient(ellipse at center,
        rgba(212, 175, 55, 0) 0%,
        rgba(212, 175, 55, 0) 100%);
    pointer-events: none;
    z-index: 5;
    opacity: 0;
    filter: blur(var(--book-spine-blur));
    transition: opacity 0.3s ease;
    /* Prevent glow from leaking through to back face in preserve-3d context */
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

.book-page--turning .book-page-spine-glow {
    opacity: var(--book-turn-spine-glow-intensity, 1);
    animation: spineGlowPulse var(--book-page-turn-duration) ease-in-out forwards;
}

@keyframes spineGlowPulse {
    0% {
        opacity: 0;
        background: radial-gradient(ellipse at center,
            rgba(212, 175, 55, 0) 0%,
            transparent 100%);
    }
    15% {
        opacity: 0.7;
        background: radial-gradient(ellipse at center,
            rgba(212, 175, 55, 0.25) 0%,
            rgba(212, 175, 55, 0.08) 50%,
            transparent 100%);
    }
    45% {
        opacity: 1;
        background: radial-gradient(ellipse at center,
            rgba(212, 175, 55, 0.35) 0%,
            rgba(212, 175, 55, 0.12) 50%,
            transparent 100%);
    }
    70% {
        opacity: 0.5;
        background: radial-gradient(ellipse at center,
            rgba(212, 175, 55, 0.15) 0%,
            rgba(212, 175, 55, 0.04) 50%,
            transparent 100%);
    }
    100% {
        opacity: 0;
        background: radial-gradient(ellipse at center,
            rgba(212, 175, 55, 0) 0%,
            transparent 100%);
    }
}

/* ─── Global spine light on the viewport ─── */
.book-spine-light {
    position: absolute;
    left: var(--book-light-offset);
    top: 0;
    bottom: 0;
    width: var(--book-light-width);
    background: linear-gradient(to right,
        rgba(212, 175, 55, 0) 0%,
        rgba(212, 175, 55, 0) 100%);
    pointer-events: none;
    z-index: 5;
    opacity: 0;
    filter: blur(var(--book-light-blur));
    transition: opacity 0.4s ease;
}

.book-viewport.book-viewport--turning .book-spine-light {
    opacity: 1;
    animation: spineAmbient 1.2s ease-in-out forwards;
}

@keyframes spineAmbient {
    0%   { opacity: 0; background: linear-gradient(to right, rgba(212,175,55,0), transparent); }
    30%  { opacity: 1; background: linear-gradient(to right, rgba(212,175,55,0.18) 0%, rgba(212,175,55,0.04) 60%, transparent); }
    60%  { opacity: 0.7; background: linear-gradient(to right, rgba(212,175,55,0.10) 0%, transparent 50%); }
    100% { opacity: 0; background: linear-gradient(to right, rgba(212,175,55,0), transparent); }
}

/* ─── Permanent Spine Shadow — central darkening where pages meet binding (Phase 5A) ─── */
.book-viewport::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to right,
        rgba(0, 0, 0, 0.15) 0%,
        rgba(0, 0, 0, 0.10) 1.5%,
        rgba(255, 255, 255, 0.04) 2%,  /* Highlight ridge — paper catching light at gutter edge */
        rgba(0, 0, 0, 0.06) 4%,
        transparent 12%,
        transparent 90%,
        rgba(0, 0, 0, 0.03) 96%,
        rgba(0, 0, 0, 0.06) 100%);
    pointer-events: none;
    z-index: 6;
}

/* ─── Enhanced Turn-Time Spine Bloom (Phase 5B) ─── */
.book-viewport.book-viewport--turning::before {
    content: '';
    position: absolute;
    left: -2%;
    top: 5%;
    bottom: 5%;
    width: 10%;
    background: radial-gradient(ellipse at center,
        rgba(212, 175, 55, 0.08) 0%,
        rgba(212, 175, 55, 0.03) 50%,
        transparent 100%);
    pointer-events: none;
    z-index: 5;
    animation: spineBloom 1.2s ease-in-out forwards;
}

@keyframes spineBloom {
    0%   { opacity: 0; filter: blur(4px); }
    30%  { opacity: 1; filter: blur(8px); }
    60%  { opacity: 0.6; filter: blur(6px); }
    100% { opacity: 0; filter: blur(4px); }
}

/* ─── Page edge — visible paper thickness during turn ─── */
.book-page-edge {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: var(--book-edge-width-base);
    background: linear-gradient(to right,
        #e8e0d0 0%,
        #f0ebe3 40%,
        #d8d0c2 100%);
    transform: translateX(-3px);
    opacity: 0;
    pointer-events: none;
    z-index: 11;
    box-shadow: -1px 0 3px rgba(0, 0, 0, 0.08);
    /* Prevent edge from leaking through to back face in preserve-3d context */
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

.book-page--turning .book-page-edge {
    animation: pageEdgeShow var(--book-page-turn-duration) ease-in-out forwards;
}

@keyframes pageEdgeShow {
    0%   { opacity: 0; width: 2px; }
    15%  { opacity: 0.6; width: calc(1px * var(--book-turn-edge-thickness, 4) * 0.75); }
    40%  { opacity: 1; width: calc(1px * var(--book-turn-edge-thickness, 4)); }
    60%  { opacity: 1; width: calc(1px * var(--book-turn-edge-thickness, 4)); }
    85%  { opacity: 0.4; width: calc(1px * var(--book-turn-edge-thickness, 4) * 0.75); }
    100% { opacity: 0; width: 2px; }
}

/* ─── Curl shadow on front face — curved gradient following the bend ─── */
.book-page-curl-shadow {
    position: absolute;
    inset: 0;
    z-index: 9;
    pointer-events: none;
    opacity: 0;
}

.book-page--turning-forward .book-page-curl-shadow {
    animation: curlShadowForward var(--book-page-turn-duration) cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
    --curl-shadow-scale: var(--book-turn-curl-shadow-max, 0.35);
}

@keyframes curlShadowForward {
    /* Start diagonal (corner peel) and transition to horizontal as page swings */
    0% {
        opacity: 0;
        background: linear-gradient(135deg, transparent 55%, rgba(0,0,0,0.06) 75%, rgba(0,0,0,0.15) 92%, rgba(0,0,0,0.25) 100%);
    }
    6% {
        opacity: 0.8;
        background: linear-gradient(130deg, transparent 45%, rgba(0,0,0,0.08) 65%, rgba(0,0,0,0.2) 88%, rgba(0,0,0,0.32) 100%);
    }
    15% {
        opacity: 1;
        background: linear-gradient(120deg, transparent 35%, rgba(0,0,0,0.06) 55%, rgba(0,0,0,0.18) 80%, rgba(0,0,0,0.35) 100%);
    }
    30% {
        opacity: 1;
        background: linear-gradient(108deg, transparent 25%, rgba(0,0,0,0.05) 45%, rgba(0,0,0,0.16) 72%, rgba(0,0,0,0.32) 100%);
    }
    48% {
        opacity: 0.9;
        background: linear-gradient(95deg, transparent 10%, rgba(0,0,0,0.06) 35%, rgba(0,0,0,0.2) 68%, rgba(0,0,0,0.38) 100%);
    }
    65% {
        opacity: 0.5;
        background: linear-gradient(90deg, rgba(0,0,0,0.08) 0%, rgba(0,0,0,0.14) 40%, rgba(0,0,0,0.22) 80%, rgba(0,0,0,0.3) 100%);
    }
    85% {
        opacity: 0.15;
    }
    100% {
        opacity: 0;
    }
}

.book-page--turning-backward .book-page-curl-shadow {
    animation: curlShadowBackward var(--book-page-turn-duration) cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
}

@keyframes curlShadowBackward {
    /* Start diagonal (corner peel from top-left) and transition to horizontal */
    0% {
        opacity: 0;
        background: linear-gradient(315deg, transparent 55%, rgba(0,0,0,0.06) 75%, rgba(0,0,0,0.15) 92%, rgba(0,0,0,0.25) 100%);
    }
    6% {
        opacity: 0.7;
        background: linear-gradient(310deg, transparent 45%, rgba(0,0,0,0.08) 65%, rgba(0,0,0,0.2) 88%, rgba(0,0,0,0.3) 100%);
    }
    15% {
        opacity: 1;
        background: linear-gradient(295deg, transparent 35%, rgba(0,0,0,0.06) 55%, rgba(0,0,0,0.18) 80%, rgba(0,0,0,0.32) 100%);
    }
    30% {
        opacity: 1;
        background: linear-gradient(280deg, transparent 25%, rgba(0,0,0,0.05) 45%, rgba(0,0,0,0.16) 72%, rgba(0,0,0,0.3) 100%);
    }
    48% {
        opacity: 0.8;
        background: linear-gradient(270deg, transparent 15%, rgba(0,0,0,0.06) 38%, rgba(0,0,0,0.18) 68%, rgba(0,0,0,0.3) 100%);
    }
    65% {
        opacity: 0.4;
    }
    85% {
        opacity: 0.12;
    }
    100% {
        opacity: 0;
    }
}

/* ─── Golden dust motes — float upward during page turns ─── */
.book-page-particles {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 103;
    overflow: hidden;
    /* Prevent particle container from leaking through to back face in preserve-3d context */
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

.book-mote {
    position: absolute;
    width: var(--book-mote-md);
    height: var(--book-mote-md);
    border-radius: 50%;
    background: radial-gradient(circle, var(--book-turn-mote-color, rgba(212,175,55,0.9)) 0%, rgba(212,175,55,0.3) 60%, transparent 100%);
    opacity: 0;
    pointer-events: none;
}

/* Motes only animate during turns */
.book-page--turning .book-mote {
    animation: moteDrift var(--book-page-turn-duration) ease-out forwards;
}

.book-mote--1 { left: 15%; bottom: 30%; animation-delay: 0.05s; --mote-wind-1: 3px; --mote-wind-2: -8px; --mote-wind-3: 12px; --mote-wind-4: -5px; --mote-wind-5: 8px; --mote-spin-1: 15deg; --mote-spin-2: 45deg; --mote-spin-3: 90deg; --mote-spin-4: 135deg; --mote-spin-5: 180deg; }
.book-mote--2 { left: 35%; bottom: 50%; width: var(--book-mote-sm); height: var(--book-mote-sm); animation-delay: 0.12s; --mote-wind-1: -5px; --mote-wind-2: 10px; --mote-wind-3: -3px; --mote-wind-4: 8px; --mote-wind-5: -12px; --mote-spin-1: -20deg; --mote-spin-2: -50deg; --mote-spin-3: -85deg; --mote-spin-4: -120deg; --mote-spin-5: -160deg; }
.book-mote--3 { left: 55%; bottom: 20%; animation-delay: 0.2s; --mote-wind-1: 6px; --mote-wind-2: -4px; --mote-wind-3: 9px; --mote-wind-4: -7px; --mote-wind-5: 5px; --mote-spin-1: 10deg; --mote-spin-2: 30deg; --mote-spin-3: 65deg; --mote-spin-4: 100deg; --mote-spin-5: 140deg; }
.book-mote--4 { left: 70%; bottom: 65%; width: var(--book-mote-lg); height: var(--book-mote-lg); animation-delay: 0.08s; --mote-wind-1: -8px; --mote-wind-2: 15px; --mote-wind-3: -10px; --mote-wind-4: 12px; --mote-wind-5: -6px; --mote-spin-1: -25deg; --mote-spin-2: -60deg; --mote-spin-3: -95deg; --mote-spin-4: -130deg; --mote-spin-5: -170deg; }
.book-mote--5 { left: 25%; bottom: 75%; width: var(--book-mote-sm); height: var(--book-mote-sm); animation-delay: 0.16s; --mote-wind-1: 4px; --mote-wind-2: -6px; --mote-wind-3: 8px; --mote-wind-4: -4px; --mote-wind-5: 10px; --mote-spin-1: 18deg; --mote-spin-2: 40deg; --mote-spin-3: 75deg; --mote-spin-4: 110deg; --mote-spin-5: 155deg; }
.book-mote--6 { left: 45%; bottom: 40%; animation-delay: 0.24s; --mote-wind-1: -3px; --mote-wind-2: 7px; --mote-wind-3: -6px; --mote-wind-4: 9px; --mote-wind-5: -4px; --mote-spin-1: -12deg; --mote-spin-2: -35deg; --mote-spin-3: -70deg; --mote-spin-4: -105deg; --mote-spin-5: -145deg; }
.book-mote--7 { left: 80%; bottom: 55%; width: var(--book-mote-sm); height: var(--book-mote-sm); animation-delay: 0.3s; --mote-wind-1: 7px; --mote-wind-2: -12px; --mote-wind-3: 14px; --mote-wind-4: -8px; --mote-wind-5: 6px; --mote-spin-1: 22deg; --mote-spin-2: 55deg; --mote-spin-3: 80deg; --mote-spin-4: 115deg; --mote-spin-5: 165deg; }
.book-mote--8 { left: 60%; bottom: 10%; width: var(--book-mote-lg); height: var(--book-mote-lg); animation-delay: 0.1s; --mote-wind-1: -6px; --mote-wind-2: 8px; --mote-wind-3: -11px; --mote-wind-4: 7px; --mote-wind-5: -9px; --mote-spin-1: -18deg; --mote-spin-2: -42deg; --mote-spin-3: -78deg; --mote-spin-4: -112deg; --mote-spin-5: -150deg; }
.book-mote--9  { left: 20%; bottom: 15%; width: var(--book-mote-sm); height: var(--book-mote-sm); animation-delay: 0.18s; --mote-wind-1: 5px; --mote-wind-2: -9px; --mote-wind-3: 7px; --mote-wind-4: -6px; --mote-wind-5: 11px; --mote-spin-1: 14deg; --mote-spin-2: 38deg; --mote-spin-3: 72deg; --mote-spin-4: 108deg; --mote-spin-5: 148deg; border-radius: 50% 35%; }
.book-mote--10 { left: 50%; bottom: 80%; animation-delay: 0.26s; --mote-wind-1: -4px; --mote-wind-2: 11px; --mote-wind-3: -8px; --mote-wind-4: 5px; --mote-wind-5: -7px; --mote-spin-1: -16deg; --mote-spin-2: -48deg; --mote-spin-3: -82deg; --mote-spin-4: -118deg; --mote-spin-5: -155deg; }
.book-mote--11 { left: 75%; bottom: 35%; width: var(--book-mote-lg); height: var(--book-mote-lg); animation-delay: 0.14s; --mote-wind-1: 9px; --mote-wind-2: -7px; --mote-wind-3: 13px; --mote-wind-4: -10px; --mote-wind-5: 4px; --mote-spin-1: 20deg; --mote-spin-2: 52deg; --mote-spin-3: 88deg; --mote-spin-4: 122deg; --mote-spin-5: 168deg; border-radius: 35% 50%; }
.book-mote--12 { left: 40%; bottom: 60%; width: var(--book-mote-sm); height: var(--book-mote-sm); animation-delay: 0.22s; --mote-wind-1: -7px; --mote-wind-2: 6px; --mote-wind-3: -5px; --mote-wind-4: 11px; --mote-wind-5: -8px; --mote-spin-1: -22deg; --mote-spin-2: -55deg; --mote-spin-3: -88deg; --mote-spin-4: -125deg; --mote-spin-5: -162deg; }

@keyframes moteDrift {
    0% {
        opacity: 0;
        transform: translateY(0) translateX(0) scale(0.2) rotate(0deg);
    }
    8% {
        opacity: var(--book-turn-mote-opacity, 0.9);
        transform: translateY(-8px) translateX(var(--mote-wind-1, 3px)) scale(1) rotate(var(--mote-spin-1, 15deg));
    }
    25% {
        opacity: var(--book-turn-mote-opacity, 0.9);
        transform: translateY(-35px) translateX(var(--mote-wind-2, -8px)) scale(1.1) rotate(var(--mote-spin-2, 45deg));
    }
    45% {
        opacity: calc(var(--book-turn-mote-opacity, 0.9) * 0.7);
        transform: translateY(-65px) translateX(var(--mote-wind-3, 12px)) scale(0.95) rotate(var(--mote-spin-3, 90deg));
    }
    65% {
        opacity: calc(var(--book-turn-mote-opacity, 0.9) * 0.45);
        transform: translateY(-95px) translateX(var(--mote-wind-4, -5px)) scale(0.8) rotate(var(--mote-spin-4, 135deg));
    }
    100% {
        opacity: 0;
        transform: translateY(calc(-1px * var(--book-turn-mote-travel, 140))) translateX(var(--mote-wind-5, 8px)) scale(0.3) rotate(var(--mote-spin-5, 180deg));
    }
}

/* ═══════════════════════════════════════════════════════════════
   CINEMATIC PAGE FOLD KEYFRAMES
   Pure rotateY page turn — no rotateX/skewY which cause spine detachment
   under perspective projection. Paper curl illusion comes from shadow/light
   overlays (curl-shadow, light-catch, fold-crease), not page-level tilt.
   translateZ(2px) applied BEFORE rotateY operates in WORLD space, keeping
   the hinge point fixed at a constant Z offset. Overshoot at 92% adds
   natural follow-through before settling. rotateZ flutter at landing.
   Custom easing: cubic-bezier(0.22, 0.68, 0.18, 1) — organic settle.
   ═══════════════════════════════════════════════════════════════ */

@keyframes pageFoldForward {
    /* Pure rotateY page turn — no rotateX/skewY which cause spine detachment
       under perspective projection (confirmed by CodePen reference analysis).
       Paper curl illusion comes from shadow/light overlays, not page-level tilt.
       translateZ is applied BEFORE rotateY so it operates in WORLD space,
       keeping the hinge point at a constant Z offset from the spine. A constant
       2px lift prevents coplanar z-fighting with sibling pages in the preserve-3d
       context (turned pages are pushed to Z=-3px, giving a 5px total gap).
       The old approach (translateZ AFTER rotateY) shifted the hinge along the
       page's local Z axis, which rotates with the page — at -90deg the hinge
       moved ~8px sideways away from the spine.
       B7 fix: No backward tilt at start (was rotateY(1deg) at 3% — the positive
       rotation pushed the far edge ~10px behind the stacked pages, causing the
       right page to clip through the page behind it at the start of the turn).
       B7 fix: Reduced overshoot at end (was -184deg at 92% — the 4deg overshoot
       pushed the far edge ~42px behind turned pages at Z=-3px, causing the page
       to clip through the left page of the destination spread). */
    0% {
        transform: translateZ(2px) rotateY(0deg);
    }
    3% {
        transform: translateZ(2px) rotateY(-2deg);
    }
    10% {
        transform: translateZ(2px) rotateY(-14deg);
    }
    18% {
        transform: translateZ(2px) rotateY(-32deg);
    }
    25% {
        transform: translateZ(2px) rotateY(-50deg);
    }
    38% {
        transform: translateZ(2px) rotateY(-80deg);
    }
    45% {
        transform: translateZ(2px) rotateY(-88deg);
    }
    52% {
        transform: translateZ(2px) rotateY(-100deg);
    }
    62% {
        transform: translateZ(2px) rotateY(-126deg);
    }
    74% {
        transform: translateZ(2px) rotateY(-155deg);
    }
    85% {
        transform: translateZ(2px) rotateY(-174deg);
    }
    /* Subtle overshoot — kept under 0.5deg so the far edge stays within the
       turned-page Z gap. At 0.5deg on a 600px page, far-edge Z ≈ 2 − 600×sin(0.5°)
       ≈ −3.2px, just in front of turned pages pushed to Z=−5px during active turns. */
    92% {
        transform: translateZ(2px) rotateY(-180.5deg);
    }
    96% {
        transform: translateZ(2px) rotateY(-180.15deg);
    }
    100% {
        transform: translateZ(2px) rotateY(-180deg);
    }
}

@keyframes pageFoldBackward {
    /* B7 fix: Symmetric fixes — no backward tilt at start, reduced overshoot at end. */
    0% {
        transform: translateZ(2px) rotateY(-180deg);
    }
    3% {
        transform: translateZ(2px) rotateY(-178deg);
    }
    10% {
        transform: translateZ(2px) rotateY(-166deg);
    }
    18% {
        transform: translateZ(2px) rotateY(-148deg);
    }
    25% {
        transform: translateZ(2px) rotateY(-130deg);
    }
    38% {
        transform: translateZ(2px) rotateY(-100deg);
    }
    45% {
        transform: translateZ(2px) rotateY(-92deg);
    }
    52% {
        transform: translateZ(2px) rotateY(-80deg);
    }
    62% {
        transform: translateZ(2px) rotateY(-54deg);
    }
    74% {
        transform: translateZ(2px) rotateY(-25deg);
    }
    85% {
        transform: translateZ(2px) rotateY(-6deg);
    }
    92% {
        transform: translateZ(2px) rotateY(0.5deg);
    }
    96% {
        transform: translateZ(2px) rotateY(0.15deg);
    }
    100% {
        transform: translateZ(2px) rotateY(0deg);
    }
}

/* ─── Fold Crease Shadow (sweeps across front face during turn) ───
   Multi-band gradient: dark edge shadow, highlight ridge, ambient.
   The gradient is oversized (320%) and pans across the face to simulate
   a moving crease as the paper curves through 3D space. */

.book-page--turning-forward .book-page-front::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 10;
    pointer-events: none;
    background: linear-gradient(to left,
        rgba(0, 0, 0, 0.28) 0%,
        rgba(0, 0, 0, 0.18) 5%,
        rgba(0, 0, 0, 0.08) 10%,
        rgba(255, 255, 255, 0.06) 15%,
        rgba(255, 255, 255, 0.03) 18%,
        transparent 30%
    );
    background-size: 320% 320%;
    background-position: 100% 100%;
    animation: foldCreaseSweep var(--book-page-turn-duration) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes foldCreaseSweep {
    /* Diagonal sweep: starts from bottom-right corner, straightens as page swings */
    0% {
        background-position: 105% 105%;
        opacity: 0;
    }
    5% {
        opacity: 0.6;
    }
    12% {
        opacity: 1;
        background-position: 92% 85%;
    }
    30% {
        background-position: 70% 55%;
        opacity: 1;
    }
    50% {
        background-position: 48% 40%;
        opacity: 0.9;
    }
    70% {
        background-position: 28% 30%;
        opacity: 0.5;
    }
    88% {
        opacity: 0.12;
    }
    100% {
        background-position: 0% 20%;
        opacity: 0;
    }
}

/* ─── Back-face fold highlight ───
   Light catching the inside of the paper curl — warm golden tint. */

.book-page--turning-forward .book-page-back::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: var(--book-highlight-width);
    background: linear-gradient(to right,
        rgba(212, 175, 55, 0.08) 0%,
        rgba(255, 255, 255, 0.18) 15%,
        rgba(255, 255, 255, 0.06) 50%,
        transparent 100%);
    z-index: 10;
    pointer-events: none;
    animation: foldHighlight var(--book-page-turn-duration) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes foldHighlight {
    0%   { opacity: 0; }
    25%  { opacity: 0; }
    40%  { opacity: 0.8; }
    55%  { opacity: 1; }
    75%  { opacity: 0.5; }
    100% { opacity: 0; }
}

/* ─── Backward turn: fold crease on back face ─── */

.book-page--turning-backward .book-page-back::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 10;
    pointer-events: none;
    background: linear-gradient(to right,
        rgba(0, 0, 0, 0.28) 0%,
        rgba(0, 0, 0, 0.18) 5%,
        rgba(0, 0, 0, 0.08) 10%,
        rgba(255, 255, 255, 0.06) 15%,
        rgba(255, 255, 255, 0.03) 18%,
        transparent 30%
    );
    background-size: 320% 320%;
    background-position: 100% 0%;
    animation: foldCreaseSweepBack var(--book-page-turn-duration) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes foldCreaseSweepBack {
    /* Diagonal sweep: starts from top-left corner, straightens as page swings */
    0% {
        background-position: 105% 0%;
        opacity: 0;
    }
    5% {
        opacity: 0.6;
    }
    12% {
        opacity: 1;
        background-position: 92% 18%;
    }
    30% {
        background-position: 70% 40%;
        opacity: 1;
    }
    50% {
        background-position: 48% 52%;
        opacity: 0.9;
    }
    70% {
        background-position: 28% 65%;
        opacity: 0.5;
    }
    88% {
        opacity: 0.12;
    }
    100% {
        background-position: 0% 75%;
        opacity: 0;
    }
}

/* Backward: warm highlight on the front face as it's revealed */
.book-page--turning-backward .book-page-front::before {
    content: '';
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: var(--book-highlight-width);
    background: linear-gradient(to left,
        rgba(212, 175, 55, 0.08) 0%,
        rgba(255, 255, 255, 0.18) 15%,
        rgba(255, 255, 255, 0.06) 50%,
        transparent 100%);
    z-index: 10;
    pointer-events: none;
    animation: foldHighlight var(--book-page-turn-duration) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* ─── Dynamic shadow on the page being revealed underneath ───
   Animated shadow that grows as the page lifts and fades as it lands.
   Uses an inset box-shadow plus a gradient overlay for depth. */
.book-page--turning + .book-page:not(.book-page--turned) .book-page-front {
    animation: underPageShadow var(--book-page-turn-duration) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes underPageShadow {
    /* Diagonal shadow that starts from corner and expands as page lifts */
    0% {
        box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0);
    }
    10% {
        box-shadow: inset 50px 40px 50px -20px rgba(0, 0, 0, 0.06);
    }
    25% {
        box-shadow: inset 120px 50px 100px -25px rgba(0, 0, 0, 0.14);
    }
    40% {
        box-shadow: inset 180px 30px 130px -35px rgba(0, 0, 0, 0.2);
    }
    55% {
        box-shadow: inset 210px 15px 140px -40px rgba(0, 0, 0, 0.18);
    }
    72% {
        box-shadow: inset 130px 8px 90px -30px rgba(0, 0, 0, 0.1);
    }
    90% {
        box-shadow: inset 35px 0 35px -10px rgba(0, 0, 0, 0.03);
    }
    100% {
        box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0);
    }
}

/* ─── Drop shadow from the turning page onto the stack ─── */
.book-page--turning-forward {
    animation: pageFoldForward var(--book-page-turn-duration) cubic-bezier(0.22, 0.68, 0.18, 1) forwards;
}

.book-page--turning-backward {
    animation: pageFoldBackward var(--book-page-turn-duration) cubic-bezier(0.22, 0.68, 0.18, 1) forwards;
}

/* Face-level box-shadow replaces turnDropShadow (filter breaks preserve-3d / backface-visibility) */
.book-page--turning-forward > .book-page-front {
    animation: turnShadowOnFront var(--book-page-turn-duration) cubic-bezier(0.22, 0.68, 0.18, 1) forwards;
}
.book-page--turning-backward > .book-page-back {
    animation: turnShadowOnBack var(--book-page-turn-duration) cubic-bezier(0.22, 0.68, 0.18, 1) forwards;
}

/* turnDropShadow removed — filter breaks preserve-3d and backface-visibility.
   Replaced by turnShadowOnFront / turnShadowOnBack (box-shadow on child faces). */
/*
@keyframes turnDropShadow {
    0%   { filter: drop-shadow(0 0 0 rgba(0,0,0,0)) brightness(1); }
    20%  { filter: drop-shadow(-12px 6px 18px rgba(0,0,0,0.2)) brightness(1); }
    42%  { filter: drop-shadow(-18px 10px 28px rgba(0,0,0,0.28)) brightness(1.02); }
    45%  { filter: drop-shadow(-18px 10px 28px rgba(0,0,0,0.28)) brightness(1.03); }
    52%  { filter: drop-shadow(-16px 9px 24px rgba(0,0,0,0.24)) brightness(1); }
    65%  { filter: drop-shadow(-14px 7px 20px rgba(0,0,0,0.18)) brightness(1); }
    85%  { filter: drop-shadow(-4px 2px 8px rgba(0,0,0,0.06)) brightness(1); }
    100% { filter: drop-shadow(0 0 0 rgba(0,0,0,0)) brightness(1); }
}
*/

@keyframes turnShadowOnFront {
    0%   { box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
    15%  { box-shadow: -8px 4px 12px rgba(0, 0, 0, 0.10); }
    25%  { box-shadow: -16px 8px 22px rgba(0, 0, 0, 0.20), -4px 0 10px rgba(212, 175, 55, 0.04); }
    35%  { box-shadow: -20px 12px 28px rgba(0, 0, 0, 0.25), -5px 0 14px rgba(212, 175, 55, 0.06); }
    45%  { box-shadow: -18px 10px 24px rgba(0, 0, 0, 0.20); }
    55%  { box-shadow: -14px 8px 20px rgba(0, 0, 0, 0.14); }
    70%  { box-shadow: -8px 5px 14px rgba(0, 0, 0, 0.08); }
    85%  { box-shadow: -3px 2px 6px rgba(0, 0, 0, 0.03); }
    100% { box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
}
@keyframes turnShadowOnBack {
    0%   { box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
    15%  { box-shadow: 8px 4px 12px rgba(0, 0, 0, 0.10); }
    25%  { box-shadow: 16px 8px 22px rgba(0, 0, 0, 0.20), 4px 0 10px rgba(212, 175, 55, 0.04); }
    35%  { box-shadow: 20px 12px 28px rgba(0, 0, 0, 0.25), 5px 0 14px rgba(212, 175, 55, 0.06); }
    45%  { box-shadow: 18px 10px 24px rgba(0, 0, 0, 0.20); }
    55%  { box-shadow: 14px 8px 20px rgba(0, 0, 0, 0.14); }
    70%  { box-shadow: 8px 5px 14px rgba(0, 0, 0, 0.08); }
    85%  { box-shadow: 3px 2px 6px rgba(0, 0, 0, 0.03); }
    100% { box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
}

/* ─── Page Corner Fold Effect ───
   Hover to peek, click to turn. A diagonal gradient inside a growing
   pseudo-element simulates a folded paper corner at the bottom edges.
   States: default (subtle hint) → hover (fold reveals) → active (pull) → turn. */

.book-page-corner {
    position: absolute;
    z-index: 5;
    width: var(--book-corner-zone);
    height: var(--book-corner-zone);
    cursor: pointer;
}

.book-page-corner--next {
    bottom: 0;
    right: 0;
}

/* ── Previous page fold corners — on .book-page-back (left page of spread) ──
   Positioned at left:0 in CSS space. Although the back face has rotateY(180deg),
   the parent .book-page also has rotateY(-180deg) when turned — the double
   rotation cancels to 0deg, so the CSS coordinate system is NOT mirrored.
   left:0 = the outer-left edge of the left page (book edge), which is where
   backward fold corners should appear. */
.book-page-corner--prev-top {
    top: 0;
    left: 0;
}

.book-page-corner--prev-bottom {
    bottom: 0;
    left: 0;
}

/* ── Next page fold (bottom-right) ──
   Enhanced multi-layer gradient: crease line, paper peek, fold shadow, highlight. */
.book-page-corner--next::before {
    content: '';
    position: absolute;
    bottom: 0;
    right: 0;
    border-radius: 5px 0 0 0;
    background: linear-gradient(135deg,
        transparent 36%,
        rgba(0, 0, 0, 0.03) 39%,
        rgba(139, 119, 75, 0.18) 42%,
        rgba(255, 255, 255, 0.15) 44%,
        rgba(250, 245, 240, 0.92) 47%,
        rgba(180, 160, 130, 0.20) 50%,
        rgba(250, 245, 240, 0.96) 53%,
        rgba(255, 255, 255, 0.10) 56%,
        var(--book-page-cream, #faf5f0) 62%,
        #f0e8dc 100%);
    width: var(--book-corner-default);
    height: var(--book-corner-default);
    transition: width 0.5s ease, height 0.5s ease, box-shadow 0.5s ease, border-radius 0.5s ease;
    box-shadow: -2px -2px 4px rgba(0, 0, 0, 0.04);
    pointer-events: none;
}

.book-page-corner--next:hover::before {
    width: var(--book-corner-hover);
    height: var(--book-corner-hover);
    box-shadow: -3px -3px 8px rgba(0, 0, 0, 0.1),
                -1px -1px 3px rgba(0, 0, 0, 0.05);
    animation: cornerBreathe 2s ease-in-out infinite;
}

.book-page-corner--next.book-page-corner--active::before {
    width: var(--book-corner-active);
    height: var(--book-corner-active);
    box-shadow: -5px -5px 15px rgba(0, 0, 0, 0.15),
                -2px -2px 5px rgba(0, 0, 0, 0.08);
    transition-duration: 0.2s;
    transition-timing-function: ease-out;
}

/* ── Next page fold (top-right) ──
   Mirror of bottom-right corner, positioned at top-right. */
.book-page-corner--next-top {
    bottom: auto;
    top: 0;
    right: 0;
}

.book-page-corner--next-top::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    border-radius: 0 0 0 5px;
    background: linear-gradient(225deg,
        transparent 36%,
        rgba(0, 0, 0, 0.03) 39%,
        rgba(139, 119, 75, 0.18) 42%,
        rgba(255, 255, 255, 0.15) 44%,
        rgba(250, 245, 240, 0.92) 47%,
        rgba(180, 160, 130, 0.20) 50%,
        rgba(250, 245, 240, 0.96) 53%,
        rgba(255, 255, 255, 0.10) 56%,
        var(--book-page-cream, #faf5f0) 62%,
        #f0e8dc 100%);
    width: var(--book-corner-default);
    height: var(--book-corner-default);
    transition: width 0.5s ease, height 0.5s ease, box-shadow 0.5s ease, border-radius 0.5s ease;
    box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.04);
    pointer-events: none;
}

.book-page-corner--next-top:hover::before {
    width: var(--book-corner-hover);
    height: var(--book-corner-hover);
    box-shadow: -3px 3px 8px rgba(0, 0, 0, 0.1),
                -1px 1px 3px rgba(0, 0, 0, 0.05);
    animation: cornerBreathe 2s ease-in-out infinite;
}

.book-page-corner--next-top.book-page-corner--active::before {
    width: var(--book-corner-active);
    height: var(--book-corner-active);
    box-shadow: -5px 5px 15px rgba(0, 0, 0, 0.15),
                -2px 2px 5px rgba(0, 0, 0, 0.08);
    transition-duration: 0.2s;
    transition-timing-function: ease-out;
}

/* Top-right corner triggers top-right peel shadow */
.book-page-corner--next-top:hover ~ .book-page-peel-shadow--tr {
    width: calc(var(--book-peel-max) * 0.6);
    height: calc(var(--book-peel-max) * 0.6);
}

.book-page-corner--next-top:hover ~ .book-page-peel-underpage--tr {
    width: calc(var(--book-peel-max) * 0.5);
    height: calc(var(--book-peel-max) * 0.5);
}

/* Hide top-right corner on turned pages */
.book-page--turned .book-page-corner--next-top {
    display: none;
}

/* ── Previous page fold (top-left) ──
   Fold peels from the top-left corner of the left page (outer edge).
   Gradient 315deg goes from bottom-right toward top-left, with paper peek at corner.
   Net rotation of the back face is 0deg (parent -180 + back +180), so CSS
   coordinates map directly to visual coordinates. */
.book-page-corner--prev-top::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    border-radius: 0 0 5px 0;
    background: linear-gradient(315deg,
        transparent 36%,
        rgba(0, 0, 0, 0.03) 39%,
        rgba(139, 119, 75, 0.18) 42%,
        rgba(255, 255, 255, 0.15) 44%,
        rgba(250, 245, 240, 0.92) 47%,
        rgba(180, 160, 130, 0.20) 50%,
        rgba(250, 245, 240, 0.96) 53%,
        rgba(255, 255, 255, 0.10) 56%,
        var(--book-page-cream, #faf5f0) 62%,
        #f0e8dc 100%);
    width: var(--book-corner-default);
    height: var(--book-corner-default);
    transition: width 0.5s ease, height 0.5s ease, box-shadow 0.5s ease, border-radius 0.5s ease;
    box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.04);
    pointer-events: none;
}

.book-page-corner--prev-top:hover::before {
    width: var(--book-corner-hover);
    height: var(--book-corner-hover);
    box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.1),
                1px 1px 3px rgba(0, 0, 0, 0.05);
    animation: cornerBreathe 2s ease-in-out infinite;
}

.book-page-corner--prev-top.book-page-corner--active::before {
    width: var(--book-corner-active);
    height: var(--book-corner-active);
    box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.15),
                2px 2px 5px rgba(0, 0, 0, 0.08);
    transition-duration: 0.2s;
    transition-timing-function: ease-out;
}

/* ── Previous page fold (bottom-left) ──
   Fold peels from the bottom-left corner of the left page (outer edge).
   Gradient 45deg goes from top-right toward bottom-left, with paper peek at corner. */
.book-page-corner--prev-bottom::before {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    border-radius: 0 5px 0 0;
    background: linear-gradient(45deg,
        transparent 36%,
        rgba(0, 0, 0, 0.03) 39%,
        rgba(139, 119, 75, 0.18) 42%,
        rgba(255, 255, 255, 0.15) 44%,
        rgba(250, 245, 240, 0.92) 47%,
        rgba(180, 160, 130, 0.20) 50%,
        rgba(250, 245, 240, 0.96) 53%,
        rgba(255, 255, 255, 0.10) 56%,
        var(--book-page-cream, #faf5f0) 62%,
        #f0e8dc 100%);
    width: var(--book-corner-default);
    height: var(--book-corner-default);
    transition: width 0.5s ease, height 0.5s ease, box-shadow 0.5s ease, border-radius 0.5s ease;
    box-shadow: 2px -2px 4px rgba(0, 0, 0, 0.04);
    pointer-events: none;
}

.book-page-corner--prev-bottom:hover::before {
    width: var(--book-corner-hover);
    height: var(--book-corner-hover);
    box-shadow: 3px -3px 8px rgba(0, 0, 0, 0.1),
                1px -1px 3px rgba(0, 0, 0, 0.05);
    animation: cornerBreathe 2s ease-in-out infinite;
}

.book-page-corner--prev-bottom.book-page-corner--active::before {
    width: var(--book-corner-active);
    height: var(--book-corner-active);
    box-shadow: 5px -5px 15px rgba(0, 0, 0, 0.15),
                2px -2px 5px rgba(0, 0, 0, 0.08);
    transition-duration: 0.2s;
    transition-timing-function: ease-out;
}

/* Corner lift → page turn continuity */
.book-page--turning .book-page-corner,
.book-page--dragging .book-page-corner {
    pointer-events: none;
    animation: cornerLiftForward 0.3s ease-out forwards;
}

@keyframes cornerBreathe {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.06); }
}

@keyframes cornerLiftForward {
    0%   { opacity: 1; transform: scale(1); }
    40%  { opacity: 0.8; transform: scale(1.8); }
    100% { opacity: 0; transform: scale(2); }
}

/* Turned pages only show back face — front-face corners not needed */
.book-page--turned .book-page-corner--next {
    display: none;
}

/* Unturned pages only show front face — back-face prev corners not needed */
.book-page:not(.book-page--turned) .book-page-corner--prev-top,
.book-page:not(.book-page--turned) .book-page-corner--prev-bottom {
    display: none;
}

/* Previous corner peel shadow triggers (left-side, on back face) */
.book-page-corner--prev-top:hover ~ .book-page-peel-shadow--tl {
    width: calc(var(--book-peel-max) * 0.6);
    height: calc(var(--book-peel-max) * 0.6);
}

.book-page-corner--prev-top:hover ~ .book-page-peel-underpage--tl {
    width: calc(var(--book-peel-max) * 0.5);
    height: calc(var(--book-peel-max) * 0.5);
}

.book-page-corner--prev-bottom:hover ~ .book-page-peel-shadow--bl {
    width: calc(var(--book-peel-max) * 0.6);
    height: calc(var(--book-peel-max) * 0.6);
}

.book-page-corner--prev-bottom:hover ~ .book-page-peel-underpage--bl {
    width: calc(var(--book-peel-max) * 0.5);
    height: calc(var(--book-peel-max) * 0.5);
}

/* ── Close corners on flyleaf 4 back face (first spread left page) ──
   Only visible when the flyleaf is turned (book is open). Hidden by default
   because flyleaf back faces are only visible after the opening animation. */
.book-flyleaf-back > .book-page-corner--close {
    display: none;
}

.book-flyleaf--turned .book-flyleaf-back > .book-page-corner--close {
    display: block;
}

/* ── Corner fold responsive — clamp() handles sizing, no discrete overrides needed ── */

/* ─── Drag-to-Flip — interactive page drag state ───
   When a user clicks/drags near a page edge, the page tracks the pointer's
   horizontal position via inline rotateY set by JS. The CSS below handles
   the visual state: disabling CSS animation interference, providing edge
   cursors, and suppressing text selection during drag. */

.book-page--dragging {
    /* Override CSS animation/transition during drag — JS controls visuals directly */
    animation: none !important;
    transition: none !important;
    z-index: 200;
    pointer-events: none; /* Prevent internal content interaction while dragging */
}

/* Suppress transitions on child faces during drag — JS drives box-shadow inline */
.book-page--dragging > .book-page-front,
.book-page--dragging > .book-page-back {
    transition: none !important;
}

/* Enable JS-driven curl visuals during drag (normally only active during --turning) */
.book-page--dragging .book-page-curl-shadow,
.book-page--dragging .book-page-light-catch,
.book-page--dragging .book-page-edge {
    animation: none !important;
    /* JS sets opacity/background inline — just ensure no transition fights */
    transition: none !important;
}

.book-page--dragging .book-page-corner {
    opacity: 0;
    pointer-events: none;
}

.book-page--dragging .book-page-peel-shadow,
.book-page--dragging .book-page-peel-underpage,
.book-page--dragging .book-page-peel-shadow--tr,
.book-page--dragging .book-page-peel-underpage--tr,
.book-page--dragging .book-page-peel-shadow--tl,
.book-page--dragging .book-page-peel-underpage--tl,
.book-page--dragging .book-page-peel-shadow--bl,
.book-page--dragging .book-page-peel-underpage--bl {
    display: none;
}

/* ── Drag Fold Flap — the reflected back-face visible during 2D fold ──
   JS sets clip-path to the reflected polygon shape. The flap background
   mimics the page back face. drop-shadow creates a realistic fold shadow. */
.book-page-drag-flap {
    position: absolute;
    inset: 0;
    z-index: 8;
    display: none;
    pointer-events: none;
    filter: drop-shadow(-2px 0 8px rgba(0, 0, 0, 0.25));
    /* Back face appearance — matches .book-page-back gradient */
    background: linear-gradient(to left,
        #ebe3d6 0%,
        #f8f2ea 10%,
        #f8f2ea 88%,
        #ede5d8 100%);
    /* Prevent flap from leaking through in preserve-3d context */
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

/* ── Fold line gradient — oversized element rotated to align with fold crease ──
   50% is the crease center: dark shadow → bright highlight → ambient fade.
   JS positions this via translate + rotate to track the fold line. */
.book-page-drag-flap-gradient {
    position: absolute;
    width: 300%;
    height: 300%;
    left: -150%;
    top: -150%;
    pointer-events: none;
    background: linear-gradient(to right,
        transparent 49%,
        rgba(0, 0, 0, 0.25) 49.8%,
        rgba(0, 0, 0, 0.35) 50%,
        rgba(255, 255, 255, 0.6) 50.8%,
        rgba(255, 255, 255, 0.3) 51.5%,
        rgba(0, 0, 0, 0.06) 55%,
        transparent 62%
    );
}

/* Prevent text selection during drag */
.book-page-stack[style*="cursor: grabbing"] {
    user-select: none;
    -webkit-user-select: none;
}

/* ─── Book Navigation Arrows (removed — drag & corner fold replace arrows) ─── */
.book-nav-arrow {
    position: absolute;
    bottom: 12px;
    width: var(--book-nav-size);
    height: var(--book-nav-size);
    border: 1.5px solid rgba(212, 184, 150, 0.15);
    border-radius: 50%;
    background: rgba(250, 245, 240, 0.15);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    cursor: pointer;
    z-index: 200;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--color-text-dark);
    opacity: 0.2;
}

.book-nav-arrow svg {
    width: var(--book-nav-icon);
    height: var(--book-nav-icon);
}

.book-nav-arrow--prev { left: 12px; }
.book-nav-arrow--next { right: 12px; }

.book-nav-arrow:hover:not(:disabled) {
    background: rgba(212, 175, 55, 0.15);
    border-color: rgba(212, 175, 55, 0.4);
    transform: scale(1.08);
    opacity: 0.6;
}

.book-nav-arrow:disabled {
    opacity: 0.08;
    cursor: default;
    pointer-events: none;
}

/* ─── Page Indicator — italic serif page number (Phase 3E) ─── */
/* Global indicator replaced by per-page numbers inside BookPage faces */
.book-nav-indicator {
    display: none;
}

/* ─── Per-Page Number — attached to each page face, moves with 3D turns ─── */
.book-page-number {
    position: absolute;
    bottom: 16px;
    font-family: var(--font-heading);
    font-style: italic;
    font-size: clamp(0.65rem, 0.8vw, 0.85rem);
    color: rgba(44, 44, 44, 0.25);
    letter-spacing: 0.08em;
    pointer-events: none;
    z-index: 5;
}

.book-page-number--right {
    right: 28px;
}

.book-page-number--left {
    left: 28px;
}

/* ═══════════════════════════════════════════════════════════════
   RIBBON BOOKMARK NAVIGATION (Phase 3)
   Physical satin ribbon markers protruding from book top edge.
   Each ribbon: body (satin strip) + fold (crease at book edge)
   + tail (V-cut point) + label (vertical text).
   ═══════════════════════════════════════════════════════════════ */

/* Legacy flex container — kept for any remaining BookNavigator indicator usage */
.book-nav-ribbons {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%) translateY(-100%);
    display: flex;
    flex-direction: row;
    gap: var(--ribbon-gap);
    z-index: 205;
    padding-bottom: 2px;
}

/* Per-page ribbon: absolutely positioned inside .book-page-ribbon-container.
   Protrudes upward from the page's top edge. Left position set via inline style
   from RibbonConfiguration.HorizontalOffsetPercent. Width controlled by --ribbon-width
   CSS variable for uniform sizing. Height uses depth staggering via --stack-position
   (set per-page by book-interop.js) so deeper pages protrude less. */
.book-ribbon {
    position: absolute;
    top: 0;
    transform: translateY(-100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    width: var(--ribbon-width);
    cursor: pointer;
    border: none;
    background: none;
    padding: 0;
    pointer-events: auto;
    /* Depth-staggered protrusion: page 0 (topmost in stack) gets full height,
       each deeper page loses 4px, creating visual depth layering like the closed state.
       Uses --page-z-offset (always set to PageIndex in BookPage.razor markup).
       The .book-ribbon--active override ensures the current page is always tallest. */
    --ribbon-depth-offset: calc(var(--page-z-offset, 0) * 4px);
    height: calc(var(--ribbon-protrude) - var(--ribbon-depth-offset));
    transition: height 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94),
                filter 0.3s ease;
}

/* ── Ribbon Body — satin strip with sheen overlay ── */
.book-ribbon-body {
    position: relative;
    width: 100%;
    flex: 1;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--ribbon-color) 70%, #1a1a2e) 0%,
        color-mix(in srgb, var(--ribbon-color) 50%, #2c1a0e) 40%,
        color-mix(in srgb, var(--ribbon-color) 40%, #1a1a2e) 100%);
    border-radius: 2px 2px 0 0;
    overflow: hidden;
    box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.08),
                inset 0 -1px 2px rgba(0, 0, 0, 0.15);
    border-left: 0.5px solid rgba(212, 175, 55, 0.2);
    border-right: 0.5px solid rgba(212, 175, 55, 0.2);
}

/* Satin sheen — diagonal gradient overlay */
.book-ribbon-sheen {
    position: absolute;
    inset: 0;
    background: linear-gradient(165deg,
        rgba(212, 175, 55, 0.12) 0%,
        rgba(255, 255, 255, 0.06) 20%,
        transparent 40%,
        rgba(212, 175, 55, 0.05) 60%,
        transparent 80%);
    pointer-events: none;
}

/* ── Ribbon Fold — crease where ribbon bends at book edge ── */
.book-ribbon-fold {
    width: 100%;
    height: var(--ribbon-fold-height);
    background: color-mix(in srgb, var(--ribbon-color, var(--color-sage)) 45%, #1a1a2e);
    flex-shrink: 0;
    transform: perspective(40px) rotateX(25deg);
    transform-origin: top center;
    /* Pull fold up 1px to eliminate the sub-pixel gap between
       body and fold caused by perspective distortion at the
       rotateX hinge. Without this, the background bleeds through. */
    margin-top: -1px;
}

/* ── Ribbon Tail — clean flat bottom with subtle darkening ── */
.book-ribbon-tail {
    width: 100%;
    height: var(--ribbon-tail-height);
    background: linear-gradient(180deg,
        var(--ribbon-color, var(--color-sage)) 0%,
        color-mix(in srgb, var(--ribbon-color, var(--color-sage)) 60%, #1a1a2e) 100%);
    flex-shrink: 0;
    border-radius: 0 0 2px 2px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
}

/* ── Ribbon Label — horizontal text, always partially visible ── */
.book-ribbon-label {
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    font-family: 'Cormorant Garamond', var(--font-heading);
    font-size: clamp(9px, 0.8vw, 12px);
    color: #f0e6d0;
    text-shadow: 0 1px 3px rgba(0,0,0,0.6), 0 0 8px rgba(212, 175, 55, 0.15);
    opacity: 0.75;
    transition: opacity 0.4s ease;
    white-space: nowrap;
    z-index: 100;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 600;
    pointer-events: none;
}

/* ── Ribbon States ── */

/* Hover: extend (depth-aware), show label, brightness bump */
.book-ribbon:hover {
    height: calc(var(--ribbon-protrude-hover, 60px) - var(--ribbon-depth-offset, 0px));
    filter: brightness(1.2) saturate(1.1);
}

.book-ribbon:hover .book-ribbon-body {
    box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.08),
                inset 0 -1px 2px rgba(0, 0, 0, 0.15),
                0 0 12px rgba(212, 175, 55, 0.3),
                0 0 25px rgba(212, 175, 55, 0.15);
}

.book-ribbon:hover .book-ribbon-label {
    opacity: 1;
}

/* Active (current page): fully extended, no depth offset, label always visible */
.book-ribbon--active {
    --ribbon-depth-offset: 0px;
    height: var(--ribbon-protrude-active);
    z-index: 210;
}

.book-ribbon--active .book-ribbon-label {
    opacity: 1;
    font-weight: 700;
    color: #f5ecd8;
    font-size: clamp(10px, 0.9vw, 13px);
    text-shadow: 0 1px 4px rgba(0,0,0,0.7), 0 0 12px rgba(212, 175, 55, 0.35);
}

.book-ribbon--active .book-ribbon-body {
    box-shadow: 1px 0 3px rgba(0, 0, 0, 0.15),
                -1px 0 3px rgba(0, 0, 0, 0.15),
                0 0 8px rgba(212, 175, 55, 0.15);
}

.book-ribbon--active .book-ribbon-tail {
    position: relative;
    border-bottom: 1.5px solid var(--book-gold-leaf, #d4af37);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2),
                0 0 4px rgba(212, 175, 55, 0.3);
}

/* Past (turned pages): slightly desaturated */
.book-ribbon--past:not(.book-ribbon--active) {
    filter: saturate(0.7) brightness(0.92);
}

/* Phase 7: Directional ribbon tilt — passed pages lean left, upcoming lean right */
.book-ribbon--before .book-ribbon-body {
    transform: rotate(-2deg);
    transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.book-ribbon--after .book-ribbon-body {
    transform: rotate(2deg);
    transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.book-ribbon--active .book-ribbon-body {
    transform: rotate(0deg);
    transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* Focus-visible: gold outline, extend like hover */
.book-ribbon:focus-visible {
    height: var(--ribbon-protrude-hover);
    outline: 2px solid var(--book-gold-leaf);
    outline-offset: 2px;
    border-radius: 3px;
}

.book-ribbon:focus-visible .book-ribbon-label {
    opacity: 1;
}

/* Desktop: labels always partially visible for legibility */
@media (min-width: 993px) {
    .book-ribbon-label {
        opacity: 0.8;
    }
    .book-ribbon:hover .book-ribbon-label {
        opacity: 1;
    }
}

/* ── Direction-aware ribbon flutter during page turns ── */
/* Forward turn: wind from turning page pushes ribbons left */
.book-frame-right:has(.book-viewport--turning-forward) .book-ribbon:not(.book-ribbon--active) {
    animation: ribbonFlutterForward var(--book-page-turn-duration, 1.2s) ease-in-out;
}

/* Backward turn: wind pushes ribbons right */
.book-frame-right:has(.book-viewport--turning-backward) .book-ribbon:not(.book-ribbon--active) {
    animation: ribbonFlutterBackward var(--book-page-turn-duration, 1.2s) ease-in-out;
}

/* Fallback: generic flutter if no direction class (e.g. drag-to-flip) */
.book-frame-right:has(.book-viewport--turning:not(.book-viewport--turning-forward):not(.book-viewport--turning-backward)) .book-ribbon:not(.book-ribbon--active) {
    animation: ribbonFlutterForward var(--book-page-turn-duration, 1.2s) ease-in-out;
}

@keyframes ribbonFlutterForward {
    0%   { transform: translateY(-100%) rotate(0deg); }
    15%  { transform: translateY(-100%) rotate(-1.2deg) translateX(-0.5px); }
    35%  { transform: translateY(-100%) rotate(0.5deg); }
    55%  { transform: translateY(-100%) rotate(-0.3deg); }
    75%  { transform: translateY(-100%) rotate(0.1deg); }
    100% { transform: translateY(-100%) rotate(0deg); }
}

@keyframes ribbonFlutterBackward {
    0%   { transform: translateY(-100%) rotate(0deg); }
    15%  { transform: translateY(-100%) rotate(1.2deg) translateX(0.5px); }
    35%  { transform: translateY(-100%) rotate(-0.5deg); }
    55%  { transform: translateY(-100%) rotate(0.3deg); }
    75%  { transform: translateY(-100%) rotate(-0.1deg); }
    100% { transform: translateY(-100%) rotate(0deg); }
}

/* ── Expanded touch targets for mobile accessibility ── */
.book-ribbon::after {
    content: '';
    position: absolute;
    inset: -6px -4px;
    pointer-events: auto;
}

@keyframes ribbonMagicShimmer {
    0%   { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

.book-ribbon:hover .book-ribbon-sheen {
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(212, 175, 55, 0.15) 25%,
        rgba(255, 255, 255, 0.1) 50%,
        rgba(212, 175, 55, 0.15) 75%,
        transparent 100%);
    background-size: 200% 100%;
    animation: ribbonMagicShimmer 1.5s ease-in-out;
}

/* ═══════════════════════════════════════════════════════════════
   3D RIBBON GEOMETRY
   Physical depth for true 3D ribbon bookmarks. Each ribbon has:
   - Front face (satin strip with label) at +Z
   - Back face (darker mirror) at -Z
   - Left/right edge strips connecting front to back
   The ribbon participates in the page's preserve-3d context so it
   rotates correctly during 3D page turns.
   ═══════════════════════════════════════════════════════════════ */

.book-ribbon--3d {
    transform-style: preserve-3d;
    transform: translateY(-100%) translateZ(0);
}

/* Front face — the visible satin surface with sheen, fold, tail, label */
.book-ribbon--3d .book-ribbon-face--front {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    transform: translateZ(calc(var(--ribbon-3d-thickness) / 2));
    backface-visibility: hidden;
}

/* Back face — slightly darker mirror of the front */
.book-ribbon--3d .book-ribbon-face--back {
    position: absolute;
    inset: 0;
    transform: translateZ(calc(var(--ribbon-3d-thickness) / -2)) rotateY(180deg);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--ribbon-color) 55%, #1a1a2e) 0%,
        color-mix(in srgb, var(--ribbon-color) 35%, #1a1a2e) 100%);
    border-radius: 2px 2px 0 0;
    backface-visibility: hidden;
}

/* Left edge — thin strip connecting front to back at left side */
.book-ribbon--3d .book-ribbon-edge--left {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--ribbon-3d-thickness);
    transform-origin: left center;
    transform: rotateY(-90deg);
    background: color-mix(in srgb, var(--ribbon-color) 40%, #1a1a2e);
}

/* Right edge — thin strip connecting front to back at right side */
.book-ribbon--3d .book-ribbon-edge--right {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: var(--ribbon-3d-thickness);
    transform-origin: right center;
    transform: rotateY(90deg);
    background: color-mix(in srgb, var(--ribbon-color) 40%, #1a1a2e);
}

/* In 3D mode, the fold uses actual rotateX instead of pseudo perspective */
.book-ribbon--3d .book-ribbon-fold {
    transform: rotateX(25deg);
    transform-origin: top center;
}

/* ─── Page Light Catch — simulates ambient light catching the paper curve during turns ─── */
.book-page-light-catch {
    position: absolute;
    inset: 0;
    z-index: 10;
    pointer-events: none;
    opacity: 0;
}

.book-page--turning-forward .book-page-light-catch {
    animation: lightCatchForward var(--book-page-turn-duration) ease-in-out forwards;
}

.book-page--turning-backward .book-page-light-catch {
    animation: lightCatchBackward var(--book-page-turn-duration) ease-in-out forwards;
}

@keyframes lightCatchForward {
    0%   { opacity: 0; background: linear-gradient(90deg, transparent 0%, transparent 100%); }
    20%  { opacity: 0.4; background: linear-gradient(100deg, transparent 20%, rgba(255,255,255,0.12) 40%, rgba(255,255,255,0.04) 55%, transparent 70%); }
    35%  { opacity: 0.7; background: linear-gradient(95deg, transparent 10%, rgba(255,255,255,0.18) 30%, rgba(255,255,255,0.06) 50%, transparent 65%); }
    50%  { opacity: 0.5; background: linear-gradient(90deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.12) 20%, transparent 45%); }
    70%  { opacity: 0.2; background: linear-gradient(85deg, rgba(255,255,255,0.04) 0%, transparent 30%); }
    100% { opacity: 0; }
}

@keyframes lightCatchBackward {
    0%   { opacity: 0; }
    20%  { opacity: 0.3; background: linear-gradient(270deg, transparent 20%, rgba(255,255,255,0.10) 40%, transparent 60%); }
    35%  { opacity: 0.6; background: linear-gradient(265deg, transparent 10%, rgba(255,255,255,0.15) 30%, transparent 55%); }
    50%  { opacity: 0.4; background: linear-gradient(270deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0.10) 20%, transparent 45%); }
    70%  { opacity: 0.15; }
    100% { opacity: 0; }
}

/* ─── Air Displacement — subtle line sweep simulating air pushed by turning page ─── */
.book-page-air-line {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 2px;
    background: linear-gradient(to right,
        transparent 0%,
        rgba(255, 255, 255, 0.12) 30%,
        rgba(255, 255, 255, 0.22) 50%,
        rgba(255, 255, 255, 0.12) 70%,
        transparent 100%);
    opacity: 0;
    pointer-events: none;
    z-index: 12;
    transform: scaleX(0.3) translateY(-50%);
}

.book-page--turning-forward .book-page-air-line {
    animation: airDisplacement var(--book-page-turn-duration) ease-out forwards;
}

@keyframes airDisplacement {
    0%   { opacity: 0; transform: scaleX(0.3) translateY(-50%) translateX(40%); }
    25%  { opacity: 0; }
    35%  { opacity: 0.5; transform: scaleX(0.8) translateY(-50%) translateX(10%); }
    45%  { opacity: 0.25; transform: scaleX(1.2) translateY(-50%) translateX(-20%); }
    55%  { opacity: 0; transform: scaleX(1.5) translateY(-50%) translateX(-50%); }
    100% { opacity: 0; }
}

/* ─── Animation Scrubbing — freeze page turns at any position ─── */
/* Applied by editor-interop.js scrubPageTurn(). Uses negative animation-delay
   to seek to a specific frame while paused. The --scrub-delay custom property
   is set dynamically by JS based on the slider position. */

.book-page--scrubbing,
.book-page--scrubbing > .book-page-front,
.book-page--scrubbing > .book-page-back,
.book-page--scrubbing .book-page-spine-glow,
.book-page--scrubbing .book-page-curl-shadow,
.book-page--scrubbing .book-page-light-catch,
.book-page--scrubbing .book-page-air-line,
.book-page--scrubbing .book-page-edge,
.book-page--scrubbing .book-mote,
.book-page--scrubbing .book-page-front::before,
.book-page--scrubbing .book-page-back::before,
.book-page--scrubbing .book-page-drag-flap,
.book-page--scrubbing .book-page-peel-shadow,
.book-page--scrubbing .book-page-peel-underpage {
    animation-play-state: paused !important;
    animation-delay: var(--scrub-delay, 0s) !important;
}

/* ─── Opacity hardening — prevent content behind bleeding through during turns ───
   B7 fix: The parent .book-page background must be TRANSPARENT during turns.
   In a preserve-3d context, the parent's background renders as its own flat plane,
   separate from the child face planes. Near -90deg rotation, this creates a visible
   horizontal seam (the parent's edge-on background bisects the page at transform-origin
   height). Making the parent transparent removes this plane from the 3D scene.
   The child faces (.book-page-front / .book-page-back) carry fully opaque backgrounds
   so bleed-through from pages behind is still blocked by whichever face is camera-facing. */
.book-page--turning,
.book-page--scrubbing {
    background-color: transparent !important;
}

.book-page--turning > .book-page-front,
.book-page--scrubbing > .book-page-front {
    background-color: var(--book-page-cream, #f5f0e6);
}

.book-page--turning > .book-page-back,
.book-page--scrubbing > .book-page-back {
    background-color: var(--book-page-cream, #f5f0e6);
}

/* ─── Reduced motion for page-turn polish (C2-C4) ─── */
@media (prefers-reduced-motion: reduce) {
    .book-page-light-catch,
    .book-page-air-line {
        animation: none !important;
        opacity: 0 !important;
    }
}

