/**
 * presentation-widget.css — ABSOLUTE-CANVAS render layer (presentation@v1).
 * STRUCTURE + MECHANICS ONLY. Zero theming.
 *
 * This is the render face of the `presentation@v1` visual contract
 * (packages/shared/src/visual-contracts.ts · specs/spec-visual-contracts.md).
 * It realizes a slide as a FIXED 16:9 CANVAS holding absolutely-positioned
 * boxes — plain DOM only, NO html <canvas>, no raster/paint. Fonts, colors,
 * backgrounds and the layout of the content INSIDE each box belong to the
 * page's own CSS (authored by the user or the AI); the widget ships none of
 * that so every deck can look completely different.
 *
 * DOM contract (what genPrompt emits + validate enforces):
 *   <div data-pd-visual="presentation@v1" data-pageduo-presentation>
 *     <section data-pageduo-slide data-pageduo-canvas data-slide-role="cover">
 *       <div data-pageduo-box style="left:6%;top:27%;width:60%"><h1>…</h1></div>
 *       <div data-pageduo-box style="left:69%;top:15%;width:25%;height:70%"><img …></div>
 *     </section>
 *     <section data-pageduo-slide data-pageduo-canvas>…</section>
 *   </div>
 *
 *   • The <section> IS the 16:9 coordinate space (the "canvas"/slide).
 *   • Every [data-pageduo-box] is a DIRECT child of the canvas (FLAT) →
 *     ONE coordinate space, so `left/top/width/height` % mean the same for
 *     every box. Boxes are NEVER nested in boxes.
 *   • The box is the positioned unit; its guts are ordinary flow DOM.
 *
 * Two runtime modes, switched by presentation-widget.js stamping the deck
 * with data-pdp-mode="present" | "edit":
 *   - present (published / preview): one letterboxed 16:9 slide at a time,
 *     CSS scroll-snap paging; the JS owns keyboard/click nav, a fullscreen
 *     toggle and the slide counter. On an upright phone the runtime adds
 *     data-pdp-layout="flow" and the deck reflows instead (see "PRESENT on an
 *     upright PHONE" below).
 *   - edit (editor surface): slides STACK vertically with a gap so the WYSIWYG
 *     editor can scroll slide→slide and edit every one; no snap, no chrome —
 *     the editor owns editing, the runtime stays dormant.
 * Until the JS runs, the un-stamped deck falls back to the present layout
 * (graceful for no-JS environments + crawlers).
 */

/* ── The deck container ─────────────────────────────────────────────────── */
[data-pageduo-presentation] {
  /* Own a positioning + stacking context (counter / controls) without imposing
     any visual style. */
  position: relative;
  margin: 0;
  padding: 0;
  /* Gap between slides while editing; a fluid value so it reads well on any
     editor pane width. */
  --pdp-gap: clamp(16px, 3vw, 40px);
}

/* ── A single slide = a fixed 16:9 CANVAS (both modes) ──────────────────── */
[data-pageduo-slide][data-pageduo-canvas] {
  position: relative;                 /* the coordinate origin for its boxes   */
  aspect-ratio: 16 / 9;               /* the slide is ALWAYS 16:9              */
  width: 100%;                        /* base; each mode caps it below         */
  container-type: size;               /* enables cqw/cqh for proportional text */
  container-name: pdpslide;
  overflow: hidden;                   /* content never spills off the slide    */
  box-sizing: border-box;
  margin-inline: auto;                /* centre when narrower than the deck     */
}

/* The box marker → absolutely positioned. Inline style supplies left/top/width
   (+ optional height) as % of THIS slide — highest specificity, so it wins. */
/* A box whose container role has been handed to its fit wrapper (see `fitBox`). The wrapper holds
   the grid/flex layout so the author's children keep it; the box itself steps back to a plain
   block. `data-pdp-*` is stripped by the serializer, so this never reaches the saved file. */
[data-pageduo-canvas] > [data-pageduo-box][data-pdp-cfit] {
  display: block;
}

[data-pageduo-canvas] > [data-pageduo-box] {
  position: absolute;
  box-sizing: border-box;
  /* Proportional base type: 1cqh = 1% of the slide's height, so text scales
     with the slide in BOTH edit + present. Headings inheriting the default
     `em` multipliers scale with it too. Page CSS may override per element.
     Through a custom property, so the phone layout can restate it in the axis a
     flowing slide still has (cqh would there resolve against the whole screen)
     WITHOUT a higher-specificity rule that would also beat the page's own
     box-level sizes. */
  font-size: var(--pdp-box-fs, 3cqh);
  line-height: 1.3;
}

/* Media inside a box shouldn't burst its bounds. A box given an explicit height
   (e.g. style="height:70%") makes its image fill and cover; boxes sized by
   content keep the image fluid. Non-opinionated defaults — override in page CSS. */
/* `> [data-pdp-fit] >` is the fit-to-box wrapper the runtime inserts. These rules
   are DIRECT-CHILD by design (they must not reach into a nested figure), so the
   wrapper would otherwise hide the media from them — an image then loses its
   containment and bursts the box, which is exactly what happened when the wrapper
   was added. Keep both paths in step. */
[data-pageduo-box] > img,
[data-pageduo-box] > svg,
[data-pageduo-box] > video,
[data-pageduo-box] > [data-pdp-fit] > img,
[data-pageduo-box] > [data-pdp-fit] > svg,
[data-pageduo-box] > [data-pdp-fit] > video {
  display: block;
  max-width: 100%;
}
[data-pageduo-box][style*="height"] > img,
[data-pageduo-box][style*="height"] > video,
[data-pageduo-box][style*="height"] > [data-pdp-fit] > img,
[data-pageduo-box][style*="height"] > [data-pdp-fit] > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Soft fallback for a slide that (out-of-contract) lacks the canvas marker —
   keep it a visible block rather than collapsing. */
[data-pageduo-presentation] > [data-pageduo-slide]:not([data-pageduo-canvas]) {
  position: relative;
  min-height: 40vh;
  box-sizing: border-box;
}

/* ═══════════════ PRESENT mode (published / preview / no-JS) ═══════════════ */
/* The deck is the scroll container; each 16:9 slide is centred + letterboxed
   into its own viewport-tall page via scroll-snap. The margin-block trick
   reserves exactly one viewport per slide WITHOUT stretching the canvas
   (stretching would break the % coordinates), so paging is always 1-slide. */
/* PAGING IS NOT NEGOTIABLE in present mode (owner 2026-08-17). The deck's own
   CSS is AI-authored, and one stray `height:auto` or `overflow:hidden` on the
   wrapper leaves nothing to scroll — the counter advances and the slide never
   does, which is the same symptom a page-authored slide engine produces. The
   geometry that makes paging WORK is therefore `!important`; everything about how
   the deck LOOKS (colour, type, the inside of a box) is still entirely the
   page's. Scoped to present mode, so editing is untouched. */
[data-pageduo-presentation][data-pdp-mode="present"],
[data-pageduo-presentation]:not([data-pdp-mode]) {
  /* The screen the deck actually HAS. A published page carries PageDuo's sticky
     top bar in normal flow above the deck, so a deck of `100svh` made the page
     one bar taller than the screen: the document scrolled underneath a deck that
     was itself scrolling, and once it had, the sticky bar sat on the top of the
     slide. The runtime measures that bar and publishes it as --pdp-top-inset
     (unset = 0: the editor's preview, an unbadged page, a no-JS visitor). */
  --pdp-avail-h: calc(100svh - var(--pdp-top-inset, 0px));
  height: var(--pdp-avail-h) !important;
  overflow-y: auto !important;
  overflow-x: hidden !important;
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  background: #000;                   /* the letterbox bars                    */
  /* The largest 16:9 box that fits the room above (both are consistent:
     fit-h = fit-w * 9/16). */
  --pdp-fit-w: min(100vw, calc(var(--pdp-avail-h) * 16 / 9));
  --pdp-fit-h: min(var(--pdp-avail-h), calc(100vw * 9 / 16));
}

/* ── THE PAGE'S OWN MARGIN IS NOT PART OF THE LETTERBOX ────────────────────
   The deck sizes itself to the VIEWPORT (100vw × 100svh) but it lives in the
   page's flow, so any inset on <body> shifts it off that viewport instead of
   fitting inside it. The browser default alone — `body { margin: 8px }` — is
   enough: measured on an iPad (gen 7, 810px wide) the slide ran from x=8 to
   x=818, so its right-hand 8px was clipped by the deck's own `overflow-x`, and
   the document itself gained 16px of scroll underneath a surface that is
   supposed to be exactly one screen. On a pointer device that reads as a hairline;
   on a touch device you feel it, because the whole page rubber-bands against a
   deck that is already scroll-snapping (owner report 2026-08-22: "viewing on
   iPad… the view seems cut off").

   So present mode takes the page's insets to zero, for the same reason the
   paging geometry above is `!important`: the letterbox is MECHANICS, and it is
   only correct if the box it measures is the one the reader is looking at. Edit
   mode and ordinary pages are untouched (`:has()` scopes this to a deck actually
   presenting — the same mechanism that hides the site header below). */
body:has([data-pageduo-presentation][data-pdp-mode="present"]),
body:has([data-pageduo-presentation]:not([data-pdp-mode])) {
  margin: 0 !important;
  padding: 0 !important;
}

[data-pageduo-presentation][data-pdp-mode="present"] > [data-pageduo-slide][data-pageduo-canvas],
[data-pageduo-presentation]:not([data-pdp-mode]) > [data-pageduo-slide][data-pageduo-canvas] {
  /* Each slide occupies exactly one page. Enforced for the same reason as the
     deck above: a page that resizes its own slides breaks 1-slide paging, and
     the 16:9 box is what every `%` coordinate on the slide is measured against. */
  width: var(--pdp-fit-w) !important;   /* aspect-ratio derives the height      */
  aspect-ratio: 16 / 9 !important;
  margin-inline: auto !important;      /* centre horizontally (side letterbox)  */
  /* Fill the rest of the viewport above/below with scrollable space so every
     slide occupies exactly one page and snaps cleanly. */
  margin-block: calc((var(--pdp-avail-h) - var(--pdp-fit-h)) / 2) !important;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}

/* ── The page's site header is CHROME, and a slide has no room for it ──────
   A deck published from a project carries the same shared <header> nav as its
   sibling pages. On a slide that header is a strip of someone else's furniture
   across the top of the artwork: it steals vertical space from a canvas whose
   coordinates assume the whole 16:9 box, and it is the single clearest tell
   that this is a web page pretending to be a deck.

   So PRESENT mode hides it, and the widget lifts its links into the control bar
   instead (see the nav menu in presentation-widget.v1.js) — the navigation is
   still one click away, it just stops sitting on the slide.

   Scoped by :has() to a deck in present mode, so:
     • EDIT mode keeps the header — the author still has to be able to edit it;
     • an ordinary page is untouched;
     • a no-JS visitor (deck present, no mode stamp yet) gets the same hiding as
       a JS one, because the fallback layout is present mode too.
   Opt out per deck with data-pdp-header="show". */
body:has([data-pageduo-presentation][data-pdp-mode="present"]:not([data-pdp-header="show"])) > header,
body:has([data-pageduo-presentation]:not([data-pdp-mode]):not([data-pdp-header="show"])) > header {
  display: none !important;
}

/* ── A page that ships its OWN slide engine ───────────────────────────────
   Some generated decks include an inline script that pages by setting
   `style="display:none"` on every slide but one. Two engines then fight over one
   deck: the page hides six slides, and the widget — which pages by SCROLLING —
   has nothing to scroll to. The counter advances and the slide never does, which
   is precisely how it looks to a presenter (owner report 2026-08-17, measured on
   a real 7-slide deck: slides 2-7 computed 0×0).

   We do NOT rewrite the page to fix it. A generated file is the user's, a model
   can be wrong in ways a rewrite makes worse, and healing bytes is a bigger
   promise than this needs. Instead the RUNTIME guarantees what the contract
   already says: in present mode the widget owns paging, so every slide is
   visible and the hiding is overridden.

   Deliberately surgical — it matches only a slide whose INLINE style hides it,
   and `revert` hands display back to the page's own stylesheet rather than
   forcing `block` on a deck whose slides are legitimately flex or grid. A deck
   with no such script matches nothing here. */
[data-pageduo-presentation][data-pdp-mode="present"] > [data-pageduo-slide][style*="display: none"],
[data-pageduo-presentation][data-pdp-mode="present"] > [data-pageduo-slide][style*="display:none"],
[data-pageduo-presentation]:not([data-pdp-mode]) > [data-pageduo-slide][style*="display: none"],
[data-pageduo-presentation]:not([data-pdp-mode]) > [data-pageduo-slide][style*="display:none"] {
  display: revert !important;
}

/* A page's own fixed HUD at the top of the screen (a progress bar, a "◀ 1/12 ▶")
   would sit UNDER PageDuo's sticky top bar, its buttons unreachable. The runtime
   marks such elements while a deck presents; they move down by the bar's height.
   `translate` composes with whatever transform the page gave them. */
[data-pdp-below-chrome] {
  translate: 0 var(--pdp-top-inset, 0px) !important;
}
/* …and on the phone, a HUD the runtime has PROVEN is a second copy of the pill
   (prev / next and an "N/M" whose M is this deck's slide count) is hidden rather
   than left floating over the first lines of every slide. */
html:has([data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"]) [data-pdp-dupnav] {
  display: none !important;
}

/* When the deck itself is the fullscreen element, let it fill the screen. Nothing
   of the page is drawn above it there, so the top bar's inset does not apply. */
[data-pageduo-presentation]:fullscreen {
  --pdp-top-inset: 0px;
  --pd-bottom-inset: 0px;
  width: 100vw;
  height: 100vh;
  background: #000;
}
[data-pageduo-presentation]:-webkit-full-screen {
  --pdp-top-inset: 0px;
  --pd-bottom-inset: 0px;
  width: 100vw;
  height: 100vh;
  background: #000;
}

/* ═══════════════════════════ EDIT mode ════════════════════════════════════ */
/* Slides stack vertically with a gap; the editor scrolls the whole document
   and reaches every slide. Each slide fits the pane width but is capped so it
   never grows taller than the viewport — you can see one full slide and scroll
   to the next. No snap, no chrome; the editor owns editing. */
[data-pageduo-presentation][data-pdp-mode="edit"] {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--pdp-gap);
  padding: var(--pdp-gap) 0;
  height: auto;
  overflow: visible;
  scroll-snap-type: none;
  background: transparent;
}

[data-pageduo-presentation][data-pdp-mode="edit"] > [data-pageduo-slide][data-pageduo-canvas] {
  width: min(100%, calc((100svh - 2 * var(--pdp-gap)) * 16 / 9));
  flex: 0 0 auto;
  scroll-snap-align: none;
  /* A hairline slide boundary via box-SHADOW (not border) — a border would
     shrink the canvas content box and shift every box's % origin. */
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.14), 0 0 0 1px rgba(100, 116, 139, 0.30);
  border-radius: 2px;
}

/* ═══════════════ PRESENT on an upright PHONE — the FLOW layout ════════════════
   A 16:9 canvas on a 390px-wide screen is a 390×219 strip: two thirds of the
   screen is letterbox, and type authored as ~2.4cqw renders at ~9px. Nothing a
   page author does can fix that — the canvas is a fixed proportion of a screen
   whose proportions are the opposite of its own. Measured on five live decks
   (2026-09-23): 88% of their text below 12px, a median of 6–10px.

   So where the screen is portrait and narrow the runtime stamps
   data-pdp-layout="flow" and the deck REFLOWS, the way a phone reads anything:
     • each slide is a full-width section at least one screen tall, keeping its
       own background, with its boxes stacked in document order;
     • the document scrolls (not the deck) — no nested scroller, and the phone's
       own toolbars collapse the way they do on every other page;
     • the runtime scales TEXT boxes up (one zoom for the deck, chosen from the
       deck's own body size) and evens out the extremes per line of text, so body
       reads at ~16px and titles do not run to seven lines (see the JS);
     • a full-slide picture stays the slide's backdrop; a textless decoration is
       dropped, because its coordinates mean nothing once the canvas is gone.
   Landscape phones, tablets and desktops keep the 16:9 stage exactly as above;
   rotating the phone switches live. Everything here is keyed off attributes the
   runtime stamps (data-pdp-*), which the editor strips on save — no file ever
   carries this layout, and edit mode never enters it. */
[data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"] {
  height: auto !important;
  overflow: visible !important;
  scroll-snap-type: none !important;
  display: block !important;
  width: 100% !important;
  max-width: none !important;
  margin: 0 !important;
  /* No letterbox to paint: the slides are full-bleed, and the strip below the
     last one is the page's own background. */
  background: transparent !important;
  /* Room under the last slide so its final lines can scroll clear of the pill. */
  padding: 0 0 calc(76px + var(--pd-bottom-inset, 0px) + env(safe-area-inset-bottom, 0px)) !important;
}

[data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"] > [data-pageduo-slide][data-pageduo-canvas] {
  width: 100% !important;
  aspect-ratio: auto !important;
  height: auto !important;
  max-height: none !important;
  /* One screen under the top bar at least, so a short slide still reads as a
     page of its own and the next one starts below the fold. */
  min-height: calc(100svh - var(--pdp-top-inset, 0px)) !important;
  margin: 0 !important;
  padding: 36px 20px 84px !important;
  box-sizing: border-box !important;
  /* A slide the page made absolute would stack on its neighbours, and the
     backdrop's `inset:0` would resolve against the wrong box. */
  position: relative !important;
  inset: auto !important;
  display: flex !important;
  flex-direction: column !important;
  justify-content: center !important;
  align-items: stretch !important;
  gap: 16px !important;
  /* `size` containment would collapse an auto-height slide to nothing; the
     inline axis is all cqw needs. */
  container-type: inline-size !important;
  overflow: hidden !important;
  scroll-snap-align: none !important;
  scroll-margin-top: var(--pdp-top-inset, 0px);
  /* A hairline where two slides meet: consecutive slides often share one
     background, and without it their content runs together into one page. */
  box-shadow: inset 0 1px 0 rgba(128, 128, 128, 0.28);
}
[data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"] > [data-pageduo-slide][data-pageduo-canvas]:first-child {
  box-shadow: none;
}

/* A page that ships its own snap on <html> (the contract forbids it, some decks
   do it anyway) would yank a reader back to a slide's top mid-paragraph once a
   slide is taller than the screen. In flow the reader's own scroll wins. */
html:has([data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"]) {
  scroll-snap-type: none !important;
}

/* ── In flow the DOCUMENT has to scroll ──────────────────────────────────────
   On the stage the deck is its own scroller, so a page could pin itself to one
   screen (`html,body{height:100%;overflow:hidden}`, a `100vh` flex body that
   centres the deck) and nothing noticed. In flow those same rules strand every
   line below the first screen, or centre a column taller than the screen so its
   top cannot be scrolled to. So in flow <html>, <body> and every element between
   <body> and the deck (the runtime stamps them data-pdp-flowanc) give up their
   height caps and clipping. <html> goes to `visible` so <body>'s own overflow
   propagates to the viewport (instead of <body> becoming a scroller of its own,
   which would also stop the sticky top bar from sticking). */
html:has([data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"]),
html:has([data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"]) > body {
  height: auto !important;
  min-height: 0 !important;
  max-height: none !important;
}
html:has([data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"]) {
  overflow: visible !important;
}
html:has([data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"]) > body {
  overflow-y: visible !important;
  display: block !important;
  /* `place-items:center` on a block body still centres (shrink-wraps) its
     block children in Chromium — the column would be as wide as a word. */
  justify-items: normal !important;
  align-items: normal !important;
}
[data-pdp-flowanc] {
  justify-self: stretch !important;
  align-self: stretch !important;
  justify-items: normal !important;
  width: auto !important;
  height: auto !important;
  min-height: 0 !important;
  max-height: none !important;
  overflow: visible !important;
  transform: none !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  max-width: none !important;
}

/* An element the page put BETWEEN the slide and its boxes (a nested 16:9 wrapper
   — out of contract, but real decks ship one) would hold the boxes to 219px.
   The runtime stamps such wrappers; in flow they step out of layout so their
   boxes join the slide's column. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-flowwrap] {
  display: contents !important;
}

[data-pageduo-presentation][data-pdp-layout="flow"] [data-pageduo-box] {
  position: relative !important;
  inset: auto !important;
  width: auto !important;
  height: auto !important;
  min-height: 0 !important;
  max-width: none !important;
  margin: 0 !important;
  transform: none !important;
  translate: none !important;
  rotate: none !important;
  scale: none !important;
  flex: none !important;
  z-index: 1;
}
/* The canvas default `3cqh`, restated in the axis a flowing slide still has. */
[data-pageduo-presentation][data-pdp-mode="present"][data-pdp-layout="flow"] {
  --pdp-box-fs: calc(3cqw * 9 / 16);
}
/* A box the page made a SIZE container would collapse to nothing at auto height
   (size containment ignores content); the runtime stamps it and it keeps only
   inline-axis containment, so its own `cqw` still resolve against it. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-csize] {
  container-type: inline-size !important;
}
/* Text the page placed on the canvas OUTSIDE any box (a "3 / 12" chip, a
   positioned caption) joins the column in reading order instead of landing on
   top of it. The runtime stamps it data-pdp-loose. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-loose] {
  position: relative !important;
  inset: auto !important;
  transform: none !important;
  translate: none !important;
  rotate: none !important;
  scale: none !important;
  margin: 0 !important;
  max-width: 100% !important;
  align-self: flex-start;
  z-index: 1;
}

/* The fit pass shrinks a box's contents to its fixed height on the stage. There
   is no fixed height in flow, so nothing is shrunk (the runtime also resets it). */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pageduo-box] > [data-pdp-fit] {
  transform: none !important;
  width: auto !important;
  height: auto !important;
}

/* A DRAWING box (its content absolutely placed in a height:100% frame) keeps its
   stage proportions — the runtime stamps data-pdp-ratio and the matching
   aspect-ratio — and its fit wrapper fills it, so the frame has a height to be
   100% of. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pageduo-box][data-pdp-ratio] > [data-pdp-fit] {
  height: 100% !important;
}

/* A wide SVG diagram whose labels would render below ~10px is drawn wider than
   the column (the runtime sizes it: data-pdp-pan) and its container pans
   sideways, like a wide table on a phone. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-panwrap] {
  overflow-x: auto !important;
  overflow-y: hidden !important;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  /* Centred, a child wider than its container overflows on BOTH sides, and the
     left half can never be scrolled to. */
  justify-content: flex-start !important;
  justify-items: start !important;
}

/* A textless picture that covered most of the slide was its BACKDROP: it stays
   one, behind the column, filling whatever height the slide now has. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pageduo-box][data-pdp-role="bg"] {
  position: absolute !important;
  inset: 0 !important;
  z-index: 0;
  pointer-events: none;
}
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-role="bg"] :is(img, video, picture, svg),
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-role="bg"] > [data-pdp-fit] {
  width: 100% !important;
  height: 100% !important;
  max-height: none !important;
  object-fit: cover;
}

/* A textless shape (an accent bar, a blob, an empty frame) has nothing to say
   once its coordinates are gone, and stacked it is just a stray rectangle. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pageduo-box][data-pdp-role="decor"] {
  display: none !important;
}

/* A picture keeps its proportions and stays inside one screen. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-role="media"] :is(img, video, picture, svg, canvas) {
  max-width: 100% !important;
  max-height: 46svh !important;
  margin-inline: auto;
}
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-role="media"] :is(img, video) {
  height: auto !important;
  object-fit: cover;
}

/* A row or grid the author laid out inside a box collapses to one column when
   its cells would be too narrow to read (the runtime decides and stamps it). */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-stack] {
  grid-template-columns: minmax(0, 1fr) !important;
  grid-template-areas: none !important;
  flex-direction: column !important;
  flex-wrap: nowrap !important;
}
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-inline] {
  display: block !important;
}
/* …and an inline-flex badge in the middle of a sentence stays inline. */
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-inline="inline"] {
  display: inline !important;
}
[data-pageduo-presentation][data-pdp-layout="flow"] [data-pdp-stack] > * {
  grid-column: auto !important;
  grid-row: auto !important;
  width: auto !important;
  max-width: 100% !important;
  flex: none !important;
}

/* The fullscreen toggle has nothing to offer a phone held upright (and iOS has
   no element fullscreen at all); the pill keeps prev · N / M · next. */
[data-pageduo-presentation][data-pdp-layout="flow"] .pdp-btn[data-pdp-fs] {
  display: none !important;
}

/* ═══════════════════════ Present-mode chrome (JS-built) ═══════════════════ */
/* A compact control bar the runtime appends in present mode only. Neutral,
   readable on light OR dark slides without theming the deck. Positioned
   `fixed` so it stays put during scroll AND anchors correctly inside the
   fullscreen element. */
.pdp-controls {
  position: fixed;
  left: 50%;
  /* Above anything PageDuo pins to the bottom edge (the cookie notice publishes
     its height as --pd-bottom-inset while it shows — unset once the visitor
     chooses) and above the home indicator. Before this, the notice sat ON the
     pill, so a visitor's first tap on "next" landed on the notice instead. */
  bottom: calc(clamp(10px, 2.4vh, 22px) + var(--pd-bottom-inset, 0px) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 2147483000;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 8px;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.58);
  color: #ffffff;
  font: 600 12px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  user-select: none;
  opacity: 0.32;
  transition: opacity 0.25s ease;
  backdrop-filter: saturate(150%) blur(6px);
  -webkit-backdrop-filter: saturate(150%) blur(6px);
}
.pdp-controls:hover,
.pdp-controls:focus-within {
  opacity: 1;
}
/* Docked to a side when something else floats over the centre (the runtime
   hit-tests the pill after layout — see placePill). */
.pdp-controls[data-pdp-dock="left"] {
  left: max(12px, env(safe-area-inset-left, 0px));
  transform: none;
}
.pdp-controls[data-pdp-dock="right"] {
  left: auto;
  right: max(12px, env(safe-area-inset-right, 0px));
  transform: none;
}

/* ── A TOUCH DEVICE HAS NO HOVER, so 0.32 is where the bar STAYS ───────────
   The resting dim above is a deliberate design choice — the bar should recede
   into the artwork until you reach for it — but "reaching for it" is a hover,
   and a finger cannot hover. On an iPad the pill therefore sat at 32% opacity
   for the entire presentation with no gesture in the platform able to raise it:
   present, hit-testable, and effectively invisible over a bright slide (owner
   report 2026-08-22: "I don't see the transition pill at the bottom").

   So where hover does not exist, the bar simply is visible. It is small, it is
   translucent, and it is the only way to page a deck without a keyboard — on
   these devices it is not chrome to be gotten out of the way, it is the control
   surface. Fullscreen's idle auto-hide still applies on top of this when a
   device actually has fullscreen, and any input still brings it back. */
@media (hover: none) {
  .pdp-controls {
    opacity: 0.94;
  }
}

/* IDLE — after a few still seconds in fullscreen the controls and the pointer
   get out of the way entirely, and any input brings them back (the JS owns the
   timer and stamps data-pdp-idle on the deck). Hover/focus still win above, so
   a presenter reaching for the bar never has to chase it. */
[data-pageduo-presentation][data-pdp-idle] ~ .pdp-controls,
[data-pageduo-presentation][data-pdp-idle] .pdp-controls {
  opacity: 0;
  pointer-events: none;
}
[data-pageduo-presentation][data-pdp-idle] {
  cursor: none;
}

.pdp-btn {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  margin: 0;
  /* !important because a generated deck routinely ships a CSS reset that reaches
     every button on the page (`button,input{font:inherit}`, `max-width:100%`,
     `min-height:2.3em` …). The control bar is the WIDGET's chrome, not the page's
     content: the page may not size it. Owner 2026-08-18 reported the transport
     icons rendering as huge slabs. */
  padding: 0 !important;
  width: 30px !important;
  height: 30px !important;
  min-width: 0 !important;
  min-height: 0 !important;
  max-width: none !important;
  flex: none !important;
  line-height: 1 !important;
  font-size: 12px !important;
  border-radius: 50% !important;
  background: transparent;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.pdp-btn:hover {
  background: rgba(255, 255, 255, 0.16);
}
.pdp-btn:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 1px;
}
.pdp-btn svg {
  /* An <svg> with no intrinsic size falls back to 300x150 — so if these two ever
     lose, the icon does not shrink, it EXPLODES. */
  width: 18px !important;
  height: 18px !important;
  max-width: none !important;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.pdp-counter-text {
  min-width: 3.4em;
  padding: 0 2px;
  text-align: center;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

/* ── And a 30px circle is not a touch target ───────────────────────────────
   Apple's own floor is 44px, and these are the buttons a reader taps to move
   through a deck one-handed. Sized up only where the pointer is coarse, so the
   compact desktop bar is unchanged. The icon keeps its 18px so the chrome grows
   without the artwork inside it growing too. */
@media (pointer: coarse) {
  .pdp-controls {
    gap: 6px;
    padding: 6px 10px;
  }
  .pdp-btn {
    width: 44px !important;
    height: 44px !important;
  }
  .pdp-counter-text {
    font-size: 14px;
  }
}

/* Belt-and-suspenders: the runtime never builds chrome in edit mode, but if a
   mode flip leaves stale chrome behind, keep it hidden while editing. */
[data-pdp-mode="edit"] .pdp-controls {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-pageduo-presentation][data-pdp-mode="present"],
  [data-pageduo-presentation]:not([data-pdp-mode]) {
    scroll-behavior: auto;
  }
  .pdp-controls {
    transition: none;
  }
}

/* ── The lifted nav ───────────────────────────────────────────────────────
   The links the header used to show, in the control bar. Sized for a slide
   viewed from the back of a room, not for a toolbar. */
.pdp-nav { position: relative; display: inline-flex; }
.pdp-nav-panel {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%);
  min-width: 180px;
  max-height: 52vh;
  overflow-y: auto;
  padding: 6px;
  border-radius: 12px;
  background: rgba(15, 23, 42, 0.94);
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.7);
  backdrop-filter: saturate(150%) blur(8px);
  -webkit-backdrop-filter: saturate(150%) blur(8px);
}
.pdp-nav-panel a {
  display: block;
  padding: 8px 12px;
  border-radius: 8px;
  color: #f1f5f9;
  font: 500 13px/1.35 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  text-decoration: none;
  white-space: nowrap;
}
.pdp-nav-panel a:hover { background: rgba(255, 255, 255, 0.12); }
.pdp-nav-panel a[aria-current="page"] { color: #5eead4; }

/* `.pdp-btn` sets `display`, which beats the UA sheet's `[hidden]{display:none}` —
   so a hidden button stayed on screen with the attribute faithfully set. */
.pdp-btn[hidden] { display: none; }



/* An active tool has to look active — a pen that draws while the button looks
   idle is how you end up scribbling on a slide you meant to page past. */
.pdp-btn-on {
  background: rgba(255, 45, 85, 0.9);
  color: #fff;
}
