/* ════════════════════════════════════════════════════════════════
   yours.  ·  v12
   theme split:
     landing + drawer = cream paper (like an open book interior)
     letter view      = cream paper on DARK surround (variant #5 fidelity)
     reply view       = cream paper on deep dark (variant #4)
   ════════════════════════════════════════════════════════════════ */

:root {
  --paper-base:   #e5e2de;
  --paper-reply:  #d8dbe2;
  --ink-color:    #1a1b26;
  /* Contrast bump (2026-05-10): old --ink-soft 0.65 ≈ 4.4:1 (just under AA),
     old --ink-faint 0.40 ≈ 3.0:1 (clear AA fail).
     New: 0.72 ≈ 5.1:1 (AA pass) and 0.55 ≈ 3.8:1 (AA-large only — only used for
     incidental labels like the drawer kicker, never body copy). */
  --ink-soft:     rgba(26,27,38,0.72);
  --ink-faint:    rgba(26,27,38,0.55);
  --hairline:     rgba(26,27,38,0.15);
  --hairline-soft:rgba(26,27,38,0.08);
  --surround:     #121212;
  --surround-deep:#0d0d0d;
  --brand:        #6B1E1E;

  /* Cross-platform font stacks. Latin: Cormorant + Noto Serif SC (now actually
     loaded — see index.html google fonts link). Han: covers macOS/iOS/Win/Linux
     so we never tofu. */
  --stack-serif-i18n: 'Cormorant Garamond', 'Noto Serif SC', 'Noto Serif TC',
                      'PingFang SC', 'PingFang TC', 'Songti SC', 'Songti TC',
                      'Microsoft YaHei', 'Microsoft JhengHei', serif;
  --stack-cjk-songti: 'Songti SC', 'STSong', 'Songti TC', 'PMingLiU',
                      'Microsoft JhengHei', 'PingFang SC', 'PingFang TC',
                      'Noto Serif SC', 'Noto Serif TC', serif;
  --stack-cjk-chillkai: 'ChillKai', 'Songti SC', 'STSong', 'PingFang SC',
                        'Noto Serif SC', 'Microsoft YaHei', serif;
  --stack-cjk-kinghwa: 'KingHwaOldSong', 'Songti SC', 'STSong', 'PingFang SC',
                       'Noto Serif SC', 'Microsoft YaHei', serif;
}

/* Global keyboard focus ring. Was missing app-wide (audit 2026-05-10).
   Uses brand color so it reads "intentional", not browser-default blue. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
  border-radius: 2px;
}
/* Brand-mark and other "no background" elements: small underline cue
   instead of a haloing outline. (Old text-shadow had invalid syntax —
   `2px spread` is a box-shadow concept, browser dropped that token.) */
.brand-mark:focus-visible,
.tagline-enter:focus-visible,
.send-btn-link:focus-visible {
  outline: none;
  border-bottom: 1px solid var(--brand);
  color: var(--brand);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { min-height: 100%; }
html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

body {
  background-color: var(--surround);          /* default = letter view dark */
  color: var(--ink-color);
  min-height: 100vh;
  overflow-x: hidden;
  font-family: var(--stack-serif-i18n);
  transition: background-color 0.6s ease;
}

/* ── cursor: feather quill, single style across the app ── */
body {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'><g fill='%231a1b26' stroke='%231a1b26' stroke-width='0.5' stroke-linejoin='round'><path d='M3 25 L18 10 L24 4 L22 12 L13 21 Z' opacity='0.85'/><line x1='3' y1='25' x2='13' y2='15' stroke='%23000' stroke-width='1.2'/></g></svg>") 3 25, auto;
}

/* clickable elements get the system finger pointer */
a, button,
.brand-mark, .back-tiny, .nav-content button, .nav-content a,
.drawer-item, .scroll-hint,
.settings-row .opt button, .settings-close, .settings-wide-button,
.reply-head .back, .seal-send {
  cursor: pointer !important;
}

/* (2026-05-10) Removed legacy body.cursor-default rule — applyCursor() in
   yours.js never adds the class; only removes it. Dead code. */

body:not(.is-authenticated) .nav-trigger,
body:not(.is-authenticated) .nav-content,
body:not(.is-authenticated) .brand-mark {
  display: none;
}

/* CREAM theme — landing + drawer */
body.theme-cream {
  background-color: var(--paper-base);
  background-image: url('https://www.transparenttextures.com/patterns/paper-fibers.png');
}

::selection { background: var(--ink-color); color: var(--paper-base); }

a, button {
  font: inherit; color: inherit; background: none; border: none;
  cursor: pointer; text-decoration: none;
}


/* ════════════════════════════════════════════════════════════════
   GLOBAL CHROME
   ════════════════════════════════════════════════════════════════ */

/* grain overlay — only on dark theme; cream theme has paper-fibers */
.grain-overlay {
  position: fixed; inset: 0;
  background-image: url('https://www.transparenttextures.com/patterns/60-lines.png');
  opacity: 0.05;
  pointer-events: none;
  z-index: 999;
  transition: opacity 0.6s ease;
}
body.theme-cream .grain-overlay { opacity: 0.025; }

/* hover-trigger nav strip */
.nav-trigger { position: fixed; top: 0; left: 0; width: 100%; height: 80px; z-index: 100; }
.nav-content {
  position: fixed;
  top: -64px; left: 0; width: 100%;
  padding: 18px 36px;
  display: flex; justify-content: space-between; align-items: center;
  transition: top 0.4s ease, color 0.6s ease, background 0.6s ease;
  /* Contrast bump (2026-05-10): old 0.45 ≈ 3.7:1 was below AA on dark
     surround, especially with grain overlay — "drawer / settings" was
     near-invisible until hover. Bumped to 0.7 ≈ 4.9:1 (AA pass). */
  color: rgba(255,255,255,0.7);
  font-family: 'IM Fell English', serif;
  font-style: italic; font-size: 13px;
  letter-spacing: 0.04em;
  z-index: 101;
  background: linear-gradient(180deg, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 100%);
}
body.theme-cream .nav-content {
  color: var(--ink-soft);
  background: linear-gradient(180deg, rgba(229,226,222,0.6) 0%, rgba(229,226,222,0) 100%);
}
.nav-trigger:hover ~ .nav-content,
.nav-content:hover { top: 0; }
.nav-content .nav-left, .nav-content .nav-right { display: flex; gap: 24px; align-items: center; }
.nav-content button, .nav-content a {
  color: inherit; opacity: 0.8;
  transition: opacity 0.3s ease;
  letter-spacing: 0.04em;
  /* Hit-area bump (audit 2026-05-10): was effectively text-bbox (~50×14px),
     now ≥44px tall to meet WCAG 2.5.5 minimum target size. */
  padding: 14px 12px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}
.nav-content button:hover, .nav-content a:hover { opacity: 1; }

/* brand mark — adapts to theme */
.brand-mark {
  position: fixed;
  bottom: 22px; right: 26px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: 'IM Fell English', serif;
  font-style: italic; font-size: 13px;
  /* Contrast bump (2026-05-10): old 0.40 ≈ 3.7:1, new 0.55 ≈ 4.6:1 (AA). */
  color: rgba(255,255,255,0.55);
  z-index: 100;
  letter-spacing: -0.005em;
  user-select: none;
  transition: color 0.6s ease;
  /* Hit-area: padding gives ≥44px target without changing visual position. */
  padding: 10px 8px;
}
.brand-mark:hover { color: rgba(255,255,255,0.95); }
body.theme-cream .brand-mark { color: var(--ink-soft); }
body.theme-cream .brand-mark:hover { color: var(--ink-color); }
.brand-mark-icon {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  object-fit: cover;
  flex: 0 0 auto;
}
.brand-mark .dot { color: var(--brand); font-weight: 500; }


/* ════════════════════════════════════════════════════════════════
   AUTH GATE
   ════════════════════════════════════════════════════════════════ */
.auth-gate {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 32px;
  color: var(--ink-color);
}
.auth-panel {
  width: min(420px, 100%);
  text-align: left;
}
.auth-kicker {
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 22px;
  letter-spacing: 0.04em;
  margin-bottom: 34px;
}
.auth-panel h1 {
  font-family: 'Cormorant Garamond', serif;
  font-size: 34px;
  font-weight: 400;
  font-style: italic;
  line-height: 1.1;
  margin-bottom: 14px;
}
.auth-copy {
  font-size: 17px;
  color: var(--ink-soft);
  font-style: italic;
  line-height: 1.6;
  margin-bottom: 28px;
}
.auth-input {
  margin-bottom: 12px;
}
.auth-submit {
  background: rgba(255,255,255,0.25);
}
/* (2026-05-12) Google sign-in button — sits above the magic-link
   email form. We keep it understated to match yours' quiet voice;
   the Google glyph is a small mono "G" in the brand orange rather
   than the full multicolor logo (the official mark would visually
   shout and break the ritual). Same wide-button shape as the
   email submit so the two read as siblings.

   Lives in TWO contexts:
   1. .auth-gate — dark backdrop; needs light-tinted fill
   2. .settings-panel (showAccountPanel modal) — cream backdrop;
      inherits .settings-wide-button's transparent + border styling
   Default (no parent scope) = cream-friendly; .auth-gate overrides. */
.auth-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-bottom: 4px;
}
.auth-gate .auth-google {
  background: rgba(255, 255, 255, 0.12);
}
.auth-gate .auth-google:hover {
  background: rgba(255, 255, 255, 0.22);
}
.auth-google .google-glyph {
  display: inline-block;
  width: 18px;
  height: 18px;
  line-height: 18px;
  text-align: center;
  border-radius: 50%;
  font-family: 'Cormorant Garamond', 'EB Garamond', serif;
  font-style: italic;
  font-size: 13px;
  font-weight: 500;
  background: var(--brand, #B7613D);
  color: var(--paper-base, #ece8e2);
}
/* "or" divider between Google and email — a thin hairline with the
   word floating on it. Same visual idiom as a letterhead rule. */
.auth-or {
  display: flex;
  align-items: center;
  text-align: center;
  margin: 14px 0;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 12px;
  color: var(--ink-faint);
  letter-spacing: 0.12em;
}
.auth-or::before,
.auth-or::after {
  content: "";
  flex: 1;
  height: 0;
  border-top: 0.5px solid var(--hairline);
}
.auth-or span {
  padding: 0 12px;
  text-transform: lowercase;
}
.auth-status {
  min-height: 22px;
  margin-top: 12px;
}
/* Auth-gate language switcher (2026-05-10) — fixed top-right, tiny.
   Lets unauthenticated users pick their lang BEFORE seeing the gate
   in the wrong language. */
.auth-lang {
  position: fixed;
  top: 16px; right: 16px;
  display: flex; gap: 4px;
  z-index: 110;
}
.auth-lang button {
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 12px;
  padding: 8px 10px;
  min-width: 36px;
  border: 0.5px solid var(--hairline);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: all 0.3s ease;
  letter-spacing: 0.04em;
}
.auth-lang button.active {
  background: var(--ink-color);
  color: var(--paper-base);
  border-color: var(--ink-color);
}
.auth-lang button:hover { color: var(--ink-color); }
/* Auth-gate explain + privacy paragraphs */
.auth-explain {
  font-size: 13px;
  color: var(--ink-soft);
  font-style: italic;
  line-height: 1.6;
  margin-bottom: 18px;
  letter-spacing: 0.005em;
}
.auth-privacy {
  font-size: 11px;
  color: var(--ink-faint);
  font-style: italic;
  line-height: 1.6;
  margin-top: 28px;
  letter-spacing: 0.04em;
}


/* ════════════════════════════════════════════════════════════════
   VIEW SWITCHING
   ════════════════════════════════════════════════════════════════ */
.view { animation: view-fade 0.6s ease both; }
@keyframes view-fade { from { opacity: 0; } to { opacity: 1; } }


/* ════════════════════════════════════════════════════════════════
   LANDING  ·  cream theme, like an open book flyleaf
   ════════════════════════════════════════════════════════════════ */
/* (2026-06-16) Landing redesign — "入口文案-B-翻角" (Claude Design handoff).
   Two stacked scenes ported from the design prototype: a cream FLYLEAF on top
   (signature parade `yours, NAME.` → brush-sweep → `yours.`, tagline, dog-ear)
   over the app's restrained dark "{author} 正在给你写信" scene. Turning the
   dog-ear rotates the flyleaf away from its bottom-right corner, revealing the
   dark caption IN PLACE — no black gap, no separate navigation flash. */
.landing {
  position: relative;
  min-height: 100vh;
  overflow: hidden;
  color: var(--ink-color);
}

/* dark writing scene — sits underneath, revealed as the flyleaf turns away */
.landing .landing-dark {
  position: absolute; inset: 0; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  padding: 60px 24px;
  background: var(--surround);
}
.landing .landing-cap { opacity: 0; transition: opacity 0.7s ease 0.3s; }
.landing[data-scene="writing"] .landing-cap { opacity: 1; }

/* cream flyleaf — the page you turn */
.landing .flyleaf {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 80px 32px;
  text-align: center;
  background-color: var(--paper-base);
  background-image: url('https://www.transparenttextures.com/patterns/paper-fibers.png');
  transform-origin: 100% 100%;
  transition: transform 0.85s cubic-bezier(.4, 0, .2, 1), opacity 0.85s ease 0.05s;
}
.landing[data-scene="writing"] .flyleaf {
  transform: rotate(6deg) translate(12%, 9%);
  opacity: 0;
  pointer-events: none;
}

/* On the landing, hide the persistent bottom-right brand mark (it collides with
   the dog-ear and duplicates the hero wordmark). Toggled via body.on-landing. */
body.on-landing .brand-mark { display: none; }

/* Optical centering: lift the group ~6vh above geometric center so it reads
   anchored, not adrift in a tall viewport. (dog-ear stays pinned to the corner.) */
.landing .opener {
  position: relative; display: flex; flex-direction: column; align-items: center;
  transform: translateY(-6vh);
}

/* ── signature parade ── */
.landing .hero {
  font-family: var(--stack-serif-i18n);
  font-style: italic; font-weight: 500;
  color: var(--ink-color);
  font-size: clamp(38px, 5vw, 56px);
  line-height: 1; letter-spacing: -0.012em;
  display: inline-flex; align-items: baseline; white-space: nowrap;
}
.landing .hero .tail {
  display: inline-block; overflow: hidden; white-space: nowrap;
  vertical-align: baseline; flex: 0 0 auto; opacity: 1; padding-right: 0.08em;
  transition: width 0.85s cubic-bezier(.2,.7,.2,1), opacity 0.6s ease;
}
.landing .hero .cm { color: var(--ink-color); margin-left: 0.02em; }
.landing .hero .nmwrap { display: inline-block; margin-left: 0.5ch; }
.landing .hero .nm {
  display: inline-block; color: var(--brand); white-space: nowrap;
  transition: opacity 0.5s ease;
}
/* CJK author names: upright, a touch smaller, lightly slanted to echo the
   surrounding italic Latin without faux-italicizing the Han glyphs. */
.landing .hero .nm .han {
  font-family: var(--stack-cjk-songti);
  font-style: normal; font-weight: 400; font-size: 0.92em;
  display: inline-block; transform: skewX(-6deg);
}
.landing .hero .dot {
  color: var(--brand); display: inline-block; flex: 0 0 auto;
  position: relative; top: 0;
  transition: margin-left 0.6s cubic-bezier(.2,.7,.2,1), top 0.6s cubic-bezier(.2,.7,.2,1);
}
.landing.collapsed .hero .dot { margin-left: 0.10em; top: -0.02em; }
@media (prefers-reduced-motion: no-preference) {
  .landing.collapsed .hero .dot { animation: dotSettle 0.6s ease 1.2s both; }
}
@keyframes dotSettle { 0% { transform: scale(1); } 42% { transform: scale(1.16); } 100% { transform: scale(1); } }

/* tagline — revealed after the logo forms */
.landing .after {
  display: flex; flex-direction: column; align-items: center;
  margin-top: clamp(22px, 3vh, 30px); min-height: 84px;
}
.landing .appear {
  opacity: 0; transform: translateY(9px);
  transition: opacity 0.95s ease, transform 0.95s cubic-bezier(.2,.7,.2,1);
}
.landing.revealed .appear { opacity: 1; transform: none; }
.landing .tagline {
  font-family: var(--stack-serif-i18n);
  font-style: italic; font-weight: 400;
  font-size: clamp(13px, 1.05vw, 15px); line-height: 1.72;
  color: var(--ink-soft); letter-spacing: 0.01em; text-align: center;
}

/* ── dog-ear corner: turn the flyleaf to open today's letter ── */
.landing .dogear {
  -webkit-appearance: none; appearance: none;
  position: absolute; right: 0; bottom: 0; width: 104px; height: 104px;
  z-index: 6; background: transparent; border: none; padding: 0;
  opacity: 0; pointer-events: none; transition: opacity 0.9s ease;
  cursor: inherit !important;          /* keep the quill, not the finger pointer */
}
.landing.revealed .dogear { opacity: 1; pointer-events: auto; }
.landing .dogear::before {
  content: ''; position: absolute; right: 0; bottom: 0;
  border-style: solid; border-width: 0 0 60px 60px;
  border-color: transparent transparent rgba(26,27,38,0.06) transparent;
  transition: border-width 0.5s cubic-bezier(.2,.7,.25,1);
}
.landing .dogear::after {
  content: ''; position: absolute; right: 0; bottom: 0; width: 60px; height: 60px;
  background: linear-gradient(135deg, #d7d3cc 0%, #e2dfd9 60%);
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  box-shadow: -3px -3px 10px rgba(0,0,0,0.13);
  transition: width 0.5s cubic-bezier(.2,.7,.25,1), height 0.5s cubic-bezier(.2,.7,.25,1), box-shadow 0.5s ease;
}
@media (prefers-reduced-motion: no-preference) {
  .landing.revealed .dogear::after { animation: dogidle 4.2s ease-in-out infinite; }
}
@keyframes dogidle { 0%, 80%, 100% { width: 60px; height: 60px; } 89% { width: 76px; height: 76px; } }
.landing .dogear:hover::before { border-width: 0 0 88px 88px; }
.landing .dogear:hover::after { animation: none; width: 88px; height: 88px; box-shadow: -5px -5px 16px rgba(0,0,0,0.2); }


/* ════════════════════════════════════════════════════════════════
   DRAWER  ·  cream theme, editorial TOC
   ════════════════════════════════════════════════════════════════ */
.drawer {
  min-height: 100vh;
  padding: 120px 32px 140px;
  display: flex; flex-direction: column; align-items: center;
  color: var(--ink-color);
}
.drawer-head {
  width: 100%; max-width: 720px;
  margin-bottom: 64px;
  font-family: 'IM Fell English', serif;
}
.drawer-head .label {
  font-style: italic;
  font-size: 12px; letter-spacing: 0.30em; text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 12px;
}
.drawer-head .h {
  font-size: 30px; font-style: italic;
  color: var(--ink-color);
  letter-spacing: -0.005em;
}

.drawer-list {
  width: 100%; max-width: 720px;
  list-style: none;
  border-top: 0.5px solid var(--hairline);
}
.drawer-item {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  column-gap: 24px;
  align-items: baseline;
  padding: 28px 8px 26px;
  border-bottom: 0.5px solid var(--hairline);
  cursor: pointer;
  transition: background 0.4s ease, padding-left 0.4s ease;
  color: var(--ink-color);
}
.drawer-item:hover {
  background: rgba(26,27,38,0.025);
  padding-left: 16px;
}
.drawer-item .from {
  grid-column: 1; grid-row: 1;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 22px;
  color: var(--ink-color);
  letter-spacing: -0.005em;
  line-height: 1.2;
}
.drawer-item .from-sub {
  grid-column: 1; grid-row: 2;
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-size: 14px;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
  margin-top: 4px;
}
.drawer-item .date {
  grid-column: 2; grid-row: 1;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 14px;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
  align-self: baseline;
}

/* read state — entire item fades, italic name loses weight */
.drawer-item.read .from { color: var(--ink-faint); }
.drawer-item.read .from-sub { color: var(--ink-faint); opacity: 0.7; }
.drawer-item.read .date { color: var(--ink-faint); opacity: 0.7; }


/* ════════════════════════════════════════════════════════════════
   LETTER VIEW  ·  variant #5 EXACT  (kept on DARK surround)
   ════════════════════════════════════════════════════════════════ */
.paper-stack {
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 80px 20px 120px;
  perspective: 2000px;
}

.letter-sheet {
  background-color: var(--paper-base);
  width: 100%;
  max-width: 850px;
  /* (2026-05-10 second pass) Live observation: even with clamp(560, 70vh, 1100),
     a typical Woolf sample sheet had ~300px empty bottom. Removed min-height
     entirely — `padding: 120px 100px` already enforces visual minimum, and
     `box-shadow + transform` give the "letter sheet" affordance. */
  padding: 120px 100px;
  box-shadow:
    0 30px 60px -12px rgba(0,0,0,0.5),
    0 18px 36px -18px rgba(0,0,0,0.5);
  position: relative;
  background-image:
    radial-gradient(circle at 50% 50%, rgba(255,255,255,0.1) 0%, rgba(0,0,0,0.05) 100%),
    url('https://www.transparenttextures.com/patterns/paper-fibers.png');
  transform: rotateX(2deg);
  transition: transform 0.8s cubic-bezier(0.23, 1, 0.32, 1);
  /* Long English URLs / long Han titles must wrap, not blow out the sheet. */
  overflow-wrap: anywhere;
}

.watermark {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  opacity: 0.03;
  pointer-events: none; user-select: none;
  /* (2026-05-10) Was width:300; height:300 fixed — long signatures like
     `京華老宋紀念出版社` (12 Han chars × 4px letter-spacing) overflowed the
     circle. Now elastic via clamp + aspect-ratio. */
  width: clamp(220px, 38%, 360px);
  aspect-ratio: 1 / 1;
  border: 2px solid var(--ink-color);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-family: 'IM Fell English', serif;
  font-size: clamp(14px, 2.4vw, 22px);
  letter-spacing: 0.18em;        /* relative — was 4px hardcoded, clobbered short text */
  text-transform: uppercase;
  text-align: center;
  padding: 24px; line-height: 1.3;
  word-break: break-word;
}

/* Self-hosted ChillKai (寒蝉正楷体) — full CJK glyphs.
   (2026-05-10) Now serves woff2 first (compressed ~1/3 of TTF). Files were
   already in the fonts/ChillKai/ directory; old @font-face just didn't use them. */
@font-face {
  font-family: 'ChillKai';
  src: url('fonts/ChillKai/ChillKai.woff2') format('woff2');
  font-weight: normal; font-style: normal; font-display: swap;
}

/* (2026-05-10) Chiron Sung HK @font-face removed. The user-facing zh-Hant pick
   was deactivated in yours.js FONT_OPTIONS (commented "撤回 2026-05-10"); the
   CSS rule below was dead code that still pulled the ~6 MB TTF. Both gone. */

/* ════════════════════════════════════════════════════════════════
   INLINE REPLY PAD — sits below the letter on the same page
   ════════════════════════════════════════════════════════════════ */
/* (2026-05-10) Inline scroll-hint for the letter→reply pad transition.
   This is the ONE definition of .scroll-hint; the legacy `position: fixed`
   variant from the deprecated .reply-stage view was deleted (it doubly-matched
   this same element and pinned it to the viewport, colliding with .brand-mark). */
.scroll-hint {
  margin: 56px 0 36px;
  font-family: var(--stack-serif-i18n);
  font-style: italic;
  font-size: 15px;
  color: rgba(229,226,222,0.7);     /* bumped from 0.55 for AA on dark */
  letter-spacing: 0.03em;
  text-align: center;
  cursor: pointer;
  padding: 12px 16px;                /* hit-area */
  transition: color 0.3s ease, transform 0.3s ease;
  animation: hint-breathe 3s ease-in-out infinite;
}
.scroll-hint:hover { color: rgba(229,226,222,1); transform: translateY(2px); }
@keyframes hint-breathe { 0%,100% { opacity: 0.7; } 50% { opacity: 1; } }

/* REPLY SHEET — same construction as .letter-sheet (sister letter on the same desk).
   inherits all .letter-sheet styling, only overrides tilt direction + minor padding.
   #replyPad selector also matches .letter-sheet rules above. */
.reply-sheet {
  margin-top: 12px;                /* breath between received letter and reply */
  margin-bottom: 60px;
  transform: rotateX(2deg) rotate(-0.6deg);  /* slight counter-rotation, same X tilt */
}
.reply-letterhead {
  margin-bottom: 60px;
  font-size: 14px;
  opacity: 0.65;
}
.reply-content .reply-text {
  width: 100%;
  background: transparent;
  border: none; outline: none; resize: none;
  color: var(--ink-color);
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  font-style: inherit;
  padding: 0;
  margin-top: 4px;
  display: block;
}
.reply-content .reply-text::placeholder {
  /* Was 0.32 ≈ 2.4:1 — clear AA fail for placeholder copy that conveys
     instructions ("Write slowly..."). Bumped to AA-passing tone. */
  color: rgba(26,27,38,0.55);
  font-style: italic;
}
.reply-signoff {
  margin-top: 48px;
  text-align: right;
}
/* reply pad — Level A polish (2026-05-10).
   was: brand-color, IM Fell italic, prominent button-styled.
   now: muted ink default, brand only on hover, smaller, quieter.
   audit (2026-05-10): bumped contrast 0.5→0.72 (AA) and padding for ≥44px hit. */
.send-btn-link {
  background: none; border: none;
  padding: 12px 6px;                 /* was 2px 0 — far below 44px touch target */
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 15px;
  letter-spacing: 0.04em;
  color: rgba(26, 27, 38, 0.72);     /* AA pass on cream */
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color 0.3s ease, border-bottom-color 0.3s ease;
}
.send-btn-link:hover,
.send-btn-link:focus-visible {
  color: var(--brand);
  border-bottom-color: var(--brand);
}

.reply-prior {
  position: absolute;
  top: 28px; right: 32px;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 11px;
  /* Was 0.35 ≈ 2.7:1 (fail). 0.55 brings it into AA-large-text territory,
     fine for a metadata label this small. */
  color: rgba(26,27,38,0.55);
  letter-spacing: 0.06em;
}

/* WRITING MODE — received letter recedes when reply textarea has focus */
body.writing-mode .letter-sheet:not(.reply-sheet) {
  transform: rotateX(2deg) scale(0.92);
  opacity: 0.5;
  filter: blur(0.4px);
  pointer-events: none;
}
body.writing-mode .reply-sheet {
  transform: rotateX(2deg) rotate(0);    /* straightens up */
  box-shadow:
    0 28px 60px -10px rgba(0,0,0,0.55),
    0 14px 30px -8px rgba(0,0,0,0.4);
}

/* shake on empty send */
.reply-sheet.shake { animation: pad-shake 0.4s; }
@keyframes pad-shake {
  0%,100% { transform: rotateX(2deg) rotate(-0.6deg) translateX(0); }
  25%     { transform: rotateX(2deg) rotate(-0.6deg) translateX(-6px); }
  50%     { transform: rotateX(2deg) rotate(-0.6deg) translateX(6px); }
  75%     { transform: rotateX(2deg) rotate(-0.6deg) translateX(-3px); }
}

/* SEALING — silent fold + drift away. no wax stamp (cliché). */
.reply-sheet.sealing {
  animation: pad-fold 1.0s cubic-bezier(0.5, 0, 0.4, 1) forwards;
  transform-origin: center center;
}
.reply-sheet.flying {
  animation: pad-fly 1.1s 0.1s cubic-bezier(0.5, 0, 0.7, 0) forwards;
}
@keyframes pad-fold {
  0%   { transform: rotateX(2deg) rotate(-0.6deg) scaleY(1); }
  100% { transform: rotateX(2deg) rotate(-0.6deg) scaleY(0.55); }
}
@keyframes pad-fly {
  to {
    transform: translate(40vw, -60vh) rotate(-6deg) scaleY(0.45) scale(0.85);
    opacity: 0;
  }
}

/* SEALED toast — small subtle confirmation */
.sealed-toast {
  position: fixed;
  bottom: 36px; left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: rgba(20,20,22,0.85);
  color: rgba(229,226,222,0.9);
  font-family: var(--stack-serif-i18n);
  font-style: italic;
  font-size: 14px;
  letter-spacing: 0.02em;
  padding: 12px 22px;
  border: 0.5px solid rgba(229,226,222,0.15);
  border-radius: 2px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  transition: opacity 0.5s ease, transform 0.5s ease;
  z-index: 300;
  pointer-events: none;
}
.sealed-toast.visible { opacity: 1; transform: translateX(-50%) translateY(0); }


/* ════════════════════════════════════════════════════════════════
   LOADING STATE — text only, italic, breathing dots
   ════════════════════════════════════════════════════════════════ */
.envelope-stage {
  min-height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 60px 24px;
}
.envelope-wrap {
  animation: env-fade-in 0.8s ease both;
}
.loading-caption {
  font-family: var(--stack-serif-i18n);
  font-style: italic;
  font-size: 18px;
  line-height: 1.8;
  letter-spacing: 0.04em;
  color: rgba(255,255,255,0.55);
  text-align: center;
}
.loading-caption span { animation: dot-pulse 1.6s ease-in-out infinite; opacity: 0.3; }
.loading-caption .dot-2 { animation-delay: 0.3s; }
.loading-caption .dot-3 { animation-delay: 0.6s; }
@keyframes dot-pulse {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 1; }
}
@keyframes env-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.envelope-wrap.opening {
  animation: env-out 0.9s ease-out forwards;
}
@keyframes env-out {
  to { opacity: 0; transform: translateY(-8px); }
}
.no-letter-stage {
  color: rgba(255,255,255,0.74);
}
.no-letter-wrap {
  width: min(520px, calc(100vw - 48px));
  text-align: center;
}
.no-letter-mark {
  font-family: var(--stack-serif-i18n);
  font-size: 15px;
  letter-spacing: 0;
  color: rgba(255,255,255,0.34);
  margin-bottom: 22px;
}
.no-letter-mark span {
  color: rgba(255,255,255,0.52);
}
.no-letter-title {
  font-family: var(--stack-serif-i18n);
  font-size: 25px;
  line-height: 1.45;
  font-weight: 500;
  letter-spacing: 0;
  color: rgba(255,255,255,0.78);
}
.no-letter-sub {
  margin-top: 16px;
  font-family: var(--stack-serif-i18n);
  font-size: 17px;
  line-height: 1.8;
  font-style: italic;
  letter-spacing: 0;
  color: rgba(255,255,255,0.52);
}
.no-letter-drawer {
  margin-top: 32px;
  padding: 6px 2px 5px;
  border-bottom: 1px solid rgba(255,255,255,0.28);
  font-family: var(--stack-serif-i18n);
  font-size: 15px;
  line-height: 1.5;
  letter-spacing: 0;
  color: rgba(255,255,255,0.66);
  transition: color 0.2s ease, border-color 0.2s ease;
}
.no-letter-drawer:hover,
.no-letter-drawer:focus-visible {
  color: rgba(255,255,255,0.9);
  border-color: rgba(255,255,255,0.72);
}

/* base — set color/line-height; font-family is decided below by lang × font choice.
   (2026-05-10) Default fallback now spans Latin AND CJK so the early-paint
   window (before body[data-lang] is set by JS) doesn't render Han chars in
   Homemade Apple → tofu. */
.handwritten-all {
  color: var(--ink-color);
  line-height: 2.2;
  font-family: 'Homemade Apple', var(--stack-cjk-chillkai), cursive, serif;
}

/* ───────── 直排模式 — only enabled by JS when author.nativeLang === "zh-Hant"
   AND current letter language is 繁體. JS sets data-vertical="true" on .letter-sheet. */
.letter-sheet[data-vertical="true"] {
  writing-mode: vertical-rl;
  text-orientation: upright;
  /* aspect changes — paper becomes vertical */
}
.letter-sheet[data-vertical="true"] .letterhead {
  text-align: start;
  margin-bottom: 0;
  margin-left: 32px;       /* in vertical mode this becomes column-spacing */
  font-size: 14px;
}
.letter-sheet[data-vertical="true"] .letter-content p { margin-left: 18px; margin-bottom: 0; }
.letter-sheet[data-vertical="true"] .signature-block {
  margin-top: 0;
  margin-left: 32px;
  padding-top: 60px;
}
.letter-sheet[data-vertical="true"] em { font-style: italic; }
/* (2026-05-10) Embedded Latin in vertical-rl was rendering each letter on its
   own row (text-orientation: upright on Latin). text-combine-upright: all packs
   short Latin runs (≤4 chars) into a single horizontal glyph cluster (縦中横/
   tate-chū-yoko), the conventional fix for English in CJK vertical text. */
.letter-sheet[data-vertical="true"] em,
.letter-sheet[data-vertical="true"] .latin {
  text-combine-upright: all;
}

/* ───────── Original (English) — 4 font options via body[data-en-font] ───────── */
body[data-lang="en"] .handwritten-all {
  font-family: 'Homemade Apple', cursive;     /* default */
  line-height: 2.1;
}
body[data-en-font="homemade"][data-lang="en"] .handwritten-all {
  font-family: 'Homemade Apple', cursive;
  line-height: 2.1;
}
body[data-en-font="special-elite"][data-lang="en"] .handwritten-all {
  /* mid-century typewriter — Smith-Corona / Olivetti / Hermes feel.
     Hemingway, Plath, Faulkner all typed. fits "received letter as carbon-paper print". */
  font-family: 'Special Elite', 'Courier Prime', 'Courier New', monospace;
  font-style: normal;
  line-height: 1.85;
  font-size: 19px;        /* monospaced renders slightly heavy — pull back */
  letter-spacing: 0.005em;
}
body[data-en-font="fell"][data-lang="en"] .handwritten-all {
  /* vintage book — IM Fell English italic */
  font-family: 'IM Fell English', 'Cormorant Garamond', Georgia, serif;
  font-style: italic;
  line-height: 1.7;
  font-size: 21px;
}
body[data-en-font="caveat"][data-lang="en"] .handwritten-all {
  /* modern handwriting — Caveat (less formal than Homemade Apple) */
  font-family: 'Caveat', 'Homemade Apple', cursive;
  line-height: 1.6;
  font-size: 26px;        /* Caveat is small at body size */
}

/* ───────── Chinese modes — three font options, picked via body[data-cn-font] ─────────
   All stacks now use the cross-platform CJK fallback variables defined in :root.
   Old stacks ended in `serif` after macOS-only `Songti TC`/`PingFang TC`, so
   Windows/Linux繁體 users were dropping straight to system serif → SimSun tofu. */

/* default (no setting yet) → ChillKai */
body[data-lang="zh-Hans"] .handwritten-all,
body[data-lang="zh-Hant"] .handwritten-all {
  font-family: var(--stack-cjk-chillkai);
  line-height: 2.0;
}

/* explicit picks */
body[data-cn-font="chillkai"][data-lang="zh-Hans"] .handwritten-all,
body[data-cn-font="chillkai"][data-lang="zh-Hant"] .handwritten-all {
  font-family: var(--stack-cjk-chillkai);
  line-height: 2.0;
}
body[data-cn-font="kinghwa"][data-lang="zh-Hans"] .handwritten-all,
body[data-cn-font="kinghwa"][data-lang="zh-Hant"] .handwritten-all {
  font-family: var(--stack-cjk-kinghwa);
  line-height: 2.0;
}
body[data-cn-font="songti"][data-lang="zh-Hans"] .handwritten-all,
body[data-cn-font="songti"][data-lang="zh-Hant"] .handwritten-all {
  font-family: var(--stack-cjk-songti);
  line-height: 2.0;
}
/* (2026-05-10) Chiron Sung HK rule removed — the user-facing pick was withdrawn
   in yours.js FONT_OPTIONS and the @font-face was deleted earlier in this file.
   Keeping the rule risked an orphan request if a stale localStorage value
   somehow set data-cn-font="chiron". */

.letterhead {
  text-align: right;
  margin-bottom: 80px;
  font-size: 16px;
  opacity: 0.85;
}

.letter-content {
  font-size: 18px;
  position: relative;
  opacity: 0.95;
}
.letter-content p {
  margin-bottom: 22px;
}
.letter-content p:last-of-type { margin-bottom: 0; }

.salutation {
  font-size: 22px;
  margin-bottom: 30px;
  display: block;
  opacity: 1;
}

/* inline marginalia — italic, integrated into prose. no UI bars.
   (2026-05-11) <em> in letter content is now ALSO the LLM's signal that
   this phrase was borrowed from the reader's marks (per v5 prompt's
   "Marking borrowed lines" directive). Render it with the same warm
   paper underwash as .quoted-mark so the two paths produce a consistent
   visual. The italic stays — period-correct for marked-up phrases. */
.letter-content em {
  font-style: italic;
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 30%,
    rgba(183, 97, 61, 0.16) 30%,
    rgba(183, 97, 61, 0.16) 92%,
    transparent 92%
  );
  border-radius: 1px;
  padding: 0 1px;
}
.letter-sheet[data-vertical="true"] .letter-content em {
  background: linear-gradient(
    270deg,
    transparent 0%,
    transparent 30%,
    rgba(183, 97, 61, 0.16) 30%,
    rgba(183, 97, 61, 0.16) 92%,
    transparent 92%
  );
}
/* When the highlight setting is off, suppress the warm wash on BOTH paths
   so the letter is clean prose with just italic for <em>. Toggling the
   setting flips this via body.no-highlight added in showSettings. */
body.no-highlight .letter-content em,
body.no-highlight .quoted-mark {
  background: none;
}

.signature-block {
  margin-top: 60px;
  opacity: 0.9;
}

.signature-name {
  font-size: 26px;
  display: block;
  margin-top: 10px;
  transform: rotate(-1deg);
}
/* CJK signatures look thinner at 26px next to English equivalents — bump
   slightly and reduce the rotation so 4-char Han names sit on a more even
   baseline when compared to "Yours, V. Woolf". */
body[data-lang^="zh"] .signature-name {
  font-size: 30px;
  transform: rotate(-0.5deg);
}
/* In vertical-rl mode, rotate-1deg becomes a sloped column — undo it. */
.letter-sheet[data-vertical="true"] .signature-name {
  transform: none;
}

/* (2026-05-10) Removed the second `.scroll-hint { position: fixed; … }` block.
   It double-defined the inline scroll-hint — `position: fixed` won and pinned
   the hint to the viewport, colliding with .brand-mark at the bottom-right.
   The inline definition above (margin-based) is the only one now.
   `@keyframes pulse` was only used by this rule, so it's gone too. */


/* ════════════════════════════════════════════════════════════════
   DRAWER → LETTER TRANSITION
   no fake envelope.
   on click: drawer .leaving fades + body bg cream→dark gradient.
   then letter view fades in via standard view-fade.
   ════════════════════════════════════════════════════════════════ */
.view.leaving {
  animation: view-leave 0.7s ease both;
}
@keyframes view-leave {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-8px); }
}
body.leaving-cream {
  background-color: var(--surround) !important;
  background-image: none !important;
  transition: background-color 0.7s ease;
}

/* (2026-06-14) LANDING → TODAY'S LETTER transition.
   The bug: route() flipped <body> to dark the instant we navigated, while the
   cream landing DOM (dark ink, sized for cream paper) was still mounted — a
   muddy "dark-ink-on-dark-bg" frame — then a "checking your seal" gate flashed
   before the loading caption.
   Fix (orchestrated in enterTodayFromLanding()): beat 1 fades the landing out
   while the bg is STILL cream (this rule — higher specificity than .view.leaving
   so it wins, and a touch quicker so content is gone before any darkening);
   beat 2 adds .leaving-cream to cross-fade the bg cream→dark; beat 3 navigates,
   and the dark loading caption fades in. The dark ink never sits on a dark bg. */
/* (2026-06-16) The flyleaf TURNS away from its dog-ear corner (transform-origin
   bottom-right), echoing the design's page-turn. The opacity is fully gone by
   ~55% (≈440ms) — just before beat-2 cross-fades the bg cream→dark at 450ms —
   so the cream content never sits on a darkening bg (no muddy dark-ink frame).
   The rotate keeps running invisibly for the lift's follow-through. */
.view.landing.leaving {
  transform-origin: 100% 100%;
  animation: landing-turn 0.8s cubic-bezier(.4,0,.2,1) both;
}
@keyframes landing-turn {
  0%   { opacity: 1; transform: none; }
  55%  { opacity: 0; }
  100% { opacity: 0; transform: rotate(7deg) translate(12%, 9%); }
}


/* (2026-05-10) Removed the entire legacy "REPLY VIEW · variant #4 fidelity"
   block (.reply-stage / .reply-sheet override / .reply-head / .reply-salu /
   .reply-text / .reply-foot / .inkwell-* / .seal-send).

   Why: the inline reply pad in viewLetter() reuses the .reply-sheet class as
   a "sister sheet" alongside .letter-sheet, and the legacy block re-declared
   .reply-sheet with a notebook-paper background, cool blue paper color, and
   min-height: 800px — those won the cascade because they came later, so the
   inline reply pad was rendering with the wrong paper. The /#/reply route
   handler in yours.js now redirects to /letter/:id, so this entire view is
   unreachable. Dead code + active visual bug → deleted.

   If we ever resurrect it, restore from git history and put each rule under a
   .reply-stage parent selector to scope it. */


/* ════════════════════════════════════════════════════════════════
   SETTINGS MODAL
   ════════════════════════════════════════════════════════════════ */
.settings-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 200;
  display: flex; align-items: center; justify-content: center;
  animation: view-fade 0.3s ease both;
}
.settings-panel {
  background: var(--paper-base);
  color: var(--ink-color);
  padding: 48px 44px 36px;
  width: 90%;
  max-width: 460px;
  max-height: 88vh;
  overflow-y: auto;
  box-shadow: 0 30px 80px rgba(0,0,0,0.6);
  font-family: 'Cormorant Garamond', serif;
}
/* font row = 3 options always, single row */
.settings-row .opt#fontOpt button { flex: 1 1 0; min-width: 0; padding: 10px 8px; font-size: 13px; }
/* (2026-05-10) Removed `.settings-row .opt#cursorOpt button` rule — the cursor
   row was dropped from showSettings() in v25; only this CSS dangler remained. */
.settings-panel h2 {
  font-style: italic;
  font-size: 22px;
  font-weight: 400;
  margin-bottom: 18px;
  text-align: center;
  letter-spacing: 0.02em;
}
.settings-panel hr {
  border: none;
  border-top: 0.5px solid var(--hairline);
  margin: 18px 0;
}
.settings-row { margin-bottom: 16px; }
.settings-row .label {
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  display: block;
  margin-bottom: 10px;
}
.settings-row .opt { display: flex; gap: 10px; }
.settings-row .opt button {
  flex: 1;
  padding: 10px 14px;
  border: 0.5px solid var(--hairline);
  font-family: 'Cormorant Garamond', serif;
  font-size: 14px;
  font-style: italic;
  color: var(--ink-soft);
  cursor: pointer;
  transition: all 0.3s ease;
}
.settings-row .opt button:hover { border-color: var(--ink-soft); color: var(--ink-color); }
.settings-row .opt button.active {
  background: var(--ink-color);
  color: var(--paper-base);
  border-color: var(--ink-color);
}
.settings-note {
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-soft);
  font-style: italic;
}
.settings-input {
  width: 100%;
  padding: 9px 12px;
  border: 0.5px solid var(--hairline);
  background: rgba(255,255,255,0.5);
  font: inherit;
  font-size: 14px;
  color: var(--ink-color);
  font-style: normal;
}
.settings-wide-button {
  width: 100%;
  padding: 11px 14px;
  border: 0.5px solid var(--hairline);
  font-family: 'Cormorant Garamond', serif;
  font-size: 15px;
  font-style: italic;
  color: var(--ink-soft);
  transition: all 0.3s ease;
}
.settings-wide-button:hover {
  border-color: var(--ink-soft);
  color: var(--ink-color);
}
.settings-close {
  display: block;
  margin: 24px auto 0;
  padding: 12px 18px;          /* hit-area: was 0; ≥44px target */
  min-height: 44px;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 13px;
  color: var(--ink-soft);
  cursor: pointer;
  letter-spacing: 0.04em;
  text-align: center;
}
.settings-close:hover { color: var(--ink-color); }

/* Carbon preview modal (2026-05-10) — wider than settings, biases to
   reading the user's own reply text. */
.carbon-preview-panel {
  max-width: 600px;
  padding: 40px 44px 28px;
}
.carbon-preview-body {
  max-height: 50vh;
  overflow-y: auto;
}

/* Empty-send hint (2026-05-10) — appears above textarea on shake.
   Was silent before. */
.reply-empty-hint {
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 13px;
  color: var(--brand);
  letter-spacing: 0.04em;
  margin-bottom: 12px;
  min-height: 18px;
  transition: opacity 0.3s ease;
}


/* ════════════════════════════════════════════════════════════════
   DRAWER · archive catalog
   horizontal-scroll catalog of all correspondence (received + carbons).
   per-letter chronological feed; letter content rendered at micro-scale
   on the paper itself (artifact, not summary).
   prototype: yours/prototypes/drawer/_drawer-archive-catalog.html
   ════════════════════════════════════════════════════════════════ */
.archive {
  position: relative;           /* anchors the owner-only .dev-registry-pin */
  padding: 80px 60px;
  min-height: 100vh;
  display: flex;
  align-items: center;          /* vertically centered; safe because .archive-paper
                                   uses clamp() so total height never exceeds viewport. */
}
.archive-scroll {
  display: flex;
  gap: 80px;
  padding-right: 120px;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  align-items: flex-start;
}
.archive-scroll::-webkit-scrollbar { display: none; }

/* (2026-05-11) Owner-only "Writer Registry" pin — drawer bottom-left,
   diagonally opposite to the top-right `settings` nav so the two never
   visually collide. Visible only when authSession.user.email is in
   OWNER_EMAILS. Light tint because the drawer sits on the dark
   --surround backdrop — dark ink would be invisible. */
.dev-registry-pin {
  position: absolute;
  bottom: 30px;
  left: 36px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: 'Cormorant Garamond', 'EB Garamond', serif;
  font-style: italic;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.4);
  text-decoration: none;
  padding: 4px 0;
  border-bottom: 0.5px solid transparent;
  transition: color .15s ease, border-color .15s ease;
  letter-spacing: 0.02em;
  z-index: 5;
}
.dev-registry-pin:hover {
  color: rgba(255, 255, 255, 0.85);
  border-bottom-color: rgba(255, 255, 255, 0.45);
}
.dev-registry-pin .pin-mark {
  font-family: ui-monospace, monospace;
  font-style: normal;
  font-size: 11px;
  opacity: 0.7;
  letter-spacing: 0;
}
.dev-registry-pin .pin-label {
  font-size: 12px;
  letter-spacing: 0.05em;
}
/* (2026-05-12) Second owner-only pin — sits above the Writer Registry
   pin in the drawer's bottom-left stack. Same dev-back-office register.
   Both stay subtle: faint italic, soft hairline on hover. */
.dev-registry-pin.dev-room-pin {
  bottom: 56px;  /* stack above Writer Registry (bottom: 30px + line) */
}
/* (2026-07-06) Popular Underlines gets its own owner-only inventory page,
   parallel to Prompt Editor / Reading Room / Writer Registry. */
.dev-registry-pin.dev-popular-pin {
  bottom: 82px;
}
/* (2026-06-15) Prompt Editor, now stacked above Popular Underlines. */
.dev-registry-pin.dev-prompt-pin {
  bottom: 108px;
}

/* (2026-05-12) Reading Room — owner-only cross-user feed. Visual
   register: paper-like cards on dark surround, NOT the letter-sheet
   tilt (this is a metadata view, not a ritual). Compact, scannable. */
.view.reading-room {
  background: var(--surround, #121212);
  color: var(--paper-base, #ece8e2);
  min-height: 100vh;
  padding: 80px 60px 120px;
  display: flex;
  flex-direction: column;
  gap: 32px;
}
.reading-room .room-head {
  max-width: 920px;
  margin: 0 auto;
  width: 100%;
}
.reading-room .room-head h1 {
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-weight: 300;
  font-size: 36px;
  margin: 0 0 6px;
  color: var(--paper-base, #ece8e2);
}
.reading-room .room-sub {
  font-family: 'Cormorant Garamond', 'EB Garamond', serif;
  font-style: italic;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.5);
  margin: 0;
}
.reading-room .room-empty {
  max-width: 920px;
  margin: 60px auto 0;
  color: rgba(255, 255, 255, 0.45);
  font-style: italic;
  text-align: center;
}
.reading-room .room-stream {
  max-width: 920px;
  margin: 0 auto;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.reading-room .room-day {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.reading-room .room-date {
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.4);
  margin: 4px 0 6px;
  padding-bottom: 6px;
  border-bottom: 0.5px solid rgba(255, 255, 255, 0.1);
}
.reading-room .room-card {
  background: var(--paper-base, #ece8e2);
  color: var(--ink, #1a1b26);
  padding: 22px 26px 18px;
  box-shadow: 0 8px 20px -10px rgba(0, 0, 0, 0.5);
  font-family: 'Cormorant Garamond', 'Noto Serif SC', serif;
  font-size: 15px;
  line-height: 1.65;
}
.reading-room .room-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 10px;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 0.5px solid rgba(0, 0, 0, 0.1);
  font-family: 'IM Fell English', serif;
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--ink-soft, rgba(26, 27, 38, 0.65));
}
.reading-room .room-from {
  font-style: italic;
  font-weight: 500;
  color: var(--ink, #1a1b26);
  font-size: 14px;
  letter-spacing: 0.03em;
}
.reading-room .room-arrow {
  color: var(--ink-faint, rgba(26, 27, 38, 0.35));
}
.reading-room .room-to {
  font-style: italic;
  font-weight: 500;
  color: var(--brand, #B7613D);
  font-size: 14px;
  letter-spacing: 0.03em;
}
.reading-room .room-watermark,
.reading-room .room-dateline {
  margin-left: auto;
  font-family: 'IM Fell English', serif;
  font-size: 10px;
  color: var(--ink-faint, rgba(26, 27, 38, 0.35));
  letter-spacing: 0.1em;
}
.reading-room .room-body p {
  margin: 0 0 8px;
  text-align: justify;
}
.reading-room .room-body em {
  font-style: italic;
  background: linear-gradient(
    180deg,
    transparent 30%,
    rgba(183, 97, 61, 0.18) 30%,
    rgba(183, 97, 61, 0.18) 92%,
    transparent 92%
  );
  padding: 0 1px;
}
.reading-room .room-replies {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 0.5px dashed rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.reading-room .room-reply {
  background: rgba(183, 97, 61, 0.06);
  padding: 10px 14px;
  border-left: 2px solid rgba(183, 97, 61, 0.35);
}
.reading-room .reply-head {
  display: flex;
  justify-content: space-between;
  font-family: 'IM Fell English', serif;
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--ink-faint, rgba(26, 27, 38, 0.45));
  margin-bottom: 6px;
}
.reading-room .reply-from {
  font-style: italic;
  text-transform: uppercase;
}
.reading-room .reply-body {
  margin: 0;
  font-style: italic;
  color: var(--ink-soft, rgba(26, 27, 38, 0.75));
  font-size: 14px;
  line-height: 1.55;
}
.reading-room .room-no-reply {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 0.5px dashed rgba(0, 0, 0, 0.12);
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 11px;
  color: var(--ink-faint, rgba(26, 27, 38, 0.35));
  letter-spacing: 0.08em;
}
@media (max-width: 720px) {
  .view.reading-room { padding: 56px 24px 100px; }
  .reading-room .room-head h1 { font-size: 28px; }
  .reading-room .room-card { padding: 18px 18px 14px; font-size: 14px; }
}

/* (2026-05-11) Owner-only DEV — Bailian model picker in Settings panel.
   Vintage back-office tone: keeps the section visually distinct from
   normal user preferences so it's obvious this is a dev-only knob, not
   something to tinker with by accident. */
.dev-divider {
  border: 0;
  border-top: 0.5px dashed rgba(0, 0, 0, 0.14);
  margin: 24px 0 8px;
}
.dev-section .label { font-size: 13px; opacity: 0.75; }
.dev-section .dev-tag {
  display: inline-block;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand, #B7613D);
  padding: 1px 5px;
  border: 0.5px solid rgba(183, 97, 61, 0.35);
  border-radius: 2px;
  margin-right: 4px;
  vertical-align: middle;
}
.dev-model-opt {
  flex-wrap: wrap;
  gap: 6px;
}
.dev-model-opt button {
  font-family: ui-monospace, 'SF Mono', 'Menlo', monospace;
  font-size: 11px !important;
  letter-spacing: 0;
  text-transform: none;
  padding: 4px 8px !important;
}
.dev-note {
  font-size: 11px;
  line-height: 1.55;
  margin-top: 8px;
  opacity: 0.55;
  font-style: italic;
}
.dev-note code {
  font-family: ui-monospace, 'SF Mono', 'Menlo', monospace;
  font-size: 10.5px;
  background: rgba(0, 0, 0, 0.05);
  padding: 1px 4px;
  border-radius: 2px;
  font-style: normal;
}

/* (2026-05-11, moved 2026-05-12) DEV badge on the letter — small italic
   line with `via <model> · <Ns> · <N>tok`. Top-right of the sheet so it
   reads like a postmark/cancellation stamp: visible the moment you glance
   at the letter (so you know which model wrote it), without competing
   with body text. Rendered only when data._meta is present AND the
   reader is in OWNER_EMAILS. */
.letter-meta-dev {
  position: absolute;
  top: 24px;
  right: 32px;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 10px;
  letter-spacing: 0.08em;
  color: rgba(26, 27, 38, 0.32);
  pointer-events: none;
  user-select: none;
  /* Stay above the .watermark seal (which is centered behind text). */
  z-index: 2;
}
/* In vertical (直排) mode flip to top-left so the badge doesn't sit at
   the start of the rightmost column. */
.letter-sheet[data-vertical="true"] .letter-meta-dev {
  right: auto;
  left: 32px;
}
/* Narrower sheets — pull the badge inward a touch so it doesn't hit
   the sheet edge on mobile. */
@media (max-width: 720px) {
  .letter-meta-dev {
    top: 14px;
    right: 18px;
    font-size: 9px;
  }
  .letter-sheet[data-vertical="true"] .letter-meta-dev {
    right: auto;
    left: 18px;
  }
}

/* (2026-05-11) Quoted-mark highlight — phrases the letter borrowed
   from the reader's marks. Subtle warm-paper underwash (looks like
   the page itself slightly remembered the line); not a marker pen,
   not an italic, not a border — just a tone shift so the eye can see
   what came from "real Hesse" vs. what came from the model.
   Hover surfaces the source book via the data-source attribute as
   a plain tooltip (title attr already set on the span). */
.quoted-mark {
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 30%,
    rgba(183, 97, 61, 0.16) 30%,    /* warm umber, tone-warn family */
    rgba(183, 97, 61, 0.16) 92%,
    transparent 92%
  );
  border-radius: 1px;
  padding: 0 1px;
  cursor: help;
  transition: background .15s ease;
}
.quoted-mark:hover {
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 30%,
    rgba(183, 97, 61, 0.32) 30%,
    rgba(183, 97, 61, 0.32) 92%,
    transparent 92%
  );
}
/* In handwritten / 直排 vertical mode the underwash needs to wash the
   right edge instead of bottom, so the highlight reads as a column
   tint rather than a horizontal underline. */
.letter-sheet[data-vertical="true"] .quoted-mark {
  background: linear-gradient(
    270deg,
    transparent 0%,
    transparent 30%,
    rgba(183, 97, 61, 0.16) 30%,
    rgba(183, 97, 61, 0.16) 92%,
    transparent 92%
  );
}

.archive-item {
  display: flex;
  flex-direction: column;
  gap: 30px;
  flex: 0 0 auto;                      /* (2026-05-12) don't grow/shrink */
  min-width: 400px;
  max-width: 460px;                    /* (2026-05-12) keep card a letter-shape
                                          even when drawer has only 1 item — without
                                          this the flex row stretched a lone card
                                          across the viewport, making the mock text
                                          run as one giant horizontal line instead
                                          of wrapping into letter paragraphs. */
  opacity: 0.55;                       /* read default */
  transition: opacity 0.4s ease, transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
  cursor: pointer;
}
.archive-item.unread { opacity: 0.92; }
.archive-item:hover { opacity: 1; transform: translateY(-10px); }

.archive-paper {
  background-color: var(--paper-base, #e5e2de);
  width: 100%;
  height: clamp(360px, calc(100vh - 240px), 550px);
  padding: 40px;
  box-shadow: 0 15px 35px -5px rgba(0,0,0,0.4);
  position: relative;
  background-image:
    radial-gradient(circle at 50% 50%, rgba(255,255,255,0.1) 0%, rgba(0,0,0,0.05) 100%),
    url('https://www.transparenttextures.com/patterns/paper-fibers.png');
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* miniaturized letter content rendered ON the paper */
.archive-mock {
  font-family: 'Homemade Apple', cursive;
  color: var(--ink-color, #1a1b26);
  font-size: 10px;
  line-height: 2.5;
  opacity: 0.3;
  transform: scale(0.8);
  transform-origin: top left;
  user-select: none;
}
.archive-mock em { font-style: italic; }
.archive-mock.letterhead-mock { /* positioned via inline style on the element */ }

/* CJK micro-content uses serif (Homemade Apple has no CJK glyphs) */
.archive-paper[data-lang^="zh"] .archive-mock {
  font-family: 'ChillKai', 'Songti SC', 'STSong', serif;
  font-size: 11px;
  line-height: 2.2;
}

/* vertical-rl override: zh-Hant + zh-Hant native author */
.archive-paper[data-vertical="true"] .archive-mock {
  writing-mode: vertical-rl;
  text-orientation: upright;
  transform: scale(0.85);
  transform-origin: top right;
}
.archive-paper[data-vertical="true"] .archive-mock.letterhead-mock {
  margin-left: 0; margin-right: 0; text-align: start;
}

/* metadata block beneath the paper */
.archive-meta {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.archive-sender {
  font-family: 'Homemade Apple', cursive;
  font-size: 24px;
  color: #fff;
  line-height: 1.2;
}
.archive-sender.cjk {
  font-family: 'Cormorant Garamond', 'Songti SC', serif;
  font-style: italic;
  font-size: 26px;
}
.archive-sender .to {
  font-family: 'IM Fell English', serif;
  font-style: italic;
  font-size: 14px;
  letter-spacing: 2px;
  color: rgba(255,255,255,0.45);
  text-transform: uppercase;
  margin-right: 10px;
  vertical-align: 4px;
}
.archive-year {
  font-family: 'IM Fell English', serif;
  font-size: 14px;
  letter-spacing: 2px;
  color: rgba(255,255,255,0.6);   /* was 0.4 (≈3.7:1, fail) → ≈5:1 (AA pass) */
}
.archive-item.unread .archive-year::before {
  content: "";
  display: inline-block;
  width: 18px; height: 1px;
  background: var(--brand);
  margin-right: 10px;
  vertical-align: 4px;
}

/* CARBON COPIES — sent letters rendered as carbon paper.
   stronger differentiation now: cooler bluish-grey paper, thinner shadow,
   slight rotation, and the whole paper a touch translucent (carbon paper IS thin). */
.archive-item[data-kind="carbon"] .archive-paper {
  background-color: #b8bbc0;          /* clearly cooler/greyer — was #d4d0c4 (too close to original) */
  background-image:
    radial-gradient(circle at 50% 50%, rgba(255,255,255,0.04) 0%, rgba(0,0,0,0.06) 100%),
    url('https://www.transparenttextures.com/patterns/paper-fibers.png');
  box-shadow: 0 6px 16px -6px rgba(0,0,0,0.35);
  transform: rotate(-0.6deg);
  opacity: 0.92;
}
.archive-item[data-kind="carbon"] .archive-mock {
  color: #2a2e3c;                     /* bluish-black (carbon ink, not iron-gall) */
  opacity: 0.32;
}

/* empty state — no letters yet */
.archive-empty {
  width: 100%;
  text-align: center;
  padding: 120px 24px;
  font-family: 'IM Fell English', serif;
  font-style: italic;
  color: rgba(255,255,255,0.65);   /* was 0.4 — empty state copy needs to be readable */
  font-size: 15px;
  letter-spacing: 0.04em;
}

/* responsive */
@media (max-width: 720px) {
  .archive { padding: 80px 24px 40px; }
  .archive-scroll { gap: 48px; padding-right: 60px; }
  .archive-item { min-width: 320px; }
  .archive-paper { padding: 28px; height: clamp(320px, calc(100vh - 200px), 480px); }
  .archive-sender { font-size: 22px; }
}


/* ════════════════════════════════════════════════════════════════
   responsive
   ════════════════════════════════════════════════════════════════ */

/* Tablet (2026-05-10): there was no breakpoint between 720 and 850
   (.letter-sheet max-width). 768-1024 viewports were eating desktop
   120/100 padding, leaving the sheet narrow with huge gutters. */
@media (max-width: 1024px) and (min-width: 721px) {
  .letter-sheet { padding: 96px 64px 80px; }
  .drawer { padding: 96px 48px 120px; }
}

@media (max-width: 720px) {
  .letter-sheet { padding: 80px 36px 60px; }
  .reply-sheet  { padding: 64px 32px; }
  .salutation, .reply-salu { font-size: 20px; }
  /* (2026-05-10) was 17px on mobile — at 320px width × line-height 2.0
     felt cramped. 16px breathes better while staying readable. */
  .letter-content, .reply-text { font-size: 16px; }
  .signature-name { font-size: 22px; }
  body[data-lang^="zh"] .signature-name { font-size: 24px; }
  .watermark { width: clamp(180px, 50%, 260px); font-size: clamp(13px, 4vw, 18px); }
  /* Nav: keep ≥44px hit area even on narrow screens; just shrink the type. */
  .nav-content { padding: 8px 12px; font-size: 11px; }
  .nav-content button, .nav-content a { padding: 12px 10px; }
  .drawer { padding: 80px 24px; }
  .drawer-head .h { font-size: 24px; }
  .drawer-item .from { font-size: 19px; }
  /* Auth-gate: lang switcher gets thumb-sized */
  .auth-lang button { padding: 10px 12px; font-size: 13px; }
}

/* Reduced motion (2026-05-10) — was missing entirely. App has letter fade,
   scroll-hint breathing, handwriting reveal, pad-fold, pad-fly, view-leave.
   Vestibular-sensitive users couldn't opt out. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Keep the fade-in subtle but immediate */
  .view { animation: none; }
  .scroll-hint { animation: none; }
  .loading-caption span { animation: none; opacity: 1; }
}

/* Print (2026-05-10) — a writing-product where you couldn't print a letter.
   Strip dark surround, hide chrome, drop shadows + transforms. */
@media print {
  html, body {
    background: #fff !important;
    color: #000 !important;
  }
  .grain-overlay,
  .nav-trigger, .nav-content,
  .brand-mark,
  .scroll-hint,
  .send-btn-link,
  .reply-foot, .reply-sheet,
  .settings-backdrop {
    display: none !important;
  }
  .letter-sheet {
    box-shadow: none !important;
    transform: none !important;
    background: #fff !important;
    background-image: none !important;
    max-width: 100% !important;
    padding: 0 !important;
    min-height: 0 !important;
  }
  .watermark { display: none !important; }
}
