/* ════════════════════════════════════════════════════════════════
   THEME: kaiunta update august 7 0241
   Quiet editorial look, ported from kaiunta.com: warm paper, single
   italic monospace voice, hairline rules instead of thick borders,
   pill buttons instead of hard drop shadows.

   Two parts:
   1. Palette + font tokens (these alone are enough for basic reskin).
   2. Shape overrides for the highest-impact components — header, hero,
      buttons, cards, tabs, modals — so the comic "sticker" shape
      language (thick borders, hard offset shadows, ALL CAPS) is
      replaced with kaiunta's flat/hairline/italic language too.
      This section doesn't touch every selector in index.php, just
      the ones that define most of what a person actually sees.
   ════════════════════════════════════════════════════════════════ */

/* ── 1. Palette + fonts ── */
html[data-theme="kaiunta"] {
  --paper:      #f7f4ee;
  --ink:        #1a1a1a;
  --ink-mid:    #444;
  --ink-faint:  #888;
  --rule:       #e0ddd6;
  --red:        #c0392b;   /* kaiunta's one accent — used sparingly */
  --yellow:     #eee8dc;   /* no bright yellow in kaiunta; soft warm wash instead */
  --black:      #1a1a1a;   /* "ink" instead of true black */
  --surface:    #f7f4ee;   /* flat — cards sit on the same paper, no white panels */
  --on-accent:  #f7f4ee;   /* paper-colored text on ink-filled buttons */
  --icon-fill:  rgba(26,26,26,.07); /* header/footer icon-button fill (bell, burger, theme toggle, footer nav icons) — replaces the old 1px hairline-border look */
  --notif-unread-bg: #fdf6f5; /* faint red-tinted wash for unread notification cards, see html[data-theme="kaiunta"] .notif-item.unread far below */

  /* kaiunta only ever uses one family (Courier Prime, italic for display) —
     point all three theme fonts at it. Italic/weight is applied per
     component below since font-family alone can't carry font-style. */
  --font-display: 'Courier Prime', monospace;
  --font-mono:    'Courier Prime', monospace;
  --font-accent:  'Courier Prime', monospace;

  /* kaiunta.com's header is 64px tall vs this app's 56px default */
  --hdr-h: 64px;
}

/* ── 1b. Dark variant ──
   Opt-in via data-kai-dark="1" on <html> (footer toggle next to the theme
   selector — see #footerKaiDarkToggle in theme.js, same pattern as
   terminal's data-term-invert toggle). This is NOT a literal color invert:
   kaiunta's palette has more going on than a flat two-tone terminal screen
   (a dedicated accent red, several ink tints, a soft warm wash), so
   flipping RGB values would muddy the red toward cyan and throw off
   contrast everywhere. Instead every token below is a hand-picked dark
   counterpart that keeps the same warm, paper-like feel — just a dark
   sheet of paper with light ink instead of a light sheet with dark ink.
   Every rule elsewhere in this file reads these tokens via var(...), so
   this block is the only place dark mode needs to be defined; nothing
   below needs a [data-kai-dark] variant of its own — except the one
   hardcoded hex this file has (.notif-item.unread's background), which is
   why that got pulled into --notif-unread-bg above instead of staying
   inline. */
html[data-theme="kaiunta"][data-kai-dark="1"] {
  --paper:      #18140f;
  --ink:        #ece6d8;
  --ink-mid:    #b6ad9c;
  --ink-faint:  #7d7566;
  --rule:       #332d24;
  --red:        #e2604f;   /* brightened from the light-mode #c0392b so it still hits AA contrast on a dark paper */
  --yellow:     #2a2318;   /* the "soft warm wash" becomes a dark warm wash instead of a light one */
  --black:      #ece6d8;   /* kaiunta uses --black to mean "ink", i.e. the highest-contrast tone — that's now the light one */
  --surface:    #18140f;   /* stays flat with --paper, same as light mode */
  --on-accent:  #18140f;   /* text sitting on ink-filled buttons — ink-filled now means light-filled, so this needs to be dark */
  --icon-fill:  rgba(236,230,216,.08);
  --notif-unread-bg: rgba(226,96,79,.14);
}

/* Mobile drag/bounce: the base stylesheet already sets overscroll-behavior-y:
   none on html/body site-wide, which is what actually suppresses iOS
   Safari's rubber-band bounce (not just scroll-chaining to a parent). This
   kaiunta-only rule used to override that with the weaker "contain" value
   — which stops chaining but leaves the bounce/drag itself in place —
   so it was silently undoing the fix for this theme only. Match "none"
   here instead so kaiunta gets the same non-draggy behavior as the rest
   of the site. */
html[data-theme="kaiunta"],
html[data-theme="kaiunta"] body {
  overscroll-behavior-y: none;
}

/* ── 2. Shape overrides ── */

/* Header: thick red bar → thin hairline on paper */
html[data-theme="kaiunta"] .hdr {
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  padding: 0 2rem;
}
html[data-theme="kaiunta"] .hdr-logo {
  color: var(--ink);
  font-style: normal;
  letter-spacing: -0.02em;
  font-size: .95rem;
  text-transform: lowercase;

}
/* kaiunta's only "mark" — no illustrated logo (see conversation: the
   justwitter mascot's thick-outline comic style doesn't belong in this
   theme's flat/hairline language, and a bare wordmark reads confident
   on its own). This one red slash before the wordmark is the sole
   exception — styled as a tuning notch, not punctuation, so it carries
   a little of the station/tuning idea without becoming an icon. */
html[data-theme="kaiunta"] .hdr-logo::before {
  content: "/";
  display: inline-block;
  color: var(--red);
  margin-right: .3em;
  font-style: normal;
}
html[data-theme="kaiunta"] .hdr-user { color: var(--ink-mid); }
/* Was a transparent, hairline-bordered pill (border:1px solid var(--rule),
   background:none) — kaiunta's own version of the thin "outline" look
   every theme used for these buttons. Now a soft filled circle instead
   (--icon-fill, an ink-tinted translucent wash defined in this theme's
   root block below), matching justwitter's equivalent restyle and the
   rest of kaiunta's own header/footer icon buttons. */
html[data-theme="kaiunta"] .hdr-btn {
  height: 34px;
  padding: 0 1rem;
  border: none;
  border-radius: 999px;
  background: var(--icon-fill);
  color: var(--ink-mid);
  letter-spacing: .12em;
  text-transform: lowercase;
  font-size: .72rem;
  transition: background .15s, color .15s;
}
html[data-theme="kaiunta"] .hdr-btn:hover { background: var(--ink); color: var(--paper); }
html[data-theme="kaiunta"] .hdr-btn-primary {
  height: 36px;
  padding: 0 1.25rem;
  border: 1px solid var(--ink);
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
}
html[data-theme="kaiunta"] .hdr-btn-primary:hover { background: var(--ink); color: var(--paper); opacity: .8; }

/* The header's search icon just opens #viewSearch and focuses its input —
   but kaiunta's own Voices/Stations/Following layout already puts a
   "search voices..." box front-and-center in the main content, so this
   is a straight duplicate once that view is showing (same reasoning as
   #menuGuestCtas being hidden here once the header already covers login/
   signup — see renderHeader()'s comment on that). */
html[data-theme="kaiunta"] #btnSearchHeader {
  display: none;
}
/* The markup hardcodes an arrow into the button label ("START WITTERING→").
   kaiunta's quieter voice drops it — swap in a text override rather than
   editing the shared HTML, so other themes keep the arrow untouched. */
html[data-theme="kaiunta"] #btnHdrSignup {
  font-size: 0;
}
html[data-theme="kaiunta"] #btnHdrSignup::before {
  content: "start wittering";
  font-size: .72rem;
}

/* Mobile: kaiunta's lowercase Courier Prime runs noticeably wider per
   character than justwitter's condensed all-caps Bangers, so at phone
   widths "log in" / "start wittering" wrap to two lines inside their
   pills and the logo — squeezed for space by the now-taller buttons —
   overflows underneath them. Nothing here changes layout structure,
   just tightens sizing enough for everything to fit on one line. */
@media (max-width: 480px) {
  html[data-theme="kaiunta"] .hdr,
  html[data-theme="kaiunta"] .hdr * {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }
  html[data-theme="kaiunta"] .hdr { padding: 0 .75rem; }
  html[data-theme="kaiunta"] .hdr-actions { gap: .3rem; }
  /* Matches kaiunta.com's .hdr-brand, which stays .95rem at every width —
     no mobile-specific shrink there. This used to drop to .7rem and cap
     at 26vw to make room for text CTA pills at phone widths, but the
     header buttons are icon-only 34px circles at all sizes now (see
     #btnHdrLogin/#btnHdrSignup below), so there's real room next to them —
     the fixed 26vw cap was truncating "just.witter" even when it fit.
     overflow/ellipsis stay on as a genuine safety net for very long
     future site names on very narrow screens, without an arbitrary cap. */
  html[data-theme="kaiunta"] .hdr-logo {
    font-size: .95rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex-shrink: 1;
  }
  /* NOTE: previously this block forced min-height:0 on #viewAuth at mobile
     widths, reasoning that the base .view rule's min-height:100dvh left a
     "shorter login card centered in a mostly-empty screen-height box."
     But that centering (align-items/justify-content:center, set on
     #viewAuth itself) is the intended behavior — a full-height box with
     the card centered, extra space split evenly above/below, is the
     normal clean pattern for a login screen. Killing the height instead
     left the card pinned right under the header with all that same
     leftover space now dumped entirely below it as one lopsided gap
     before the fixed footer bar — worse, not better. Reverted; #viewAuth
     is left to the base centered behavior on kaiunta at every width. */
}
/* Header always shows icon-only log in / start wittering — desktop and
   mobile alike, not gated behind the ?hdrmode dev toggle or a max-width
   breakpoint. The burger used to be footer-only by design here ("no
   burger anywhere in the header; the footer's hamburger is the only
   entry point to #viewMenu") — that's since been swapped: the burger now
   lives in the header (#btnHdrBurger, styled by the base .hdr-burger
   rule in index.php) and the theme toggle took its old spot in the
   footer instead (#footerThemeToggle). No override needed here anymore;
   kaiunta's header burger just uses the shared base style. */
html[data-theme="kaiunta"] #btnHdrLogin,
html[data-theme="kaiunta"] #btnHdrSignup {
  width: 34px;
  height: 34px;
  padding: 0;
  font-size: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
html[data-theme="kaiunta"] #btnHdrLogin::before,
html[data-theme="kaiunta"] #btnHdrSignup::before {
  content: "";
  display: block;
  width: 15px;
  height: 15px;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
html[data-theme="kaiunta"] #btnHdrLogin::before {
  background-color: var(--ink-mid);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 20c0-4 4-6 8-6s8 2 8 6'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 20c0-4 4-6 8-6s8 2 8 6'/%3E%3C/svg%3E");
}
html[data-theme="kaiunta"] #btnHdrSignup::before {
  background-color: var(--paper);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
}

html[data-theme="kaiunta"] .hdr-bell {
  background: var(--icon-fill);
  color: var(--ink-mid);
  font-size: 0; /* hide the color emoji glyph — a flat mask icon replaces it below */
}
html[data-theme="kaiunta"] .hdr-bell:hover { background: var(--ink); }
/* The bell is a literal 🔔 emoji character, not an SVG/icon-font glyph, so
   it renders with the OS's built-in color emoji font and ignores CSS
   `color` entirely — no property on the glyph itself can flatten it to
   kaiunta's ink palette (a grayscale filter only dims the gold, it never
   reads as a real ink/paper icon). Hiding the glyph and drawing the bell
   as a CSS mask instead gives a flat silhouette that colors — and inverts
   on hover — like every other icon in this theme. */
html[data-theme="kaiunta"] .hdr-bell::before {
  content: "";
  display: block;
  width: 15px;
  height: 15px;
  background-color: var(--ink-mid);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9'/%3E%3Cpath d='M13.73 21a2 2 0 0 1-3.46 0'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9'/%3E%3Cpath d='M13.73 21a2 2 0 0 1-3.46 0'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: background-color .15s;
}
html[data-theme="kaiunta"] .hdr-bell:hover::before { background-color: var(--paper); }

/* Feed/Stations/Search page titles: visually redundant in kaiunta — Feed's
   duplicates the active tab label right above it, and Stations'/Search's
   are already covered by their search input's placeholder ("search
   stations...", "search voices..."). Hidden visually only (not display:none)
   so they stay in the accessibility tree as the page's heading for screen
   reader users, who don't have the placeholder-as-context shortcut. Profile
   isn't included — it has no equivalent placeholder doing that job. */
html[data-theme="kaiunta"] #feedTitle,
html[data-theme="kaiunta"] #stationsTitle,
html[data-theme="kaiunta"] #searchTitle {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Stations subtitle ("every station that's posted a voice, most recently
   active first") explains the list's sort order — unlike the redundant
   titles above, sighted users have no other visual cue for this, so hiding
   it is a real (if minor) omission for them. It stays in the a11y tree
   because screen reader users, who can't glance at the list to infer
   sort order the way sighted users can, benefit from it more, not less. */
html[data-theme="kaiunta"] #stationsSubtitle {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Profile page's "PROFILE" heading, same redundant story as feed/stations/
   search above (duplicates the avatar+handle right below it) — hidden the
   same way. Scoped to #viewProfile specifically since .feed-title is
   shared with THREAD/NOTIFICATIONS, which haven't been asked about. */
html[data-theme="kaiunta"] #viewProfile .feed-title {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
html[data-theme="kaiunta"] .hdr-bell:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* Hero: bangers' animated ambient slogan → kaiunta's quiet editorial hero
   (eyebrow, headline, one line of copy, CTAs baked into the hero itself —
   the only theme where the hero is actionable). */
html[data-theme="kaiunta"] .hero {
  background: var(--paper);
  align-items: flex-start;
  justify-content: center;
  text-align: left;
  padding: calc(var(--hdr-h) + 2rem) clamp(1.5rem, 8vw, 6rem) 5rem;
}
html[data-theme="kaiunta"] .hero::before { display: none; }

html[data-theme="kaiunta"] .hero-bangers { display: none; }

html[data-theme="kaiunta"] .hero-kaiunta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 1.5rem;
  max-width: 820px;
  margin: 0;
}
html[data-theme="kaiunta"] .hero-eyebrow {
  font-family: var(--font-mono);
  font-style: normal;
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: lowercase;
  color: var(--ink-faint);
  margin: 0;
}
html[data-theme="kaiunta"] .hero-kai-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -.03em;
  line-height: 1.15;
  font-size: clamp(1.9rem, 4.2vw, 3.4rem);
  color: var(--ink);
  margin: 0;
}
html[data-theme="kaiunta"] .hero-kai-copy {
  font-family: var(--font-mono);
  font-style: italic;
  color: var(--ink-mid);
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  line-height: 1.8;
  max-width: 58ch;
  margin: 0;
}
html[data-theme="kaiunta"] .hero-kai-ctas {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  margin-top: .25rem;
}
html[data-theme="kaiunta"] .hero-kai-ctas .btn-primary {
  width: auto;
  height: 48px;
  padding: 0 1.75rem;
  font-size: .78rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5em;
}
html[data-theme="kaiunta"] .hero-kai-login {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-mono);
  font-style: italic;
  font-size: .9rem;
  color: var(--ink-mid);
  cursor: pointer;
  transition: color .15s;
  display: inline-flex;
  align-items: center;
  gap: .4em;
}
html[data-theme="kaiunta"] .hero-kai-login:hover { color: var(--ink); }
/* Icons inherit each button's own text color via currentColor, so they
   track hover/mobile-restyle states for free instead of needing separate
   color rules per state. */
html[data-theme="kaiunta"] .hero-kai-cta-icon {
  flex-shrink: 0;
}

/* Large/5K displays (27"+ iMac range and similar): every hero measurement
   above is a clamp() that tops out around normal-desktop widths — title
   caps at 3.4rem, copy at 1.15rem, the whole column at 820px — because
   vw-based clamps are anchored to typical laptop/desktop viewports, not
   a 2560px-plus logical viewport (5K's actual CSS pixel width once you
   account for the display's 2x pixel density). Below that ceiling the
   hero was designed to feel quiet and editorial; above it, the exact
   same numbers just read as small text stranded in empty paper. This
   tier only raises the ceilings — same font, same weights, same
   left-aligned layout, same animation — it doesn't restructure anything,
   so every rule above still applies unchanged below 1800px. */
@media (min-width: 1800px) {
  html[data-theme="kaiunta"] .hero-kaiunta {
    max-width: 1180px;
    gap: 2rem;
  }
  html[data-theme="kaiunta"] .hero-eyebrow {
    font-size: .85rem;
  }
  html[data-theme="kaiunta"] .hero-kai-title {
    font-size: clamp(3.4rem, 4.8vw, 5.6rem);
  }
  html[data-theme="kaiunta"] .hero-kai-copy {
    font-size: 1.35rem;
    max-width: 62ch;
  }
  html[data-theme="kaiunta"] .hero-kai-ctas .btn-primary {
    height: 56px;
    padding: 0 2.25rem;
    font-size: .88rem;
  }
  html[data-theme="kaiunta"] .hero-kai-login {
    font-size: 1.02rem;
  }

  /* Giant background watermark, X-style, but built from the site's own
     mark — the same red "/" + lowercase wordmark pairing used in
     .hdr-logo — instead of a generic logo, so it reads as "this brand,
     scaled up." (A first pass used just the bare "/" blown up to 48vw;
     at that size an italic slash stops reading as a character and just
     becomes an abstract diagonal band, which is why this version is
     smaller and paired with the "j.v.i" abbreviation for legibility.)
     Two pseudo-elements, not one string, so the slash can carry the
     header's --red while "j.v.i" stays --ink, matching .hdr-logo::before
     exactly. They're aligned using ch units rather than two independent
     vw offsets: Courier Prime is a monospace face (see --font-display
     above), so 1ch reliably equals the width of any glyph at a given
     font-size, including "/" — that's what lets the slash sit locked
     directly against the "j" at every viewport width instead of the gap
     between them drifting as vw changes. Sits on #viewHero (already
     position:relative — .hero-kai-media is absolutely positioned inside
     it a few rules up), at z-index:0, so both sit behind .hero-kaiunta's
     z-index:1 text stack automatically. Only shown when the hero isn't
     already displaying an actual video/image backdrop — those are real
     media and don't need a decorative mark competing with them; this is
     purely for the plain "none"/"wave"/"mark" modes, the ones that read
     as empty on very wide screens. #viewHero's ID gives both pseudo-
     elements enough specificity to beat the earlier ".hero::before {
     display:none }" rule near the top of this file without needing
     !important. */
  html[data-theme="kaiunta"] #viewHero:not([data-hero-media="video"]):not([data-hero-media="image"])::after {
    content: "j.v.i";
    position: absolute;
    top: 50%;
    right: 3vw;
    transform: translateY(-50%);
    font-family: var(--font-display);
    font-style: italic;
    font-size: 11vw;
    line-height: 1;
    color: var(--ink);
    opacity: .05;
    z-index: 0;
    pointer-events: none;
    user-select: none;
    white-space: nowrap;
  }
  html[data-theme="kaiunta"] #viewHero:not([data-hero-media="video"]):not([data-hero-media="image"])::before {
    content: "/";
    position: absolute;
    top: 50%;
    /* "j.v.i" is 5 characters; this locks the slash directly against
       the "j" regardless of the 3vw right-offset above. */
    right: calc(3vw + 5ch);
    transform: translateY(-50%);
    font-family: var(--font-display);
    font-style: italic;
    font-size: 11vw;
    line-height: 1;
    color: var(--red);
    opacity: .16;
    z-index: 0;
    pointer-events: none;
    user-select: none;
  }
}

/* Hero CTAs: primary pill + two text links sit side by side on desktop,
   but once there are three of them (start wittering / log in / go to
   feed) that row wraps awkwardly well before phone width — anywhere
   from small laptops down to phones. Stack them at a wider breakpoint
   than the header's own 480px cutoff above, same left edge as the copy.
   Placed after the desktop .hero-kai-ctas .btn-primary rule above (not
   up near the header's 480px block) so these overrides actually win the
   cascade — same selector + equal specificity means source order decides,
   and the desktop padding/pill rule would otherwise clobber this. */
@media (max-width: 640px) {
  html[data-theme="kaiunta"] .hero-kai-ctas {
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }
  /* start wittering drops the pill treatment here too, matching log in /
     go to feed's plain-text style rather than standing out as a filled
     button once everything's stacked — same left edge, same voice.
     letter-spacing and box-shadow also need resetting here: the base
     .btn-primary rule sets letter-spacing:.15em and a 6px hard drop-
     shadow, and neither was in this override list — both were still
     leaking through even with background/border stripped, making
     "start wittering" render visibly larger/heavier than the plain
     "log in →" / "go to feed →" links beside it. */
  html[data-theme="kaiunta"] .hero-kai-ctas .btn-primary {
    width: auto;
    height: auto;
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    margin: 0;
    align-self: flex-start;
    text-align: left;
    color: var(--ink);
    font-family: var(--font-mono);
    font-style: italic;
    font-weight: 400;
    font-size: .9rem;
    letter-spacing: normal;
    display: inline-flex;
    align-items: center;
    gap: .4em;
  }
  html[data-theme="kaiunta"] .hero-kai-ctas .btn-primary::after {
    content: "→";
    margin-left: .35em;
  }
  /* .hero-kai-login buttons pick up base button UA/shared styles (centered
     text, possible min-width) that read fine inline on desktop but leave
     "log in →" / "go to feed →" visually indented once stacked — force
     them flush to the same left edge as the copy and pill above. */
  html[data-theme="kaiunta"] .hero-kai-login {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    width: auto;
    min-width: 0;
    text-align: left;
    align-self: flex-start;
    margin: 0;
  }
}


/* Quiet fade-up entrance, staggered eyebrow → title → copy, in keeping
   with kaiunta's understated editorial pacing (vs. justwitter's animated
   ambient hero, which is untouched — this only targets .hero-kaiunta's
   children). */
html[data-theme="kaiunta"] .hero-eyebrow,
html[data-theme="kaiunta"] .hero-kai-title,
html[data-theme="kaiunta"] .hero-kai-copy,
html[data-theme="kaiunta"] .hero-kai-ctas {
  opacity: 0;
  transform: translateY(.6em);
  animation: kaiuntaHeroIn .8s ease forwards;
}
html[data-theme="kaiunta"] .hero-eyebrow  { animation-delay: .1s; }
html[data-theme="kaiunta"] .hero-kai-title { animation-delay: .25s; }
html[data-theme="kaiunta"] .hero-kai-copy  { animation-delay: .45s; }
html[data-theme="kaiunta"] .hero-kai-ctas  { animation-delay: .65s; }

@keyframes kaiuntaHeroIn {
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  html[data-theme="kaiunta"] .hero-eyebrow,
  html[data-theme="kaiunta"] .hero-kai-title,
  html[data-theme="kaiunta"] .hero-kai-copy,
  html[data-theme="kaiunta"] .hero-kai-ctas {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

/* Hero waveform: the "alive" element in place of an image/video, per the
   ink-on-paper constraint. A thin equalizer strip pinned to the very
   bottom edge of the hero — pure CSS, low-opacity --ink, never touching
   the copy above it. Leans into the voices/radio idea as motion, not
   decoration. */
html[data-theme="kaiunta"] #viewHero[data-hero-media="wave"] .hero-kai-wave {
  display: flex;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 44px;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2px;
  padding: 0 clamp(1.5rem, 8vw, 6rem) .5rem;
  pointer-events: none;
  z-index: 0;
}
html[data-theme="kaiunta"] .hero-kai-wave span {
  flex: 1 1 0;
  max-width: 3px;
  min-height: 4px;
  height: 4px;
  border-radius: 1px;
  background: var(--ink);
  opacity: .1;
  animation: kaiuntaWave 2.2s ease-in-out infinite;
  animation-delay: calc(var(--i) * -0.09s);
}
/* Slight duration variance per bar so the strip pulses organically
   instead of scrolling as one uniform sine wave. */
html[data-theme="kaiunta"] .hero-kai-wave span:nth-child(3n) { animation-duration: 1.7s; }
html[data-theme="kaiunta"] .hero-kai-wave span:nth-child(5n) { animation-duration: 2.6s; }
html[data-theme="kaiunta"] .hero-kai-wave span:nth-child(7n) { animation-duration: 1.4s; }

@keyframes kaiuntaWave {
  0%, 100% { height: 4px; opacity: .08; }
  50%      { height: 34px; opacity: .16; }
}

@media (prefers-reduced-motion: reduce) {
  html[data-theme="kaiunta"] .hero-kai-wave span {
    animation: none;
    height: 12px;
    opacity: .1;
  }
}

/* Keep the strip clear of the CTAs on short/mobile viewports rather than
   letting it run underneath them. */
@media (max-width: 480px) {
  html[data-theme="kaiunta"] .hero-kai-wave { height: 32px; }
}

/* ── Hero background media (video / image options) ──
   Both share one layer, absolutely filling .hero behind the copy.
   Only the option selected by #viewHero's data-hero-media attribute
   actually renders — see the base .hero-kai-media{display:none} rule
   and the video/image {display:none} rules below, each flipped on by
   its own attribute selector.

   Legibility approach: rather than a flat dark scrim across the whole
   thing (which would fight kaiunta's light-paper voice everywhere,
   not just where the text sits), the scrim is a left-to-right gradient
   — solid paper behind the eyebrow/title/copy block, fading to fully
   transparent past it so the media actually reads as media on the
   right/lower part of the hero. .hero-kaiunta gets z-index:1 so the
   text stack sits above the media+scrim regardless of DOM order. */
html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-media,
html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kai-media {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}
html[data-theme="kaiunta"] .hero-kai-media-video,
html[data-theme="kaiunta"] .hero-kai-media-image {
  display: none; /* only the matching option below turns its element on */
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-media-video {
  display: block;
}
html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kai-media-image {
  display: block;
}
html[data-theme="kaiunta"] .hero-kai-media-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    var(--paper) 0%,
    var(--paper) 34%,
    rgba(247, 244, 238, .82) 50%,
    rgba(247, 244, 238, .35) 68%,
    rgba(247, 244, 238, 0) 85%
  );
}

/* Sound toggle: hidden by default, only shown for the "video" option —
   there's nothing to mute/unmute on image, mark, or wave. Sits over the
   visible (unscrimmed) part of the video, bottom-right, with its own
   solid-ish paper background since it needs to stay legible over
   whatever's actually playing behind it. */
html[data-theme="kaiunta"] .hero-kai-mute {
  display: none;
}
html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-mute {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  position: absolute;
  right: clamp(1rem, 3vw, 2rem);
  /* .hero is min-height:100vh, so a small bottom offset here lands right
     underneath the fixed, opaque .site-footer (z-index:99) — same issue
     as the waveform earlier. Clear the footer's own height first, then
     add breathing room on top of that. */
  bottom: calc(var(--ftr-h) + clamp(1rem, 3vw, 2rem));
  z-index: 2;
  height: 38px;
  padding: 0 1rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: rgba(247, 244, 238, .88);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: var(--ink-mid);
  font-family: var(--font-mono);
  font-style: italic;
  font-size: .72rem;
  letter-spacing: .04em;
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-mute:hover {
  border-color: var(--ink);
  color: var(--ink);
}
html[data-theme="kaiunta"] .hero-kai-mute-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  /* Muted (default) glyph: speaker with an X. */
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='11 5 6 9 2 9 2 15 6 15 11 19 11 5'/%3E%3Cline x1='23' y1='9' x2='17' y2='15'/%3E%3Cline x1='17' y1='9' x2='23' y2='15'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='11 5 6 9 2 9 2 15 6 15 11 19 11 5'/%3E%3Cline x1='23' y1='9' x2='17' y2='15'/%3E%3Cline x1='17' y1='9' x2='23' y2='15'/%3E%3C/svg%3E");
}
/* Unmuted glyph: speaker with sound waves. Swaps in via aria-pressed,
   which the JS toggle sets to match the video's actual muted state. */
html[data-theme="kaiunta"] .hero-kai-mute[aria-pressed="true"] .hero-kai-mute-icon {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='11 5 6 9 2 9 2 15 6 15 11 19 11 5'/%3E%3Cpath d='M15.54 8.46a5 5 0 0 1 0 7.07'/%3E%3Cpath d='M18.36 5.64a9 9 0 0 1 0 12.73'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='11 5 6 9 2 9 2 15 6 15 11 19 11 5'/%3E%3Cpath d='M15.54 8.46a5 5 0 0 1 0 7.07'/%3E%3Cpath d='M18.36 5.64a9 9 0 0 1 0 12.73'/%3E%3C/svg%3E");
}
/* Mobile: the media's scrim already flips to top-to-bottom so the video
   only shows through low in the hero — keep the button in that same
   visible band rather than crowding it against the very bottom edge. */
@media (max-width: 640px) {
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-mute {
    right: 1rem;
    bottom: calc(var(--ftr-h) + 1rem);
    font-size: .68rem;
    padding: 0 .85rem;
  }
}
/* Portrait phones only: .hero itself is now position:fixed and already
   clipped to bottom:var(--ftr-h) (see the shared mobile-portrait fix in
   index.php's base <style> block) — its own bottom edge sits right at
   the footer already, so this button's "bottom" offset no longer needs
   to add var(--ftr-h) on top of that (same fix as justwitter.css's own
   .hero-kai-mute). Left as-is above for any width where .hero ISN'T
   fixed (e.g. landscape). */
@media (max-width: 640px) and (orientation: portrait) {
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-mute {
    bottom: 1rem;
  }
}
/* Text stack needs to sit above the media layer; it's a plain flex child
   with no z-index today, which is fine when there's nothing behind it,
   but now needs to explicitly win the stacking order. */
html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kaiunta,
html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kaiunta {
  position: relative;
  z-index: 1;
}

/* Mobile: rather than giving each text group its own opaque paper chip
   (the old approach — read as a stack of disconnected pills, and broke
   the "same hero as desktop" feel), the video itself is darkened via
   the scrim below and the text switches to light/paper-toned color here.
   One continuous, cohesive block over dimmed footage, matching desktop's
   confidence instead of floating patches. */
@media (max-width: 640px) {
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kaiunta,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kaiunta {
    width: 100%;
  }
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-eyebrow,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-eyebrow {
    color: rgba(247, 244, 238, .78);
  }
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-title,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kai-title {
    color: var(--paper);
  }
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-copy,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kai-copy {
    color: rgba(247, 244, 238, .88);
  }
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-login,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kai-login,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-ctas .btn-primary,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kai-ctas .btn-primary {
    color: var(--paper);
  }
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-login:hover,
  html[data-theme="kaiunta"] #viewHero[data-hero-media="image"] .hero-kai-login:hover {
    color: var(--red);
  }
}

/* Reduced motion: don't play the video at all — swap to the still poster
   frame instead. (CSS can't pause a <video>'s playback attribute, so this
   hides the video element outright and shows the image in its place,
   reusing the same asset the video's poster= points at is recommended.) */
@media (prefers-reduced-motion: reduce) {
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-media-video {
    display: none;
  }
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"] .hero-kai-media-image {
    display: block;
  }
}

/* Mobile: same video→still swap as the reduced-motion rule above, for a
   different reason — narrow screens don't have room for a full hero
   paragraph, two CTAs, AND legible motion at once without one of them
   losing. Video is a desktop-only feature by default; phones get the
   still frame instead (lighter — no autoplay decode cost, no battery
   hit) UNLESS #viewHero carries data-mobile-video="on" (set by the
   $mobile_hero_video flag in index.php), in which case mobile plays
   the same video as desktop and none of the rules below apply.
   When the flag is off, the static image is hidden too, not just the
   video — no fallback photo, just plain paper background. The JS pause
   alongside this actually stops playback/decoding, not just hides the
   element, since a display:none video can still keep running. */
@media (max-width: 640px) {
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"]:not([data-mobile-video="on"]) .hero-kai-media-video {
    display: none;
  }
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"]:not([data-mobile-video="on"]) .hero-kai-media-image {
    display: none;
  }
  /* Nothing to unmute once there's no video playing. */
  html[data-theme="kaiunta"] #viewHero[data-hero-media="video"]:not([data-mobile-video="on"]) .hero-kai-mute {
    display: none;
  }
}

/* Mobile: this used to swap the scrim to a top-to-bottom gradient that
   hid the video under solid paper everywhere except a fixed band near
   the bottom (see the removed rule this replaces). That made sense when
   mobile only ever showed video "low in the hero" — but now that each
   text group (eyebrow/title/copy/CTAs) gets its own solid marker patch
   for legibility, the scrim's job is done by those patches instead.
   Going fully transparent, though, made the video read as raw full-
   contrast footage — a different, punchier style than desktop, where
   the gradient never fades completely to 0 (max ~85% before clear) so
   there's always a light paper tint bleeding through even where the
   video's "visible." A flat, light wash here matches that same muted,
   ink-on-paper feel instead of an untinted video pasted on top. */
@media (max-width: 640px) {
  html[data-theme="kaiunta"] .hero-kai-media-scrim {
    background: linear-gradient(
      180deg,
      rgba(15, 13, 10, .78) 0%,
      rgba(15, 13, 10, .5) 45%,
      rgba(15, 13, 10, .72) 100%
    );
  }
}

/* ── Hero mark (reused mic icon, blown up as a faint watermark) ──
   Same SVG data URI as .footer-feed-btn::before / .menu-nav-icon-voices
   — deliberately not a new asset, so this option can't introduce a
   visual style the rest of the theme doesn't already use. Sits large
   and low-opacity in the hero's lower-right, behind the copy. */
html[data-theme="kaiunta"] #viewHero[data-hero-media="mark"] .hero-kai-mark {
  display: block;
  position: absolute;
  right: clamp(-2rem, 4vw, 3rem);
  bottom: clamp(-1rem, 4vw, 2rem);
  width: clamp(260px, 34vw, 480px);
  height: clamp(260px, 34vw, 480px);
  background-color: var(--ink);
  opacity: .05;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z'/%3E%3Cpath d='M19 10v2a7 7 0 0 1-14 0v-2'/%3E%3Cline x1='12' y1='19' x2='12' y2='23'/%3E%3Cline x1='8' y1='23' x2='16' y2='23'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z'/%3E%3Cpath d='M19 10v2a7 7 0 0 1-14 0v-2'/%3E%3Cline x1='12' y1='19' x2='12' y2='23'/%3E%3Cline x1='8' y1='23' x2='16' y2='23'/%3E%3C/svg%3E") center / contain no-repeat;
  pointer-events: none;
  z-index: 0;
}
html[data-theme="kaiunta"] #viewHero[data-hero-media="mark"] .hero-kaiunta {
  position: relative;
  z-index: 1;
}
/* Keep it from crowding the copy at narrow widths rather than shrinking
   it down to an odd in-between size. */
@media (max-width: 640px) {
  html[data-theme="kaiunta"] #viewHero[data-hero-media="mark"] .hero-kai-mark {
    width: 55vw;
    height: 55vw;
    right: -6vw;
    bottom: -4vw;
    opacity: .045;
  }
  /* Was a font/gap-shrinking hack to force .cast-stats's 6-8 items to
     fit on one line at narrow widths (pin/delete/report used to live
     here and were the usual overflow culprit). Those three now live
     behind the .cast-more (⋯) menu instead (see index.php), so the
     remaining row — date · like · replies · share · bookmark — fits
     comfortably at the base theme's normal sizing and this override
     is no longer needed. */
}


/* Duration reads at the same weight as everything else in the play row
   right now — but unlike like/play counts (social-proof noise), it's the
   one number that actually helps someone decide whether to commit before
   hitting play. Worth keeping, worth quieting down rather than cutting. */
html[data-theme="kaiunta"] .cast-duration {
  color: var(--ink-faint);
  font-size: .72rem;
}

html[data-theme="kaiunta"] .btn-primary,
html[data-theme="kaiunta"] .btn-ghost {
  height: 52px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  box-shadow: none;
  letter-spacing: .12em;
  font-style: normal;
  text-transform: lowercase;
  font-size: .72rem;
  transition: opacity .15s, transform .12s;
}
html[data-theme="kaiunta"] .btn-primary { background: var(--ink); color: var(--paper); }
html[data-theme="kaiunta"] .btn-primary:hover,
html[data-theme="kaiunta"] .btn-ghost:hover { transform: none; opacity: .8; box-shadow: none; }
html[data-theme="kaiunta"] .btn-primary:active,
html[data-theme="kaiunta"] .btn-ghost:active { transform: scale(.97); box-shadow: none; }
html[data-theme="kaiunta"] .btn-ghost { background: none; color: var(--ink-mid); }
html[data-theme="kaiunta"] .btn-ghost:hover { border-color: var(--ink); color: var(--ink); background: none; }

/* Cards: white panel + thick border + drop shadow → hairline only.
   --rule on --surface (both effectively --paper) was reading as almost
   no boundary at all, especially on mobile where cards stack directly
   on top of each other with no white panel to separate them. Keep the
   flat, quiet surface (no white panel, no comic drop-shadow) but give
   the edge enough contrast to actually read as a card: a slightly
   darker ink-based border plus a soft, low-opacity shadow for depth. */
html[data-theme="kaiunta"] .auth-card,
html[data-theme="kaiunta"] .record-card,
html[data-theme="kaiunta"] .cast-card,
html[data-theme="kaiunta"] .modal,
html[data-theme="kaiunta"] .profile-header,
html[data-theme="kaiunta"] .notif-item,
html[data-theme="kaiunta"] .search-user {
  border: 1px solid rgba(26, 26, 26, .14);
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(26, 26, 26, .06);
  background: var(--surface);
}
html[data-theme="kaiunta"] .notif-item:active,
html[data-theme="kaiunta"] .search-user:active { transform: none; box-shadow: none; }
/* Extra breathing room between feed cards on small screens — with only
   a hairline border and no white panel to separate cards, the base
   theme's 1.1rem gap read as too tight once the boundary itself got
   harder to see at phone widths. */
@media (max-width: 480px) {
  html[data-theme="kaiunta"] .feed-list { gap: 1.5rem; }
}
html[data-theme="kaiunta"] .notif-item.unread { border-color: var(--red); background: var(--notif-unread-bg); }
html[data-theme="kaiunta"] .auth-title,
html[data-theme="kaiunta"] .record-title {
  color: var(--ink);
  font-style: italic;
  letter-spacing: -.01em;
}
html[data-theme="kaiunta"] .profile-lock-card { box-shadow: none; border-radius: 8px; }

/* Modal titles get programmatically focused on open (see enterDialog() in
   the JS) so they're accessible to screen readers — the base theme gives
   this a thick 3px hard-edged ring to match its comic sticker language
   (see .record-title:focus-visible in index-theme.php). That's too loud
   for kaiunta's hairline/quiet-editorial voice, so swap in a thin ring
   with more breathing room instead of just inheriting the base weight. */
html[data-theme="kaiunta"] .record-title:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 6px;
  border-radius: 0;
}

/* Record modal's tags label: base theme sets it bold/uppercase mono,
   which reads as the same "comic loud" register kaiunta rejects elsewhere.
   Drop the weight and caps, keep it lowercase and quieter — the "#" stays
   red, same sparing accent-only-on-a-symbol rule as .tag-pill below. */
html[data-theme="kaiunta"] .record-caption-label {
  font-weight: 400;
  text-transform: none;
  font-style: italic;
  letter-spacing: 0;
  color: var(--ink-mid);
}
html[data-theme="kaiunta"] .record-caption-label .tag-accent {
  font-weight: 400;
}

/* Station tags: base theme's pill is comic-styled (thick black border,
   solid fill) — bring it down to kaiunta's hairline language, same
   treatment .btn-ghost already gets. The "#" stays red — same sparing
   accent-only-on-a-symbol move as .hdr-logo::before's tuning-notch slash,
   not a full line of alarm-red text. */
html[data-theme="kaiunta"] .tag-pill {
  border: 1px solid var(--rule);
  background: none;
  color: var(--ink-mid);
  font-style: italic;
}

/* Avatars: the fill colors already theme correctly since AVATAR_COLORS in
   the JS is built from var(--yellow)/var(--red)/var(--black)/var(--on-accent)
   — but the base 3px solid border is the one piece that doesn't come from
   a token, so it still reads as kaiunta's rejected "thick border" language.
   Bring it down to the same hairline as everything else. */
html[data-theme="kaiunta"] .cast-avatar,
html[data-theme="kaiunta"] .profile-avatar {
  border: 1px solid var(--rule);
}

/* #btnReply's markup hardcodes "● " before the label (same pattern as
   #btnHdrSignup's "→" above) — kaiunta drops the dot. Note the element
   also carries an inline style="...font-size:.9rem" in the shared HTML,
   and inline styles beat stylesheet rules regardless of selector, so
   font-size:0 needs !important here to actually win — #btnHdrSignup didn't
   need this since that button has no inline font-size to fight. */
html[data-theme="kaiunta"] #btnReply {
  font-size: 0 !important;
}
html[data-theme="kaiunta"] #btnReply::before {
  content: "reply to this voice";
  font-size: .9rem;
}

/* Profile page's "PROFILE" heading is the one .feed-title without its own
   id (feed/stations/search all had ids and got hidden above), so it slipped
   through with the base theme's hard offset shadow and comic sizing. */
html[data-theme="kaiunta"] .feed-title {
  font-family: var(--font-mono);
  font-style: italic;
  font-size: 1.3rem;
  letter-spacing: -.01em;
  color: var(--ink);
  text-shadow: none;
}

/* Follow/share/report buttons — and upgrade-account/save-tags on the
   profile page — all carry a hardcoded inline box-shadow in the shared JS
   (justwitter wants it) that beats ordinary CSS specificity — !important
   is the only way to flatten it for kaiunta without touching markup
   shared with the other theme. */
html[data-theme="kaiunta"] #btnFollow,
html[data-theme="kaiunta"] #btnShareStation,
html[data-theme="kaiunta"] #btnReportUser,
html[data-theme="kaiunta"] #btnUpgradeAccount,
html[data-theme="kaiunta"] #btnSaveTags,
html[data-theme="kaiunta"] #btnCopyToken,
html[data-theme="kaiunta"] #btnCopyProfileUrl {
  box-shadow: none !important;
}

/* Auth panel "switch" buttons (← back, ← back to recovery code, forgot
   password?, log in with username & password) have no styling in the base
   sheet — they were falling through to native button chrome, which is why
   they rendered as a bordered pill instead of kaiunta's usual quiet text
   link. Give them the same ink-mid → ink treatment as footer/header links. */
html[data-theme="kaiunta"] .auth-switch {
  background: none;
  border: none;
  padding: .3rem 0;
  width: auto;
  box-shadow: none;
  color: var(--ink-mid);
  font-family: var(--font-mono);
  font-style: normal;
  font-size: .78rem;
  letter-spacing: .04em;
  text-transform: lowercase;
  text-decoration: none;
  cursor: pointer;
  transition: color .15s;
}
html[data-theme="kaiunta"] .auth-switch:hover {
  background: none;
  color: var(--ink);
  text-decoration: underline;
}

/* Slide-out menu's "← close" had no override — still justwitter's larger,
   wide-tracked, solid-black scale. Bring it down to the same quiet text-link
   register as .auth-switch. */
html[data-theme="kaiunta"] .section-back {
  font-family: var(--font-mono);
  font-size: .78rem;
  letter-spacing: .04em;
  color: var(--ink-mid);
}
html[data-theme="kaiunta"] .section-back:hover {
  opacity: 1;
  color: var(--ink);
}

/* Tabs / pills: comic outline + all-black active → understated */
html[data-theme="kaiunta"] .auth-tab,
html[data-theme="kaiunta"] .feed-tab {
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--ink-mid);
}
html[data-theme="kaiunta"] .auth-tab.active { background: var(--ink); color: var(--paper); }
html[data-theme="kaiunta"] .feed-tab.active { background: var(--ink); color: var(--paper); }

/* Legal sub-nav (terms/privacy/report abuse) had no kaiunta override, so it
   was still rendering justwitter's thick-border hard-shadow sticker
   buttons — bring it in line with the auth-tab/feed-tab hairline pill. */
html[data-theme="kaiunta"] .legal-subnav-btn {
  font-family: var(--font-mono);
  font-style: normal;
  font-size: .78rem;
  letter-spacing: .04em;
  text-transform: lowercase;
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--ink-mid);
  box-shadow: none;
  padding: .5em 1.1em;
}
html[data-theme="kaiunta"] .legal-subnav-btn:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--ink);
  color: var(--ink);
}
html[data-theme="kaiunta"] .legal-subnav-btn:active { transform: none; box-shadow: none; }
html[data-theme="kaiunta"] .legal-subnav-btn.active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* Station tuner ("FM dial"): justwitter builds this as a literal radio-dial
   metaphor — thick black/red frequency bar, hard shadows, yellow sticker
   readout. That skeuomorphic language has no equivalent in kaiunta's quiet
   editorial voice, so this is a real redesign rather than a strip-down:
   a hairline track with faint ink ticks, and a plain pill readout. */
html[data-theme="kaiunta"] .stations-dial-readout {
  font-family: var(--font-mono);
  font-style: italic;
  font-size: .8rem;
  letter-spacing: 0;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  box-shadow: none;
}
html[data-theme="kaiunta"] .stations-dial-readout:hover,
html[data-theme="kaiunta"] .stations-dial-readout:active {
  transform: none;
  box-shadow: none;
  border-color: var(--ink);
}
html[data-theme="kaiunta"] .stations-dial-range::-webkit-slider-runnable-track {
  height: 2px;
  border-radius: 1px;
  border: none;
  background:
    repeating-linear-gradient(90deg, var(--ink-faint) 0 1px, transparent 1px 4%),
    var(--rule);
}
html[data-theme="kaiunta"] .stations-dial-range::-moz-range-track {
  height: 2px;
  border-radius: 1px;
  border: none;
  background:
    repeating-linear-gradient(90deg, var(--ink-faint) 0 1px, transparent 1px 4%),
    var(--rule);
}
html[data-theme="kaiunta"] .stations-dial-range::-webkit-slider-thumb {
  width: 3px;
  height: 18px;
  margin-top: -8px;
  background: var(--ink);
  border: none;
  border-radius: 1px;
  box-shadow: none;
}
html[data-theme="kaiunta"] .stations-dial-range::-moz-range-thumb {
  width: 3px;
  height: 18px;
  background: var(--ink);
  border: none;
  border-radius: 1px;
  box-shadow: none;
}

/* Readout used to just sit statically centered above the track regardless
   of where the thumb was — reads as if it's describing "the middle" when
   it's actually always describing whatever the thumb is on. Pull it out
   of normal flow and let JS (positionDialReadout) place it directly above
   the thumb instead, same as a native slider tooltip would track it. */
html[data-theme="kaiunta"] .stations-dial {
  position: relative;
  padding-top: 2.25rem;
}
html[data-theme="kaiunta"] .stations-dial-readout {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
}

/* Record / FAB circles: thick border → hairline. The FAB is a single,
   persistent floating element competing against the whole page for
   attention, so it keeps kaiunta's red — the one place "used sparingly"
   actually calls for it. record-circle (inside the record modal, alongside
   other ink buttons) and cast-play (repeats on every card in the feed)
   would either clash or spam the accent, so those stay on ink. */
html[data-theme="kaiunta"] .record-fab {
  border: 1px solid var(--black);
  box-shadow: none;
}
/* .record-fab is position:fixed, so it always sits in the same spot on
   screen no matter what's scrolled underneath — with a short feed (or
   near the end of a long one) it lands directly on top of a card's
   action row, clipping "share"/flag. Reserve real space at the bottom of
   the scrollable list so content never renders fully behind it. */
html[data-theme="kaiunta"] .feed-list,
html[data-theme="kaiunta"] #threadList {
  padding-bottom: 6rem;
}
html[data-theme="kaiunta"] .record-circle,
html[data-theme="kaiunta"] .cast-play {
  background: var(--ink);
  border: 1px solid var(--ink);
  box-shadow: none;
}
html[data-theme="kaiunta"] .record-circle.recording { background: var(--yellow); color: var(--ink); }

/* Back/close nav circle: solid red sticker → kaiunta.com's overlay-close
   treatment (hairline border, transparent, ink-mid arrow, inverts on hover) */
html[data-theme="kaiunta"] .feed-back {
  background: none;
  border: 1px solid var(--rule);
  box-shadow: none;
  color: var(--ink-mid);
  transition: background .15s, border-color .15s, color .15s;
}
html[data-theme="kaiunta"] .feed-back:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* #feedBack and #stationsBack both only ever go to viewFeed or viewHero —
   exactly what tapping the logo (#hdrLogo) already does. #feedBack at
   least hides itself once logged in (feed is home base then), but
   #stationsBack never hid at all — always present, always redundant with
   the logo, for both logged-in and logged-out visitors. Rather than fix
   the inconsistency by making stationsBack match feedBack's conditional
   hide, cut both: the logo already covers this, and voices/stations/
   following read as peer tabs of one destination, not a chain you need
   a dedicated "back" step out of. !important because #feedBack's
   visibility is also toggled inline by JS based on login state. */
html[data-theme="kaiunta"] #feedBack,
html[data-theme="kaiunta"] #stationsBack {
  display: none !important;
}

/* Stations toolbar had its own top padding (.75rem, from the base theme)
   stacked on top of .feed-header's margin-bottom — feed's search row has
   no such padding of its own, so stations' search box sat noticeably
   further from the back button/tabs than feed's did. Drop it to zero so
   the gap matches. Its 3px solid black divider was also never converted
   to the hairline language used everywhere else — bring it in line too. */
html[data-theme="kaiunta"] .stations-toolbar {
  padding-top: 0;
  border-bottom: 1px solid var(--rule);
}

/* Inputs / search / scan: hard shadow → hairline */
html[data-theme="kaiunta"] .auth-field input,
html[data-theme="kaiunta"] .feed-search,
html[data-theme="kaiunta"] .feed-scan-btn,
html[data-theme="kaiunta"] .search-box input,
html[data-theme="kaiunta"] .record-caption,
html[data-theme="kaiunta"] .modal select,
html[data-theme="kaiunta"] .modal textarea,
html[data-theme="kaiunta"] .profile-edit textarea {
  border: 1px solid var(--rule);
  box-shadow: none;
}
html[data-theme="kaiunta"] .auth-field input:focus,
html[data-theme="kaiunta"] .record-caption:focus { box-shadow: none; border-color: var(--ink); }

/* #tagsInput ("up to 3 tags...") and #profileShareUrlInput (the visible
   profile-link field) were left out of the block above — both set their
   own thick black border + sharp radius via an inline style="" attribute
   in the shared markup, and inline styles beat any stylesheet selector
   regardless of specificity, so the hairline rule above never actually
   reached them. Same fix as #anonTokenInput's font-size earlier: match
   with !important. */
html[data-theme="kaiunta"] #tagsInput,
html[data-theme="kaiunta"] #profileShareUrlInput {
  border: 1px solid var(--rule) !important;
  border-radius: 8px !important;
  background: var(--surface) !important;
  color: var(--ink-mid);
}
html[data-theme="kaiunta"] #tagsInput:focus,
html[data-theme="kaiunta"] #profileShareUrlInput:focus {
  outline: none;
  border-color: var(--ink) !important;
}

/* Footer nav (voices/stations): icon-only circles instead of labeled
   pills — text is hidden via font-size:0 (markup untouched, so other
   themes keep their "voices"/"stations" labels) and each icon is drawn
   as a CSS mask, same technique as .hdr-bell above so header and footer
   icon buttons read as one system. Sized to match .hdr-btn/.hdr-bell. */
html[data-theme="kaiunta"] .footer-link,
html[data-theme="kaiunta"] .footer-feed-btn,
html[data-theme="kaiunta"] .footer-stations-btn,
html[data-theme="kaiunta"] .footer-autostream-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-mid);
  background: var(--icon-fill);
  border: none;
  border-radius: 50%;
  font-size: 0;
  transition: background .15s;
}
html[data-theme="kaiunta"] .footer-feed-btn::before,
html[data-theme="kaiunta"] .footer-stations-btn::before,
html[data-theme="kaiunta"] .footer-autostream-btn::before {
  content: "";
  display: block;
  width: 16px;
  height: 16px;
  background-color: var(--ink-mid);
  transition: background-color .15s;
}
/* voices = mic icon */
html[data-theme="kaiunta"] .footer-feed-btn::before {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z'/%3E%3Cpath d='M19 10v2a7 7 0 0 1-14 0v-2'/%3E%3Cline x1='12' y1='19' x2='12' y2='23'/%3E%3Cline x1='8' y1='23' x2='16' y2='23'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z'/%3E%3Cpath d='M19 10v2a7 7 0 0 1-14 0v-2'/%3E%3Cline x1='12' y1='19' x2='12' y2='23'/%3E%3Cline x1='8' y1='23' x2='16' y2='23'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* stations = broadcast-tower icon (same glyph as the station-avatar fallback) */
html[data-theme="kaiunta"] .footer-stations-btn::before {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.93 4.93a10 10 0 0 0 0 14.14'/%3E%3Cpath d='M7.76 7.76a6 6 0 0 0 0 8.49'/%3E%3Cpath d='M16.24 7.76a6 6 0 0 1 0 8.49'/%3E%3Cpath d='M19.07 4.93a10 10 0 0 1 0 14.14'/%3E%3Ccircle cx='12' cy='12' r='1.5' fill='%23000' stroke='none'/%3E%3Cline x1='12' y1='13.5' x2='12' y2='20'/%3E%3Cline x1='9.5' y1='20' x2='14.5' y2='20'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.93 4.93a10 10 0 0 0 0 14.14'/%3E%3Cpath d='M7.76 7.76a6 6 0 0 0 0 8.49'/%3E%3Cpath d='M16.24 7.76a6 6 0 0 1 0 8.49'/%3E%3Cpath d='M19.07 4.93a10 10 0 0 1 0 14.14'/%3E%3Ccircle cx='12' cy='12' r='1.5' fill='%23000' stroke='none'/%3E%3Cline x1='12' y1='13.5' x2='12' y2='20'/%3E%3Cline x1='9.5' y1='20' x2='14.5' y2='20'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* stream = play triangle, same 16px/contain treatment as the other two */
html[data-theme="kaiunta"] .footer-autostream-btn::before {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Cpolygon points='6,4 20,12 6,20'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Cpolygon points='6,4 20,12 6,20'/%3E%3C/svg%3E") center / contain no-repeat;
}
html[data-theme="kaiunta"] .footer-feed-btn:hover,
html[data-theme="kaiunta"] .footer-stations-btn:hover,
html[data-theme="kaiunta"] .footer-autostream-btn:hover { background: var(--ink); }
html[data-theme="kaiunta"] .footer-feed-btn:hover::before,
html[data-theme="kaiunta"] .footer-stations-btn:hover::before,
html[data-theme="kaiunta"] .footer-autostream-btn:hover::before { background-color: var(--paper); }

/* Streaming (.active, toggled by the JS): swap to a pause icon and pick
   out the button in --red, kaiunta's one accent, so it reads as "live"
   against the otherwise monochrome icon row — mirrors how justwitter.css
   fills this same state with --yellow. */
html[data-theme="kaiunta"] .footer-autostream-btn.active {
  background: var(--red);
  border-color: var(--red);
}
html[data-theme="kaiunta"] .footer-autostream-btn.active::before {
  background-color: var(--paper);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Crect x='5' y='4' width='5' height='16'/%3E%3Crect x='14' y='4' width='5' height='16'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Crect x='5' y='4' width='5' height='16'/%3E%3Crect x='14' y='4' width='5' height='16'/%3E%3C/svg%3E") center / contain no-repeat;
}
html[data-theme="kaiunta"] .footer-autostream-btn.active:hover {
  background: var(--ink);
  border-color: var(--ink);
}

/* Footer: black bar + thick red top border → flat paper with hairline */
html[data-theme="kaiunta"] .site-footer {
  background: var(--paper);
  border-top: 1px solid var(--rule);
}
html[data-theme="kaiunta"] .hdr-burger { background: var(--icon-fill); }
html[data-theme="kaiunta"] .hdr-burger:hover { background: var(--ink); }
html[data-theme="kaiunta"] .hdr-burger span { background: var(--ink); }
html[data-theme="kaiunta"] .hdr-burger:hover span { background: var(--paper); }

/* Slide-out menu: thick borders + shouty accordion → hairline + italic */
html[data-theme="kaiunta"] .menu-logout-btn {
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--red);
  font-style: normal;
  letter-spacing: .08em;
  text-transform: lowercase;
  font-size: .78rem;
}
html[data-theme="kaiunta"] .menu-logout-btn:hover { background: var(--red); color: var(--paper); }
html[data-theme="kaiunta"] .menu-guest-ctas {
  display: flex;
  flex-direction: column;
  gap: .6rem;
}
html[data-theme="kaiunta"] .menu-guest-ctas .hdr-btn,
html[data-theme="kaiunta"] .menu-guest-ctas .hdr-btn-primary {
  width: 100%;
  height: 44px;
  font-size: .78rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
}
/* Same person/plus glyphs as the header's icon-only login/signup circles
   (#btnHdrLogin/#btnHdrSignup) — here they sit inline next to the label
   since the burger menu keeps full text, unlike the header's icon-only
   treatment. */
html[data-theme="kaiunta"] .menu-cta-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
html[data-theme="kaiunta"] .menu-cta-icon-login {
  background-color: var(--ink-mid);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 20c0-4 4-6 8-6s8 2 8 6'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 20c0-4 4-6 8-6s8 2 8 6'/%3E%3C/svg%3E");
}
html[data-theme="kaiunta"] .menu-guest-ctas .hdr-btn:hover .menu-cta-icon-login {
  background-color: var(--paper);
}
html[data-theme="kaiunta"] .menu-cta-icon-signup {
  background-color: var(--paper);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
}

/* Voices / stations — same nav destinations as the footer's icon-only
   circles (.footer-feed-btn/.footer-stations-btn below), reusing their
   exact mic/tower glyphs so the icon language matches everywhere it
   shows up. Menu keeps the text label alongside the icon since this is
   a full-width list row, not a compact chrome button. */
html[data-theme="kaiunta"] .menu-nav-links {
  display: flex;
  flex-direction: column;
  margin-top: .75rem;
}
html[data-theme="kaiunta"] .menu-nav-link {
  display: flex;
  align-items: center;
  gap: .75rem;
  width: 100%;
  background: none;
  border: none;
  border-bottom: 1px solid var(--rule);
  text-align: left;
  font-family: var(--font-mono);
  font-style: italic;
  letter-spacing: -.01em;
  color: var(--ink);
  font-size: 1.05rem;
  padding: .85rem .25rem;
  cursor: pointer;
  transition: color .15s;
}
html[data-theme="kaiunta"] .menu-nav-link:hover { color: var(--red); }
html[data-theme="kaiunta"] .menu-nav-icon {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  background-color: var(--ink-mid);
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  transition: background-color .15s;
}
html[data-theme="kaiunta"] .menu-nav-link:hover .menu-nav-icon { background-color: var(--red); }
html[data-theme="kaiunta"] .menu-nav-icon-voices {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z'/%3E%3Cpath d='M19 10v2a7 7 0 0 1-14 0v-2'/%3E%3Cline x1='12' y1='19' x2='12' y2='23'/%3E%3Cline x1='8' y1='23' x2='16' y2='23'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z'/%3E%3Cpath d='M19 10v2a7 7 0 0 1-14 0v-2'/%3E%3Cline x1='12' y1='19' x2='12' y2='23'/%3E%3Cline x1='8' y1='23' x2='16' y2='23'/%3E%3C/svg%3E");
}
html[data-theme="kaiunta"] .menu-nav-icon-stations {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.93 4.93a10 10 0 0 0 0 14.14'/%3E%3Cpath d='M7.76 7.76a6 6 0 0 0 0 8.49'/%3E%3Cpath d='M16.24 7.76a6 6 0 0 1 0 8.49'/%3E%3Cpath d='M19.07 4.93a10 10 0 0 1 0 14.14'/%3E%3Ccircle cx='12' cy='12' r='1.5' fill='%23000' stroke='none'/%3E%3Cline x1='12' y1='13.5' x2='12' y2='20'/%3E%3Cline x1='9.5' y1='20' x2='14.5' y2='20'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.93 4.93a10 10 0 0 0 0 14.14'/%3E%3Cpath d='M7.76 7.76a6 6 0 0 0 0 8.49'/%3E%3Cpath d='M16.24 7.76a6 6 0 0 1 0 8.49'/%3E%3Cpath d='M19.07 4.93a10 10 0 0 1 0 14.14'/%3E%3Ccircle cx='12' cy='12' r='1.5' fill='%23000' stroke='none'/%3E%3Cline x1='12' y1='13.5' x2='12' y2='20'/%3E%3Cline x1='9.5' y1='20' x2='14.5' y2='20'/%3E%3C/svg%3E");
}
/* Menu — bookmarks row: same ribbon glyph as the cast-card bookmark
   button (see .cast-bookmark svg in index.php), so the icon language
   matches between the per-cast toggle and this menu entry. */
html[data-theme="kaiunta"] .menu-nav-icon-bookmarks {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
}

/* Menu — stream row: same play-triangle glyph as the footer's icon-only
   stream circle (.footer-autostream-btn::before above), swapped to
   pause bars via the same .active class the footer button gets — see
   setAutoStreamBtnState() in index-theme.php, which now keeps this row
   and the footer button in sync. Picked out in --red on active, same
   accent the footer's filled circle uses for "this one's live", instead
   of the plain hover color. */
html[data-theme="kaiunta"] .menu-nav-icon-stream {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Cpolygon points='6,4 20,12 6,20'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Cpolygon points='6,4 20,12 6,20'/%3E%3C/svg%3E");
}
html[data-theme="kaiunta"] .menu-nav-stream.active {
  color: var(--red);
}
html[data-theme="kaiunta"] .menu-nav-stream.active .menu-nav-icon {
  background-color: var(--red);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Crect x='5' y='4' width='5' height='16'/%3E%3Crect x='14' y='4' width='5' height='16'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Crect x='5' y='4' width='5' height='16'/%3E%3Crect x='14' y='4' width='5' height='16'/%3E%3C/svg%3E");
}

/* Stream loading toast — same flat/hairline language as .search-user's
   card (paper fill, faint shadow, hairline border) instead of
   justwitter's hard offset shadow, italic monospace matching kaiunta's
   one voice throughout. */
html[data-theme="kaiunta"] .stream-loading-toast {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  box-shadow: 0 1px 3px rgba(26, 26, 26, .06);
  font-family: var(--font-mono);
  font-style: italic;
  letter-spacing: -.01em;
}

html[data-theme="kaiunta"] .menu-accordion-item { border-bottom: 1px solid var(--rule); }
html[data-theme="kaiunta"] .menu-accordion-trigger {
  color: var(--ink);
  font-style: italic;
  letter-spacing: -.01em;
  text-transform: none;
  font-size: 1.3rem;
}

html[data-theme="kaiunta"] .menu-accordion-trigger:hover { color: var(--red); }

/* Nav/menu body copy (about, use cases, our story, legal, etc.) — the base
   theme boxes each paragraph like a comic sticker (thick border, hard drop
   shadow) and only needed a first-letter lowercase patch since the copy
   itself was already sentence case. kaiunta drops the box entirely for its
   flat/hairline language, and lowercases the whole paragraph — not just
   the opening letter — to match its voice throughout. text-transform
   inherits into the <strong> and <a> children too, so nothing needs
   separate handling for those. */
html[data-theme="kaiunta"] .legal-text {
  display: block;
  width: auto;
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: none;
  padding: 0;
  text-transform: lowercase;
}

/* Founder photo on "our story" — base theme gives it a thick comic-sticker
   border + hard offset shadow, which fights kaiunta's flat/hairline
   language. Drop to a plain 1px hairline ring, no shadow, so it reads as
   a quiet editorial byline photo rather than a badge. */
html[data-theme="kaiunta"] .story-signoff-photo {
  border: 1px solid var(--rule);
  box-shadow: none;
}
html[data-theme="kaiunta"] .story-signoff {
  font-style: italic;
}

/* Badges */
html[data-theme="kaiunta"] .bell-badge,
html[data-theme="kaiunta"] .feed-new-pill {
  box-shadow: none;
  border: 1px solid var(--ink);
}

/* Dark-station hidden-count note: base theme's underline treatment on the
   "hit scan" action is fine but reads a bit too "link-like" for kaiunta's
   quieter voice — swap to italic with no underline, consistent with
   .sites-list-link and .story-signoff's italic-not-underlined pattern
   elsewhere. The informational half of the sentence (the plain text in
   .feed-hidden-note itself) just inherits kaiunta's normal ink-faint/mono
   look with no override needed — only the clickable action needed one. */
html[data-theme="kaiunta"] .feed-hidden-note-action {
  font-style: italic;
  text-decoration: none;
  letter-spacing: -.01em;
}
html[data-theme="kaiunta"] .feed-hidden-note-action:hover {
  color: var(--ink);
}

/* Today's-prompt banner had no override — still justwitter's yellow-filled,
   thick-bordered, hard-shadowed sticker with an all-caps red label (the
   generic lowercase rule only targets button/h1/h2/etc., not this span). */
html[data-theme="kaiunta"] .prompt-banner {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
  box-shadow: none;
}
html[data-theme="kaiunta"] .prompt-banner-label {
  font-family: var(--font-mono);
  font-style: italic;
  letter-spacing: 0;
  text-transform: lowercase;
  color: var(--ink-faint);
}

/* "Our sites" list links had no kaiunta override at all, so they were still
   rendering in justwitter's red Bangers font with a hard drop-shadow —
   swap in kaiunta's flat italic mono link treatment to match legal-text's
   own links. */
html[data-theme="kaiunta"] .sites-list-link {
  font-family: var(--font-mono);
  font-style: italic;
  font-size: 1rem;
  letter-spacing: -.01em;
  color: var(--ink);
  text-shadow: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: .3em;
}
html[data-theme="kaiunta"] .sites-list-link:hover {
  color: var(--red);
  border-bottom-color: var(--red);
}

/* Titles & buttons: the base theme types these in ALL CAPS (LOG IN, VOICES,
   STATIONS, LOG IN WITH RECOVERY CODE, etc.) — kaiunta's voice is lowercase
   everywhere, including UI chrome, not just body copy. */
html[data-theme="kaiunta"] button,
html[data-theme="kaiunta"] h1,
html[data-theme="kaiunta"] h2,
html[data-theme="kaiunta"] .auth-panel-heading,
html[data-theme="kaiunta"] .record-title,
html[data-theme="kaiunta"] .modal th {
  text-transform: lowercase;
}

/* Desktop: same reasoning as the mobile block below — #anonTokenOverlay
   (the only way back into an anonymous account) and #welcomeOverlay (the
   app's one-time orientation) are too important to sit in the same small
   380px card every other modal uses. Unlike mobile, there's no reason to
   go full-bleed here — desktop has room to keep the centered card +
   dimmed backdrop pattern (matches how e.g. a 2FA recovery-code screen
   is usually handled), just sized up: wider card, bigger type, bigger
   input/buttons. Scoped to min-width so it hands off cleanly to the
   mobile full-bleed block below at 600px and under. Left #recordOverlay
   alone for the same reason as mobile — its timer/circle/caption/preview
   stack needs its own layout pass, not just bigger type. */
@media (min-width: 601px) {
  html[data-theme="kaiunta"] #anonTokenOverlay .record-card,
  html[data-theme="kaiunta"] #welcomeOverlay .record-card,
  html[data-theme="kaiunta"] #confirmStationOverlay .record-card {
    width: min(480px, 90vw);
    padding: 2.5rem 2.25rem;
    gap: 1.5rem;
  }
  html[data-theme="kaiunta"] #anonTokenOverlay .record-title,
  html[data-theme="kaiunta"] #welcomeOverlay .record-title,
  html[data-theme="kaiunta"] #confirmStationOverlay .record-title {
    font-size: 1.75rem;
  }
  html[data-theme="kaiunta"] #anonTokenOverlay .record-hint,
  html[data-theme="kaiunta"] #welcomeOverlay .record-hint,
  html[data-theme="kaiunta"] #confirmStationOverlay .record-hint {
    font-family: var(--font-mono);
    font-style: italic;
    font-size: 1rem;
    line-height: 1.7;
    max-width: 38ch;
  }
  html[data-theme="kaiunta"] #anonTokenInput {
    font-size: 1rem !important;
    height: 56px !important;
    letter-spacing: .02em;
  }
  html[data-theme="kaiunta"] #anonTokenOverlay .btn-ghost,
  html[data-theme="kaiunta"] #anonTokenOverlay .btn-primary,
  html[data-theme="kaiunta"] #welcomeOverlay .btn-primary,
  html[data-theme="kaiunta"] #confirmStationOverlay .btn-ghost,
  html[data-theme="kaiunta"] #confirmStationOverlay .btn-primary {
    height: 52px;
    font-size: .8rem;
  }
}

/* Mobile: the recovery-code overlay (#anonTokenOverlay) and the welcome
   overlay (#welcomeOverlay) are the two moments a new station is most
   likely to skim past — one holds the only way back into an anonymous
   account, the other is the app's one-time orientation. At phone widths
   both go full-bleed instead of sitting in the small centered card every
   other modal uses, so the copy is unmissable. Kept kaiunta-only (base
   justwitter theme keeps its small card everywhere) and scoped out of
   #recordOverlay on purpose — that modal has a timer/circle/caption/
   preview stack that needs its own layout pass, not just bigger type. */
@media (max-width: 600px) {
  html[data-theme="kaiunta"] #anonTokenOverlay,
  html[data-theme="kaiunta"] #welcomeOverlay,
  html[data-theme="kaiunta"] #confirmStationOverlay {
    padding: 0;
  }
  html[data-theme="kaiunta"] #anonTokenOverlay .record-card,
  html[data-theme="kaiunta"] #welcomeOverlay .record-card,
  html[data-theme="kaiunta"] #confirmStationOverlay .record-card {
    width: 100%;
    max-width: 100%;
    height: 100dvh;
    border: none;
    border-radius: 0;
    box-shadow: none;
    justify-content: center;
    padding: 2.5rem 1.75rem;
    gap: 1.75rem;
  }
  html[data-theme="kaiunta"] #anonTokenOverlay .record-title,
  html[data-theme="kaiunta"] #welcomeOverlay .record-title,
  html[data-theme="kaiunta"] #confirmStationOverlay .record-title {
    font-size: 1.6rem;
  }
  html[data-theme="kaiunta"] #anonTokenOverlay .record-hint,
  html[data-theme="kaiunta"] #welcomeOverlay .record-hint,
  html[data-theme="kaiunta"] #confirmStationOverlay .record-hint {
    font-family: var(--font-mono);
    font-style: italic;
    font-size: 1.05rem;
    line-height: 1.75;
    max-width: 32ch;
  }
  html[data-theme="kaiunta"] #anonTokenInput {
    font-size: 1rem !important;
    height: 58px !important;
    letter-spacing: .02em;
  }
  html[data-theme="kaiunta"] #anonTokenOverlay .btn-ghost,
  html[data-theme="kaiunta"] #anonTokenOverlay .btn-primary,
  html[data-theme="kaiunta"] #welcomeOverlay .btn-primary,
  html[data-theme="kaiunta"] #confirmStationOverlay .btn-ghost,
  html[data-theme="kaiunta"] #confirmStationOverlay .btn-primary {
    height: 58px;
    font-size: .8rem;
  }
}

/* Profile: "you're browsing anonymously..." notice + its two buttons
   (upgrade account / copy recovery code). In the base markup this is a
   plain <p style="..."> at ink-mid gray, .82rem, sitting with the same
   .6rem flex gap as everything else in .profile-edit — same visual
   weight as a bio textarea's helper text, easy to skim past even though
   it's guarding the only way back into an anonymous account.

   Marked up with an extra .profile-anon-card class on the same div
   (index-theme.php) rather than styled via a structural selector like
   :has() or ":first-child" — those are one markup reorder away from
   silently drifting onto the wrong block, and :has() specifically isn't
   supported everywhere, which will make the *entire* rule get dropped
   with zero effect if the browser doesn't parse it, rather than failing
   loudly. A plain class is the boring, reliable option.

   Rather than just re-coloring the paragraph text, this wraps the whole
   notice — message + both buttons — in one bordered card, so it reads
   as a distinct "you're anonymous" section instead of copy that happens
   to sit near some buttons. Inline styles on the <p> win over stylesheet
   rules regardless of selector specificity, so its font-size/color still
   need !important to actually override them. */
html[data-theme="kaiunta"] .profile-anon-card {
  margin-top: 1.25rem;
  background: var(--yellow);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--red);
  border-radius: 8px;
  padding: 1rem 1.1rem 1.1rem;
}
html[data-theme="kaiunta"] .profile-anon-card > p:first-child {
  font-size: 1rem !important;
  color: var(--ink) !important;
  line-height: 1.6;
  margin: 0 0 .2rem;
}
/* ── Go Live (per-station live mic broadcasting) ─────────────────────────
   kaiunta's quieter voice: same flat-paper, ink-and-typewriter language as
   the rest of this theme — red used sparingly (just the live dot/border),
   no offset shadows, no shouty caps beyond what .btn-primary already does.
   Gated behind LIVE_BROADCASTING_ENABLED server-side (index.php) — these
   rules just style the markup when it's present, they don't control
   whether it renders. */
html[data-theme="kaiunta"] .go-live-panel {
  border: 1px solid var(--rule);
  border-left: 3px solid var(--red);
  border-radius: 8px;
  padding: 1rem 1.1rem;
  background: var(--surface);
}
html[data-theme="kaiunta"] .btn-go-live {
  width: 100%;
  height: 48px;
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 6px;
  font-family: var(--font-display);
  font-size: .85rem;
  letter-spacing: .04em;
  color: var(--paper);
  cursor: pointer;
  transition: opacity .15s;
}
html[data-theme="kaiunta"] .btn-go-live:hover {
  opacity: .8;
}
html[data-theme="kaiunta"] .go-live-hint {
  font-family: var(--font-mono);
  font-size: .75rem;
  color: var(--ink-mid);
  text-align: center;
  margin: .5rem 0 0;
}
html[data-theme="kaiunta"] .go-live-active {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
}
html[data-theme="kaiunta"] .live-pill,
html[data-theme="kaiunta"] .live-badge {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  border: 1px solid var(--red);
  border-radius: 4px;
  padding: .35rem .75rem;
  font-family: var(--font-mono);
  font-size: .78rem;
  letter-spacing: .02em;
  color: var(--red);
}
html[data-theme="kaiunta"] .live-badge {
  padding: .2rem .5rem;
  font-size: .68rem;
  margin-left: .5rem;
  vertical-align: middle;
  cursor: pointer;
  transition: opacity .15s;
}
html[data-theme="kaiunta"] .live-badge:hover {
  opacity: .75;
}
html[data-theme="kaiunta"] .live-badge.active {
  background: var(--red);
  color: var(--paper);
}
html[data-theme="kaiunta"] .live-pill-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--red);
  display: inline-block;
  animation: kaiuntaLivePulse 1.2s infinite;
}
@keyframes kaiuntaLivePulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .3; }
}
html[data-theme="kaiunta"] .live-pill-timer {
  color: var(--ink-mid);
  font-family: var(--font-mono);
}
html[data-theme="kaiunta"] .go-live-url-row {
  display: flex;
  gap: .6rem;
  align-items: center;
  margin-top: .85rem;
}

/* ── Global on-air bar ─────────────────────────────────────────────────
   Sits fixed just below the header, visible on every view (see the HTML
   comment above #globalOnAirBar for why). kaiunta's quieter voice, same
   as its own .go-live-panel: flat paper, hairline border, no shadows. */
html[data-theme="kaiunta"] .on-air-bar {
  position: fixed;
  top: var(--hdr-h);
  left: 0;
  right: 0;
  z-index: 99; /* one below .hdr's z-index:100, so header stays on top */
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  background: var(--surface);
  border-bottom: 1px solid var(--red);
  padding: 0 1rem;
}
/* BUGFIX: see the matching comment in justwitter.css — without this,
   [hidden] (toggled by setGoLiveUiLive() in index.php) has no effect
   because the display:flex rule above wins on equal specificity. */
html[data-theme="kaiunta"] .on-air-bar[hidden] {
  display: none;
}
html[data-theme="kaiunta"] .on-air-bar .on-air-label {
  font-family: var(--font-display);
  font-size: .78rem;
  letter-spacing: .02em;
  color: var(--red);
}
html[data-theme="kaiunta"] .on-air-bar .live-pill-timer {
  font-family: var(--font-mono);
  font-size: .78rem;
  color: var(--ink-mid);
}
html[data-theme="kaiunta"] .on-air-bar .live-pill-timer.on-air-ending-soon {
  color: var(--red);
  animation: kaiuntaLivePulse 1.2s infinite;
}
html[data-theme="kaiunta"] .on-air-end-btn {
  height: 26px;
  padding: 0 .7rem;
  background: var(--ink);
  border: none;
  border-radius: 4px;
  color: var(--paper);
  font-family: var(--font-display);
  font-size: .65rem;
  letter-spacing: .02em;
  cursor: pointer;
  margin-left: .4rem;
}
html[data-theme="kaiunta"] .on-air-end-btn:hover {
  opacity: .8;
}