/* =============================================================================
   KilusNa — PUBLICATION DETAIL PAGE (publication.html only)
   -----------------------------------------------------------------------------
   Page grid · back link · article hero card (cover + gallery + title block) ·
   rich-content typography · engagement bar · detail sections · metadata rail ·
   entity cards · hidden-dialog extras.

   Consumes ONLY tokens.css — no hardcoded hex anywhere (the cover's
   broken-image plate reuses the .report-thumb precedent: --border in light,
   --hover in dark, overridden at the bottom of this file). Loaded between
   components.css and mobile.css, so every breakpoint rule and every
   [data-theme="dark"] decision for the classes below also lives HERE.
   The shared ?auth=1 chrome (state swap, header identity cluster,
   notification dropdown, drawer verified row, reaction/Save pressed
   plates) is NOT here — it lives in css/auth-state.css, loaded after
   mobile.css and before dark.css on both two-version pages.
   ============================================================================= */

/* ===== Page grid (mobile-first: one column) ============================== */
.pub-page {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  /* detail pages cap at --max-w-detail (narrower than the homepage grid
     so the side gutters stay visible — see the token's rationale) */
  max-width: var(--max-w-detail);
  margin: var(--gap) auto;
  padding: 0 var(--gap);
}

.pub-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);   /* minmax(0,…) guards grid blowout */
  gap: var(--gap);
  align-items: start;
}

.pub-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}

/* ===== Back link (secondary control, ≥44px target via .btn) ============== */
.pub-back {
  align-self: flex-start;
}

/* ===== Article hero card ================================================= */
/* .card supplies the shell (--card / --border / --r-card / --shadow-card);
   the radius also rounds the cover via overflow:hidden. */
.pub-hero {
  overflow: hidden;
}

/* Cover: 16:10 on mobile, 16:8 from ≥641 (live breakpoint kept). The plate
   behind the image is the broken-image fallback (see dark override below). */
.pub-cover {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background-color: var(--border);
}

.pub-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Photo-count chip: same construction as the feed-card .thumb-count
   (--img-chip scrim, white label) — bottom-left here, as on the homepage. */

/* --- thumbnail strip: a toggle group, not a tablist -------------------- */
.pub-thumbs {
  display: flex;
  gap: var(--s2);
  padding: var(--s3) var(--s4) 0;
  overflow-x: auto;          /* strip scrolls internally — never the page */
  scrollbar-width: none;     /* thin themed bar suppressed, like .feed-tabs */
}

.pub-thumbs::-webkit-scrollbar { display: none; }

.pub-thumb {
  flex: 0 0 auto;
  width: 88px;
  height: 60px;              /* ≥44px touch target both axes */
  padding: 0;
  background: var(--hover);
  border: 2px solid transparent;
  border-radius: var(--r-btn);
  overflow: hidden;
  transition: border-color .15s ease;
}

.pub-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Selected thumb. The live page outlines it in red (#DC2626); the system
   reserves red for negative/urgent, so the active state uses the accent —
   --accent-text, not the fill colour: 5.93:1 on --card in light, 12.06:1 in
   dark (a bare --accent border would be 1.55:1 on a light card). */
.pub-thumb[aria-pressed="true"] {
  border-color: var(--accent-text);
}

/* --- prev/next photo buttons (overlaid on the cover) --------------------- */
/* 44px circles pinned to the cover's left/right edges, vertically centred.
   Scrim = --img-chip (the photo-count chip's and the homepage play control's
   ingredient) with a --white icon, so both are theme-independent by design:
   the same dark scrim sits under white in light AND dark. Hidden entirely by
   publication.js when a record has fewer than 2 photos. */
.pub-photo-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;                 /* min touch target, both axes */
  height: 44px;
  padding: 0;
  background-color: var(--img-chip);
  color: var(--white);
  border-radius: 50%;
  transition: transform .15s ease;   /* the only property that changes smoothly */
}

.pub-photo-prev { left: var(--s2); }
.pub-photo-next { right: var(--s2); }

.pub-photo-nav svg { width: 20px; height: 20px; }

/* Hover: a 1px lift + a stronger scrim — a second --img-chip layer stacked
   as a background-image (paints above the background colour, below the icon,
   so the white glyph never dims). background-image itself swaps instantly;
   only the lift animates, and base_theme collapses that duration under
   prefers-reduced-motion. */
.pub-photo-nav:hover {
  transform: translateY(calc(-50% - 1px));
  background-image: linear-gradient(var(--img-chip), var(--img-chip));
}

/* Focus ring over arbitrary photo content: white + offset, deliberately NOT
   a token colour — an accent/dark ring can disappear into a photo of the same
   hue, while --white reads against any image and is identical in both
   themes. The --img-chip halo fills the 2px offset gap so the ring also
   holds over white/bright photo regions (outlines paint above box-shadow).
   This also overrides base_theme's :focus-visible outline for these two
   buttons only. */
.pub-photo-nav:focus-visible {
  outline: 2px solid var(--white);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--img-chip);
}

/* The class's own display beats the UA [hidden] default, so hiding needs
   an explicit rule (publication.js sets the hidden attribute for
   1-photo records). */
.pub-photo-nav[hidden] { display: none; }

/* --- title block ------------------------------------------------------- */
.pub-titleblock {
  padding: var(--s4) var(--s5) var(--s5);
}

.pub-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-bottom: var(--s2);
}

/* Type pill + status badge reuse the feed-card constructions verbatim
   (.report-category pill, .verdict-badge dot+border badge). */

.pub-titleblock h1 {
  margin-bottom: var(--s2);
  font-size: 1.25rem;
  line-height: 1.3;
  color: var(--text);
}

.pub-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--s3);
  font-size: var(--fs-desc);
  color: var(--faint);
}

/* author link follows the .report-author convention (body → accent on hover) */
.pub-meta-row a {
  color: var(--body);
  transition: color .15s ease;
}

.pub-meta-row a:hover { color: var(--accent-text); text-decoration: underline; }

/* ===== Article body (rich content) ======================================= */
.pub-body {
  padding: var(--s5);
}

/* The live page renders these paragraphs bold + font-size:large via inline
   styles; here the emphasis stays semantic (<strong>) and the measure,
   colour and rhythm come from tokens. 16px everywhere (master-prompt floor
   applies to this reading text on every viewport), capped at ~68ch so the
   detail layout never runs the measure too wide. */
.rich-content p {
  max-width: 68ch;
  font-size: 16px;
  line-height: 1.7;
  color: var(--body);
}

.rich-content p + p { margin-top: var(--s4); }

.rich-content strong { color: var(--text); font-weight: 700; }

/* ===== Engagement bar ==================================================== */
/* Card shell + the homepage's .react-btn action pattern (44px targets,
   icon + count, --muted → --accent-text on hover). */
.eng-bar {
  padding: var(--s2) var(--s4);
}

.engagement-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}

/* Live's .rd-spacer: pushes Save to the far side; shrinks to nothing when
   the row wraps so it can never force horizontal scroll. */
.eng-spacer {
  flex: 1 1 auto;
  min-width: 0;
}

/* ===== Detail sections =================================================== */
/* Main-column section shell (Cases / Related Knowledge / Claims / Comments)
   and the metadata-rail section shell share this header rhythm. */
.pub-section {
  padding: var(--s5);
}

.pub-section-header {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-bottom: var(--s3);
}

.pub-section h2 {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--fs-modal);
  font-weight: 700;
  color: var(--text);
}

/* Count pill: accent-soft plate + accent-text label (5.38:1 light / 9.14:1 dark). */
.section-count {
  display: inline-flex;
  align-items: center;
  padding: 1px var(--s2);
  background: var(--accent-soft);
  color: var(--accent-text);
  font-size: var(--fs-tiny);
  font-weight: 700;
  line-height: 1.6;
  border-radius: var(--r-pill);
}

/* Detail-level empty state: a quiet muted line, not the feed-scale centred
   .empty-state block (extrapolated — listed in the delivery notes). */
.pub-empty {
  font-size: var(--fs-sm);
  color: var(--muted);
}

/* --- Claims: published fact-check reviews --------------------------------
   Each review is a plate inside the section. A checker is never named here:
   the API masks the individual, so the byline is the institutional label. A
   superseded review stays in the list with a correction note pointing at what
   replaced it, because a verdict changing is part of the verdict. */
.claim-list {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  list-style: none;
}

.claim-item {
  padding: var(--s3);
  background: var(--hover);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
}

.claim-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin-bottom: var(--s2);
}

/* The content label is the other half of a verdict: what kind of content was
   judged, not how it scored. Muted caps so it reads as a category rather than
   a second verdict badge. */
.claim-label {
  font-size: var(--fs-tiny);
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--muted);
}

.claim-text {
  margin-bottom: var(--s1);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text);
}

.claim-explanation {
  font-size: var(--fs-desc);
  line-height: 1.55;
  color: var(--body);
}

.claim-correction {
  margin-top: var(--s2);
  font-size: var(--fs-meta);
  color: var(--muted);
}

.claim-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s3);
  padding-top: var(--s3);
  border-top: 1px solid var(--border);
  font-size: var(--fs-meta);
  color: var(--muted);
}

.comment-login {
  margin-bottom: var(--s4);
  font-size: var(--fs-sm);
  color: var(--muted);
}

.comments-list {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  margin: 0;                   /* it is a real <ul> now */
  padding: 0;
  list-style: none;
}

/* ===== Comment card + reply thread (extrapolated) ========================
   Anatomy mirrors live's renderComment exactly (header / body / reply button
   / reply form / replies container; nested replies carry header + body only)
   — the visuals are ours, built from the entity-card and chip ingredients. */
.comment {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-btn);
  padding: 10px var(--s3);
}

/* Header: name + role badge + time — wraps as a row on narrow screens */
.comment-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px var(--s2);
  margin-bottom: var(--s1);
}

.comment-header strong { font-size: var(--fs-base); }

.comment-time {
  font-size: var(--fs-desc);
  color: var(--muted);
}

.comment-body {
  font-size: 16px;             /* reading size on every viewport (page rule) */
  line-height: 1.6;
  color: var(--body);
  overflow-wrap: break-word;   /* API content with long URLs can never scroll */
}

/* Role badge — NOTES §1 construction: semantic colour sits on the dot AND
   the border, the label stays --text (16.3–17.0:1 light / 12.5–13.7:1 dark),
   never live's coloured-pill text. Sentence case, unlike the uppercase
   verdict badges: it labels a person, not a report state. Tokens only —
   both themes resolve through the tone tokens (no dark override needed). */
.role-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: var(--fs-badge);
  font-weight: 700;
  line-height: 1.5;
  color: var(--text);
}

.role-badge::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--tone, var(--muted));
  border-radius: 50%;
}

.role-verified {
  background: var(--green-soft);
  border-color: var(--green);
  --tone: var(--green);
}

/* Regular: neutral plate + border (the entity-rel pill's ingredients) so the
   outline still clears 3:1 as a meaningful badge boundary in both themes. */
.role-regular {
  background: var(--hover);
  border-color: var(--muted);
  --tone: var(--muted);
}

/* The Reply toggle needs no page rule of its own: it is the engagement
   row's ghost action verbatim (.react-btn = 44px target, muted → accent-text
   hover, 12.5px label) — floored to 16px at ≤647 in the block below. */

/* Reply form: hidden until toggled; .form-control textarea + .btn primary */
.reply-form-container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s2);
}

/* the class's own display beats the UA [hidden] default */
.reply-form-container[hidden] { display: none; }

.reply-input {
  flex: 1 1 220px;
  min-width: 0;
}

/* Reply thread: hairline marks the nesting level; replies render lighter
   than top-level cards (header + body only, as live does) */
.comment-replies:empty { display: none; }

.comment-replies:not(:empty) {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  margin: var(--s3) 0 0;
  padding-left: var(--s4);
  border-left: 1px solid var(--border);
  list-style: none;
}

.comment-replies .comment.reply {
  background: none;
  border: 0;
  padding: 0;
}

/* Related Knowledge hint (hidden section, copy preserved from live) */
.kg-hint {
  display: block;
  margin-top: var(--s2);
  font-size: var(--fs-xs);
  color: var(--muted);
}

/* ===== Metadata rail ===================================================== */
.meta-rail {
  padding: var(--s4);
}

/* One rail card, sections divided by hairlines (same rhythm the homepage
   rails use between stacked cards). */
.meta-section + .meta-section {
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--border);
}

/* Sidebar headings step down to the rail-card title size, as
   .sidebar-card-title does on the homepage (14.5px, not the 21px h2). */
.meta-section h2 {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-bottom: var(--s3);
  font-size: var(--fs-h4);
  font-weight: 700;
  color: var(--text);
}

.entity-list {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

/* Entity card (extrapolated): chip + card ingredients — a bordered mini
   card carrying a name link, a muted role/type line and a relation pill. */
.entity-card {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding: 10px var(--s3);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-btn);
}

.entity-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.entity-main strong { font-size: var(--fs-base); }

/* links keep the system's accent colour; underline on hover like every text link */
.entity-link { transition: color .15s ease; }
.entity-link:hover { text-decoration: underline; }

.entity-role,
.entity-type {
  font-size: var(--fs-xs);
  color: var(--muted);
}

.entity-rel {
  align-self: flex-start;
  padding: 2px var(--s2);
  background: var(--hover);
  color: var(--muted);
  font-size: var(--fs-meta);
  font-weight: 600;
  border-radius: var(--r-pill);
}

/* Topic/tag chips reuse .pub-pill (topic = the accent variant); this list
   just wraps them with consistent 6px gaps and allows internal scrolling
   only through normal wrapping — chips wrap, they never scroll. */
.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.entity-list .chip-list { gap: 6px; }

/* ===== Auth-only dialogs (extras beyond the shared modal component) ======= */
/* The claim modal's intro paragraph (live renders it as 13px muted copy). */
.claim-intro {
  margin-bottom: var(--s4);
  font-size: var(--fs-desc);
  line-height: 1.55;
  color: var(--muted);
}

/* --- Follow toggle (meta row, auth-only) --------------------------------
   Outline at rest; accent-soft plate + accent-text label when following
   (5.38:1 light / 9.14:1 dark). The pressed state is also carried by the
   label text swap in publication.js (Follow → Following), so state never
   relies on colour alone (WCAG 1.4.1). border-strong is reinforcement,
   not the sole identifier — the visible label identifies the control
   (WCAG 1.4.11). */
.follow-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 var(--s3);
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-btn);
  color: var(--body);
  font-size: var(--fs-sm);
  font-weight: 600;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.follow-btn svg { width: 15px; height: 15px; }

.follow-btn:hover {
  background: var(--accent-soft);
  border-color: var(--accent-text);
  color: var(--accent-text);
}

.follow-btn[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent-text);
  color: var(--accent-text);
}

/* --- Disputes (auth-only, between hero and layout, outside the grid) ---- */
#disputeSection .btn { margin-top: var(--s3); }

/* --- Comment composer (auth-only, above the list) ----------------------- */
.comment-form {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  margin-bottom: var(--s4);
}

/* .form-control supplies the field shell + focus ring; ~3 rows at the
   16px/1.55 reading size, vertically resizable like live's textarea. */
.comment-input {
  min-height: 92px;
  resize: vertical;
}

.comment-form-actions { display: flex; justify-content: flex-end; }

/* --- Claim pill (People rail, auth-only) --------------------------------
   "I am <person>": accent-soft plate + accent-text label (5.38:1 light /
   9.14:1 dark); gold fill on hover with the on-accent label (11.42:1 /
   12.15:1). The row only flexes in auth mode: in guest markup .entity-meta
   holds just the relation pill and must lay out exactly as before. */
html.is-auth .entity-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
}

.claim-pill {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  min-height: 44px;
  padding: 0 var(--s3);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-pill);
  color: var(--accent-text);
  font-size: var(--fs-sm);
  font-weight: 600;
  transition: background .15s ease, color .15s ease;
}

.claim-pill:hover { background: var(--accent); color: var(--on-accent); }

/* ===== Breakpoints ======================================================= */
/* ≤480 — tighter sheets */
@media (max-width: 480px) {
  .pub-page { padding: 0 var(--s4); }

  .pub-titleblock { padding: var(--s4); }
  .pub-body,
  .pub-section { padding: var(--s4); }
  .meta-rail { padding: var(--s3); }

  /* reply thread keeps its hairline but sheds most of the indent so the
     nested body never crowds the 360px column */
  .comment-replies:not(:empty) { padding-left: var(--s2); }
}

/* ≤647 — master-prompt type floor (mobile.css bumps the shared classes;
   the classes below are page-only, so they get their floor here). */
@media (max-width: 647px) {
  .pub-meta-row,
  .pub-empty,
  .comment-login,
  .comment-time,
  .claim-intro,
  .rich-content p { font-size: 16px; }

  .entity-main strong { font-size: 16px; }
  .comment-header strong { font-size: 16px; }
  .entity-role,
  .entity-type { font-size: 15px; }

  /* compact pill labels stay pill-shaped but get legible */
  .section-count { font-size: 12px; }
  .role-badge { font-size: 12px; }
  .entity-rel { font-size: 13px; }
  .kg-hint { font-size: 14px; }

  /* .react-btn keeps 12.5px elsewhere, but this text action carries the
     page's floor like every other page-scoped label */
  .comment-reply-btn { font-size: 16px; }

  /* page-only auth labels keep the floor here (the shared auth-state
     labels — header text links, profile name, notification copy — are
     floored in css/auth-state.css) */
  .follow-btn,
  .claim-pill { font-size: 16px; }
}

/* ≥641 — cover returns to the live 16:8 crop; title grows one step */
@media (min-width: 641px) {
  .pub-cover { aspect-ratio: 16 / 8; }
  .pub-titleblock h1 { font-size: 1.5rem; }
}

/* ≥768 — two columns: article + metadata rail */
@media (min-width: 768px) {
  .pub-layout {
    grid-template-columns: minmax(0, 1fr) 300px;
  }
}

/* ≥1280 — the metadata rail widens to the system's right-rail width */
@media (min-width: 1280px) {
  .pub-layout {
    grid-template-columns: minmax(0, 1fr) var(--col-r);
  }
}

/* ===== Dark theme ========================================================
   Colour-level values live in tokens.css; only component decisions that
   cannot be expressed as a single token swap appear here — scoped to
   [data-theme="dark"] (never a media query), so a manual override always
   wins over the OS preference.

   Fallback plate behind a missing/broken cover image, mirroring the
   .report-thumb override in dark.css (light uses --border). */
[data-theme="dark"] .pub-cover {
  background-color: var(--hover);
}
