/* FLIPBOOTH — a diary. Written in gel pens on faintly blush paper, decorated in the
   margins by someone who kept switching colours. Not a picture book: no primaries, no
   markers, no creatures. Marks, not characters. */

@font-face {
  font-family: 'Caveat';
  src: url('assets/fonts/Caveat-VariableFont_wght.ttf') format('truetype');
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
}

:root {
  --paper:     #fbf4f4;
  --paper-hi:  #fffafb;
  /* Ink is violet-black, the colour a gel pen actually lays down — brown-black reads as
     pencil and pure black reads as print. */
  --ink:       #4a4250;

  /* One gel pen set. The whole palette lives here. */
  --marker:    #c15c86;   /* magenta — hearts, emphasis, the pen she liked best */
  --biro:      #7a5ea8;   /* violet — the everyday pen */
  --pencil:    #8d84a0;   /* soft lilac-grey — the quiet one */
  --highlight: #4f9a94;   /* muted teal */
  --gold:      #c9a961;   /* the one that was almost out of ink */
  --rule:      rgba(138, 124, 168, 0.13);

  /* One hand throughout. Bradley Hand ships with macOS but nowhere else, so Caveat sits
     behind it as the cross-platform fallback — close enough in feel that a Windows visitor
     gets handwriting rather than Times. Both tokens resolve to the same stack; they stay
     separate only so every call site does not have to change. */
  --hand:  'Bradley Hand', 'Bradley Hand ITC', 'Caveat', 'Segoe Script', cursive;
  --hand2: 'Bradley Hand', 'Bradley Hand ITC', 'Caveat', 'Segoe Script', cursive;
}

* { box-sizing: border-box; }

/* Several elements here set display in CSS, which silently beats the UA rule for [hidden]
   — the REC badge sat on screen permanently because of it. Make hidden mean hidden. */
[hidden] { display: none !important; }

/* Only the root is pinned to the viewport. The body must be free to grow past it, or a
   short screen silently crops the last control instead of scrolling to it. */
html { height: 100%; }

body {
  margin: 0;
  min-height: 100%;
  min-height: 100dvh;         /* dvh, so mobile browser chrome does not steal a row */
  /* Landscape notches cut into the page box when viewport-fit=cover is set, which it is. */
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  font-family: var(--hand);
  color: var(--ink);
  background-color: var(--paper);
  /* Plain paper. Ruled lines and a margin rule are the notebook, and the notebook is what
     we are getting away from — so all that is left is a soft wash to keep it from reading
     as flat white. */
  background-image:
    radial-gradient(120% 80% at 50% 0%, rgba(255, 252, 253, 0.85) 0%, rgba(255, 252, 253, 0) 65%),
    linear-gradient(165deg, rgba(150, 130, 175, 0.05), rgba(193, 92, 134, 0.045));
  background-attachment: fixed;
  overflow-x: hidden;
}

/* Paper tooth. */
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 40;
  opacity: 0.5;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------------ */
/* Masthead                                                            */
/* ------------------------------------------------------------------ */

.masthead {
  position: relative;
  z-index: 5;
  text-align: center;
  padding: 20px 16px 2px;
  flex: 0 0 auto;
}

.wordmark {
  margin: 0;
  font-family: var(--hand);
  font-weight: 700;
  font-size: clamp(34px, 6.6vw, 62px);
  line-height: 1;
  letter-spacing: 0.005em;
  color: var(--biro);
}

/* Barely uneven — a steady hand writing a title, not letters bouncing along a wall.
   Alternating a colour per letter is the picture-book tell, so the only accent is the
   heart she drew after it. */
.wordmark span {
  display: inline-block;
  transform: rotate(var(--r, 0deg)) translateY(var(--y, 0px));
  transition: transform 0.25s ease;
}
.wordmark .heart { color: var(--marker); font-size: 0.55em; margin-left: 0.14em; vertical-align: 0.28em; }
.wordmark:hover span { transform: rotate(calc(var(--r, 0deg) * -1.6)) translateY(calc(var(--y, 0px) * -1)); }

body[data-scene="book"] .masthead { padding-top: 10px; }
body[data-scene="book"] .wordmark { font-size: clamp(26px, 4.4vw, 40px); }

/* ------------------------------------------------------------------ */
/* Stage                                                               */
/* ------------------------------------------------------------------ */

.stage {
  position: relative;
  z-index: 2;
  flex: 1 1 auto;
  min-height: 340px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px 12px;
}

.doodles {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.dood {
  position: absolute;
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg));
  animation: bob 5.4s ease-in-out infinite;
  animation-delay: var(--delay, 0s);
  will-change: transform;
}

@keyframes bob {
  0%, 100% { transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) translateY(0); }
  50%      { transform: translate(-50%, -50%) rotate(calc(var(--rot, 0deg) + 2.5deg)) translateY(-8px); }
}

.dood.scrawl {
  font-family: var(--hand2);
  font-weight: 700;
  white-space: nowrap;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
}

.doodle { display: block; overflow: visible; }

/* The boil: three drawings of the same thing, cycled at ~8fps.
   b0 is visible by default rather than transparent, so that if the animation never starts
   — an element that was display:none at load, a browser that has animations off — you get
   one clean drawing instead of an invisible one. The running animation overrides it. */
.boil { opacity: 0; }
.boil.b0 { opacity: 1; }
@keyframes boil { 0%, 33.33% { opacity: 1; } 33.34%, 100% { opacity: 0; } }
.boil.b0 { animation: boil 0.36s linear infinite 0s; }
.boil.b1 { animation: boil 0.36s linear infinite -0.12s; }
.boil.b2 { animation: boil 0.36s linear infinite -0.24s; }

/* ------------------------------------------------------------------ */
/* The book                                                            */
/* ------------------------------------------------------------------ */

.bookwrap, .camwrap, .stitching, .trouble { display: none; }
body[data-scene="intro"] .bookwrap,
body[data-scene="book"]  .bookwrap { display: flex; }
body[data-scene="live"]      .camwrap,
body[data-scene="countdown"] .camwrap,
body[data-scene="recording"] .camwrap { display: flex; }
body[data-scene="stitch"]  .stitching { display: flex; }
body[data-scene="trouble"] .trouble   { display: flex; }

.bookwrap {
  position: relative;
  z-index: 3;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: min(980px, 96vw);
}

.book {
  width: 100%;
  height: min(54vh, 470px);
  min-height: 260px;
  display: block;
  touch-action: none;
  cursor: grab;
  outline: none;
  border-radius: 4px;
}
.book.grabbing { cursor: grabbing; }
.book:focus-visible { box-shadow: 0 0 0 3px rgba(193, 92, 134, 0.45); }

body[data-scene="intro"] .book { height: min(42vh, 340px); opacity: 0.97; }

.hint {
  margin: 0;
  font-family: var(--hand2);
  font-size: 18px;
  color: var(--pencil);
  /* Explicitly centred. This looked centred for years only because the paragraph box always
     shrank to fit its text — box-centred and text-centred were the same thing. Giving it a
     max-width on phones made the box wider than the words, and left-aligned text hugs the
     left of its box: measured at 390px the text sat 29px off centre. */
  text-align: center;
  transform: rotate(-1deg);
}
/* The margin scrawls get hidden on narrower screens, so the one instruction that actually
   matters lives here instead — centred under the book, where nothing can cover it. */
.hint .lead {
  color: var(--marker);
  font-weight: 700;
  font-size: 21px;
}
.hint kbd {
  font-family: var(--hand2);
  font-size: 17px;
  border: 1.5px solid var(--pencil);
  border-radius: 5px;
  padding: 0 6px;
}

/* ------------------------------------------------------------------ */
/* Camera                                                              */
/* ------------------------------------------------------------------ */

.camwrap {
  position: relative;
  z-index: 3;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

/* Two strips of tape holding the viewfinder to the page. They live out here rather than on
   .camwindow because that element clips its overflow and was slicing them into triangles. */
.camwrap::before, .camwrap::after {
  content: '';
  position: absolute;
  width: 104px;
  height: 31px;
  background: rgba(216, 202, 226, 0.66);
  box-shadow: inset 0 0 0 1px rgba(146, 126, 166, 0.26), 0 1px 3px rgba(96, 78, 112, 0.14);
  z-index: 6;
  pointer-events: none;
}
.camwrap::before { top: -15px; left: -30px; transform: rotate(-32deg); }
.camwrap::after  { top: -15px; right: -30px; transform: rotate(32deg); }

.camwindow {
  position: relative;
  /* Capped on BOTH axes. Width alone let a 1024x768 window grow this to 600x450 and shove
     "start recording" 6px past the bottom edge. The vh term only bites below ~900px tall,
     so a normal desktop still gets the full 600. */
  width: min(600px, 88vw, calc(50vh * 4 / 3));
  aspect-ratio: 4 / 3;
  background: #2b2531;
  border-radius: 5px;
  overflow: hidden;
  transform: rotate(-0.7deg);
  box-shadow: 0 14px 30px rgba(70, 52, 30, 0.28), 0 2px 0 rgba(255, 255, 255, 0.6);
}

#video, .fakecam {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transform: scaleX(-1);      /* mirror, so it behaves like a mirror */
  filter: saturate(1.02) contrast(1.03) sepia(0.06);
}
.fakecam[hidden] { display: none; }

.camveil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at center, rgba(0,0,0,0) 45%, rgba(34, 22, 40, 0.42) 100%),
    linear-gradient(rgba(255, 198, 200, 0.10), rgba(140, 124, 180, 0.12));
}

.frame-svg {
  position: absolute;
  inset: -6px;
  width: calc(100% + 12px);
  height: calc(100% + 12px);
  pointer-events: none;
  overflow: visible;
  z-index: 5;
}

/* The viewfinder clips its contents, so its frame has to be drawn just inside the edge
   rather than around the outside, or overflow:hidden eats it. */
.camwindow > .frame-svg {
  inset: 2px;
  width: calc(100% - 4px);
  height: calc(100% - 4px);
}

.countdown {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: clamp(90px, 20vw, 190px);
  color: #fff8e8;
  text-shadow: 0 5px 0 rgba(0, 0, 0, 0.35);
  pointer-events: none;
  z-index: 7;
}
.countdown:empty { display: none; }
.countdown span { display: block; animation: pop 0.9s ease-out; }
@keyframes pop {
  0%   { transform: scale(0.4) rotate(-14deg); opacity: 0; }
  22%  { transform: scale(1.14) rotate(4deg);  opacity: 1; }
  55%  { transform: scale(1) rotate(-1deg);    opacity: 1; }
  100% { transform: scale(0.9) rotate(0deg);   opacity: 0.15; }
}

.recbadge {
  position: absolute;
  top: 14px;
  left: 16px;
  z-index: 7;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--hand2);
  font-weight: 700;
  font-size: 19px;
  letter-spacing: 0.14em;
  color: #f7e6ef;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.recbadge .dot {
  width: 13px; height: 13px;
  border-radius: 50%;
  background: #c15c86;
  box-shadow: 0 0 10px rgba(193, 92, 134, 0.85);
  animation: blink 1s steps(1) infinite;
}
@keyframes blink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0.15; } }

.framecount {
  position: absolute;
  right: 14px;
  bottom: 12px;
  z-index: 7;
  font-family: 'Courier New', monospace;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--gold);
  text-shadow: 0 0 9px rgba(201, 169, 97, 0.7);
}

.flash {
  position: absolute;
  inset: 0;
  z-index: 8;
  background: #fff;
  opacity: 0;
  pointer-events: none;
}
.flash.pop { animation: flashpop 0.22s ease-out; }
@keyframes flashpop { 0% { opacity: 0.85; } 100% { opacity: 0; } }

/* The little pile of frames building up as they are captured.
 *
 * This used to be 40px thumbnails in a wrapping row with a hard 46px height and
 * overflow:hidden — one row's worth. Only 13 fit across 600px, so frames 14-24 wrapped onto
 * a second row and were sliced clean off.
 *
 * Now the thumbnail is derived from the strip width rather than fixed, sized so exactly
 * twelve fit per row and all 24 land in two rows at ANY screen width. Because the count is
 * known the height can be reserved up front, so the strip does not grow and shove the
 * controls down when the thirteenth frame lands mid-recording.
 */
.shots {
  --strip: min(600px, 88vw, calc(50vh * 4 / 3));
  --thumb: calc((var(--strip) - 46px) / 12);   /* 11 gaps + 2px of rounding slack */
  display: flex;
  gap: 4px;
  width: var(--strip);
  height: calc(var(--thumb) * 1.5 + 8px);      /* two rows of 4:3, plus the gap */
  align-items: center;
  align-content: center;
  justify-content: center;
  flex-wrap: wrap;
}
.shots canvas {
  width: var(--thumb);
  height: calc(var(--thumb) * 0.75);
  border: 2px solid #fff;
  box-sizing: border-box;
  box-shadow: 0 2px 5px rgba(80, 60, 30, 0.3);
  transform: rotate(var(--r, 0deg));
  animation: shotin 0.34s ease-out backwards;
}
@keyframes shotin {
  0%   { transform: translateY(-52px) scale(1.5) rotate(0deg); opacity: 0; }
  100% { transform: translateY(0) scale(1) rotate(var(--r, 0deg)); opacity: 1; }
}

/* ------------------------------------------------------------------ */
/* Stitching                                                           */
/* ------------------------------------------------------------------ */

.stitching {
  position: relative;
  z-index: 3;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.stitchtext {
  margin: 0;
  font-size: clamp(21px, 3.2vw, 30px);
  transform: rotate(-1.2deg);
}
.dots i { animation: dotfade 1.2s infinite; font-style: normal; }
.dots i:nth-child(2) { animation-delay: 0.2s; }
.dots i:nth-child(3) { animation-delay: 0.4s; }
@keyframes dotfade { 0%, 100% { opacity: 0.2; } 40% { opacity: 1; } }

.stitchbar {
  width: min(360px, 70vw);
  height: 14px;
  border: 2px solid var(--biro);
  border-radius: 10px;
  transform: rotate(-0.6deg);
  overflow: hidden;
  background: rgba(255, 255, 255, 0.5);
}
.stitchfill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--biro), var(--marker));
  transition: width 0.14s linear;
}
.stitchdoodle svg { width: 108px; height: 108px; }

/* ------------------------------------------------------------------ */
/* Trouble card                                                        */
/* ------------------------------------------------------------------ */

.trouble {
  position: relative;
  z-index: 6;
  align-items: center;
  justify-content: center;
  width: 100%;
}
.troublecard {
  position: relative;
  background: var(--paper-hi);
  padding: 26px 30px 22px;
  max-width: min(560px, 92vw);
  transform: rotate(-0.9deg);
  box-shadow: 0 12px 26px rgba(80, 60, 30, 0.22);
  border: 2px solid var(--biro);
  border-radius: 4px;
}
.troublecard::before {
  content: '';
  position: absolute;
  top: -16px; left: 50%;
  width: 120px; height: 32px;
  transform: translateX(-50%) rotate(-3deg);
  background: rgba(216, 202, 226, 0.72);
  box-shadow: inset 0 0 0 1px rgba(146, 126, 166, 0.3);
}
.troublecard h2 { margin: 0 0 8px; font-size: 29px; color: var(--marker); }
.troublecard p { margin: 0 0 12px; font-family: var(--hand2); font-size: 20px; line-height: 1.4; }
.cmd {
  font-family: 'Courier New', monospace;
  font-size: 15px;
  background: #4a4250;
  color: #f4ecf6;
  padding: 10px 12px;
  border-radius: 5px;
  overflow-x: auto;
  margin: 0 0 14px;
  user-select: all;
}

/* ------------------------------------------------------------------ */
/* Controls                                                            */
/* ------------------------------------------------------------------ */

.controls {
  position: relative;
  z-index: 5;
  flex: 0 0 auto;
  padding: 4px 16px 22px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.row, .scrubwrap { display: flex; gap: 14px; align-items: center; justify-content: center; flex-wrap: wrap; }
/* Only the footer's rows are scene-switched. Rows elsewhere — the trouble card's buttons,
   for one — must not be swept up by that. */
.controls > .row, .controls > .scrubwrap { display: none; }
body[data-scene="intro"]     .row-intro     { display: flex; }
body[data-scene="live"]      .row-live      { display: flex; }
body[data-scene="countdown"] .row-recording { display: flex; }
body[data-scene="recording"] .row-recording { display: flex; }
body[data-scene="book"]      .row-book      { display: flex; }
body[data-scene="book"]      .scrubwrap     { display: flex; flex-direction: column; gap: 2px; }

.btn {
  position: relative;
  /* WebKit paints a grey native push-button over any background you set unless the
     appearance is explicitly cleared. Caught this in a screenshot, not in Chrome. */
  appearance: none;
  -webkit-appearance: none;
  font-family: var(--hand);
  font-size: 19px;
  color: var(--ink);
  background: rgba(255, 252, 253, 0.78);
  border: none;
  padding: 9px 22px;
  cursor: pointer;
  transform: rotate(var(--btn-rot, -0.7deg));
  /* Only transform transitions. These buttons go from display:none to visible on every
     scene change, and WebKit starts a background transition from the UA buttonface grey —
     which is a grey flash at best, and a permanently grey button if the transition never
     gets to tick. Caught in a screenshot; not visible in Chrome. */
  transition: transform 0.13s ease;
  -webkit-tap-highlight-color: transparent;
}
.btn:nth-of-type(even) { --btn-rot: 0.8deg; }
.btn:hover  { transform: rotate(calc(var(--btn-rot, -0.7deg) * -1.4)) scale(1.045); background: rgba(255, 255, 255, 0.72); }
.btn:active { transform: scale(0.965); }
.btn:focus-visible { outline: 3px dashed var(--biro); outline-offset: 4px; }
.btn .frame-svg { inset: -4px; width: calc(100% + 8px); height: calc(100% + 8px); }

.btn-big { font-size: clamp(21px, 2.9vw, 27px); padding: 10px 30px; }
.btn-rec, .btn-play { color: var(--marker); }
.btn-ghost { font-family: var(--hand2); font-size: 18px; color: var(--pencil); background: transparent; }
.btn-ghost .frame-svg { display: none; }
.btn-ghost { text-decoration: underline wavy rgba(125, 116, 102, 0.5) 1.5px; text-underline-offset: 5px; }
.btn-icon { font-size: 28px; line-height: 1; padding: 2px 18px 9px; }

/* Capture speed — how much you move between one page and the next. */
.paces { gap: 6px; }
.pace {
  appearance: none;
  -webkit-appearance: none;
  border: none;
  background: transparent;
  cursor: pointer;
  font-family: var(--hand2);
  font-size: 18px;
  color: var(--pencil);
  padding: 4px 12px 6px;
  transform: rotate(var(--pr, 0deg));
  -webkit-tap-highlight-color: transparent;
}
.pace:hover { color: var(--ink); }
.pace[aria-checked="true"] {
  color: var(--marker);
  font-family: var(--hand);
  font-weight: 700;
  font-size: 18px;
}
.pace[aria-checked="true"] .tick { opacity: 1; }
.pace .tick {
  display: block;
  height: 7px;
  margin-top: 1px;
  opacity: 0;
}
.pace .tick svg { width: 100%; height: 100%; overflow: visible; }
.pace:focus-visible { outline: 3px dashed var(--biro); outline-offset: 2px; }

.pacenote {
  display: none;
  margin: 0;
  font-family: var(--hand2);
  font-size: 17px;
  color: var(--pencil);
  text-align: center;
  transform: rotate(-0.5deg);
}
body[data-scene="live"] .pacenote { display: block; }

/* Scrubber — a wobbly ruler under the book. */
/* Full width of whatever column it lands in, so the ruler can never be wider than its
   container — in the landscape two-column layout that container is only ~350px. */
.scrubwrap { margin-top: 2px; width: 100%; }
.scrub {
  position: relative;
  width: min(520px, 100%);
  height: 34px;
  cursor: pointer;
  touch-action: none;
}
.scrubticks { position: absolute; inset: 0; pointer-events: none; }
.scrubticks svg { width: 100%; height: 100%; overflow: visible; }
.scrubmark {
  position: absolute;
  top: 50%;
  left: 0;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  pointer-events: none;
  /* No transition: the marker must sit exactly where the book is, and a transition on it
     lags every drag by a frame — or sticks at zero entirely if transitions do not tick. */
}
.scrubmark svg { width: 100%; height: 100%; overflow: visible; }
.pagelabel {
  margin: 0;
  font-family: var(--hand2);
  font-size: 17px;
  color: var(--pencil);
  text-align: center;
  transform: rotate(-0.6deg);
}
.row-again { margin-top: 2px; }

/* ------------------------------------------------------------------ */
/* Odds and ends                                                       */
/* ------------------------------------------------------------------ */

.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

.topcontrols {
  position: fixed;
  right: 14px;
  top: 14px;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 7px;
}

.languagecontrol { display: flex; }

.languagepicker {
  height: 42px;
  min-width: 92px;
  padding: 2px 27px 2px 12px;
  border: 1px solid rgba(122, 94, 168, 0.24);
  border-radius: 999px;
  background: rgba(251, 244, 244, 0.92);
  color: var(--ink);
  font-family: var(--hand);
  font-size: 18px;
  font-weight: 700;
  cursor: pointer;
}
.languagepicker:focus-visible {
  outline: 2px solid var(--highlight);
  outline-offset: 3px;
}

.soundcontrols {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 2px;
  border: 1px solid rgba(122, 94, 168, 0.18);
  border-radius: 999px;
  background: rgba(251, 244, 244, 0.86);
}

.mutebtn {
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  border: none;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  cursor: pointer;
  font-size: 22px;
  color: var(--pencil);
  transform: rotate(6deg);
  font-family: var(--hand);
}
.mutebtn:hover { transform: rotate(-6deg) scale(1.1); }

.volumeslider {
  width: 96px;
  height: 28px;
  margin: 0;
  padding: 0;
  accent-color: var(--marker);
  cursor: pointer;
  touch-action: pan-x;
}
.volumeslider:focus-visible {
  outline: 2px solid var(--highlight);
  outline-offset: 3px;
  border-radius: 999px;
}

.errbar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 999;
  background: #c0392b;
  color: #fff;
  font-family: 'Courier New', monospace;
  font-size: 13px;
  padding: 10px 14px;
  white-space: pre-wrap;
  max-height: 45vh;
  overflow: auto;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}

.noscript {
  font-family: var(--hand);
  text-align: center;
  padding: 40px;
  font-size: 21px;
}

/* ------------------------------------------------------------------ */
/* Motion off — for people who asked, and for stable screenshots.      */
/* ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .dood, .boil, .recbadge .dot, .dots i, .shots canvas { animation: none !important; }
  .boil.b0 { opacity: 1; }
  .boil.b1, .boil.b2 { display: none; }
}

body.nofx .dood,
body.nofx .boil,
body.nofx .recbadge .dot,
body.nofx .dots i,
body.nofx .shots canvas,
body.nofx .countdown span { animation: none !important; }
body.nofx .boil.b0 { opacity: 1; }
body.nofx .boil.b1, body.nofx .boil.b2 { display: none; }
body.nofx .countdown span { opacity: 1; transform: none; }

/* Touch devices have neither of the two things this half of the hint refers to. Keyed off
   the input type rather than width, because that is the actual condition. */
@media (hover: none) and (pointer: coarse) {
  .hint .keyboardonly { display: none; }
}

/* ------------------------------------------------------------------ */
/* Phones                                                              */
/* ------------------------------------------------------------------ */

/* Two problems pull in opposite directions on a narrow screen.
 *
 * The book is a 2.7:1 landscape spread, so sizing its canvas off vh leaves a screenful of
 * dead space: measured at 360px wide the drawing came to 106px tall inside a 358px canvas,
 * 250px of nothing, with the book itself shrunk to a stamp. But the controls underneath are
 * tall, and at 360x640 "do it again" was already pushed off the bottom edge.
 *
 * Sizing the book by its own aspect ratio instead of by vh solves both at once — the gap
 * closes, the book grows to the full width, and the height it gives back is exactly what
 * the controls needed. Everything below is then trimmed enough to fit one screen without
 * relying on the scroll, which the book cannot offer anyway: it sets touch-action: none so
 * a drag turns a page rather than scrolling the document.
 */
@media (max-width: 640px) {
  body::after { display: none; }
  .dood { transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(0.72); }
  @keyframes bob {
    0%, 100% { transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(0.72) translateY(0); }
    50%      { transform: translate(-50%, -50%) rotate(calc(var(--rot, 0deg) + 2.5deg)) scale(0.72) translateY(-6px); }
  }

  .stage { min-height: 0; padding: 2px 8px; }

  /* The phone book is ONE page bound along its top edge, flipping upward, so the canvas
     needs the page's own height plus room above for the turned pages to stack into. A 4:3
     page at full width is 0.75 tall, and the stack wants about as much again — hence 1:1.4.
     Capped by vh so a short phone shrinks the book rather than pushing a button off screen;
     on those the page stops being full-bleed, which is the honest trade. */
  .book,
  body[data-scene="intro"] .book {
    height: auto;
    min-height: 0;
    aspect-ratio: 1 / 1.4;
    /* Two caps. The aspect wants ~1.4x the width; a tall phone can afford that. A short one
       cannot, and the vh term alone does not know how much room the controls need — so the
       second term reserves it explicitly. Whichever is smaller wins, which means a short
       phone quietly shrinks the book instead of pushing a button off the bottom. */
    max-height: min(64vh, calc(100dvh - 310px));
  }

  .masthead { padding-top: 6px; }
  body[data-scene="book"] .masthead { padding-top: 4px; }

  /* The hint carries the only instruction there is once the margin doodles are hidden, so
     it gets room to wrap evenly rather than running to both edges. */
  .hint { font-size: 15px; padding: 0 10px; max-width: 34ch; text-wrap: balance; }
  .hint .lead { font-size: 17px; }

  /* Three options have to stay on one line — wrapped, the chosen one's underline orphans
     onto a row of its own and stops reading as a selection. */
  .paces { gap: 0; }
  .pace,
  .pace[aria-checked="true"] { font-size: 15px; padding: 3px 6px 5px; }
  .pacenote { font-size: 14px; padding: 0 10px; max-width: 36ch; text-wrap: balance; }

  .controls {
    gap: 4px;
    /* The home indicator sits over the last row otherwise. */
    padding: 2px 8px calc(10px + env(safe-area-inset-bottom));
  }
  .row, .scrubwrap { gap: 6px; }
  /* Small enough that "save the gif / print it out / do it again" stay on ONE row. Wrapped
     they cost ~46px, and every pixel down here becomes page width up there. */
  .btn { font-size: 15px; padding: 5px 11px; }
  .btn-big { font-size: 18px; padding: 6px 16px; }
  .btn-ghost { font-size: 15px; }
  .btn-icon { font-size: 19px; padding: 0 11px 5px; }
  .pagelabel { font-size: 13px; }
  .scrub { height: 26px; }

  .topcontrols { right: 6px; top: 6px; gap: 4px; }
  .languagepicker { height: 38px; min-width: 76px; padding: 1px 22px 1px 9px; font-size: 16px; }
  .soundcontrols { gap: 3px; padding: 2px 6px 2px 1px; }
  .mutebtn { width: 34px; height: 34px; flex-basis: 34px; font-size: 19px; }
  .volumeslider { width: 70px; height: 24px; }

  /* The tape hung off the viewfinder by 30px, which on a phone is off the screen entirely
     and got sliced flat by the window edge. */
  .camwindow { width: min(600px, 86vw); }
  .shots { --strip: min(600px, 86vw); }
  .camwrap { gap: 10px; }
  .camwrap::before, .camwrap::after { width: 74px; height: 24px; top: -10px; }
  .camwrap::before { left: -10px; }
  .camwrap::after  { right: -10px; }
}

@media (max-width: 400px) {
  .volumeslider { width: 52px; }
  .languagepicker { min-width: 70px; width: 70px; }
  .wordmark { font-size: 30px; text-align: left; }
  .masthead { padding-left: 6px; padding-right: 180px; }
}

/* ------------------------------------------------------------------ */
/* Anything short — phones held sideways, mostly                       */
/* ------------------------------------------------------------------ */

/* A landscape phone is wide enough to dodge the rules above but only ~390px tall, and the
   book was eating all of it: the actions row was off-screen with no way to reach it. Here
   height is the scarce axis, so the book goes back to being vh-driven and everything else
   gets compressed hard. Ordered after the width query on purpose — a 640x360 phone matches
   both, and this is the one that has to win. */
@media (max-height: 520px) {
  .stage { min-height: 0; padding: 2px 8px; }
  .book,
  body[data-scene="intro"] .book {
    height: min(52vh, 300px);
    min-height: 0;
    aspect-ratio: auto;
  }
  .masthead, body[data-scene="book"] .masthead { padding-top: 2px; }
  .wordmark, body[data-scene="book"] .wordmark { font-size: 24px; }
  .hint { font-size: 14px; }
  .hint .lead { font-size: 15px; }
  .controls { gap: 4px; padding: 2px 10px calc(8px + env(safe-area-inset-bottom)); }
  .camwindow { width: min(600px, 46vw); }
  .shots { --strip: min(600px, 46vw); }
}

/* A landscape phone has height to spare in exactly one direction: sideways. Stacked, the
   masthead + book + controls came to 476px in a 390px-tall viewport and the actions row was
   simply gone. So the body turns into a row here — title across the top, book on the left,
   every control on the right. Nothing shrinks below legibility and nothing leaves the
   screen. (.errbar and .grain are position:fixed, so the only flex children in play are
   these three.) */
@media (max-height: 520px) and (orientation: landscape) {
  body { flex-flow: row wrap; align-content: center; }

  .masthead { width: 100%; padding: 2px 0 0; }

  .stage { flex: 1 1 54%; min-width: 0; padding: 2px 6px; }
  .controls { flex: 1 1 42%; min-width: 0; justify-content: center; padding-top: 0; }

  .bookwrap { width: 100%; }
  .book,
  body[data-scene="intro"] .book {
    height: auto;
    min-height: 0;
    aspect-ratio: 2 / 1;
  }

  .camwindow { width: min(600px, 42vw); }
  .shots { --strip: min(600px, 42vw); }
}

/* The shortest case there is — an SE on its side, 375px tall. The two-column layout above
   gets it most of the way, but the three action buttons still wrap to a second row and put
   the last one 11px past the bottom. Everything in the control column tightens by a notch;
   nothing here drops below 14px. */
@media (max-height: 400px) and (orientation: landscape) {
  .stage { flex: 1 1 50%; }
  .controls { flex: 1 1 46%; gap: 3px; }
  .row, .scrubwrap { gap: 6px; }
  .btn { font-size: 15px; padding: 5px 12px; }
  .btn-big { font-size: 16px; padding: 5px 14px; }
  .btn-ghost { font-size: 14px; }
  .btn-icon { font-size: 18px; padding: 0 10px 5px; }
  .pace, .pace[aria-checked="true"] { font-size: 14px; padding: 2px 5px 4px; }
  .pacenote { font-size: 13px; }
  .pagelabel { font-size: 13px; }
  .hint { font-size: 13px; }
  .scrub { height: 26px; }
}

/* ------------------------------------------------------------------ */
/* Margin decoration only when there is room for it                    */
/* ------------------------------------------------------------------ */

/* The book is min(980px, 96vw) and the doodles sit at 8%/92% of the stage, so below about
   1385px the two overlap and the labels get sliced in half by the paper. Measured, not
   guessed: at 1280px both "this way" and "keep this one" sat under the book.
   Rather than shrink the book, the decoration steps aside — everything it says is also in
   the hint underneath, which is centred and can never be covered. */
@media (max-width: 1340px) {
  .doodles { display: none; }
}
