.jpb {
  position: relative;
  --jpb-safe-top: env(safe-area-inset-top, 0px);
  --jpb-safe-right: env(safe-area-inset-right, 0px);
  --jpb-safe-bottom: env(safe-area-inset-bottom, 0px);
  --jpb-safe-left: env(safe-area-inset-left, 0px);
  width: min(980px, 100%);
  /* Height should be driven by content (stage + controls), not a fixed aspect box. */
  height: auto;
  margin: 16px auto;
  box-sizing: border-box;
  --jpb-gutter: 10px;
  --jpb-edge-pad: var(--jpb-gutter);
  --jpb-nav-outset: 52px;
  --jpb-flip-dur: 0.92s;
  --jpb-border-w: 0px;
  --jpb-border-c: #ffffff;
  --jpb-frame-shadow: none;
  --jpb-mat: 0px;
  --jpb-mat-c: transparent;
  /* Responsive “effective” values (mirrors joodit_photos pattern).
     We keep the original config values in the base vars above and scale the *-eff vars
     down on smaller screens, so thick frames/mats never consume the whole page. */
  --jpb-gutter-eff: var(--jpb-gutter);
  --jpb-edge-pad-eff: var(--jpb-edge-pad);
  --jpb-outer-border-w-eff: var(--jpb-outer-border-w);
  --jpb-border-w-eff: var(--jpb-border-w);
  --jpb-mat-eff: var(--jpb-mat);
  --jpb-desc-pad-top-eff: var(--jpb-desc-pad-top);
  --jpb-desc-pad-right-eff: var(--jpb-desc-pad-right);
  --jpb-desc-pad-bottom-eff: var(--jpb-desc-pad-bottom);
  --jpb-desc-pad-left-eff: var(--jpb-desc-pad-left);
  --jpb-desc-pad-eff: var(--jpb-desc-pad);
  --jpb-page-shadow: 0 10px 30px rgba(0,0,0,0.12);
  --jpb-cap-below-h: 58px;
  --jpb-cap-align: center;
  --jpb-cap-font: inherit;
  --jpb-desc-font: inherit;
  --jpb-cap-size: 14px;
  --jpb-cap-color: #ffffff;
  --jpb-cap-reserve: 34px;
    /* Universal caption offset (px). Applies to all caption positions.
      Semantics: positive = away from the nearest edge toward the text area,
      negative = toward/over the nearest edge. */
  --jpb-cap-mat-inset: 8px;
  /* Responsive caption “effective” values.
     JS sets base vars (e.g. --jpb-cap-size); CSS scales the *-eff vars on small screens. */
  --jpb-cap-size-eff: var(--jpb-cap-size);
  --jpb-cap-below-h-eff: var(--jpb-cap-below-h);
  --jpb-cap-gap-eff: var(--jpb-cap-gap, 10px);
  --jpb-cap-mat-inset-eff: var(--jpb-cap-mat-inset);
  --jpb-cap-offset: var(--jpb-cap-mat-inset-eff);
  --jpb-cap-gap: 10px;
  /* Overlay caption padding (scales down on small screens). */
  --jpb-cap-pad-y-eff: 10px;
  --jpb-cap-pad-x-eff: 12px;
  --jpb-page-bg: #ffffff;
  --jpb-desc-pad: 18px;
  --jpb-desc-pad-top: var(--jpb-desc-pad);
  --jpb-desc-pad-right: var(--jpb-desc-pad);
  --jpb-desc-pad-bottom: var(--jpb-desc-pad);
  --jpb-desc-pad-left: var(--jpb-desc-pad);
  --jpb-desc-bg: var(--jpb-page-bg);
  /* Surface behind the book (table/background). */
  --jpb-surface-bg: #f3f3f3;
  --jpb-shell-pad: 0px;
    --jpb-outer-border-w: 2px;
    --jpb-outer-border-c: #d6d6d6;
  /* Default book ratio (spread): A4 landscape is 297/210 ≈ 1.414 per page → spread ≈ 2.828. */
  --jpb-stage-ar-w: 2.828;
  --jpb-stage-ar-h: 1;
  --jpb-text-c: #111111;
  background: var(--jpb-surface-bg);
}

/* Responsive scaling for thick frames/mats/borders (similar to joodit_photos frontend.css).
   We only touch the *-eff vars so desktop rendering stays unchanged. */
@media (max-width: 900px) {
  .jpb {
    --jpb-gutter-eff: clamp(0px, calc(var(--jpb-gutter) * 0.7), var(--jpb-gutter));
    --jpb-edge-pad-eff: clamp(0px, calc(var(--jpb-edge-pad) * 0.7), var(--jpb-edge-pad));
    --jpb-outer-border-w-eff: min(var(--jpb-outer-border-w), max(1px, calc(var(--jpb-outer-border-w) * 0.5)), 6px);
    --jpb-border-w-eff: min(var(--jpb-border-w), max(1px, calc(var(--jpb-border-w) * 0.5)), 6px);
    --jpb-mat-eff: min(26px, clamp(0px, calc(var(--jpb-mat) * 0.5), var(--jpb-mat)));
    --jpb-desc-pad-top-eff: clamp(0px, calc(var(--jpb-desc-pad-top) * 0.85), var(--jpb-desc-pad-top));
    --jpb-desc-pad-right-eff: clamp(0px, calc(var(--jpb-desc-pad-right) * 0.85), var(--jpb-desc-pad-right));
    --jpb-desc-pad-bottom-eff: clamp(0px, calc(var(--jpb-desc-pad-bottom) * 0.85), var(--jpb-desc-pad-bottom));
    --jpb-desc-pad-left-eff: clamp(0px, calc(var(--jpb-desc-pad-left) * 0.85), var(--jpb-desc-pad-left));
    --jpb-desc-pad-eff: var(--jpb-desc-pad-top-eff);
    --jpb-cap-size-eff: clamp(11px, calc(var(--jpb-cap-size) * 0.90), var(--jpb-cap-size));
    --jpb-cap-below-h-eff: clamp(28px, calc(var(--jpb-cap-below-h) * 0.85), var(--jpb-cap-below-h));
    --jpb-cap-gap-eff: clamp(6px, calc(var(--jpb-cap-gap) * 0.85), var(--jpb-cap-gap));
    --jpb-cap-pad-y-eff: clamp(6px, calc(var(--jpb-cap-pad-y-eff) * 0.85), 10px);
    --jpb-cap-pad-x-eff: clamp(7px, calc(var(--jpb-cap-pad-x-eff) * 0.85), 12px);
    --jpb-cap-mat-inset-eff: clamp(-80px, calc(var(--jpb-cap-mat-inset) * 0.85), 80px);
  }
}

@media (max-width: 600px) {
  .jpb {
    --jpb-gutter-eff: clamp(0px, calc(var(--jpb-gutter) * 0.5), var(--jpb-gutter));
    --jpb-edge-pad-eff: clamp(0px, calc(var(--jpb-edge-pad) * 0.5), var(--jpb-edge-pad));
    --jpb-outer-border-w-eff: min(var(--jpb-outer-border-w), max(1px, calc(var(--jpb-outer-border-w) * 0.33)), 3px);
    --jpb-border-w-eff: min(var(--jpb-border-w), max(1px, calc(var(--jpb-border-w) * 0.33)), 3px);
    --jpb-mat-eff: min(20px, clamp(0px, calc(var(--jpb-mat) * 0.33), var(--jpb-mat)));
    --jpb-desc-pad-top-eff: clamp(0px, calc(var(--jpb-desc-pad-top) * 0.7), var(--jpb-desc-pad-top));
    --jpb-desc-pad-right-eff: clamp(0px, calc(var(--jpb-desc-pad-right) * 0.7), var(--jpb-desc-pad-right));
    --jpb-desc-pad-bottom-eff: clamp(0px, calc(var(--jpb-desc-pad-bottom) * 0.7), var(--jpb-desc-pad-bottom));
    --jpb-desc-pad-left-eff: clamp(0px, calc(var(--jpb-desc-pad-left) * 0.7), var(--jpb-desc-pad-left));
    --jpb-desc-pad-eff: var(--jpb-desc-pad-top-eff);
    --jpb-cap-size-eff: clamp(10px, calc(var(--jpb-cap-size) * 0.85), var(--jpb-cap-size));
    --jpb-cap-below-h-eff: clamp(24px, calc(var(--jpb-cap-below-h) * 0.70), var(--jpb-cap-below-h));
    --jpb-cap-gap-eff: clamp(5px, calc(var(--jpb-cap-gap) * 0.70), var(--jpb-cap-gap));
    --jpb-cap-pad-y-eff: clamp(5px, calc(var(--jpb-cap-pad-y-eff) * 0.70), 9px);
    --jpb-cap-pad-x-eff: clamp(6px, calc(var(--jpb-cap-pad-x-eff) * 0.70), 10px);
    --jpb-cap-mat-inset-eff: clamp(-80px, calc(var(--jpb-cap-mat-inset) * 0.70), 80px);
  }
}

@media (max-width: 480px) {
  .jpb {
    margin: 10px auto;
    --jpb-gutter-eff: clamp(0px, calc(var(--jpb-gutter) * 0.40), var(--jpb-gutter));
    --jpb-edge-pad-eff: clamp(0px, calc(var(--jpb-edge-pad) * 0.40), var(--jpb-edge-pad));
    --jpb-outer-border-w-eff: min(var(--jpb-outer-border-w), max(1px, calc(var(--jpb-outer-border-w) * 0.25)), 2px);
    --jpb-border-w-eff: min(var(--jpb-border-w), max(1px, calc(var(--jpb-border-w) * 0.25)), 2px);
    --jpb-mat-eff: min(16px, clamp(0px, calc(var(--jpb-mat) * 0.25), var(--jpb-mat)));
    --jpb-desc-pad-top-eff: clamp(0px, calc(var(--jpb-desc-pad-top) * 0.65), var(--jpb-desc-pad-top));
    --jpb-desc-pad-right-eff: clamp(0px, calc(var(--jpb-desc-pad-right) * 0.65), var(--jpb-desc-pad-right));
    --jpb-desc-pad-bottom-eff: clamp(0px, calc(var(--jpb-desc-pad-bottom) * 0.65), var(--jpb-desc-pad-bottom));
    --jpb-desc-pad-left-eff: clamp(0px, calc(var(--jpb-desc-pad-left) * 0.65), var(--jpb-desc-pad-left));
    --jpb-desc-pad-eff: var(--jpb-desc-pad-top-eff);
    --jpb-cap-size-eff: clamp(10px, calc(var(--jpb-cap-size) * 0.80), var(--jpb-cap-size));
    --jpb-cap-below-h-eff: clamp(22px, calc(var(--jpb-cap-below-h) * 0.62), var(--jpb-cap-below-h));
    --jpb-cap-gap-eff: clamp(4px, calc(var(--jpb-cap-gap) * 0.62), var(--jpb-cap-gap));
    --jpb-cap-pad-y-eff: clamp(4px, calc(var(--jpb-cap-pad-y-eff) * 0.62), 8px);
    --jpb-cap-pad-x-eff: clamp(5px, calc(var(--jpb-cap-pad-x-eff) * 0.62), 9px);
    --jpb-cap-mat-inset-eff: clamp(-80px, calc(var(--jpb-cap-mat-inset) * 0.62), 80px);
  }

  .jpb-stage {
    border-radius: 8px;
  }
}

@media (max-width: 360px) {
  .jpb {
    --jpb-mat-eff: min(12px, var(--jpb-mat-eff));
    --jpb-border-w-eff: min(2px, var(--jpb-border-w-eff));
    --jpb-outer-border-w-eff: min(2px, var(--jpb-outer-border-w-eff));
    --jpb-cap-below-h-eff: min(20px, var(--jpb-cap-below-h-eff));
    --jpb-cap-gap-eff: min(3px, var(--jpb-cap-gap-eff));
    --jpb-cap-pad-y-eff: min(6px, var(--jpb-cap-pad-y-eff));
    --jpb-cap-pad-x-eff: min(7px, var(--jpb-cap-pad-x-eff));
  }
}

@media (max-width: 320px) {
  .jpb {
    --jpb-mat-eff: min(10px, var(--jpb-mat-eff));
    --jpb-border-w-eff: min(2px, var(--jpb-border-w-eff));
    --jpb-outer-border-w-eff: min(2px, var(--jpb-outer-border-w-eff));
    --jpb-cap-below-h-eff: min(18px, var(--jpb-cap-below-h-eff));
    --jpb-cap-gap-eff: min(2px, var(--jpb-cap-gap-eff));
    --jpb-cap-pad-y-eff: min(5px, var(--jpb-cap-pad-y-eff));
    --jpb-cap-pad-x-eff: min(6px, var(--jpb-cap-pad-x-eff));
  }
}

/* Height-constrained viewports (e.g. phones in landscape):
   shrink mats/borders/captions further so panoramas still get usable image area. */
@media (max-height: 520px) {
  .jpb {
    --jpb-gutter-eff: clamp(0px, calc(var(--jpb-gutter) * 0.40), var(--jpb-gutter));
    --jpb-edge-pad-eff: clamp(0px, calc(var(--jpb-edge-pad) * 0.40), var(--jpb-edge-pad));
    --jpb-outer-border-w-eff: min(var(--jpb-outer-border-w), max(1px, calc(var(--jpb-outer-border-w) * 0.30)), 3px);
    --jpb-border-w-eff: min(var(--jpb-border-w), max(1px, calc(var(--jpb-border-w) * 0.30)), 3px);
    --jpb-mat-eff: min(18px, clamp(0px, calc(var(--jpb-mat) * 0.30), var(--jpb-mat)));
    --jpb-cap-size-eff: clamp(10px, calc(var(--jpb-cap-size) * 0.80), var(--jpb-cap-size));
    --jpb-cap-below-h-eff: clamp(20px, calc(var(--jpb-cap-below-h) * 0.60), var(--jpb-cap-below-h));
    --jpb-cap-gap-eff: clamp(4px, calc(var(--jpb-cap-gap) * 0.60), var(--jpb-cap-gap));
    --jpb-cap-mat-inset-eff: clamp(-80px, calc(var(--jpb-cap-mat-inset) * 0.60), 80px);
  }
}

@media (max-height: 380px) {
  .jpb {
    --jpb-gutter-eff: clamp(0px, calc(var(--jpb-gutter) * 0.30), var(--jpb-gutter));
    --jpb-edge-pad-eff: clamp(0px, calc(var(--jpb-edge-pad) * 0.30), var(--jpb-edge-pad));
    --jpb-outer-border-w-eff: min(var(--jpb-outer-border-w), max(1px, calc(var(--jpb-outer-border-w) * 0.20)), 2px);
    --jpb-border-w-eff: min(var(--jpb-border-w), max(1px, calc(var(--jpb-border-w) * 0.20)), 2px);
    --jpb-mat-eff: min(14px, clamp(0px, calc(var(--jpb-mat) * 0.20), var(--jpb-mat)));
    --jpb-cap-size-eff: clamp(10px, calc(var(--jpb-cap-size) * 0.75), var(--jpb-cap-size));
    --jpb-cap-below-h-eff: clamp(18px, calc(var(--jpb-cap-below-h) * 0.55), var(--jpb-cap-below-h));
    --jpb-cap-gap-eff: clamp(3px, calc(var(--jpb-cap-gap) * 0.55), var(--jpb-cap-gap));
    --jpb-cap-mat-inset-eff: clamp(-80px, calc(var(--jpb-cap-mat-inset) * 0.55), 80px);
  }
}

/* Diagnostic: minimal rendering mode.
   Enabled via JS by adding .jpb-minimal (e.g. ?jpb_min=1 or debug checkbox).
   This is CSS-only and should not affect normal users (default off). */
.jpb.jpb-minimal {
  /* Use !important to override inline CSS variables set from config. */
  --jpb-gutter: 0px !important;
  --jpb-edge-pad: 0px !important;
  --jpb-border-w: 0px !important;
  --jpb-mat: 0px !important;
  --jpb-frame-shadow: none !important;
  --jpb-cap-below-h: 0px !important;
  --jpb-cap-reserve: 0px !important;
}

.jpb.jpb-minimal .jpb-caption,
.jpb.jpb-minimal .jpb-caption-below {
  display: none !important;
}

/* Remove extra frame/mat/border visuals (keeps contain-fit intact). */
.jpb.jpb-minimal .jpb-frame,
.jpb.jpb-minimal .jpb-media-box {
  border: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
}

/* Prefer a single paint path in minimal mode: use <img> only, disable bg-layer switching. */
.jpb.jpb-minimal.jpb-engine-stpageflip .jpb-stpage-bg-layer {
  display: none !important;
  opacity: 0 !important;
}

.jpb.jpb-minimal.jpb-engine-stpageflip img.jpb-stpage-img {
  display: block !important;
  opacity: 1 !important;
}

@media (max-width: 640px) {
  .jpb {
    --jpb-nav-outset: 12px;
    --jpb-gutter: 10px;
  }
}

.jpb-wrap {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  position: relative;
  /* Keep the same surface behind stage + nav, so there is no visible seam. */
  background: var(--jpb-page-bg);
  padding: var(--jpb-shell-pad);
  box-sizing: border-box;
}

.jpb-titlebar {
  flex: 0 0 auto;
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  text-align: center;
  font-family: var(--jpb-title-font, inherit);
  font-size: var(--jpb-title-size, 18px);
  font-weight: 600;
  color: var(--jpb-title-color, var(--jpb-text-c, #111111));
  background: var(--jpb-page-bg);
}

.jpb-stage {
  --jpb-inset: 0px;
  --jpb-gap: 10px;
  --jpb-pre-shift: 8px;
  position: relative;
  width: 100%;
  /* Book viewport ratio; JS sets --jpb-stage-ar-w/--jpb-stage-ar-h (must be a real ratio). */
  aspect-ratio: var(--jpb-stage-ar-w, 16) / var(--jpb-stage-ar-h, 10);
  height: auto;
  background: var(--jpb-page-bg);
  border: 0;
  border-radius: 10px;
  overflow: hidden;
  /* Outer margin (gutter) should be visible on all sides in all engines. */
  padding: var(--jpb-gutter-eff);
  box-sizing: border-box;

  /* Smooth closed→open transition (layout-only) + allow JS to gate rendering. */
  transition: max-width 320ms ease, margin 320ms ease, opacity 140ms linear;
}

/* Fullscreen return-to-cover: user prefers a small final blink over a sideways slide.
   JS toggles .jpb-no-stage-anim during the transition to disable max-width/margin animation. */
.jpb.jpb-no-stage-anim .jpb-stage {
  transition: opacity 140ms linear !important;
}

/* During the cover turn we run a slightly longer, softer opening animation. */
.jpb.jpb-opening .jpb-stage {
  transition: max-width var(--jpb-flip-dur, 0.92s) cubic-bezier(0.22, 0.90, 0.22, 1), margin var(--jpb-flip-dur, 0.92s) cubic-bezier(0.22, 0.90, 0.22, 1), opacity 140ms linear;
}

/* Cover layout: show a single page "closed book" centered with a smaller max width.
   JS toggles .jpb-layout-cover / .jpb-layout-open and re-inits PageFlip to match. */
.jpb.jpb-layout-cover .jpb-stage {
  max-width: var(--jpb-cover-max-w, 540px);
  margin-left: auto;
  margin-right: auto;
}

.jpb.jpb-layout-open .jpb-stage {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
}

/* The 3D/page-flip viewport (sheet + flip overlay). */
.jpb-viewport {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  perspective: 1200px;
  perspective-origin: 50% 50%;
  touch-action: none;
  cursor: grab;
  user-select: none;
}

.jpb-viewport:active {
  cursor: grabbing;
}

/* === StPageFlip (page-flip) engine === */
.jpb-engine-stpageflip .jpb-viewport {
  /* Let StPageFlip handle touch gestures; keep overflow hidden. */
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  /* PageFlip manages its own 3D context. Avoid nesting our legacy perspective which can
     make the built-in shadow look like a full-surface “film”. */
  perspective: none;
  perspective-origin: initial;
  /* Give PageFlip full control of drag gestures (matches demo behaviour). */
  touch-action: none;
  cursor: default;
  /* Avoid the default “grey void” showing through around photos when using contain. */
  background: var(--jpb-page-bg);
}

/* Render gate: hide stage/controls while JS performs multi-pass sizing.
   Prevents users from seeing the book "fipple" (grow/shrink) during init/fullscreen/resize.
   JS toggles .jpb-init-hide (all modes) and keeps .jpb-v-init-hide for backwards compat. */
.jpb-engine-stpageflip .jpb-controls {
  transition: opacity 140ms linear;
}

.jpb-engine-stpageflip.jpb-init-hide .jpb-stage,
.jpb-engine-stpageflip.jpb-init-hide .jpb-controls,
.jpb-engine-stpageflip.jpb-v-init-hide .jpb-stage,
.jpb-engine-stpageflip.jpb-v-init-hide .jpb-controls {
  opacity: 0;
  pointer-events: none;
}

/* Inner host used by the StPageFlip engine (lets us rotate/size PF without changing viewport). */
.jpb-engine-stpageflip .jpb-book-host {
  /* JS assigns explicit pixel width/height. Keep it out of normal flow. */
  position: absolute;
  left: 0;
  top: 0;
  right: auto;
  bottom: auto;
}

/* Horizontal mode cover: PageFlip renders the cover as the right page in a landscape spread.
  Shift the whole spread left so the cover page is visually centered.
   IMPORTANT: In portrait/single-page this causes cropping; disable it below. */
.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-book-host {
  /* JS can override with a deterministic px-shift to avoid visible corrections when returning to cover. */
  transform: translateX(var(--jpb-cover-shift-x, -25%));
  will-change: transform;
}

/* Portrait/single-page: do NOT shift (it will clip/offset in portrait layouts). */
.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .stf__wrapper.--portrait .jpb-book-host,
.jpb.jpb-layout-cover.jpb-engine-stpageflip.jpb-st-single-page:not([data-jpb-direction="vertical"]) .jpb-book-host {
  transform: none;
}

.jpb.jpb-layout-cover.jpb-initial-backcover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-book-host,
.jpb.jpb-layout-cover.jpb-initial-backcover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .stf__wrapper.--portrait .jpb-book-host,
.jpb.jpb-layout-cover.jpb-initial-backcover.jpb-engine-stpageflip.jpb-st-single-page:not([data-jpb-direction="vertical"]) .jpb-book-host {
  transform: translateX(var(--jpb-cover-shift-x, 25%));
}

.jpb.jpb-layout-open.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-book-host {
  transform: none;
}

/* Vertical mode: show 2 pages stacked (over/under) and flip up/down.
   Implementation: rotate the PageFlip spread -90deg so its horizontal 2-page layout becomes vertical.
   Then counter-rotate the inner page content so images/text are upright. */
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-book-host {
  /* Center the rotated spread so it doesn't get clipped to a thin middle strip. */
  left: 50%;
  top: 50%;
  right: auto;
  bottom: auto;
  transform: translate(-50%, -50%) rotate(90deg);
  transform-origin: 50% 50%;
}

/* Counter-rotate page content back upright.
   IMPORTANT: swap the box dimensions so the rotated content fits (otherwise it gets clipped).
   JS keeps --jpb-st-page-w/h in sync with the current PageFlip page size. */
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page .jpb-media {
  position: absolute;
  left: 50%;
  top: 50%;
  right: auto;
  bottom: auto;
  width: var(--jpb-st-page-h, 100%);
  height: var(--jpb-st-page-w, 100%);
  transform: translate(-50%, -50%) rotate(-90deg);
  transform-origin: 50% 50%;
  padding: var(--jpb-edge-pad-eff);
  box-sizing: border-box;
}

/* Cover page uses custom markup (no .jpb-media). Counter-rotate it too so it is upright in vertical mode. */
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page.jpb-stpage-cover .jpb-coverwrap {
  position: absolute;
  left: 50%;
  top: 50%;
  right: auto;
  bottom: auto;
  width: var(--jpb-st-page-h, 100%);
  height: var(--jpb-st-page-w, 100%);
  transform: translate(-50%, -50%) rotate(-90deg);
  transform-origin: 50% 50%;
  box-sizing: border-box;
  padding: var(--jpb-edge-pad-eff);
}

/* Vertical mode: don't apply left/right “spine gap” padding/alignment (it becomes vertical stripes). */
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-media,
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-media {
  padding: var(--jpb-edge-pad-eff);
}

.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page .jpb-frame-stack {
  width: 100%;
  height: 100%;
  align-items: center;
  justify-content: center;
}

.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page .jpb-caption-below {
  flex: 0 0 auto;
}

.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-frame-stack,
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-frame-stack {
  align-items: stretch;
}

/* Vertical mode: allow PageFlip's per-page sizing to control the page box.
   The base rule uses `inset: 0` which can force each page to fill the whole spread,
   making the framed content look like it sits centered in the middle. */
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page {
  inset: auto;
  left: 0;
  top: 0;
  right: auto;
  bottom: auto;
}

/* Vertical mode: DO NOT stretch the frame to full page footprint.
   The opening (.jpb-media-box) is aspect-fitted by JS; if the frame is stretched, the extra
   empty background inside the frame looks like a too-thick passepartout.
   Keep it shrink-wrapped and centered instead. */
.jpb-engine-stpageflip[data-jpb-direction="vertical"] .jpb-stpage-page .jpb-frame {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  flex: 0 0 auto;
  align-self: center;
}

/* If a theme/plugin forces controls to be overlay-positioned, reserve space below the book so
   the control row never covers the bottom of the image. JS sets --jpb-controls-reserve. */
.jpb-engine-stpageflip .jpb-stage {
  margin-bottom: var(--jpb-controls-reserve, 0px);
  overflow: visible;
}

.jpb-engine-stpageflip .jpb-wrap {
  /* Avoid a visible seam/gap between book area and controls. */
  gap: 0;
}

/* PageFlip internal wrappers sometimes have a default background; keep it consistent. */
.jpb-engine-stpageflip .stf__parent,
.jpb-engine-stpageflip .stf__wrapper,
.jpb-engine-stpageflip .stf__block {
  background: transparent;
  /* Needed so the PageFlip UI fills our viewport. Without this the book can render as a white box. */
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}


.jpb-stpage-page {
  width: 100%;
  height: 100%;
}

/* StPageFlip basic: pages contain content directly (no custom surface/face wrappers). */

/* StPageFlip basic: do not add extra per-side inset shadows on pages.
   (The built-in PageFlip shadow/crease provides the depth cue.) */

/* StPageFlip basic: pages contain content directly (no custom surface/face wrappers). */



/* StPageFlip: use the same framed media stack as the normal renderer so
   frame/mat/border visuals are preserved. */
.jpb-engine-stpageflip .jpb-stpage-page {
  /* IMPORTANT: keep pages out of normal document flow.
     If they participate in flow, you can end up with the real (sharp) page rendered
     below the book area while only shadows/helpers appear in the book surface. */
  position: absolute;
  inset: 0;
  margin: 0;
  background: var(--jpb-page-bg);
  overflow: hidden;
  box-sizing: border-box;
}

.jpb-engine-stpageflip .jpb-stpage-page .jpb-media {
  position: absolute;
  inset: 0;
  display: flex;
  min-width: 0;
  min-height: 0;
  align-items: flex-start;
  justify-content: center;
  padding: var(--jpb-edge-pad-eff);
  box-sizing: border-box;
  overflow: hidden;
}

/* Cover layout: keep the cover page content visually centered.
   Without this, PageFlip's left/right alignment rules can make the cover feel offset in its frame. */
.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-stpage-page .jpb-media {
  align-items: center;
}

.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-stpage-page .jpb-frame-stack {
  justify-content: center;
}

.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-frame-stack,
.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-frame-stack {
  align-items: center;
}

.jpb-engine-stpageflip .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-media {
  padding-right: var(--jpb-spine-gap, var(--jpb-edge-pad-eff));
  box-sizing: border-box;
}

.jpb-engine-stpageflip .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-media {
  padding-left: var(--jpb-spine-gap, var(--jpb-edge-pad-eff));
  box-sizing: border-box;
}

/* Cover layout has no visible spine; avoid asymmetric padding that makes the cover feel offset. */
.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-media,
.jpb.jpb-layout-cover.jpb-engine-stpageflip:not([data-jpb-direction="vertical"]) .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-media {
  padding-left: var(--jpb-edge-pad-eff);
  padding-right: var(--jpb-edge-pad-eff);
}

/* StPageFlip single-page (mobile) mode: treat the page as centered, not left/right of a spine. */
.jpb-engine-stpageflip.jpb-st-single-page .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-media,
.jpb-engine-stpageflip.jpb-st-single-page .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-media {
  padding-left: var(--jpb-edge-pad-eff);
  padding-right: var(--jpb-edge-pad-eff);
}

.jpb-engine-stpageflip .jpb-stpage-page .jpb-frame-stack {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
}

.jpb-engine-stpageflip .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-frame-stack {
  align-items: flex-end;
}

.jpb-engine-stpageflip .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-frame-stack {
  align-items: flex-start;
}

.jpb-engine-stpageflip.jpb-st-single-page .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-frame-stack,
.jpb-engine-stpageflip.jpb-st-single-page .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-frame-stack {
  align-items: center;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-half-left .jpb-frame-stack {
  align-items: flex-end;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-half-right .jpb-frame-stack {
  align-items: flex-start;
}

.jpb-engine-stpageflip .jpb-stpage-page .jpb-frame {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  box-sizing: border-box;
  /* When JS forces the frame to fill the page (common on small screens),
     ensure the actual opening (.jpb-media-box) stays centered inside the mat.
     Without this, the opening sits at top-left and creates large whitespace bottom/right. */
  display: flex;
  align-items: center;
  justify-content: center;
}

.jpb-engine-stpageflip .jpb-stpage-page .jpb-media-box {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  min-width: 40px;
  min-height: 40px;
  position: relative;
}

/* Photo descriptions: rich text page on the RIGHT side.
   Keep book sizing image-driven by not using `.jpb-media-box` here; only the inner content scrolls. */
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page {
  background: var(--jpb-page-bg);
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-media {
  align-items: stretch;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-frame-stack {
  min-height: 0;
  max-height: 100%;
}

/* For description pages, the frame should fill the available page area (like a photo page)
   while the content scrolls inside the inner opening. */
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-frame {
  width: 100%;
  height: 100%;
  flex: 1 1 auto;
  min-height: 0;
  max-height: 100%;
  align-self: stretch;
  /* No passepartout/mat on description pages. Keep only the outer border. */
  padding: 0;
  background: transparent;
  overflow: hidden;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-caption,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-caption-below {
  display: none !important;
}

/* Desc box is the text surface INSIDE the outer border.
   It intentionally does NOT mirror the photo opening (no mat, no inner border). */
.jpb-desc-box {
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
  border: 0;
  width: 100%;
  height: 100%;
  min-height: 0;
  max-height: 100%;
  padding-top: var(--jpb-desc-pad-top-eff);
  padding-right: var(--jpb-desc-pad-right-eff);
  padding-bottom: var(--jpb-desc-pad-bottom-eff);
  padding-left: var(--jpb-desc-pad-left-eff);
  background: var(--jpb-desc-bg);
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-scroll {
  width: 100%;
  height: 100%;
  min-height: 0;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  overflow: auto;
  scrollbar-gutter: stable;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-box {
  padding-right: 0;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content {
  flex: 0 0 auto;
  width: 100%;
  min-height: 0;
  max-width: 100%;
  box-sizing: border-box;
  padding-right: var(--jpb-desc-pad-right-eff);
  color: #111;
  font-family: var(--jpb-desc-font, inherit);
  font-size: 15px;
  line-height: 1.55;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content :where(p, li, blockquote, div) {
  line-height: inherit;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content :where(span, em, strong, b, i, a, small, sup, sub, mark) {
  line-height: inherit !important;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content > :first-child {
  margin-top: 0;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content > :last-child {
  margin-bottom: 0;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content code,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content kbd,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content samp,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content pre {
  font-family: inherit;
  font-size: inherit;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page.jpb-desc-hide-title .jpb-desc-title {
  display: none;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page.jpb-desc-v-center .jpb-desc-scroll {
  justify-content: center;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page.jpb-desc-v-bottom .jpb-desc-scroll {
  justify-content: flex-end;
}

.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-box {
  padding-left: var(--jpb-desc-pad-left-eff);
  padding-right: 0;
  padding-bottom: calc(var(--jpb-desc-pad-bottom-eff) + 16px);
}

.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page.jpb-toc-spread-page .jpb-desc-box {
  padding-bottom: var(--jpb-desc-pad-bottom-eff);
}

.jpb-engine-stpageflip .jpb-stpage-page .jpb-page-number {
  display: none;
  position: absolute;
  left: auto;
  right: 8px;
  bottom: 8px;
  z-index: 5;
  min-width: 1.6em;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.42);
  color: #fff;
  font-size: 11px;
  line-height: 1.2;
  font-weight: 600;
  text-align: center;
  box-shadow: none;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}

.jpb-engine-stpageflip .jpb-stpage-page:not(.jpb-desc-page) .jpb-page-number {
  right: 6px;
  bottom: 6px;
}

.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page .jpb-page-number {
  display: block;
}

.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page .jpb-page-number:not([data-jpb-page-number]) {
  display: none;
}

.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-spread-page .jpb-page-number {
  display: none;
}

.jpb-engine-stpageflip .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-page-number,
.jpb-engine-stpageflip .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-page-number,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-half-left .jpb-page-number,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-half-right .jpb-page-number {
  left: auto;
  right: 8px;
}

.jpb-engine-stpageflip.jpb-st-single-page .jpb-stpage-page .jpb-page-number {
  left: auto;
  right: 8px;
}

.jpb-engine-stpageflip .jpb-stpage-page .jpb-page-number-blank {
  right: 14px;
  bottom: 14px;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-page .jpb-toc-content {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-page .jpb-toc-title {
  margin: 0;
  font-size: 24px;
  line-height: 1.1;
  font-weight: 700;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-page .jpb-toc-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-page .jpb-toc-link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.14);
  color: inherit;
  text-decoration: none;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-page .jpb-toc-link-label {
  flex: 1 1 auto;
  min-width: 0;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-page .jpb-toc-link-page {
  flex: 0 0 auto;
  font-weight: 600;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-toc-page .jpb-toc-empty {
  opacity: 0.75;
  font-style: italic;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content .jpb-desc-title {
  margin: 0 0 10px 0;
  font-family: inherit;
  font-size: 18px;
  line-height: 1.2;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content img,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content video,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-content iframe {
  max-width: 100%;
  height: auto;
}

/* Responsive helper: JS toggles .jpb-desc-hide-images on the .jpb root.
   This hides images INSIDE description content only (not the photo pages). */
.jpb.jpb-desc-hide-images .jpb-desc-content img,
.jpb.jpb-desc-hide-images .jpb-desc-content picture,
.jpb.jpb-desc-hide-images .jpb-desc-content figure,
.jpb.jpb-desc-hide-images .jpb-desc-content .wp-block-image,
.jpb.jpb-desc-hide-images .jpb-desc-content .elementor-widget-image,
.jpb.jpb-desc-hide-images .jpb-desc-content .elementor-image {
  display: none !important;
}

/* Responsive gate: when the photobook is hidden under a minimum viewport size,
   JS toggles .jpb-small-screen-blocked and replaces content with .jpb-small-screen-message. */
.jpb.jpb-small-screen-blocked {
  padding: 18px;
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 10px;
  background: #f7f7f7;
}

.jpb .jpb-small-screen-message {
  max-width: 60ch;
  margin: 0 auto;
  color: #111;
  font-size: 15px;
  line-height: 1.55;
  text-align: center;
}

/* Dedicated fullscreen viewer: center message in the viewport while keeping the back button fixed. */
body.jpb-fullscreen .jpb.jpb-small-screen-blocked {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100%;
  /* Respect safe-area insets so content never sits under notches. */
  padding: calc(18px + var(--jpb-safe-top)) calc(18px + var(--jpb-safe-right)) calc(18px + var(--jpb-safe-bottom)) calc(18px + var(--jpb-safe-left));
  box-sizing: border-box;
}

.jpb .jpb-small-screen-message p {
  margin: 0 0 10px 0;
}

.jpb .jpb-small-screen-message p:last-child {
  margin-bottom: 0;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-desc-page .jpb-desc-missing {
  opacity: 0.7;
  font-style: italic;
}

/* Legacy helper layers (no longer created). Keep them forcibly hidden if they exist. */
.jpb-engine-stpageflip .jpb-stpage-bg-layer,
.jpb-engine-stpageflip .jpb-stpage-anim-bleed {
  display: none !important;
  opacity: 0 !important;
}

/* Fallback <img> layer for StPageFlip: improves paint reliability under 3D transforms.
   We keep bg-layer for compatibility and panorama slicing, but prefer <img> for single pages. */
.jpb-engine-stpageflip .jpb-stpage-page .jpb-stpage-img {
  position: absolute;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
  pointer-events: none;
  -webkit-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
  user-select: none;
}



/* Panoramas (half-spread): show left/right half by widening the <img> and shifting it.
   JS toggles .jpb-stpage-img-half + .jpb-stpage-img-half-right and also sets inline styles,
   but keep CSS as a safety net. */
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-stpage-img.jpb-stpage-img-half {
  left: 0;
  right: auto;
  width: 200%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: cover;
  object-position: left center;
  transform: translateX(0);
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-stpage-img.jpb-stpage-img-half.jpb-stpage-img-half-right {
  left: auto;
  right: 0;
  object-position: right center;
  transform: translateX(0);
}

/* When a border frame is enabled, keep the photo inside the border opening,
   and draw the miter highlights on top (so the frame looks 3D again). */
.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page .jpb-stpage-bg-layer {
  /* In StPageFlip mode the border is a real CSS border on .jpb-media-box.
     The bg-layer is already positioned within the padding box (i.e. inside the border).
     Do NOT inset again, or the opening shrinks twice and creates side gaps. */
  inset: 0;
}

.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page .jpb-media-box::before,
.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page .jpb-media-box::after {
  /* Default: keep bevel overlays below during animation for stability.
     When NOT animating, we lift them above the photo (see rules below) to restore 3D look. */
  z-index: 1;
}

/* In StPageFlip, transforms can make clip-path overlays unreliable.
   Use a classic bevel by tinting the actual CSS border sides instead.
   This restores the 3D look without ever covering the photo. */
.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page .jpb-media-box {
  border-top-color: color-mix(in srgb, var(--jpb-border-c) 78%, #ffffff);
  border-left-color: color-mix(in srgb, var(--jpb-border-c) 82%, #ffffff);
  border-bottom-color: color-mix(in srgb, var(--jpb-border-c) 78%, #000000);
  border-right-color: color-mix(in srgb, var(--jpb-border-c) 82%, #000000);
}

.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page .jpb-media-box::before,
.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page .jpb-media-box::after {
  display: none;
}

/* Note: spread halves override the generic cover rule below. */

/* Full-spread images should read as one image across the gutter.
   Remove mat/outer frame/inner border on the spine-facing edge of each half. */
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-frame.jpb-stpage-half-left {
  padding-right: 0;
  border-right-width: 0;
  border-top-right-radius: 8px !important;
  border-bottom-right-radius: 8px !important;
  justify-content: flex-end;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-frame.jpb-stpage-half-right {
  padding-left: 0;
  border-left-width: 0;
  border-top-left-radius: 8px !important;
  border-bottom-left-radius: 8px !important;
  justify-content: flex-end;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-spread-caption-host-left,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-spread-caption-host-right {
  overflow: visible;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-spread-caption-host-active {
  z-index: 12 !important;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-spread-caption-host-left .jpb-caption,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-spread-caption-host-right .jpb-caption,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-spread-caption-host-left .jpb-caption-below,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half.jpb-stpage-spread-caption-host-right .jpb-caption-below {
  overflow: visible;
}

.jpb-engine-stpageflip .stf__item[data-jpb-spread-caption-wrap="1"] {
  overflow: visible !important;
  z-index: 6 !important;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-caption.jpb-caption-spread,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-caption-below.jpb-caption-spread {
  max-width: none;
}

.jpb-engine-stpageflip .jpb-spread-caption-overlay {
  display: none;
  position: absolute;
  z-index: 30;
  max-width: none;
  max-height: calc(100% - 16px);
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  white-space: normal;
  word-break: break-word;
  pointer-events: auto;
}

.jpb-engine-stpageflip .jpb-spread-caption-overlay.is-active {
  display: block;
}

.jpb-engine-stpageflip .jpb-spread-caption-overlay .jpb-caption-text {
  display: block;
  max-width: none;
  white-space: normal;
  word-break: break-word;
}

.jpb-engine-stpageflip.jpb-spread-caption-overlay-active .jpb-stpage-page.jpb-stpage-is-half .jpb-caption.jpb-caption-spread,
.jpb-engine-stpageflip.jpb-spread-caption-overlay-active .jpb-stpage-page.jpb-stpage-is-half .jpb-caption-below.jpb-caption-spread {
  visibility: hidden !important;
  pointer-events: none !important;
}

.jpb.jpb-debug-caption-visual .jpb-stpage-page[data-jpb-caption-debug="host"] .jpb-media-box {
  outline: 3px solid rgba(255, 84, 84, 0.95);
  outline-offset: -2px;
}

.jpb.jpb-debug-caption-visual .jpb-stpage-page[data-jpb-caption-debug="pair"] .jpb-media-box {
  outline: 3px solid rgba(72, 170, 255, 0.95);
  outline-offset: -2px;
}

.jpb.jpb-debug-caption-visual .jpb-stpage-page[data-jpb-caption-debug="host"] .jpb-frame::after,
.jpb.jpb-debug-caption-visual .jpb-stpage-page[data-jpb-caption-debug="pair"] .jpb-frame::after,
.jpb.jpb-debug-caption-visual .jpb-spread-caption-overlay[data-jpb-caption-debug="overlay"]::after {
  content: attr(data-jpb-caption-label);
  position: absolute;
  left: 8px;
  top: 8px;
  z-index: 50;
  display: inline-block;
  padding: 3px 6px;
  font: 11px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: #ffffff;
  background: rgba(0, 0, 0, 0.78);
  border-radius: 4px;
  pointer-events: none;
  white-space: nowrap;
}

.jpb.jpb-debug-caption-visual .jpb-stpage-page[data-jpb-caption-debug="host"] .jpb-frame::after {
  border: 1px solid rgba(255, 84, 84, 0.95);
}

.jpb.jpb-debug-caption-visual .jpb-stpage-page[data-jpb-caption-debug="pair"] .jpb-frame::after {
  border: 1px solid rgba(72, 170, 255, 0.95);
}

.jpb.jpb-debug-caption-visual .jpb-spread-caption-overlay[data-jpb-caption-debug="overlay"] {
  outline: 3px dashed rgba(255, 198, 64, 0.98);
  outline-offset: 1px;
  background: rgba(255, 198, 64, 0.08);
}

.jpb.jpb-debug-caption-visual .jpb-spread-caption-overlay[data-jpb-caption-debug="overlay"]::after {
  border: 1px solid rgba(255, 198, 64, 0.98);
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-caption.jpb-caption-spread-left {
  right: auto;
  width: calc(200% - (2 * (var(--jpb-mat-eff) + var(--jpb-border-w-eff))) + var(--jpb-spine-gap, var(--jpb-edge-pad-eff)));
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-caption.jpb-caption-spread-right {
  left: calc(-100% - var(--jpb-spine-gap, var(--jpb-edge-pad-eff)) + var(--jpb-mat-eff) + var(--jpb-border-w-eff));
  right: auto;
  width: calc(200% - (2 * (var(--jpb-mat-eff) + var(--jpb-border-w-eff))) + var(--jpb-spine-gap, var(--jpb-edge-pad-eff)));
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-caption-below.jpb-caption-spread-left {
  width: calc(200% + var(--jpb-spine-gap, var(--jpb-edge-pad-eff)));
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-caption-below.jpb-caption-spread-right {
  margin-left: calc(-100% - var(--jpb-spine-gap, var(--jpb-edge-pad-eff)));
  width: calc(200% + var(--jpb-spine-gap, var(--jpb-edge-pad-eff)));
}

.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-media-box.jpb-stpage-half-left {
  border-right-width: 0;
}

.jpb.jpb-has-border.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-is-half .jpb-media-box.jpb-stpage-half-right {
  border-left-width: 0;
}

/* Spread-half rendering (for items marked as layout=spread) */
.jpb-halfwrap {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.jpb-halfwrap > .jpb-img-half {
  width: 200%;
  height: 100%;
  display: block;
  object-fit: contain;
  object-position: left center;
  transform: translateX(0);
}

.jpb-halfwrap > .jpb-img-half.right {
  transform: translateX(-50%);
}

.jpb.jpb-has-frame .jpb-halfwrap > .jpb-img-half,
.jpb.jpb-cap-mat .jpb-halfwrap > .jpb-img-half {
  object-fit: cover;
}

/* Optional cover/first page (virtual spread at index 0).
   Rendered inside the right page so it can flip like other pages. */
.jpb-coverwrap {
  position: absolute;
  inset: 0;
  padding: var(--jpb-edge-pad-eff);
  box-sizing: border-box;
  display: none;
}

/* StPageFlip cover page: center the cover card vertically (especially in fullscreen). */
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-cover .jpb-coverwrap {
  display: flex !important;
  align-items: center;
  justify-content: center;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-cover .jpb-covercard {
  height: auto;
  max-height: 100%;
}

/* If the cover is rendered as a normal photo page (coverIsOnlyImage), center its content too */
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-cover .jpb-frame-stack {
  justify-content: center;
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-cover .jpb-media {
  align-items: center;
}

.jpb-on-cover .jpb-coverwrap {
  display: block;
}

.jpb-covercard {
  height: 100%;
  width: 100%;
  background: var(--jpb-page-bg);
  border-radius: 8px;
  box-shadow: var(--jpb-page-shadow, 0 10px 30px rgba(0,0,0,0.12));
  border: var(--jpb-border-w-eff) solid var(--jpb-border-c);
  padding: clamp(14px, 2.2vw, 26px);
  box-sizing: border-box;
  overflow: hidden;
  color: var(--jpb-text-c);
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
  text-align: center;
}

.jpb-covertitle {
  font-weight: 700;
  letter-spacing: 0.2px;
  font-size: clamp(20px, 2.6vw, 34px);
  line-height: 1.1;
}

.jpb-covertext {
  font-size: clamp(14px, 1.6vw, 16px);
  line-height: 1.45;
  opacity: 0.92;
  white-space: pre-wrap;
}

.jpb-coverimgwrap {
  margin-top: 4px;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  position: relative;
}

.jpb-coverimg {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 6px;
  box-shadow: 0 10px 24px rgba(0,0,0,0.12);
}

/* Cover title overlay (caption-like). */
.jpb-coverimgtitle {
  position: absolute;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);
  max-width: calc(100% - 20px);
  padding: 10px 12px;
  border-radius: 8px;
  background: rgba(0,0,0,0.58);
  color: #fff;
  box-shadow: 0 8px 20px rgba(0,0,0,0.18);
  font-weight: 700;
  letter-spacing: 0.2px;
  font-size: clamp(16px, 2.2vw, 28px);
  line-height: 1.15;
  text-align: center;
  text-wrap: balance;
  pointer-events: none;
}

.jpb-sheet {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform var(--jpb-flip-dur, 0.92s) ease;
  will-change: transform;
}

.jpb-sheet.is-flipped {
  transform: rotateY(-14deg);
}

.jpb-sheet.is-flipping-back {
  transform: rotateY(14deg);
}

.jpb-spread {
  position: absolute;
  inset: var(--jpb-inset);
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr;
  align-items: stretch;
  gap: var(--jpb-gap);
}

/* Subtle center crease to sell the "book" even when idle. */
.jpb-spread::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 30px;
  transform: translateX(-50%);
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(0,0,0,0.10), rgba(0,0,0,0) 70%);
  mix-blend-mode: multiply;
  opacity: 0.55;
}

/* Cover open/close: shift pages toward the center when "closed". */
.jpb-opening .jpb-page-left,
.jpb-opening .jpb-page-right {
  transition: transform var(--jpb-flip-dur, 0.92s) ease;
}

.jpb-closed .jpb-page-left {
  transform: translateX(calc(50% + (var(--jpb-gap) / 2)));
}

.jpb-closed .jpb-page-right {
  transform: translateX(calc(-50% - (var(--jpb-gap) / 2)));
}

.jpb-page {
  background: var(--jpb-page-bg);
  border-radius: 8px;
  box-shadow: var(--jpb-page-shadow, 0 10px 30px rgba(0,0,0,0.12));
  position: relative;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  will-change: transform, opacity;
}

/* Inner gutter shading (like stacked paper near the fold). */
.jpb-page-left::after,
.jpb-page-right::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 18px;
  pointer-events: none;
  opacity: 0.55;
}

.jpb-page-left::after {
  right: 0;
  background: linear-gradient(270deg, rgba(0,0,0,0.16), rgba(0,0,0,0));
}

.jpb-page-right::after {
  left: 0;
  background: linear-gradient(90deg, rgba(0,0,0,0.16), rgba(0,0,0,0));
}

.jpb-media {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}

/* Keep the frame away from the book spine (inner edge) */
.jpb-page-left .jpb-media {
  padding-top: var(--jpb-edge-pad-eff);
  padding-left: var(--jpb-edge-pad-eff);
  padding-right: var(--jpb-edge-pad-eff);
  padding-bottom: var(--jpb-edge-pad-eff);
}

.jpb-page-right .jpb-media {
  padding-top: var(--jpb-edge-pad-eff);
  padding-left: var(--jpb-edge-pad-eff);
  padding-right: var(--jpb-edge-pad-eff);
  padding-bottom: var(--jpb-edge-pad-eff);
}

.jpb-page-full .jpb-media {
  padding-top: var(--jpb-edge-pad-eff);
  padding-left: var(--jpb-edge-pad-eff);
  padding-right: var(--jpb-edge-pad-eff);
  padding-bottom: var(--jpb-edge-pad-eff);
}

.jpb-frame {
  position: relative;
  box-sizing: border-box;
  /* Passepartout (mat) sits OUTSIDE the frame/border. */
  border: var(--jpb-outer-border-w-eff) solid var(--jpb-outer-border-c);
  padding: var(--jpb-mat-eff);
  background: var(--jpb-mat-c);
  border-radius: 8px;
  max-width: 100%;
  max-height: 100%;
  overflow: hidden;
}

/* Frame shadow should sit on the OUTER edge of the mat+frame stack. */
.jpb.jpb-has-frame .jpb-frame {
  box-shadow: var(--jpb-frame-shadow);
}

/* Inner border/frame that sits closest to the photo. */
.jpb-media-box {
  position: relative;
  overflow: hidden;
  border-radius: 6px;
  box-sizing: border-box;
  border: 0;
  background: transparent;
}

/* Also remove rounding whenever a frame/mat is present (regardless of toned mode). */
.jpb.jpb-has-frame .jpb-frame,
.jpb.jpb-has-frame .jpb-media-box,
.jpb.jpb-has-frame .jpb-split,
.jpb.jpb-has-frame .jpb-split-half,
.jpb.jpb-has-frame .jpb-imgwrap,
.jpb.jpb-has-frame .jpb-imgwrap > .jpb-img-layer {
  border-radius: 0 !important;
}

/* === Miter frame (matches the provided .miter-frame CSS) === */
.jpb.jpb-has-border .jpb-frame {
  /* Keep mat on the outside even when a border is enabled. */
  padding: var(--jpb-mat-eff);
  background: var(--jpb-mat-c);
}

/* Frame/border sits closest to the photo (inside the mat). */
.jpb.jpb-has-border .jpb-media-box {
  border: var(--jpb-border-w-eff) solid var(--jpb-border-c);
  background: var(--jpb-border-c);
  z-index: 1;
}

.jpb.jpb-has-border .jpb-media-box::before,
.jpb.jpb-has-border .jpb-media-box::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-sizing: border-box;
  z-index: 1;
  --jpb-fw: var(--jpb-border-w-eff);
}

/* FAS */
.jpb.jpb-has-border .jpb-media-box::before {
  background: linear-gradient(
    color-mix(in srgb, var(--jpb-border-c) 85%, #ffffff),
    color-mix(in srgb, var(--jpb-border-c) 85%, #000000)
  );
  clip-path: polygon(
    0 0,
    100% 0,
    calc(100% - var(--jpb-fw)) var(--jpb-fw),
    var(--jpb-fw) var(--jpb-fw)
  );
}

/* SIDOR */
.jpb.jpb-has-border .jpb-media-box::after {
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--jpb-border-c) 90%, #ffffff),
    color-mix(in srgb, var(--jpb-border-c) 80%, #000000)
  );
  clip-path: polygon(
    0 0,
    var(--jpb-fw) var(--jpb-fw),
    var(--jpb-fw) calc(100% - var(--jpb-fw)),
    0 100%,
    100% 100%,
    calc(100% - var(--jpb-fw)) calc(100% - var(--jpb-fw)),
    calc(100% - var(--jpb-fw)) var(--jpb-fw),
    100% 0
  );
}

/* Image/split fill the opening inside the border. */
.jpb.jpb-has-border .jpb-imgwrap {
  inset: 0;
  z-index: 2;
}

.jpb.jpb-has-border .jpb-imgwrap > .jpb-img-layer {
  background: #fff;
}

/* Split rendering should also be inside the frame. */
.jpb.jpb-has-border .jpb-split {
  inset: 0;
  z-index: 2;
}

/* === VECK (ENDAT 2 HÖRN) === */
.jpb.jpb-has-border .jpb-imgwrap::before,
.jpb.jpb-has-border .jpb-imgwrap::after {
  display: none;
}

/* Ensure the pseudo-element also becomes square in square mode. */
.jpb.jpb-square .jpb-media-box::before {
  border-radius: 0 !important;
}

/* (moved earlier) */

.jpb-page-full {
  grid-column: 1 / span 2;
}

.jpb-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: transparent;
}

/* When a frame/mat is present we still want the full photo visible.
   The JS resizer sizes the opening to the image AR, so `contain` should not create letterboxing,
   and it avoids unwanted cropping (especially panoramas). */
.jpb.jpb-has-frame .jpb-img,
.jpb.jpb-cap-mat .jpb-img {
  object-fit: contain;
  object-position: center;
}

/* Double-buffered images (two layers crossfading) */
.jpb-imgwrap {
  position: absolute;
  inset: 0;
  /* Must sit above the frame pseudo-elements; otherwise a browser that can't clip them
     can end up covering the entire image area, making photos look "missing". */
  z-index: 2;
}

.jpb-imgwrap > .jpb-img-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transition: opacity 140ms linear;
  will-change: opacity;
}

/* When we snap to a final state after a flip, avoid any opacity tween that can flash an old layer. */
.jpb-no-fade .jpb-imgwrap > .jpb-img-layer {
  transition: none !important;
}

/* Split rendering (used by flip overlay for full-width spreads) */
.jpb-split {
  position: absolute;
  inset: 0;
  display: none;
  z-index: 2;
  border-radius: 6px;
  overflow: hidden;
}

.jpb-media-box.is-split .jpb-img {
  display: none;
}

.jpb-media-box.is-split .jpb-split {
  display: flex;
}

.jpb-split-half {
  flex: 1 1 50%;
  position: relative;
  overflow: hidden;
}

.jpb-split-half .jpb-img-half {
  width: 200%;
  height: 100%;
  display: block;
  object-fit: contain;
}

.jpb.jpb-has-frame .jpb-split-half .jpb-img-half,
.jpb.jpb-cap-mat .jpb-split-half .jpb-img-half {
  object-fit: cover;
}

.jpb-split-half.left .jpb-img-half {
  object-position: left center;
  transform: translateX(0);
}

.jpb-split-half.right .jpb-img-half {
  object-position: left center;
  transform: translateX(-50%);
}

.jpb-caption {
  position: absolute;
  /* Default (overlay) captions sit INSIDE the image opening (inside mat+border). */
  left: calc(var(--jpb-mat-eff) + var(--jpb-border-w-eff));
  right: calc(var(--jpb-mat-eff) + var(--jpb-border-w-eff));
  bottom: calc(var(--jpb-mat-eff) + var(--jpb-border-w-eff));
  padding: 0;
  font-family: var(--jpb-cap-font);
  font-size: var(--jpb-cap-size-eff, var(--jpb-cap-size));
  line-height: 1.35;
  color: var(--jpb-cap-color);
  pointer-events: none;
  /* Must sit above the StPageFlip <img> fallback layer (z=3). */
  z-index: 4;
  text-align: var(--jpb-cap-align);
}

/* Mat captions use a dedicated offset that is clamped to the actual mat thickness.
   Otherwise a positive mat-inset (e.g. 8px) can push the caption out of the mat on small screens. */
.jpb {
  --jpb-cap-offset-mat-eff: clamp(
    calc(0px - var(--jpb-mat-eff)),
    var(--jpb-cap-mat-inset-eff),
    max(0px, calc(var(--jpb-mat-eff) - 2px))
  );
}

.jpb-caption .jpb-caption-text {
  display: inline-block;
  max-width: 100%;
  box-sizing: border-box;
  padding: var(--jpb-cap-pad-y-eff) var(--jpb-cap-pad-x-eff);
  border-radius: 0;
  background: transparent;
  color: inherit;
}


.jpb-caption-below {
  display: none;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  color: var(--jpb-cap-color);
  font-family: var(--jpb-cap-font);
  font-size: var(--jpb-cap-size-eff, var(--jpb-cap-size));
  line-height: 1.35;
  text-align: var(--jpb-cap-align);
  background: transparent;
  /* Align text with the opening (mat+border) on below/above modes.
     This also makes left/right caption alignment feel consistent with the image area. */
  padding: 0 calc(var(--jpb-mat-eff) + var(--jpb-border-w-eff));
  pointer-events: none;
}

/* Keep below/above captions readable even when caption color is light on a light background. */
.jpb.jpb-cap-above .jpb-caption-below,
.jpb.jpb-cap-below .jpb-caption-below,
.jpb.jpb-cap-mat .jpb-caption,
.jpb.jpb-cap-mat-top .jpb-caption,
.jpb.jpb-cap-mat-bottom .jpb-caption {
  text-shadow: 0 1px 2px rgba(0,0,0,0.65);
}

.jpb-caption-below .jpb-caption-text {
  display: inline;
  background: transparent;
  padding: 0;
  border-radius: 0;
}

.jpb-caption.jpb-caption-empty,
.jpb-caption-below.jpb-caption-empty {
  display: none !important;
}

.jpb-frame-stack {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  max-width: 100%;
}

/* Caption visibility/position modes (set on .jpb root by JS) */
.jpb.jpb-cap-hide .jpb-caption {
  display: none;
}

.jpb.jpb-cap-hide .jpb-caption-below {
  display: none;
}

.jpb.jpb-cap-overlay-top .jpb-caption {
  top: calc(var(--jpb-mat-eff) + var(--jpb-border-w-eff));
  bottom: auto;
  transform: translateY(var(--jpb-cap-offset));
}

.jpb.jpb-cap-overlay-bottom .jpb-caption {
  top: auto;
  transform: translateY(calc(0px - var(--jpb-cap-offset)));
}

.jpb.jpb-page-numbers-enabled .jpb-caption,
.jpb.jpb-page-numbers-enabled .jpb-caption-below {
  padding-left: 8px;
  padding-right: 8px;
}

.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-caption,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.--left:not(.jpb-stpage-is-half) .jpb-caption-below,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-caption,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.--right:not(.jpb-stpage-is-half) .jpb-caption-below,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-half-left .jpb-caption,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-half-left .jpb-caption-below,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-half-right .jpb-caption,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-half-right .jpb-caption-below,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip.jpb-st-single-page .jpb-stpage-page .jpb-caption,
.jpb.jpb-page-numbers-enabled.jpb-engine-stpageflip.jpb-st-single-page .jpb-stpage-page .jpb-caption-below {
  padding-right: max(2.3em, 32px);
}

.jpb.jpb-cap-below .jpb-frame-stack {
  flex-direction: column;
  gap: max(0px, calc(var(--jpb-cap-gap-eff, var(--jpb-cap-gap)) + var(--jpb-cap-offset)));
}

/* Ensure below/above captions have enough reserved height for at least one readable line
   even when responsive/JS caps reduce --jpb-cap-below-h-eff aggressively. */
.jpb.jpb-cap-below,
.jpb.jpb-cap-above {
  --jpb-cap-below-reserve-eff: max(
    var(--jpb-cap-below-h-eff, var(--jpb-cap-below-h)),
    calc(var(--jpb-cap-size-eff, var(--jpb-cap-size)) * 2.4)
  );
}

.jpb.jpb-cap-below .jpb-frame {
  max-height: calc(100% - var(--jpb-cap-below-reserve-eff, var(--jpb-cap-below-h-eff, var(--jpb-cap-below-h))) - max(0px, calc(var(--jpb-cap-gap-eff, var(--jpb-cap-gap)) + var(--jpb-cap-offset))));
}

.jpb.jpb-cap-below .jpb-caption {
  display: none;
}

.jpb.jpb-cap-below .jpb-caption-below {
  display: block;
  /* Below captions sit on the page surface; use readable text color by default.
     (Many setups use white caption color intended for overlay-on-photo.) */
  color: var(--jpb-text-c, #111111);
  max-height: var(--jpb-cap-below-reserve-eff, var(--jpb-cap-below-h-eff, var(--jpb-cap-below-h)));
  overflow: hidden;
}

/* Caption in passepartout (bottom-left). */
.jpb.jpb-cap-mat .jpb-caption {
  /* Legacy/back-compat class: behave like mat-bottom caption. */
  left: var(--jpb-mat-eff);
  right: var(--jpb-mat-eff);
  top: calc(100% - var(--jpb-mat-eff));
  bottom: auto;
  padding: 0;
  background: transparent;
  color: var(--jpb-cap-color);
  text-align: var(--jpb-cap-align);
  text-shadow: none;
  transform: translateY(var(--jpb-cap-offset));
}

/* Square corners for mat + photo (requested for the angso-style layout). */
.jpb.jpb-square .jpb-frame,
.jpb.jpb-square .jpb-media-box,
.jpb.jpb-square .jpb-split,
.jpb.jpb-square .jpb-split-half {
  border-radius: 0 !important;
}

.jpb.jpb-cap-mat .jpb-caption-below {
  display: none;
}

.jpb-caption.jpb-cap-error,
.jpb-caption-below.jpb-cap-error {
  color: #b32d2e;
}

.jpb-caption.jpb-cap-error .jpb-caption-text,
.jpb-caption-below.jpb-cap-error .jpb-caption-text {
  background: rgba(255,255,255,0.92);
  text-shadow: none;
  border-radius: 6px;
}

/* Caption above image + passepartout (outside the frame/mat). */
.jpb.jpb-cap-above .jpb-frame-stack {
  flex-direction: column-reverse;
  gap: max(0px, calc(var(--jpb-cap-gap-eff, var(--jpb-cap-gap)) + var(--jpb-cap-offset)));
}

.jpb.jpb-cap-above .jpb-frame {
  max-height: calc(100% - var(--jpb-cap-below-reserve-eff, var(--jpb-cap-below-h-eff, var(--jpb-cap-below-h))) - max(0px, calc(var(--jpb-cap-gap-eff, var(--jpb-cap-gap)) + var(--jpb-cap-offset))));
}

.jpb.jpb-cap-above .jpb-caption {
  display: none;
}

.jpb.jpb-cap-above .jpb-caption-below {
  display: block;
  color: var(--jpb-text-c, #111111);
  max-height: var(--jpb-cap-below-reserve-eff, var(--jpb-cap-below-h-eff, var(--jpb-cap-below-h)));
  overflow: hidden;
  padding: 0;
}

/* Captions placed in the mat/page area should default to the normal text color. */
.jpb.jpb-cap-mat .jpb-caption,
.jpb.jpb-cap-mat-top .jpb-caption,
.jpb.jpb-cap-mat-bottom .jpb-caption {
  color: var(--jpb-text-c, #111111);
}

/* Caption in passepartout (inside mat), above or below the image opening. */
.jpb.jpb-cap-mat-top .jpb-caption {
  left: var(--jpb-mat-eff);
  right: var(--jpb-mat-eff);
  top: var(--jpb-mat-eff);
  bottom: auto;
  padding: 0;
  background: transparent;
  color: var(--jpb-cap-color);
  text-shadow: none;
  max-height: max(0px, calc(var(--jpb-mat-eff) - 2px));
  overflow: hidden;
  /* Anchor at the opening top edge and move UP into the mat.
     Positive offset moves further into mat, negative can overlap the opening. */
  transform: translateY(calc(-100% - var(--jpb-cap-offset-mat-eff)));
}

.jpb.jpb-cap-mat-bottom .jpb-caption {
  left: var(--jpb-mat-eff);
  right: var(--jpb-mat-eff);
  top: calc(100% - var(--jpb-mat-eff));
  bottom: auto;
  padding: 0;
  background: transparent;
  color: var(--jpb-cap-color);
  text-shadow: none;
  max-height: max(0px, calc(var(--jpb-mat-eff) - 2px));
  overflow: hidden;
  /* Anchor at the opening bottom edge and move DOWN into the mat.
     Positive offset moves further into mat, negative can overlap the opening. */
  transform: translateY(var(--jpb-cap-offset-mat-eff));
}

/*
  Small-screen safety: when mat captions don't fit in the passepartout, JS can add
  `.jpb-cap-mat-fallback` to render the caption inside the opening instead.
  The class is only toggled for small pages, so desktop layout stays unchanged.
*/
.jpb.jpb-cap-mat-fallback.jpb-cap-mat .jpb-caption,
.jpb.jpb-cap-mat-fallback.jpb-cap-mat-top .jpb-caption,
.jpb.jpb-cap-mat-fallback.jpb-cap-mat-bottom .jpb-caption {
  /* Anchor inside the opening, with a little air from the frame/border. */
  left: calc(var(--jpb-mat-eff) + var(--jpb-border-w-eff) + 8px + var(--jpb-open-gap-left, 0px));
  right: auto;
  top: auto;
  bottom: calc(var(--jpb-mat-eff) + var(--jpb-border-w-eff) + 8px + var(--jpb-open-gap-bottom, 0px));
  max-height: none;
  overflow: visible;
  transform: none;
  padding: 0;
  background: transparent;
  color: var(--jpb-cap-color);
  /* Always place the pill at bottom-left on fallback (requested). */
  text-align: left;
  /* Prevent long captions from spanning the whole opening on tiny screens. */
  max-width: 78%;
  font-size: clamp(9px, calc(var(--jpb-page-w-eff, 360px) * 0.026), 12px);
  line-height: 1.2;
  z-index: 12;
  pointer-events: none;
}

/* If JS moves the fallback caption into the opening itself, anchor to the opening interior.
   This guarantees the pill never straddles the passepartout/mat. */
.jpb.jpb-cap-mat-fallback .jpb-media-box > .jpb-caption {
  left: 8px;
  right: auto;
  top: auto;
  bottom: 8px;
  max-width: 92%;
}

/* Only the text gets the "pill" background (so it doesn't fill full width). */
.jpb.jpb-cap-mat-fallback.jpb-cap-mat .jpb-caption .jpb-caption-text,
.jpb.jpb-cap-mat-fallback.jpb-cap-mat-top .jpb-caption .jpb-caption-text,
.jpb.jpb-cap-mat-fallback.jpb-cap-mat-bottom .jpb-caption .jpb-caption-text {
  display: inline-block;
  max-width: 100%;
  box-sizing: border-box;
  padding: clamp(3px, calc(var(--jpb-page-w-eff, 360px) * 0.010), 6px)
           clamp(6px, calc(var(--jpb-page-w-eff, 360px) * 0.020), 10px);
  background: rgba(0,0,0,0.55);
  color: #ffffff;
  text-shadow: 0 1px 2px rgba(0,0,0,0.8);
  border-radius: 6px;
  word-break: break-word;
}

/* Prefer CSS `translate` (separate from `transform`) when available.
   This lets us un-mirror back-face captions with `transform: scaleX(-1)` without
   breaking the user's caption offset. */
@supports (translate: 0 1px) {
  .jpb.jpb-cap-overlay-top .jpb-caption {
    transform: none;
    translate: 0 var(--jpb-cap-offset);
  }

  .jpb.jpb-cap-overlay-bottom .jpb-caption {
    transform: none;
    translate: 0 calc(0px - var(--jpb-cap-offset));
  }

  .jpb.jpb-cap-mat .jpb-caption {
    transform: none;
    translate: 0 var(--jpb-cap-offset);
  }

  .jpb.jpb-cap-mat-top .jpb-caption {
    transform: none;
    translate: 0 calc(-100% - var(--jpb-cap-offset-mat-eff));
  }

  .jpb.jpb-cap-mat-bottom .jpb-caption {
    transform: none;
    translate: 0 var(--jpb-cap-offset-mat-eff);
  }

  .jpb.jpb-cap-mat-fallback.jpb-cap-mat .jpb-caption,
  .jpb.jpb-cap-mat-fallback.jpb-cap-mat-top .jpb-caption,
  .jpb.jpb-cap-mat-fallback.jpb-cap-mat-bottom .jpb-caption {
    translate: 0 0;
  }

}

.jpb.jpb-cap-mat-top .jpb-caption-below,
.jpb.jpb-cap-mat-bottom .jpb-caption-below {
  display: none;
}

/* Mat/above/below captions should not get the overlay "pill" background. */
.jpb.jpb-cap-mat .jpb-caption .jpb-caption-text,
.jpb.jpb-cap-mat-top .jpb-caption .jpb-caption-text,
.jpb.jpb-cap-mat-bottom .jpb-caption .jpb-caption-text,
.jpb.jpb-cap-above .jpb-caption-below .jpb-caption-text,
.jpb.jpb-cap-below .jpb-caption-below .jpb-caption-text {
  background: transparent;
  padding: 0;
  border-radius: 0;
}

/* Note: we intentionally do NOT reserve extra mat space for captions.
   If the mat is too small, the caption will be clipped instead of making the mat thicker. */

/* Flip overlay (strong flip animation) */
.jpb-flip {
  position: absolute;
  top: var(--jpb-inset);
  bottom: var(--jpb-inset);
  width: calc((100% - (2 * var(--jpb-inset)) - var(--jpb-gap)) / 2);
  transform-style: preserve-3d;
  display: none;
  will-change: transform;
  transform: translateZ(0);
  z-index: 5;
  pointer-events: none;
}

/* Interactive (drag-controlled) flip: JS sets --jpb-flip-angle/shift/shadow/crease */
.jpb-flip.is-interactive {
  animation: none !important;
  transform: translateX(var(--jpb-flip-shift, 0px)) rotateY(var(--jpb-flip-angle, 0deg)) rotateX(var(--jpb-flip-tilt, 0deg)) translateZ(0px);
}

.jpb-flip.is-interactive.is-settling {
  transition: transform 320ms cubic-bezier(0.15, 0.8, 0.15, 1);
}

/* Navigation controls (overlayed arrows) */
.jpb-controls {
  /* Bottom bar: takes its own space under the viewport */
  position: relative;
  display: block;
  /* Keep buttons on the same surface color as the book and let gutter continue below. */
  background: var(--jpb-page-bg);
  padding-top: 6px;
  padding-left: var(--jpb-gutter-eff);
  padding-right: var(--jpb-gutter-eff);
  padding-bottom: var(--jpb-gutter-eff);
  min-height: calc(46px + var(--jpb-gutter-eff));
  box-sizing: border-box;
  pointer-events: auto;
  z-index: 3;
  flex: 0 0 auto;
}

.jpb-flip.is-prep {
  display: block;
}

.jpb-flip.is-forward {
  left: calc(var(--jpb-inset) + ((100% - (2 * var(--jpb-inset)) - var(--jpb-gap)) / 2) + var(--jpb-gap));
  transform-origin: left center;
}

.jpb-flip.is-back {
  left: var(--jpb-inset);
  transform-origin: right center;
}

.jpb-flip.is-full {
  left: var(--jpb-inset);
  width: calc(100% - (2 * var(--jpb-inset)));
}

.jpb-flip.is-full.is-forward {
  transform-origin: center center;
}

.jpb-flip.is-full.is-back {
  transform-origin: center center;
}

.jpb-crease {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-1px);
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    180deg,
    rgba(0,0,0,0),
    rgba(0,0,0,0.35),
    rgba(255,255,255,0.25),
    rgba(0,0,0,0.35),
    rgba(0,0,0,0)
  );
  mix-blend-mode: multiply;
}

.jpb-flip.is-interactive .jpb-crease {
  opacity: var(--jpb-flip-crease, 0);
  animation: none;
}

@keyframes jpbCreasePulse {
  0% { opacity: 0.0; }
  30% { opacity: 0.35; }
  55% { opacity: 0.70; }
  80% { opacity: 0.35; }
  100% { opacity: 0.0; }
}

.jpb-flip-face {
  position: absolute;
  inset: 0;
  border-radius: 8px;
  overflow: hidden;
  /* Softer, more "paper" shadow (less steel-plate look). */
  box-shadow: 0 10px 28px rgba(0,0,0,0.18);
  background: linear-gradient(180deg, #ffffff, #fbfbfb);
  transform-style: preserve-3d;
  backface-visibility: hidden;
}

.jpb-flip-template {
  display: none !important;
}

/* Sliced page rendering: each slice shows a clipped portion of the page clone. */
.jpb-flip-slices {
  position: absolute;
  inset: 0;
  display: flex;
  transform-style: preserve-3d;
}

.jpb-flip-slice {
  flex: 1 1 0;
  position: relative;
  overflow: hidden;
  transform-style: preserve-3d;
  /* Default; overridden per-direction for a spine-like bend. */
  transform-origin: center center;
  will-change: transform;
  backface-visibility: hidden;
  /* Avoid visible seams between slices due to subpixel rounding. */
  margin-left: -1px;
}

.jpb-flip-slice:first-child {
  margin-left: 0;
}

.jpb-flip-slice .jpb-slice-content {
  position: absolute;
  top: 0;
  left: calc(var(--jpb-slice-i, 0) * -100%);
  width: calc(var(--jpb-slice-n, 8) * 100%);
  height: 100%;
}

/* During drag, JS sets per-slice bend variables. */
.jpb-flip.is-interactive .jpb-flip-slice {
  transform: rotateY(var(--jpb-slice-bend, 0deg)) translateZ(var(--jpb-slice-z, 0px));
}

/* Bend around the spine edge, not the slice center (much more visible). */
.jpb-flip.is-forward .jpb-flip-slice {
  transform-origin: left center;
}

.jpb-flip.is-back .jpb-flip-slice {
  transform-origin: right center;
}

@keyframes jpbSliceBendFwd {
  0% { transform: rotateY(0deg) translateZ(0px); }
  55% { transform: rotateY(var(--jpb-bend-max, 0deg)) translateZ(var(--jpb-bend-z, 0px)); }
  100% { transform: rotateY(0deg) translateZ(0px); }
}

@keyframes jpbSliceBendBack {
  0% { transform: rotateY(0deg) translateZ(0px); }
  55% { transform: rotateY(calc(-1 * var(--jpb-bend-max, 0deg))) translateZ(var(--jpb-bend-z, 0px)); }
  100% { transform: rotateY(0deg) translateZ(0px); }
}

/* Click flip: run a per-slice bend animation while the leaf rotates. */
.jpb-flip.is-forward.is-active .jpb-flip-slice {
  animation: jpbSliceBendFwd var(--jpb-flip-dur, 0.92s) cubic-bezier(0.15, 0.8, 0.15, 1) both;
}

.jpb-flip.is-back.is-active .jpb-flip-slice {
  animation: jpbSliceBendBack var(--jpb-flip-dur, 0.92s) cubic-bezier(0.15, 0.8, 0.15, 1) both;
}

/* Paper curl shading that moves during the flip (best-effort illusion). */
.jpb-flip-face::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  /* Soft vertical gradient with a bright band (curl highlight) and a darker band (curl shadow). */
  background: linear-gradient(
    90deg,
    rgba(255,255,255,0.00) 0%,
    rgba(255,255,255,0.00) 30%,
    rgba(255,255,255,0.35) 48%,
    rgba(0,0,0,0.12) 58%,
    rgba(0,0,0,0.00) 100%
  );
  mix-blend-mode: multiply;
  transform: translateX(0%);
}

.jpb-flip.is-forward.is-active .jpb-flip-face.front::before {
  animation: jpbCurlFwd var(--jpb-flip-dur, 0.92s) ease both;
}

.jpb-flip.is-back.is-active .jpb-flip-face.front::before {
  animation: jpbCurlBack var(--jpb-flip-dur, 0.92s) ease both;
}

/* Interactive (drag) curl: JS controls --jpb-flip-curl (0..1). */
.jpb-flip.is-interactive .jpb-flip-face.front::before {
  opacity: calc(0.55 * var(--jpb-flip-curl, 0));
  transform: translateX(calc((1 - var(--jpb-flip-curl, 0)) * 16%));
}

@keyframes jpbCurlFwd {
  0% { opacity: 0.00; transform: translateX(22%); }
  25% { opacity: 0.22; transform: translateX(10%); }
  55% { opacity: 0.50; transform: translateX(-8%); }
  100% { opacity: 0.14; transform: translateX(-18%); }
}

@keyframes jpbCurlBack {
  0% { opacity: 0.00; transform: translateX(-22%); }
  25% { opacity: 0.22; transform: translateX(-10%); }
  55% { opacity: 0.50; transform: translateX(8%); }
  100% { opacity: 0.14; transform: translateX(18%); }
}

.jpb-flip-face.back {
  transform: rotateY(180deg);
}

@keyframes jpbFlipFwd {
  /* Tiny lateral settle BEFORE rotation starts (more discrete). */
  0% { transform: translateX(0) rotateY(0deg) rotateX(0deg) skewY(0deg) translateZ(0px); }
  8% { transform: translateX(calc(-1 * var(--jpb-pre-shift))) rotateY(0deg) rotateX(0deg) skewY(0deg) translateZ(0px); }
  /* Add slight tilt + skew mid-flip to fake curvature. */
  40% { transform: translateX(calc(-1 * var(--jpb-pre-shift))) rotateY(-85deg) rotateX(1.2deg) skewY(-1.2deg) translateZ(0px); }
  70% { transform: translateX(calc(-1 * var(--jpb-pre-shift))) rotateY(-150deg) rotateX(0.8deg) skewY(-0.6deg) translateZ(0px); }
  100% { transform: translateX(calc(-1 * var(--jpb-pre-shift))) rotateY(-180deg) rotateX(0deg) skewY(0deg) translateZ(0px); }
}

@keyframes jpbFlipBack {
  /* Tiny lateral settle BEFORE rotation starts (more discrete). */
  0% { transform: translateX(0) rotateY(0deg) rotateX(0deg) skewY(0deg) translateZ(0px); }
  8% { transform: translateX(var(--jpb-pre-shift)) rotateY(0deg) rotateX(0deg) skewY(0deg) translateZ(0px); }
  40% { transform: translateX(var(--jpb-pre-shift)) rotateY(85deg) rotateX(1.2deg) skewY(1.2deg) translateZ(0px); }
  70% { transform: translateX(var(--jpb-pre-shift)) rotateY(150deg) rotateX(0.8deg) skewY(0.6deg) translateZ(0px); }
  100% { transform: translateX(var(--jpb-pre-shift)) rotateY(180deg) rotateX(0deg) skewY(0deg) translateZ(0px); }
}

@keyframes jpbFlipFwdFull {
  0% { transform: rotateY(0deg) scaleX(1) rotateX(0deg); }
  25% { transform: rotateY(-35deg) scaleX(0.985) rotateX(1deg); }
  55% { transform: rotateY(-110deg) scaleX(0.975) rotateX(1.5deg); }
  100% { transform: rotateY(-180deg) scaleX(0.985) rotateX(0deg); }
}

@keyframes jpbFlipBackFull {
  0% { transform: rotateY(0deg) scaleX(1) rotateX(0deg); }
  25% { transform: rotateY(35deg) scaleX(0.985) rotateX(1deg); }
  55% { transform: rotateY(110deg) scaleX(0.975) rotateX(1.5deg); }
  100% { transform: rotateY(180deg) scaleX(0.985) rotateX(0deg); }
}

.jpb-flip.is-forward.is-active {
  animation: jpbFlipFwd var(--jpb-flip-dur, 0.92s) cubic-bezier(0.15, 0.8, 0.15, 1) both;
}

.jpb-flip.is-back.is-active {
  animation: jpbFlipBack var(--jpb-flip-dur, 0.92s) cubic-bezier(0.15, 0.8, 0.15, 1) both;
}

.jpb-flip.is-full.is-forward.is-active {
  animation: jpbFlipFwdFull var(--jpb-flip-dur, 0.92s) cubic-bezier(0.15, 0.8, 0.15, 1) both;
}

.jpb-flip.is-full.is-back.is-active {
  animation: jpbFlipBackFull var(--jpb-flip-dur, 0.92s) cubic-bezier(0.15, 0.8, 0.15, 1) both;
}

.jpb-flip.is-full.is-active .jpb-crease {
  animation: jpbCreasePulse var(--jpb-flip-dur, 0.92s) ease both;
}

/* Stronger “paper” feel: subtle highlight + shadow while turning */
.jpb-flip::before,
.jpb-flip::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: 8px;
  opacity: 0;
}

.jpb-flip::before {
  background: linear-gradient(90deg, rgba(0,0,0,0.0), rgba(0,0,0,0.52));
}

.jpb-flip::after {
  background: linear-gradient(90deg, rgba(255,255,255,0.32), rgba(255,255,255,0.0));
}

/* Direction-aware gradients for interactive flip. */
.jpb-flip.is-interactive.is-back::before {
  background: linear-gradient(270deg, rgba(0,0,0,0.0), rgba(0,0,0,0.52));
}

.jpb-flip.is-interactive.is-back::after {
  background: linear-gradient(270deg, rgba(255,255,255,0.32), rgba(255,255,255,0.0));
}

.jpb-flip.is-interactive::before,
.jpb-flip.is-interactive::after {
  opacity: var(--jpb-flip-shadow, 0);
  animation: none;
}

@keyframes jpbShadowFwd {
  0% { opacity: 0.10; }
  40% { opacity: 0.55; }
  70% { opacity: 0.35; }
  100% { opacity: 0.10; }
}

@keyframes jpbShadowBack {
  0% { opacity: 0.10; }
  40% { opacity: 0.55; }
  70% { opacity: 0.35; }
  100% { opacity: 0.10; }
}

.jpb-flip.is-forward.is-active::before,
.jpb-flip.is-forward.is-active::after {
  animation: jpbShadowFwd var(--jpb-flip-dur, 0.92s) ease both;
}

.jpb-flip.is-back.is-active::before,
.jpb-flip.is-back.is-active::after {
  animation: jpbShadowBack var(--jpb-flip-dur, 0.92s) ease both;
}

/* Fullscreen: remove outer margins and fill viewport height */
body.jpb-inline-fullbrowser-active {
  overflow: hidden;
}

.jpb-inline-fullscreen-shell {
  position: relative;
}

.jpb-fullscreen-inline {
  position: fixed;
  inset: 0;
  z-index: 2147483300;
  display: none;
  background: var(--jpb-page-bg);
}

.jpb-fullscreen-inline.is-open {
  display: block;
}

body.jpb-fullscreen {
  margin: 0;
  padding: 0;
  /* Ensure the full fullscreen surface matches the selected book background. */
  background: var(--jpb-page-bg);
}

/* Fullscreen viewer: safe-area variables for the top-left HUD. */
body.jpb-fullscreen {
  --jpb-safe-top: env(safe-area-inset-top, 0px);
  --jpb-safe-left: env(safe-area-inset-left, 0px);
  --jpb-safe-right: env(safe-area-inset-right, 0px);
  --jpb-safe-bottom: env(safe-area-inset-bottom, 0px);
}

body.jpb-fullscreen .jpb-hud {
  position: fixed;
  top: calc(12px + var(--jpb-safe-top));
  left: calc(12px + var(--jpb-safe-left));
  right: auto;
  bottom: auto;
  z-index: 2147483400;
}

body.admin-bar.jpb-fullscreen .jpb-hud {
  top: calc(12px + var(--jpb-safe-top) + 32px);
}

@media screen and (max-width: 782px) {
  body.admin-bar.jpb-fullscreen .jpb-hud {
    top: calc(12px + var(--jpb-safe-top) + 46px);
  }
}

html, body.jpb-fullscreen {
  height: 100%;
}

body.jpb-fullscreen .jpb {
  width: 100vw;
  max-width: none;
  /* Let the stage decide the book height via its dynamic aspect ratio. */
  height: 100%;
  aspect-ratio: auto;
  margin: 0;
  padding: 0;
  background: var(--jpb-page-bg);

  /* Cover layout can be wider in fullscreen without feeling cramped. */
  --jpb-cover-max-w: min(860px, 62vw);
}

body.jpb-fullscreen .jpb-wrap {
  padding: 0;
  gap: 0;
  background: var(--jpb-page-bg);
  min-height: 100%;
  align-items: center;
  /* Center the whole book+controls block vertically in fullscreen. */
  justify-content: var(--jpb-fs-justify, center);
  box-sizing: border-box;
}

/* StPageFlip: avoid grey hover overlays and backface artifacts.
   NOTE: Do NOT set backface-visibility:hidden on everything.
   PageFlip uses 3D transforms and can rely on backfaces while dragging; hiding them can
   cause “blank left page” or make backward drags look like the page comes from under the book. */
.jpb-engine-stpageflip .stf__parent,
.jpb-engine-stpageflip .stf__wrapper,
.jpb-engine-stpageflip .stf__block {
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
}



/* StPageFlip basic: do not force GPU promotion on inner layers.
   Extra translateZ/will-change can trigger compositing quirks on some GPUs,
   especially on backward flips. */

.jpb-engine-stpageflip .jpb-stpage-page {
  background: var(--jpb-page-bg);
}

/* Blank filler page (odd image counts): no frame/mat, just background. */
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-blank {
  background: var(--jpb-page-bg);
}

.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-blank .jpb-media,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-blank .jpb-frame,
.jpb-engine-stpageflip .jpb-stpage-page.jpb-stpage-blank .jpb-frame-stack {
  display: none !important;
}

/* Back face is rotated 180deg in our two-sided markup.
   If we render captions on the back face, the text would otherwise appear mirrored.
   Fix by flipping ONLY the caption text span back (keeps all positioning transforms intact). */
/* No dedicated back-face markup in basic StPageFlip mode. */

/* Some StPageFlip versions inject shadow nodes; keep them from tinting photos on hover. */
.jpb-engine-stpageflip [class*="shadow"],
.jpb-engine-stpageflip [class*="Shadow"] {
  pointer-events: none;
}

body.jpb-fullscreen .jpb-stage {
  /* Use the same dynamic stage aspect ratio as embedded. */
  height: auto;
  aspect-ratio: var(--jpb-stage-ar-w, 16) / var(--jpb-stage-ar-h, 10);
}

body.jpb-fullscreen .jpb-controls {
  position: relative;
  width: 100%;
  align-self: stretch;
  left: auto;
  right: auto;
  bottom: auto;
  padding: 10px 12px;
  z-index: 2147483000;
  flex: 0 0 auto;
  background: var(--jpb-page-bg);
  box-shadow: none;
  box-sizing: border-box;
}

/* Embedded browser fullscreen (triggered by the HUD fullscreen button). */
.jpb:fullscreen,
.jpb.jpb--fs-active {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  max-width: none;
  margin: 0;
  padding: 0;
  background: var(--jpb-page-bg);
}
.jpb:-webkit-full-screen {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  max-width: none;
  margin: 0;
  padding: 0;
  background: var(--jpb-page-bg);
}

.jpb:fullscreen .jpb-wrap,
.jpb.jpb--fs-active .jpb-wrap,
.jpb:-webkit-full-screen .jpb-wrap {
  padding: 0;
  gap: 0;
  background: var(--jpb-page-bg);
  min-height: 100%;
  align-items: center;
  justify-content: var(--jpb-fs-justify, center);
}

.jpb:fullscreen .jpb-controls,
.jpb.jpb--fs-active .jpb-controls,
.jpb:-webkit-full-screen .jpb-controls {
  background: var(--jpb-page-bg);
  box-shadow: none;
}

/* HUD (like Exhibition): fullscreen + info buttons in the bottom-right. */
.jpb-hud {
  position: absolute;
  right: calc(12px + var(--jpb-safe-right));
  bottom: calc(6px + var(--jpb-safe-bottom) + var(--jpb-hud-lift, 0px));
  z-index: 12;
  display: flex;
  flex-direction: row;
  gap: 10px;
  align-items: center;
  justify-content: flex-end;
  pointer-events: auto;
}

body.jpb-fullscreen .jpb-hud {
  position: fixed;
  top: calc(12px + var(--jpb-safe-top));
  left: calc(12px + var(--jpb-safe-left));
  right: auto;
  bottom: auto;
  z-index: 2147483400;
}

.jpb:fullscreen .jpb-hud,
.jpb.jpb--fs-active .jpb-hud,
.jpb:-webkit-full-screen .jpb-hud {
  position: fixed;
  top: calc(12px + var(--jpb-safe-top));
  left: calc(12px + var(--jpb-safe-left));
  right: auto;
  bottom: auto;
}

.jpb-hudbtn {
  appearance: none;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(17, 17, 17, 0.62);
  color: rgba(255, 255, 255, 0.96);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  font-size: 18px;
  line-height: 32px;
  -webkit-tap-highlight-color: transparent;
}

.jpb-hudbtn svg {
  width: 18px;
  height: 18px;
  display: block;
  pointer-events: none;
}

/* Dedicated fullscreen exit button (top-right) like Exhibition. */
.jpb-fsclose {
  position: absolute;
  top: calc(12px + var(--jpb-safe-top));
  right: calc(12px + var(--jpb-safe-right));
  z-index: 11;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(17, 17, 17, 0.62);
  color: rgba(255, 255, 255, 0.96);
  font-size: 30px;
  line-height: 44px;
  cursor: pointer;
  display: none;
}

.jpb-fsclose:hover,
.jpb-fsclose:focus,
.jpb-fsclose:focus-visible {
  background: rgba(17, 17, 17, 0.74);
  border-color: rgba(255, 255, 255, 0.32);
  outline: none;
}

/* In real fullscreen, show the dedicated X; the enter button is redundant. */
.jpb:fullscreen .jpb-fsopen,
.jpb:-webkit-full-screen .jpb-fsopen,
.jpb.jpb--fs-active .jpb-fsopen {
  display: none;
}

.jpb:fullscreen .jpb-fsclose,
.jpb:-webkit-full-screen .jpb-fsclose {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Robust: JS adds this class when fullscreen is active within this book. */
.jpb.jpb--fs-active .jpb-fsclose {
  position: fixed;
  top: calc(12px + var(--jpb-safe-top));
  right: calc(12px + var(--jpb-safe-right));
  z-index: 2147483400;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* If a descendant gets full-screened, still show the close button. */
.jpb :fullscreen .jpb-fsclose,
.jpb :-webkit-full-screen .jpb-fsclose,
.jpb :-moz-full-screen .jpb-fsclose,
.jpb :-ms-fullscreen .jpb-fsclose {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.jpb-hudbtn:hover,
.jpb-hudbtn:focus,
.jpb-hudbtn:focus-visible {
  background: rgba(17, 17, 17, 0.74);
  border-color: rgba(255, 255, 255, 0.32);
  outline: none;
}

/* Modal (like Exhibition): basic info overlay. */
.jpb-modal {
  position: absolute;
  inset: 0;
  display: none;
  z-index: 20;
}

/* In Photobook's own fullscreen mode, other UI uses extremely high z-index. Keep the modal above that UI. */
body.jpb-fullscreen .jpb-modal {
  position: fixed;
  z-index: 2147483600;
}

.jpb-modal.is-open {
  display: block;
}

.jpb-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
}

.jpb-modal__panel {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: auto;
  max-width: min(620px, calc(100% - 24px - var(--jpb-safe-left) - var(--jpb-safe-right)));
  max-height: calc(100% - 24px - var(--jpb-safe-top) - var(--jpb-safe-bottom));
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  background: rgba(255, 255, 255, 0.96);
  color: #111;
  border: 1px solid rgba(0, 0, 0, 0.28);
  border-radius: 12px;
  padding-top: 6px;
  box-sizing: border-box;
}

@media (max-width: 640px), (max-height: 640px) {
  .jpb-modal__panel {
    top: calc(12px + var(--jpb-safe-top));
    transform: translate(-50%, 0);
  }
}

@media (max-width: 640px), (max-height: 640px) {
  .jpb-modal__panel {
    top: 12px;
    transform: translate(-50%, 0);
  }
}

.jpb-modal__close {
  appearance: none;
  position: absolute;
  top: 0;
  right: 0;
  z-index: 2;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(0, 0, 0, 0.22);
  background: rgba(255, 255, 255, 0.92);
  color: #111;
  border-radius: 999px;
  font-size: 26px;
  line-height: 44px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.jpb-modal__close:hover,
.jpb-modal__close:focus,
.jpb-modal__close:focus-visible {
  border-color: rgba(0, 0, 0, 0.32);
  background: rgba(255, 255, 255, 0.96);
  outline: none;
  box-shadow: none;
}

.jpb-modal__close:hover,
.jpb-modal__close:focus,
.jpb-modal__close:focus-visible {
  background: rgba(0, 0, 0, 0.06);
  outline: none;
}

.jpb-modal__content {
  padding: 10px 12px 14px;
  align-items: stretch;
}

.jpb-modal__title {
  font-weight: 600;
  font-size: 20px;
  text-align: center;
  margin-bottom: 8px;
}

.jpb-modal__desc {
  white-space: normal;
  font-size: 14px;
  line-height: 1.4;
}

/* Content padding is on .jpb-modal__content; reserve only for the close button here. */
.jpb-modal__body {
  padding-right: 44px;
}

.jpb-modal__desc > :first-child {
  margin-top: 0;
}

.jpb-modal__desc > :last-child {
  margin-bottom: 0;
}

.jpb-modal__nav {
  margin-top: 0;
}

.jpb-modal__navbox {
  margin-top: 18px;
  padding: 10px 12px;
  border: 1px solid rgba(0, 0, 0, 0.16);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.03);
  color: rgba(0, 0, 0, 0.82);
  white-space: normal;
  font-size: 14px;
  line-height: 1.4;
}

.jpb-modal__navbox > :first-child {
  margin-top: 0;
}

.jpb-modal__navbox > :last-child {
  margin-bottom: 0;
}

.jpb-modal__remember {
  margin-top: 10px;
  font-size: 14px;
  line-height: 1.3;
}

.jpb-modal__desc p {
  margin: 0 0 10px 0;
}

.jpb-modal__desc ul,
.jpb-modal__desc ol {
  margin: 0 0 10px 18px;
  padding: 0;
}

.jpb-modal__desc a {
  color: inherit;
  text-decoration: underline;
}

.jpb-modal__navbox p {
  margin: 0 0 10px 0;
}

.jpb-modal__navbox ul,
.jpb-modal__navbox ol {
  margin: 0 0 10px 18px;
  padding: 0;
}

.jpb-modal__navbox a {
  color: inherit;
  text-decoration: underline;
}

.jpb-modal__remember label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.jpb-modal__remember input[type="checkbox"] {
  margin: 0;
}

.jpb-modal__share {
  margin-top: 12px;
}

.jpb-modal__shareurl {
  display: block;
  width: 100%;
  border: 1px solid rgba(0, 0, 0, 0.22);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 14px;
  background: rgba(255, 255, 255, 0.92);
  white-space: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  user-select: text;
  cursor: text;
}

.jpb-modal__shareurl:focus,
.jpb-modal__shareurl:focus-visible {
  outline: none;
  border-color: rgba(0, 0, 0, 0.32);
}

.jpb-modal__shareactions {
  margin-top: 10px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
}

.jpb-modal__iconbtn {
  border: 1px solid rgba(0, 0, 0, 0.25);
  background: transparent;
  color: inherit;
  cursor: pointer;
  border-radius: 999px;
  padding: 0;
  width: 42px;
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.jpb-modal__iconbtn:hover,
.jpb-modal__iconbtn:focus,
.jpb-modal__iconbtn:focus-visible {
  border-color: rgba(0, 0, 0, 0.32);
  background: rgba(0, 0, 0, 0.06);
  outline: none;
  box-shadow: none;
}

.jpb-modal__iconbtn svg {
  width: 20px;
  height: 20px;
  display: block;
}

body.jpb-fullscreen .jpb-stage {
  border: 0;
  border-radius: 0;
}

/* Vanilla StPageFlip: no extra “animating” CSS hacks. */

/* Fullscreen keeps same nav arrow style/position */

@media (prefers-reduced-motion: reduce) {
  .jpb-flip.is-forward.is-active,
  .jpb-flip.is-back.is-active {
    animation: none;
  }
}

.jpb-controls button {
  --jpb-nav-btn-size: 40px;
  --jpb-nav-gap: 12px;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: auto;
  appearance: none;
  width: var(--jpb-nav-btn-size);
  height: var(--jpb-nav-btn-size);
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(17,17,17,0.45);
  color: rgba(255,255,255,0.96);
  font-size: 26px;
  line-height: 38px;
  text-align: center;
  padding: 0;
  cursor: pointer;
  user-select: none;
}

.jpb-controls .jpb-prev {
  left: calc(50% - (var(--jpb-nav-gap) / 2));
  transform: translate(-100%, -50%);
}

.jpb-controls .jpb-next {
  left: calc(50% + (var(--jpb-nav-gap) / 2));
}

.jpb-controls .jpb-slideshow {
  left: calc(50% + (var(--jpb-nav-gap) / 2) + var(--jpb-nav-btn-size) + var(--jpb-nav-gap));
  appearance: none;
  background: rgba(17,17,17,0.45);
  background-image: none;
  border: 1px solid rgba(255,255,255,0.18);
  box-shadow: none;
  color: rgba(255,255,255,0.96);
  font-size: 18px;
  line-height: 36px;
}

.jpb-controls .jpb-slideshow .jpb-slideshow-play {
  display: inline-block;
  width: 0;
  height: 0;
  border-top: 9px solid transparent;
  border-bottom: 9px solid transparent;
  border-left: 14px solid rgba(255,255,255,0.98);
  margin-left: 3px;
  vertical-align: middle;
}

.jpb-controls .jpb-slideshow .jpb-slideshow-pause {
  display: inline-block;
  color: rgba(255,255,255,0.96);
  font-size: 18px;
  font-weight: 700;
  line-height: 1;
  vertical-align: middle;
}

.jpb-controls .jpb-slideshow.is-running {
  background: rgba(17,17,17,0.56);
  border-color: rgba(255,255,255,0.24);
  color: rgba(255,255,255,0.92);
}

.jpb-controls .jpb-toc {
  left: calc(50% - (var(--jpb-nav-gap) / 2) - (2 * var(--jpb-nav-btn-size)) - var(--jpb-nav-gap));
}

.jpb-controls .jpb-toc {
  font-size: 22px;
  line-height: 36px;
}

.jpb-controls button:hover,
.jpb-controls button:focus,
.jpb-controls button:focus-visible {
  background: rgba(17,17,17,0.62);
  border-color: rgba(255,255,255,0.32);
  outline: none;
}

.jpb-controls button:disabled {
  visibility: hidden;
  pointer-events: none;
}

.jpb-controls.is-animating button {
  pointer-events: none;
  opacity: 0.65;
}
