/* ============================================================
   TALESPOKE PAGE                              (2026-09-25)
   Loaded on /apps/talespoke/ only, after styles.css and apps.css.

   Two things are the show: the film at the top (a Blender render,
   drawn, played by scrolling), and the dragon who then flies down the
   whole page along a trail of starlight, turning at every bend.

   Everything else is kept plain and alike: at each stop on the trail,
   a heading and one piece of paper (on a wide screen, on one side, the
   dragon's lane on the other, switching at every stop), drawn in Blender in the dragon's
   style (docs/talespoke-hero/props.py): a notebook page, a scroll,
   parchment, a torn note, a letter, all held with the same masking tape.
   Night, paper, ink; gold only for the trail and the places on it. Two
   typefaces: Fraunces, and the app's serif for the GM's words.

   Text on a drawn piece fits it at any size: notes are sized by their
   own width and their words in cqw; paper that holds text of changing
   length is a border-image whose drawn edges stay while its plain middle
   stretches.

   Arrivals follow the scroll: talespoke.js sets --p (0 to 1, eased
   and smoothed) on each [data-reveal] as it comes up the screen, so
   things glide in as you scroll and back out as you scroll away. Without
   JS or with reduced motion, --p is 1: everything simply in place.
   ============================================================ */

.bs {
  --ts-night: #171A3C;
  --ts-deep: #10122C;
  --ts-moon: #F4EFFF;
  --ts-moon2: #B9B4D9;
  --ts-gold: #FFD66B;
  --ts-ink: #1C1B19;
  --ts-ink2: #5F5B55;
  --ts-brown: #3A2A16;
  --ts-brown2: #6A5230;
  --ts-rule: #D5DDEB;
  --ts-npc: #A3303F;  --ts-thr: #8A6200;  --ts-plc: #2B6B65;
  --ts-npc-n: #E88A96; --ts-thr-n: #D9AE55; --ts-plc-n: #74BDB4;

  --ts-display: 'Fraunces', 'Source Serif 4', Georgia, serif;
  --ts-serif: ui-serif, 'New York', 'Source Serif 4', Georgia, serif;

  --ts-wrap: 1240px;
  --ts-gutter: clamp(20px, 4.2vw, 64px);
  --ts-left: max(var(--ts-gutter), calc((100vw - var(--ts-wrap)) / 2));
  --ts-soft: cubic-bezier(0.22, 1, 0.36, 1);
  /* a drawn piece's edges show at half their rendered size */
  --ts-px: 0.5;

  background: var(--ts-deep);
  color: var(--ts-moon);
}
.bs main { overflow-x: clip; }
.bs .visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.bs :focus-visible { outline: 2px solid var(--ts-gold); outline-offset: 4px; border-radius: 6px; }
.ts-wrap {
  position: relative;
  width: 100%;
  max-width: calc(var(--ts-wrap) + var(--ts-gutter) * 2);
  margin: 0 auto;
  padding: 0 var(--ts-gutter);
  box-sizing: border-box;
}

/* ---------- the two voices ---------- */
.ts-h2 {
  margin: 0;
  font-family: var(--ts-display);
  font-variation-settings: 'SOFT' 100, 'WONK' 1;
  font-weight: 600;
  font-size: clamp(2.6rem, 1.6rem + 3.2vw, 4.8rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ts-moon);
  text-wrap: balance;
}
.ts-lede {
  margin: 0;
  font-family: var(--ts-serif);
  font-style: italic;
  font-size: clamp(1.2rem, 1.05rem + 0.6vw, 1.55rem);
  line-height: 1.4;
  color: var(--ts-moon2);
}
.ts-fine { margin: clamp(28px, 4vw, 44px) auto 0; max-width: 36rem; font-family: var(--ts-serif); font-style: italic; font-size: 1rem; line-height: 1.5; color: var(--ts-moon2); text-align: center; }
.ts-fine a { color: var(--ts-moon); text-decoration: underline; text-underline-offset: 3px; }
/* A place on the map: a gold star and its name. */
.ts-loc {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 1rem;
  font-family: var(--ts-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(1.05rem, 1rem + 0.25vw, 1.25rem);
  letter-spacing: 0.02em;
  color: var(--ts-gold);
}
.ts-loc::before {
  content: '';
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  background: currentColor;
  clip-path: path('M8 0 Q9.3 6.7 16 8 Q9.3 9.3 8 16 Q6.7 9.3 0 8 Q6.7 6.7 8 0 Z');
}

/* The app's signature: a name underlined in its kind's colour where it
   was said; double when new. At night the app's dark-theme hues. */
.bs .mk {
  text-decoration-line: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.24em;
  text-decoration-skip-ink: none;
}
.bs .mk.npc { text-decoration-color: var(--ts-npc-n); }
.bs .mk.thr { text-decoration-color: var(--ts-thr-n); }
.bs .mk.plc { text-decoration-color: var(--ts-plc-n); }
.bs .mk.new { text-decoration-style: double; text-decoration-thickness: 1.5px; }
.bs .mk.loose { text-decoration-style: dotted; text-decoration-color: var(--ts-moon2); }

/* A button is a slip of paper (p-slip), the words in ink on it. */
.ts-slip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16rem;
  max-width: 100%;
  aspect-ratio: 612 / 200;
  padding: 0 1.4em 0.2em;
  box-sizing: border-box;
  border: 0;
  background: url(/apps/talespoke/art/p-slip.webp?v=2026-09-25) center / 100% 100% no-repeat;
  color: var(--ts-ink);
  font-family: var(--ts-display);
  font-variation-settings: 'SOFT' 100, 'WONK' 1;
  font-size: 1.15rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  rotate: -1.5deg;
  filter: drop-shadow(0 10px 14px rgba(0, 0, 0, 0.4));
  transition: rotate 0.5s var(--ts-soft), translate 0.5s var(--ts-soft), filter 0.5s ease;
}
.ts-slip:hover { rotate: 1deg; translate: 0 -3px; filter: drop-shadow(0 16px 20px rgba(0, 0, 0, 0.45)); }
.ts-slip:active { translate: 0 1px; }

/* ---------- arrivals, following the scroll ---------- */
[data-reveal] { --p: 1; }
.ts-motion [data-reveal] { --p: 0; will-change: transform, opacity; }
/* up from below, and in from their own side of the page (--dx) */
[data-reveal]:not([data-reveal="scroll"]):not([data-reveal="fan"]) {
  opacity: var(--p);
  translate: calc((1 - var(--p)) * var(--dx, 0) * 48px) calc((1 - var(--p)) * 60px);
}
/* paper settles to its own angle (--r) as it arrives */
.ts-drop { transform: rotate(var(--r, 0deg)); filter: drop-shadow(0 26px 30px rgba(0, 0, 0, 0.5)); }
.ts-drop[data-reveal],
.ts-scrap[data-reveal] { rotate: calc((1 - var(--p)) * 6deg); }

/* ============================================================
   THE FILM
   ============================================================ */
.ts-film { position: relative; background: var(--ts-night); color: var(--ts-moon); }
.ts-film-track { height: 620vh; }
.ts-film-stage { position: sticky; top: 0; height: 100vh; height: 100svh; overflow: hidden; }
.ts-film-media { position: absolute; inset: 0; }
.ts-film-media picture,
.ts-film-poster,
#tsFilm { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.ts-film-poster { object-fit: cover; transition: opacity 0.4s ease; }
#tsFilm { opacity: 0; transition: opacity 0.4s ease; }
.ts-film.is-live #tsFilm { opacity: 1; }
.ts-film.is-live .ts-film-poster { opacity: 0; }
.ts-film-shade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to right, rgba(23, 26, 60, 0.94) 0%, rgba(23, 26, 60, 0.72) 30%, rgba(23, 26, 60, 0) 54%);
  transition: opacity 0.8s ease;
}
.ts-film.is-past .ts-film-shade { opacity: 0.35; }
.ts-film.is-done .ts-film-shade { opacity: 0; }

.ts-intro {
  position: absolute;
  left: var(--ts-left);
  top: 50%;
  width: min(36rem, 40vw);
  transform: translateY(-46%);
  transition: opacity 0.7s ease, transform 0.9s var(--ts-soft);
}
.ts-film.is-past .ts-intro { opacity: 0; transform: translateY(-52%); pointer-events: none; }
.ts-brand {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem 0.7rem;
  margin: 0 0 1.2rem;
  font-family: var(--ts-display);
}
.ts-brand img { width: 36px; height: 36px; border-radius: 8px; box-shadow: 0 0 0 1px rgba(255,255,255,0.14); }
.ts-brand-name { font-size: 1.15rem; font-weight: 600; }
.ts-brand-soon { font-style: italic; font-size: 1.05rem; color: var(--ts-gold); }
/* What it is, in the page's headline voice, in the night left of the scene. */
.ts-hero-title {
  margin: 0;
  max-width: 11.5em;
  font-family: var(--ts-display);
  font-variation-settings: 'SOFT' 100, 'WONK' 1;
  font-weight: 600;
  font-size: clamp(2.5rem, 1.3rem + 3vw, 4.4rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--ts-moon);
  text-wrap: balance;
  text-shadow: 0 0 28px rgba(23, 26, 60, 0.9);
}
.ts-intro .ts-lede { margin: 1.3rem 0 0; max-width: 30rem; color: var(--ts-moon); text-shadow: 0 0 22px rgba(23, 26, 60, 0.9); }
.ts-intro .ts-slip { margin: 1.8rem 0 0; }
.ts-hint { margin: 1.4rem 0 0 0.2rem; font-family: var(--ts-serif); font-style: italic; font-size: 1rem; color: var(--ts-moon2); }
.ts-hint::after { content: '\2193'; display: inline-block; margin-left: 0.4em; }
.ts-motion .ts-hint::after { animation: ts-nudge 2.4s ease-in-out infinite; }
@keyframes ts-nudge { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

/* The notes that come and go in the night left of the scene: torn
   scraps (p-scrap-a, b, c) on one tape. They drift while they're up. */
.ts-scraps { position: absolute; inset: 0; pointer-events: none; }
.ts-scrap {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: clamp(200px, 17vw, 270px);
  aspect-ratio: 512 / 345;
  box-sizing: border-box;
  container-type: inline-size;
  background: url(/apps/talespoke/art/p-scrap-a.webp?v=2026-09-25) center / 100% 100% no-repeat;
  filter: drop-shadow(0 14px 16px rgba(0, 0, 0, 0.45));
  margin: 0;
}
.ts-scrap--b { background-image: url(/apps/talespoke/art/p-scrap-b.webp?v=2026-09-25); }
.ts-scrap--c { background-image: url(/apps/talespoke/art/p-scrap-c.webp?v=2026-09-25); }
.ts-scrap > span,
.ts-scrap > small { display: block; margin: 0 12cqw 0 11cqw; text-align: center; }
.ts-scrap > span { margin-top: 7cqw; font-family: var(--ts-display); font-variation-settings: 'SOFT' 100, 'WONK' 1; font-size: 10.5cqw; font-weight: 600; line-height: 1.08; color: var(--ts-ink); }
.ts-scrap > small { margin-top: 2.6cqw; font-family: var(--ts-serif); font-style: italic; font-size: 6cqw; color: var(--ts-ink2); }
.ts-scraps .ts-scrap {
  will-change: transform, opacity;
  width: clamp(220px, 19vw, 300px);
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  transform: translate(-50%, -50%) translateY(calc(30px - var(--drift, 0) * 22px)) rotate(calc(var(--r) + 5deg + var(--drift, 0) * 2deg)) scale(0.94);
  transition: opacity 0.6s ease, transform 0.9s var(--ts-soft);
}
.ts-scraps .ts-scrap.is-on { opacity: 1; transform: translate(-50%, -50%) translateY(calc(var(--drift, 0) * -22px)) rotate(calc(var(--r) + var(--drift, 0) * 2deg)); }

/* No JS, or reduced motion: the first frame and the title. */
.no-js .ts-film-track,
.ts-film.is-static .ts-film-track { height: auto; }
.no-js .ts-film-stage,
.ts-film.is-static .ts-film-stage { position: relative; }
.no-js .ts-scraps,
.ts-film.is-static .ts-scraps,
.no-js .ts-hint,
.ts-film.is-static .ts-hint { display: none; }

/* ============================================================
   THE MAP: the night, the trail through it, the dragon on it
   ============================================================ */
.ts-map {
  position: relative;
  padding: clamp(60px, 8vw, 110px) 0 clamp(40px, 6vw, 80px);
  background: linear-gradient(180deg, var(--ts-night) 0%, #141736 40%, var(--ts-deep) 100%);
  isolation: isolate;
}
.ts-map::before,
.ts-map::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    radial-gradient(1.2px 1.2px at 12% 18%, rgba(255,255,255,0.8) 50%, transparent 51%),
    radial-gradient(1px 1px at 38% 72%, rgba(255,255,255,0.55) 50%, transparent 51%),
    radial-gradient(1.4px 1.4px at 67% 34%, rgba(255,255,255,0.7) 50%, transparent 51%),
    radial-gradient(1px 1px at 86% 81%, rgba(255,255,255,0.5) 50%, transparent 51%),
    radial-gradient(1.6px 1.6px at 54% 9%, rgba(255,214,107,0.85) 50%, transparent 51%);
  background-size: 310px 290px, 230px 250px, 390px 360px, 170px 210px, 530px 470px;
}
.ts-map::after {
  background-image:
    radial-gradient(1.3px 1.3px at 22% 44%, rgba(255,255,255,0.85) 50%, transparent 51%),
    radial-gradient(1px 1px at 77% 12%, rgba(255,255,255,0.7) 50%, transparent 51%),
    radial-gradient(2px 2px at 91% 57%, rgba(255,214,107,0.95) 50%, transparent 51%);
  background-size: 270px 330px, 350px 240px, 610px 520px;
}
/* The rest of the film's lit table, under it (film/<set>/below.webp),
   so the circle of candlelight closes in the night rather than being cut
   off by the film's edge. talespoke.js sizes and places it by the
   film's own cover crop; it melts into the map at its foot. */
.ts-below {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 0;
  height: 0;
  background: no-repeat 0 0 / 100% auto;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(#000 calc(100% - 60px), transparent);
  mask-image: linear-gradient(#000 calc(100% - 60px), transparent);
}
/* The trail is drawn on one window-sized canvas that stays in view while
   the map scrolls past (sticky, within a layer the map's full size), so
   only the dots on screen are drawn. */
.ts-trail-wrap { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.ts-trail { position: sticky; top: 0; display: block; width: 100%; height: 100vh; }
/* The dragon (dragon-flight: 8 wingbeats across, 4 turns down, from
   flying across to flying at you). He flies under the paper, never over
   what you're reading, and shows in the night between. */
.ts-flyer {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
  width: clamp(130px, 12vw, 180px);
  aspect-ratio: 392 / 263;
  background: url(/apps/talespoke/art/dragon-flight.webp?v=2026-09-25-2) 0 0 / 800% 400% no-repeat;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s ease;
  will-change: transform, background-position;
}
.ts-map.is-flying .ts-flyer { opacity: 1; }

/* A stop: a heading, and its piece of paper: on a narrow screen on the
   page's centre line, on a wide one to one side (see the foot). */
.ts-stop { position: relative; z-index: 1; padding: clamp(80px, 10vw, 150px) 0; }
.ts-head { display: flex; flex-direction: column; align-items: center; text-align: center; }
.ts-head .ts-lede { margin-top: 0.9rem; }
.ts-piece { position: relative; width: min(100% - 2 * var(--ts-gutter), 760px); margin: clamp(32px, 4.5vw, 56px) auto 0; }
/* Names on an old map wear a halo of the paper they're on, so the roads
   drawn under them never run through the letters. Here, a halo of night. */
.ts-head,
.ts-fine,
.ts-die-note,
.ts-shot figcaption {
  text-shadow: 0 0 3px var(--ts-deep), 0 0 8px var(--ts-deep), 0 0 14px var(--ts-deep), 0 0 22px var(--ts-deep);
}

/* ============================================================
   THE SCRIPTORIUM
   ============================================================ */
/* The page, torn out of a spiral notebook (p-notepage). It stretches in
   its plain middle to fit each note. */
.ts-notepage {
  --s: var(--ts-px);
  position: relative;
  border-style: solid;
  border-width: 0;
  border-image-source: url(/apps/talespoke/art/p-notepage.webp?v=2026-09-25);
  border-image-slice: 120 200 165 180 fill;
  border-image-width: calc(120px * var(--s)) calc(200px * var(--s)) calc(165px * var(--s)) calc(180px * var(--s));
  border-image-repeat: stretch;
  padding: calc(150px * var(--s)) calc(96px * var(--s)) calc(110px * var(--s)) calc(196px * var(--s));
  color: var(--ts-ink);
}
.ts-notepage-meta { margin: 0; font-family: var(--ts-serif); font-style: italic; font-size: 1rem; color: var(--ts-ink2); }
.ts-notepage-text {
  /* the rules run from the pink margin to just short of the paper's edge */
  margin: 0.5rem calc(-16px * var(--s)) 0 calc(-40px * var(--s));
  padding: 0 calc(16px * var(--s)) 0 calc(40px * var(--s));
  font-family: var(--ts-serif);
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.5rem);
  line-height: 1.52em;
  background: repeating-linear-gradient(transparent 0 1.3em, var(--ts-rule) 1.3em calc(1.3em + 1px), transparent calc(1.3em + 1px) 1.52em);
  transition: opacity 0.35s ease;
}
.ts-notepage.is-changing .ts-notepage-text { opacity: 0; }
/* The marks draw themselves in reading order (a quarter of a second
   each, as the app draws them). On the page, in the page's hues. */
.ts-notepage .mk { text-decoration: none; background-repeat: no-repeat; background-position: 0 100%; background-size: 100% 2px; }
.ts-notepage .mk.npc { background-image: linear-gradient(var(--ts-npc), var(--ts-npc)); }
.ts-notepage .mk.thr { background-image: linear-gradient(var(--ts-thr), var(--ts-thr)); }
.ts-notepage .mk.plc { background-image: linear-gradient(var(--ts-plc), var(--ts-plc)); }
.ts-notepage .mk.new.npc { background-image: linear-gradient(var(--ts-npc), var(--ts-npc)), linear-gradient(var(--ts-npc), var(--ts-npc)); }
.ts-notepage .mk.new.thr { background-image: linear-gradient(var(--ts-thr), var(--ts-thr)), linear-gradient(var(--ts-thr), var(--ts-thr)); }
.ts-notepage .mk.new.plc { background-image: linear-gradient(var(--ts-plc), var(--ts-plc)), linear-gradient(var(--ts-plc), var(--ts-plc)); }
.ts-notepage .mk.new { background-position: 0 100%, 0 calc(100% - 3.5px); background-size: 100% 1.5px, 100% 1.5px; }
.ts-notepage .mk.loose { background-image: radial-gradient(circle, var(--ts-ink2) 0.9px, transparent 1.1px); background-repeat: repeat-x; background-size: 5px 2px; }
.ts-motion .ts-notepage .mk { transition: background-size 0.3s ease-out; }
.ts-motion .ts-notepage:not(.is-marked) .mk { background-size: 0% 2px; }
.ts-motion .ts-notepage:not(.is-marked) .mk.new { background-size: 0% 1.5px, 0% 1.5px; }
.ts-motion .ts-notepage .mk.loose { background-size: 5px 2px; transition: none; }

/* The d20, under the page: roll it for another note. */
.ts-die {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: row;
  gap: 0.8rem;
  align-items: center;
  margin: 1.8rem auto 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ts-moon);
  cursor: pointer;
}
.ts-die img {
  display: block;
  width: clamp(100px, 9vw, 132px);
  height: auto;
  rotate: -10deg;
  filter: drop-shadow(0 14px 16px rgba(0, 0, 0, 0.5));
  transition: rotate 0.6s var(--ts-soft), scale 0.6s var(--ts-soft);
}
.ts-die:hover img { rotate: -22deg; scale: 1.05; }
.ts-die.is-rolling img { animation: ts-tumble 0.8s cubic-bezier(0.3, 0.7, 0.4, 1); }
@keyframes ts-tumble {
  0% { transform: translate(0, 0) rotate(0deg); }
  25% { transform: translate(-6px, -40px) rotate(-130deg) scale(0.94); }
  50% { transform: translate(4px, 0) rotate(-250deg); }
  70% { transform: translate(2px, -12px) rotate(-320deg); }
  100% { transform: translate(0, 0) rotate(-360deg); }
}
.ts-die-note { margin: 0; max-width: 8rem; font-family: var(--ts-serif); font-style: italic; font-size: 0.95rem; line-height: 1.25; color: var(--ts-moon2); text-align: left; }

/* The names found, each on its own note pinned under the page. */
.ts-names {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem 0.6rem;
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
}
.ts-names li {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: clamp(140px, 22%, 176px);
  aspect-ratio: 512 / 345;
  container-type: inline-size;
  background: url(/apps/talespoke/art/p-scrap-a.webp?v=2026-09-25) center / 100% 100% no-repeat;
  rotate: var(--r, 0deg);
  filter: drop-shadow(0 10px 12px rgba(0, 0, 0, 0.45));
  text-align: center;
}
.ts-names li:nth-child(1) { --r: -3deg; }
.ts-names li:nth-child(2) { --r: 3deg; background-image: url(/apps/talespoke/art/p-scrap-b.webp?v=2026-09-25); }
.ts-names li:nth-child(3) { --r: -2deg; background-image: url(/apps/talespoke/art/p-scrap-c.webp?v=2026-09-25); }
.ts-names li:nth-child(4) { --r: 2.5deg; background-image: url(/apps/talespoke/art/p-scrap-b.webp?v=2026-09-25); }
.ts-names b,
.ts-names i { display: block; margin: 0 12cqw 0 11cqw; }
.ts-names b {
  margin-top: 8cqw;
  font-family: var(--ts-display);
  font-variation-settings: 'SOFT' 100, 'WONK' 1;
  font-size: 12cqw;
  font-weight: 600;
  line-height: 1.05;
  color: var(--ts-ink);
  text-decoration: underline 2px var(--k);
  text-underline-offset: 0.2em;
}
.ts-names i { margin-top: 3cqw; font-family: var(--ts-serif); font-size: 7.5cqw; color: var(--ts-ink2); }
.ts-names .k-npc { --k: var(--ts-npc); }
.ts-names .k-thr { --k: var(--ts-thr); }
.ts-names .k-plc { --k: var(--ts-plc); }
/* they're pinned up one by one once the page's names are marked */
.ts-motion .ts-names li { opacity: 0; translate: 0 -24px; transition: opacity 0.5s ease, translate 0.8s var(--ts-soft); }
.ts-motion .ts-names.is-shown li { opacity: 1; translate: 0 0; }
.ts-motion .ts-names.is-shown li:nth-child(2) { transition-delay: 0.12s; }
.ts-motion .ts-names.is-shown li:nth-child(3) { transition-delay: 0.24s; }
.ts-motion .ts-names.is-shown li:nth-child(4) { transition-delay: 0.36s; }

/* ============================================================
   THE OWLERY: a question on a note, the answer on a scroll
   ============================================================ */
/* a note pinned to a piece sits at its own angle */
.ts-piece .ts-scrap { transform: rotate(var(--r, 0deg)); }
.ts-ask { width: min(100% - 2 * var(--ts-gutter), 660px); margin-top: 7.5rem; }
.ts-ask-q { position: absolute; left: -2rem; top: -6.5rem; z-index: 2; }
/* The scroll: two rollers (p-roller-top, bottom) and the sheet between
   (p-scroll-sheet), which runs under the rollers' paper from 14% to 89%
   of their width. It unrolls as you come to it. */
.ts-scroll {
  position: relative;
  container-type: inline-size;
  padding: 6.5% 0;
  rotate: 0.8deg;
  opacity: clamp(0, var(--p) * 5, 1);
  filter: drop-shadow(0 28px 36px rgba(0, 0, 0, 0.55));
}
.ts-scroll::before,
.ts-scroll::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 13cqw;
  z-index: 1;
  background: url(/apps/talespoke/art/p-roller-top.webp?v=2026-09-24) center / 100% 100% no-repeat;
}
.ts-scroll::before { top: 0; }
.ts-scroll::after { top: calc(5cqw + (100% - 18cqw) * var(--p)); background-image: url(/apps/talespoke/art/p-roller-bottom.webp?v=2026-09-24); }
.ts-scroll-body {
  --s: var(--ts-px);
  position: relative;
  margin: 0 11cqw 0 14.6cqw;
  border-style: solid;
  border-width: 0;
  border-image-source: url(/apps/talespoke/art/p-scroll-sheet.webp?v=2026-09-24);
  border-image-slice: 24 34 104 34 fill;
  border-image-width: calc(24px * var(--s)) calc(34px * var(--s)) calc(104px * var(--s)) calc(34px * var(--s));
  padding: calc(8cqw + 0.6rem) 6cqw calc(8cqw + 0.8rem);
  clip-path: inset(0 0 calc((1 - var(--p)) * 100%) 0);
  color: var(--ts-brown);
  text-align: center;
}
.ts-scroll-label { margin: 0; font-family: var(--ts-serif); font-style: italic; font-size: 1rem; color: var(--ts-brown2); }
.ts-scroll-answer { margin: 0.6rem 0 0; font-family: var(--ts-serif); font-size: clamp(1.15rem, 0.95rem + 0.8vw, 1.6rem); line-height: 1.45; }
.ts-scroll .mk.npc { text-decoration-color: var(--ts-npc); }
.ts-scroll .mk.plc { text-decoration-color: var(--ts-plc); }
.ts-scroll-foot { margin: 1rem 0 0; font-family: var(--ts-serif); font-style: italic; font-size: 0.95rem; color: var(--ts-brown2); }

/* ============================================================
   THE CARD TABLE: the app, taped up and fanned as you arrive
   ============================================================ */
.ts-hand-of-cards {
  --cw: clamp(170px, 19vw, 270px);
  --spread: calc(var(--cw) * 0.84);
  position: relative;
  height: calc(var(--cw) * 2.17 + 7rem);
  margin-top: clamp(40px, 5vw, 64px);
}
.ts-shot {
  position: absolute;
  left: 50%;
  top: 1.6rem;
  width: var(--cw);
  margin: 0 0 0 calc(var(--cw) / -2);
  transform: translateX(calc(var(--x) * var(--spread) * var(--p))) translateY(calc((1 - var(--p)) * 90px)) rotate(calc(var(--r) * var(--p)));
  transform-origin: 50% 110%;
  opacity: clamp(0, var(--p) * 3, 1);
}
.ts-shot:nth-child(2) { z-index: 2; top: 0.6rem; }
.ts-shot img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10% / 4.6%;
  box-shadow: 0 0 0 2.5px var(--ts-ink), 0 0 0 6.5px #FFF3E0, 0 30px 40px rgba(0, 0, 0, 0.5);
  background: #F6F5F2;
}
/* one strip of tape across each */
.ts-shot::before {
  content: '';
  position: absolute;
  left: 30%;
  top: -3.4%;
  z-index: 1;
  width: 40%;
  aspect-ratio: 213 / 71;
  background: url(/apps/talespoke/art/p-tape.webp?v=2026-09-25) center / 100% 100% no-repeat;
  rotate: -4deg;
}
.ts-shot:nth-child(2)::before { rotate: 3deg; }
/* each caption keeps to its own card's share of the fan */
.ts-shot figcaption { max-width: calc(var(--spread) - 1rem); margin: 1.2rem auto 0; font-family: var(--ts-serif); font-style: italic; font-size: 1rem; line-height: 1.3; color: var(--ts-moon2); text-align: center; text-wrap: balance; }

/* ============================================================
   THE HALL OF RECORDS: the character sheet, on parchment
   (p-parchment: deckled, edges curling into shade, a corner lifting)
   ============================================================ */
.ts-sheet {
  --s: var(--ts-px);
  position: relative;
  border-style: solid;
  border-width: 0;
  border-image-source: url(/apps/talespoke/art/p-parchment.webp?v=2026-09-24);
  border-image-slice: 150 160 64 72 fill;
  border-image-width: calc(150px * var(--s)) calc(160px * var(--s)) calc(64px * var(--s)) calc(72px * var(--s));
  padding: clamp(34px, 4.4vw, 54px) clamp(26px, 5.4vw, 64px) clamp(30px, 4vw, 46px);
  color: var(--ts-brown);
}
.ts-sheet-head { padding-bottom: 1rem; border-bottom: 2px solid var(--ts-brown); text-align: center; }
.ts-sheet-name { margin: 0; font-family: var(--ts-display); font-variation-settings: 'SOFT' 100, 'WONK' 1; font-weight: 600; font-size: clamp(1.9rem, 1.3rem + 2vw, 2.9rem); line-height: 1.02; letter-spacing: -0.02em; }
.ts-sheet-class { margin: 0.3rem 0 0; font-family: var(--ts-serif); font-style: italic; font-size: clamp(1.05rem, 1rem + 0.3vw, 1.25rem); color: var(--ts-brown2); }
.ts-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; border-bottom: 1px solid rgba(58, 42, 22, 0.35); }
.ts-stats div { display: flex; flex-direction: column-reverse; align-items: center; gap: 0.2rem; padding: 1.1rem 0.4rem; text-align: center; }
.ts-stats div + div { border-left: 1px solid rgba(58, 42, 22, 0.25); }
.ts-stats dt { font-family: var(--ts-serif); font-style: italic; font-size: 0.95rem; color: var(--ts-brown2); }
.ts-stats dd { margin: 0; font-family: var(--ts-display); font-variation-settings: 'SOFT' 100, 'WONK' 1; font-size: clamp(1.8rem, 1.4rem + 1.4vw, 2.7rem); font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.ts-traits { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(24px, 4vw, 48px); margin: 0.4rem 0 0; padding: 0; list-style: none; }
.ts-traits li { padding: 0.8rem 0; border-bottom: 1px dashed rgba(58, 42, 22, 0.3); font-family: var(--ts-serif); font-style: italic; font-size: clamp(1rem, 0.95rem + 0.3vw, 1.15rem); color: var(--ts-brown2); }
.ts-traits b { display: block; margin-bottom: 0.1rem; font-family: var(--ts-display); font-variation-settings: 'SOFT' 100, 'WONK' 1; font-style: normal; font-size: 1.25em; font-weight: 600; color: var(--ts-brown); }
.ts-equip { margin: 1.1rem 0 0; font-family: var(--ts-serif); font-style: italic; font-size: 1rem; color: var(--ts-brown2); text-align: center; }
.ts-equip b { font-style: normal; color: var(--ts-brown); }

/* ============================================================
   THE LAIR: the dragon on its hoard, what it guards on a note
   ============================================================ */
.ts-lair { width: min(100% - 2 * var(--ts-gutter), 620px); }
.ts-lair-art { display: block; width: 78%; height: auto; margin: 0 0 0 auto; filter: drop-shadow(0 26px 30px rgba(0, 0, 0, 0.5)); }
.ts-lair-note { position: absolute; left: 0; bottom: 8%; z-index: 2; width: clamp(200px, 40%, 250px); }

/* ============================================================
   THE TREASURY: the price on a scroll
   ============================================================ */
.ts-scroll--wide { width: min(100% - 2 * var(--ts-gutter), 820px); margin: clamp(32px, 4.5vw, 56px) auto 0; }
.ts-plans { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ts-plan { padding: 0.4rem 1rem 1.2rem; }
.ts-plan + .ts-plan { border-left: 1px solid rgba(58, 42, 22, 0.3); }
.ts-plan-name { margin: 0; font-family: var(--ts-display); font-variation-settings: 'SOFT' 100, 'WONK' 1; font-weight: 600; font-size: clamp(2.2rem, 1.6rem + 2vw, 3.2rem); line-height: 1; color: var(--ts-brown); }
.ts-plan-price { margin: 0.6rem 0 0; font-family: var(--ts-display); font-variation-settings: 'SOFT' 100, 'WONK' 1; font-weight: 500; font-size: clamp(1.05rem, 0.95rem + 0.4vw, 1.3rem); color: var(--ts-brown); }
.ts-plan-note { margin: 0.4rem 0 0; font-family: var(--ts-serif); font-style: italic; font-size: 1rem; line-height: 1.4; color: var(--ts-brown2); }
.ts-smallprint { margin: 0.6rem 0 0; padding-top: 1rem; border-top: 1px solid rgba(58, 42, 22, 0.3); font-family: var(--ts-serif); font-size: 0.9rem; line-height: 1.55; color: var(--ts-brown); text-align: left; }

/* ============================================================
   THE ROOKERY: the form, on a letter (p-letter)
   ============================================================ */
#notify .ts-piece { width: min(100% - 2 * var(--ts-gutter), 560px); }
.ts-letter {
  --s: var(--ts-px);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  border-style: solid;
  border-width: 0;
  border-image-source: url(/apps/talespoke/art/p-letter.webp?v=2026-09-24);
  border-image-slice: 36 24 24 24 fill;
  border-image-width: calc(36px * var(--s)) calc(24px * var(--s)) calc(24px * var(--s)) calc(24px * var(--s));
  padding: clamp(34px, 5vw, 48px) clamp(24px, 5vw, 48px) clamp(26px, 4vw, 36px);
  color: var(--ts-ink);
  text-align: center;
}
.ts-letter-title { margin: 0; font-family: var(--ts-display); font-variation-settings: 'SOFT' 100, 'WONK' 1; font-weight: 600; font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem); }
.ts-letter .form-group { display: flex; flex-direction: column; gap: 0.3rem; margin: 1.4rem 0 0; text-align: left; }
.ts-letter label { font-family: var(--ts-serif); font-style: italic; font-size: 1rem; color: var(--ts-ink2); }
.ts-letter input[type="email"] {
  height: 46px;
  padding: 0;
  border: 0;
  border-bottom: 2px solid var(--ts-ink);
  border-radius: 0;
  background: transparent;
  font-family: var(--ts-serif);
  font-size: 1.15rem;
  color: var(--ts-ink);
}
.ts-letter input[type="email"]::placeholder { color: rgba(28, 27, 25, 0.4); }
.ts-letter input[type="email"]:focus { outline: none; border-bottom-color: var(--ts-npc); }
.ts-letter .ts-slip { align-self: center; margin-top: 1.6rem; }
.ts-letter .sp-notify-status { margin: 0.8rem 0 0; font-family: var(--ts-serif); font-style: italic; font-size: 1.05rem; color: var(--ts-ink); text-align: center; }
.ts-letter .sp-notify-status.is-error { color: var(--ts-npc); }
.ts-letter.is-done .form-group,
.ts-letter.is-done .cf-turnstile { display: none; }

/* ---------- the site's close, at night ---------- */
.bs .cta-section--light { background: var(--ts-deep); border-top: 1px solid rgba(255, 255, 255, 0.08); }
.bs .cta-section--light h2 { color: var(--ts-moon); }
.bs .cta-section--light p { color: var(--ts-moon2); }
.bs .sp-cluster { background: var(--ts-night); border-top: 1px solid rgba(255, 255, 255, 0.08); }
.bs .sp-cluster-name { color: var(--ts-moon); }
.bs .sp-cluster-links a { color: var(--ts-moon2); }
.bs .sp-cluster-links a[aria-current="page"],
.bs .sp-cluster-links a:hover { color: var(--ts-gold); }

/* ============================================================
   NARROWER SCREENS AND PHONES
   ============================================================ */
/* Wide screens: each stop to one side, its heading, paper and small print
   in one column, and the dragon's lane on the other side, where he comes
   down turned to look at it. The sides switch at every stop, and he
   crosses with them. (talespoke.js finds the lane from the stop's
   padding.) */
@media (min-width: 1000px) {
  .ts-stop { --lane: calc((100vw - 2 * var(--ts-left)) * 0.4); }
  .ts-stop--l { --dx: -1; padding-left: var(--ts-left); padding-right: calc(var(--ts-left) + var(--lane)); }
  .ts-stop--r { --dx: 1; padding-left: calc(var(--ts-left) + var(--lane)); padding-right: var(--ts-left); }
  .ts-stop .ts-wrap { max-width: none; margin: 0; padding: 0; }
  .ts-stop .ts-head { align-items: flex-start; text-align: left; }
  .ts-stop .ts-fine { margin-left: 0; text-align: left; }
  .ts-stop .ts-piece,
  .ts-stop .ts-scroll--wide { width: min(100%, var(--w, 760px)); margin-left: 0; margin-right: 0; }
  .ts-stop .ts-ask { --w: 660px; }
  .ts-stop .ts-lair { --w: 620px; }
  .ts-stop .ts-scroll--wide { --w: 820px; }
  #notify .ts-piece { width: min(100%, 560px); }
  .ts-scr .ts-die { margin-left: 0; }
  .ts-stop .ts-names { justify-content: flex-start; }
  .ts-stop .ts-names li { width: clamp(118px, calc(25% - 0.5rem), 176px); }
}

@media (max-width: 800px) {
  .ts-film-track { height: 580vh; }
  .ts-film-shade { background: linear-gradient(to top, rgba(23, 26, 60, 0.97) 0%, rgba(23, 26, 60, 0.86) 38%, rgba(23, 26, 60, 0) 62%); }
  /* the intro on a phone: centred under the scene */
  .ts-intro { left: 20px; right: 20px; top: auto; bottom: calc(24px + env(safe-area-inset-bottom)); width: auto; transform: none; text-align: center; }
  .ts-film.is-past .ts-intro { transform: translateY(-12px); }
  .ts-brand { justify-content: center; margin: 0 0 0.5rem; }
  .ts-brand img { width: 28px; height: 28px; }
  .ts-hero-title { max-width: none; margin: 0 auto; font-size: clamp(1.85rem, 1rem + 4vw, 2.8rem); }
  .ts-intro .ts-lede { margin: 0.6rem auto 0; font-size: 1.05rem; line-height: 1.35; }
  .ts-intro .ts-slip { margin-top: 1rem; width: 14rem; font-size: 1.05rem; }
  .ts-hint { margin: 0.6rem 0 0; font-size: 0.95rem; }
  .ts-die { gap: 0.6rem; margin-top: 1.6rem; }
  .ts-die img { width: 84px; }
  .ts-ask-q { left: 50%; top: -6.5rem; margin-left: -100px; width: 200px; }
  .ts-hand-of-cards { --cw: clamp(130px, 38vw, 200px); --spread: calc(var(--cw) * 0.5); height: calc(var(--cw) * 2.17 + 6rem); }
  .ts-shot figcaption { display: none; }
  .ts-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ts-stats div:nth-child(3) { border-left: 0; }
  .ts-stats div:nth-child(n+3) { border-top: 1px solid rgba(58, 42, 22, 0.25); }
  .ts-traits { grid-template-columns: minmax(0, 1fr); }
  .ts-lair-art { width: 86%; margin: 0 auto; }
  .ts-lair-note { position: relative; left: auto; bottom: auto; margin: -3rem auto 0; }
  .ts-plans { grid-template-columns: minmax(0, 1fr); }
  .ts-plan + .ts-plan { border-left: 0; border-top: 1px solid rgba(58, 42, 22, 0.3); padding-top: 1.2rem; }
}

@media (max-width: 600px) {
  .bs { --ts-px: 0.38; }
  .ts-names li { width: calc(50% - 0.6rem); max-width: 180px; }
}

@media (prefers-reduced-motion: reduce) {
  .bs *, .bs *::before, .bs *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
}
