@font-face {
  font-family: "Fraunces"; font-style: normal; font-weight: 400 900; font-display: swap;
  src: url(/fonts/fraunces-latin.woff2) format("woff2");
}

:root {
  --paper: #FBF3DE; --paper-2: #F3E6C6; --surface: #fffbf7;
  --ink: #221B12; --ink-soft: #6A5D48; --line: rgba(34,27,18,.14);
  /* Jamaican-flag accents (sampled): gold #f8df25, green #4e994a, red #ce3b2b.
     -deep variants darkened for legible text/edges on the light paper. */
  --palm: #299546; --palm-deep: #2F5C2C; --gold: #f3c11c; --gold-deep: #A07F0A; --hibiscus: #d23227;
  --link: var(--palm-deep); /* content hyperlinks: green in light, flag-yellow in dark */
  --em: var(--palm-deep); /* emphasised (italic) text: deep green in light, brighter green in dark */
  --sk-warm: var(--gold-deep); /* back-page poster "warm" words: brown-gold in light, bright gold in dark */
  --alpha-bg: #F3E6C6; /* A–Z index chip background (per theme) */
  --bar-ink: #FBF3DE; --bar-soft: rgba(251,243,222,.72);
  --scrim: 
    linear-gradient(
        180deg, lab(5.4% -4.66 3.03 / 0.04), 
        rgba(22,17,9,.5)  64%, 
        rgba(22,17,9,.9) 92%,
        rgba(22,17,9,1)  100%
      );
  --shadow: 0 14px 34px -18px rgba(40,28,8,.5);
  --font-display: "Fraunces", Georgia, "Times New Roman", serif;
  --font-body: Georgia, "Times New Roman", serif;
  --font-letter: "Fraunces", Georgia, serif;
  --header-h: 80px;
  /* Reading measure for body copy. Shared by .prose, .b-rt and .b-imgtext so a
     paragraph, a plain richtext block and text wrapping a floated image all break
     lines at the same width. Leads and glosses are deliberately narrower. */
  --measure: 66ch;
}
/* Dark theme, declared twice on purpose: the media query covers "OS is dark and the
   visitor hasn't chosen", the attribute covers "toggled to dark on a light-OS
   machine". The two lists MUST stay identical — they are kept byte-for-byte the same
   below so a diff catches drift.

   The :not() guard is load-bearing. Without it the media query still applies on a
   forced-light page, and every token the light theme doesn't explicitly restate
   stays dark. That is exactly how --paper-2 broke: it was absent from both
   [data-theme] blocks, so .pl-art on /music kept the wrong paper in both mismatched
   combinations. Guarding here fixes the whole class, not just that one token — light
   is now plain :root, with nothing to restate and nothing to forget. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --paper:#161109; --paper-2:#211812; --surface:#241C12; --ink:#F1E7D2; --ink-soft:#AD9E82; --line:rgba(255,255,255,.12); --link: var(--gold); --em: #7BC47B; --sk-warm: var(--gold); --alpha-bg: #242019; }
}
:root[data-theme="dark"]        { --paper:#161109; --paper-2:#211812; --surface:#241C12; --ink:#F1E7D2; --ink-soft:#AD9E82; --line:rgba(255,255,255,.12); --link: var(--gold); --em: #7BC47B; --sk-warm: var(--gold); --alpha-bg: #242019; }

/* ── Breakpoints ──────────────────────────────────────────────────────────────
   Four width values, deliberately kept to four. Add rules to an existing one
   rather than introducing a fifth.

     max-width: 640px   phones — image-block sizing, playlist rows
     max-width: 700px   phones+ — swap the hero to the smaller image file
                        *** MIRRORED IN JS: Site.astro's hero preloader calls
                        matchMedia('(max-width: 700px)') to decide which hero
                        width to prefetch. Change one, change both. ***
     min-width: 721px   desktop — the primer's two-column term layout
     max-width: 940px   collapse the nav into the hamburger drawer
                        *** MIRRORED IN JS: Site.astro's isMobile() drives the
                        transition axis, the nav pill and arrow-key nav.
                        Change one, change both. ***

   @media cannot read a custom property, and @custom-media needs a build plugin,
   so these stay as literals — adding a PostCSS dependency to deduplicate four
   numbers would cost more complexity than it saves.
   ──────────────────────────────────────────────────────────────────────────── */

* { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: var(--font-body); color: var(--ink); background: var(--paper);
  line-height: 1.55; -webkit-font-smoothing: antialiased; overflow-x: hidden; }

/* hover-to-edit overlay — only ever active when the server rendered edit mode (?edit=1 by
   a signed-in admin user), which is also the only time data-edit attributes exist.
   See src/client/editoverlay.ts and src/site/lib/edit.ts. */
.edit-box { position: absolute; z-index: 70; pointer-events: none; opacity: 0;
  border: 2px solid var(--palm); border-radius: 6px; transition: opacity .12s; }
.edit-box.on { opacity: 1; }
.edit-bar { position: absolute; top: -25px; left: -2px; display: flex; align-items: center; gap: 8px;
  padding: 2px 9px; border-radius: 6px 6px 0 0; background: var(--palm); color: #fff;
  font-family: var(--font-body); font-size: .72rem; line-height: 1.7; white-space: nowrap;
  pointer-events: auto; }
.edit-bar span { text-transform: capitalize; opacity: .85; }
.edit-bar a { color: #fff; text-decoration: none; font-weight: 600; }
.edit-bar a:hover { text-decoration: underline; }

/* draft-preview banner — only rendered when ?preview=<secret> is valid */
.previewbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: var(--hibiscus); color: #fff; text-align: center;
  font-family: var(--font-body); font-size: .82rem; letter-spacing: .02em;
  padding: 7px 14px; box-shadow: 0 -6px 18px -10px rgba(0,0,0,.6); }
.previewbar b { font-weight: 700; }

/* header — the site bar. Scoped to `body > header` deliberately: as a bare `header`
   selector this also captured <header> elements *inside* content, which is valid
   semantics (a comment's byline is an article header, not a banner). It turned every
   comment header into a fixed 80px dark navbar stacked at the top of the viewport —
   invisible to functional tests, obvious the moment anyone looked at the page. */
body > header { position: fixed; top: 0; left: 0; right: 0; z-index: 50; height: var(--header-h);
  /* Keep both radii equal — they were 8px/14px, so Safari rendered a visibly
     heavier header blur than Chrome. 12px is the agreed value for both. */
  background: rgba(20,15,9,0.7); backdrop-filter: blur(12px) saturate(1.3); -webkit-backdrop-filter: blur(12px) saturate(1.3);
  border-bottom: 1px solid rgba(255,255,255,.12); color: var(--bar-ink); font-family: var(--font-display); font-weight: 500; font-size: .92rem; letter-spacing: -.01em; }
.bar { max-width: 1120px; margin: 0 auto; padding: 0 18px; height: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.brand { display: flex; align-items: center; cursor: pointer; background: none; border: 0; color: inherit; }
.brand img { height: 60px; margin: 10px 0 10px -18px;}


nav.menu { position: relative; display: flex; gap: 4px; }
nav.menu .pill { position: absolute; top: 50%; transform: translateY(-50%); height: 34px; border-radius: 999px;
  background: var(--gold); left: 0; width: 0; z-index: 0;
  transition: left .42s cubic-bezier(.6,0,.15,1), width .42s cubic-bezier(.6,0,.15,1); }
nav.menu button { position: relative; z-index: 1; background: none; border: 0; color: var(--bar-soft); cursor: pointer;
  font-family: var(--font-display); font-weight: 500; font-size: .92rem; padding: 7px 12px; border-radius: 999px;
  white-space: nowrap; transition: color .3s; }
nav.menu button:hover { color: #fff; } nav.menu button.active { color: #2a1e05; font-weight: 600; }
nav.menu button:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.mctl { display: none; align-items: center; gap: 8px; }
.iconbtn { width: 26px; height: 26px; border: 0; background: none; padding: 0; color: var(--bar-ink); cursor: pointer; display: grid; place-items: center; }
.iconbtn svg { width: 22px; height: 22px; } .iconbtn[disabled] { opacity: .35; }
.menubtn { display: flex; align-items: center; width: auto; height: auto; gap: 7px; color: var(--bar-ink); font-family: var(--font-body); font-weight: 600; font-size: .92rem; }
.menubtn svg { width: 18px; height: 18px; }
.bar-right { display: flex; align-items: center; gap: 10px; }
.themetoggle svg { width: 20px; height: 20px; }
/* show the icon for the mode you'll switch TO: moon in light, sun in dark */
.themetoggle .sun { display: none; } .themetoggle .moon { display: block; }
/* Same :not() guard as the token blocks above, for the same reason — otherwise a
   dark-OS visitor who forces light sees the sun icon, offering to switch to the mode
   they are already in. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .themetoggle .sun { display: block; }
  :root:not([data-theme="light"]) .themetoggle .moon { display: none; }
}
:root[data-theme="dark"] .themetoggle .sun { display: block; } :root[data-theme="dark"] .themetoggle .moon { display: none; }
.drawer { position: fixed; inset: var(--header-h) 0 auto 0; z-index: 45; background: rgba(20,15,9,.9);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border-bottom: 1px solid rgba(255,255,255,.12);
  padding: 8px 12px 14px; color: var(--bar-ink); transform: translateY(-12px); opacity: 0; pointer-events: none;
  transition: opacity .28s, transform .28s; box-shadow: 0 24px 40px -20px rgba(0,0,0,.6); }
.drawer.open { transform: translateY(0); opacity: 1; pointer-events: auto; }
.drawer button { display: flex; align-items: center; width: 100%; text-align: left; background: none; border: 0;
  color: var(--bar-soft); font-family: var(--font-body); font-weight: 500; font-size: 1.05rem; padding: 12px 14px; border-radius: 12px; cursor: pointer; gap: 12px; }
.drawer button .idx { font-size: .78rem; color: var(--gold); width: 20px; font-variant-numeric: tabular-nums; }
.drawer button.active { background: var(--gold); color: #2a1e05; font-weight: 600; } .drawer button.active .idx { color: #2a1e05; }
/* theme toggle rendered as the last drawer item (mobile menu). Its sun/moon
   icon AND label swap via the shared .themetoggle .sun/.moon rules above. */
.drawer-theme { margin-top: 6px; border-top: 1px solid rgba(255,255,255,.1); border-radius: 0 0 12px 12px; }
.drawer-theme .idx { display: inline-flex; align-items: center; justify-content: center; }
.drawer-theme svg { width: 18px; height: 18px; }

/* persistent hero — image stays; only height morphs between pages; photo per page */
.hero { position: relative; height: 100vh; margin-top: 0px; overflow: hidden;
  background-image: var(--scrim), var(--pageimg); background-size: cover; background-position: var(--pagepos, center 40%);
  transition: height .4s cubic-bezier(.5,0,.15,1); }
.hero-fade { position: absolute; inset: 0; background-image: var(--scrim), var(--pageimg);
  background-size: cover; background-position: var(--pagepos, center 40%); z-index: 0; pointer-events: none; }
/* Phones get a much smaller hero file (see HERO_WIDTH_SM in src/lib/images.ts).
   Both properties are set together in JS, so the fade clone switches too. */
@media (max-width: 700px) {
  .hero, .hero-fade { background-image: var(--scrim), var(--pageimg-sm, var(--pageimg)); }
}
.hero-cap { position: absolute; left: 20px; right: 0; bottom: 0; max-width: 1120px; margin: 0 auto; padding: 26px 20px 50px 20px; color: #fff; }
.hero-cap .eyebrow { color: rgba(255,255,255,.82); }
.hero-cap .vtitle { color: #fff; }
#heroCap { z-index: 2; }
.cap-out { z-index: 1; pointer-events: none; }
.body-out { position: absolute; top: 0; left: 0; right: 0; padding: 30px 20px 90px; z-index: -1; pointer-events: none; }

.stage { max-width: 1120px; margin: 0 auto; padding: 30px 20px 90px 40px; position: relative; font-size: 1.1rem;}
.stg { will-change: transform, opacity; }
#pages { display: none; }

.eyebrow { font-family: var(--font-display); font-weight: 600; font-size: 1rem; letter-spacing: .2em; text-transform: uppercase; color: var(--gold); }
.vtitle { text-shadow:  2px 2px 4px rgba(21, 15, 4, 0.8);font-family: var(--font-display); font-weight: 600; font-size: clamp(2rem, 5vw, 3.2rem); line-height: 1.02; margin: 8px 0 0; letter-spacing: -.018em; text-wrap: balance; }
.hero[data-big] .vtitle { font-size: clamp(2.6rem, 7vw, 4.5rem); }
.lead, .letterintro { font-size: 1.24rem; color: var(--ink-soft); max-width: 60ch; font-family: var(--font-display); line-height: 1.55; margin: 0 0 30px; }
/* .lead holds rendered RichText (Body.astro), so it contains <p> and inline marks.
   Its own `margin: 0 0 30px` is the intended gap, but the browser's default 1em
   paragraph margins were being added on top of it — so the lead sat ~20px low and
   left ~50px below itself. Zero them and space the paragraphs internally instead.
   NOTE: do NOT "fix" this by adding .prose to .lead — the two rules tie on
   specificity and .prose comes later, so it would silently drop the lead from
   1.24rem/60ch to 1.1rem/66ch. */
.lead p { margin: 0 0 15px; } .lead p:last-child { margin-bottom: 0; }
.prose { font-size: 1.1rem; max-width: var(--measure); } .prose p { margin: 0 0 15px; }
.prose em, .gloss em, .letterintro em, .lead em, .cmt-note em { color: var(--em); font-style: italic; }
.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 { font-family: var(--font-display); font-weight: 600; letter-spacing: -.018em; }
.prose h2 strong, .prose h3 strong, .prose h4 strong  { font-weight: 900; color: var(--hibiscus) }
.prose ul, .prose ol { padding-left: 1.2em; } .prose li { margin: 4px 0; }
/* Flower bullets for rich-text lists, via MASK rather than list-style-image.
   The distinction matters: an external SVG used as an image (list-style-image,
   background-image, ::marker content) is loaded in its own isolated document and
   never sees this stylesheet — so it cannot inherit currentColor or a theme token,
   and flower.svg's baked-in fill:#1d140c would stay near-black and disappear on the
   dark paper. A mask uses only the file's alpha and takes its colour from
   background-color, so one file themes correctly both ways and the fill in the file
   is irrelevant.
   Scoped to ul > li so ordered lists keep their numbers. -webkit- prefix for Safari,
   same as the backdrop-filter pairs above. */
.prose ul { list-style: none; padding-left: 1.6em; }
.prose ul > li { position: relative; }
.prose ul > li::before {
  content: "";
  position: absolute;
  left: -1.5em;
  top: .4em;                     /* nudged to sit on the first line's cap height */
  width: .8em; height: .7em;     /* artwork is 55.12 x 48.13 — keep that ratio */
  background-color: var(--palm);
  -webkit-mask: url("/images/flower.svg") no-repeat center / contain;
          mask: url("/images/flower.svg") no-repeat center / contain;
}
.prose blockquote { margin: 18px 0; padding: 4px 0 4px 18px; border-left: 3px solid var(--palm); color: var(--ink-soft); font-style: italic; }
.prose a, .b-rt a, .b-txt a, .gloss a, .letterintro a, .lead a { color: var(--link); text-underline-offset: 2px; }
/* Remaining nodes the RichText editor can emit. Only <hr> is in content today (the
   Bibliography divider on /unfinished-history, which was rendering as the browser's
   default grey inset groove against the cream paper); the rest are reachable from
   the editor but unused, and styled now so they don't arrive unstyled later.
   An inline image shares .b-figure img's treatment — including drop-shadow rather
   than box-shadow, so a transparent PNG casts a shadow in its own shape — but caps
   at max-width so it can't be upscaled past its natural size or break the measure. */
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 26px 0; }
.prose img { display: block; max-width: 100%; height: auto; margin: 22px 0; border-radius: 16px;
  filter: drop-shadow(0 12px 18px rgba(40,28,8,.32)); }
.prose code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em;
  background: var(--paper-2); padding: .12em .35em; border-radius: 5px; }
.prose pre { background: var(--paper-2); padding: 14px 16px; border-radius: 12px; overflow-x: auto; }
.prose pre code { background: none; padding: 0; }


/* content blocks */
.blocks { display: flex; flex-direction: column;  }
.b-rt { max-width: var(--measure); }
.b-rt p, .b-txt { margin: 0 0 15px; }

/* text-with-audio blocks — the H2 itself is the play control, reusing the primer's
   .letterplay circle/hover/equalizer styles wholesale. A <button> doesn't inherit
   typography, so hand the heading's own font and colour back to the text inside it
   (letter-spacing separately: the `font` shorthand doesn't carry it). */
.b-taudio h2 .letterplay { font: inherit; letter-spacing: inherit; color: inherit; text-align: left; }


/* image blocks — real <img> so radius + shadow hug the image (no bg-image quirks).
   `size` is a WIDTH relative to the containing column, not a cap: the figure is
   sized by its container so 1/3, 1/2 and 2/3 genuinely differ. (It was
   `width: fit-content` + `max-width`, which made the figure shrink to the image
   and the size act only as a ceiling — so any image narrower than the cap
   ignored the setting entirely.)
   Consequence: a source narrower than its rendered width is scaled up and will
   look soft. Upload images at least as wide as they will render — roughly
   2/3 x 700px = 470px, doubled for retina. */
.b-figure { margin: 0; width: 100%; max-width: 100%; }
/* drop-shadow (not box-shadow) so the shadow follows the image's actual alpha
   shape — a transparent circular PNG casts a circular shadow, a rectangular
   photo (rounded by border-radius) casts a rounded-rectangle shadow. */
.b-figure img { display: block; width: 100%; height: auto; border-radius: 16px;
  filter: drop-shadow(0 12px 18px rgba(40,28,8,.32)); }
.b-figure figcaption { margin-top: 10px; font-size: .82rem; color: var(--ink-soft); font-style: italic; }
.b-figure.size-1-3 { width: 33.3%; } .b-figure.size-1-2 { width: 50%; }
.b-figure.size-2-3 { width: 66.6%; } .b-figure.size-full { width: 100%; }

/* text + image: float the figure so the rich text wraps around it.
   Match the regular richtext measure (.b-rt) so text lines read consistently. */
.b-imgtext { max-width: var(--measure); }
.b-imgtext::after { content: ""; display: block; clear: both; } /* contain the float */
.b-imgtext .b-figure.align-left  { float: left;  margin: 4px 26px 12px 0; }
.b-imgtext .b-figure.align-right { float: right; margin: 4px 0 12px 26px; }
.b-imgtext .b-figure.align-center { float: none; margin: 0 auto 18px; }

/* standalone image: a fraction of the column, aligned via flex */
.b-standalone.align-left  { align-self: flex-start; }
.b-standalone.align-right { align-self: flex-end; }
.b-standalone.align-center { align-self: center; }

@media (max-width: 640px) {
  /* keep the float on a phone — just make the sized images smaller so the
     wrapped text keeps enough room, and render 2/3 at the 1/2 tier. Tighten the
     side gap for the narrow column (match .align-* specificity to win). */
  .b-imgtext .b-figure.align-left  { margin-right: 16px; }
  .b-imgtext .b-figure.align-right { margin-left: 16px; }
  .b-figure.size-1-3 { width: 33%; }
  .b-figure.size-1-2 { width: 40%; }
  .b-figure.size-2-3 { width: 40%; } /* 2/3 → styled as 1/2 on mobile */
  /* size-full stays 100% */
}

/* primer */
.alpha { display: flex; flex-wrap: wrap; gap: 5px; margin: 0 0 26px; }
.alpha span { font-size: .78rem; width: 26px; height: 26px; display: grid; place-items: center; border-radius: 7px; color: var(--ink-soft); font-family: var(--font-display); }
.alpha span.has { color: var(--palm); font-weight: 600; cursor: pointer; transition: background .15s, color .15s; background-color: var(--alpha-bg); }
.alpha span.has:hover { background: var(--palm); color: #fff; }
/* letter divider: big letter + integrated play, with a rule spanning the page */
/* clear the fixed header (+ a little gap) when jumping to a letter from the A–Z strip */
.letterblock { margin-bottom: 30px; scroll-margin-top: calc(var(--header-h) + 14px); }
.letterdivide { display: flex; align-items: center; gap: 16px; margin: 52px 0 26px; }
.letterblock:first-of-type .letterdivide { margin-top: 6px; }
.letterplay { display: inline-flex; align-items: center; gap: 12px; background: none; border: 0; padding: 0; cursor: pointer; }
.letterplay .lp { width: 32px; height: 32px; border-radius: 50%; background: var(--palm); color: #fff; display: grid; place-items: center; flex: none; transition: background .18s; }
.letterplay .lp svg { width: 12px; height: 12px; }
.letterplay:hover .lp { background: var(--palm-deep); }
.letterplay:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 8px; }
.letterplay.playing .lp { background: var(--hibiscus); }
.letterplay.playing .lp svg { display: none; }
.letterglyph { font-family: var(--font-letter); font-weight: 600; font-size: 3.4rem; line-height: .82; color: var(--palm); }
.letterrule { flex: 1; height: 1px; background: var(--line); }
/* overrides the shared .lead/.letterintro sizing above; the 60ch measure is
   inherited from there rather than restated */
.letterintro { font-size: 1rem; margin: 4px 0 22px; }
.term-block { margin: 10px 0 30px; scroll-margin-top: calc(var(--header-h) + 14px); }
/* Landing flash for #term- deep links: a gold wash that holds while the smooth
   scroll arrives, then fades. rgba of --gold (#f3c11c), which both themes share;
   the box-shadow extends the wash 8px past the block so it doesn't hug the text. */
@keyframes term-hit {
  from { background: rgba(243, 193, 28, .22); box-shadow: 0 0 0 8px rgba(243, 193, 28, .22); }
  to   { background: transparent;             box-shadow: 0 0 0 8px transparent; }
}
.term-block.term-hit, .phrase-block.term-hit, .b-footnotes li.term-hit { animation: term-hit 1.4s ease-out .35s backwards; border-radius: 10px; }
/* Footnotes land clear of the fixed header, like terms and phrases. */
.b-footnotes li { scroll-margin-top: calc(var(--header-h) + 14px); }
/* Footnote references: a superscript link shouldn't push the line apart. */
.prose sup, .gloss sup, .lead sup { line-height: 0; }
.ticket { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; font-family: var(--font-display); font-size: 1.02rem; font-weight: 600; color: var(--ink);
  background: var(--surface); padding: 8px 9px 8px 15px; border-radius: 9px; border: 1px solid var(--line);
  box-shadow: 0 2px 0 var(--alpha-bg), 0 4px 12px -5px rgba(0,0,0,.35); transition: transform .12s, box-shadow .18s; }
.ticket:hover { transform: translateY(-1px); }
.ticket:hover:not(.playing):not(:active) { box-shadow: 0 2px 0 var(--palm), 0 4px 12px -5px rgba(0,0,0,.35); }
.ticket:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--alpha-bg); }
.ticket:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.ticket[data-audio=""] { opacity: .55; cursor: default; box-shadow: none; border-style: dashed; }
.ticket .play { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 7px; background: var(--palm); color: #fff; flex: none; }
.ticket .play svg { width: 11px; height: 11px; }
.ticket[data-audio=""] .play { background: var(--ink-soft); }
.ticket.playing { box-shadow: 0 2px 0 var(--hibiscus), 0 4px 14px -4px rgba(0,0,0,.4); } .ticket.playing .play { background: var(--hibiscus); }
.ticket.playing .play svg { display: none; }

/* Animated equalizer bars, shown in place of the play triangle while a clip runs.
   Shared by the letter play button and the term tickets — the markup is the same
   <span class="eq"><b><b><b> in both (Primer.astro, Ticket.astro), so the rules are
   grouped rather than written out twice. Only the parent-specific colours differ,
   and those stay with their parent above. */
.letterplay .eq, .ticket .eq { display: none; gap: 2.5px; align-items: flex-end; height: 13px; }
.letterplay .eq b, .ticket .eq b { width: 2.5px; background: #fff; height: 40%; border-radius: 2px; animation: eq .8s ease-in-out infinite; }
.letterplay .eq b:nth-child(2), .ticket .eq b:nth-child(2) { animation-delay: .15s }
.letterplay .eq b:nth-child(3), .ticket .eq b:nth-child(3) { animation-delay: .3s }
.letterplay.playing .eq, .ticket.playing .eq { display: inline-flex; }
@keyframes eq { 0%,100%{ height:30% } 50%{ height:100% } }
/* text-wrap: pretty asks the browser to avoid leaving a single word on the last line.
   Preferred over the traditional fix of replacing the final space with &nbsp;, which
   would mean post-processing rendered HTML and breaks when the last word sits inside
   <em> or an example link — both common here. Purely a hint: browsers without it just
   wrap as before, so there is nothing to fall back to. `balance` is the wrong tool —
   it is for headings (see .vtitle) and caps out at a few lines. */
.gloss { font-size: .95rem; color: var(--ink); margin: 9px 0 0; max-width: 66ch; text-wrap: pretty; }
/* The primer's desktop layout, in one place — this was two separate
   `min-width: 721px` blocks 30 lines apart. Idiom Bank phrases follow it (below). */
@media (min-width: 721px) {
  /* indent the terms under each letter heading to show hierarchy */
  .letterbody { padding-left: 46px; }
  /* definition sits to the right of the ticket for scannability */
  .term-main { display: flex; align-items: center; gap: 20px; }
  .term-main .ticket { flex: none; }
  .term-main .gloss { margin-top: 0; flex: 1; }
}
/* Idiom Bank phrases blocks — primer-style tickets with rich descriptions,
   examples nested via the same .alts treatment as a term's alternates. The
   indent variant matches .b-playlists.indent's role under a heading. */
.b-phrases { margin: 4px 0 26px; }
.b-phrases.indent { padding-left: 36px; }
@media (max-width: 640px) { .b-phrases.indent { padding-left: 18px; } }
.phrase-block { margin: 10px 0 30px; scroll-margin-top: calc(var(--header-h) + 14px); }
/* A phrase with no recording: a plain heading in the ticket's typeface.
   Deliberately a bit LARGER than the ticket's 1.02rem text — without the card
   chrome it needs the extra size to carry equal visual weight — and with no
   bottom padding, since the description's own 9px top margin provides the gap
   a boxed ticket needs padding for. */
.phrase-heading {
    font-family: var(--font-display);
    font-size: 1.2rem;
    font-weight: 600;
    color: var(--ink);
    padding: 9px 0 0 0;
}
.phrase-heading.alt { font-size: .9rem; padding: 7px 0; }
/* .gloss's look, but as a container for renderRich() output — the primer's
   .gloss is itself a <p>, so its rule never handles child-paragraph margins. */
.phrase-desc { font-size: .95rem; color: var(--ink); margin: 9px 0 0; max-width: 66ch; text-wrap: pretty; }
.phrase-desc p { margin: 0 0 8px; } .phrase-desc p:last-child { margin-bottom: 0; }
.phrase-desc em { color: var(--em); font-style: italic; }
.phrase-desc a { color: var(--link); text-underline-offset: 2px; }
.phrase-example { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
/* Optional short clip on a phrase (e.g. the cooyah mouth gesture). Kept small —
   these are seconds-long demos, not features. */
.phrase-video { display: block; margin: 12px 0 0; width: 100%; max-width: 180px; border-radius: 14px; background: #000; box-shadow: var(--shadow); }
.phrase-example .phrase-desc { margin-top: 4px; }
/* Wide screens: the phrase floats left and its description starts beside it, then wraps
   underneath at a small indent — so the text's left edge stays consistent down the page
   instead of starting wherever each phrase happens to end. The video and the examples always
   start below the phrase, at the same indent, which also covers short or missing
   descriptions. After the base rules above on purpose — same specificity, so this must come
   later to win. Phones keep the stacked layout. */
@media (min-width: 721px) {
  /* flow-root contains the float, so the next phrase never tucks up beside this one. */
  .phrase-main { display: flow-root; padding-left: 22px; }
  .phrase-main > .ticket, .phrase-main > .phrase-heading { float: left; margin: 0 18px 8px -22px; }
  /* Centre the description's first line on the ticket (≈44px tall; a line is ≈24px). */
  .phrase-text > .phrase-desc:first-child { margin-top: 10px; }
  .phrase-main > .phrase-heading + .phrase-text > .phrase-desc:first-child { margin-top: 11px; }
  .phrase-text > .phrase-video, .phrase-text > .alts { clear: left; }
  .phrase-text > .alts { margin-left: 0; }

  /* An example's own description still sits to the right of its (smaller) ticket. */
  .phrase-example { flex-direction: row; align-items: baseline; gap: 14px; }
  .phrase-example > .ticket, .phrase-example > .phrase-heading { flex: none; }
  .phrase-example > .phrase-desc { margin-top: 0; flex: 1; min-width: 0; }
}

.alts { margin: 12px 0 0 20px; padding-left: 18px; border-left: 2px solid var(--line); display: flex; flex-direction: column; gap: 9px; }
.alts-label { font-family: var(--font-display); font-size: .66rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft); }
.ticket.alt { font-size: .9rem; padding: 6px 7px 6px 12px; box-shadow: 0 2px 0 var(--alpha-bg), 0 3px 9px -5px rgba(0,0,0,.3); }
.ticket.alt .play { width: 22px; height: 22px; background: var(--gold-deep); border-radius: 6px; } .ticket.alt .play svg { width: 9px; height: 9px; }
.ticket.alt.playing { box-shadow: 0 2px 0 var(--hibiscus), 0 3px 10px -4px rgba(0,0,0,.35); } .ticket.alt.playing .play { background: var(--hibiscus); }
.altrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* The "Example" music-video links (a.yt in Primer.astro) need no rule of their own:
   they only ever appear inside p.gloss, and the grouped link rule above already
   styles .gloss a with higher specificity. */

/* playlists */
/* gallery blocks — a square-thumbnail grid; the full image opens in the shared
   lightbox (dialog.glight below) with the caption in a dedicated bar beneath
   the photo, never overlaid on it. Several galleries can share one page. */
.b-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; max-width: var(--measure); margin: 4px 0 26px; }
.g-thumb { aspect-ratio: 1; overflow: hidden; padding: 0; border: 1px solid var(--line); border-radius: 12px; background: var(--paper-2); cursor: zoom-in; }
.g-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .18s; }
.g-thumb:hover img { transform: scale(1.04); }
.g-thumb:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
/* Subscribe block: a sub-footer card. --alpha-bg and --line are per-theme tokens, so it reads
   as a gentle panel on the paper in light mode and a slightly lifted one in dark mode. */
.b-subscribe { margin: 40px 0 24px; max-width: 760px; padding: 26px 28px 22px;
  background: var(--alpha-bg); border: 1px solid var(--line); border-radius: 18px; }
.b-subscribe .cmt-form { gap: 12px; }
.subscribe-heading { margin: 0; font-family: var(--font-display); font-size: 1.45rem; font-weight: 700;
  line-height: 1.2; color: var(--ink); }
.subscribe-intro { margin: -4px 0 2px; font-size: 1rem; line-height: 1.45; color: var(--ink-soft); text-wrap: pretty; }
/* Email and button on one row; the button keeps its size, the field takes the rest. */
.subscribe-row { display: flex; gap: 10px; align-items: stretch; }
.subscribe-row .cmt-field { flex: 1; min-width: 0; }
.subscribe-row .cmt-submit { flex: none; }
.b-subscribe .cmt-status:empty { display: none; }
/* The card is the frame, so the ALTCHA check sits on it without its own box. */
.b-subscribe .cmt-altcha .altcha-main { background: none; border: 0; }
@media (max-width: 640px) {
  .b-subscribe { padding: 20px 18px 18px; border-radius: 14px; }
  .subscribe-row { flex-direction: column; }
}
/* Visually hidden but read by screen readers (e.g. a label the placeholder already covers). */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
/* Hover caption that follows the pointer (computers only — see site.ts). Positioned with
   transform from the top-left corner so moving it never triggers layout. */
.g-tip { position: fixed; top: 0; left: 0; z-index: 60; pointer-events: none; max-width: 280px;
  padding: 6px 10px; border-radius: 8px; background: rgba(20,15,9,.9); color: #FBF3DE;
  font-family: var(--font-body); font-size: .82rem; line-height: 1.35;
  box-shadow: 0 6px 18px -8px rgba(0,0,0,.5); opacity: 0; transition: opacity .12s; }
.g-tip.on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .g-tip { transition: none; } }

/* The lightbox <dialog> (built once by site.ts, appended to <body>). Theme
   tokens keep it correct in both modes; the caption bar hides when empty. */
dialog.glight { border: 0; padding: 0; border-radius: 16px; background: var(--surface); color: var(--ink); max-width: min(92vw, 960px); box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .55); }
dialog.glight::backdrop { background: rgba(12, 9, 4, .78); }
.glight figure { margin: 0; }
.glight img { display: block; max-width: min(92vw, 960px); max-height: 72vh; width: auto; height: auto; margin: 0 auto; }
/* The caption bar doubles as the control row: prev | caption | next. It stays
   visible on uncaptioned photos so the arrows keep their home, and only
   disappears for a single uncaptioned photo (set from site.ts via [hidden]). */
.glight-bar { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-top: 1px solid var(--line); }
.glight-bar[hidden] { display: none; }
.glight-cap { flex: 1; min-width: 0; font-family: var(--font-body); font-size: .95rem; line-height: 1.45; color: var(--ink); text-align: center; }
.glight-x { position: absolute; top: 10px; right: 10px; display: grid; place-items: center; width: 36px; height: 36px; border-radius: 50%; border: 0; cursor: pointer; background: rgba(20, 15, 9, .55); color: #fff; transition: background .18s; }
.glight-prev, .glight-next { flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; border: 0; cursor: pointer; background: var(--palm); color: #fff; transition: background .18s; }
.glight-x svg, .glight-prev svg, .glight-next svg { width: 16px; height: 16px; display: block; }
.glight-prev[hidden], .glight-next[hidden] { display: none; }
.glight-prev:disabled, .glight-next:disabled { opacity: .25; cursor: default; }
.glight-x:hover, .glight-prev:hover:not(:disabled), .glight-next:hover:not(:disabled) { background: var(--palm-deep); }
.glight-x:hover { background: var(--palm); }
.glight-x:focus-visible, .glight-prev:focus-visible, .glight-next:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* Term / phrase peek (client/peek.ts): a link to a primer term or Idiom Bank phrase opens a copy
   of that entry here, so the reader keeps their place. The page's own paper behind it, so the
   tickets read exactly as they do on the page. The body scrolls for long descriptions and many
   examples; the heading and the "See it in …" link stay put. The page behind doesn't scroll.
   The hairline border keeps its edge visible in dark mode, where paper meets dimmed paper. */
dialog.peek { border: 1px solid var(--line); padding: 0; border-radius: 16px; background: var(--paper); color: var(--ink);
  width: min(100vw - 32px, 640px); max-height: min(100dvh - 48px, 760px); box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .55); }
dialog.peek[open] { display: flex; flex-direction: column; }
dialog.peek::backdrop { background: rgba(12, 9, 4, .62); }
html:has(dialog.peek[open]) { overflow: hidden; }
.peek-head { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 12px 4px 24px; }
.peek-kicker { font-family: var(--font-display); font-size: .7rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft); }
.peek-x { flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; border: 0; cursor: pointer; background: var(--paper-2); color: var(--ink); transition: background .18s, color .18s; }
.peek-x svg { width: 15px; height: 15px; display: block; }
.peek-x:hover { background: var(--palm); color: #fff; }
.peek-x:focus-visible, .peek-go:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
/* .stage's type size, which the entry's rules are written against. */
.peek-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 8px 24px 22px; font-size: 1.1rem; }
.peek-body > .term-block, .peek-body > .phrase-block { margin: 0; }
.peek-foot { flex: none; padding: 12px 24px 14px; border-top: 1px solid var(--line); text-align: right; }
.peek-go { font-family: var(--font-display); font-size: .95rem; font-weight: 600; color: var(--link); text-underline-offset: 3px; border-radius: 4px; }
@media (max-width: 640px) {
  .peek-head { padding-left: 18px; }
  .peek-body { padding: 6px 18px 18px; }
  .peek-foot { padding: 12px 18px 14px; }
}

/* A playlists block on a blocks page (including /music, which is one of these
   now). The indent variant nudges a group right so it reads as belonging to a
   heading in the Text block above it. */
.b-playlists { margin: 4px 0 26px; }
.b-playlists.indent { padding-left: 36px; }
@media (max-width: 640px) { .b-playlists.indent { padding-left: 18px; } }

.pl-list { display: flex; flex-direction: column; gap: 16px; }
.pl-row { display: flex; align-items: center; gap: 22px; padding: 16px 18px; border-radius: 18px; background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow); cursor: pointer; transition: transform .16s, box-shadow .22s; text-decoration: none; color: inherit; }
.pl-row:hover { transform: translateY(-2px); box-shadow: 0 22px 46px -22px rgba(40,28,8,.6); }
.pl-art { width: 118px; height: 118px; border-radius: 12px; flex: none; background-color: var(--paper-2); background-size: cover; background-position: center; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); overflow: hidden; }
.pl-meta { flex: 1; min-width: 0; }
.pl-num { font-size: .7rem; letter-spacing: .16em; text-transform: uppercase; color: var(--gold-deep); font-weight: 700; }
.pl-name { font-family: var(--font-display); font-weight: 600; font-size: 1.7rem; letter-spacing: -.01em; margin: 3px 0 6px; color: var(--ink); }
.pl-desc { color: var(--ink-soft); font-size: .96rem; max-width: 54ch; }
.pl-play { flex: none; display: inline-flex; align-items: center; gap: 9px; color: var(--palm); font-weight: 700; font-size: .76rem; text-transform: uppercase; letter-spacing: .09em; }
.pl-play .ic { width: 42px; height: 42px; border-radius: 50%; border: 1.5px solid var(--palm); display: grid; place-items: center; font-size: .75rem; padding-left: 2px; transition: background .18s, color .18s; }
.pl-row:hover .pl-play .ic { background: var(--palm); color: #fff; }
@media (max-width: 640px) { .pl-row { gap: 15px; padding: 13px; } .pl-art { width: 78px; height: 78px; } .pl-name { font-size: 1.32rem; } .pl-play .txt { display: none; } }

/* Skankin' Sweet — the back-page lyric poster. Markup lives in a CMS block-html
   block; these classes give it an on-brand, theme-aware, responsive treatment.
   Word sizes use em so they scale with the responsive base font-size. */
.lyric { max-width: 42ch; margin: 0; font-family: var(--font-body);
  font-size: clamp(1.05rem, 1.1vw + .82rem, 1.35rem); line-height: 1.5; color: var(--ink); }
.lyric p { margin: 0 0 .55em; }
.lyric .lyric-label { margin: 1.5em 0 .5em; color: var(--palm); font-family: var(--font-display);
  font-weight: 600; font-size: .82em; letter-spacing: .06em; }
.lyric-hi { background: var(--gold); color: #221B12; padding: 0 .28em; border-radius: 4px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone; }
.lyric-big { font-family: var(--font-display); font-weight: 700; font-size: 1.7em; line-height: .9; color: var(--sk-warm); }
.lyric-serif-big { font-family: var(--font-display); font-style: italic; font-weight: 600; font-size: 1.9em; color: var(--hibiscus); }
.lyric-faint { color: var(--ink-soft); font-size: .82em; }
.sk-split { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .3em 1.2em; }
.lyric-aside { font-style: italic; color: var(--em); }
.lyric-script { font-family: var(--font-display); font-style: italic; font-weight: 600; font-size: 2.1em; line-height: 1; color: var(--sk-warm); }
.lyric-huge { font-family: var(--font-display); font-weight: 600; font-size: clamp(1.9rem, 6vw, 3rem);
  line-height: 1.02; color: var(--em); margin: .35em 0 .55em; }
.lyric-red { color: var(--hibiscus); font-weight: 600; }
.lyric-shout { color: var(--hibiscus); font-weight: 800; letter-spacing: .02em; }
.lyric-ul { font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.lyric-indent { padding-left: clamp(1rem, 6vw, 3.5rem); }
footer { border-top: 1px solid var(--line); padding: 22px 20px 40px; color: var(--ink-soft); font-size: .82rem; max-width: 1120px; margin: 0 auto;
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
/* Unlisted pages (showInMenu off) live here — see footerLinks in Site.astro. These
   are buttons with data-go, not links, so they navigate through the same
   client-side transition as the menu instead of reloading the page. */
.footlinks { display: flex; gap: 14px; }
.footlinks button, footer a { background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--link); font-family: var(--font-body); font-size: .82rem;
  text-decoration: underline; text-underline-offset: 2px; }
.footlinks button:hover { color: var(--ink); }
.footlinks button:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 4px; }

/* ── comments & contact ────────────────────────────────────────────────────────
   Both pages share this block: the form markup is nearly identical, and the only
   real difference is that Contact has no thread. Class names are deliberately
   prefixed `cmt-` and there are NO ids anywhere, because every section renders
   twice (once into hidden #pages, once into #stage) and an id would be duplicated
   in the document. */
.cmt-wrap { max-width: var(--measure); }
.cmt-list { display: flex; flex-direction: column; gap: 18px; }
.cmt { background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
  padding: 16px 18px; box-shadow: var(--shadow); }
.cmt-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }
.cmt-who { font-family: var(--font-display); font-weight: 600; font-size: 1.02rem; color: var(--ink); }
.cmt-when { font-size: .78rem; color: var(--ink-soft); }
.cmt-body { font-size: 1rem; }
/* Bare element selectors: the body's <p>/<br> are generated by renderComment from
   plain text, so there is no class to hang these on. */
.cmt-body p { margin: 0 0 10px; }
.cmt-body p:last-child { margin-bottom: 0; }
.cmt-none { color: var(--ink-soft); font-style: italic; }

.cmt-morewrap { margin-top: 20px; }
.cmt-more { background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: 9px 20px; cursor: pointer; color: var(--ink); background-color: var(--surface);
  font-family: var(--font-display); font-weight: 600; font-size: .92rem;
  transition: border-color .18s, transform .12s; }
.cmt-more:hover:not(:disabled) { border-color: var(--palm); transform: translateY(-1px); }
.cmt-more:disabled { opacity: .55; cursor: default; }
.cmt-more:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

.cmt-formwrap { max-width: var(--measure); margin-top: 42px; }
.cmt-formtitle { font-family: var(--font-display); font-weight: 600; font-size: 1.5rem;
  letter-spacing: -.018em; margin: 0 0 6px; }
.cmt-note { color: var(--ink-soft); font-size: .92rem; margin: 0 0 18px; }
.cmt-form { display: flex; flex-direction: column; gap: 14px; }
/* Two-up on desktop (name + email on the contact form), stacking on phones via the
   640px breakpoint below. */
.cmt-row { display: flex; gap: 14px; }
.cmt-row .cmt-field { flex: 1; min-width: 0; }
.cmt-field { display: flex; flex-direction: column; gap: 5px; }
.cmt-label { font-family: var(--font-display); font-weight: 600; font-size: .86rem; color: var(--ink); }
.cmt-optional { font-weight: 400; color: var(--ink-soft); }
.cmt-input { font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; width: 100%; transition: border-color .18s; }
.cmt-input:focus { outline: none; border-color: var(--palm); }
.cmt-input:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.cmt-textarea { resize: vertical; min-height: 96px; line-height: 1.5; }

/* Honeypot. Positioned off-screen rather than display:none or type=hidden on
   purpose: an automated form-filler should see an ordinary text input and fill it,
   while a person sees nothing. Removing it from the layout entirely is what makes
   many bots skip it. */
.cmt-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Let the proof-of-work widget inherit the site's palette instead of shipping its
   own look. It renders in a shadow root, so these custom properties are the
   supported way in. */
.cmt-altcha {
  --altcha-color-base: var(--surface);
  --altcha-color-base-content: var(--ink);
  --altcha-color-border: var(--line);
  --altcha-border-color: var(--line);
  --altcha-color-primary: var(--palm);
  --altcha-color-primary-content: #fff;
  --altcha-color-error: var(--hibiscus);
  --altcha-color-success: var(--palm);
  --altcha-border-radius: 10px;
  --altcha-max-width: 100%;
  font-family: var(--font-body);
  font-size: .86rem;
}
/* One row instead of two: checkbox and label, then "Protected by ALTCHA" beside the logo.
   The widget renders in the light DOM; its first child (checkbox + logo) is flattened so
   the footer can sit between them. Anything else it adds (errors) takes a row of its own. */
.cmt-altcha .altcha-main { flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: .6rem; }
.cmt-altcha .altcha-main > :first-child { display: contents; }
.cmt-altcha .altcha-checkbox-wrap { flex: 1 1 auto; order: 0; }
.cmt-altcha .altcha-footer { flex: 0 0 auto; order: 1; width: auto; padding-top: 0; }
.cmt-altcha .altcha-main > :first-child > :last-child { order: 2; width: auto; }
.cmt-altcha .altcha-main > :not(:first-child):not(.altcha-footer) { order: 3; flex-basis: 100%; }

.cmt-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.cmt-submit { font-family: var(--font-display); font-weight: 600; font-size: .98rem;
  color: #fff; background: var(--palm); border: 0; border-radius: 999px;
  padding: 11px 24px; cursor: pointer; transition: background .18s, transform .12s; }
.cmt-submit:hover:not(:disabled) { background: var(--palm-deep); transform: translateY(-1px); }
.cmt-submit:disabled { opacity: .6; cursor: default; }
.cmt-submit:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.cmt-status { margin: 0; font-size: .9rem; color: var(--ink-soft); }
.cmt-status.cmt-ok { color: var(--palm-deep); font-weight: 600; }
.cmt-status.cmt-error { color: var(--hibiscus); font-weight: 600; }

@media (max-width: 640px) {
  .cmt-row { flex-direction: column; }
  .cmt-formwrap { margin-top: 32px; }
}

@media (max-width: 940px) { nav.menu { display: none; } .mctl { display: flex; } #themeToggle { display: none; } }
