/* The Frontend Strikes Back - Custom Styles */

/*
 * Color comes from the `--nf-*` custom properties that `theme.js` writes onto
 * :root at import time, so the palette is defined in exactly one place.
 *
 * `--chapter-accent` is set per chapter by `chapters.js` on the slide element
 * and cascades into everything on that slide -- including markdown content,
 * which never receives a component prop. The values below are the fallback for
 * slides outside a chapter, and for the deck chrome, which renders as a sibling
 * above the slides rather than inside the active one (see Template).
 */
:root {
  --chapter-accent: var(--nf-green-50, #78eec5);
  --chapter-accent-base: var(--nf-green-base, #00e5a4);

  --surface-1: rgba(255, 255, 255, 0.06);
  --surface-2: rgba(255, 255, 255, 0.02);
  --hairline: rgba(255, 255, 255, 0.1);
  --muted: var(--nf-midnight-30, #97a1b8);

  --sans: "Inter", system-ui, sans-serif;
  --mono: "Fira Code", ui-monospace, SFMono-Regular, Consolas, monospace;

  /*
   * The band along the bottom of the canvas that the deck chrome owns.
   *
   * The chrome (see `Template`) is an absolutely positioned sibling ABOVE the
   * active slide, not a box inside it -- so without a reservation, content can
   * sit entirely within the 768px slide and still be laid on top of the slide
   * counter. Nothing in the layout prevents that; it used to be avoided only by
   * per-slide magic numbers, each found by measuring rather than by design.
   *
   * This number is used exactly twice, and that is the point: it sizes the
   * chrome (`.deck-chrome`) and it is subtracted from every slide (`.slide`).
   * They cannot disagree, so the space cannot be lost again.
   */
  --chrome-h: 122px;
}

/* --- Slide canvas ---------------------------------------------------------
 *
 * `.slide` is on every slide in the deck, applied in one place -- `DeckSlide`
 * in `components.js` -- rather than typed per slide. Do NOT reach for
 * `.spectacle-v7-slide`: Spectacle 10.2.3 does emit that class, but on 35
 * empty 0x0 marker divs in a sibling container, not on the slides.
 */
#root .slide {
  /* Spectacle sizes the slide `height: 100%` under the default content-box, so
   * without this the reservation grows the slide to 890px instead of shrinking
   * what is usable inside it -- the content lands in exactly the same place and
   * only the box moves. */
  box-sizing: border-box;
  padding-bottom: var(--chrome-h);
}

/*
 * The title slide has no chrome to clear -- `Template` returns `null` for slide
 * 1 -- so it owns the full canvas. `<DeckSlide chrome=${false}>` is the other
 * half of that early return; the two have to stay in agreement.
 */
#root .slide--full {
  padding-bottom: 0;
}

/* --- Page box -------------------------------------------------------------
 *
 * The UA's default `body { margin: 8px }` was never reset, and it costs
 * something in every mode:
 *
 *   on screen -- Spectacle scales the 1366x768 canvas to fit its container, and
 *                the container is the body box. 8px on each edge makes that
 *                752px tall, so the whole deck renders at 0.979 scale for no
 *                reason.
 *   on paper  -- the stacked slides are exactly 35 x 768px, and the margins push
 *                the document 16px past that. The overflow lands on a 36th sheet
 *                that is blank apart from 8px of background: the phantom last
 *                slide in `?exportMode=true` PDFs.
 *
 * `@page` margins do the same thing from the printer's side, and Save-as-PDF
 * applies them whatever the dialog's paper setting is. A slide IS the page here.
 */
html,
body {
  margin: 0;
}

@page {
  margin: 0;
}

/* --- Film grain ----------------------------------------------------------
 *
 * The deck background is built from gradients (see DECK_BACKGROUND in
 * theme.js), and gradients alone read as flat vector fill. A noise tile at a
 * few percent gives the surface some tooth. It lives here rather than in the
 * background stack because it must tile, while Spectacle applies a single
 * `background-size: cover` to every layer of a slide.
 *
 * STRICTLY a screen effect -- see the `@media print` reset further down. A
 * `position: fixed` overlay is laid out against the page box when paginating,
 * and Chrome pays for it with an extra, otherwise-empty sheet at the end of
 * every PDF. That was the phantom 36th slide in `?exportMode=true`.
 */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

/* --- Typography ----------------------------------------------------------
 *
 * Spectacle's `Heading` is a styled div, not an `<h1>` -- there is not a single
 * heading element in the rendered deck -- so element selectors reach nothing.
 * Every heading therefore carries `.slide-title` or `.slide-subtitle`, applied
 * by prop on hand-written slides and by `componentMap` on markdown ones.
 *
 * Class, not attribute: styled-components used to leak `font-size="h1"` onto
 * the DOM, but v6 requires a `shouldForwardProp` guard that filters exactly
 * those props back out. `className` survives both versions.
 *
 * Sizes and weights stay in the theme; only tracking and leading are set here.
 * Inter's default tracking is drawn for text, not for 64px display -- closing
 * it up is most of what separates a set headline from a browser default.
 */
#root .slide-title {
  letter-spacing: -0.03em;
  line-height: 1.05;
}

#root .slide-subtitle {
  letter-spacing: -0.02em;
  line-height: 1.15;
}

/* --- Heading treatment ----------------------------------------------------
 *
 * Which of these applies is chosen by `HEADING_STYLE` in `theme.js`, which
 * writes the name onto `:root` as `data-heading`; `?heading=<name>` overrides it
 * for a single load, and `/styles.html` has a picker for the same attribute.
 *
 * Spectacle's `Heading` takes its color from the `secondary` theme token, and a
 * theme token cannot be made chapter-aware -- styled-system resolves it from a
 * plain object with no access to the DOM. So every chapter-aware treatment below
 * reads `--chapter-accent`, which already cascades from the slide element and
 * reaches markdown content that never receives a component prop.
 *
 * Both heading classes are covered: `.slide-title` for slide titles and
 * `.slide-subtitle` for the code-slide titles. Headings that own their color on
 * purpose -- the chapter dividers, the AgntCon slide -- carry `heading--fixed`
 * and are left alone.
 *
 * Selector weight is (1,3,0), which clears the single generated class
 * styled-components emits for the `color` prop, so no `!important` is needed.
 */
[data-heading] #root :is(.slide-title, .slide-subtitle):not(.heading--fixed) {
  /* Switching out of `gradient` has to undo its text fill, or every later
   * treatment paints transparent glyphs. */
  background: none;
  -webkit-text-fill-color: currentColor;
}

/*
 * Hands headings back to the `secondary` theme color. A no-op in the deck, where
 * Spectacle already applies it -- but it reads the same custom property rather
 * than a literal, so it follows `theme.colors.secondary` if that changes, and it
 * gives `styles.html` (which has no Spectacle to inherit from) a true baseline.
 */
[data-heading="system"]
  #root
  :is(.slide-title, .slide-subtitle):not(.heading--fixed) {
  color: var(--nf-secondary);
}

[data-heading="white"]
  #root
  :is(.slide-title, .slide-subtitle):not(.heading--fixed) {
  color: var(--nf-primary, #fff);
}

[data-heading="accent"]
  #root
  :is(.slide-title, .slide-subtitle):not(.heading--fixed) {
  color: var(--chapter-accent);
}

[data-heading="tint"]
  #root
  :is(.slide-title, .slide-subtitle):not(.heading--fixed) {
  color: color-mix(in srgb, var(--chapter-accent) 45%, #fff);
}

/*
 * Two things keep a clipped-text gradient looking like plain white type:
 *
 * 1. The background box is the ELEMENT, not the glyphs. A slide title is a
 *    full-width block, so a heading whose text stops at 60% of the slide only
 *    ever reveals the first 60% of the ramp -- the white end. `fit-content`
 *    shrinks the box onto the text so the whole ramp lands on the letters.
 * 2. A steep angle across a wide, short box spends most of its length getting
 *    nowhere. `.title-display` can afford 165deg because it is two lines of
 *    118px; a one-line title cannot, so this runs nearly horizontal.
 *
 * `--heading-grad-hold` is how far the white plateau extends before the accent
 * starts -- the "how much color" knob, driven by the toggle in styles.html and
 * by `?headingGrad=<0-100>` on a deck URL.
 */
[data-heading="gradient"]
  #root
  :is(.slide-title, .slide-subtitle):not(.heading--fixed) {
  width: fit-content;
  max-width: 100%;
  background: linear-gradient(
    100deg,
    #fff 0%,
    #fff var(--heading-grad-hold, 18%),
    var(--chapter-accent) 96%
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/*
 * Underlined emphasis, in headings and body copy alike.
 *
 * The browser default is a hairline sitting on the baseline, which at 72px reads
 * as a hyperlink and collides with descenders. Thickened, offset, and drawn in the
 * chapter accent so it reads as a deliberate mark. Only the line takes the accent;
 * the word keeps the color of the text around it. `text-decoration-color` is
 * independent of `-webkit-text-fill-color`, so this survives the `gradient`
 * heading treatment too.
 */
#root .slide u {
  text-decoration-thickness: 0.07em;
  text-underline-offset: 0.14em;
  text-decoration-color: var(--chapter-accent);
}

#root p {
  line-height: 1.4;
}

#root li {
  line-height: 1.4;
}

/* Emphasis inside markdown, colored by the current chapter. */
#root .em {
  color: var(--chapter-accent);
  font-weight: 600;
}

/*
 * Bold in slide copy takes the chapter accent. White bold on white body text
 * only differs by weight, which is too little to pick out from the back of a
 * room. Speaker notes stay plain.
 */
#root .slide :is(strong, b):not(.notes *) {
  color: var(--chapter-accent);
}

/* Uppercase tracked-out label. The wide tracking is what makes it read as a
 * label rather than as small body text. */
#root .eyebrow {
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--chapter-accent);
}

.accent-rule {
  width: 120px;
  height: 5px;
  flex-shrink: 0;
  margin-top: 28px;
  border-radius: 999px;
  background: var(--chapter-accent);
}

/* A markdown `***`, drawn as a thinner accent rule. Splits the Links slide into
 * deck links and demo links. */
#root hr {
  width: 120px;
  height: 3px;
  /* 32px in: the 16px margin + 16px padding on the text blocks around it. */
  margin: 8px 32px;
  border: 0;
  border-radius: 999px;
  background: var(--chapter-accent);
}

/* Markdown bullets. Spectacle's default discs sit at body weight and read as
 * filler; a chapter-colored marker ties the list to its section. */
#root ul {
  list-style: none;
}

#root ul > li {
  position: relative;
  padding-left: 1.1em;
}

#root ul > li::before {
  content: "\25b8";
  position: absolute;
  left: 0;
  color: var(--chapter-accent);
}

#root ol > li::marker {
  color: var(--chapter-accent);
  font-weight: 700;
}

/*
 * Nested bullets step down a size and change marker.
 *
 * A sub-point set at the parent's size, with the parent's marker, reads as a
 * sibling that has been indented for no reason -- the deck's densest slide runs
 * four points and three sub-points and looked like seven equal ones. `em` keeps
 * this tied to `theme.fontSizes.text` rather than introducing a second body
 * size to keep in sync, and the step is most of what the 7-bullet slide needed
 * back from the chrome band.
 */
#root li ul > li {
  font-size: 0.82em;
}

#root li ul > li::before {
  content: "\2013";
  opacity: 0.85;
}

/* --- Title slide --------------------------------------------------------- */

/*
 * Display type, flat. It used to carry a slight Star Wars perspective tilt; to
 * bring that back, add:
 *
 *   transform: perspective(1400px) rotateX(8deg);
 *   transform-origin: 50% 100%;
 *
 * The `transform: none` overrides in the print and reduced-motion blocks below
 * are there for that tilt, and do nothing while it is off.
 */
#root .title-display {
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 0.92;
  color: #fff;
}

/*
 * The title slide's headings stop short of the QR block.
 *
 * The QR is absolutely positioned into the top-right corner, outside the flow
 * these headings lay out in, so nothing stops a long title line from running
 * underneath it. Reserving the gutter on the headings holds for any string. At
 * 170px the card is 194px wide, so 280px (with the 44px offset) leaves room.
 *
 * Only the headings: the speakers row sits below the QR and needs the full
 * width, or the names wrap.
 */
#root .title-copy > :not(.speakers) {
  max-width: calc(100% - 280px);
}

/* The display type owns the slide; the subtitle should read as a deck line,
 * not compete at the same weight. Sized to stay on ONE line inside the column
 * above -- at h2 it wrapped, which moved the whole composition off centre. */
#root .title-subtitle {
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--nf-midnight-10, #f4f8fa);
}

.qr-card {
  background: #fff;
  border-radius: 16px;
  padding: 12px;
  line-height: 0;
  box-shadow: 0 14px 36px rgba(0, 14, 56, 0.55);
}

/* The title slide's code: the card matches the SVG's slate, so the padding
 * still counts as quiet zone, and the shadow goes, since lifting the card off
 * the slide is the opposite of what it is for. */
.qr-card--quiet {
  background: #97a1b8;
  box-shadow: none;
}

#root .qr-caption {
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The speakers row under the subtitle: introducer, then speaker. Photos are
 * desaturated so a studio headshot and a selfie read as one pair. */
#root .speakers {
  gap: 72px;
}

/* Title to subtitle is one unit on this slide; the default rule spacing
 * reads as a section break. */
#root .title-rule {
  margin-top: 4px;
}

/* Spectacle's heading line-height leaves a tall box around one line, which
 * reads as extra space under the rule. */
#root .title-copy .title-subtitle {
  line-height: 1.15;
}

.speaker__photo {
  border-radius: 16px;
  object-fit: cover;
  filter: grayscale(1);
  border: 1px solid var(--hairline);
  box-shadow: 0 14px 36px rgba(0, 14, 56, 0.55);
}

#root .speaker__label--muted {
  color: var(--muted);
}

/* "Why do we care?": the last step, an aside under the audience cards. */
#root .question-aside {
  color: var(--muted);
  font-style: italic;
}

#root .speaker * {
  white-space: nowrap;
}

#root .speaker__name {
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #fff;
}

#root .speaker__role {
  color: var(--muted);
}

/* --- Chapter dividers ---------------------------------------------------- */

/* `Box` sets no font, so without `font-family` this fell back to a serif. */
#root .divider__numeral {
  font-family: var(--sans);
  position: absolute;
  right: 3%;
  top: 50%;
  transform: translateY(-50%);
  font-size: 360px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.05em;
  color: rgba(255, 255, 255, 0.06);
  pointer-events: none;
}

#root .divider__title {
  max-width: 15ch;
}

/* --- Cards --------------------------------------------------------------- */

/*
 * A flat translucent fill and a hairline edge. A pure-white border reads as a
 * debug outline; a gradient fill, glossy top edge and drop shadow read as a
 * template. This sits in between.
 */
.card {
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  padding: 24px;
}

.card--label {
  background: none;
  border-color: transparent;
  box-shadow: none;
}

#root .card__label {
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--chapter-accent);
}

#root .card__icon {
  font-size: 1.6em;
  color: var(--chapter-accent);
}

/*
 * `TilesSlide` cells. Content-sized, so the tile is only as tall as the icon
 * and title inside it -- an earlier percentage-height wrapper made the tiles
 * fill the slide and collide with the heading.
 */
.card--tile {
  box-sizing: border-box;
  padding: 22px 24px;
}

/*
 * Readiness is the tile's surface, not a label. Here-now pieces take the
 * accent; emerging ones drop the card fill and keep only a dashed edge. The legend
 * swatches reuse these two classes so they cannot drift from the tiles.
 */
.card--tile-ready {
  background: color-mix(in srgb, var(--chapter-accent) 24%, transparent);
  border: 2px solid var(--chapter-accent);
}

.card--tile-emerging {
  background: transparent;
  border: 2px dashed var(--muted);
  box-shadow: none;
}

/* A wrapping row rather than a grid, so a short last row centers. */
.tile-grid {
  gap: 18px;
}

.tile-legend {
  gap: 40px;
}

#root .tile-legend > * {
  color: var(--muted);
}

.tile-swatch {
  display: inline-block;
  width: 18px;
  height: 18px;
  margin-right: 6px;
  border-radius: 4px;
  vertical-align: -2px;
}

#root .tile-legend .tile__demo {
  font-size: 22px;
  vertical-align: -3px;
}

/*
 * Dense cards. Nothing to do with fitting the canvas any more -- `RowsSlide`
 * gives its grid a definite height and `minmax(0, 1fr)` rows, so the grid
 * cannot overrun the slide whatever the padding is. This is now only what it
 * says: less air inside a card that is carrying three items across.
 */
.card--dense {
  padding: 14px 18px;
}

#root .tile__title {
  font-weight: 700;
  color: #fff;
}

#root .tile__demo {
  font-size: 26px;
  color: var(--chapter-accent);
}

#root .tile__icon {
  font-size: 32px;
  color: var(--chapter-accent);
}

/* --- Takeaways -----------------------------------------------------------
 *
 * The recurring device: each of the six claims appears three times, in the same
 * treatment every time, so the repetition reads as one running thread rather
 * than three unrelated summary slides.
 *
 * The badge is a circle drawn here with a plain digit inside, not the ➊-➏
 * dingbats (U+279A-279F). Inter has no glyphs at those code points, so they fall
 * back to tofu -- visible on a projector and nowhere else -- and a font glyph
 * could not take `--chapter-accent` the way this can.
 */
.takeaway {
  padding: 22px 26px;
  gap: 20px;
}

/*
 * Compact takeaways, for the two slides that show all six at once: the roadmap
 * up front and the recap at the close. At full size six cards plus their part
 * labels overrun the canvas by roughly 200px.
 */
.takeaway--compact {
  padding: 16px 26px;
  gap: 20px;
}

#root .takeaway--compact .takeaway__badge {
  width: 34px;
  height: 34px;
  font-size: 19px;
  line-height: 30px;
}

/* Heading line box matches the badge height, so the two share a centre. */
#root .takeaway--compact .takeaway__text {
  line-height: 34px;
}

#root .takeaway--compact .takeaway__detail {
  line-height: 1.35;
}

/*
 * Part label above each group on the roadmap slide. Needs `#root` to outrank
 * the `margin="0"` styled-system class that `Eyebrow` sets on every instance.
 */
#root .takeaway-part {
  margin: 12px 0 8px;
}

/*
 * `border-box` so the stated size is the circle's outer size. Without it the
 * border added 4px and every digit sat 2px above centre.
 */
#root .takeaway__badge {
  flex: 0 0 auto;
  box-sizing: border-box;
  width: 44px;
  height: 44px;
  border: 2px solid var(--chapter-accent);
  border-radius: 999px;
  color: var(--chapter-accent);
  font-family: var(--mono);
  font-size: 24px;
  font-weight: 700;
  line-height: 40px;
  text-align: center;
}

#root .takeaway__text {
  font-weight: 700;
  color: #fff;
}

#root .takeaway__detail {
  color: var(--muted);
}

/* A source credit in a detail line: the browser default blue disappears on navy. */
#root .takeaway__detail a {
  color: var(--chapter-accent);
  text-underline-offset: 0.15em;
}

/* An open-link icon after a takeaway's heading, like `.demo__open`. */
#root .takeaway__link {
  margin-left: 0.3em;
  font-size: 0.9em;
  line-height: 1;
  color: var(--chapter-accent);
  text-decoration: none;
}

/*
 * The verdict mark: one icon, tucked into the card's top-right.
 *
 * It sits outside `.takeaway__body` so the body keeps the full text column, and
 * `margin-left: auto` in the row flex pushes it to the corner without absolute
 * positioning (which would need the card to become a containing block and would
 * overlap long text at compact sizes).
 *
 * Colour is `--chapter-accent`. The chapter progression already carries
 * confidence, so the icon supplies recognition, not a second colour system.
 */
#root .takeaway__verdict {
  flex: 0 0 auto;
  margin-left: auto;
  align-self: flex-start;
  font-size: 30px;
  line-height: 1;
  color: var(--chapter-accent);
  opacity: 0.85;
}

.takeaway--compact #root .takeaway__verdict,
#root .takeaway--compact .takeaway__verdict {
  font-size: 24px;
}

/*
 * A verdict alone on its slide.
 *
 * Every chapter now closes on exactly one of these, so the card is the slide
 * rather than the first row of a list -- bigger type, more air, and a badge
 * scaled to match. `TakeawaySlide` centres it in the space under the heading.
 */
.takeaway--solo {
  padding: 40px 44px;
  gap: 28px;
}

#root .takeaway--solo .takeaway__badge {
  width: 60px;
  height: 60px;
  font-size: 32px;
  line-height: 56px;
}

/*
 * The heading's line box is exactly the badge height, so the first line and the
 * digit share a centre. With the inherited line-height the text sat lower than
 * the badge.
 */
#root .takeaway--solo .takeaway__text {
  line-height: 60px;
}

#root .takeaway--solo .takeaway__detail {
  line-height: 1.3;
}

#root .takeaway--solo .takeaway__verdict {
  font-size: 44px;
  line-height: 60px;
}

/* --- The seam ------------------------------------------------------------
 *
 * One tool interface, two implementations, drawn in boxes so every label stays a
 * real text node the harvest can read. See `SeamDiagram` in components.js for
 * why that matters more than the connectors being pretty.
 *
 * The two connector shapes are the only interesting part:
 *
 *   .seam__link   a single 2px drop, agent down to the boundary bar
 *   .seam__fork   a horizontal bar with a drop at each column centre. Built from
 *                 two spans rather than one rule plus two ticks, so each half
 *                 measures itself against its own column and the fork stays
 *                 centred no matter how the impl boxes size.
 */

.seam {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: 52%;
  flex: 0 0 auto;
}

.seam__points {
  width: 42%;
  flex: 0 0 auto;
}

/*
 * Padding here is measured, not chosen. The diagram is five stacked bands plus
 * two connectors, so every vertical value is spent twice or three times over --
 * at 16px it ran 52px past the slide's content box, which the chrome band's
 * bottom padding hides until it is on a projector.
 */
.seam__box {
  background: linear-gradient(180deg, var(--surface-1), var(--surface-2));
  border: 1px solid var(--hairline);
  border-radius: 14px;
  padding: 16px 20px;
  text-align: center;
}

/*
 * SPECTACLE'S `Text` CARRIES 16px OF VERTICAL PADDING from the theme, and this
 * diagram is seven of them stacked in five bands -- 224px of padding nobody asked
 * for, which is what pushed the whole thing off the bottom of the slide. Every
 * label here is inside a box that already owns its own padding, so the Text's is
 * pure loss. Zeroed by class rather than by shrinking the boxes, which is what
 * the first attempt did and why it kept not converging.
 */
#root .seam__name,
#root .seam__note,
#root .seam__bar-label,
#root .seam__bar-tools,
#root .seam__caption {
  padding-top: 0;
  padding-bottom: 0;
}

#root .seam__name {
  font-weight: 700;
  color: #fff;
}

#root .seam__note {
  color: var(--muted);
}

/*
 * The boundary itself, and the only accented thing in the picture. It is what
 * the slide is about, so it is the one element that does not read as neutral
 * furniture.
 */
.seam__bar {
  border: 2px solid var(--chapter-accent);
  border-radius: 12px;
  padding: 14px 20px;
  text-align: center;
  background: color-mix(in srgb, var(--chapter-accent) 10%, transparent);
}

#root .seam__bar-label {
  font-family: var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--chapter-accent);
}

#root .seam__bar-tools {
  font-family: var(--mono);
  color: #fff;
}

#root .seam__caption {
  color: var(--chapter-accent);
  text-align: center;
}

.seam__link {
  width: 2px;
  height: 24px;
  margin: 0 auto;
  background: var(--hairline);
}

.seam__fork {
  display: grid;
  grid-template-columns: 1fr 1fr;
  height: 28px;
  margin-top: 12px;
}

.seam__fork span {
  position: relative;
}

/* Each span draws its half of the horizontal bar... */
.seam__fork span::before {
  content: "";
  position: absolute;
  top: 0;
  height: 2px;
  background: var(--hairline);
}

.seam__fork span:first-child::before {
  left: 50%;
  right: 0;
}

.seam__fork span:last-child::before {
  left: 0;
  right: 50%;
}

/* ...and its own drop, down to the box below it. */
.seam__fork span::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: var(--hairline);
}

/* --- The two halves of the room ------------------------------------------ */

.audience {
  /* `height: 100%` fills the grid row; border-box keeps the padding inside it, or
     the cards spill over whatever follows (the aside on "Why do we care?"). */
  box-sizing: border-box;
  padding: 30px 32px;
}

#root .audience__icon {
  font-size: 44px;
  color: var(--chapter-accent);
}

/* Tracked-out and quiet: this line is the label, not the message. */
#root .audience__who {
  font-family: var(--mono);
  letter-spacing: 0.04em;
  color: var(--chapter-accent);
}

#root .audience__claim {
  font-weight: 700;
  color: #fff;
}

#root .audience__action {
  color: var(--muted);
}

/* The title's payoff, on the closing slide. The one line the talk ends on. */
#root .close-line {
  color: var(--chapter-accent);
  font-weight: 600;
  text-align: center;
}

/* --- Reference table (MatrixSlide) --------------------------------------
 *
 * Rows are separated by hairlines rather than being cards, which is what lets
 * five or more of them fit the canvas. Both cells carry the border so the rule
 * runs the full width of the grid.
 */
.matrix {
  align-items: stretch;
}

#root .matrix__name,
#root .matrix__note {
  font-family: var(--sans);
  padding: 15px 0;
  border-bottom: 1px solid var(--hairline);
  font-size: 26px;
  line-height: 1.25;
}

#root .matrix__name {
  padding-right: 24px;
  font-family: var(--mono);
  font-weight: 600;
  white-space: nowrap;
  color: var(--chapter-accent);
}

#root .matrix__icon {
  margin-right: 0.5em;
}

#root .matrix__note {
  color: #fff;
  padding-right: 24px;
  /* One line per row. A cell too long for it should be shortened, not wrapped. */
  white-space: nowrap;
}

/* The qualifier after a cell's text: an icon, then an optional short value. */
#root .matrix__after {
  margin-left: 0.4em;
  color: var(--muted);
}

#root .matrix__after i {
  margin-right: 0.15em;
}

/* Read by the harvest and screen readers, not drawn. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

#root .matrix__head {
  font-family: var(--sans);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 24px 10px 0;
  border-bottom: 1px solid var(--hairline);
}

/*
 * No red: the palette has none, so "no" is muted rather than alarming. "manual"
 * keeps the cell's white, and no second accent is borrowed from another chapter.
 */
#root .matrix__mark--yes {
  color: var(--chapter-accent);
}

#root .matrix__mark--no {
  color: var(--muted);
}

/* The table and its link, anchored under the heading like any content slide.
 * Centering them in the height left over floated the table loose of its title. */
#root .matrix__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

/* The links under the table: the write-up on the left, a demo on the right. */
#root .matrix__links {
  display: flex;
  justify-content: space-between;
  margin-top: 28px;
}

/* Each link: text first, then the open-link icon. */
#root .matrix__source {
  font-family: var(--sans);
  font-size: 24px;
  font-weight: 600;
  line-height: 1;
  color: var(--chapter-accent);
  text-decoration: none;
}

#root .matrix__demo {
  margin-left: auto;
}

#root .matrix__source i {
  margin-left: 0.15em;
  vertical-align: -0.1em;
}

/* --- Demo hand-off ------------------------------------------------------- */

#root .demo__url {
  font-family: var(--mono);
  color: var(--chapter-accent);
}

/* Links inside the points: the browser default blue disappears on the navy slide. */
#root .demo__points a {
  color: var(--chapter-accent);
  text-underline-offset: 0.15em;
}

/*
 * `PieceMap`: the pieces slide as a 4 + 3 grid of icons. 300px fits four
 * 60px tiles and their gaps, but not five, so it wraps like `TilesSlide`.
 */
/* A demo title wraps short of the piece strip, which sits top right. Beats the
   heading rules' own `max-width: 100%`. */
#root .slide-title.demo__title--pieces {
  max-width: 74%;
}

.piece-map {
  gap: 10px;
}

.piece-map__tile {
  width: 60px;
  height: 60px;
  padding: 0;
  align-items: center;
  justify-content: center;
}

.piece-map__tile--off {
  opacity: 0.2;
}

#root .piece-map .tile__icon {
  font-size: 28px;
}

/*
 * `CallGrid`: "Who calls, and from where?" as a 2x2 of `TilesSlide` cards, with
 * row and column heads in the legend's muted type. On a demo slide the same grid
 * sits under the URL, a little smaller, and the cells it isn't about fade out
 * the way the piece strip's do.
 */
.call-grid {
  gap: 18px 22px;
  width: 100%;
}

/* Air between the overview's title and the grid. */
.call-grid:not(.call-grid--demo) {
  margin-top: 36px;
}

.call-grid--demo {
  width: auto;
  gap: 12px 16px;
}

#root .call-grid__head {
  gap: 10px;
  align-items: center;
  color: var(--muted);
}

#root .call-grid__head--row {
  padding-right: 8px;
}

/*
 * Overview row heads read into their row: title, bar and detail flush right
 * against the tiles, the icon inline before the title.
 */
#root .call-grid:not(.call-grid--demo) .call-grid__head--row {
  padding-right: 0;
}

#root .call-grid:not(.call-grid--demo) .call-grid__head--row .call-grid__label {
  text-align: right;
}

#root .call-grid__head-title .call-grid__head-icon {
  margin-right: 4px;
  vertical-align: -3px;
}

#root .call-grid:not(.call-grid--demo) .call-grid__rule {
  margin-left: auto;
}

#root .call-grid__head-icon {
  font-size: 30px;
  color: var(--chapter-accent);
}

/* Icon over label, so a head fits its square column. */
#root .call-grid--demo .call-grid__head:not(.call-grid__head--row) {
  flex-direction: column;
  gap: 4px;
}

#root .call-grid--demo .call-grid__head-icon {
  font-size: 22px;
}

.call-grid__cell {
  position: relative;
  min-height: 150px;
}

/* Square on a demo slide, where the grid shares the width with the points. */
#root .call-grid--demo .call-grid__cell {
  min-height: 0;
  aspect-ratio: 1;
}

/*
 * A WebMCP demo keeps the usual centered column, and its grid sits to the
 * right of it like the piece strip: the title wraps short of the grid, and the
 * URL and points stay under the title.
 */
#root .slide-title.demo__title--calls,
#root .demo__calls-text {
  max-width: 54%;
}

/*
 * Centered by stretching top to bottom, not `top: 50%` and a -50% translate:
 * printing paginates the untransformed box, which then starts mid-slide and
 * runs past the page break, so the grid's lower row splits onto the next page.
 */
#root .demo__calls {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 40px;
  display: flex;
  align-items: center;
}

/* The heads are one line each, short on a demo slide so they fit a square column. */
#root .call-grid--demo .call-grid__head > *,
#root .demo__calls-text .demo__url {
  white-space: nowrap;
}

#root .call-grid--demo .call-grid__cell {
  padding: 16px;
}

#root .call-grid--demo .tile__title {
  font-size: 20px;
}

.call-grid__icons {
  gap: 10px;
}

/* Centered on the overview, the icon pair is the whole tile, so it runs larger. */
#root .call-grid:not(.call-grid--demo) .tile__icon {
  font-size: 44px;
}

/* Spectacle's `Text` pads 16px all round, which pulls a two-line head apart. */
#root .call-grid__label {
  padding: 0;
}

/* A short accent bar between a row head's title and its detail line. */
.call-grid__rule {
  width: 36px;
  height: 3px;
  margin: 3px 0;
  border-radius: 2px;
  background: var(--chapter-accent);
}

#root .call-grid__head-title {
  font-weight: 700;
  color: #fff;
}

#root .call-grid__demo {
  position: absolute;
  top: 14px;
  right: 16px;
}

/* An earlier demo's cell: the lit tint, dimmed, with an emerging tile's dashed edge. */
.call-grid__cell--visited {
  opacity: 0.5;
  border-style: dashed;
  background: color-mix(in srgb, var(--chapter-accent) 6%, transparent);
}

.call-grid__cell--off {
  opacity: 0.2;
}

#root .demo__open {
  margin-left: 0.5em;
  font-size: 30px;
  line-height: 1;
  color: var(--chapter-accent);
  text-decoration: none;
}

/* --- Spectacle banner ----------------------------------------------------
 *
 * The hexagon in the deck chrome opens the banner. See `spectacle-banner.js`;
 * the scrim behind the card is `.chat-sheet`, shared with the chat sheets.
 */

/*
 * The closing banner slide, as a card. 16:9 so it reads as the slide it is, and
 * the logo and URL scale with the card rather than off the edge of a small
 * window.
 */
.spectacle-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2%;
  width: min(1000px, 100%);
  aspect-ratio: 16 / 9;
  /* So the URL below can size itself off the card and not the window. */
  container-type: inline-size;
  border: 1px solid var(--hairline);
  border-radius: 12px;
  background-color: var(--nf-midnight-base, #000e38);
  background-position: center;
  background-size: cover;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
}

/*
 * `cqw` and not `%`: the logo is a flex item sized by its content, so a
 * percentage width would be a percentage of the SVG's own attribute width. 30cqw
 * is what the 400px logo is on the 1366px slide.
 */
.spectacle-card svg {
  width: min(300px, 30cqw);
  height: auto;
}

/*
 * Two classes deep on purpose. Spectacle's `Text` carries its own generated
 * class, styled-components injects it after this file, and at equal specificity
 * the later rule wins -- so a single class here would lose the font size.
 */
.spectacle-card .spectacle-card__url {
  font-size: clamp(18px, 3.2cqw, 32px);
}

/*
 * A demo's backup recording, over everything (`demo-video.js`). Black, not the
 * `.chat-sheet` scrim: it stands in for the live app, so the slide behind it
 * should not show through. z-index clears `.chat-sheet`'s 10001.
 *
 * `object-fit: contain` keeps the recording's own aspect ratio; a HiDPI screen
 * recording is rarely 16:9.
 */
.demo-video {
  position: fixed;
  inset: 0;
  z-index: 10002;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;
}

.demo-video:focus-visible {
  outline: none;
}

.demo-video__player {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.demo-video__missing {
  max-width: 900px;
  padding: 24px;
  color: var(--muted);
}

.demo-video .demo-video__close {
  position: absolute;
  top: 16px;
  right: 16px;
}

/* --- Deck chrome --------------------------------------------------------- */

/*
 * Fixed height, bottom-aligned contents -- deliberately not content-sized.
 * The counter is set in Fira Code, so a chrome that measured itself from its
 * own text would be one height before the webfont lands and another after, and
 * the reservation every slide makes against `--chrome-h` would be wrong for one
 * of them. Reserving the band is only sound if the band is a constant.
 */
.deck-chrome {
  box-sizing: border-box;
  height: var(--chrome-h);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  color: var(--muted);
}

.deck-meta {
  box-sizing: border-box;
  padding: 0 44px 14px;
  font-family: var(--mono);
}

/*
 * Optically align the fullscreen icon with the sparkle beside it.
 *
 * Spectacle renders its fullscreen control as an `<svg>` left at `display: inline`,
 * so the wrapper is taller than the icon by the font's descender -- 23.7px around a
 * 19px glyph, measured -- and centring the WRAPPER therefore lifts the ICON about
 * 2.4px above true centre. The sparkle next to it is a flex-centred button with no
 * such gap, so the two read as misaligned even though both boxes centre perfectly.
 *
 * `display: block` drops the baseline, and the wrapper collapses to the icon.
 */
.spectacle-fullscreen-button svg {
  display: block;
}

/*
 * One gap, and three boxes of the same size to measure it between.
 *
 * The controls used to space themselves with a `margin-left` on each button after
 * the first, which looked even in the markup and was not on screen: Spectacle's
 * fullscreen control is a bare 19px svg, while the buttons beside it are 28px
 * boxes with the glyph centred. The same 8px margin therefore left 11px of visible
 * space after the svg and 15px between two buttons. Giving the svg the same box
 * makes the whitespace between glyphs equal, and `gap` on the row then means what
 * it says -- add or remove a control and the spacing needs no arithmetic.
 *
 * `gap` rather than margins for the deletability reason too: with the assistant
 * removed this row has one child, and a gap between one thing is nothing.
 */
.deck-controls {
  gap: 8px;
}

.deck-controls .spectacle-fullscreen-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
}

/*
 * Position information, reserved but not shown, on the title slide.
 *
 * The deliberate decision is unchanged -- a counter and a progress bar reading 1-of-N
 * are a stub rather than information, so the title slide does not display them. What
 * changed is that they now keep their BOXES, because `.deck-chrome` bottom-aligns its
 * contents and every one of these elements that vanished moved the two controls beside
 * them. `visibility` rather than `display` is the whole point.
 *
 * Note the split this draws: position readouts are hidden here, CONTROLS are not. The
 * fullscreen button and the assistant render on every slide, because they are what a
 * presenter reaches for and slide 1 is where fullscreen is needed most.
 */
.deck-meta__count--reserved,
.deck-progress--reserved {
  visibility: hidden;
}

#root .deck-meta__count {
  font-family: var(--mono);
  letter-spacing: 0.12em;
  color: var(--muted);
}

.deck-progress {
  height: 4px;
  background: rgba(255, 255, 255, 0.08);
}

/*
 * Neutral, like the counter beside it. The chrome cannot see the active slide's
 * chapter (see `Template`), so an accent here was always the deck green, which
 * clashed with the blue and purple chapters.
 */
.deck-progress__fill {
  height: 100%;
  background: rgba(255, 255, 255, 0.35);
  transition: width 400ms ease;
}

/* --- Code ---------------------------------------------------------------- */

#root code {
  font-family: var(--mono);
  color: var(--nf-grey-30, #d9d9d9);
}

/*
 * Framed code pane with a filename bar. The snippets are real files fetched
 * from `examples/`, so naming them is information, not decoration.
 */
/*
 * No height cap. The frame is a flex item in the slide's content column, and
 * that column now has a definite height -- the canvas less the chrome band
 * (`--chrome-h`) less the slide padding -- so the frame shrinks to fit whatever
 * is left under the title, and `.code-frame pre` scrolls inside it.
 *
 * This used to be `max-height: 480px`, arrived at by measuring: frame top at
 * y=153, counter at y=646. It was correct for exactly one title length and one
 * heading size.
 */
.code-frame {
  border: 1px solid var(--hairline);
  border-radius: 14px;
  overflow: hidden;
  background: #0b1220;
  box-shadow: 0 16px 40px rgba(0, 14, 56, 0.5);
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.code-frame__bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  background: rgba(255, 255, 255, 0.04);
  border-bottom: 1px solid var(--hairline);
}

.code-frame__dots {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--chapter-accent);
  flex: none;
}

.code-frame__name {
  font-family: var(--mono);
  font-size: 18px;
  letter-spacing: 0.06em;
  color: var(--muted);
}

/* Scoped under the bar so it outranks the underline on Spectacle's `Link`. */
.code-frame__bar .code-frame__source {
  margin-left: auto;
  font-size: 26px;
  line-height: 1;
  color: var(--chapter-accent);
  text-decoration: none;
}

/*
 * `CodePane` wraps its `<pre>` in an unclassed div, and THAT div is the frame's
 * flex child -- not the pre. Without this rule the wrapper keeps its natural
 * height, `flex: 1` on the pre applies in a non-flex parent and does nothing, and
 * a long snippet gets silently clipped by the frame instead of scrolling.
 *
 * Excluding the bar and Spectacle's `.step-placeholder` (a zero-height sibling it
 * injects) leaves exactly the code wrapper.
 */
.code-frame > div:not(.code-frame__bar):not(.step-placeholder) {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/*
 * Pane size comes from `theme.fontSizes.monospace`. Capped and scrollable, so a
 * snippet longer than the canvas scrolls in the frame rather than running off the
 * slide. Examples are kept short enough to need no scrolling; this is the safety
 * net for the ones that grow.
 */
.code-frame pre {
  margin: 0 !important;
  padding: 10px 4px 10px 0 !important;
  border-radius: 0 !important;
  line-height: 1.34 !important;
  flex: 1;
  min-height: 0;
  overflow: auto !important;
  overscroll-behavior: contain;
}

/* A scrollbar you can actually see against the pane, and grab mid-talk. */
.code-frame pre::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

.code-frame pre::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: color-mix(in srgb, var(--chapter-accent) 45%, transparent);
}

.code-frame pre::-webkit-scrollbar-track {
  background: transparent;
}

.code-frame__bar {
  flex: none;
}

/*
 * Paged output has no scrollbars, so a long snippet is simply cut off. That is
 * the accepted trade: the deck reads correctly on screen, and the handout loses
 * the tail of an over-long example.
 */
.paged-mode .code-frame pre {
  overflow: hidden !important;
}

/* --- React Live editor --------------------------------------------------- */

.code-editor-container {
  border: 1px solid var(--hairline);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 16px 40px rgba(0, 14, 56, 0.5);
  background-color: var(--nf-midnight-base, #000e38);
  margin: 20px 0;
}

.react-live-editor {
  background: rgb(30, 30, 30); /* match PRR theme */
  font-family: var(--mono);
  font-size: 18px;
  line-height: 1.5;
  padding: 16px;
  border: none;
  outline: none;
  resize: none;
  overflow: auto;
}

.react-live-editor > pre {
  text-wrap-mode: nowrap !important;
  white-space: nowrap !important;
}

.react-live-editor:focus {
  box-shadow: inset 0 0 0 2px var(--nf-green-base, #00e5a4);
}

.react-live-preview {
  border-top: 1px solid var(--hairline);
  background: var(--nf-purple-base, #8950ff);
  color: var(--nf-midnight-10, #f4f8fa);
  padding: 16px;
  min-height: 200px;
  display: flex;
  font-family: "Inter", system-ui, sans-serif;
  font-size: 24px;
}

.react-live-preview > * {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Inter", system-ui, sans-serif;
}

.react-live-error {
  background: linear-gradient(
    135deg,
    var(--nf-feedback-error, #ff6b6b),
    var(--nf-feedback-errorSoft, #ff8f8f)
  );
  color: var(--nf-midnight-base, #000e38);
  padding: 12px 16px;
  font-size: 14px;
  font-family: var(--mono);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

/* --- Presenter notes ------------------------------------------------------
 *
 * The notes pane is dense plain text in a separate view; the deck's list and
 * heading treatments above would only get in its way.
 *
 * EVERY RULE HERE IS `#root`-SCOPED, and that is the fix rather than a tidy-up.
 * Notes styling used to be split across two files at two specificities, and all
 * three rules lost the fight they were in:
 *
 *   - `.notes ul > li::before { content: none }` (0,1,2) lost to
 *     `#root ul > li::before` (1,0,2), so the deck's marker was never switched
 *     off.
 *   - `.notes ul { list-style: disc }` (0,1,1) lost to `#root ul` (1,0,1), so
 *     the plain disc meant to replace it never arrived either.
 *   - `.notes * { padding: 0 !important }` -- in a <style> block in index.html,
 *     which is why it was easy to miss from here -- beat `#root ul > li
 *     { padding-left: 1.1em }`, because `!important` outranks any specificity.
 *
 * The result on screen was the worst combination available: the deck's marker
 * rendered, the 1.1em gutter reserved for it did not, and `::before` is
 * `position: absolute; left: 0` -- so every bullet painted directly on top of
 * the first character of its own text.
 *
 * Scoping to `#root` lets these win on specificity, so no `!important` is
 * needed. That matters beyond neatness: `!important` is what let a blanket
 * padding reset silently outrank a rule about list gutters that it had no
 * business touching.
 */
#root .notes * {
  padding: 0;
  line-height: 1;
  font-size: 20px;
}

/*
 * The deck's chapter-colored marker off, a plain disc on.
 *
 * The gutter lives on the `ul` rather than the `li` here, unlike the deck's own
 * lists above: a native disc is drawn by the browser in the list's padding,
 * where the deck's `::before` was absolutely positioned inside each item's.
 * More specific than the reset above, so it survives it.
 */
#root .notes ul {
  list-style: disc;
  padding-left: 1.1em;
}

#root .notes ul > li::before {
  content: none;
}

/* A markdown slide's notes are the one thing here Spectacle does not style.
 *
 * The two paths through `Notes` are not equivalent. `MdNotes` wraps its text in
 * Spectacle's `Markdown`, which applies that component's DEFAULT component map,
 * so a hand-written slide's notes arrive as `Text` and `ListItem` -- styled, in
 * Inter, matching the deck. A markdown slide's `Notes:` line never reaches that
 * path: Spectacle converts it to raw HTML with no component map at all, so it
 * lands as a bare `<p>` and inherits the system-font stack that Spectacle's own
 * notes pane sets on itself. Six of the seven markdown slides were rendering
 * their notes in the browser's UI font at a different size to every other slide.
 *
 * `<p>` IS A PRECISE SELECTOR HERE, unlikely as that sounds. This deck renders
 * no paragraphs anywhere else: Spectacle's `Text` is a styled div, and markdown
 * slides map `p` through `MARKDOWN_COMPONENTS` to that same `Text`. So the only
 * `<p>` that can exist is one Spectacle emitted for a note.
 *
 * Chosen over the notes pane's own class, which is a generated `sc-*` hash --
 * the same trap `.spectacle-v7-slide` is called out for at the top of this file.
 *
 * The `Notes:` line must be ONE logical line (see `mdSlideProps` in
 * components.js), so a paragraph is the only tag this path can produce. If that
 * ever stops being true, lists and inline code need entries here too.
 */
#root p {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 20px;
}

/* --- Paged output: ?printMode=true / ?exportMode=true ---------------------
 *
 * Both modes stack every slide and render the deck template inside each one.
 * `theme.js` sets `.paged-mode` for both and `.print-mode` for the handout
 * only -- the one case where Spectacle has swapped to its light palette.
 */

/* Interactive-only chrome. The slide counter earns its place on a page; a
 * fullscreen button does not. */
.paged-mode .spectacle-fullscreen-button {
  display: none;
}

/* QR codes are for a room full of phones. In a PDF the deck links are already
 * on the Thanks slide, and on the title slide the code only crowds the
 * speakers. */
.paged-mode #root .title-qr,
.paged-mode #root .thanks-qr {
  display: none;
}

/* Grain is a screen effect. On paper it is just a dirty plate. */
.paged-mode body::after {
  display: none;
}

/*
 * Chapter accents are darkened per chapter in `chapters.js`; this is the
 * fallback for slides outside a chapter (the title slide's eyebrow and rule),
 * where the deck green would otherwise print as a pale wash.
 */
.print-mode {
  --chapter-accent: color-mix(in srgb, var(--nf-green-base, #00e5a4) 62%, #000);
}

/*
 * Spectacle's print palette sets `primary` to #777, which is too light to read
 * as a heading and too grey to read as body copy. Put real ink back.
 *
 * `.slide-title` / `.slide-subtitle` are on every heading in the deck, so this
 * catches the one-off 150px and 118px display headings too.
 */
.print-mode #root .slide-title,
.print-mode #root .slide-subtitle,
.print-mode #root .title-subtitle,
.print-mode #root .speaker__name,
.print-mode #root .tile__title,
.print-mode #root .takeaway__text,
.print-mode #root .matrix__note,
.print-mode #root .audience__claim,
.print-mode #root .seam__name,
.print-mode #root .seam__bar-tools {
  color: var(--nf-midnight-base, #000e38);
}

/*
 * The seam on paper. `--hairline` is white at 10%, so every connector and box
 * edge in the diagram vanishes on a white sheet -- the one slide in the deck
 * that is nothing but lines needs its own ink.
 */
.print-mode .seam__box {
  background: none;
  border-color: rgba(0, 14, 56, 0.28);
}

.print-mode .seam__link,
.print-mode .seam__fork span::before,
.print-mode .seam__fork span::after {
  background: rgba(0, 14, 56, 0.35);
}

.print-mode #root p,
.print-mode #root li {
  color: #12233f;
}

.print-mode #root .deck-meta__count {
  color: var(--nf-midnight-50, #526288);
}

.print-mode #root .title-display {
  color: var(--nf-midnight-base, #000e38);
  transform: none;
}

/* --- Actually printing ----------------------------------------------------
 *
 * `?printMode=true` / `?exportMode=true` only change how the deck lays out
 * on screen. Sending it to a printer or to Save-as-PDF additionally activates
 * `@media print`, where three things break that are invisible any other way.
 * Verify changes here against a real `--print-to-pdf`, not the paged views.
 */

/*
 * Spectacle ships `@media print { height: inherit; }` on Box, FlexBox AND Grid
 * (see the shared `St` fragment in its source). Printing therefore makes every
 * fixed-height box swell to its parent's height: the 5px accent rule under a
 * chapter title becomes a full-height pill, and the progress bar does the same.
 *
 * Their rule is a single generated class, and styled-components injects it
 * after this file, so matching specificity loses. `#root` wins it back.
 */
@media print {
  /*
   * The film grain again, this time for the printing path itself.
   * `.paged-mode` above covers the two paged URLs; this covers everything,
   * including a plain Ctrl-P of the interactive deck. A `position: fixed`
   * overlay is sized against the page box during pagination, and Chrome emits
   * a trailing blank sheet for it -- 36 pages out of a 35-slide deck. It also
   * rasterizes the noise tile once per page, which is most of a 100MB PDF.
   */
  body::after {
    display: none;
  }

  #root .deck-chrome,
  #root .deck-meta,
  #root .divider__numeral,
  #root .tile-grid,
  #root .piece-map,
  #root .call-grid,
  #root .demo__calls,
  #root .demo__calls-text,
  #root .demo__calls-text > *,
  #root .takeaway-grid,
  #root .takeaway__body,
  #root .matrix,
  #root .seam,
  #root .seam__box,
  #root .seam__impls,
  #root .seam__points,
  #root .card {
    height: auto;
  }

  /* The title slide's column needs its full height back, or the speakers row,
   * which is pushed down by an `auto` top margin, rides up under the
   * subtitle. The row and its cards are content-sized. */
  #root .title-copy {
    height: 100%;
  }

  #root .speakers,
  #root .speaker {
    height: auto;
  }

  /* The badge is a circle, so it needs its height back explicitly. */
  #root .takeaway__badge {
    height: 44px;
  }

  /* The demo-slide piece tiles are squares, and are cards too. */
  #root .piece-map__tile {
    height: 60px;
  }

  #root .takeaway--compact .takeaway__badge {
    height: 34px;
  }

  #root .takeaway--solo .takeaway__badge {
    height: 60px;
  }

  /* Both connectors are fixed-height Boxes, so both swell without this. */
  #root .seam__link {
    height: 24px;
  }

  #root .seam__fork {
    height: 28px;
  }

  #root .accent-rule {
    height: 5px;
  }

  #root .deck-progress {
    height: 4px;
  }

  #root .deck-progress__fill {
    height: 100%;
  }

  /*
   * Print rasterizes box-shadows without their blur, so every soft shadow
   * lands as a hard dark rectangle offset behind its element -- most visible
   * as a slab behind the title-slide QR. The borders carry the edges on their
   * own.
   */
  #root .qr-card,
  #root .speaker__photo,
  #root .code-frame,
  #root .code-editor-container {
    box-shadow: none;
  }

  /* The perspective tilt prints skewed. Print gets flat type. */
  #root .title-display {
    transform: none;
  }
}

.print-mode .card {
  background: none;
  border-color: rgba(0, 14, 56, 0.18);
  box-shadow: none;
}

.print-mode #root .divider__numeral {
  color: rgba(0, 14, 56, 0.08);
}

.print-mode .deck-progress {
  display: none;
}

.print-mode .qr-card,
.print-mode .speaker__photo {
  box-shadow: none;
  border: 1px solid rgba(0, 14, 56, 0.18);
}

/* --- Motion -------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .deck-progress__fill {
    transition: none;
  }

  #root .title-display {
    transform: none;
  }
}

@media (max-width: 768px) {
  .react-live-editor {
    font-size: 14px;
    padding: 12px;
  }

  .react-live-preview {
    padding: 12px;
    min-height: 150px;
  }

  .code-editor-container {
    margin: 10px 0;
  }
}

/*
 * `JsSlide`'s `run` panel: one bubble holding what the snippet logged, with the
 * send button in its bottom-right corner (the chat composer's, at slide size).
 * Outside `.code-frame` on purpose, so none of the code pane's `pre` overrides
 * reach it.
 */
.code-run {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  margin-top: 20px;
  padding: 10px 10px 10px 18px;
  border: 1px solid var(--hairline);
  border-radius: 14px;
  background: #0b1220;
}

.code-run__output {
  flex: 1;
  align-self: center;
  font-family: var(--mono);
  font-size: 24px;
  line-height: 1.4;
  white-space: pre-wrap;
}

.code-run__placeholder {
  color: var(--muted);
}

.code-run__error {
  color: #ff8a8a;
}

.code-run__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: none;
  color: var(--chapter-accent);
  font-size: 28px;
  cursor: pointer;
}

.code-run__button:hover:not(:disabled) {
  background: color-mix(in srgb, var(--chapter-accent) 22%, transparent);
}

.code-run__button--clear {
  color: var(--muted);
}

.code-run__button:disabled {
  opacity: 0.4;
  cursor: progress;
}

/*
 * `deck/live-code.js`: the editable pane. Its `pre` already gets the
 * `.code-frame pre` rules above; these add `CodePane`'s line numbers back, as
 * CSS counters, since react-live's `Editor` draws none. Pseudo-element text is
 * not in the DOM's text, so the editor never sees the numbers as code.
 */
.live-code {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.live-code pre {
  counter-reset: line;
  caret-color: var(--chapter-accent);
}

.live-code .token-line::before {
  counter-increment: line;
  content: counter(line);
  display: inline-block;
  min-width: 22.5px;
  padding-right: 18px;
  text-align: right;
  color: #6a9955;
  user-select: none;
}
