/* zattabbs static assets, embedded into the binary via go:embed.
   Design constraints (docs/ui-design.md §3, §6, v2): system fonts only,
   light/dark via prefers-color-scheme only, a single dark-green accent
   (never red/blue, which read as political-camp colors), one
   breakpoint (600px), 44px minimum tap targets, no horizontal scroll.
   Density direction (v2): modern-dense — body text keeps its size,
   headers/metadata shrink and fade, spacing tightens. */

:root {
  /* Page background (v4.18): a very faint green tint off pure white — the
     one-accent constitution stays intact (this is a tone of the same
     --accent hue, not a new color), but the site no longer reads as flat
     white. --panel-bg stays pure white so cards/sheets (forms, quick-reply,
     the >>n preview panel) lift visibly off this tinted page. */
  --bg: #f6f9f7;
  --fg: #1a1a1a;
  --muted: #6b6b6b;
  --border: #dddddd;
  --accent: #1f5c3d;
  --accent-fg: #ffffff;
  --accent-soft: #eef4f0;
  --panel-bg: #ffffff;
  /* Sticky header band (v4.18): halfway between --bg and --accent-soft, a
     touch greener than the page it sits above so the header stays visually
     distinct from content scrolling under it without introducing a new
     hue. */
  --header-bg: #f2f6f4;
  --panel-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
  /* Visited thread titles (v4.15): a quieter, grayer green — enough to
     tell "already read" at a glance without the row dropping out. */
  --visited: #6b8577;
  /* Sticky header clearance (v4.5): measured header height (~60px: 8px
     top/bottom padding plus the 44px-tall logo row) plus 8px of breathing
     room, so #target-jumped anchors (>>n res, reply/new-thread forms,
     top board sections) land below the fixed header instead of under it. */
  --header-clearance: 68px;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Same tinted-surface move as light (v4.18): a green-tinted near-black
       page, with --panel-bg a step lighter so cards still lift off it. */
    --bg: #121614;
    --fg: #e6e6e6;
    --muted: #969696;
    --border: #333333;
    --accent: #3f9a6e;
    --accent-fg: #0a0a0a;
    --accent-soft: #1a2620;
    --panel-bg: #1d1d1d;
    --header-bg: #161e1a;
    --panel-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
    --visited: #6d8a7c;
  }
}

* {
  box-sizing: border-box;
}

html {
  overflow-x: hidden;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--fg);
  line-height: 1.55;
}

a {
  color: var(--accent);
}

/* No custom :focus-visible rule (removed post-v4.5): keyboard focus falls
   back to the UA default outline. The accent outline read as an
   unwanted "glow on click" in mouse/touch use even though :focus-visible
   is meant to gate on keyboard-only focus — browsers disagree enough on
   when it fires that it wasn't reliably keyboard-only in practice. */

/* JS-only controls (the new-responses button) stay hidden until the
   thread page's inline script marks the document as JS-capable. */
.js-only {
  display: none;
}

.js .js-only {
  display: block;
}

/* v4.18: 1.25rem -> 1.375rem — a touch more presence now that the page
   has surfaces to set headings against; body/list density (§2's modern-
   dense direction) is untouched, this is heading-only. */
h1 {
  font-size: 1.375rem;
  margin: 12px 0 4px;
  overflow-wrap: anywhere;
}

h2 {
  font-size: 1.05rem;
}

.container {
  max-width: 720px;
  margin: 0 auto;
  padding: 0 12px;
}

/* Bottom breathing room now that the footer is gone (v4.9): pages no
   longer end flush against the viewport edge. Scoped to main (not
   header.site-header's own .container) so the header's flex row isn't
   affected. */
main.container {
  padding-bottom: 64px;
}

/* Sticky (v4.5): the header stays reachable (hamburger menu, board nav,
   search) while scrolling a long thread. z-index sits above ordinary page
   content, but below the touch popover bottom sheet (20). */
header.site-header {
  position: sticky;
  top: 0;
  z-index: 13;
  background: var(--header-bg);
  border-bottom: 1px solid var(--border);
  padding: 8px 0;
}

header.site-header .container {
  display: flex;
  align-items: center;
  gap: 14px;
}



.site-nav {
  display: flex;
  gap: 10px;
  font-size: 0.875rem;
}

.site-nav a {
  color: var(--muted);
  text-decoration: none;
  padding: 8px 0;
}

.site-nav a {
  white-space: nowrap;
}

/* Header nav doubles as a location indicator (v4.21): the board the
   reader is inside carries the same active treatment as list tabs. */
.site-nav a.tab-active {
  color: var(--fg);
  font-weight: bold;
  border-bottom: 2px solid var(--accent);
}

/* The header's leading slot is a plain home glyph, not the logo — the
   brand mark lives in the top-page hero (v4.21). */
.site-home {
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
}

/* --- header hamburger menu (v4.5), replacing the bare アカウント link;
   v4.6: the header search box is gone, so this is the header's only
   right-aligned element ---
   A <details> disclosure, same JS-free pattern as .res-menu. */
.site-menu {
  position: relative;
  margin-left: auto;
}

.site-menu > summary {
  cursor: pointer;
  color: var(--fg);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
  padding: 0 4px;
}

.site-menu > summary::-webkit-details-marker {
  display: none;
}

.site-menu-body {
  position: absolute;
  top: 100%;
  right: 0;
  /* Above the sticky header (13) so the panel is never clipped. */
  z-index: 14;
  display: flex;
  flex-direction: column;
  width: max-content;
  min-width: 160px;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  box-shadow: var(--panel-shadow);
}

.site-menu-body a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  color: var(--fg);
  text-decoration: none;
  font-size: 0.9375rem;
}

.site-menu-body a:not(:last-child) {
  border-bottom: 1px solid var(--border);
}

/* heavier group divider */
.site-menu-body a.site-menu-divider {
  border-top: 2px solid var(--fg);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* --- board tabs --- */

nav.board-nav {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin: 8px 0 4px;
}

nav.board-nav a {
  padding: 8px 12px;
  text-decoration: none;
  color: var(--muted);
  border-bottom: 2px solid transparent;
  min-height: 44px;
  display: flex;
  align-items: center;
}

nav.board-nav a.tab-active {
  color: var(--fg);
  font-weight: bold;
  border-bottom-color: var(--accent);
}

/* Standalone action button (board-nav above holds only view-switching
   tabs now, an action doesn't belong in that row): styled like the
   accent <button> elsewhere on an <a>, since it's a same-page anchor
   jump, not a form submit. */
.new-thread-cta {
  margin: 8px 0;
  text-align: right;
}

a.button-accent {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 8px 16px;
  background: var(--accent);
  color: var(--accent-fg);
  text-decoration: none;
}

/* --- lists --- */

ul.board-list,
ul.thread-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul.board-list li,
ul.thread-list li {
  border-bottom: 1px solid var(--border);
  padding: 8px 0;
}

ul.thread-list a {
  text-decoration: none;
  font-weight: bold;
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* Pseudo read-tracking (v4.15): visited titles fade. Driven purely by
   browser history, so it costs nothing and never conflicts with the
   purge-driven cache (unlike real per-user unread state, which was
   rejected for exactly that conflict). Note :visited only matches the
   exact URL — a >50-res row links to /l50, so reading the full page
   doesn't fade it; accepted. Excludes the 返信 shortcut link. */
ul.thread-list a:visited:not(.thread-row-reply) {
  color: var(--visited);
}

/* one-line rows: title left, count/time right; a long title wraps and
   pushes the meta to its own line, nothing overlaps */
li.thread-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
}

li.thread-row .thread-meta {
  margin-left: auto;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.thread-meta {
  color: var(--muted);
  font-size: 0.8125rem;
}

.thread-meta-hot {
  color: var(--accent);
}

/* Per-row reply shortcut: quiet so it doesn't compete with the title,
   placed after the meta so the row still reads as one line. The 44px
   tap target is padding around a visually small label rather than a
   larger label, so it doesn't inflate the row's line height. */
.thread-row-reply {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 4px;
  margin: -12px -4px -12px 0;
  color: var(--muted);
  font-size: 0.75rem;
  text-decoration: none;
}

/* "同じ板のほかのスレ" (v4.9): bottom-of-thread-page related list, the
   same one-line .thread-row rows board_list.templ uses. */
.related-threads {
  margin-top: 24px;
  padding-top: 12px;
  border-top: 2px solid var(--border);
}

/* --- top page --- */

/* The top page's <h1> (v4.5): the logo mark already carries the site
   identity in the header, so this reads as a modest subtitle rather than
   a second big heading — sized down from the default h1 and kept muted,
   not restyled up to compete with it. */

/* Board entrance cards (v4.13): the top page's front doors to the three
   board pages. Equal-width cards (auto-fit, stacking on narrow
   screens), each the board title over its one-line description; the
   accent tops up on hover/focus so the whole card reads as clickable.
   Descriptions matter more to a first-time visitor than activity
   numbers, so there's no count here. */
.board-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: 8px;
  margin: 12px 0 4px;
}

.board-cards a {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-height: 56px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-top: 2px solid var(--accent);
  text-decoration: none;
  color: var(--fg);
}

.board-cards a:hover,
.board-cards a:focus {
  background: var(--accent-soft);
}

.board-card-title {
  font-weight: bold;
  color: var(--accent);
  font-size: 0.9375rem;
}

.board-card-desc {
  color: var(--muted);
  font-size: 0.75rem;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

.top-board {
  margin-top: 20px;
  scroll-margin-top: var(--header-clearance);
}

.top-board h2 {
  margin: 0 0 4px;
  font-size: 1.125rem;
  letter-spacing: 0.01em;
  border-bottom: 2px solid var(--accent);
  padding-bottom: 4px;
}

.top-board h2 a {
  text-decoration: none;
  color: var(--fg);
}

.top-board-more {
  margin: 6px 0 0;
  font-size: 0.8125rem;
}

/* 最新の書き込み (v4.18): the same .thread-row as every other listing,
   plus one extra line. flex-basis: 100% forces it onto its own row inside
   the flex-wrap row rather than fighting the title/time for space on the
   first line — the title/time pair above it behaves exactly like an
   ordinary thread-row. */
.recent-row .recent-excerpt {
  flex-basis: 100%;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* --- responses (docs/ui-design.md §3: 1レス=ヘッダ1行+本文) --- */

.res {
  border-bottom: 1px solid var(--border);
  padding: 8px 0;
  scroll-margin-top: var(--header-clearance);
}

.thread-meta-line {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 4px;
}

.thread-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.thread-nav a {
  margin-right: 8px;
  color: var(--muted);
}

.thread-nav-context {
  font-size: 0.8125rem;
  color: var(--muted);
  margin: 2px 0 4px;
}

.thread-nav .tab-active {
  margin-right: 8px;
  font-weight: bold;
}

nav.board-nav .tab-kako {
  margin-left: auto;
}

.thread-meta-line .res-menu {
  margin-left: auto;
}

.res-header {
  display: flex;
  flex-wrap: wrap;
  column-gap: 6px;
  align-items: baseline;
  font-size: 0.8125rem;
  color: var(--muted);
}

/* Date+ID wrap together as one unit on narrow screens (the span pair
   is grouped in the template); inside the group they can still split
   in the extreme (<320px) case. */
.res-stamp {
  display: inline-flex;
  flex-wrap: wrap;
  column-gap: 6px;
}

.res-no {
  font-weight: bold;
  color: var(--fg);
}

/* v2: the bare default name stays muted; only named posts (custom name,
   trip, or verified handle) get the accent color. */
.res-name {
  color: var(--muted);
}

.res-name-named {
  color: var(--accent);
  font-weight: bold;
}

.res-verified::after {
  content: " ✓";
  color: var(--accent);
}

.res-body {
  margin-top: 2px;
  white-space: pre-wrap;
}

.res-abone {
  margin-top: 2px;
  color: var(--muted);
  font-style: italic;
}

/* --- client-side NG (localStorage, JS exception #5) ---
   ng.js adds .ng-hidden to a matching .res and inserts .ng-row as its
   first child; everything else in that .res is hidden so the response
   collapses to just this one line. Deliberately not res-abone's look
   (no italic) so a reader can tell "I hid this" apart from "moderation
   hid this". */
.res.ng-hidden > :not(.ng-row) {
  display: none;
}

/* このIDの投稿だけ表示 (ng.js): a plain view filter, so the whole .res
   is hidden outright — unlike ng-hidden, no per-res row is left behind. */
.id-filter-hidden {
  display: none;
}

.ng-row {
  color: var(--muted);
  font-size: 0.8125rem;
}

.ng-row button {
  margin-left: 6px;
  min-height: 28px;
  padding: 2px 8px;
  font-size: 0.75rem;
}

/* --- >>n references (v2): JS-free hover/tap preview.
   Baseline everywhere: .res-ref is a plain in-page anchor and the tap
   button is hidden. On hover devices the wrapped panel shows on hover.
   On touch devices *with* native popover support, the button (which
   toggles the panel as a real popover) replaces the anchor. --- */

.res-ref {
  color: var(--accent);
}

/* inline-block (not inline) so this is a proper positioning context: an
   inline box's containing-block geometry for an absolutely-positioned
   child is unreliable across browsers (notably Safari), which let the
   hover panel below overlap the wrapped link/text instead of clearing
   it. inline-block keeps the wrap flowing inline with surrounding
   pre-wrap body text — it never spans a line break itself since >>n
   refs are short tokens, so wrapping is unaffected. */
.res-ref-wrap {
  position: relative;
  display: inline-block;
  vertical-align: baseline;
}

.res-ref-tap {
  display: none;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  min-height: 0;
  min-width: 0;
  font: inherit;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.res-ref-panel {
  margin: 0;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  color: var(--fg);
  box-shadow: var(--panel-shadow);
  padding: 8px;
  font-size: 0.875rem;
  max-width: min(90vw, 480px);
  white-space: normal;
}

.res-ref-panel-header {
  display: block;
  color: var(--muted);
  font-size: 0.8125rem;
}

.res-ref-panel-body {
  display: block;
  margin-top: 2px;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.res-ref-panel-jump {
  display: none;
  margin-top: 6px;
  font-size: 0.8125rem;
}

@media (hover: hover) {
  /* The hover preview is display-only: pointer-events none keeps it
     from sitting between the pointer and the >>n link itself (which
     must stay clickable to jump). The tap path opens the same element
     as a real popover instead, where it stays interactive. */
  .res-ref-wrap:hover > .res-ref-panel:not(:popover-open) {
    display: block;
    position: absolute;
    /* The [popover] UA stylesheet sets `inset: 0` (top/right/bottom/left
       all 0); without an explicit top/right here that 0 survives
       alongside our bottom below and pins the panel back down over the
       link. inset: auto clears all four before left/bottom re-set the
       two we actually want. */
    inset: auto;
    left: 0;
    /* 6px clears the hovered line with a visible gap; inline-block above
       makes this offset reliable instead of overlapping the link/text
       it's anchored to. */
    bottom: calc(100% + 6px);
    width: max-content;
    z-index: 10;
    pointer-events: none;
  }
}

@supports selector(:popover-open) {
  @media (hover: none) {
    .res-ref-wrap > .res-ref {
      display: none;
    }
    .res-ref-tap {
      display: inline;
    }
    .res-ref-panel:popover-open {
      position: fixed;
      inset: auto 8px 8px 8px;
      z-index: 20;
    }
    .res-ref-panel:popover-open .res-ref-panel-jump {
      display: inline-block;
      min-height: 44px;
    }
  }
}

/* --- per-response actions, folded into one ⋯ menu at the header's
   right edge (report / appeal / takedown) --- */

/* wraps 返信+⋯ as one unit; also the .res-menu-body anchor inside .res */
.res-actions {
  position: relative;
  margin-left: auto;
  display: flex;
  align-items: baseline;
}

.res-reply {
  color: var(--muted);
  font-size: 0.75rem;
  text-decoration: none;
}

.res-menu {
  position: relative;
}

/* inside .res, .res-actions anchors the panel instead */
.res .res-menu {
  position: static;
}

.res-menu > summary {
  cursor: pointer;
  color: var(--muted);
  list-style: none;
  padding: 0 8px;
}

.res-menu > summary::-webkit-details-marker {
  display: none;
}

/* v4.16: the panel itself is a clean vertical menu of plain items only
   (44px rows, dividers), same look as .site-menu-body — no nested
   <details>, no forms live inside it anymore (those moved to
   .inline-form, below). */
.res-menu-body {
  position: absolute;
  right: 0;
  /* 16: above the sticky header (13). */
  z-index: 16;
  display: flex;
  flex-direction: column;
  width: max-content;
  min-width: 160px;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  box-shadow: var(--panel-shadow);
}

.res-menu-body a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  color: var(--fg);
  text-decoration: none;
  font-size: 0.9375rem;
}

.res-menu-body a:not(:last-child) {
  border-bottom: 1px solid var(--border);
}

/* Quiet helper text under a report/appeal field (v4.7): a shade smaller
   and muteder than the surrounding text so it reads as an aside, not
   another instruction competing with the label. */
.field-help {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 0.75rem;
}

/* --- per-response quick-reply (v4.5) ---
   Appended beforeend inside .res by hx-swap; the sibling selector below
   hides any stray duplicate if the same response's 返信 is clicked again
   before the first one is submitted or cancelled (simplest guard against
   stacking — see thread_detail.templ's QuickReplyForm doc comment for the
   fuller rationale). */
.res .quick-reply ~ .quick-reply {
  display: none;
}

.quick-reply {
  margin-top: 8px;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  padding: 8px;
  /* hx-swap's show:#quick-reply-{n}:top (v4.9) scrolls this element to
     the top of the viewport when it's off-screen after opening; without
     this, "top" would tuck it directly under the sticky header. */
  scroll-margin-top: var(--header-clearance);
}

.quick-reply > summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 0.8125rem;
}

form.reply.quick-reply-form {
  border-top: none;
  margin-top: 8px;
  padding-top: 0;
}

.quick-reply-done {
  margin-top: 8px;
}

/* --- inline report/appeal forms (v4.16, docs/ui-design.md) ---
   Revealed by CSS :target when the ⋯/スレの操作 menu's 報告/異議を
   申し立てる link is clicked (no JS). Visually the same bordered-box
   look as .quick-reply — a shared class wasn't worth it here since the
   two also differ in structural position (.quick-reply is appended by
   htmx; this is always in the DOM, just hidden), so the box styling is
   duplicated rather than factored out. The field-level rules
   (label/select/input/textarea/button) are what .res-menu-body's form
   styling used to cover before the forms moved out of that panel. */
.inline-form {
  display: none;
  margin-top: 8px;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  padding: 8px;
  font-size: 0.8125rem;
  /* So the sticky header doesn't cover the top of the revealed form
     when the browser jumps to it as the :target (same reasoning as
     .quick-reply's scroll-margin-top, v4.9). */
  scroll-margin-top: var(--header-clearance);
}

.inline-form:target {
  display: block;
}

.inline-form label {
  display: block;
  margin-top: 6px;
}

.inline-form select,
.inline-form input[type="text"],
.inline-form textarea {
  width: 100%;
  padding: 6px;
  /* 1rem, not smaller: iOS Safari auto-zooms the page when a focused
     field's font-size is under 16px. */
  font-size: 1rem;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  color: var(--fg);
  min-height: 36px;
}

.inline-form textarea {
  min-height: 60px;
}

.inline-form button {
  margin-top: 6px;
}

.inline-form > a {
  display: inline-block;
  margin-top: 6px;
  color: var(--muted);
}

/* --- htmx new-responses button --- */

.js .new-res-btn {
  display: block;
  width: 100%;
  margin-top: 8px;
  background: var(--bg);
  color: var(--accent);
  border: 1px solid var(--accent);
}

/* --- reverse anchors (返信(n)) --- */

.res-replies {
  margin-top: 3px;
  font-size: 0.75rem;
}

.res-replies summary {
  cursor: pointer;
  color: var(--muted);
  display: inline-block;
}

.res-replies-list a {
  margin-right: 8px;
}

/* --- forms --- */

/* Name/email sit side by side only while each column keeps enough room
   for its placeholder text (~240px); below that they stack. Content-
   driven via auto-fit, so there's no cliff at any particular viewport
   width — the 600px breakpoint doesn't apply here. */
.form-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: 0 10px;
}

form.new-thread,
form.reply,
form.ng {
  border-top: 2px solid var(--border);
  margin-top: 24px;
  padding-top: 12px;
  scroll-margin-top: var(--header-clearance);
}

form.new-thread label,
form.reply label,
form.takedown label,
form.contact label,
form.ng label {
  display: block;
  margin-top: 8px;
}

form.new-thread input[type="text"],
form.new-thread textarea,
form.reply input[type="text"],
form.reply textarea,
form.takedown input[type="text"],
form.takedown input[type="number"],
form.takedown select,
form.takedown textarea,
form.contact input[type="text"],
form.contact textarea,
form.ng input[type="text"] {
  width: 100%;
  padding: 8px;
  font-size: 1rem;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  color: var(--fg);
  min-height: 44px;
}

form.new-thread textarea,
form.reply textarea,
form.takedown textarea,
form.contact textarea {
  min-height: 120px;
}

button,
input[type="submit"] {
  min-height: 44px;
  min-width: 44px;
  padding: 8px 16px;
  background: var(--accent);
  color: var(--accent-fg);
  border: none;
  font-size: 1rem;
  cursor: pointer;
}

.error {
  border: 2px solid var(--fg);
  padding: 8px;
  margin-top: 8px;
}

/* --- /takedown (v4.7) --- */

/* The locked-mode read-only target line (resolved board/thread titles);
   accent-tinted like .notice so it reads as "this is fixed", not just
   another muted line of text. */
.takedown-target-summary {
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  padding: 8px;
  margin-top: 8px;
}

.takedown-target-mode {
  margin-top: 8px;
}

/* Generic radio row (also used by the poll vote form below): not scoped
   to .takedown-target-mode despite living in this section, since both
   forms share the exact same tap-target/spacing needs. */
.radio-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  margin-right: 16px;
}

/* CSS-only reveal (no JS): the レス番号 field only appears once 特定の
   レス is selected. #scope-res and .takedown-res-field are siblings
   inside .takedown-target-mode, so the general sibling combinator can
   reach across the intervening スレッド全体 label. */
.takedown-res-field {
  display: none;
  margin-top: 4px;
}

#scope-res:checked ~ .takedown-res-field {
  display: block;
}

.notice {
  border: 1px solid var(--accent);
  padding: 8px;
  margin-top: 8px;
}

/* long tokens/URLs must wrap, never force horizontal scroll */
pre,
code,
.body-text {
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* --- /login (v4.5) --- */

.login-lead {
  color: var(--muted);
  max-width: 60ch;
}

/* Two method cards side by side while each keeps enough room to read
   comfortably; stacks below that — same content-driven auto-fit as
   .form-pair, no extra breakpoint. */
.login-methods {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 12px;
  margin-top: 16px;
}

.login-card {
  border: 1px solid var(--border);
  padding: 12px;
}

.login-card h2 {
  margin-top: 0;
}

.login-card label {
  display: block;
  margin-top: 8px;
}

.login-card input[type="email"] {
  width: 100%;
  padding: 8px;
  font-size: 1rem;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  color: var(--fg);
  min-height: 44px;
}

.login-card button {
  margin-top: 10px;
}

.login-note {
  color: var(--muted);
  font-size: 0.8125rem;
  margin: 8px 0 0;
}

/* A button-styled link, not a bare <a>: it's presented as an equal,
   one-step alternative to the email form's submit button, not a small
   secondary link under it. */
.login-google-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  margin-top: 8px;
  background: var(--accent);
  color: var(--accent-fg);
  text-decoration: none;
  font-weight: bold;
}

form.search {
  display: flex;
  gap: 8px;
  align-items: flex-end;
  margin: 16px 0;
}

form.search label {
  display: none;
}

form.search input[type="text"] {
  flex: 1;
  padding: 8px;
  font-size: 1rem;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  color: var(--fg);
  min-height: 44px;
}

/* Cloudflare Turnstile renders a fixed-width iframe; never let it force
   horizontal scroll on narrow screens. cf-turnstile-lazy is the
   per-response report/appeal widget (thread_detail.templ's reportForm/
   appealForm): it deliberately skips Turnstile's own "cf-turnstile"
   class so implicit auto-render ignores it (rendered later, on demand,
   by the delegated click listener), but once turnstile.render() fires
   it grows the same iframe and needs the same guard. */
.cf-turnstile,
.cf-turnstile iframe,
.cf-turnstile-lazy,
.cf-turnstile-lazy iframe {
  max-width: 100%;
}

/* Rendered Markdown pages (/policy, /guide) — scoped to .md so these
   heading styles never leak into app pages' h2s (forms, top page). */
.md h1,
.md h2,
.md h3 {
  text-wrap: balance;
}

.md h2 {
  border-top: 1px solid var(--border);
  margin-top: 32px;
  padding-top: 16px;
  scroll-margin-top: var(--header-clearance);
}

.md h3 {
  margin-top: 20px;
  scroll-margin-top: var(--header-clearance);
}

.md ol,
.md ul {
  padding-left: 1.4em;
}

/* Markdown tables (the privacy policy's data inventory). display:block
   + overflow-x lets a wide table scroll inside itself on narrow
   screens instead of forcing the page sideways. */
.md table {
  display: block;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.md th,
.md td {
  border: 1px solid var(--border);
  padding: 6px 10px;
  text-align: left;
  vertical-align: top;
}

.md th {
  background: var(--accent-soft);
  white-space: nowrap;
}

/* --- /account: 退会 (v4.8) --- */

/* Quietly distinct from the rest of the page (a border, not color) —
   this is a permanent, irreversible action, not an error state, so it
   deliberately does not reuse .error's border-var(--fg) look or any red
   (docs/ui-design.md: never red/blue). */
.account-delete {
  border: 1px solid var(--border);
  padding: 12px;
  margin-top: 32px;
}

/* Shared checkbox row (v4.23: also used by the post forms' sage
   checkbox, not just .account-delete's 退会 confirmation). */
.checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  margin-top: 8px;
}

/* Sage checkbox (v4.23): sits in the form-pair's second column next to
   名前, vertically centered against that field rather than stacked
   under a label — it's a single toggle, not a labeled text field, so
   it doesn't need the same top-aligned layout. */
.form-pair .sage-field {
  align-self: center;
}

.form-pair .sage-field .checkbox-label {
  margin-top: 0;
}

/* Outline button: same footprint as the default accent button, but
   neutral (fg-on-bg) so it doesn't read as the page's one call-to-action
   the way the accent-green buttons do. */
.button-neutral {
  margin-top: 8px;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--fg);
}

/* Reply count as タイトル (n), riding inline right after the title
   (v4.15, user decision) instead of folded into the right-aligned meta.
   Inline flow means it follows the title's last word even when the
   title wraps. */
.thread-count {
  color: var(--muted);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

/* --- top-page hero (v4.20): the site's face — mark + name only. The
   art layer is currentColor bubbles at low opacity, riding on the
   accent so it adapts to both themes over the transparent ground. --- */
.hero {
  position: relative;
  margin: 16px 0 8px;
  padding: 28px 0 24px;
  overflow: hidden;
}

.hero-art {
  position: absolute;
  inset: 0;
  color: var(--accent);
  pointer-events: none;
}

.hero-art svg {
  width: 100%;
  height: 100%;
}

.hero-lockup {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--accent);
}

.hero-lockup svg {
  width: 44px;
  height: 44px;
  flex: none;
}

.hero-lockup h1 {
  margin: 0;
  font-size: 1.75rem;
  letter-spacing: 0.04em;
  color: var(--fg);
}

/* --- thread polls --- */

.poll {
  border: 1px solid var(--border);
  padding: 12px;
  margin-top: 16px;
}

.poll-option {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}

.poll-option-label {
  flex: 1;
}

.poll-option-count {
  color: var(--muted);
  white-space: nowrap;
}

.poll-bar {
  flex-basis: 120px;
  height: 8px;
  background: var(--border);
}

.poll-bar-fill {
  height: 100%;
  background: var(--accent);
}

.poll-total {
  color: var(--muted);
  margin-top: 8px;
}

.poll-form .radio-label {
  display: flex;
  margin-right: 0;
}

/* gap so this doesn't crowd the Turnstile widget below it */
.poll-fieldset {
  margin: 20px 0;
}

/* 楽天広告. .ad-pr: ステマ規制 disclosure */
.ad-pr {
  text-align: right;
  color: var(--muted);
  font-size: 0.65rem;
}

.ad-rail,
.ad-footer {
  display: none;
}

/* second breakpoint (beyond 600px): room for a rail beside the 720px
   column; main.wide only appears when the rail actually has items */
@media (min-width: 1080px) {
  main.container.wide {
    max-width: 976px;
  }
  .thread-page-wrap {
    display: flex;
    gap: 24px;
  }
  .thread-page {
    flex: 0 0 720px;
  }
  .ad-rail {
    display: block;
    flex: 1;
    max-width: 232px;
  }
  .ad-books-inline {
    display: none;
  }
}

@media (hover: none) {
  main.container {
    padding-bottom: 96px;
  }
  .ad-footer {
    display: flex;
    gap: 8px;
    align-items: center;
    justify-content: space-between;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 15;
    min-height: 40px;
    padding: 0 12px;
    background: var(--header-bg);
    border-top: 1px solid var(--border);
    font-size: 0.8125rem;
  }
}

.ad-thumb {
  object-fit: cover;
  flex-shrink: 0;
}

.ad-footer-link {
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

