/* Full-screen reader for write-ups and slide decks.
   slides-and-reader-mode.md §3 (S3-S7, S10, S12, S13).

   One shared immersive shell for both content types -- a fixed, full
   viewport (100dvh, NOT 100vh -- see §3.1) overlay that works completely
   without the Fullscreen API (required for iOS Safari, which doesn't
   support requestFullscreen() on non-<video> elements). Native fullscreen
   is called only as an enhancement by reader.js; this CSS is the real UI
   either way.

   Palette: existing warm cream/gold tokens from home.css (--ink, --paper,
   --paper-warm, --gold, --border, --shadow, --font-*) -- no new colors.

   editor-alignment-and-theming.md §8 D6: the reader is appended to
   <body> as a sibling of a themed project's page content, so it inherits
   whatever `--hpz-*` values that page's `:root` override sets
   (workspace/themes.py::render_theme_style_tag) -- no separate wiring
   needed for that part. Only the CONTENT surfaces below (article body,
   slide body/text) consume `--hpz-*`; the reader's own CHROME (title
   bar, nav buttons, progress bar) intentionally keeps the bare tokens,
   the same "app UI stays in the house palette" boundary the console
   chrome keeps -- a themed project restyles what it publishes, not the
   reading tool itself.

   Light and dark (2026-10-15). Every colour in this file comes from the
   --rd-* tokens below, set on .hpz-reader for light and overridden by
   [data-reader-theme="dark"], which reader.js sets from the reader's own
   Light/Dark switch (default: the device's prefers-color-scheme,
   remembered per browser). Dark ignores a project theme's --hpz-* colours,
   which are all light presets, but keeps its fonts and alignment. Each
   theme declares its color-scheme so a browser's forced-dark mode leaves
   the reader alone. No bold anywhere: headings are set apart by size,
   **strong** runs by colour. */

.hpz-reader {
  --rd-bg: var(--hpz-surface-alt, var(--paper-warm, #fbf3e5));
  --rd-chrome: var(--paper, #fffdf8);
  --rd-text: var(--hpz-text, var(--ink, #26221f));
  --rd-muted: var(--hpz-muted-text, var(--soft-ink, #554f49));
  --rd-strong: #7a5300;
  --rd-link: #7a5300;
  --rd-accent: var(--hpz-accent, var(--gold, #e6aa05));
  --rd-btn: var(--paper-warm, #fbf3e5);
  --rd-btn-hover: var(--gold-light, #ffd875);
  --rd-btn-hover-text: var(--ink, #26221f);
  --rd-track: rgba(38, 34, 31, .12);
  --rd-quote-bg: rgba(230, 170, 5, .1);
  --rd-slide: var(--hpz-surface, var(--paper, #fffdf8));
  --rd-shadow: var(--shadow, 0 10px 30px rgba(30, 27, 24, .1));
  color-scheme: light;
  position: fixed;
  inset: 0;
  z-index: 9999;
  height: 100dvh;
  width: 100vw;
  display: flex;
  flex-direction: column;
  background: var(--rd-bg);
  color: var(--rd-text);
  font-family: var(--hpz-body-font, var(--font-sans));
}

.hpz-reader[data-reader-theme="dark"] {
  --rd-bg: #1d1a17;
  --rd-chrome: #27231f;
  --rd-text: #eee5d6;
  --rd-muted: #c4b8a6;
  --rd-strong: #f2c868;
  --rd-link: #f2c868;
  --rd-accent: #e6aa05;
  --rd-btn: #342e28;
  --rd-btn-hover: #4a4034;
  --rd-btn-hover-text: #fff7e8;
  --rd-track: rgba(255, 247, 232, .14);
  --rd-quote-bg: rgba(242, 200, 104, .1);
  --rd-slide: #26221e;
  --rd-shadow: 0 10px 30px rgba(0, 0, 0, .45);
  color-scheme: dark;
}

.hpz-reader[hidden] {
  display: none;
}

.hpz-reader-backdrop {
  position: absolute;
  inset: 0;
  background: var(--rd-bg);
}

/* The dialog carries its own background: reader.js puts THIS element into
   native fullscreen, where the backdrop sibling above is not shown and the
   browser paints black behind a transparent element -- which made the
   write-up dark ink on near-black (Arthur, 2026-10-15). */
.hpz-reader-dialog {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
  background: var(--rd-bg);
  color: var(--rd-text);
}

.hpz-reader-dialog::backdrop {
  background: #fbf3e5;
}

.hpz-reader[data-reader-theme="dark"] .hpz-reader-dialog::backdrop {
  background: #1d1a17;
}

/* -- Chrome: title, counter, actions -- auto-hides after inactivity -- */

.hpz-reader-chrome {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  background: var(--rd-chrome);
  box-shadow: var(--rd-shadow);
  flex-shrink: 0;
  transition: transform .25s ease, opacity .25s ease;
}

@media (prefers-reduced-motion: reduce) {
  .hpz-reader-chrome {
    transition: none;
  }
}

.hpz-reader.is-chrome-hidden .hpz-reader-chrome {
  transform: translateY(-100%);
  opacity: 0;
}

.hpz-reader-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 400;
  color: var(--rd-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hpz-reader-counter {
  font-size: .85rem;
  color: var(--rd-muted);
  font-weight: 400;
  white-space: nowrap;
}

.hpz-reader-actions {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
}

.hpz-reader-btn[hidden] {
  display: none;
}

/* Icon-buttons-always-carry-a-visible-caption: every reader control shows
   its icon stacked over a text label -- never icon-only, at any width. */
.hpz-reader-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 56px;
  padding: 6px 10px;
  border: none;
  border-radius: 10px;
  background: var(--paper-warm);
  color: var(--soft-ink);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: .68rem;
  font-weight: 400;
  line-height: 1.1;
}

/* Inside the reader the buttons follow its light/dark tokens. */
.hpz-reader .hpz-reader-btn {
  background: var(--rd-btn);
  color: var(--rd-muted);
}

.hpz-reader .hpz-reader-btn:hover,
.hpz-reader .hpz-reader-btn:focus-visible {
  background: var(--rd-btn-hover);
  color: var(--rd-btn-hover-text);
}

.hpz-reader .hpz-reader-btn:focus-visible {
  outline: 2px solid var(--rd-accent);
  outline-offset: 2px;
}

.hpz-reader-btn i {
  font-size: 1rem;
}

.hpz-reader-btn:hover,
.hpz-reader-btn:focus-visible {
  background: var(--gold-light);
  color: var(--ink);
}

.hpz-reader-btn:disabled {
  opacity: .4;
  cursor: default;
}

/* -- Body: write-up (continuous scroll) or slide stage -- */

.hpz-reader-body {
  flex: 1;
  min-height: 0;
  position: relative;
  display: flex;
  overflow: hidden;
}

/* Write-up mode: a plain scrollable column. One type scale for the whole
   write-up: the lead and intro paragraphs (which carry home.css's own
   .lead/.intro sizes on the article page) read at the body size here, so
   the text no longer jumps between the first blocks and the rest. */
.hpz-reader-article {
  flex: 1;
  overflow-y: auto;
  padding: 40px 20px 80px;
  outline: none;
}

.hpz-reader-article-inner {
  max-width: 680px;
  margin: 0 auto;
  font-family: var(--hpz-body-font, var(--font-serif));
  font-size: calc(1.15rem * var(--hpz-size-scale, 1));
  font-weight: 400;
  line-height: 1.7;
  color: var(--rd-text);
  text-align: var(--hpz-body-align, left);
}

.hpz-reader-article-inner h1,
.hpz-reader-article-inner h2,
.hpz-reader-article-inner h3,
.hpz-reader-article-inner h4,
.hpz-reader-article-inner h5,
.hpz-reader-article-inner h6 {
  font-family: var(--hpz-heading-font, var(--font-display));
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: -.005em;
  color: var(--rd-text);
  text-align: var(--hpz-heading-align, left);
}

.hpz-reader-article-inner h1 {
  font-size: 1.9em;
  margin: 0 0 .7em;
}

.hpz-reader-article-inner h2 {
  font-size: 1.4em;
  margin: 1.6em 0 .5em;
}

.hpz-reader-article-inner h3 {
  font-size: 1.2em;
  margin: 1.4em 0 .4em;
}

.hpz-reader-article-inner h4,
.hpz-reader-article-inner h5,
.hpz-reader-article-inner h6 {
  font-size: 1.05em;
  margin: 1.2em 0 .4em;
}

.hpz-reader-article-inner p,
.hpz-reader-article-inner p.lead,
.hpz-reader-article-inner p.intro {
  max-width: none;
  margin: 0 0 1.1em;
  font-family: inherit;
  font-size: inherit;
  font-weight: 400;
  line-height: inherit;
  letter-spacing: normal;
  color: var(--rd-text);
  text-align: inherit;
}

.hpz-reader-article-inner strong,
.hpz-reader-article-inner b {
  font-weight: 400;
  color: var(--rd-strong);
}

.hpz-reader-article-inner a {
  color: var(--rd-link);
  text-decoration: underline;
  text-underline-offset: .15em;
}

.hpz-reader-article-inner ul,
.hpz-reader-article-inner ol {
  margin: 0 0 1.1em;
  padding-left: 1.5em;
}

.hpz-reader-article-inner ul {
  list-style: disc;
}

.hpz-reader-article-inner ol {
  list-style: decimal;
}

.hpz-reader-article-inner ul ul {
  list-style: circle;
}

.hpz-reader-article-inner li {
  margin: 0 0 .45em;
  padding-left: .2em;
}

.hpz-reader-article-inner li::marker {
  color: var(--rd-accent);
}

.hpz-reader-article-inner li > ul,
.hpz-reader-article-inner li > ol {
  margin: .45em 0 .2em;
}

.hpz-reader-article-inner blockquote {
  margin: 0 0 1.1em;
  padding: .8em 1.1em;
  border-radius: 12px;
  background: var(--rd-quote-bg);
  font-family: var(--hpz-heading-font, var(--font-display));
  text-align: var(--hpz-quote-align, left);
}

.hpz-reader-article-inner .article-references h2 {
  font-size: 1.2em;
}

.hpz-reader-article-inner .article-references ol {
  font-size: .9em;
  color: var(--rd-muted);
}

.hpz-reader-article-inner img,
.hpz-reader-article-inner video {
  max-width: 100%;
  height: auto;
  border-radius: 12px;
}

.hpz-reader-progress {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 4px;
  width: 100%;
  background: var(--rd-track);
  flex-shrink: 0;
}

.hpz-reader-progress-bar {
  height: 100%;
  width: 0%;
  background: var(--rd-accent);
}

/* Slide mode: a fixed 16:9 stage, scaled with transform to fit the
   viewport, letterboxed -- §3.2/S6. Identical proportions at any size. */
/* `[hidden]` needs to explicitly win here -- the UA stylesheet's
   `[hidden] { display: none }` has the SAME specificity as a plain class
   selector, and author CSS beats the UA sheet on a tie, so this rule's
   own `display: flex` below would otherwise override `hidden` and leave
   the stage visible (and overlapping the write-up reader) even when
   reader.js sets `.hidden = true` on it. */
.hpz-reader-stage-wrap[hidden] {
  display: none;
}

.hpz-reader-stage-wrap {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  touch-action: pan-y;
}

/* Picture slide with a zoomable image (reader.js, HpzZoom): the browser hands
   pinch and drag to the module. While zoomed the slide stops clipping so the
   picture can fill the whole stage area, and its caption text steps aside. */
.hpz-reader-stage-wrap.is-picture {
  touch-action: none;
}

.hpz-reader-stage-wrap.is-zoomed .hpz-reader-slide,
.hpz-reader-stage-wrap.is-zoomed .hpz-reader-slide-inner.layout-image {
  overflow: visible;
}

.hpz-reader-stage-wrap.is-zoomed .hpz-reader-slide-inner.layout-image > *:not(img):not(:has(img)) {
  opacity: 0;
  pointer-events: none;
}

.hpz-reader-stage-wrap.is-zoomed .hpz-reader-slide-inner.layout-image::after {
  display: none;
}

.hpz-reader-stage {
  position: relative;
  width: 1280px;
  height: 720px;
  flex-shrink: 0;
  transform: scale(var(--hpz-reader-scale, 1));
}

.hpz-reader-slide {
  position: absolute;
  inset: 0;
  width: 1280px;
  height: 720px;
  background: var(--rd-slide);
  box-shadow: var(--rd-shadow);
  overflow: hidden;
  display: none;
}

.hpz-reader-slide.is-current {
  display: block;
}

.hpz-reader-slide-inner {
  height: 100%;
  width: 100%;
  box-sizing: border-box;
  padding: 64px 72px;
  overflow: auto;
  font-family: var(--hpz-body-font, var(--font-sans));
  font-size: calc(1.5rem * var(--hpz-size-scale, 1));
  line-height: 1.5;
  color: var(--rd-text);
  text-align: var(--hpz-body-align, left);
}

.hpz-reader-slide-inner strong,
.hpz-reader-slide-inner b {
  font-weight: 400;
  color: var(--rd-strong);
}

.hpz-reader-slide-inner h1,
.hpz-reader-slide-inner h2,
.hpz-reader-slide-inner h3,
.hpz-reader-slide-inner h4,
.hpz-reader-slide-inner h5,
.hpz-reader-slide-inner h6 {
  font-family: var(--hpz-heading-font, inherit);
  font-weight: 400;
  text-align: var(--hpz-heading-align, left);
}

/* S13: the four preset layouts render distinctly. */
.hpz-reader-slide-inner.layout-title {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.hpz-reader-slide-inner.layout-title h1,
.hpz-reader-slide-inner.layout-title h2 {
  font-family: var(--hpz-heading-font, var(--font-display));
  font-size: 3.4rem;
  margin: 0 0 .3em;
  text-align: var(--hpz-heading-align, center);
}

.hpz-reader-slide-inner.layout-body h1,
.hpz-reader-slide-inner.layout-free h1,
.hpz-reader-slide-inner.layout-free h2,
.hpz-reader-slide-inner.layout-body h2 {
  font-family: var(--hpz-heading-font, var(--font-display));
  font-size: 2.2rem;
  margin: 0 0 .5em;
  text-align: var(--hpz-heading-align, left);
}

.hpz-reader-slide-inner.layout-image {
  display: flex;
  flex-direction: column;
  padding: 0;
  position: relative;
}

/* The markdown renderer wraps a standalone `![]()` image in its own <p>
   (`<p><img></p>`) -- so the DIRECT child that needs to fill the slide is
   sometimes that <p>, not the <img> itself. `:has()` targets whichever
   ancestor actually holds the image, so this works whether the image is a
   markdown image (wrapped) or raw `<img>` HTML (unwrapped). */
.hpz-reader-slide-inner.layout-image img:first-of-type {
  width: 100%;
  height: 100%;
  object-fit: cover;
  position: absolute;
  inset: 0;
}

.hpz-reader-slide-inner.layout-image > p:has(> img:only-child) {
  position: absolute;
  inset: 0;
  margin: 0;
}

.hpz-reader-slide-inner.layout-image > *:not(:has(img)) {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 6px 48px;
  color: #fff;
}

.hpz-reader-slide-inner.layout-image > *:not(:has(img)) strong,
.hpz-reader-slide-inner.layout-image > *:not(:has(img)) b {
  color: inherit;
}

/* One shared gradient behind the whole header + body block, rather than
   one per element (which banded when a picture slide had both). */
.hpz-reader-slide-inner.layout-image:has(> *:not(:has(img)))::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 70%;
  background: linear-gradient(to top, rgba(38, 34, 31, .78), transparent);
  z-index: 0;
  pointer-events: none;
}

/* The first overlaid element (after the picture) pushes the block to the
   bottom of the slide; the last gets room above the slide edge. */
.hpz-reader-slide-inner.layout-image > p:has(> img:only-child) + *:not(:has(img)),
.hpz-reader-slide-inner.layout-image > img + *:not(:has(img)) {
  margin-top: auto;
}

.hpz-reader-slide-inner.layout-image > *:not(:has(img)):last-child {
  padding-bottom: 40px;
}

/* youtube-leads.md: a picture slide's picture may be an uploaded video
   (`<p><video></p>`) or a YouTube poster (`<div data-youtube-id>`, played in
   the popup by youtube_popup.js). Either fills the slide like a photo; the
   header and text still sit over its lower part. */
.hpz-reader-slide-inner.layout-image > [data-youtube-id],
.hpz-reader-slide-inner.layout-image > p:has(> video:only-child) {
  position: absolute;
  inset: 0;
  z-index: 0;
  max-width: none;
  margin: 0;
  padding: 0;
  border-radius: 0;
  aspect-ratio: auto;
  background: #26221f;
}

.hpz-reader-slide-inner.layout-image > p:has(> video:only-child) video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
}

.hpz-reader-slide-inner.layout-image > [data-youtube-id] + *,
.hpz-reader-slide-inner.layout-image > p:has(> video:only-child) + * {
  margin-top: auto;
}

.hpz-reader-slide-inner.layout-quote {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.hpz-reader-slide-inner.layout-quote blockquote {
  font-family: var(--hpz-heading-font, var(--font-display));
  font-style: italic;
  font-size: 2.4rem;
  line-height: 1.4;
  margin: 0;
  max-width: 900px;
  text-align: var(--hpz-quote-align, center);
}

/* Tap zones -- left/right third of the stage, never over interactive
   content (reader.js checks for an interactive ancestor before treating a
   click as a tap-zone nav, §3.3). Visual affordance only on hover/focus,
   otherwise invisible so it never looks like a button drawn over content. */
.hpz-reader-tap-hint {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* -- Slide nav + live region -- */

.hpz-reader-slidenav[hidden] {
  display: none;
}

.hpz-reader-slidenav {
  display: flex;
  justify-content: center;
  gap: 12px;
  padding: 10px 0 16px;
  background: var(--rd-bg);
  flex-shrink: 0;
  transition: transform .25s ease, opacity .25s ease;
}

@media (prefers-reduced-motion: reduce) {
  .hpz-reader-slidenav {
    transition: none;
  }
}

.hpz-reader.is-chrome-hidden .hpz-reader-slidenav {
  transform: translateY(100%);
  opacity: 0;
}

.hpz-reader-sr-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

/* -- Mobile -- portrait: fit width, center vertically (§3.3) -- */

@media (max-width: 768px) {
  .hpz-reader-chrome {
    padding: 10px 12px;
  }

  .hpz-reader-title {
    font-size: .92rem;
  }

  .hpz-reader-btn {
    min-width: 44px;
    padding: 5px 8px;
  }

  .hpz-reader-article {
    padding: 24px 16px 64px;
  }

  .hpz-reader-article-inner {
    font-size: 1.05rem;
  }
}

/* -- Print / Save as PDF (S12, A4): one 16:9 slide per printed page, all
      chrome hidden, speaker notes never printed (they're never sent to
      the client's DOM at all -- see reader.js/render_deck). -- */

@media print {
  body * {
    visibility: hidden;
  }

  .hpz-reader,
  .hpz-reader * {
    visibility: visible;
  }

  .hpz-reader {
    position: static;
    height: auto;
    background: #fff;
  }

  .hpz-reader-chrome,
  .hpz-reader-slidenav,
  .hpz-reader-progress,
  .hpz-reader-tap-hint {
    display: none !important;
  }

  .hpz-reader-body,
  .hpz-reader-stage-wrap {
    overflow: visible;
    display: block;
  }

  .hpz-reader-stage {
    transform: none;
    width: 100%;
    height: 100vh;
    page-break-after: always;
  }

  .hpz-reader-slide {
    display: none !important;
    position: static;
    box-shadow: none;
    width: 100%;
    height: 100vh;
    page-break-after: always;
    break-after: page;
  }

  .hpz-reader-slide:last-child {
    page-break-after: auto;
    break-after: auto;
  }

  /* Every slide prints (one per page), not just the current one -- unlike
     screen mode where only `.is-current` is visible. */
  .hpz-reader[data-reader-type="slides"] .hpz-reader-slide {
    display: block !important;
  }

  .hpz-reader-article {
    overflow: visible;
  }
}

/* A deck's cover: the Present trigger is the page's main action, so it is a
   wide, labelled, horizontal button rather than the small stacked chrome
   button. Still icon + visible caption. */
.hpz-reader-btn.is-primary {
  flex-direction: row;
  justify-content: center;
  gap: 10px;
  min-width: 0;
  padding: 14px 32px;
  border-radius: 14px;
  background: var(--gold-light);
  color: var(--ink);
  font-size: 1.05rem;
  box-shadow: var(--shadow);
}

.hpz-reader-btn.is-primary i {
  font-size: 1.15rem;
}

.hpz-reader-btn.is-primary:hover,
.hpz-reader-btn.is-primary:focus-visible {
  background: var(--gold-light);
  filter: brightness(.95);
  color: var(--ink);
}

/* The "Read full screen" trigger beside a write-up (article page and
   public project page): an inline icon + caption pill, on its own line in
   the article's text column with room above and below, so it never butts
   against the gold rule above or the first heading below (Arthur,
   2026-10-15). Still the shared .hpz-reader-btn, just laid out in a row. */
.hpz-reader-btn.is-inline {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  width: fit-content;
  min-width: 0;
  margin: 8px 0 28px;
  padding: 8px 16px;
  border-radius: 999px;
  background: var(--paper-warm);
  color: var(--ink);
  font-size: .85rem;
  line-height: 1.2;
}

.hpz-reader-btn.is-inline i {
  font-size: .9rem;
  color: var(--gold-dark, #b98400);
}

.hpz-reader-btn.is-inline:hover,
.hpz-reader-btn.is-inline:focus-visible {
  background: var(--gold-light);
  color: var(--ink);
}

.hpz-reader-btn.is-inline:hover i,
.hpz-reader-btn.is-inline:focus-visible i {
  color: var(--ink);
}

@media (max-width: 768px) {
  .hpz-reader-btn.is-inline {
    min-width: 0;
    padding: 8px 16px;
    margin: 4px 0 24px;
  }
}

/* Arthur, 2026-10-15: the article's picture gallery inside the reader, after
   the text (reader.js appendGallery). Thumbnails open the shared photo viewer
   over the reader; uses the reader's own palette so it reads in light and dark. */
.hpz-reader-gallery {
  margin-top: 2.5em;
  padding-top: 1.5em;
  border-top: 1px solid var(--rd-track);
}

.hpz-reader-gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  margin: 0 0 1.25em;
}

.hpz-reader-gallery-thumb {
  display: block;
  padding: 0;
  border: 0;
  border-radius: 12px;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--rd-btn);
  cursor: zoom-in;
  box-shadow: var(--rd-shadow);
}

.hpz-reader-gallery-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .15s ease;
}

.hpz-reader-gallery-thumb:hover img {
  transform: scale(1.03);
}

.hpz-reader-gallery-thumb:focus-visible {
  outline: 2px solid var(--rd-accent);
  outline-offset: 2px;
}

@media print {
  .hpz-reader-gallery-open {
    display: none;
  }
}
