/*
  Palette
  desk       #d3cfc4  the surface everything sits on
  onionskin  #e6edf0  airmail paper, translucent
  ink        #1b2340  blue-black ribbon
  ink-soft   #5a6383
  saffron    #f2a93b  poster
  magenta    #c8256a  poster
  cobalt     #2447a8  airmail stripe
  vermilion  #d9412b  airmail stripe
*/
:root {
  --desk: #d3cfc4;
  --onion: 230 237 240;
  --ink: #1b2340;
  --ink-soft: #5a6383;
  --saffron: #f2a93b;
  --magenta: #c8256a;
  --cobalt: #2447a8;
  --vermilion: #d9412b;
  --mono: "Courier Prime", "Courier New", Courier, monospace;
  --poster: "Yatra One", "Noto Sans Devanagari", "Devanagari MT", serif;
}

* { box-sizing: border-box; }

html { min-height: 100%; }
html, body {
  margin: 0;
  background: var(--desk);
  color: var(--ink);
  font-family: var(--mono);
  -webkit-font-smoothing: antialiased;
}

body {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  background: #b9b3a5;
}
/* linen weave and paper grain over the desk, fixed so it doesn't scroll with the page */
/* the desk under a lamp: one warm pool of light up and to the left, the rest
   falling away into shadow. Fixed to the screen, so the light stays where it is
   while the paper moves under it. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(0deg,  rgba(255,255,255,.07) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(60,50,30,.06) 0 1px, transparent 1px 3px),
    radial-gradient(ellipse 62% 48% at 24% 6%,  rgba(255,240,205,.95) 0%, rgba(255,236,196,.62) 28%, rgba(250,230,190,.22) 55%, rgba(250,230,190,0) 75%),
    radial-gradient(ellipse 140% 120% at 24% 6%, rgba(0,0,0,0) 30%, rgba(48,36,18,.22) 62%, rgba(40,30,14,.55) 100%),
    linear-gradient(160deg, #dcd7ca 0%, #cbc5b7 45%, #a9a293 100%);
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 20;
  pointer-events: none;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 .5 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  opacity: .30;
  mix-blend-mode: multiply;
}
body > * { position: relative; z-index: 1; }

[hidden] { display: none !important; }

/* ---------- deck: cassette + platen ---------- */

.deck {
  width: min(94vw, 640px);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 20vh;
  transition: padding .9s cubic-bezier(.4, 0, .2, 1);
}
/* once the tape is playing the deck sticks to the top and stays there */
.deck.docked {
  position: sticky;
  top: 0;
  z-index: 3;
  padding-top: 10px;
  padding-bottom: 6px;
  background: rgba(214, 210, 199, .72);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.cassette {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  width: min(86vw, 400px);
  padding: 0;
  color: var(--ink);
  font-family: inherit;
  transition: width .9s cubic-bezier(.4, 0, .2, 1);
  -webkit-tap-highlight-color: transparent;
}
.cassette:focus-visible { outline: 2px solid var(--cobalt); outline-offset: 10px; border-radius: 14px; }
.cassette svg { width: 100%; height: auto; display: block; overflow: visible; }
.deck { perspective: 900px; }
.cassette.flip-out { animation: flipout .48s cubic-bezier(.5, 0, .9, .5) forwards; }
.cassette.flip-in  { animation: flipin  .52s cubic-bezier(.1, .6, .3, 1) forwards; }
@keyframes flipout { to { transform: rotateY(90deg) translateY(-6px); } }
@keyframes flipin  { from { transform: rotateY(-90deg) translateY(-6px); } to { transform: none; } }

.label-side  { font: 700 15px var(--mono); fill: var(--ink); }
.label-title { font: 26px var(--poster); fill: var(--magenta); }
.label-latin { font: 400 11px var(--mono); fill: var(--ink); letter-spacing: .02em; }
.label-small { font: 400 9.5px var(--mono); fill: var(--ink); }

.reel { transform-origin: 0 0; }
.playing .reel { animation: spin 1.5s linear infinite; }
.rewinding .reel { animation: spin .28s linear infinite reverse; }
@keyframes spin { to { transform: rotate(-360deg); } }
/* the ribbon slides left to right while the tape plays */
.playing #tape-sheen { animation: travel .5s linear infinite; }
@keyframes travel { to { stroke-dashoffset: -31; } }

.hint {
  display: block;
  margin-top: 26px;
  font-size: 14px;
  color: var(--ink-soft);
  transition: opacity .4s;
}
.docked .hint { display: none; }
.docked .cassette { width: min(40vw, 168px); }

/* ---------- sheet ---------- */

.platen {
  width: 100%;
  height: 16px;
  margin-top: 10px;
  border-radius: 8px;
  background: linear-gradient(#4a4744, #201e1c 55%, #302d2a);
  box-shadow: 0 3px 4px -2px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.12);
}

/* ---------- volume knob ---------- */

.knob {
  position: absolute;
  left: calc(50% + min(20vw, 84px) + 10px);
  top: calc(10px + (min(40vw, 168px) * 254 / 400) / 2);
  transform: translateY(-50%);
  width: 60px;
  height: 60px;
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.knob:active { cursor: grabbing; }
.knob:focus-visible { outline: 2px solid var(--cobalt); outline-offset: 4px; border-radius: 50%; }
.knob svg { width: 100%; height: 100%; display: block; overflow: visible; }
.knob .dial { transform-origin: 40px 40px; transform: rotate(var(--angle, 0deg)); transition: transform 80ms linear; }
.knob.dragging .dial { transition: none; }
.deck { position: relative; }

/* ---------- sheet ---------- */

.sheet { width: min(94vw, 640px); margin-top: -8px; margin-bottom: 10vh; position: relative; }

/* onion-skin airmail: thin, faintly translucent, red and blue chevrons at the edge.
   The stripes are drawn on fixed-size tiles so they never shift as the page lays out. */
.paper {
  position: relative;
  min-height: 40vh;
  background:
    repeating-linear-gradient(0deg, rgba(255,255,255,.16) 0 1px, transparent 1px 3px),
    rgb(var(--onion) / .80);
  margin: 0 10px;
  padding: 46px clamp(20px, 6vw, 54px) 84px;
  box-shadow:
    0 1px 0 rgba(255,255,255,.6) inset,
    10px 18px 30px -20px rgba(20,25,50,.55);
  clip-path: inset(0 0 100% 0);
}
.paper::before, .paper::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: 7px;
  background: repeating-linear-gradient(-45deg,
      var(--vermilion) 0 10px, rgb(var(--onion)) 10px 18px,
      var(--cobalt) 18px 28px, rgb(var(--onion)) 28px 36px);
  background-size: 51px 51px;
  pointer-events: none;
}
.paper::before { left: 0; }
.paper::after  { right: 0; }
.sheet::after {
  content: "";
  position: absolute;
  left: 10px; right: 10px; bottom: 0;
  height: 7px;
  background: repeating-linear-gradient(-45deg,
      var(--vermilion) 0 10px, rgb(var(--onion)) 10px 18px,
      var(--cobalt) 18px 28px, rgb(var(--onion)) 28px 36px);
  background-size: 51px 51px;
  clip-path: inset(100% 0 0 0);
  pointer-events: none;
}
.sheet.in::after { clip-path: none; }

/* the sheet is fed in from under the platen, ratcheted like a real feed */
.sheet.feeding .paper { animation: feed 3.2s steps(24, end) forwards; }
.sheet.in .paper { clip-path: none; }
@keyframes feed { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0 0 0 0); } }

/* the ghost holds the sheet open at its final height; the print layer types over it */
.ghost { color: transparent !important; user-select: none; }
.ghost .soft, .ghost .strong { color: transparent !important; }
.print { position: absolute; top: 46px; left: clamp(20px, 6vw, 54px); right: clamp(20px, 6vw, 54px); }

.typed {
  margin: 0;
  font: 400 clamp(14.5px, 2.7vw, 17px)/1.7 var(--mono);
  white-space: pre-wrap;
  overflow-wrap: break-word;
  color: var(--ink);
  display: inline;
}
.ghost { display: block; }

/* the signature: a block in both layers so the typed name lands in the same place.
   Only takes up room once the image has loaded. */
.sig { display: none; height: 0; }
.hassig .sig { display: block; height: 3.9em; }
img.sig {
  width: auto;
  max-width: 60%;
  margin: .3em 0 .1em -.4em;
  transform: rotate(-2deg);
  clip-path: inset(0 100% 0 0);
  object-fit: contain;
  object-position: left center;
}
img.sig.drawn { animation: sign 2.4s cubic-bezier(.45, .05, .55, .95) forwards; }
img.sig.drawn.instant { animation: none; clip-path: none; }
@keyframes sign { to { clip-path: inset(0 0 0 0); } }

.ch {
  position: relative;
  top: var(--y, 0);
  opacity: var(--o, 1);
}
.ch.live { animation: strike 110ms cubic-bezier(.2, .9, .3, 1) both; }
@keyframes strike {
  0%   { transform: scale(1.35) translateY(.5px); opacity: 1; text-shadow: 0 0 1.5px rgba(27,35,64,.9); }
  60%  { transform: scale(.97); }
  100% { transform: none; opacity: var(--o, 1); text-shadow: none; }
}
/* carriage return: the sheet kicks sideways and settles */
.paper.jolt { animation: jolt 240ms cubic-bezier(.2, .8, .3, 1); }
@keyframes jolt {
  0% { transform: translateX(0); }
  25% { transform: translateX(5px); }
  60% { transform: translateX(-1.5px); }
  100% { transform: translateX(0); }
}
.typed .soft { color: var(--ink-soft); }
.typed .strong { font-weight: 700; }

.cursor {
  display: inline-block;
  position: relative;
  width: 0;
  height: 1.05em;
  vertical-align: text-bottom;
  opacity: 0;
}
.cursor::after {
  content: "";
  position: absolute;
  left: 1px; top: 0;
  width: .6em; height: 100%;
  background: var(--ink);
}
.sheet.in .cursor, .ending.in .cursor { animation: blink 1s steps(1) infinite; }
@keyframes blink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }
body.done .sheet .cursor { animation: none; opacity: 0; }

/* ---------- ending ---------- */

.ending {
  position: fixed;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  padding: 24px;
  opacity: 0;
  pointer-events: none;
}
.ending figure {
  margin: 0;
  width: min(92vw, 520px);
  transform: scale(.78) translateY(6vh);
  opacity: 0;
  transition: transform 4.2s cubic-bezier(.16, .8, .2, 1), opacity 3.2s ease;
}
body.done .ending { opacity: 1; pointer-events: auto; }
body.done .ending figure { transform: none; opacity: 1; }
/* the deck and the letter fall back and fade */
.deck, .sheet { transition: opacity 2.6s ease, transform 2.6s ease, filter 2.6s ease; }
body.done .deck, body.done .sheet { opacity: .07; transform: scale(.985); filter: blur(1.5px); }
body.done { overflow: hidden; }

.print-wrap {
  position: relative;
  background: #f7f5ee;
  padding: 3.6%;
  transform: rotate(-1.6deg);
  box-shadow:
    14px 22px 34px -22px rgba(20,20,30,.7),
    0 1px 0 rgba(255,255,255,.7) inset;
}
.print-wrap::after {
  /* gloss across the print, and a hint of curl at the corner */
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(112deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,0) 38%, rgba(255,255,255,0) 70%, rgba(255,255,255,.10) 100%),
    radial-gradient(60% 40% at 100% 100%, rgba(0,0,0,.08), transparent 70%);
  pointer-events: none;
}
.ending img {
  display: block;
  width: 100%;
  height: auto;
  background: #e9e5da;
  filter: saturate(.94) contrast(1.03) sepia(.06);
}
.date-stamp {
  position: absolute;
  right: 8%; bottom: 8%;
  font: 700 clamp(12px, 2.6vw, 15px)/1 var(--mono);
  letter-spacing: .12em;
  color: #ff9a2e;
  text-shadow: 0 0 6px rgba(255,150,40,.85), 0 0 1px rgba(255,200,120,.9);
  opacity: .92;
}
.ending figcaption {
  margin-top: 26px;
  text-align: center;
  font-size: clamp(14.5px, 2.7vw, 17px);
  line-height: 1.7;
}
.ending figcaption .typed { display: inline; }
body.done .ending { cursor: pointer; }
.ending-hint {
  position: absolute;
  left: 0; right: 0; bottom: max(18px, env(safe-area-inset-bottom));
  margin: 0;
  text-align: center;
  font-size: 13px;
  color: var(--ink-soft);
  opacity: 0;
  transition: opacity 1.4s ease;
}
.ending-hint.show { opacity: 1; }

/* ---------- side B ---------- */

.compose {
  position: fixed;
  left: 0; top: 0;
  width: 1px; height: 1px;
  padding: 0; border: 0;
  font-size: 16px;
  opacity: .01;
  resize: none;
}
.compose-hint {
  display: inline;
  margin-left: .6em;
  color: var(--ink-soft);
  font: italic 400 .92em/1.6 var(--mono);
  opacity: .75;
  pointer-events: none;
}
.paper.side-b { min-height: 70vh; cursor: text; }

.post {
  position: absolute;
  right: calc(50% + min(20vw, 84px) + 12px);
  top: calc(10px + (min(40vw, 168px) * 254 / 400) / 2);
  transform: translateY(-50%) rotate(-8deg);
  width: 62px; height: 62px;
  border-radius: 50%;
  border: 2.5px solid var(--vermilion);
  background: rgba(247, 245, 238, .92);
  color: var(--vermilion);
  font: 700 12px/1 var(--mono);
  letter-spacing: .16em;
  cursor: pointer;
  box-shadow: 6px 10px 18px -10px rgba(20,20,30,.6);
  -webkit-tap-highlight-color: transparent;
  animation: stampin .5s cubic-bezier(.2, 1.4, .5, 1) both;
}
@keyframes stampin { from { transform: translateY(-50%) rotate(-8deg) scale(.4); opacity: 0; } }
.post::before {
  content: "";
  position: absolute; inset: 5px;
  border-radius: 50%;
  border: 1px solid var(--vermilion);
  opacity: .6;
}
.post:disabled { opacity: .5; cursor: default; }
.post:focus-visible { outline: 2px solid var(--cobalt); outline-offset: 4px; }

.postmark {
  position: absolute;
  right: clamp(18px, 6vw, 48px);
  bottom: 28px;
  width: 118px; height: 118px;
  border-radius: 50%;
  border: 3px solid var(--vermilion);
  color: var(--vermilion);
  display: grid; place-content: center; gap: 4px;
  text-align: center;
  font: 700 16px/1 var(--mono);
  letter-spacing: .22em;
  transform: rotate(-14deg) scale(1.6);
  opacity: 0;
  mix-blend-mode: multiply;
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1.6 -.35'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1.6 -.35'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  pointer-events: none;
}
.postmark::before { content: ""; position: absolute; inset: 6px; border-radius: 50%; border: 1px solid var(--vermilion); }
.postmark small { font-size: 9.5px; letter-spacing: .1em; font-weight: 400; }
.postmark.down { transition: transform .14s cubic-bezier(.3, 1.4, .6, 1), opacity .1s; transform: rotate(-14deg) scale(1); opacity: .88; }

/* a quiet line at the foot of the screen */
.screen-hint {
  position: fixed;
  left: 0; right: 0; bottom: max(18px, env(safe-area-inset-bottom));
  z-index: 4;
  margin: 0;
  text-align: center;
  font-size: 13px;
  color: var(--ink-soft);
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.4s ease;
}
.screen-hint.show { opacity: 1; }
.paper.sent ~ * .cursor, .sheet:has(.paper.sent) .cursor { animation: none; opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .sheet.feeding .paper { animation-duration: .01s; }
  .playing .reel { animation-duration: 4s; }
  .playing #tape-sheen { animation-duration: 2s; }
  .ch.live { animation: none; }
  .paper.jolt { animation: none; }
  .ending figure { transition-duration: .6s; }
  img.sig.drawn { animation-duration: .01s; }
  .cassette.flip-out, .cassette.flip-in { animation-duration: .01s; }
}
