/* media.css — gallery styles, extracted from media/index.html. */

    .talks-page {
      --talks-feature-surface: #1F3A4D;
      --talks-feature-nav-border: rgba(255, 255, 255, 0.45);
      --talks-feature-nav-bg: rgba(16, 18, 24, 0.6);
      --talks-feature-nav-bg-hover: rgba(16, 18, 24, 0.9);
      --talks-feature-nav-text: #FFFFFF;
      --talks-feature-dot: rgba(255, 255, 255, 0.38);
      --talks-feature-dot-active: #FFFFFF;
      --talks-feature-scrim: linear-gradient(to top, rgba(0, 0, 0, 0.66) 0%, rgba(0, 0, 0, 0.12) 48%, rgba(0, 0, 0, 0) 72%);
      --talks-feature-text: #FFFFFF;
      --talks-feature-text-muted: rgba(255, 255, 255, 0.9);
      --talks-feature-cta-bg: #FFFFFF;
      --talks-feature-cta-text: #1A1A1A;
      --talks-shelf-rule: #000000;
      --talks-shelf-button-border: #1A1A1A;
      --talks-shelf-button-text: #1A1A1A;
      --talks-shelf-button-hover-bg: #1A1A1A;
      --talks-shelf-button-hover-text: #FFFFFF;
      --talks-shelf-button-disabled: #888888;
      --talks-shelf-fade: linear-gradient(to left, #FFFFFF 0%, transparent 100%);
      --talks-thumb-fallback: #DDDDDD;
      --talks-card-type-bg: rgba(255, 255, 255, 0.55);
      --talks-card-type-text: #1A1A1A;
      --talks-modal-scrim: rgba(0, 0, 0, 0.92);
      --talks-modal-rail-bg: rgba(18, 19, 22, 0.96);
      --talks-modal-rail-border: rgba(255, 255, 255, 0.1);
      --talks-modal-text: #FFFFFF;
      --talks-modal-spec-label: rgba(255, 255, 255, 0.4);
      --talks-modal-context: rgba(255, 255, 255, 0.85);
      --talks-modal-placeholder: rgba(255, 255, 255, 0.82);
      --talks-modal-open-bg: #FFFFFF;
      --talks-modal-open-text: #1A1A1A;
    }

    @media (prefers-color-scheme: dark) {
      .talks-page {
        --talks-feature-surface: #14181D;
        --talks-feature-nav-border: rgba(232, 225, 216, 0.34);
        --talks-feature-nav-bg: rgba(20, 23, 28, 0.72);
        --talks-feature-nav-bg-hover: rgba(20, 23, 28, 0.92);
        --talks-feature-nav-text: var(--color-text);
        --talks-feature-dot: rgba(232, 225, 216, 0.34);
        --talks-feature-dot-active: #F2ECE3;
        --talks-feature-scrim: linear-gradient(to top, rgba(0, 0, 0, 0.74) 0%, rgba(0, 0, 0, 0.24) 46%, rgba(0, 0, 0, 0) 74%);
        --talks-feature-text: #F7F4EE;
        --talks-feature-text-muted: rgba(247, 244, 238, 0.88);
        --talks-feature-cta-bg: rgba(247, 244, 238, 0.96);
        --talks-feature-cta-text: #171310;
        --talks-shelf-rule: rgba(232, 225, 216, 0.18);
        --talks-shelf-button-border: rgba(232, 225, 216, 0.3);
        --talks-shelf-button-text: var(--color-text);
        --talks-shelf-button-hover-bg: rgba(232, 225, 216, 0.12);
        --talks-shelf-button-hover-text: var(--color-headline);
        --talks-shelf-button-disabled: rgba(232, 225, 216, 0.22);
        --talks-shelf-fade: linear-gradient(to left, var(--color-bg) 0%, transparent 100%);
        --talks-thumb-fallback: #2A2621;
        --talks-card-type-bg: rgba(28, 25, 22, 0.55);
        --talks-card-type-text: var(--color-text);
        --talks-modal-scrim: rgba(0, 0, 0, 0.92);
        --talks-modal-rail-bg: rgba(18, 19, 22, 0.96);
        --talks-modal-rail-border: rgba(255, 255, 255, 0.08);
        --talks-modal-text: #F7F4EE;
        --talks-modal-spec-label: rgba(247, 244, 238, 0.42);
        --talks-modal-context: rgba(247, 244, 238, 0.88);
        --talks-modal-placeholder: rgba(247, 244, 238, 0.82);
        --talks-modal-open-bg: rgba(247, 244, 238, 0.96);
        --talks-modal-open-text: #171310;
      }
    }

    /* Talks & Presentations — page-scoped styles.
       Streaming browsing model (feature wall + horizontal shelves) inside
       the Atlantic system: white field, serif headlines, black section
       rules, red accent reserved for kickers. */
    /* Option B (Media page only): the nav keeps its shared sticky/opaque
       behaviour from work.css (no override here). The 16px top padding gives a
       visible gap below the nav; the 16px horizontal padding insets the rounded
       hero from the page edges so it reads as separated, not full bleed. */
    .talks-page .talks-main {
      max-width: 1280px;
      margin: 0 auto;
      padding: 48px 16px 80px;
    }

    /* Visually-hidden but read by assistive tech (page-level h1). */
    .talks-page .sr-only {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
    }
    .talks-page .talks-kicker {
      font-family: var(--font-kicker);
      font-size: 13px;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--color-accent);
      margin: 0 0 12px;
    }

    /* Feature wall */
    .talks-page .talks-feature {
      margin: 0 0 56px;
    }
    .talks-page .talks-feature-carousel {
      position: relative;
      width: 100%;
      aspect-ratio: 16 / 9;
      max-height: 560px;
      overflow: hidden;
      border-radius: 8px;
    }
    .talks-page .talks-feature-slide {
      position: absolute;
      inset: 0;
      opacity: 0;
      transition: opacity 0.6s ease;
      pointer-events: none;
    }
    .talks-page .talks-feature-slide--active {
      opacity: 1;
      pointer-events: auto;
    }
    .talks-page .talks-feature-card {
      position: absolute;
      inset: 0;
      display: block;
      width: 100%;
      height: 100%;
      border: 0;
      padding: 0;
      cursor: pointer;
      text-align: left;
      background: var(--talks-feature-surface);
      overflow: hidden;
    }
    /* Carousel prev/next buttons */
    .talks-page .talks-feature-nav {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      z-index: 3;
      width: 38px;
      height: 38px;
      border-radius: 50%;
      border: 0.5px solid var(--talks-feature-nav-border);
      background: var(--talks-feature-nav-bg);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--talks-feature-nav-text);
      padding: 0;
    }
    .talks-page .talks-feature-nav--prev { left: 16px; }
    .talks-page .talks-feature-nav--next { right: 16px; }
    .talks-page .talks-feature-nav:hover { background: var(--talks-feature-nav-bg-hover); border-color: var(--talks-feature-nav-text); }
    /* No focus outline ANYWHERE on the media page or modal (Hubert's call). This
       kills the user-agent default ring too — deleting our custom outline rules was
       not enough, because the browser still paints its own blue ring on programmatic
       focus (e.g. when closeModal refocuses the just-clicked card). !important so no
       global rule (style.css a:focus-visible, work.css, etc.) or UA default can
       reintroduce it. Ring-free focus cues (lift, background/border changes) use
       other properties and are unaffected. */
    .talks-page :focus,
    .talks-page :focus-visible,
    .talks-page *:focus,
    .talks-page *:focus-visible,
    .talks-modal :focus,
    .talks-modal :focus-visible,
    .talks-modal *:focus,
    .talks-modal *:focus-visible {
      outline: none !important;
      outline-offset: 0 !important;
    }
    /* Dot indicators */
    .talks-page .talks-feature-dots {
      position: absolute;
      bottom: 14px;
      left: 50%;
      transform: translateX(-50%);
      display: flex;
      gap: 7px;
      z-index: 3;
    }
    .talks-page .talks-feature-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      border: 0;
      background: var(--talks-feature-dot);
      cursor: pointer;
      padding: 0;
      transition: background 0.2s ease;
    }
    .talks-page .talks-feature-dot--active { background: var(--talks-feature-dot-active); }
    .talks-page .talks-feature-card img {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
    .talks-page .talks-feature-scrim {
      position: absolute;
      inset: 0;
      background: var(--talks-feature-scrim);
    }
    .talks-page .talks-feature-body {
      position: absolute;
      left: 0;
      bottom: 0;
      max-width: 620px;
      padding: 40px;
      color: var(--talks-feature-text);
    }
    .talks-page .talks-feature-type {
      font-family: var(--font-kicker);
      font-size: 12px;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--talks-feature-text);
      opacity: 0.92;
      margin: 0 0 12px;
    }
    .talks-page .talks-feature-headline {
      font-family: var(--font-work-serif);
      font-size: clamp(26px, 5.5vw, 38px);
      line-height: 1.06;
      font-weight: 400;
      margin: 0 0 12px;
      color: var(--talks-feature-text);
      text-wrap: pretty;
    }
    .talks-page .talks-feature-desc {
      font-family: var(--font-work-serif);
      font-size: 17px;
      line-height: 1.4;
      margin: 0 0 20px;
      color: var(--talks-feature-text-muted);
      max-width: 520px;
    }
    .talks-page .talks-feature-cta {
      display: inline-flex;
      align-items: center;
      gap: 9px;
      background: var(--talks-feature-cta-bg);
      color: var(--talks-feature-cta-text);
      font-family: var(--font-sans);
      font-size: 14px;
      font-weight: 500;
      padding: 11px 20px;
      border-radius: 2px;
    }
    .talks-page .talks-feature-card:hover .talks-feature-cta { background: var(--color-accent); color: var(--color-accent-contrast); }

    /* Shelves */
    .talks-page .talks-shelf { margin: 0 0 48px; }
    .talks-page .talks-shelf-head {
      display: flex;
      align-items: flex-end;
      justify-content: space-between;
      border-bottom: 1px solid var(--talks-shelf-rule);
      padding-bottom: 12px;
      margin-bottom: 20px;
    }
    /* Shelf subheading — Spaced Sans Label: a quiet magazine section label, not a headline */
    .talks-page .talks-shelf-title {
      font-family: var(--font-sans);
      font-size: 13px;
      line-height: 1;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.14em;
      color: var(--color-headline);
      margin: 0;
    }

    /* Shelf nav — count + prev/next chevrons in the head */
    .talks-page .talks-shelf-nav {
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .talks-page .talks-shelf-count {
      font-family: var(--font-mono);
      font-size: 12px;
      color: var(--color-text-secondary);
      letter-spacing: 0.04em;
      margin-right: 4px;
    }
    .talks-page .talks-shelf-nav-btn {
      width: 26px;
      height: 26px;
      border-radius: 50%;
      border: 1px solid var(--talks-shelf-button-border);
      background: none;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 0;
      color: var(--talks-shelf-button-text);
    }
    .talks-page .talks-shelf-nav-btn:hover:not(:disabled) { background: var(--talks-shelf-button-hover-bg); color: var(--talks-shelf-button-hover-text); }
    .talks-page .talks-shelf-nav-btn:disabled { opacity: 0.22; cursor: default; border-color: var(--talks-shelf-button-disabled); }
    .talks-page .talks-shelf-nav-btn svg { display: block; flex-shrink: 0; }

    /* Scroll container with right-edge gradient mask */
    .talks-page .talks-shelf-scroll { position: relative; }
    .talks-page .talks-shelf-scroll::after {
      content: '';
      position: absolute;
      top: 0;
      right: 0;
      bottom: 8px;
      width: 80px;
      background: var(--talks-shelf-fade);
      pointer-events: none;
    }

    .talks-page .talks-row {
      display: grid;
      grid-auto-flow: column;
      grid-auto-columns: clamp(260px, 30vw, 360px);
      gap: 28px;
      overflow-x: auto;
      scroll-snap-type: x proximity;
      /* overflow-x:auto also clips the y-axis, so reserve top/bottom room for the
         focus lift (translateY -6px + scale) and its soft shadow. */
      padding-top: 16px;
      padding-bottom: 24px;
      scrollbar-width: none;
    }
    .talks-page .talks-row::-webkit-scrollbar { display: none; }

    .talks-page .talks-card {
      scroll-snap-align: start;
      display: flex;
      flex-direction: column;
      text-align: left;
      background: none;
      border: 0;
      padding: 0;
      cursor: pointer;
      color: inherit;
      transition: transform 320ms cubic-bezier(0.2, 0.7, 0.2, 1);
    }
    .talks-page .talks-card-thumb {
      position: relative;
      width: 100%;
      aspect-ratio: 3 / 2;
      overflow: hidden;
      background: var(--talks-thumb-fallback);
      border-radius: 6px;
      transition: box-shadow 320ms cubic-bezier(0.2, 0.7, 0.2, 1), filter 320ms cubic-bezier(0.2, 0.7, 0.2, 1);
    }
    .talks-page .talks-card-thumb img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }

    /* Apple TV-style lift on hover: the whole card scales up and lifts;
       the poster tile gains a soft shadow and the image brightens.
       Transform-only so there is no layout shift. Replaces the old
       hover image-zoom (which would compound with the card scale). */
    .talks-page .talks-card:hover {
      transform: translateY(-6px) scale(1.03);
    }
    .talks-page .talks-card:hover .talks-card-thumb {
      box-shadow: 0 22px 40px -16px rgba(0, 0, 0, 0.42);
      filter: brightness(1.04) saturate(1.06);
    }
    .talks-page .talks-card-type {
      position: absolute;
      left: 10px;
      bottom: 10px;
      font-family: var(--font-sans);
      font-size: 10px;
      font-weight: 500;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      background: var(--talks-card-type-bg);
      color: var(--talks-card-type-text);
      padding: 3px 8px;
      border-radius: 2px;
      backdrop-filter: blur(6px);
      -webkit-backdrop-filter: blur(6px);
    }

    .talks-page .talks-card-title {
      font-family: var(--font-work-serif);
      font-size: 20px;
      line-height: 1.2;
      font-weight: 400;
      margin: 14px 0 6px;
      text-wrap: pretty;
    }
    .talks-page .talks-card-desc {
      font-family: var(--font-sans);
      font-size: 13px;
      line-height: 1.45;
      color: var(--color-text-secondary);
      margin: 0;
    }

    /* Interstitial: cinematic content-on-scrim treatment. A near-opaque
       rgba(0,0,0,0.92) scrim recedes the page; there is no solid stage canvas
       — each asset sizes to its own shape and floats directly on the scrim,
       its edge defined by a hairline ring + soft shadow, with the chrome
       floating over it. Entrance animation gated behind prefers-reduced-motion. */
    /* The modal scrolls when content is taller than the viewport (tall PDFs, or a
       PDF plus its notes panel). Vertical centering is done with margin:auto on the
       dialog (not align-items:center) so that overflowing content stays reachable
       from the top instead of being clipped. The scrim is fixed so it always covers
       the viewport while the modal scrolls. The dialog keeps overflow:visible so the
       stage's depth shadow is not clipped. */
    .talks-modal {
      position: fixed;
      inset: 0;
      z-index: 1200;
      display: flex;
      overflow-y: auto;
      padding: clamp(16px, 4dvh, 48px) clamp(16px, 4vw, 56px);
    }
    .talks-modal[hidden] { display: none; }
    .talks-modal__scrim {
      position: fixed;
      inset: 0;
      background: var(--talks-modal-scrim);
    }
    /* Ambient backdrop (opt-in per item via bgImage). Sits above the black scrim
       and below the dialog. Scaled past the viewport so the heavy blur never shows
       a hard edge; a dark wash over it keeps the white chrome and edge controls
       legible over bright art. Hidden (opacity 0) until JS sets is-active, so items
       without a bgImage fall through to the plain scrim. */
    .talks-modal__ambient {
      position: fixed;
      inset: 0;
      background-position: center;
      background-size: cover;
      background-repeat: no-repeat;
      transform: scale(1.18);
      filter: blur(72px) saturate(1.3);
      opacity: 0;
      transition: opacity 0.45s ease;
      pointer-events: none;
    }
    .talks-modal__ambient::after {
      content: "";
      position: absolute;
      inset: 0;
      background: rgba(0, 0, 0, 0.42);
    }
    .talks-modal__ambient.is-active { opacity: 1; }
    @media (prefers-reduced-motion: reduce) {
      .talks-modal__ambient { transition: none; }
    }
    .talks-modal__dialog {
      position: relative;
      width: 100%;
      max-width: 1200px;
      margin: auto;
      overflow: visible;
      background: transparent;
      border: 0;
      border-radius: 0;
      box-shadow: none;
      padding: 0;
    }
    /* The dialog takes programmatic focus on open (a11y) but is a container, not a
       control, so it should not paint a focus ring; the controls inside keep theirs. */
    .talks-modal__dialog:focus { outline: none; }

    /* Page/slide navigation — Apple TV-style frosted vibrancy circles that
       overlay the stage edges and stay hidden until the stage is hovered (or a
       control is keyboard-focused). The backdrop-filter picks up and blurs the
       slide behind each button so contrast holds over any content — dark deck or
       busy photo. The counter pill stays bottom-center of the stage and fades in
       on hover. Drives both deck slides (postMessage) and PDF pages. Arrows +
       counter are hidden for videos, web, and placeholders. */
    .talks-modal__stage-row {
      position: relative;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .talks-modal__arrow {
      position: absolute;
      top: 50%;
      z-index: 6;
      transform: translateY(-50%);
      display: flex;
      align-items: center;
      justify-content: center;
      width: 44px;
      height: 44px;
      border-radius: 50%;
      background: rgba(28, 28, 30, 0.45);
      -webkit-backdrop-filter: blur(20px) saturate(1.5);
      backdrop-filter: blur(20px) saturate(1.5);
      border: 0.5px solid rgba(255, 255, 255, 0.18);
      color: rgba(255, 255, 255, 0.92);
      padding: 0;
      cursor: pointer;
      opacity: 0;
      transition: opacity 0.25s ease,
                  transform 0.25s cubic-bezier(0.2, 0.7, 0.2, 1),
                  background 0.2s ease;
    }
    .talks-modal__arrow--prev { left: 16px; }
    .talks-modal__arrow--next { right: 16px; }
    .talks-modal__arrow[hidden] { display: none; }
    /* Reveal on stage-row hover (the arrows sit on the backdrop beside the
       stage, not inside it). The stage-hover variant covers fullscreen,
       where the arrows are moved inside the stage (the only element the
       Fullscreen API renders). */
    .talks-modal__stage-row:hover .talks-modal__arrow:not(:disabled),
    .talks-modal__stage:hover .talks-modal__arrow:not(:disabled) { opacity: 1; }
    .talks-modal__arrow:hover:not(:disabled) {
      background: rgba(58, 58, 60, 0.55);
      transform: translateY(-50%) scale(1.07);
    }
    .talks-modal__arrow:active:not(:disabled) { transform: translateY(-50%) scale(0.96); }
    .talks-modal__arrow:disabled { opacity: 0; pointer-events: none; }
    .talks-modal__arrow svg { display: block; }
    /* Touch devices have no hover; keep the arrows visible so paging is reachable. */
    @media (hover: none) {
      .talks-modal__arrow:not(:disabled) { opacity: 1; }
    }
    /* Fullscreen: the stage fills the whole screen, so left/right:16px would jam the
       arrows against the screen edges, far from the centered page. Pull them well
       into the dark gutter near the content. (Separate prefixed rules — a combined
       selector list would be invalidated by an unknown pseudo in some browsers.) */
    .talks-modal__stage:fullscreen .talks-modal__arrow--prev { left: clamp(32px, 9vw, 200px); }
    .talks-modal__stage:fullscreen .talks-modal__arrow--next { right: clamp(32px, 9vw, 200px); }
    .talks-modal__stage:-webkit-full-screen .talks-modal__arrow--prev { left: clamp(32px, 9vw, 200px); }
    .talks-modal__stage:-webkit-full-screen .talks-modal__arrow--next { right: clamp(32px, 9vw, 200px); }
    /* The chrome cluster (fullscreen toggle / info / close) moves inside the
       stage on fullscreen entry (JS) so it stays part of the rendered subtree;
       without this it vanishes, leaving no visible way to exit fullscreen or
       close the modal beyond the browser's own transient Esc banner. Pin it
       top-right over the content, same frosted material as the arrows. */
    .talks-modal__stage:fullscreen .talks-modal__chrome-cluster {
      position: absolute;
      top: 16px;
      right: 16px;
      z-index: 6;
      margin-left: 0;
      opacity: 0;
      transition: opacity 0.25s ease;
    }
    .talks-modal__stage:-webkit-full-screen .talks-modal__chrome-cluster {
      position: absolute;
      top: 16px;
      right: 16px;
      z-index: 6;
      margin-left: 0;
      opacity: 0;
      transition: opacity 0.25s ease;
    }
    .talks-modal__stage:fullscreen:hover .talks-modal__chrome-cluster { opacity: 1; }
    .talks-modal__stage:-webkit-full-screen:hover .talks-modal__chrome-cluster { opacity: 1; }
    /* Touch devices have no hover; keep the close/exit controls visible so
       there is always a reachable way out of fullscreen. */
    @media (hover: none) {
      .talks-modal__stage:fullscreen .talks-modal__chrome-cluster,
      .talks-modal__stage:-webkit-full-screen .talks-modal__chrome-cluster { opacity: 1; }
    }
    /* Fullscreen static content (PDF / image deck) is click-to-advance. */
    .talks-modal__stage:fullscreen[data-asset-type="pdf"] .talks-modal__media,
    .talks-modal__stage:fullscreen[data-asset-type="images"] .talks-modal__media { cursor: pointer; }
    .talks-modal__stage:-webkit-full-screen[data-asset-type="pdf"] .talks-modal__media,
    .talks-modal__stage:-webkit-full-screen[data-asset-type="images"] .talks-modal__media { cursor: pointer; }
    .talks-modal__nav-counter {
      position: absolute;
      bottom: 10px;
      left: 50%;
      transform: translateX(-50%);
      font-family: var(--font-mono);
      font-size: 12px;
      letter-spacing: 0.08em;
      color: rgba(255, 255, 255, 0.78);
      background: rgba(0, 0, 0, 0.45);
      padding: 3px 10px;
      border-radius: 20px;
      font-variant-numeric: tabular-nums;
      opacity: 0;
      transition: opacity 0.2s ease, left 0.28s ease;
      pointer-events: none;
    }
    .talks-modal__nav-counter[hidden] { display: none; }
    .talks-modal__stage:hover .talks-modal__nav-counter { opacity: 1; }
    /* Touch devices have no hover, so keep the page/slide counter always visible
       — without it there is no position feedback while paging on mobile. */
    @media (hover: none) {
      .talks-modal__nav-counter { opacity: 1; }
    }
    /* When the details rail is open it covers the right of the stage; recenter
       the counter within the remaining visible (non-rail) area so it isn't
       occluded on narrow stages. */
    .talks-modal__stage.is-info-open .talks-modal__nav-counter {
      left: calc((100% - min(320px, 82%)) / 2);
    }

    /* Stage: floats on the scrim with a hairline ring + soft shadow (no solid
       canvas). Grows to fill the row between the arrows; the PDF variant below
       caps its width and the row centers it. */
    .talks-modal__stage {
      position: relative;
      flex: 1 1 auto;
      min-width: 0;
      margin: 0;
      aspect-ratio: 16 / 9;
      max-height: 82dvh;
      background: transparent;
      border-radius: 12px;
      overflow: clip;
      box-shadow: 0 0 0 1px var(--talks-modal-rail-border), 0 24px 70px -12px rgba(0, 0, 0, 0.55);
    }
    .talks-modal__media { position: absolute; inset: 0; }
    .talks-modal__media iframe,
    .talks-modal__media embed { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }

    /* Phone-shaped web asset (opt-in via item.frame === "phone"): a tall device
       aspect instead of 16:9, so a mobile prototype renders at its real shape and
       the ambient backdrop fills the gutters either side. Width is derived from the
       capped height, and flex is frozen so the stage doesn't stretch to fill. */
    .talks-modal__stage[data-asset-frame="phone"] {
      aspect-ratio: 390 / 844;
      height: min(86dvh, 880px);
      flex: 0 0 auto;
      width: auto;
      max-width: 100%;
      margin: 0 auto;
      border-radius: 28px;
    }

    /* Adaptive stage — aspect adapts to the asset type so each renders at its
       native shape instead of one global 16:9. Video/deck/web keep 16:9 (the
       default above); PDFs get a tall, full-width stage. */
    .talks-modal__stage[data-asset-type="pdf"] {
      aspect-ratio: auto;
      height: min(86dvh, 1400px);
      max-width: none;
      flex: 1 1 auto;
      border-radius: 12px;
      box-shadow: 0 0 0 1px var(--talks-modal-rail-border), 0 24px 70px -12px rgba(0, 0, 0, 0.55);
    }
    /* When the notes panel is visible, shorten the PDF stage so the page and its
       notes both fit in the viewport instead of pushing the notes below the fold. */
    .talks-modal__dialog.has-notes .talks-modal__stage[data-asset-type="pdf"] {
      height: calc(min(86dvh, 1400px) - 150px);
    }

    /* Web/interactive assets (e.g. selfNav prototypes): sized by height, not
       width, so the whole stage — plus the modal's own vertical padding and
       chrome — is guaranteed to fit within one viewport. The base rule above
       lets a wide flex row stretch the stage to a width whose 16:9-derived
       height can exceed the viewport once padding is added, forcing the
       outer .talks-modal to scroll; that defeats the point of an interstitial
       meant to be seen all at once, above the fold, with no scrolling. */
    .talks-modal__stage[data-asset-type="web"] {
      flex: 0 1 auto;
      width: auto;
      height: min(82dvh, calc(100dvh - 120px));
      max-width: 100%;
      margin: 0 auto;
    }

    /* PDF.js canvas viewer — one page fully visible at a time, fit to the stage
       and centered. The stage handles border-radius and shadow; the canvas just
       displays the page. */
    .talks-modal__pdf {
      position: absolute;
      inset: 0;
      overflow: hidden;
      display: flex;
      justify-content: center;
      align-items: center;
      background: transparent;
    }
    .talks-modal__pdf-canvas {
      display: block;
      max-width: 100%;
      max-height: 100%;
      /* Dark backdrop so a momentarily-empty canvas (during a page-size change)
         shows the scrim colour, never a white/transparent flash. */
      background: var(--talks-modal-scrim);
    }

    /* Image-sequence viewer — a pre-rendered slide series (responsive avif/webp)
       navigated in place. Two stacked layers cross-fade so a slide change never
       flashes empty. Far lighter than a PDF: only viewed slides download, at the
       resolution the viewport needs. */
    .talks-modal__imgdeck {
      position: absolute;
      inset: 0;
      background: var(--talks-modal-scrim);
    }
    .talks-modal__imgdeck-layer {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: contain;
      opacity: 0;
      transition: opacity 0.18s ease;
    }
    .talks-modal__imgdeck-layer.is-active { opacity: 1; }
    @media (prefers-reduced-motion: reduce) {
      .talks-modal__imgdeck-layer { transition: none; }
    }

    /* Speaker notes — for decks, synced via postMessage; for PDFs, taken per page
       from the item's notes array. Shown below the stage. FIXED height (treatment
       A): the panel never resizes between slides/pages regardless of note length,
       so the stage above it never shifts. The label is pinned at top; only the
       body scrolls internally. */
    .talks-modal__notes {
      position: relative;
      margin-top: 10px;
      padding: 16px 20px;
      background: var(--talks-modal-rail-bg);
      border: 0.5px solid var(--talks-modal-rail-border);
      border-radius: 12px;
      height: 120px;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }
    .talks-modal__notes[hidden] { display: none; }
    .talks-modal__notes-label {
      flex: 0 0 auto;
      font-family: var(--font-sans);
      font-size: 10px;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--talks-modal-spec-label);
      margin: 0 0 10px;
    }
    .talks-modal__notes-body {
      flex: 1 1 auto;
      min-height: 0;
      overflow-y: auto;
      font-family: var(--font-sans);
      font-size: 13px;
      line-height: 1.5;
      color: var(--talks-modal-context);
      scrollbar-width: thin;
      scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
    }
    .talks-modal__notes-body::-webkit-scrollbar { width: 5px; }
    .talks-modal__notes-body::-webkit-scrollbar-track { background: transparent; }
    .talks-modal__notes-body::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 5px; }
    .talks-modal__notes-body p { margin: 0 0 10px; }
    .talks-modal__notes-body p:last-child { margin-bottom: 0; }
    .talks-modal__notes-body em { font-style: italic; color: var(--talks-modal-text); }
    /* Soft fade at the bottom edge hints there is more to scroll. */
    .talks-modal__notes::after {
      content: "";
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: 28px;
      background: linear-gradient(to bottom, transparent, var(--talks-modal-rail-bg));
      border-radius: 0 0 12px 12px;
      pointer-events: none;
    }

    /* Chrome button cluster — ⛶ · ⓘ · × — sits to the right of the stage,
       top-aligned with the stage roof. All three share the same frosted-circle
       material so they read as one family. */
    .talks-modal__chrome-cluster {
      flex: 0 0 auto;
      align-self: flex-start;
      display: flex;
      flex-direction: column;
      gap: 8px;
      margin-left: 8px;
    }
    .talks-modal__chrome-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 30px;
      height: 30px;
      background: rgba(16, 18, 24, 0.7);
      border: 0.5px solid rgba(255, 255, 255, 0.35);
      border-radius: 50%;
      color: rgba(255, 255, 255, 0.85);
      cursor: pointer;
      padding: 0;
      transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
    }
    .talks-modal__chrome-btn:hover { color: #fff; border-color: rgba(255,255,255,0.7); background: rgba(36, 40, 50, 0.92); transform: scale(1.06); }
    .talks-modal__chrome-btn:active { transform: scale(0.94); }
    .talks-modal__chrome-btn[aria-expanded="true"] { background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.5); color: #fff; }

    .talks-modal__info-rail {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      width: min(320px, 82%);
      z-index: 11;
      box-sizing: border-box;
      padding: 30px 26px;
      background: var(--talks-modal-rail-bg);
      border-left: 0.5px solid var(--talks-modal-rail-border);
      -webkit-backdrop-filter: blur(8px);
      backdrop-filter: blur(8px);
      overflow-y: auto;
      transform: translateX(100%);
      transition: transform 0.28s ease;
    }
    .talks-modal__info-title {
      font-family: var(--font-work-serif);
      font-size: 21px;
      line-height: 1.25;
      color: var(--talks-modal-text);
      margin: 0 0 14px;
    }
    .talks-modal__info-context {
      font-family: var(--font-sans);
      font-size: 14px;
      line-height: 1.6;
      color: var(--talks-modal-context);
      margin: 0;
    }
    /* Structured abstract sub-elements — rendered when an item carries
       item.abstract (meta line / problem / approach / takeaways / metric /
       audience). Items without it fall back to the plain context string. */
    .talks-modal__info-context p { margin: 0 0 14px; }
    .talks-modal__info-context p:last-child { margin-bottom: 0; }
    .talks-modal__info-meta {
      font-size: 11px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      font-weight: 600;
      color: var(--talks-modal-text);
      opacity: 0.7;
      margin: 0 0 16px;
    }
    .talks-modal__info-label {
      font-size: 11px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      font-weight: 600;
      color: var(--talks-modal-text);
      margin: 20px 0 10px;
    }
    .talks-modal__info-list {
      margin: 0;
      padding: 0 0 0 1px;
      list-style: none;
    }
    .talks-modal__info-list li {
      position: relative;
      margin: 0 0 9px;
      padding-left: 15px;
    }
    .talks-modal__info-list li:last-child { margin-bottom: 0; }
    .talks-modal__info-list li::before {
      content: "";
      position: absolute;
      left: 0;
      top: 0.62em;
      width: 4px;
      height: 4px;
      border-radius: 50%;
      background: currentColor;
      opacity: 0.5;
    }
    .talks-modal__info-metric {
      margin: 20px 0 0;
      padding-top: 14px;
      border-top: 0.5px solid var(--talks-modal-rail-border);
      font-size: 13px;
      font-weight: 600;
      line-height: 1.5;
      color: var(--talks-modal-text);
    }
    .talks-modal__info-audience {
      margin: 16px 0 0;
      font-size: 12.5px;
      font-style: italic;
      opacity: 0.82;
    }

    /* Open: rail slides into view. The trigger now lives in the top chrome bar
       and does not move. */
    .talks-modal__stage.is-info-open .talks-modal__info-rail { transform: translateX(0); }

    @media (prefers-reduced-motion: reduce) {
      .talks-modal__info-rail { transition: none; }
    }

    /* Placeholder: item thumbnail blurred and dimmed as background so the
       dead-asset state still previews how the card will eventually look. */
    .talks-modal__placeholder {
      position: absolute;
      inset: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 14px;
      text-align: center;
      padding: 24px;
      overflow: hidden;
    }
    .talks-modal__placeholder-bg {
      position: absolute;
      inset: 0;
      background-size: cover;
      background-position: center;
      filter: blur(14px) brightness(0.38);
      transform: scale(1.1);
    }
    .talks-modal__placeholder > * { position: relative; z-index: 1; }
    .talks-modal__placeholder p {
      font-family: var(--font-sans);
      font-size: 14px;
      color: var(--talks-modal-placeholder);
      margin: 0;
      max-width: 420px;
      line-height: 1.5;
    }
    .talks-modal__ph-mark { font-family: var(--font-work-serif); font-style: italic; color: var(--color-accent); font-size: 30px; line-height: 1; }

    /* Loading variant of the placeholder: a gentle pulse signals the deck is
       still loading rather than dead. Stilled under reduced-motion. */
    .talks-modal__loading .talks-modal__ph-mark { animation: talks-loading-pulse 1.4s ease-in-out infinite; }
    @keyframes talks-loading-pulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
    @media (prefers-reduced-motion: reduce) {
      .talks-modal__loading .talks-modal__ph-mark { animation: none; }
    }

    .talks-modal__open-ext {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      background: var(--talks-modal-open-bg);
      color: var(--talks-modal-open-text);
      font-family: var(--font-sans);
      font-size: 13px;
      font-weight: 500;
      padding: 9px 16px;
      border-radius: 2px;
      text-decoration: none;
    }
    .talks-modal__open-ext:hover { background: var(--color-accent); color: var(--color-accent-contrast); }

    body.talks-modal-open { overflow: hidden; }

    @media (max-width: 1024px) {
      .talks-page .talks-main { padding: 16px 16px 60px; }
      .talks-page .talks-feature-headline { font-size: 32px; }
      .talks-page .talks-row { gap: 24px; }
    }

    @media (max-width: 760px) {
      .talks-page .talks-feature-headline { font-size: 26px; }
      .talks-page .talks-feature-body { padding: 22px; }
      .talks-page .talks-feature-desc { display: none; }
      .talks-page .talks-row { grid-auto-columns: 78%; gap: 18px; }
      .talks-page .talks-shelf-scroll::after { width: 40px; }
      .talks-modal__stage { aspect-ratio: auto; border-radius: 0; }
      .talks-modal__stage[data-asset-type="pdf"] { height: auto; max-width: none; }
      /* Narrow viewports: pull the arrows tight to the stage edge but keep the
         full 44px tap target so paging stays thumb-friendly. */
      .talks-modal__arrow--prev { left: 8px; }
      .talks-modal__arrow--next { right: 8px; }
      .talks-modal__media { position: relative; width: 100%; aspect-ratio: 16 / 9; }
      .talks-modal__stage[data-asset-type="pdf"] .talks-modal__media { aspect-ratio: auto; height: 80dvh; }
      /* Keep the phone tall on narrow screens too (don't fall back to 16:9). */
      .talks-modal__stage[data-asset-frame="phone"] { aspect-ratio: 390 / 844; height: min(80dvh, 760px); width: auto; }
      .talks-modal__stage[data-asset-frame="phone"] .talks-modal__media { position: absolute; inset: 0; aspect-ratio: auto; }
    }

    @media (prefers-reduced-motion: reduce) {
      .talks-page .talks-card,
      .talks-page .talks-card-thumb { transition: none; }
      /* No motion: drop the lift movement. The static shadow + image brighten
         still mark hover/focus (no ring, no movement). */
      .talks-page .talks-card:hover { transform: none; }
    }

    @media (prefers-reduced-motion: no-preference) {
      @keyframes talks-scrim-in { from { opacity: 0; } to { opacity: 1; } }
      @keyframes talks-dialog-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
      .talks-modal:not([hidden]) .talks-modal__scrim { animation: talks-scrim-in 180ms ease both; }
      .talks-modal:not([hidden]) .talks-modal__dialog { animation: talks-dialog-in 220ms ease both; }
    }
