/* Mailbox — storybook gouache.
   The page walks #scene through data-state closed -> envelope -> open
   (mailbox.js drives it); "empty" is the quiet 404. Everything painted is
   either inline SVG in the HTML (recolored by the custom properties below)
   or a static backdrop under /art. */

/* ---------- palette ---------------------------------------------------- */

:root {
  /* daylight */
  --sky: #f6ead6;
  --sky-low: #f2ddbd;
  --hill-far: #c5cfa8;
  --hill-near: #a9bb90;
  --grass-deep: #8ea376;
  --box: #849cb1;
  --box-shade: #67809a;
  --box-light: #9db2c3;
  --box-mouth: #3e4a55;
  --door: #8fa6ba;
  --flag: #c8604c;
  --flag-deep: #a94b3a;
  --post: #8a6f52;
  --post-shade: #6f5842;
  --ink: #6b5644;
  --ink-soft: rgba(107, 86, 68, 0.72);
  --paper: #fbf4e2;
  --paper-edge: #e9d9bd;
  /* ink for text sitting on paper (the paper stays cream at dusk) */
  --paper-ink: #6b5644;
  --shadow-warm: rgba(107, 86, 68, 0.28);
  --ground-shadow: rgba(90, 106, 70, 0.35);
  --halo: rgba(200, 96, 76, 0.4);
  --backdrop: url('/art/scene.svg');
  --grain-opacity: 0.05;
}

/* Dusk, not inversion: the same warm world after the sun has gone down.
   Every page stamps data-theme="day" or "dusk" on <html> before first
   paint (from the reader's saved choice, else their local clock). An explicit
   stamp wins in both directions; with no JS nothing is stamped and the
   system preference below still decides. The dusk palette therefore appears
   twice — once per gate — and the two copies must stay identical. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='day']) {
    --sky: #55464e;
    --sky-low: #64514d;
    --hill-far: #667054;
    --hill-near: #545f45;
    --grass-deep: #434e36;
    --box: #6d8399;
    --box-shade: #526478;
    --box-light: #8195a8;
    --box-mouth: #2c343d;
    --door: #75899c;
    --flag: #c0604d;
    --flag-deep: #98453a;
    --post: #6f5a45;
    --post-shade: #55442f;
    --ink: #eadcc2;
    --ink-soft: rgba(234, 220, 194, 0.72);
    --paper: #f1e5ca;
    --paper-edge: #d9c6a2;
    --paper-ink: #5c4936;
    --shadow-warm: rgba(24, 18, 14, 0.5);
    --ground-shadow: rgba(20, 24, 14, 0.45);
    --halo: rgba(232, 168, 124, 0.35);
    --backdrop: url('/art/scene-dusk.svg');
    --grain-opacity: 0.07;
  }
}

:root[data-theme='dusk'] {
  --sky: #55464e;
  --sky-low: #64514d;
  --hill-far: #667054;
  --hill-near: #545f45;
  --grass-deep: #434e36;
  --box: #6d8399;
  --box-shade: #526478;
  --box-light: #8195a8;
  --box-mouth: #2c343d;
  --door: #75899c;
  --flag: #c0604d;
  --flag-deep: #98453a;
  --post: #6f5a45;
  --post-shade: #55442f;
  --ink: #eadcc2;
  --ink-soft: rgba(234, 220, 194, 0.72);
  --paper: #f1e5ca;
  --paper-edge: #d9c6a2;
  --paper-ink: #5c4936;
  --shadow-warm: rgba(24, 18, 14, 0.5);
  --ground-shadow: rgba(20, 24, 14, 0.45);
  --halo: rgba(232, 168, 124, 0.35);
  --backdrop: url('/art/scene-dusk.svg');
  --grain-opacity: 0.07;
}

/* ---------- base ------------------------------------------------------- */

* {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: 'Iowan Old Style', Palatino, 'Palatino Linotype', 'Book Antiqua',
    Georgia, 'Times New Roman', serif;
  color: var(--ink);
  background: linear-gradient(var(--sky) 55%, var(--sky-low));
  min-height: 100dvh;
}

/* painted landscape, pinned behind everything */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--backdrop) center bottom / cover no-repeat;
  pointer-events: none;
}

/* paper grain over everything, very quiet */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: var(--grain-opacity);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.42 0 0 0 0 0.34 0 0 0 0 0.26 0 0 0 0.9 0'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23g)'/%3E%3C/svg%3E");
  mix-blend-mode: multiply;
}

/* ---------- scene layout ---------------------------------------------- */

#scene {
  position: relative;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4dvh, 2.5rem) 1.25rem;
  overflow: hidden;
}

#scene[data-state='open'] {
  overflow: visible;
}

#scene[data-state='closed'],
#scene[data-state='envelope'] {
  cursor: pointer;
}

/* the mailbox stands a touch left of centre, on the near hill */
#mailbox {
  position: absolute;
  left: 50%;
  bottom: clamp(4.5rem, 16dvh, 11rem);
  width: clamp(240px, 76vw, 350px);
  transform: translateX(-54%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  /* visibility rides along so the faded box also leaves the tab order:
     it stays 'visible' for the whole fade and flips only at the end */
  transition: opacity 1.1s ease, transform 1.1s cubic-bezier(0.33, 0, 0.2, 1),
    visibility 1.1s;
}

#scene[data-state='open'] #mailbox {
  opacity: 0;
  transform: translateX(-54%) translateY(4%) scale(0.94);
  pointer-events: none;
  visibility: hidden;
}

#envelope {
  position: absolute;
  left: 50%;
  top: 38%;
  width: clamp(220px, 64vw, 300px);
  transform: translate(-50%, -50%) rotate(-2.5deg);
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 0.65rem;
  transition: opacity 0.9s ease, transform 1s cubic-bezier(0.33, 0, 0.2, 1),
    visibility 1s;
}

#scene[data-state='envelope'] #envelope {
  display: flex;
}

#scene[data-state='open'] #envelope {
  display: flex;
  opacity: 0;
  transform: translate(-50%, 6%) rotate(3deg) scale(0.82);
  pointer-events: none;
  visibility: hidden;
}

/* without JS #scene can never leave data-state='closed', so #letter is
   revealed immediately (below) instead of waiting for a tap that can never
   come — step the mailbox and envelope out of the way so they don't sit on
   top of it. (#envelope is already display:none by default via the base
   rule above; #mailbox is not, so it needs the explicit hide here.) */
:root:not(.js) body.letter-page #mailbox,
:root:not(.js) body.letter-page #envelope {
  display: none;
}

/* mailbox.js is the only thing that ever moves #scene past data-state
   'closed', so hiding #letter unconditionally would strand a no-JS reader
   with no path to the doc link. Gate the hide on :root.js — stamped by the
   inline head script below, same pattern as the home page's slip — so
   without it the letter is simply visible immediately, matching what
   prefers-reduced-motion already produces. The mailbox/envelope step aside
   to match (see the no-JS rule near their own definitions). */
:root.js #letter {
  display: none;
}

:root.js #scene[data-state='open'] #letter {
  display: flex;
}

/* ---------- type & buttons -------------------------------------------- */

#open-mailbox,
#open-envelope {
  appearance: none;
  border: 0;
  background: transparent;
  /* longhands, not the `font` shorthand: `inherit` is not a valid family
     inside the shorthand and the whole declaration would be dropped */
  font: inherit;
  font-style: italic;
  font-weight: 600;
  font-size: clamp(1.05rem, 4.2vw, 1.3rem);
  line-height: 1.3;
  letter-spacing: 0.01em;
  color: var(--ink);
  padding: 0.45rem 1rem 0.7rem;
  cursor: pointer;
  border-radius: 999px;
  /* hand-drawn underline */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='8' viewBox='0 0 120 8'%3E%3Cpath d='M4 5 Q 24 2 45 4.4 Q 72 7 94 4 Q 106 2.6 116 4.2' fill='none' stroke='%23c8604c' stroke-width='2.4' stroke-linecap='round' opacity='0.85'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center bottom 0.3rem;
  background-size: min(82%, 150px) 8px;
  transition: opacity 0.5s ease, transform 0.5s ease, visibility 0.5s;
}

/* a focus style that belongs to the world: a soft warm halo, no default ring */
:where(button, a):focus {
  outline: none;
}

:where(button, a):focus-visible {
  outline: none;
  box-shadow:
    0 0 0 3px var(--paper),
    0 0 0 6px var(--halo);
}

/* ---------- the day/dusk switch ---------------------------------------- */

/* a small ink doodle in the top corner: a moon by day, a sun by dusk.
   Only shown once the head script has stamped a theme — without JS the
   button could not do anything, so it stays hidden.
   It sits on a little hand-cut paper disc — a slightly wonky circle with a
   deckled edge and a soft warm shadow — so the eye can find it against both
   skies. The doodle is drawn in paper ink (dark in both themes): at dusk
   --ink turns cream and would vanish on the cream paper. */
#theme-toggle {
  display: none;
  position: absolute;
  top: max(0.85rem, env(safe-area-inset-top));
  right: max(0.85rem, env(safe-area-inset-right));
  z-index: 40;
  width: 44px;
  height: 44px;
  padding: 9px;
  appearance: none;
  border: 0;
  background: color-mix(in srgb, var(--paper) 78%, var(--paper-edge));
  border-radius: 48% 52% 51% 49% / 53% 47% 52% 48%;
  color: var(--paper-ink);
  cursor: pointer;
  opacity: 0.9;
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--paper-edge) 60%, transparent),
    0 2px 7px -1px var(--shadow-warm);
  transition: opacity 0.4s ease, box-shadow 0.4s ease;
}

:root[data-theme] #theme-toggle {
  display: block;
}

#theme-toggle:hover,
#theme-toggle:focus-visible {
  opacity: 1;
}

/* the shared focus halo, restated: the id-specific box-shadow above would
   otherwise override the :where() rule and lose the ring */
#theme-toggle:focus-visible {
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--paper-edge) 60%, transparent),
    0 0 0 3px var(--paper),
    0 0 0 6px var(--halo);
}

#theme-toggle svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* the doodle shows the world a tap would bring */
#theme-toggle .tt-sun,
:root[data-theme='dusk'] #theme-toggle .tt-moon {
  display: none;
}

:root[data-theme='dusk'] #theme-toggle .tt-sun {
  display: block;
}

/* ---------- the mailbox, painted -------------------------------------- */

.mb-art {
  display: block;
  width: 100%;
  height: auto;
  /* the door swings past the viewBox's left edge (x < 0) mid-open; an SVG
     clips to its viewport by default, which sliced the door off */
  overflow: visible;
}

.mb-shadow { fill: var(--ground-shadow); }
.mb-post { fill: var(--post); }
.mb-plank { fill: var(--post-shade); }
.mb-grain {
  fill: none;
  stroke: var(--post-shade);
  stroke-width: 2.5;
  stroke-linecap: round;
  opacity: 0.65;
}
.mb-body { fill: var(--box); }
.mb-mouth { fill: var(--box-mouth); }
.mb-belly {
  stroke: var(--box-light);
  stroke-width: 13;
  stroke-linecap: round;
  opacity: 0.5;
}
.mb-sheen {
  stroke: var(--box-light);
  stroke-width: 9;
  stroke-linecap: round;
  opacity: 0.55;
}
.mb-endshade {
  stroke: var(--box-shade);
  stroke-width: 14;
  stroke-linecap: round;
  opacity: 0.55;
}
.mb-heart { fill: var(--flag); opacity: 0.9; }
.mb-door { fill: var(--door); }
.mb-door-edge {
  stroke: var(--box-shade);
  stroke-width: 3;
  stroke-linecap: round;
  opacity: 0.55;
}
.mb-knob { fill: var(--paper); }
.mb-flag { fill: var(--flag); }
.mb-flag-arm,
.mb-bolt { fill: var(--flag-deep); }
.mb-flag-shade {
  stroke: var(--flag-deep);
  stroke-width: 4;
  stroke-linecap: round;
  opacity: 0.5;
}

/* pivots: each rotating group sits inside a parent <g translate(...)> that
   moves local (0,0) to the hinge, so rotation happens about the hinge */
.mb-flag-rot,
.mb-door-rot,
.env-flap-rot {
  transform-box: view-box;
  transform-origin: 0 0;
}

/* ---------- choreography ----------------------------------------------- */

/* tap one: the flag drops, then the door swings, then the envelope */
.mb-flag-rot {
  transition: transform 1s cubic-bezier(0.5, 0, 0.3, 1);
}

.mb-door-rot {
  transition: transform 1.1s cubic-bezier(0.45, 0, 0.25, 1) 0.5s;
}

#scene[data-state='envelope'] .mb-flag-rot,
#scene[data-state='open'] .mb-flag-rot,
.empty-page .mb-flag-rot {
  transform: rotate(148deg);
}

#scene[data-state='envelope'] .mb-door-rot,
#scene[data-state='open'] .mb-door-rot {
  transform: rotate(-136deg);
}

#scene[data-state='envelope'] #mailbox {
  transform: translateX(-54%) scale(0.98);
  opacity: 0.92;
}

#scene[data-state='envelope'] #open-mailbox,
#scene[data-state='open'] #open-mailbox {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}

/* the mailbox settles into the scene on arrival */
#scene[data-state='closed'] #mailbox {
  animation: mb-arrive 1.2s cubic-bezier(0.25, 0, 0.25, 1) 0.15s backwards;
}

@keyframes mb-arrive {
  from {
    opacity: 0;
    transform: translateX(-54%) translateY(2.5%);
  }
}

/* the envelope slides out of the open door and comes to hand */
#scene[data-state='envelope'] #envelope {
  animation: env-out 1.4s cubic-bezier(0.3, 0, 0.22, 1) 1.1s backwards;
}

@keyframes env-out {
  from {
    opacity: 0;
    transform: translate(-64%, 4%) rotate(-11deg) scale(0.42);
  }
  45% {
    opacity: 1;
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%) rotate(-2.5deg) scale(1);
  }
}

#scene[data-state='envelope'] #open-envelope {
  animation: soft-in 0.9s ease 2.3s backwards;
}

/* the label sits on the envelope itself, like an address line */
#open-envelope {
  position: absolute;
  left: 50%;
  top: 60%;
  transform: translateX(-50%);
  width: max-content;
  color: var(--paper-ink);
}

@keyframes soft-in {
  from { opacity: 0; }
}

/* ---------- the envelope, painted -------------------------------------- */

.env-art {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 12px 16px var(--shadow-warm));
}

.env-body { fill: var(--paper); }
.env-fold {
  stroke: var(--paper-edge);
  stroke-width: 3;
  stroke-linecap: round;
  opacity: 0.9;
}
.env-flap { fill: color-mix(in srgb, var(--paper) 78%, var(--paper-edge)); }
.env-flap-edge {
  stroke: var(--paper-edge);
  stroke-width: 3;
  stroke-linecap: round;
}
.env-seal { fill: var(--flag); }

.env-flap-rot {
  transition: transform 0.8s cubic-bezier(0.4, 0, 0.25, 1);
}

/* tap two: the flap lifts as the envelope gives up the letter */
#scene[data-state='open'] .env-flap-rot {
  transform: scaleY(-1);
}

/* ---------- the letter ------------------------------------------------- */

#scene[data-state='open'] #letter {
  animation: letter-in 1.1s cubic-bezier(0.3, 0, 0.22, 1) 0.45s backwards;
}

#letter {
  position: relative;
  z-index: 10;
  /* unconditional so a no-JS reader (see the :root.js gate above) gets the
     same column layout prefers-reduced-motion forces, not block-stacked */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  width: min(92vw, 29rem);
  /* a flex item's min-width:auto would let a large photo's intrinsic
     width force the card past the viewport */
  min-width: 0;
  margin: auto;
  padding: 1.5rem 1.3rem 1.6rem;
  background: var(--paper);
  border-radius: 16px 22px 14px 20px / 20px 14px 22px 16px;
  box-shadow:
    0 16px 30px -14px var(--shadow-warm),
    0 34px 60px -34px var(--shadow-warm);
  transform: rotate(-1.2deg);
}

@keyframes letter-in {
  from {
    opacity: 0;
    transform: translateY(9dvh) rotate(0.6deg) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translateY(0) rotate(-1.2deg) scale(1);
  }
}

/* a small painted heart at the head of the card, like a broken wax seal —
   it keeps letters with no photo and no caption from feeling bare */
#letter::before {
  content: '';
  width: 30px;
  height: 28px;
  flex: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-9 -12 18 20'%3E%3Cpath d='M0 7 C -8 -2 -4 -11 0 -5 C 4 -11 8 -2 0 7 Z' fill='%23c8604c' transform='rotate(-7)'/%3E%3C/svg%3E") center / contain no-repeat;
}

#postcard {
  max-width: 100%;
  max-height: min(46dvh, 24rem);
  width: auto;
  height: auto;
  object-fit: contain;
  border: 9px solid color-mix(in srgb, var(--paper) 55%, white);
  border-radius: 5px 7px 4px 6px;
  box-shadow: 0 10px 20px -10px var(--shadow-warm);
  transform: rotate(1.3deg);
}

#caption {
  margin: 0;
  max-width: 34ch;
  font-style: italic;
  font-size: clamp(1.05rem, 4vw, 1.3rem);
  line-height: 1.45;
  text-align: center;
  color: var(--paper-ink);
}

#read-letter {
  display: inline-block;
  margin-top: 0.4rem;
  padding: 0.85rem 1.7rem 0.95rem;
  font-style: italic;
  font-weight: 600;
  font-size: 1.1rem;
  letter-spacing: 0.01em;
  color: #fdf3e4;
  text-decoration: none;
  background: var(--flag-deep);
  border-radius: 26px 30px 24px 28px / 30px 24px 28px 26px;
  box-shadow:
    inset 0 2px 0 rgba(253, 243, 228, 0.25),
    0 8px 16px -8px var(--flag-deep);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

/* the shared focus halo, restated (as on #theme-toggle): the id-specific
   box-shadow above would otherwise override the :where() rule and lose the
   ring. The stock cream + halo pair would vanish here — cream on the paper
   card, a faint warm red beside a flag-red button — so the outer ring is
   deep flag red instead, held off the button by a cream gap that reads
   against both the day and dusk cards. */
#read-letter:focus-visible {
  box-shadow:
    inset 0 2px 0 rgba(253, 243, 228, 0.25),
    0 0 0 3px var(--paper),
    0 0 0 6px var(--flag-deep),
    0 8px 16px -8px var(--flag-deep);
}

#read-letter:hover {
  transform: translateY(-1px) rotate(-0.5deg);
}

#read-letter:active {
  transform: translateY(1px);
}

/* ---------- the home mailbox (/) ---------------------------------------
   The letter page's flag starts up (you have mail) and drops; here it starts
   down and the owner raises it to post something. Raising it unfolds a paper
   slip carrying the passphrase form. The head script stamps .js on <html>;
   without it the slip is simply visible, so nothing hides behind JS. */

.home-page .mb-flag-rot {
  transform: rotate(148deg);
}

.home-page #scene[data-flag='up'] .mb-flag-rot {
  transform: rotate(0deg);
}

.home-page #scene[data-flag='down'] #mailbox {
  animation: mb-arrive 1.2s cubic-bezier(0.25, 0, 0.25, 1) 0.15s backwards;
}

/* the whole scene is tappable only once JS can answer the tap */
:root.js .home-page #scene[data-flag='down'] {
  cursor: pointer;
}

/* the raise button borrows the letter page's underlined ink look */
#raise-flag {
  appearance: none;
  border: 0;
  background: transparent;
  /* longhands, not the `font` shorthand: `inherit` is not a valid family
     inside the shorthand and the whole declaration would be dropped */
  font: inherit;
  font-style: italic;
  font-weight: 600;
  font-size: clamp(1.05rem, 4.2vw, 1.3rem);
  line-height: 1.3;
  letter-spacing: 0.01em;
  color: var(--ink);
  padding: 0.45rem 1rem 0.7rem;
  cursor: pointer;
  border-radius: 999px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='8' viewBox='0 0 120 8'%3E%3Cpath d='M4 5 Q 24 2 45 4.4 Q 72 7 94 4 Q 106 2.6 116 4.2' fill='none' stroke='%23c8604c' stroke-width='2.4' stroke-linecap='round' opacity='0.85'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center bottom 0.3rem;
  background-size: min(82%, 150px) 8px;
  transition: opacity 0.5s ease, transform 0.5s ease, visibility 0.5s;
}

/* without JS the slip is already out; a dead button would only confuse */
:root:not(.js) #raise-flag {
  display: none;
}

.home-page #scene[data-flag='up'] #raise-flag {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}

/* the slip: a small piece of paper pinned above the box */
#slip {
  position: absolute;
  z-index: 10;
  left: 50%;
  top: clamp(3.5rem, 14dvh, 7rem);
  width: min(86vw, 20rem);
  margin: 0;
  padding: 1.15rem 1.25rem 1.3rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  text-align: center;
  color: var(--paper-ink);
  background: var(--paper);
  border-radius: 14px 20px 12px 18px / 18px 12px 20px 14px;
  box-shadow:
    0 14px 26px -12px var(--shadow-warm),
    0 30px 52px -30px var(--shadow-warm);
  transform: translateX(-50%) rotate(-1.6deg);
  transform-origin: top center;
  transition: opacity 0.6s ease 0.55s,
    transform 0.7s cubic-bezier(0.3, 0, 0.22, 1) 0.55s, visibility 0.7s 0.55s;
}

/* folded away until the flag is raised — but only when JS can unfold it */
:root.js .home-page #scene[data-flag='down'] #slip {
  opacity: 0;
  transform: translateX(-50%) translateY(14%) rotate(1.5deg) scale(0.94) scaleY(0.25);
  visibility: hidden;
  pointer-events: none;
}

#slip label,
.slip-note {
  margin: 0;
  font-style: italic;
  font-size: clamp(0.95rem, 3.6vw, 1.1rem);
  line-height: 1.45;
}

.slip-error {
  margin: 0;
  font-style: italic;
  font-size: 0.95rem;
  color: var(--flag-deep);
}

#slip input[type='password'] {
  width: min(100%, 15rem);
  font: inherit;
  font-size: 1.05rem;
  text-align: center;
  color: var(--paper-ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--paper-edge);
  border-radius: 0;
  padding: 0.25rem 0.2rem 0.35rem;
}

#slip input[type='password']:focus {
  outline: none;
  border-bottom-color: var(--flag);
}

/* the way through: styled like the letter page's red button, a size down */
.slip-action {
  appearance: none;
  border: 0;
  display: inline-block;
  margin-top: 0.35rem;
  padding: 0.6rem 1.4rem 0.7rem;
  font: inherit;
  font-style: italic;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.3;
  letter-spacing: 0.01em;
  color: #fdf3e4;
  text-decoration: none;
  cursor: pointer;
  background: var(--flag-deep);
  border-radius: 24px 28px 22px 26px / 28px 22px 26px 24px;
  box-shadow:
    inset 0 2px 0 rgba(253, 243, 228, 0.25),
    0 8px 16px -8px var(--flag-deep);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

/* the shared focus halo, restated (as on #read-letter): cream + deep red
   reads against the paper slip where the stock pair would vanish */
.slip-action:focus-visible {
  box-shadow:
    inset 0 2px 0 rgba(253, 243, 228, 0.25),
    0 0 0 3px var(--paper),
    0 0 0 6px var(--flag-deep),
    0 8px 16px -8px var(--flag-deep);
}

.slip-action:hover {
  transform: translateY(-1px) rotate(-0.5deg);
}

.slip-action:active {
  transform: translateY(1px);
}

/* ---------- the empty mailbox (404) ------------------------------------ */

.empty-page #scene {
  cursor: auto;
}

#empty-message {
  position: absolute;
  left: 50%;
  bottom: clamp(1.75rem, 7dvh, 4.5rem);
  transform: translateX(-50%);
  margin: 0;
  width: max-content;
  max-width: min(30ch, 86vw);
  font-style: italic;
  font-size: clamp(1rem, 3.8vw, 1.2rem);
  line-height: 1.5;
  text-align: center;
  color: var(--ink-soft);
}

/* ---------- the desk (sender pages: unlock, /new, sealed, /sent) --------
   The back of the mailbox: the same painted world, seen from the side where
   the letter gets written. Each page is a sheet of good paper set down in
   the landscape. No scene choreography and no theme toggle — but the head
   script stamps data-theme here too, so the desk keeps the scene's clock
   and saved choice (prefers-color-scheme stays the no-JS fallback). */

.desk {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.15rem;
  padding: clamp(1.25rem, 5dvh, 3rem) 1.25rem;
}

.sheet {
  position: relative;
  width: min(92vw, 26rem);
  margin: 0;
  padding: 1.7rem 1.5rem 1.8rem;
  display: flex;
  flex-direction: column;
  gap: 1.05rem;
  color: var(--paper-ink);
  background: var(--paper);
  border-radius: 14px 20px 12px 18px / 18px 12px 20px 14px;
  box-shadow:
    0 14px 26px -12px var(--shadow-warm),
    0 30px 52px -30px var(--shadow-warm);
  transform: rotate(-0.6deg);
  /* the sheet is set down on the desk; each variant settles into its own
     resting tilt (the keyframe has no `to`, so the end state is the
     element's specified transform) */
  animation: sheet-in 0.8s cubic-bezier(0.25, 0, 0.25, 1) backwards;
}

@keyframes sheet-in {
  from {
    opacity: 0;
    transform: translateY(1.4rem) rotate(0.4deg);
  }
}

.sheet-title {
  margin: 0;
  align-self: center;
  font-style: italic;
  font-weight: 600;
  font-size: clamp(1.35rem, 5vw, 1.6rem);
  line-height: 1.25;
  letter-spacing: 0.01em;
  text-align: center;
  /* the scene buttons' hand-drawn underline, carried to this side */
  padding: 0 0.6rem 0.55rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='8' viewBox='0 0 120 8'%3E%3Cpath d='M4 5 Q 24 2 45 4.4 Q 72 7 94 4 Q 106 2.6 116 4.2' fill='none' stroke='%23c8604c' stroke-width='2.4' stroke-linecap='round' opacity='0.85'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center bottom 0.1rem;
  background-size: min(88%, 150px) 8px;
}

.sheet-note,
.sheet-empty {
  margin: 0;
  font-style: italic;
  font-size: clamp(0.95rem, 3.6vw, 1.05rem);
  line-height: 1.45;
  text-align: center;
}

.sheet-empty {
  color: color-mix(in srgb, var(--paper-ink) 65%, transparent);
}

.sheet-error {
  margin: 0;
  font-style: italic;
  font-size: 0.95rem;
  line-height: 1.4;
  text-align: center;
  color: var(--flag-deep);
}

/* writing lines: the home slip's ruled input, generalised to the desk */
.sheet input[type='url'],
.sheet input[type='text'],
.sheet input[type='password'] {
  width: 100%;
  font: inherit;
  font-size: 1rem;
  color: var(--paper-ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--paper-edge);
  border-radius: 0;
  padding: 0.3rem 0.1rem 0.4rem;
}

.sheet input[type='url']:focus,
.sheet input[type='text']:focus,
.sheet input[type='password']:focus {
  outline: none;
  border-bottom-color: var(--flag);
}

.sheet input::placeholder {
  color: color-mix(in srgb, var(--paper-ink) 45%, transparent);
  font-style: italic;
  opacity: 1;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  text-align: left;
}

.field label {
  font-style: italic;
  font-size: 0.95rem;
  line-height: 1.35;
}

.field-hint {
  font-style: normal;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  opacity: 0.6;
}

.sheet input[type='file'] {
  width: 100%;
  max-width: 100%;
  font: inherit;
  font-size: 0.88rem;
  color: color-mix(in srgb, var(--paper-ink) 72%, transparent);
  cursor: pointer;
}

/* the chooser as a small paper chip */
.sheet input[type='file']::file-selector-button {
  appearance: none;
  border: 0;
  margin: 0 0.75rem 0 0;
  padding: 0.42rem 1rem 0.5rem;
  font: inherit;
  font-style: italic;
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--paper-ink);
  background: color-mix(in srgb, var(--paper) 55%, var(--paper-edge));
  border-radius: 999px;
  cursor: pointer;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--paper-edge) 80%, transparent);
}

.sheet input[type='file']:focus-visible {
  outline: none;
  border-radius: 10px;
  box-shadow:
    0 0 0 3px var(--paper),
    0 0 0 6px var(--halo);
}

/* the one way through each sheet sits on the centre line */
.sheet .slip-action {
  align-self: center;
}

/* the standalone passphrase slip: the home slip's shape, off the scene */
.sheet--slip {
  width: min(86vw, 20rem);
  padding: 1.35rem 1.25rem 1.45rem;
  gap: 0.55rem;
  align-items: center;
  text-align: center;
  transform: rotate(-1.6deg);
}

.sheet--slip label {
  font-style: italic;
  font-size: clamp(0.95rem, 3.6vw, 1.1rem);
  line-height: 1.45;
}

.sheet--slip input[type='password'] {
  width: min(100%, 15rem);
  text-align: center;
}

/* the compose sheet wears a stamp: the sender's emblem, as the broken wax
   seal is the recipient's — a painted heart on a perforated square */
.sheet--compose {
  padding-top: 2.1rem;
}

.sheet--compose::before {
  content: '';
  position: absolute;
  top: 0.85rem;
  right: 0.9rem;
  width: 44px;
  height: 52px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 44 52'%3E%3Cfilter id='sw' x='-15%25' y='-15%25' width='130%25' height='130%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.11 0.09' numOctaves='2' seed='7'/%3E%3CfeDisplacementMap in='SourceGraphic' scale='1.8'/%3E%3C/filter%3E%3Cg filter='url(%23sw)'%3E%3Crect x='2.5' y='2.5' width='39' height='47' rx='2' fill='%23fdf6e7' stroke='%23dcc9a6' stroke-width='1.6' stroke-dasharray='2.6 3.4' stroke-linecap='round'/%3E%3Crect x='7.5' y='7.5' width='29' height='37' fill='none' stroke='%23dcc9a6' stroke-width='1.1'/%3E%3Cg transform='translate(22 28)'%3E%3Cpath d='M0 7 C -8 -2 -4 -11 0 -5 C 4 -11 8 -2 0 7 Z' fill='%23c8604c' transform='rotate(-7) scale(1.7)'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
  transform: rotate(4deg);
  filter: drop-shadow(0 2px 3px var(--shadow-warm));
  pointer-events: none;
}

/* the sealed sheet opens with the same wax heart her letter card carries */
.sheet--sealed {
  align-items: center;
  text-align: center;
}

.sheet--sealed::before {
  content: '';
  width: 30px;
  height: 28px;
  flex: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-9 -12 18 20'%3E%3Cpath d='M0 7 C -8 -2 -4 -11 0 -5 C 4 -11 8 -2 0 7 Z' fill='%23c8604c' transform='rotate(-7)'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* the link is the whole product of the action: an address label, cut out
   and ready to hand over (kept selectable even when the clipboard fails) */
.sheet input.link-line {
  width: 100%;
  font: inherit;
  font-size: 0.9rem;
  text-align: center;
  color: var(--paper-ink);
  background: color-mix(in srgb, var(--paper) 55%, white);
  border: 1.5px dashed var(--paper-edge);
  border-radius: 7px 9px 6px 8px;
  padding: 0.6rem 0.65rem;
}

.sheet input.link-line:focus {
  outline: none;
  border-color: var(--flag);
}

/* the ledger: what has been posted, one ruled line per letter */
.sheet--ledger {
  width: min(94vw, 29rem);
}

.letters {
  list-style: none;
  margin: 0;
  padding: 0;
}

.letters li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.9rem;
  padding: 0.8rem 0.15rem;
}

.letters li:first-child {
  padding-top: 0.15rem;
}

.letters li + li {
  border-top: 1.5px solid color-mix(in srgb, var(--paper-edge) 65%, transparent);
}

.letters form {
  margin: 0;
  flex: none;
}

.letter-caption {
  margin: 0;
  font-style: italic;
  font-size: 1.02rem;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

.letter-detail {
  margin: 0.2rem 0 0;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--paper-ink) 62%, transparent);
  overflow-wrap: anywhere;
}

.letter-detail a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.letter-delete {
  appearance: none;
  border: 0;
  background: transparent;
  font: inherit;
  font-style: italic;
  font-size: 0.85rem;
  color: color-mix(in srgb, var(--paper-ink) 55%, transparent);
  padding: 0.35rem 0.5rem;
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.25s ease;
}

.letter-delete:hover,
.letter-delete:focus-visible {
  color: var(--flag-deep);
}

/* quiet ways off the sheet, written on the landscape itself */
.desk-nav {
  margin: 0;
  font-style: italic;
  font-size: 0.95rem;
  line-height: 1.5;
  text-align: center;
  color: var(--ink-soft);
}

.desk-nav a {
  color: var(--ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* ---------- reduced motion: no ceremony, the letter is simply there ----- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }

  /* whatever data-state says, present the open state immediately */
  body.letter-page #mailbox,
  body.letter-page #envelope {
    display: none !important;
  }

  body.letter-page #letter {
    display: flex !important;
    opacity: 1 !important;
    transform: none !important;
  }

  body.letter-page #scene {
    cursor: auto;
    overflow: visible;
  }

  /* the home slip is simply there — no flag ceremony to sit through
     (mailbox.js also raises the flag at once, so the scene agrees) */
  body.home-page #slip {
    opacity: 1 !important;
    transform: translateX(-50%) rotate(-1.6deg) !important;
    visibility: visible !important;
    pointer-events: auto !important;
  }

  body.home-page #scene {
    cursor: auto;
  }
}
