/* ==========================================================================
   Le Festival d'Avignon — présentation
   --------------------------------------------------------------------------
   A fixed 1280×720 canvas is scaled to fit the viewport, so every slide looks
   identical at any screen size — exactly like Keynote / PowerPoint.

   Two deliberate technical choices:

   1. Photos are painted with background-image, not <img> + object-fit.
      Chrome mis-rasterises object-fit inside a transform: scale() ancestor
      (the photo paints cropped to the wrong width). Backgrounds rasterise
      in the element's own space and are correct at every scale.

   2. Colours are high-contrast and never pure white/black on screen.
      AirPlay mirroring to a TV applies a gamma shift that lifts blacks and
      washes out whites; a deep warm ink on warm ivory survives that far
      better than #000 on #fff. Effects are limited to transform/opacity so
      the mirrored stream stays cheap and settles to idle between slides.
   ========================================================================== */

:root {
  --ivory: #f7f1e5;
  --paper: #ece0cb;
  --ink: #1c130b;
  --ink-2: #4a3a2b;
  --line: rgba(28, 19, 11, 0.2);
  --terra: #b4441f;
  --ochre: #d9a03a;
  --olive: #5f6b33;
  --lavender: #6a5a91;
  --backdrop: #14100c;

  --display: "Fraunces", Georgia, "Times New Roman", serif;
  --sans: "Inter", "Helvetica Neue", Arial, sans-serif;

  --dur: 700ms;
  --ease: cubic-bezier(0.22, 0.68, 0.16, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body {
  height: 100%;
  background: var(--backdrop);
  color: var(--ink);
  font-family: var(--sans);
  overflow: hidden;
  /* Stop iOS rubber-banding and double-tap zoom from ever moving the deck. */
  position: fixed;
  inset: 0;
  width: 100%;
  overscroll-behavior: none;
  touch-action: manipulation;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

/* ---------------------------------------------------------------- progress */

.progress {
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  z-index: 60;
  background: rgba(255, 255, 255, 0.1);
}

.progress i {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--terra), var(--ochre));
  transition: width var(--dur) var(--ease);
}

/* ---------------------------------------------------------------- viewport

   iPad Safari collapses and expands its toolbars as you scroll, so a
   position:fixed element sized with inset can end up taller than what is
   actually on screen — the classic symptom being the right and bottom of the
   slide being cut off. The .app wrapper is therefore sized in JavaScript from
   window.visualViewport, which always reports the real visible area. */

.app {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.viewport {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  padding: 18px;
}

/* Centred by transform rather than by grid/flex alignment. On a narrow iPad
   the unscaled 1280px canvas is wider than the viewport, and browsers fall
   back to start-alignment for a centred item that overflows — which is what
   pushed the right-hand side of the slide off screen. */
.canvas {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1280px;
  height: 720px;
  transform: translate(-50%, -50%) scale(var(--scale, 1));
  transform-origin: center center;
  border-radius: 4px;
  overflow: hidden;
  background: var(--backdrop);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
}

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

.slide {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  justify-content: center;
  padding: 60px 72px;
  background: var(--ivory);
  color: var(--ink);
  overflow: hidden;
}

.slide.is-active,
.slide.is-leaving {
  display: flex;
}

.slide.is-active {
  z-index: 2;
}

.slide.is-leaving {
  z-index: 1;
}

/* ------------------------------------------------------- grid-based layouts */

.slide--duo,
.slide--portrait,
.slide--split,
.slide--off {
  display: none;
  grid-template-columns: 1fr 0.8fr;
  gap: 56px;
  align-items: center;
}

.slide--duo.is-active,
.slide--duo.is-leaving,
.slide--portrait.is-active,
.slide--portrait.is-leaving,
.slide--split.is-active,
.slide--split.is-leaving,
.slide--off.is-active,
.slide--off.is-leaving {
  display: grid;
}

/* ------------------------------------------------------------- typography */

.eyebrow {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--terra);
  margin-bottom: 20px;
}

.display {
  font-family: var(--display);
  font-weight: 700;
  font-size: 60px;
  line-height: 1.04;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.display em {
  font-style: italic;
  font-weight: 400;
  color: var(--terra);
}

.lede {
  font-size: 21px;
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 33em;
  margin-top: 20px;
}

.lede em {
  font-style: italic;
  color: var(--terra);
}

/* ------------------------------------------------------- shared components */

.bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

.bg--soft {
  filter: saturate(0.5) brightness(1.1);
}

.scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.scrim--bottom {
  background: linear-gradient(to top, rgba(16, 11, 7, 0.94) 0%, rgba(16, 11, 7, 0.86) 26%, rgba(16, 11, 7, 0.5) 56%, rgba(16, 11, 7, 0.08) 88%);
}

.scrim--left {
  background: linear-gradient(to right, rgba(16, 11, 7, 0.92) 0%, rgba(16, 11, 7, 0.72) 38%, rgba(16, 11, 7, 0.12) 76%);
}

.scrim--veil {
  background: linear-gradient(100deg, rgba(247, 241, 229, 0.98) 0%, rgba(247, 241, 229, 0.94) 48%, rgba(247, 241, 229, 0.74) 100%);
}

/* Photos: background-image, so they rasterise correctly under the canvas
   transform. data-img supplies the URL. */
.photo {
  height: 100%;
  border-radius: 6px;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-color: var(--paper);
}

/* Portrait sources need a lower crop point: this frame shows the producers'
   market stalls in the nave, not the vault above them. */
.photo--tall {
  background-position: center 64%;
}

/* ------------------------------------------------------------ 1 · opening */

.slide--cover {
  padding: 0;
  background: #0f0b07;
  color: var(--ivory);
  /* Bottom-aligned so the type sits in the darkest part of the scrim —
     these skies are bright, and an AirPlay gamma shift would eat the
     ochre eyebrow if it floated in the middle of the frame. */
  justify-content: flex-end;
}

.slide--cover .eyebrow {
  color: var(--ochre);
}

.cover-body {
  position: relative;
  z-index: 2;
  padding: 0 72px 60px;
}

.cover-body--center {
  text-align: center;
  padding-bottom: 56px;
}

.display--hero {
  font-size: 104px;
  line-height: 0.94;
  letter-spacing: -0.035em;
  color: var(--ivory);
}

.display--hero span,
.display--hero em {
  display: block;
}

.display--hero em {
  color: var(--ochre);
}

.cover-sub {
  margin-top: 24px;
  font-size: 23px;
  color: rgba(247, 241, 229, 0.86);
  max-width: 26em;
}

.cover-hello,
.merci {
  margin-top: 30px;
  font-family: var(--display);
  font-style: italic;
  font-size: 30px;
  color: var(--ochre);
}

.merci {
  margin-top: 18px;
  font-size: 78px;
}

/* --------------------------------------------------------- 2 & 11 · duo */

.duo-photos {
  display: grid;
  grid-template-rows: 1.35fr 1fr;
  gap: 18px;
  height: 508px;
}

.facts {
  display: flex;
  gap: 32px;
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.facts dt {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 6px;
}

.facts dd {
  font-family: var(--display);
  font-size: 20px;
  font-weight: 600;
}

/* ------------------------------------------------------- 3 · Jean Vilar */

.portrait {
  height: 500px;
  margin: 0;
  display: grid;
  grid-template-rows: 440px auto;
  gap: 14px;
}

.portrait figcaption {
  font-size: 13px;
  color: var(--ink-2);
  align-self: start;
}

.year {
  font-family: var(--display);
  font-weight: 900;
  font-size: 128px;
  line-height: 0.82;
  letter-spacing: -0.05em;
  color: var(--ochre);
  margin-bottom: 14px;
}

.role {
  margin-top: 10px;
  font-family: var(--display);
  font-style: italic;
  font-size: 24px;
  color: var(--terra);
}

/* -------------------------------------------------- 4 & 10 · veiled photo */

.slide--veil {
  padding: 0;
}

.veil-body {
  position: relative;
  z-index: 2;
  width: 900px;
  padding: 0 72px;
}

/* ---------------------------------------------------- 5 & 8 · split layout */

.split-photo,
.off-photo {
  height: 496px;
}

.slide--off {
  grid-template-columns: 1fr 0.72fr;
  gap: 60px;
}

.slide--off .off-photo {
  height: 500px;
}

.cards {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin-top: 30px;
}

.card {
  padding: 20px 22px;
  background: var(--paper);
  border-left: 3px solid var(--line);
  border-radius: 0 6px 6px 0;
}

.card--lead {
  background: var(--ink);
  border-left-color: var(--ochre);
}

.card--lead .card-title,
.card--lead .card-note {
  color: var(--ivory);
}

.card--lead .card-note {
  opacity: 0.75;
}

.card-title {
  display: block;
  font-family: var(--display);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.card-note {
  display: block;
  margin-top: 3px;
  font-size: 14px;
  color: var(--ink-2);
}

.ticks {
  list-style: none;
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.ticks li {
  position: relative;
  padding-left: 30px;
  font-size: 19px;
  color: var(--ink);
}

.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 14px;
  height: 2px;
  background: var(--terra);
}

/* ------------------------------------------------------- 7 · Cour d’honneur */

.slide--feature {
  padding: 0;
  background: #0f0b07;
  color: var(--ivory);
}

.feature-body {
  position: relative;
  z-index: 2;
  width: 660px;
  padding-left: 72px;
}

.slide--feature .eyebrow {
  color: var(--ochre);
}

.slide--feature .display {
  font-size: 68px;
  color: var(--ivory);
}

.feature-sub {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ochre);
  margin-top: 12px;
}

.slide--feature .lede {
  color: rgba(247, 241, 229, 0.88);
  margin-top: 24px;
  max-width: 29em;
}

/* ------------------------------------------------------------ 12 · bilan */

.slide--statement {
  background: var(--ink);
  color: var(--ivory);
  align-items: center;
  text-align: center;
}

.slide--statement .eyebrow {
  color: var(--ochre);
}

.display--statement {
  font-size: 52px;
  color: var(--ivory);
}

.display--statement em {
  color: var(--ochre);
}

.triplet {
  list-style: none;
  display: flex;
  gap: 20px;
  margin-top: 44px;
}

.triplet li {
  font-family: var(--display);
  font-size: 21px;
  font-weight: 600;
  color: var(--ivory);
  padding: 16px 26px;
  border: 1px solid rgba(217, 160, 58, 0.55);
  border-radius: 6px;
}

/* ------------------------------------------------------------- 13 · merci */

.reasons {
  list-style: none;
  margin-top: 34px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.reasons li {
  font-family: var(--display);
  font-size: 28px;
  font-weight: 600;
  padding: 14px 0 14px 26px;
  border-left: 3px solid var(--olive);
}

/* ==========================================================================
   Transitions
   ========================================================================== */

/* Staggered entrance for slide content; script.js tags elements with .a and
   assigns --i in document order. */
.slide.is-active .a {
  animation: itemIn 600ms var(--ease) both;
  animation-delay: calc(var(--i) * 80ms + 120ms);
}

@keyframes itemIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* curtain — theatrical open, used to bookend the deck */
.slide[data-transition="curtain"].is-active {
  animation: curtainIn var(--dur) var(--ease) both;
}

.slide[data-transition="curtain"].is-leaving {
  animation: curtainOut var(--dur) var(--ease) both;
}

@keyframes curtainIn {
  from {
    clip-path: inset(0 48% 0 48%);
    opacity: 0.45;
  }
  to {
    clip-path: inset(0 0 0 0);
    opacity: 1;
  }
}

@keyframes curtainOut {
  from {
    clip-path: inset(0 0 0 0);
  }
  to {
    clip-path: inset(0 46% 0 46%);
  }
}

/* push — directional, flips when navigating backwards */
.slide[data-transition="push"].is-active {
  animation: pushIn var(--dur) var(--ease) both;
}

.slide[data-transition="push"].is-leaving {
  animation: pushOut var(--dur) var(--ease) both;
}

@keyframes pushIn {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes pushOut {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-14%);
    opacity: 0;
  }
}

.canvas.dir-back .slide[data-transition="push"].is-active {
  animation-name: pushInBack;
}

.canvas.dir-back .slide[data-transition="push"].is-leaving {
  animation-name: pushOutBack;
}

@keyframes pushInBack {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes pushOutBack {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(14%);
    opacity: 0;
  }
}

/* reveal — a wipe across the slide */
.slide[data-transition="reveal"].is-active {
  animation: revealIn var(--dur) var(--ease) both;
}

.slide[data-transition="reveal"].is-leaving {
  animation: revealOut var(--dur) var(--ease) both;
}

@keyframes revealIn {
  from {
    clip-path: inset(0 0 0 100%);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}

@keyframes revealOut {
  from {
    clip-path: inset(0 0 0 0);
  }
  to {
    clip-path: inset(0 100% 0 0);
  }
}

/* zoom — a slow push toward the viewer */
.slide[data-transition="zoom"].is-active {
  animation: zoomIn var(--dur) var(--ease) both;
}

.slide[data-transition="zoom"].is-leaving {
  animation: zoomOut var(--dur) var(--ease) both;
}

@keyframes zoomIn {
  from {
    transform: scale(1.1);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes zoomOut {
  from {
    transform: scale(1);
    opacity: 1;
  }
  to {
    transform: scale(0.96);
    opacity: 0;
  }
}

/* fade — a calm crossfade */
.slide[data-transition="fade"].is-active {
  animation: fadeIn var(--dur) var(--ease) both;
}

.slide[data-transition="fade"].is-leaving {
  animation: fadeOut var(--dur) var(--ease) both;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

/* ==========================================================================
   Clean mode (TV / AirPlay)
   --------------------------------------------------------------------------
   AirPlay mirrors the entire browser viewport, not just the slide. Clean mode
   strips every piece of chrome and lets the canvas grow to the full screen,
   so the television shows only the presentation. Entered automatically by
   <kbd>F</kbd> (fullscreen) or manually with <kbd>T</kbd>.
   ========================================================================== */

body.clean {
  cursor: none;
}

body.clean .progress,
body.clean .hud,
body.clean .notes {
  display: none;
}

body.clean .viewport {
  padding: 0;
}

body.clean .canvas {
  border-radius: 0;
  box-shadow: none;
}

/* ==========================================================================
   Chrome: HUD, notes, overview
   ========================================================================== */

.hud {
  flex: 0 0 auto;
  height: 54px;
  z-index: 55;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 22px;
  padding-left: max(22px, env(safe-area-inset-left));
  padding-right: max(22px, env(safe-area-inset-right));
  color: rgba(255, 255, 255, 0.55);
  font-size: 12px;
}

.hud-scene {
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.75);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.hud-hint {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}

.hud-right {
  display: flex;
  align-items: center;
  gap: 18px;
}

.hud-awake {
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  user-select: none;
}

.hud-awake i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
}

.hud-awake.is-on i {
  background: #7fc47f;
}

.hud-count {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.hud-count .sep {
  margin: 0 5px;
  opacity: 0.4;
}

kbd {
  font-family: var(--sans);
  font-size: 10px;
  padding: 2px 6px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 3px;
}

.notes {
  position: fixed;
  left: 22px;
  bottom: 68px;
  z-index: 58;
  width: min(580px, calc(100vw - 44px));
  padding: 16px 20px 18px;
  background: var(--ivory);
  color: var(--ink);
  border-radius: 8px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
  font-size: 16px;
  line-height: 1.55;
}

.notes-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--terra);
  margin-bottom: 8px;
}

/* -------------------------------------------------------------- overview */

.overview {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgba(14, 11, 8, 0.97);
  padding: 34px 40px 46px;
  overflow-y: auto;
}

.overview-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--ivory);
  margin-bottom: 26px;
}

.overview-head h2 {
  font-family: var(--display);
  font-size: 26px;
  font-weight: 600;
}

.overview-close {
  font-family: var(--sans);
  font-size: 12px;
  color: rgba(255, 255, 255, 0.75);
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 4px;
  padding: 8px 14px;
  cursor: pointer;
}

.overview-close kbd {
  margin-left: 6px;
}

.overview-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 20px;
}

.thumb {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 8px;
  overflow: hidden;
  background: var(--ink);
  color: var(--ivory);
  cursor: pointer;
  transition: transform 200ms var(--ease), border-color 200ms var(--ease);
}

.thumb:hover {
  transform: translateY(-4px);
}

.thumb[aria-current="true"] {
  border-color: var(--ochre);
}

.thumb-art {
  display: block;
  aspect-ratio: 16 / 9;
  background-color: #2b201a;
  background-size: cover;
  background-position: center;
}

.thumb-art--plain {
  background: linear-gradient(135deg, var(--paper), #ddcdb2);
}

.thumb-cap {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 11px 14px;
  font-family: var(--display);
  font-size: 15px;
  text-align: left;
}

.thumb-cap span {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  color: var(--ochre);
}

/* ------------------------------------------------------------ responsive */

/* Narrow screens: the keyboard hint is the first thing to go, then the
   presenter's own controls shrink to leave the slide more room. */
@media (max-width: 900px) {
  .hud-hint {
    display: none;
  }
}

@media (max-width: 640px) {
  .hud {
    gap: 10px;
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
  }

  .hud-scene {
    font-size: 11px;
  }

  .hud-awake span:not(i) {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .slide.is-active,
  .slide.is-leaving,
  .slide.is-active .a {
    animation: none !important;
  }
}

/* --------------------------------------------------- print / PDF fallback */

/* If AirPlay misbehaves on the day, Cmd-P gives a clean 16:9 PDF of all
   13 slides that can be shown from any machine. */
@media print {
  @page {
    size: 1280px 720px;
    margin: 0;
  }

  /* The on-screen layout pins body/app to the viewport, which would clip the
     whole deck to a single page. Undo all of that for print. */
  html,
  body {
    height: auto;
    overflow: visible;
    position: static;
    inset: auto;
    width: auto;
    background: #fff;
  }

  .progress,
  .hud,
  .notes,
  .overview {
    display: none !important;
  }

  .app {
    position: static;
    height: auto;
    overflow: visible;
  }

  .viewport {
    position: static;
    display: block;
    padding: 0;
  }

  .canvas {
    position: static;
    width: 1280px;
    height: auto;
    transform: none;
    box-shadow: none;
    border-radius: 0;
  }

  .slide {
    display: flex !important;
    position: relative;
    width: 1280px;
    height: 720px;
    break-after: page;
    animation: none !important;
  }

  .slide .a {
    animation: none !important;
  }
}
