/*
 * haiku.to theme token overrides.
 *
 * One html[data-theme="..."] block per theme, each overriding the
 * :root token set defined in app.css (chrome follows the tokens).
 * Loaded via its own `<link rel="stylesheet">`, placed AFTER app.css's in
 * every page's <head> (Task 4, phase 7A hygiene -- replaces the previous
 * `@import url("/app/assets/themes.css");` at the top of app.css, which
 * render-blocked on a second round-trip before the page's own CSS could even
 * start downloading), split out to keep app.css under the project's LOC cap
 * as more themes are added.
 *
 * Specificity note: html[data-theme="x"] is (0,1,1) vs :root's (0,1,0),
 * so a theme block always wins over the :root defaults regardless of
 * source order -- load order here doesn't affect which tokens apply, only
 * file organization.
 *
 * Cascade order flip (Task 4): loading via <link> AFTER app.css REVERSES
 * same-specificity source-order ties from how `@import` (which put this
 * file's rules first) used to resolve them -- this file now wins same-
 * specificity ties against app.css where it used to lose them. Audited: every
 * rule below that competes with an app.css selector at equal specificity
 * already carries the deliberate `html`-ancestor prefix (see the mobile-
 * masthead and plumbing sections below) added specifically so it wins by
 * SPECIFICITY, never by order -- so none of them depended on the old losing
 * tie, and the flip changes nothing. No rule here relies on LOSING a tie to
 * app.css either (grep both files for shared selectors turns up none at
 * matched specificity outside the `html`-prefixed set).
 */

/* ---- masthead frost + breathe tokens --------------------------------------
   Defaults for the constellation theme (which has no html[data-theme] block
   of its own -- see the comment below -- its overrides live in :root, in
   app.css, which is at its LOC cap and not editable here). `--masthead-bg`
   is a translucent tint built on constellation's own --void-2 (#10151f ->
   16,21,31); `--masthead-breathe-a/b` reuse the exact rgba colors :root's
   own `--breathe-a`/`--breathe-b` already use for this theme (63,111,209 /
   90,140,240), just in a full-width drop-shadow geometry instead of a
   card's left-edge inset (that inset would look wrong stretched across an
   entire masthead). Each html[data-theme] block below overrides these three
   with its own theme's --void-2 tint + --breathe-a/b colors, so every theme
   gets its own frost tint and breathe glow. */
html {
  --masthead-bg: rgba(16, 21, 31, 0.68);
  --masthead-breathe-a: 0 14px 46px -18px rgba(63, 111, 209, 0.24), inset 0 -1px 0 0 var(--line);
  --masthead-breathe-b: 0 18px 64px -14px rgba(90, 140, 240, 0.4), inset 0 -1px 0 0 var(--line);
}

/* ---- themes ---------------------------------------------------------------
   A theme = one html[data-theme] token-override block (chrome follows the
   tokens) + a weather renderer (themes/index.ts manifests pick it). The
   default constellation values live in :root above. */

/* washi dawn — warm paper morning: cream washi, sumi ink, ai-iro indigo dye
   lineage, seal red. Light theme. */
html[data-theme="washi-dawn"] {
  color-scheme: light;
  --void:   #f5ecdc;
  --void-2: #ece1cb;
  --ink:    #2a2723;
  --ai:     #3f5d9e;
  --ai-dim: #6c82b8;
  --cut:    #c23b22;
  --mist:   #8b8272;
  --line:   rgba(63, 93, 158, 0.22);
  --glow-ai:  0 0 16px rgba(63, 93, 158, 0.22);
  --glow-cut: 0 0 14px rgba(194, 59, 34, 0.3);
  --halo: rgba(42, 39, 35, 0.08);
  --bg-aurora:
    radial-gradient(120% 60% at 50% 118%, rgba(228, 158, 110, 0.3) 0%, transparent 60%),
    radial-gradient(90% 50% at 50% -10%, rgba(63, 93, 158, 0.08) 0%, transparent 55%);
  --card-bg:
    radial-gradient(130% 92% at 3% 50%, rgba(63, 93, 158, 0.1), rgba(63, 93, 158, 0.03) 44%, transparent 74%),
    rgba(255, 251, 240, 0.5);
  --card-bg-hover:
    radial-gradient(130% 96% at 3% 50%, rgba(63, 93, 158, 0.16), rgba(63, 93, 158, 0.05) 46%, transparent 76%),
    rgba(255, 251, 240, 0.62);
  --card-border-c: rgba(63, 93, 158, 0.18);
  --card-inset: inset 52px 0 58px -40px rgba(63, 93, 158, 0.12);
  --breathe-a: inset 50px 0 56px -40px rgba(63, 93, 158, 0.1);
  --breathe-b: inset 64px 0 68px -36px rgba(63, 93, 158, 0.18);
  --masthead-bg: rgba(236, 225, 203, 0.68);
  --masthead-breathe-a: 0 14px 46px -18px rgba(63, 93, 158, 0.16), inset 0 -1px 0 0 var(--line);
  --masthead-breathe-b: 0 18px 64px -14px rgba(63, 93, 158, 0.28), inset 0 -1px 0 0 var(--line);
}

/* rain — grey-green twilight on water: wet slate, jade lineage light. */
html[data-theme="rain"] {
  --void:   #101714;
  --void-2: #16201c;
  --ink:    #dce8e2;
  --ai:     #63c1a7;
  --ai-dim: #3e8571;
  --cut:    #f05438;
  --mist:   #6d7f78;
  --line:   rgba(99, 193, 167, 0.16);
  --glow-ai:  0 0 22px rgba(99, 193, 167, 0.32);
  --glow-cut: 0 0 20px rgba(240, 84, 56, 0.42);
  --halo: rgba(220, 232, 226, 0.12);
  --bg-aurora:
    radial-gradient(120% 60% at 50% 118%, rgba(62, 133, 113, 0.24) 0%, transparent 60%),
    radial-gradient(90% 50% at 50% -10%, rgba(99, 193, 167, 0.07) 0%, transparent 55%);
  --card-bg:
    radial-gradient(130% 92% at 3% 50%, rgba(99, 193, 167, 0.12), rgba(99, 193, 167, 0.04) 44%, transparent 74%),
    rgba(13, 20, 17, 0.44);
  --card-bg-hover:
    radial-gradient(130% 96% at 3% 50%, rgba(99, 193, 167, 0.18), rgba(99, 193, 167, 0.06) 46%, transparent 76%),
    rgba(16, 24, 21, 0.52);
  --card-border-c: rgba(99, 193, 167, 0.1);
  --card-inset: inset 52px 0 58px -40px rgba(62, 133, 113, 0.2);
  --breathe-a: inset 50px 0 56px -40px rgba(62, 133, 113, 0.16);
  --breathe-b: inset 64px 0 68px -36px rgba(99, 193, 167, 0.26);
  --masthead-bg: rgba(22, 32, 28, 0.68);
  --masthead-breathe-a: 0 14px 46px -18px rgba(62, 133, 113, 0.26), inset 0 -1px 0 0 var(--line);
  --masthead-breathe-b: 0 18px 64px -14px rgba(99, 193, 167, 0.38), inset 0 -1px 0 0 var(--line);
}

/* ember — autumn dusk: warm charcoal, persimmon-gold lantern lineage. */
html[data-theme="ember"] {
  --void:   #16100c;
  --void-2: #1f1712;
  --ink:    #f2e6d8;
  --ai:     #ff9d45;
  --ai-dim: #c96f2a;
  --cut:    #ff4a35;
  --mist:   #8a7a6b;
  --line:   rgba(255, 157, 69, 0.16);
  --glow-ai:  0 0 22px rgba(255, 157, 69, 0.32);
  --glow-cut: 0 0 20px rgba(255, 74, 53, 0.45);
  --halo: rgba(242, 230, 216, 0.12);
  --bg-aurora:
    radial-gradient(120% 60% at 50% 118%, rgba(201, 111, 42, 0.26) 0%, transparent 60%),
    radial-gradient(90% 50% at 50% -10%, rgba(255, 157, 69, 0.07) 0%, transparent 55%);
  --card-bg:
    radial-gradient(130% 92% at 3% 50%, rgba(255, 157, 69, 0.11), rgba(255, 157, 69, 0.04) 44%, transparent 74%),
    rgba(24, 17, 12, 0.44);
  --card-bg-hover:
    radial-gradient(130% 96% at 3% 50%, rgba(255, 157, 69, 0.17), rgba(255, 157, 69, 0.06) 46%, transparent 76%),
    rgba(30, 21, 15, 0.52);
  --card-border-c: rgba(255, 157, 69, 0.11);
  --card-inset: inset 52px 0 58px -40px rgba(201, 111, 42, 0.2);
  --breathe-a: inset 50px 0 56px -40px rgba(201, 111, 42, 0.16);
  --breathe-b: inset 64px 0 68px -36px rgba(255, 157, 69, 0.26);
  --masthead-bg: rgba(31, 23, 18, 0.68);
  --masthead-breathe-a: 0 14px 46px -18px rgba(201, 111, 42, 0.26), inset 0 -1px 0 0 var(--line);
  --masthead-breathe-b: 0 18px 64px -14px rgba(255, 157, 69, 0.38), inset 0 -1px 0 0 var(--line);
}

/* snow — near-white quiet: white field, slate ink, faint winter-blue
   shadows, seal red pops. Light theme. */
html[data-theme="snow"] {
  color-scheme: light;
  --void:   #eef1f5;
  --void-2: #e4e9f0;
  --ink:    #2f3540;
  --ai:     #5a7fc0;
  --ai-dim: #8ba3cd;
  --cut:    #c23b22;
  --mist:   #97a1b0;
  --line:   rgba(90, 127, 192, 0.25);
  --glow-ai:  0 0 14px rgba(90, 127, 192, 0.25);
  --glow-cut: 0 0 14px rgba(194, 59, 34, 0.3);
  --halo: rgba(47, 53, 64, 0.07);
  --bg-aurora:
    radial-gradient(120% 60% at 50% 118%, rgba(139, 163, 205, 0.22) 0%, transparent 60%),
    radial-gradient(90% 50% at 50% -10%, rgba(90, 127, 192, 0.09) 0%, transparent 55%);
  --card-bg:
    radial-gradient(130% 92% at 3% 50%, rgba(90, 127, 192, 0.1), rgba(90, 127, 192, 0.03) 44%, transparent 74%),
    rgba(255, 255, 255, 0.5);
  --card-bg-hover:
    radial-gradient(130% 96% at 3% 50%, rgba(90, 127, 192, 0.15), rgba(90, 127, 192, 0.05) 46%, transparent 76%),
    rgba(255, 255, 255, 0.62);
  --card-border-c: rgba(90, 127, 192, 0.2);
  --card-inset: inset 52px 0 58px -40px rgba(90, 127, 192, 0.12);
  --breathe-a: inset 50px 0 56px -40px rgba(90, 127, 192, 0.1);
  --breathe-b: inset 64px 0 68px -36px rgba(90, 127, 192, 0.18);
  --masthead-bg: rgba(228, 233, 240, 0.68);
  --masthead-breathe-a: 0 14px 46px -18px rgba(90, 127, 192, 0.18), inset 0 -1px 0 0 var(--line);
  --masthead-breathe-b: 0 18px 64px -14px rgba(90, 127, 192, 0.3), inset 0 -1px 0 0 var(--line);
}

/* ---- theme picker (masthead) + ?theme= keep banner ---------------------- */
/* picker.ts appends this <select> straight into the primary nav, alongside
   the bracketed [nav] links -- app.css's generic `select { width: 100% }`
   rule (the only rule that sets `width` for a bare <select>) otherwise wins
   by default, stretching it into a full-width bar that wraps onto its own
   line. `width: auto` + `flex: 0 0 auto` keep it sized to its content as a
   quiet nav-flex sibling; `align-self: center` lines its box up with the
   links' baseline row instead of flex's default stretch-to-row-height. */
.theme-picker {
  background: transparent; color: var(--mist); border: 1px solid var(--line);
  border-radius: 0.4rem; font: 500 0.72rem/1 var(--mono); letter-spacing: 0.06em;
  padding: 0.25rem 0.4rem; cursor: pointer;
  width: auto; flex: 0 0 auto; align-self: center; display: inline-block;
  margin-left: 0.3rem; max-width: 100%;
}
.theme-picker:hover, .theme-picker:focus-visible { color: var(--ink); border-color: var(--ai); }
.theme-banner {
  position: fixed; bottom: 2rem; left: 50%; transform: translateX(-50%); z-index: 4;
  display: flex; align-items: center; gap: 0.6rem;
  background: var(--void-2); color: var(--ink); border: 1px solid var(--line);
  border-radius: 2rem; padding: 0.45rem 0.9rem; font: 500 0.78rem/1 var(--mono);
  box-shadow: var(--glow-ai);
}
.theme-banner button {
  background: transparent; color: var(--ai); border: 1px solid var(--line);
  border-radius: 1rem; font: inherit; padding: 0.2rem 0.6rem; cursor: pointer;
}
.theme-banner button:hover { color: var(--ink); border-color: var(--ai); }

/* ---- preset apply banners (presets.ts, any page via starfield boot) ------ */
.preset-banner {
  position: fixed; bottom: 2rem; left: 50%; transform: translateX(-50%); z-index: 4;
  display: flex; align-items: center; gap: 0.6rem;
  background: var(--void-2); color: var(--ink); border: 1px solid var(--line);
  border-radius: 2rem; padding: 0.45rem 1rem; font: 500 0.78rem/1.3 var(--mono);
  box-shadow: var(--glow-ai); max-width: min(90vw, 28rem); text-align: center;
}
.preset-banner.preset-error { border-color: var(--cut); color: var(--cut); }

/* ---- feed empty / error states (library.ts) -------------------------------
   Actionable panels replacing bare text that used to be dumped straight into
   `[data-testid="feed-list"]` -- on the home page's falling scene that div
   is `position: absolute; inset: 0` (a full-viewport box, see app.css), so
   plain text landed at its top-left corner, visually overlapping the
   masthead. `position: fixed` centers the panel over the whole viewport
   regardless of which feed layout is active (the plain list in `main`, or
   the full-viewport falling scene) -- always readable, never pinned to a
   corner. Both panels share `.feed-state`; `data-testid` (empty-state /
   feed-error) plus `role` (status / alert) distinguish them. */
.feed-state {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: min(24rem, 86vw);
  text-align: center;
  background: var(--card-bg);
  backdrop-filter: blur(7px) saturate(1.1);
  -webkit-backdrop-filter: blur(7px) saturate(1.1);
  border: 1px solid var(--card-border-c);
  border-radius: 1.1rem;
  padding: 2rem 1.75rem;
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}
/* app.css scopes `[data-scene="falling"] [data-testid="feed-error"]` to a
   quiet `position: relative` top-of-scene strip (two attribute selectors,
   (0,2,0) -- more specific than `.feed-state` alone). That scoping predates
   this actionable-panel redesign, so this compound selector (three
   selectors, (0,3,0)) restores the fixed, viewport-centered treatment for
   both testids without editing app.css. */
[data-scene="falling"] [data-testid="feed-error"].feed-state,
[data-scene="falling"] [data-testid="empty-state"].feed-state {
  position: fixed;
  margin: 0;
}
.feed-state p { margin: 0 0 1.2rem; }
.feed-state a, .feed-state button {
  display: inline-block;
  font: 400 0.72rem/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ai); text-decoration: none; padding: 0.6rem 1.1rem;
  border: 1px solid var(--line); border-radius: 2px; background: rgba(110, 168, 255, 0.03);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.feed-state a:hover, .feed-state button:hover {
  color: var(--ink); border-color: var(--ai); box-shadow: var(--glow-ai); background: rgba(110, 168, 255, 0.06);
}

/* ---- masthead frost + breathe ---------------------------------------------
   app.css's `header[role="banner"]` rule sets layout/position/border-bottom
   only -- no background, so a strong frosted-glass pane sits entirely here.
   `--masthead-bg`/`--masthead-breathe-a`/`--masthead-breathe-b` (defined
   above, per theme) supply the tint + glow colors; the blur is themed
   itself: it always blurs whatever the starfield/falling cards are doing
   directly underneath (the canvas stays `z-index: 0`, the falling scene
   `z-index: 1`, the masthead `z-index: 2` -- all set in app.css, unchanged),
   so the frost visibly softens the motion behind it rather than sitting on
   a flat color. The breathe pulse mirrors `.haiku-card`'s `breathe`
   keyframes (same 7s ease-in-out infinite box-shadow pulse) but needs its
   own geometry -- the card's animation targets left-edge insets, which read
   wrong stretched across a full-width bar -- so this is a small dedicated
   keyframes over a soft, centered drop-glow instead. */
header[role="banner"] {
  background: var(--masthead-bg);
  backdrop-filter: blur(16px) saturate(1.15);
  -webkit-backdrop-filter: blur(16px) saturate(1.15);
}
@media (prefers-reduced-motion: no-preference) {
  header[role="banner"] { animation: masthead-breathe 7s ease-in-out infinite; }
  @keyframes masthead-breathe {
    0%, 100% { box-shadow: var(--masthead-breathe-a); }
    50%      { box-shadow: var(--masthead-breathe-b); }
  }
}
@media (prefers-reduced-motion: reduce) {
  header[role="banner"] { box-shadow: var(--masthead-breathe-a); }
}

/* ---- fx lab nav link (moved from app.css to stay under its LOC cap) ------
   the account page's owner-only link to the fx lab -- same treatment as
   nav-metrics: no pill chrome, no glow, a dim mono aside for whoever's
   signed in to peek at, not a feature meant to draw attention. */
[data-testid="nav-lab"] {
  display: inline-block; margin: 0.5rem 0 0 1rem;
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.08em;
  color: var(--mist); text-decoration: none; opacity: 0.7;
}
/* every masthead's primary nav now also carries a `nav-lab` link (so the lab
   is reachable from anywhere, not just the account page's aside) -- account.ts
   still renders its own standalone `nav-lab` aside above, so account.html can
   carry both when signed in; this override keeps the masthead instance
   looking like its `nav-compose`/`nav-list` siblings (bracketed mono chrome)
   rather than the aside's quiet offset styling. */
header[role="banner"] nav [data-testid="nav-lab"] { margin: 0; font-size: inherit; letter-spacing: inherit; }
[data-testid="nav-lab"]:hover { opacity: 1; color: var(--ai); }

/* ---- compact mobile masthead (the desktop one wraps to ~3 tall rows) -----
   Specificity note: this file now loads via its own <link> AFTER app.css's
   (Task 4 -- see the file-level cascade-order-flip note above; it used to
   load first, via @import, when app.css's rules effectively came later and
   always won same-specificity ties). app.css's desktop masthead rules
   (`header[role="banner"]`, `...h1`, `...nav`, `...nav a`) are plain,
   unconditional (no media query) selectors, so relying on a same-specificity
   tie here would be fragile either way -- media query or not. Each selector
   below is prefixed with `html` (a real, harmless ancestor --
   document.documentElement) purely to add one more element to its
   specificity than its app.css counterpart, so it wins by SPECIFICITY,
   independent of load order, while `(max-width: 899.98px)` matches.
   `.theme-picker` doesn't need this: its only competing rule is the
   `.theme-picker` block just above, in this same file, so ordinary
   later-in-file wins already applies. */
@media (max-width: 899.98px) {
  html header[role="banner"] {
    /* safe-area: same notch clearance the app.css base rule carries. */
    padding: calc(0.55rem + env(safe-area-inset-top, 0px))
             calc(0.8rem + env(safe-area-inset-right, 0px))
             0.5rem
             calc(0.8rem + env(safe-area-inset-left, 0px));
    gap: 0.4rem 0.75rem; align-items: center;
  }
  html header[role="banner"] h1 { font-size: 0.85rem; letter-spacing: 0.22em; }
  html header[role="banner"] nav { gap: 0.55rem; row-gap: 0.15rem; font-size: 0.62rem; letter-spacing: 0.08em; }
  html header[role="banner"] nav a { padding: 0.4rem 0; }
  .theme-picker { min-height: 30px; padding: 0.15rem 0.3rem; font-size: 0.6rem; }
}

/* ---- author card styles (meta.style -> data-card-type + --card-* vars) ----
   applyCardStyle (card-style.ts) sets the attribute + vars; everything
   visual lives here. --card-accent re-colors the spine/glow through the
   SAME channels each theme already themes (--ai), so each site theme
   decides how an accent manifests. shikishi = the default card, no block.

   Specificity note: the plumbing rules below target `.haiku-card .haiku` /
   `[data-testid="poem"] .haiku` / `[data-testid="poem-lines"]` -- the same
   selectors (same specificity) app.css's own `.haiku, [data-testid="poem-
   lines"]` (font-family) and `.haiku-card .haiku, ...` (font-size) rules
   use. Relying on a same-specificity tie against app.css here would be
   fragile regardless of which file loads last (see the file-level
   cascade-order-flip note at the top of this file -- Task 4 moved this
   file's load from a first-loaded @import to a <link> AFTER app.css's, which
   flips which side any such tie would go to) -- so each plumbing selector is
   prefixed with `html` (the established mobile-masthead pattern above)
   purely to add one more type selector of specificity than its app.css
   counterpart, winning outright rather than depending on load order. The
   type-level selectors (`.haiku-card,
   [data-testid="poem"]` and the `::before` accent-glow ones) don't need
   it: nothing in app.css sets font-family on those bare selectors, and the
   accent `::before` rule already carries an extra attribute selector
   ([data-card-accent]) that outranks app.css's plain `::before` rule
   regardless of source order. `[data-card-accent]` is a presence flag
   (set/cleared by applyCardStyle alongside `--card-accent` itself) rather
   than matching on `[style*="--card-accent"]` -- a substring match on the
   whole `style` attribute value would also fire for any future inline
   style whose value happened to contain that substring, silently
   collapsing the spine. */

/* Poem faces — system stacks only (no CDN). Tuned for a literary haiku
   page, not party-script / Comic Sans fallbacks. `data-card-font` drives
   optical adjustments (italic, tracking) so faces read as deliberate even
   when the OS only has the tail of the stack. */
html {
  /* High-contrast display serif when present; otherwise the same book serif. */
  --display: "Didot", "Bodoni 72", "Bodoni MT", "Hoefler Text", "Sitka Text",
    "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  /* Real typewriter when available; never the UI mono stack. */
  --typewriter: "American Typewriter", "Courier New", Courier, monospace;
  /* Soft ink hand: chancery / roundhand only — no Brush Script, no Comic Sans. */
  --brush: "Apple Chancery", "Snell Roundhand", "URW Chancery L",
    "Palatino Linotype", Palatino, Georgia, cursive;
  /* Quiet manuscript hand — book serif with optical soft-hand treatment below. */
  --hand: "Noteworthy", "Segoe Print", "Palatino Linotype", Palatino, Georgia, serif;
}

.haiku-card, [data-testid="poem"] {
  /* var plumbing: no style set -> today's exact look (fallbacks) */
  font-family: var(--card-font, inherit);
}
html .haiku-card .haiku, html [data-testid="poem"] .haiku, html [data-testid="poem-lines"] {
  font-family: var(--card-font, var(--serif));
  opacity: calc(0.62 + 0.38 * var(--card-ink, 1));
}

/* Face-specific optics (set data-card-font from applyCardStyle / ambient). */
html .haiku-card[data-card-font="display"] .haiku,
html [data-testid="poem"][data-card-font="display"] .haiku,
html [data-testid="poem"][data-card-font="display"] [data-testid="poem-lines"] {
  letter-spacing: 0.02em;
  font-weight: 500;
}
html .haiku-card[data-card-font="typewriter"] .haiku,
html [data-testid="poem"][data-card-font="typewriter"] .haiku,
html [data-testid="poem"][data-card-font="typewriter"] [data-testid="poem-lines"] {
  letter-spacing: 0.04em;
  font-size: 0.95em;
  line-height: 1.95;
}
html .haiku-card[data-card-font="brush"] .haiku,
html [data-testid="poem"][data-card-font="brush"] .haiku,
html [data-testid="poem"][data-card-font="brush"] [data-testid="poem-lines"] {
  font-style: italic;
  letter-spacing: 0.01em;
}
html .haiku-card[data-card-font="hand"] .haiku,
html [data-testid="poem"][data-card-font="hand"] .haiku,
html [data-testid="poem"][data-card-font="hand"] [data-testid="poem-lines"] {
  font-style: italic;
  letter-spacing: 0.015em;
  font-weight: 400;
}
html .haiku-card[data-card-font="mono"] .haiku,
html [data-testid="poem"][data-card-font="mono"] .haiku,
html [data-testid="poem"][data-card-font="mono"] [data-testid="poem-lines"] {
  letter-spacing: 0.02em;
  font-size: 0.92em;
}
.haiku-card[data-card-accent]::before, [data-testid="poem"][data-card-accent]::before {
  background: linear-gradient(to bottom, var(--card-accent) 0%, var(--card-accent) 82%, transparent 100%);
  box-shadow: 0 0 22px var(--card-accent);
}

/* tanzaku — tall narrow strip with columnar (top→bottom) poem text.
   `html` prefix beats app.css's falling-scene `.haiku-card` max-width
   (same cascade pattern as other card types). Real tanzaku are
   vertical-rl; Latin glyphs rotate sideways (`text-orientation: mixed`)
   so each poem line becomes one column, columns flow right→left. Scoped
   to `.haiku` / poem-lines only so byline + tags stay horizontal-tb.
   Always on (every viewport). Falling layoutCard measures offsetWidth
   post-style and just gets a narrower lane. */
html .haiku-card[data-card-type="tanzaku"] {
  max-width: 11rem;
  padding: 1.6rem 0.85rem 1.5rem 0.95rem;
}
html .haiku-card[data-card-type="tanzaku"] .haiku,
html [data-testid="poem"][data-card-type="tanzaku"] .haiku,
html [data-testid="poem"][data-card-type="tanzaku"] [data-testid="poem-lines"] {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  line-height: 2.15; /* column gutter between the 3 lines */
  letter-spacing: 0.08em; /* air between rotated glyphs down each column */
  max-height: min(22rem, 72vh);
  /* keep the strip readable when a line is long: scroll inside the poem,
     never blow the card into a square */
  overflow: hidden;
}
[data-testid="poem"][data-card-type="tanzaku"] {
  max-width: 12.5rem;
  margin-inline: auto;
  padding: 1.8rem 1.1rem 1.7rem 1.2rem;
}
[data-testid="poem"][data-card-type="tanzaku"] .haiku,
[data-testid="poem"][data-card-type="tanzaku"] [data-testid="poem-lines"] {
  line-height: 2.35;
  max-height: min(28rem, 78vh);
}

/* strip — wide low banner. See the tanzaku comment above for the `html`
   prefix's specificity rationale (same falling-scene cascade tie) and why
   the `[data-testid="poem"]` hero equivalent doesn't need it. */
html .haiku-card[data-card-type="strip"] { max-width: 44rem; padding-top: 0.9rem; padding-bottom: 0.9rem; }
.haiku-card[data-card-type="strip"] .haiku { white-space: normal; line-height: 1.7; }
[data-testid="poem"][data-card-type="strip"] { max-width: 44rem; padding-top: 0.9rem; padding-bottom: 0.9rem; }
[data-testid="poem"][data-card-type="strip"] .haiku { white-space: normal; line-height: 1.7; }

/* scroll — soft roller shadows top/bottom */
.haiku-card[data-card-type="scroll"], [data-testid="poem"][data-card-type="scroll"] {
  border-radius: 0.4rem;
  box-shadow: inset 0 14px 16px -14px rgba(0, 0, 0, 0.55), inset 0 -14px 16px -14px rgba(0, 0, 0, 0.55);
}

/* postcard — landscape, airier padding, softer corners */
html .haiku-card[data-card-type="postcard"] { max-width: 26rem; padding: 1.4rem 1.8rem 1.3rem 1.9rem; border-radius: 0.85rem; }
.haiku-card[data-card-type="postcard"] .haiku { line-height: 1.95; letter-spacing: 0.01em; }
[data-testid="poem"][data-card-type="postcard"] { max-width: 28rem; margin-inline: auto; padding: 1.5rem 2rem; border-radius: 0.85rem; }

/* seal — compact square-ish paper, denser type */
html .haiku-card[data-card-type="seal"] { max-width: 14rem; padding: 1.35rem 1.2rem 1.25rem 1.35rem; border-radius: 0.55rem; }
.haiku-card[data-card-type="seal"] .haiku { font-size: 0.95em; line-height: 1.85; }
[data-testid="poem"][data-card-type="seal"] { max-width: 16rem; margin-inline: auto; }

/* leaf — asymmetric soft corners (organic scrap of paper) */
html .haiku-card[data-card-type="leaf"] {
  max-width: 20rem;
  border-radius: 1.4rem 0.45rem 1.2rem 0.7rem;
  padding-top: 1.35rem; padding-bottom: 1.45rem;
}
.haiku-card[data-card-type="leaf"] .haiku { line-height: 2.15; }
[data-testid="poem"][data-card-type="leaf"] {
  max-width: 22rem; margin-inline: auto;
  border-radius: 1.5rem 0.5rem 1.3rem 0.75rem;
}

/* size scale (author size field + ambient feed variety) */
html .haiku-card[data-card-size="sm"] { max-width: 15rem; }
html .haiku-card[data-card-size="sm"] .haiku { font-size: 0.92em; line-height: 1.95; }
html .haiku-card[data-card-size="lg"] { max-width: 28rem; }
html .haiku-card[data-card-size="lg"] .haiku { font-size: 1.08em; line-height: 2.15; }
[data-testid="poem"][data-card-size="sm"] { max-width: 16rem; margin-inline: auto; }
[data-testid="poem"][data-card-size="lg"] { max-width: 30rem; margin-inline: auto; }

/* Tanzaku stays a strip under size scale (size rules share (0,2,1) and
   would otherwise widen the long card back into a slab). */
html .haiku-card[data-card-type="tanzaku"][data-card-size="sm"] { max-width: 9rem; }
html .haiku-card[data-card-type="tanzaku"][data-card-size="md"] { max-width: 11rem; }
html .haiku-card[data-card-type="tanzaku"][data-card-size="lg"] { max-width: 13rem; }
[data-testid="poem"][data-card-type="tanzaku"][data-card-size="sm"] { max-width: 10rem; }
[data-testid="poem"][data-card-type="tanzaku"][data-card-size="md"] { max-width: 12.5rem; }
[data-testid="poem"][data-card-type="tanzaku"][data-card-size="lg"] { max-width: 14.5rem; }

/* compose: live style preview card (a real .haiku-card so applyCardStyle +
   the card-type blocks above style it identically to the feed) */
.style-preview { margin: 1.2rem auto; pointer-events: none; }
[data-testid="style-panel"] { border: 1px solid var(--line); border-radius: 0.7rem; padding: 0.9rem; }
[data-testid="style-panel"] legend { font: 500 0.72rem/1 var(--mono); color: var(--mist); letter-spacing: 0.08em; }
[data-testid="style-panel"] .style-types { display: flex; gap: 0.4rem; flex-wrap: wrap; }
[data-testid="style-panel"] .style-types button {
  min-height: 44px; padding: 0 0.9rem; background: transparent; color: var(--mist);
  border: 1px solid var(--line); border-radius: 2rem; font: 500 0.72rem/1 var(--mono); cursor: pointer;
}
[data-testid="style-panel"] .style-types button[aria-pressed="true"] { color: var(--ink); border-color: var(--ai); box-shadow: var(--glow-ai); }

/* ---- tag alchemy (data-alchemy + --alchemy-* vars, UNDER author style) ----
   Precedence chain, spine + glow: author --card-accent > alchemy tint >
   theme --ai. Grain dims/textures the pane subtly. Neutral cards carry no
   attr/vars => zero delta from today.

   `:not([data-card-accent])` on both selectors below: the accent rule above
   is (0,2,1) and this rule is (0,2,2), so without the guard an accented +
   glowing card would resolve box-shadow here (calc'd blur radius) instead
   of the accent rule's flat `0 0 22px var(--card-accent)` -- "author accent
   wins" broken on box-shadow even though the fallback chain already makes
   the gradient resolve identically either way. Scoping this rule off
   accented cards makes the ownership explicit: the accent rule owns
   accented cards entirely (gradient + flat glow), this rule owns
   tagged-but-unaccented cards (gradient + glow that scales with
   --alchemy-glow). */
html .haiku-card[data-alchemy]:not([data-card-accent])::before,
html [data-testid="poem"][data-alchemy]:not([data-card-accent])::before {
  background: linear-gradient(
    to bottom,
    var(--card-accent, var(--alchemy-tint, var(--ai))) 0%,
    var(--card-accent, var(--alchemy-tint, var(--ai))) 82%,
    transparent 100%
  );
  box-shadow: 0 0 calc(22px + 20px * var(--alchemy-glow, 0)) var(--card-accent, var(--alchemy-tint, var(--ai)));
}
/* Remix gene patina (Task 3, own channel): a remix child's `--gene-patina`
   (0..0.35, 0.07 per generation depth, set by apply.ts on `data-patina` --
   NOT `data-alchemy`, see that file's doc comment) desaturates the whole
   card pane and folds into the grain-driven text-shadow below -- lineage
   becomes a visible, accumulating wear the deeper a remix's ancestry goes.
   `[data-patina]` is scoped separately from `[data-alchemy]` on purpose: a
   patina-only card (a remix of an untagged parent -- the common case) must
   NEVER pick up the [data-alchemy] spine rule above with none of its
   --alchemy-* vars set (that was the bug -- a flat var(--ai) gradient +
   always-on box-shadow diverging from the untagged spine). Untagged/ungened
   cards never carry `data-patina`/`--gene-patina` at all, so neither block
   below applies to them (byte-identical to pre-Task-3 rendering). */
html .haiku-card[data-patina], html [data-testid="poem"][data-patina] {
  filter: saturate(calc(1 - 0.35 * var(--gene-patina, 0)));
}
/* Plain alchemy grain (no patina involved): unchanged from Task 2. */
html .haiku-card[data-alchemy] .haiku,
html [data-testid="poem"][data-alchemy] .haiku,
html [data-testid="poem"][data-alchemy] [data-testid="poem-lines"] {
  text-shadow: 0 0 calc(26px - 10px * var(--alchemy-grain, 0)) var(--halo);
}
/* Patina (alone, or alongside data-alchemy on a tagged remix): fold
   generation wear into the same grain calc. Same specificity as the
   [data-alchemy] rule above, declared AFTER it -- for a card carrying BOTH
   attrs, source order makes this one win (verified via computed style in
   tests/test_gene_render.py). */
html .haiku-card[data-patina] .haiku,
html [data-testid="poem"][data-patina] .haiku,
html [data-testid="poem"][data-patina] [data-testid="poem-lines"] {
  text-shadow: 0 0 calc(26px - 10px * min(1, var(--alchemy-grain, 0) + var(--gene-patina, 0))) var(--halo);
}
[data-testid="style-panel"] label { display: block; margin-top: 0.7rem; font: 500 0.72rem/1.6 var(--mono); color: var(--mist); }
