/* The deck design system — layout for docs/SLIDE_SPEC.md's markup contract.

   Two rules are load-bearing:
   - Everything lives under .deck / .deck-* and colors come only from the
     --deck-* theme tokens (deck-theme-*.css), so spec + stylesheets can be
     extracted as a standalone package post-v1. The only host tokens used
     are for the card chrome AROUND the canvas (border), which stays with
     the host view on extraction.
   - Slide chrome is structure around the content, never text inside it:
     slide numbers, kickers, quote marks are CSS-generated (pseudo-element
     text is invisible to innerText, so comment anchors that count
     visible-text occurrences never see it).

   Geometry: .deck is a size container; everything inside a slide is sized
   in cqi (1% of deck width) and em, so a slide is the same design at any
   width — the deck view today, the presenter's scaled canvas later. */

.deck {
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.deck-slide {
  position: relative;
  display: flex;
  flex-direction: column;
  aspect-ratio: 16 / 9;
  padding: 4.5cqi 6cqi;
  background: var(--deck-bg);
  color: var(--deck-ink);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.04);
  font-family: var(--deck-font-text);
  font-size: calc(2.5cqi * var(--deck-scale, 1));
  /* Content past the smallest type step scrolls rather than clips — the
     visible cue the fit report will formalize. */
  overflow-y: auto;
}

/* The type scale: content volume picks a discrete step (SLIDE_SPEC.md).
   Steps, not shrink-to-fit — sparse slides look intentional, dense slides
   fit. */
.deck-step-1 { --deck-scale: 1; }
.deck-step-2 { --deck-scale: 0.86; }
.deck-step-3 { --deck-scale: 0.74; }
.deck-step-4 { --deck-scale: 0.63; }

/* Slide number, bottom right. attr() content — chrome, not text. */
.deck-slide::after {
  content: attr(data-slide);
  position: absolute;
  right: 1.6cqi;
  bottom: 1.1cqi;
  font-size: 1.5cqi;
  color: var(--deck-muted);
}

.deck-content {
  width: 100%;
  /* Vertical centering that stays safe under overflow: auto margins
     collapse to zero once content exceeds the card, so a too-tall slide
     scrolls from its top (justify-content: center would push the start of
     the content above the card, unreachable by any scroll). */
  margin-block: auto;
}

/* ---- Base typography: slide scale, not document scale ----------------
   Overrides the app's .markdown-rendered reading styles (px sizes, 1.9
   line-height) with em sizes hanging off the slide's cqi base. */

.deck-slide .deck-content h1,
.deck-slide .deck-content h2,
.deck-slide .deck-content h3,
.deck-slide .deck-content h4,
.deck-slide .deck-content h5,
.deck-slide .deck-content h6 {
  font-family: var(--deck-font-display);
  font-weight: var(--deck-display-weight);
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0.9em 0 0.5em;
}

.deck-slide .deck-content h1 { font-size: 2.2em; }
.deck-slide .deck-content h2 { font-size: 1.75em; }
.deck-slide .deck-content h3 { font-size: 1.3em; }
.deck-slide .deck-content h4,
.deck-slide .deck-content h5,
.deck-slide .deck-content h6 { font-size: 1.05em; }

.deck-slide .deck-content > :first-child { margin-top: 0; }
.deck-slide .deck-content > :last-child { margin-bottom: 0; }

.deck-slide .deck-content p {
  margin: 0 0 0.7em;
  line-height: 1.5;
}

.deck-slide .deck-content ul,
.deck-slide .deck-content ol {
  margin: 0 0 0.7em;
  padding-left: 1.15em;
}

.deck-slide .deck-content li {
  margin-bottom: 0.4em;
  line-height: 1.45;
}

.deck-slide .deck-content li::marker {
  color: var(--deck-accent);
}

.deck-slide .deck-content a {
  color: var(--deck-accent);
}

.deck-slide .deck-content hr {
  border: none;
  border-top: 1px solid var(--deck-rule);
  margin: 1.2em 0;
}

.deck-slide .deck-content img {
  max-width: 100%;
  max-height: 26cqi;
  border-radius: 0.4em;
}

/* Code panels are part of the artifact: one fixed dark panel and one
   fixed highlight palette (One Dark) in every deck theme and every app
   scheme. The host flips its --code-* variables with the app's
   light/dark mode — inside a deck that painted deck-ink text on a
   near-black panel — so the panel pins every variable the host's code
   chrome reads (panel, header, hairlines, highlight tokens). */
.deck-slide .deck-content pre {
  font-size: 0.8em;
  line-height: 1.55;
  margin: 0.8em 0;
  background: #0d1117;
  color: #e6edf3;
  border: 1px solid rgb(255 255 255 / 0.09);
  --color-border: rgb(255 255 255 / 0.09);
  --code-header-bg: #161b22;
  --code-header-text: #8b8f98;
  --code-comment: #7f848e;
  --code-keyword: #c678dd;
  --code-entity: #61afef;
  --code-constant: #d19a66;
  --code-string: #98c379;
  --code-tag: #e06c75;
  --code-addition-fg: #98c379;
  --code-addition-bg: rgb(152 195 121 / 0.14);
  --code-deletion-fg: #e06c75;
  --code-deletion-bg: rgb(224 108 117 / 0.14);
}

.deck-slide .deck-content :not(pre) > code {
  background: color-mix(in srgb, var(--deck-ink) 9%, transparent);
  font-family: var(--deck-font-mono);
}

.deck-slide .deck-content blockquote {
  border-left: 3px solid var(--deck-accent);
  padding-left: 0.8em;
  color: var(--deck-muted);
  margin: 0 0 0.7em;
}

.deck-slide .deck-content table {
  font-size: 0.82em;
  margin: 0 0 0.7em;
}

.deck-slide .deck-content th {
  background: transparent;
  border: none;
  border-bottom: 2px solid var(--deck-accent);
  padding: 0.45em 0.7em;
}

.deck-slide .deck-content td {
  border: none;
  border-bottom: 1px solid var(--deck-rule);
  padding: 0.45em 0.7em;
}

.deck-slide .deck-content input[type="checkbox"] {
  width: 0.85em;
  height: 0.85em;
}

/* On a slide the diagram IS the content, so it loses the document's framed
   panel. That framing is painted from the host's --color-* tokens, which
   flip with the reader's light/dark mode — the same leak the code panel
   above pins shut — and it put an app-dark box behind a light deck's
   diagram. The expand chip is deck chrome for the same reason. */
.deck-slide .deck-content .mermaid-diagram {
  display: flex;
  justify-content: center;
  margin: 0.8em 0;
  padding: 0;
  background: none;
  border: none;
}

.deck-slide .deck-content .mermaid-diagram__expand {
  background: var(--deck-bg);
  border-color: var(--deck-rule);
  color: var(--deck-muted);
}

.deck-slide--title .deck-content .mermaid-diagram__expand {
  background: var(--deck-title-bg);
}

/* In the document the SVG sits in a padded scroll box, so a diagram too wide
   to read at the column's width can keep its real size and scroll. Neither
   half of that belongs on a slide: the slide is a fixed frame the diagram is
   sized to fit, and a scrollbar across it would be part of the artifact. */
.deck-slide .deck-content .mermaid-diagram__canvas {
  padding: 0;
  overflow: visible;
}

/* The diagram's own SVG only — a .mermaid-diagram also holds the expand
   button, whose icon is an svg too, and sizing that to the canvas puts a
   full-slide glyph over the diagram. It's addressed through the canvas
   because that's where the diagram controller puts it; a `> svg` child
   selector here silently matches nothing and the slide loses its sizing. */
.deck-slide .deck-content .mermaid-diagram__canvas > svg {
  max-width: 100%;
  max-height: 34cqi;
}

/* ---- title — a heading is the slide ---------------------------------- */

.deck-slide--title {
  padding-inline: 8cqi;
  background: var(--deck-title-bg);
  color: var(--deck-title-ink);
}

.deck-slide--title::after {
  color: var(--deck-title-ink);
  opacity: 0.55;
}

.deck-slide--title .deck-content h1,
.deck-slide--title .deck-content h2,
.deck-slide--title .deck-content h3 {
  display: grid;
  grid-template-columns: minmax(0, auto) auto;
  align-items: center;
  justify-content: start;
  font-size: 2.8em;
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0;
}

/* The accent bar: the one piece of pure art direction every deck opens
   with. */
.deck-slide--title .deck-content > h1::before,
.deck-slide--title .deck-content > h2::before,
.deck-slide--title .deck-content > h3::before {
  content: "";
  display: block;
  width: 1.6em;
  height: 0.16em;
  grid-column: 1 / -1;
  border-radius: 0.08em;
  background: var(--deck-title-accent, var(--deck-accent));
  margin-bottom: 0.55em;
}

.deck-slide--title .deck-content p {
  font-size: 1.2em;
  margin: 1em 0 0;
  color: var(--deck-title-ink);
  opacity: 0.75;
}

.deck-slide--title .deck-content a {
  color: inherit;
}

/* ---- stage — one visual gets the whole canvas ------------------------ */

.deck-slide--stage {
  /* The visual is the slide; the frame is a thin margin, not a stage
     apron. */
  padding: 2.5cqi 3.5cqi;
}

.deck-slide--stage .deck-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.7em;
}

.deck-slide--stage .deck-content > h1,
.deck-slide--stage .deck-content > h2,
.deck-slide--stage .deck-content > h3,
.deck-slide--stage .deck-content > h4 {
  font-size: 0.95em;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--deck-muted);
  margin: 0;
}

.deck-slide--stage .deck-content > p {
  margin: 0;
}

.deck-slide--stage .deck-content img {
  max-height: 44cqi;
}

/* Mermaid inlines a natural-size max-width on its SVGs, which pins a
   small diagram at thumbnail size; the stage overrides it so the diagram
   scales up to the canvas (preserveAspectRatio keeps it undistorted). */
.deck-slide--stage .deck-content .mermaid-diagram {
  width: 100%;
}

.deck-slide--stage .deck-content .mermaid-diagram__canvas > svg {
  width: 100%;
  height: 44cqi;
  max-width: none !important;
  max-height: none;
}

.deck-slide--stage .deck-content > p:not(:has(img)) {
  font-size: 0.9em;
  color: var(--deck-muted);
}

/* ---- code — code gets the stage, not a text box ----------------------- */

.deck-slide--code {
  padding-inline: 5cqi;
}

.deck-slide--code .deck-content > h1,
.deck-slide--code .deck-content > h2,
.deck-slide--code .deck-content > h3,
.deck-slide--code .deck-content > h4 {
  font-size: 0.95em;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--deck-muted);
  margin: 0 0 1em;
}

.deck-slide--code .deck-content > p {
  font-size: 0.9em;
  color: var(--deck-muted);
}

/* ---- quote — a pull quote, oversized ---------------------------------- */

.deck-slide--quote {
  padding-inline: 8cqi;
}

.deck-slide--quote .deck-content blockquote {
  border: none;
  padding: 0;
  margin: 0;
  color: var(--deck-ink);
  font-family: var(--deck-font-display);
  font-size: 1.55em;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -0.01em;
}

.deck-slide--quote .deck-content blockquote::before {
  content: "\201C";
  display: block;
  font-size: 2.4em;
  line-height: 0.55;
  color: var(--deck-accent);
  margin-bottom: 0.25em;
}

.deck-slide--quote .deck-content blockquote p {
  margin: 0 0 0.4em;
  line-height: inherit;
}

.deck-slide--quote .deck-content > p {
  font-size: 0.95em;
  color: var(--deck-muted);
  margin: 1.2em 0 0;
}

/* ---- table — scaled tabular typography -------------------------------- */

.deck-slide--table .deck-content table {
  width: 100%;
  font-size: 0.78em;
}

.deck-slide--table .deck-content th {
  font-weight: 650;
  letter-spacing: 0.03em;
}

.deck-slide--table .deck-content > p {
  font-size: 0.9em;
  color: var(--deck-muted);
}

/* ---- columns — two lists sit side by side ----------------------------- */

.deck-slide--columns .deck-content {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 2.6em;
  align-items: start;
}

.deck-slide--columns .deck-content > h1,
.deck-slide--columns .deck-content > h2,
.deck-slide--columns .deck-content > h3,
.deck-slide--columns .deck-content > h4 {
  grid-column: 1 / -1;
}

.deck-slide--columns .deck-content > ul,
.deck-slide--columns .deck-content > ol {
  margin: 0;
}

/* ---- directory — one long inventory flows into two columns ------------
   The list is the slide's only body block, so CSS multi-column balances
   it; an ordered list keeps continuous numbering down-then-across. */

.deck-slide--directory .deck-content > ul,
.deck-slide--directory .deck-content > ol {
  columns: 2;
  column-gap: 2.6em;
  margin: 0;
}

.deck-slide--directory .deck-content li {
  break-inside: avoid;
  margin-bottom: 0.3em;
  line-height: 1.4;
}

/* ---- split — media pane beside content --------------------------------
   DOM order is source order (media leading → left pane, trailing → right),
   so grid auto-placement does the layout with zero reordering. Guarded on
   the wrappers: when the renderer skipped them (sanitized-away shape
   mismatch), the slide keeps the content layout. */

.deck-slide--split .deck-content:has(> .deck-media) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 2.6em;
  align-items: center;
}

.deck-slide--split .deck-content > h1,
.deck-slide--split .deck-content > h2,
.deck-slide--split .deck-content > h3,
.deck-slide--split .deck-content > h4 {
  grid-column: 1 / -1;
}

.deck-slide--split .deck-media > p {
  margin: 0;
}

.deck-slide--split .deck-media img {
  width: 100%;
  max-height: 40cqi;
  object-fit: contain;
}

/* ---- presenting — the deck takes the window ---------------------------
   Present mode (coplan--deck-presenter) shows the deck as a top-layer
   popover sized to the largest 16:9 canvas that fits the viewport. That
   is the whole mechanism, in a window and under native fullscreen alike:
   viewport units mean pressing `f` needs no presentation-specific rule,
   the canvas just grows into the screen. The deck stays the size
   container, so every cqi measurement above scales with it and no
   presentation-specific typography is needed. Top layer matters:
   fixed positioning alone resolves against any backdrop-filter ancestor
   (the host's glass card), while top-layer elements always position
   against the viewport. The spread shadow blacks out the letterbox bars
   without extra DOM; ::backdrop does the same when the top layer is in
   charge. The border/padding/overflow resets neutralize the UA's popover
   styles. */

.deck--presenting {
  position: fixed;
  inset: 0;
  margin: auto;
  /* For the no-popover-API fallback: above every piece of app chrome
     (the host tops out around 300). Irrelevant in the top layer. */
  z-index: 1000;
  width: min(100vw, calc(100vh * 16 / 9));
  height: min(100vh, calc(100vw * 9 / 16));
  gap: 0;
  border: none;
  padding: 0;
  overflow: hidden;
  background: #000;
  box-shadow: 0 0 0 100vmax #000;
  /* The presenter focuses the deck on start (tabindex="-1") so stray
     focus can't soak up the navigation keys — no ring for that. */
  outline: none;
}

.deck--presenting::backdrop {
  background: #000;
}

/* Presenting is the audience view: comment anchors keep their text but
   drop their chrome — no tint, no underline. pointer-events: none means
   hovering or clicking anchored text can't raise a thread popover over
   the show; the click falls through to slide navigation. (The extra
   .deck-slide level outranks the status variants' :hover rules.) */
.deck--presenting .deck-slide .anchor-highlight {
  background: none;
  border-bottom: none;
  pointer-events: none;
}

/* Selecting text mid-show is the presenter's marker: drag across a line to
   put the room's eyes on it (the presenter swallows the drag so it doesn't
   also page forward, and starves the comment affordance). Inverse video in
   the deck's own accent, not the OS selection tint — this has to read from
   the back of a room, on a projector, in every theme. Title slides invert
   against their own canvas instead: poster's title background IS its
   accent, so an accent block would vanish there. */
.deck--presenting .deck-slide::selection,
.deck--presenting .deck-slide ::selection {
  background: var(--deck-accent);
  color: var(--deck-accent-ink);
}

.deck--presenting .deck-slide--title::selection,
.deck--presenting .deck-slide--title ::selection {
  background: var(--deck-title-accent, var(--deck-accent));
  color: var(--deck-title-bg);
}

/* ---- the pen — ephemeral ink over the canvas --------------------------
   `d` arms the pen mid-show (DeckInk): a drag paints instead of selecting.
   Strokes are a gesture in the room, so each one fades on its own timer and
   nothing is stored. The layer is a child of .deck because the presented
   deck is a top-layer popover — a sibling on the page would paint
   underneath it — and it is sized to the canvas rather than to a slide, so
   the mark stays put while the pointer roams. */

.deck-ink {
  position: absolute;
  inset: 0;
  z-index: 2;
  /* Ink never eats the click: navigation, links, and checkboxes keep
     working under it. Strokes come from listeners on the document. */
  pointer-events: none;
}

.deck-ink__stroke {
  fill: none;
  /* Themes registered before the pen existed still get a visible mark. */
  stroke: var(--deck-pen, var(--deck-accent));
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Lifts the stroke off a canvas close to its own hue without a second
     path underneath it. */
  filter: drop-shadow(0 0.15cqi 0.3cqi rgb(0 0 0 / 0.35));
}

/* A theme may art-direct its title canvas into the pen's own color
   (poster's title slide IS the accent), so the pen gets the same
   title-slide escape hatch the accent does. :has() rather than
   inheritance: the layer belongs to the canvas, not to a slide. */
.deck--presenting:has(.deck-slide--current.deck-slide--title) .deck-ink__stroke {
  stroke: var(--deck-title-pen, var(--deck-pen, var(--deck-accent)));
}

/* Held at full strength long enough to make the point, then gone. The
   stroke gets this only on release — a slow deliberate line must not start
   fading while it is still being drawn. */
.deck-ink__stroke--done {
  animation: deck-ink-fade 2.6s ease-in forwards;
}

@keyframes deck-ink-fade {
  0%, 45% { opacity: 1; }
  100% { opacity: 0; }
}

/* With the pen out the canvas is a drawing surface: no text selection to
   fight the stroke, no touch scrolling to swallow it, and a crosshair so
   the presenter can see the mode. */
.deck--inking,
.deck--inking .deck-slide {
  cursor: crosshair;
  user-select: none;
  touch-action: none;
}

/* Pen-out indicator, mirroring the slide number's inset on the other
   corner. The key hint is pseudo-element content: real text here would be
   counted as slide text by comment anchoring. */
.deck-ink-badge {
  position: absolute;
  left: 1.6cqi;
  bottom: 1.1cqi;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 0.6cqi;
  padding: 0.4cqi 0.9cqi;
  border: 1px solid var(--deck-rule);
  border-radius: 999px;
  background: var(--deck-bg);
  color: var(--deck-pen, var(--deck-accent));
  font-family: var(--deck-font-text);
  font-size: 1.5cqi;
  line-height: 1;
}

.deck-ink-badge svg {
  width: 1.8cqi;
  height: 1.8cqi;
}

.deck-ink-badge::after {
  content: "d";
  color: var(--deck-muted);
}

/* The badge's shell follows the canvas it sits on, and only the glyph is
   the pen. A theme's --deck-muted is a color for its body canvas: on an
   art-directed title canvas the hint has to come from that canvas's own
   ink instead, or it disappears into the background. */
.deck--presenting:has(.deck-slide--current.deck-slide--title) .deck-ink-badge {
  background: var(--deck-title-bg);
  color: var(--deck-title-pen, var(--deck-pen, var(--deck-accent)));
}

.deck--presenting:has(.deck-slide--current.deck-slide--title) .deck-ink-badge::after {
  color: var(--deck-title-ink);
}

/* Fullscreen is requested on the presenter wrapper (it survives
   live-update swaps of the deck inside it); paint it black so nothing of
   the page shows around the canvas. */
.deck-presenter:fullscreen {
  background: #000;
}

.deck--presenting .deck-slide {
  display: none;
}

.deck--presenting .deck-slide--current {
  display: flex;
  height: 100%;
  border: none;
  border-radius: 0;
  box-shadow: none;
  overscroll-behavior: contain;
}

.deck--presenting .deck-slide--current::after {
  content: attr(data-slide) " / " attr(data-slide-total);
}
