/* =========================================================
   Site-wide news pages (index/home, feed, tracker).
   Shares tokens.css with the map (styles.css); this file only
   covers the scrolling news chrome — map.html never loads it.
   ========================================================= */

@import url('tokens.css?v=29');

html, body {
  min-height: 100%;
}

body {
  background: var(--bg0);
  display: flex;
  flex-direction: column;
}

.page-wrap {
  max-width: 1180px;
  width: 100%;
  margin: 0 auto;
  padding: 28px 24px 60px;
  flex: 1;
}

.page-head {
  margin-bottom: 24px;
}

.page-title {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 800;
  color: var(--bright);
  letter-spacing: .3px;
}

.page-sub {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text);
  letter-spacing: .5px;
  margin-top: 4px;
}

.section-label {
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 2.5px;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 32px 0 14px;
}

.section-label::before {
  content: '';
  width: 3px;
  height: 14px;
  background: var(--accent);
  border-radius: 2px;
  flex-shrink: 0;
}

.section-label:first-child { margin-top: 0; }

/* ===== Cards ===== */

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.card-pad { padding: 16px 18px; }

/* ===== Main page: big story tiles ===== */

.story-hero {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 200px;
  grid-auto-flow: dense;
  gap: 12px;
}

.story-tile {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  background: #1a2230;
  transition: box-shadow .15s, border-color .15s;
}

.story-tile:hover { box-shadow: var(--shadow-md); border-color: var(--border2); }

.story-tile.lead { grid-column: span 2; grid-row: span 2; }

.story-tile-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.story-tile-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10,14,22,.15) 0%, rgba(10,14,22,.25) 45%, rgba(10,14,22,.88) 100%);
}

/* No-image fallback: two gradients, matching the two-hue pill rule — a warm
   one for the conflict categories, a neutral one for everything else. */
.story-tile.no-img { background: linear-gradient(150deg, #232c3d 0%, #141a26 100%); }
.story-tile.no-img.tile-cat-military,
.story-tile.no-img.tile-cat-nuclear,
.story-tile.no-img.tile-cat-terrorism    { background: linear-gradient(150deg, #3a1f24 0%, #17121a 100%); }
.story-tile.no-img .story-tile-scrim { background: linear-gradient(180deg, transparent 40%, rgba(10,14,22,.5) 100%); }

.story-tile-body {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 14px 16px;
  z-index: 2;
}

.story-tile.lead .story-tile-body { padding: 20px 22px; }

.story-tile-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

/* Tile chips sit on a white plate in BOTH themes, so they take the light-theme
   ink values — the dark-theme --text / --accent2 would wash out on white. Same
   two-hue rule as everywhere else: conflict categories red, the rest neutral. */
.story-tile-meta .pill { backdrop-filter: blur(4px); background: rgba(255,255,255,.9); color: #33342e; }
.story-tile-meta .pill-military,
.story-tile-meta .pill-nuclear,
.story-tile-meta .pill-terrorism { color: #dc2626; }

.story-tile-time {
  font-family: var(--font-mono);
  font-size: 9px;
  color: rgba(255,255,255,.75);
  letter-spacing: .3px;
}

.story-tile-title {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  color: #fff;
  line-height: 1.3;
  text-shadow: 0 1px 8px rgba(0,0,0,.5);
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.story-tile.lead .story-tile-title { font-size: 22px; -webkit-line-clamp: 3; }

.story-tile-dek {
  margin-top: 8px;
  font-family: var(--font-body);
  font-size: 12px;
  color: rgba(255,255,255,.82);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.story-tile-credit {
  position: absolute;
  top: 8px;
  right: 10px;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 7px;
  color: rgba(255,255,255,.55);
  letter-spacing: .3px;
  text-shadow: 0 1px 4px rgba(0,0,0,.6);
}

/* ===== Main page: rest of the news ===== */

.rest-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.rest-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: box-shadow .15s, border-color .15s;
}

.rest-row .rest-text { flex: 1; min-width: 0; }

.rest-row:hover { box-shadow: var(--shadow-md); border-color: var(--border2); }

/* Open-on-feed affordance — faint until the row is hovered. */
.rest-open {
  flex-shrink: 0;
  font-family: var(--font-mono);
  color: var(--accent);
  opacity: 0;
  transition: opacity .15s;
}
.rest-row:hover .rest-open { opacity: 1; }

.rest-row .pill { flex-shrink: 0; align-self: center; }

.rest-time {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text);
  flex-shrink: 0;
  width: 84px;
}

.rest-text {
  font-family: var(--font-body);
  font-size: 12px;
  color: var(--text2);
  line-height: 1.5;
}

.rest-more {
  margin-top: 14px;
  font-family: var(--font-mono);
  font-size: 10px;
}

.rest-more a { color: var(--accent); text-decoration: none; }
.rest-more a:hover { text-decoration: underline; }

@media (max-width: 860px) {
  .story-hero { grid-template-columns: 1fr 1fr; grid-auto-rows: 200px; }
  .story-tile.lead { grid-column: span 2; }
}

@media (max-width: 560px) {
  /* Single column on phones — give each image tile more vertical room so the
     photo has space to read and the title isn't cramped against the bottom. */
  .story-hero { grid-template-columns: 1fr; grid-auto-rows: 240px; }
  .story-tile.lead { grid-column: span 1; grid-row: span 1; }
  .rest-time { width: auto; }
}

/* ===== Home: hero grid ===== */

.hero-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 12px;
}

.hero-card {
  padding: 14px 16px;
  cursor: pointer;
  transition: box-shadow .15s, border-color .15s;
  border-left: 3px solid var(--border2);
}

.hero-card:hover {
  box-shadow: var(--shadow-md);
}

.hero-card.breaking { border-left-color: var(--accent2); }

.hero-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.hero-time {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text);
  letter-spacing: .3px;
  margin-left: auto;
}

.hero-title {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--bright);
  line-height: 1.4;
}

/* ===== Pills / badges (shared look across pages) ===== */

/* One hue does one job. --accent2 marks conflict/kinetic categories and
   severity; --accent marks the reader's own state (tracked / active);
   everything else is neutral. Nothing here encodes how well sourced an item
   is — we have no reliable way to establish that, so no colour claims it. */

.pill {
  font-family: var(--font-mono);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border2);
  background: var(--bg1);
  color: var(--text);
  display: inline-block;
}

/* Conflict categories — the only pills that take the alarm colour. */
.pill-military,
.pill-nuclear,
.pill-terrorism { color: var(--accent2); border-color: rgba(192,57,43,.3); background: rgba(192,57,43,.06); }

.sev-dots { display: inline-flex; gap: 2px; align-items: center; vertical-align: middle; }
.sev-dot  { width: 4px; height: 4px; border-radius: 50%; background: var(--border); }
.sev-dot.on { background: var(--accent2); }

/* ===== Tracked-story mini cards (home) ===== */

.story-mini {
  padding: 12px 14px;
  cursor: pointer;
  transition: box-shadow .15s, border-color .15s;
}

.story-mini:hover { box-shadow: var(--shadow-md); }

.story-mini-title {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  color: var(--bright);
  margin-bottom: 4px;
}

.story-mini-meta {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text);
  display: flex;
  gap: 8px;
  align-items: center;
}

/* ===== Feed page ===== */

/* Search sits above the category pills — it AND-s with them, so it reads as
   the broader filter of the two. */
.feed-search {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

.feed-search__box {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1;
  max-width: 460px;
}

.feed-search__icon {
  position: absolute;
  left: 11px;
  width: 14px;
  height: 14px;
  color: var(--text);
  pointer-events: none;
  transition: color .15s;
}

.feed-search.has-query .feed-search__icon { color: var(--accent); }

.feed-search__input {
  width: 100%;
  padding: 9px 32px 9px 33px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--bright);
  background: var(--card);
  border: 1px solid var(--border2);
  border-radius: var(--radius-pill);
  outline: none;
  transition: border-color .15s, box-shadow .15s;
  -webkit-appearance: none;
  appearance: none;
}

.feed-search__input::placeholder { color: var(--text); }
.feed-search__input::-webkit-search-cancel-button { display: none; }

.feed-search__input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.feed-search__clear {
  position: absolute;
  right: 8px;
  display: none;
  width: 20px;
  height: 20px;
  line-height: 1;
  font-size: 16px;
  color: var(--text);
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  transition: color .15s, background .15s;
}

.feed-search.has-query .feed-search__clear { display: block; }
.feed-search__clear:hover { color: var(--bright); background: var(--bg2); }

.feed-search__count {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text);
  white-space: nowrap;
}

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 18px;
}

.filter-pill {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .5px;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border2);
  cursor: pointer;
  background: var(--card);
  color: var(--text2);
  transition: color .15s, border-color .15s, opacity .15s;
  text-transform: uppercase;
  user-select: none;
}

.filter-pill.off { opacity: .35; filter: grayscale(.7); }
.filter-pill:hover { box-shadow: var(--shadow-sm); border-color: var(--border2); }

/* The Flagged filter leads the bar (the flag is the first tag) and sits on its
   own axis — it AND-s with the categories rather than being one of them, so a
   rule separates it and it never takes the greyed-out `.off` treatment. */
.filter-pill.flag-filter {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--flag);
  border-color: var(--flag-line);
}

.filter-pill.flag-filter.on {
  color: var(--on-flag);
  background: var(--flag);
  border-color: var(--flag);
}

.filter-count {
  font-size: 9px;
  opacity: .75;
}

.filter-sep {
  width: 1px;
  align-self: stretch;
  margin: 2px 4px;
  background: var(--border2);
}

.feed-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.feed-card {
  padding: 14px 16px;
  border-left: 3px solid var(--border2);
}

.feed-card.breaking { border-left-color: var(--accent2); }
.feed-card.tracked  { border-left-color: var(--accent); }
/* Flagged wins the edge stripe — it's the user's own mark on the card. */
.feed-card.flagged  { border-left-color: var(--flag); background: var(--flag-tint); }

/* Feed cards with a matched/enriched image become a thumb + body row. Cards
   without one keep the original flat layout (no .feed-card-body wrapping cost
   is visible — it just fills the width). */
.feed-card.has-img {
  display: flex;
  gap: 14px;
  align-items: stretch;
  padding: 0;
  overflow: hidden;
}

.feed-card-thumb {
  position: relative;
  flex-shrink: 0;
  width: 116px;
  align-self: stretch;
  overflow: hidden;
  background: var(--bg1);
}

.feed-card-thumb img {
  width: 100%;
  height: 100%;
  min-height: 100%;
  object-fit: cover;
  display: block;
}

.feed-card-credit {
  position: absolute;
  left: 0;
  bottom: 0;
  right: 0;
  padding: 2px 5px;
  font-size: 8px;
  line-height: 1.25;
  color: rgba(255,255,255,.82);
  background: linear-gradient(transparent, rgba(0,0,0,.55));
}

.feed-card.has-img .feed-card-body {
  flex: 1;
  min-width: 0;
  padding: 14px 16px 14px 0;
}

@media (max-width: 640px) {
  .feed-card.has-img { flex-direction: column; }
  .feed-card-thumb { width: 100%; height: 150px; align-self: auto; }
  .feed-card.has-img .feed-card-body { padding: 0 16px 14px; }
}

.feed-card-top {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.feed-time {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text);
}

/* Flag + Track sit together at the right end of the card's top row. */
.feed-card-controls {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Track / Tracking — a labelled button rather than a glyph, so the state is
   readable without decoding an icon. Off = quiet outline; on = solid accent. */
.track-btn {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  padding: 4px 9px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border2);
  background: transparent;
  color: var(--text2);
  cursor: pointer;
  line-height: 1;
  white-space: nowrap;
  transition: color .15s, border-color .15s, background .15s;
}

.track-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.track-btn.on {
  /* --on-accent, not #fff: the dark theme's accent is amber, where white text
     is unreadable. */
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
}

.track-btn.on:hover {
  /* Hover on an active button means "stop tracking" — warn rather than invite. */
  background: transparent;
  color: var(--accent);
}

/* Flag toggle — the personal bookmark. --flag is its own hue precisely so it
   never reads as the Track action, whose --accent flips amber/blue by theme. */
.flag-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 3px;
  line-height: 0;
  color: var(--text);
  opacity: .6;
  transition: color .15s, opacity .15s;
}

.flag-btn:hover,
.flag-btn.on { color: var(--flag); opacity: 1; }

.flag-glyph { display: block; }

.feed-text {
  font-family: var(--font-body);
  font-size: 12px;
  color: var(--text2);
  line-height: 1.55;
}

/* Source / perspective badge for wire items. The chip is neutral: the
   perspective is spelled out in words ("Gulf", "Russia", "UK"), which is both
   clearer than a hue and less editorial than assigning viewpoints colours. */
.source-badge {
  display: inline-flex;
  align-items: center;
  gap: 0;
  margin: 0 0 7px;
  font-family: var(--font-mono);
  font-size: 9px;
  line-height: 1;
  border-radius: 4px;
  overflow: hidden;
  border: 1px solid var(--border2);
  text-decoration: none;
  vertical-align: middle;
}
a.source-badge:hover { border-color: var(--accent); }

.source-badge-name {
  padding: 3px 6px;
  color: var(--text2);
  background: var(--bg1);
  letter-spacing: .02em;
}

.source-badge-persp {
  padding: 3px 6px;
  color: var(--text);
  background: var(--bg2);
  border-left: 1px solid var(--border2);
}

.feed-expand {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  display: none;
}

.feed-expand.open { display: block; }

.feed-expand-row { margin-bottom: 8px; }
.feed-expand-row:last-child { margin-bottom: 0; }

.feed-expand-k {
  font-family: var(--font-display);
  font-size: 8px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  margin-bottom: 3px;
}

.feed-expand-v {
  font-family: var(--font-body);
  font-size: 11px;
  color: var(--text2);
  line-height: 1.55;
}

.feed-expand-v a { color: var(--accent); text-decoration: none; }
.feed-expand-v a:hover { text-decoration: underline; }

/* Accent-filled details control: blue in light (--accent #2e86ff),
   amber/orange in dark (--accent #f0a030). Used as <button> on Feed
   and as <a> deep-links from Tracker / Timelines. */
.feed-details-btn {
  display: inline-flex;
  align-items: center;
  margin-top: 10px;
  padding: 5px 12px;
  background: var(--accent);
  color: var(--on-accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .8px;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  flex-shrink: 0;
  line-height: 1.2;
}

.feed-details-btn:hover {
  filter: brightness(1.08);
}

.feed-card-focus {
  box-shadow: 0 0 0 2px var(--accent);
  border-color: var(--accent);
}

.rest-row .feed-details-btn {
  margin-top: 0;
  margin-left: auto;
  align-self: center;
}

.timeline-node .feed-card .feed-details-btn {
  margin-top: 10px;
}

/* ===== Tracker page ===== */

.tracker-layout {
  display: flex;
  gap: 20px;
  align-items: flex-start;
}

.tracker-rail {
  width: 300px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tracker-story-item {
  padding: 12px 14px;
  cursor: pointer;
  border-left: 3px solid var(--border2);
  transition: all .15s;
}

.tracker-story-item:hover { box-shadow: var(--shadow-md); }
.tracker-story-item.selected { border-left-color: var(--accent); background: rgba(37,99,235,.04); }

.tracker-status {
  font-family: var(--font-mono);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  display: inline-block;
}

.tracker-status.active   { color: var(--accent); background: var(--accent-soft); }
.tracker-status.stale    { color: var(--text);   background: var(--bg1); }
.tracker-status.resolved { color: var(--text);    background: var(--bg1); }
.tracker-status.archived { color: var(--text);    background: var(--bg1); }

.tracker-main {
  flex: 1;
  min-width: 0;
}

.tracker-empty {
  padding: 60px 20px;
  text-align: center;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  opacity: .5;
}

.tracker-story-header {
  padding: 0;
  margin-bottom: 20px;
  overflow: hidden;
}

.tracker-story-header-body {
  padding: 18px 20px;
}

.tracker-story-hero {
  position: relative;
  width: 100%;
  height: 200px;
  overflow: hidden;
}

.tracker-story-hero img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tracker-story-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10,14,22,0) 55%, rgba(10,14,22,.5) 100%);
  pointer-events: none;
}

.tracker-story-hero-credit {
  position: absolute;
  right: 8px;
  bottom: 6px;
  font-family: var(--font-mono);
  font-size: 8px;
  color: rgba(255,255,255,.8);
  z-index: 1;
}

.tracker-story-title {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 800;
  color: var(--bright);
  margin-bottom: 8px;
}

.tracker-story-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

.tracker-btn {
  font-family: var(--font-display);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 6px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border2);
  background: var(--card);
  color: var(--text2);
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}

.tracker-btn:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

.tracker-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.tracker-btn.primary:hover { filter: brightness(1.08); }
.tracker-btn.danger { color: #c62828; border-color: rgba(198,40,40,.4); }
.tracker-btn.danger:hover { background: #c62828; border-color: #c62828; color: #fff; }

/* Pager button under the feed / rest-of-news list. */
.load-more-btn {
  display: block;
  width: 100%;
  margin: 18px 0 4px;
  padding: 11px 14px;
}

/* ── Tracker toolbar + manage panel ─────────────────────────── */
.tracker-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}

.tracker-toolbar-note {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text);
  opacity: .7;
}

/* ── Timeline search (per selected story, above its thread) ── */
.tracker-search {
  position: relative;
  display: flex;
  align-items: center;
  margin: 14px 0;
}

.tracker-search-icon {
  position: absolute;
  left: 12px;
  color: var(--text);
  opacity: .55;
  pointer-events: none;
}

.tracker-search-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 14px 10px 38px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--text2);
  background: var(--bg1);
  border: 1px solid var(--border2);
  border-radius: 8px;
  transition: border-color .15s, box-shadow .15s;
}

.tracker-search-input::placeholder { color: var(--text); opacity: .6; }

.tracker-search-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 20%, transparent);
}

.manage-panel {
  margin-bottom: 20px;
  border: 1px solid var(--border2);
  border-radius: var(--radius-md);
  background: var(--bg1);
  overflow: hidden;
}

.manage-inner {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 20px;
}

.manage-head {
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text);
  margin-bottom: 10px;
}

.manage-empty {
  font-family: var(--font-body);
  font-size: 11px;
  color: var(--text);
  opacity: .7;
}

.manage-list { display: flex; flex-direction: column; gap: 6px; }

.manage-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

/* Catalog rows — published stories this profile isn't following yet. Same
   shape as a tracked row, minus the reorder/edit controls it hasn't earned. */
.catalog-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  opacity: .82;
  transition: opacity .15s, border-color .15s;
}

.catalog-row:hover { opacity: 1; border-color: var(--border2); }

.catalog-list { max-height: 320px; overflow-y: auto; }

.catalog-countries {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .04em;
  color: var(--text);
  margin-left: 6px;
}

.manage-row-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  color: var(--bright);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manage-tag {
  font-family: var(--font-mono);
  font-size: 7px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  background: var(--bg1);
  color: var(--text);
}

.manage-row-actions { display: flex; gap: 6px; flex-shrink: 0; margin-left: auto; }

/* Reorder controls */
.manage-row[draggable="true"] { cursor: default; }
.manage-row.dragging { opacity: .5; }
.manage-row.drag-over { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.drag-handle {
  cursor: grab;
  color: var(--text2);
  font-size: 13px;
  line-height: 1;
  user-select: none;
  flex-shrink: 0;
}
.drag-handle:active { cursor: grabbing; }
.reorder-btns { display: flex; flex-direction: column; gap: 1px; flex-shrink: 0; }
.reorder-btn {
  font-family: var(--font-mono);
  font-size: 8px;
  line-height: 1;
  padding: 2px 4px;
  border: 1px solid var(--border2);
  border-radius: 3px;
  background: var(--bg1);
  color: var(--text);
  cursor: pointer;
}
.reorder-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.reorder-btn:disabled { opacity: .3; cursor: default; }
.manage-prio {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  color: var(--text2);
  min-width: 16px;
  text-align: center;
  flex-shrink: 0;
}

/* Inline edit form (replaces a row while editing) */
.manage-edit {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  background: var(--card);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
}
.manage-edit input,
.manage-edit textarea {
  font-family: var(--font-body);
  font-size: 12px;
  padding: 8px 10px;
  border: 1px solid var(--border2);
  border-radius: var(--radius-sm);
  background: var(--bg1);
  color: var(--bright);
  width: 100%;
}
.manage-edit textarea { resize: vertical; }
.manage-edit input:focus,
.manage-edit textarea:focus { outline: none; border-color: var(--accent); }
.manage-edit-actions { display: flex; gap: 6px; }

/* Story image field (edit + add-your-own forms): preview + URL/upload controls. */
.manage-image-field { display: flex; gap: 10px; align-items: flex-start; }
.manage-image-preview {
  width: 72px;
  height: 54px;
  flex-shrink: 0;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border2);
  background: var(--bg1);
}
.manage-image-preview.empty { display: none; }
.manage-image-inputs { display: flex; flex-wrap: wrap; gap: 6px; flex: 1; align-items: center; }
.manage-image-inputs input[type="text"] { flex: 1; min-width: 140px; }
.manage-upload-btn { cursor: pointer; }

/* Story freshness tag + deep-link headline */
.last-news { color: var(--text2); }
.node-feed-link { color: inherit; text-decoration: none; }
.node-feed-link:hover { text-decoration: underline; text-decoration-color: var(--accent); }

.suggest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px;
}

.suggest-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.suggest-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.suggest-countries {
  font-family: var(--font-mono);
  font-size: 8px;
  color: var(--text);
  opacity: .75;
}

.suggest-title {
  flex: 1;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  color: var(--bright);
  line-height: 1.4;
}

.suggest-card .tracker-btn { align-self: flex-start; }

.manage-form { display: flex; flex-direction: column; gap: 8px; max-width: 560px; }

.manage-form input,
.manage-form textarea {
  font-family: var(--font-body);
  font-size: 12px;
  padding: 8px 10px;
  border: 1px solid var(--border2);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--bright);
  width: 100%;
}

.manage-form textarea { resize: vertical; }

.manage-form input:focus,
.manage-form textarea:focus { outline: none; border-color: var(--accent); }

.manage-form-row { display: flex; gap: 8px; }
.manage-form-row input { flex: 1; }
.manage-form .tracker-btn { align-self: flex-start; }

.timeline-thread {
  position: relative;
  padding-left: 24px;
}

.timeline-thread::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background: var(--border);
}

.timeline-node {
  position: relative;
  margin-bottom: 16px;
}

.timeline-node::before {
  content: '';
  position: absolute;
  left: -24px;
  top: 4px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg0);
  box-shadow: 0 0 0 1px var(--accent);
}

.timeline-node.seed::before { background: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* Timeline cells now reuse the shared .feed-card markup (identical to the Feed
   page). The thread spine + node dot come from .timeline-thread / .timeline-node
   above; the card itself is styled by the .feed-card rules. A card that
   deep-links to the Feed page gets a pointer + hover lift. */
.timeline-node .feed-card { margin: 0; }

.timeline-node .feed-card.clickable {
  cursor: pointer;
  transition: box-shadow .15s, border-color .15s;
}
.timeline-node .feed-card.clickable:hover {
  border-color: var(--border2);
  box-shadow: var(--shadow-md);
}

/* Sub-track control — replaces the Track button on timeline cells. Seeds a
   child story ("sub-thread") from this news item. */
.subtrack-btn {
  margin-left: auto;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  color: var(--text2);
  transition: color .15s;
  padding: 2px 4px;
}
.subtrack-btn:hover { color: var(--accent); }
.subtrack-btn.on { color: var(--accent); font-weight: 700; }
.light-mode .subtrack-btn { color: #6b6b66; opacity: .8; }
.light-mode .subtrack-btn:hover,
.light-mode .subtrack-btn.on { color: var(--accent); opacity: 1; }

/* Nested sub-thread indicator shown under a sub-tracked cell. */
.subthread {
  margin: 4px 0 0 20px;
  padding-left: 12px;
  border-left: 2px solid var(--accent-soft);
}
.subthread-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 0;
  text-decoration: none;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--accent);
}
.subthread-link:hover .subthread-title { text-decoration: underline; }
.subthread-branch { font-size: 12px; }
.subthread-title { color: var(--text2); font-weight: 600; }
.subthread-count { color: var(--text); }

.timeline-node-source {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 9px;
}

.timeline-node-source a { color: var(--accent); text-decoration: none; }
.timeline-node-source a:hover { text-decoration: underline; }

.origin-tag {
  font-family: var(--font-mono);
  font-size: 7px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  background: var(--bg1);
  color: var(--text);
}

/* ===== Story tags (cross-link from a feed/timeline item to its tracked story) ===== */

.story-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.story-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .2px;
  padding: 2px 8px 2px 7px;
  border-radius: 999px;
  text-decoration: none;
  color: var(--accent);
  border: 1px solid var(--accent-soft);
  background: var(--accent-soft);
  transition: border-color .15s, background .15s;
}

.story-tag:hover { border-color: var(--accent); }

.story-tag-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}

/* The flag tag — first chip in the row, on its own hue so it stays distinct
   from the --accent story chips it leads in BOTH themes. */
.story-tag.flag-tag {
  color: var(--flag);
  border-color: var(--flag-line);
  background: var(--flag-soft);
  cursor: default;
}

/* Resolved / archived stories: greyed out, but still linked. */
.story-tag.muted {
  color: var(--text2);
  border-color: var(--border2);
  background: transparent;
  opacity: .7;
}

.story-tag.muted .story-tag-dot { background: var(--text2); }

.node-sev {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  color: var(--accent2);
  letter-spacing: .3px;
}

/* ===== Empty states ===== */

.empty-note {
  padding: 24px;
  text-align: center;
  font-family: var(--font-display);
  font-size: 10px;
  color: var(--text);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  opacity: .5;
}

/* ===== Home stat strip ===== */

.home-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
  margin-bottom: 8px;
}

.home-stat-cell {
  padding: 12px 14px;
  text-align: center;
}

.home-stat-val {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 700;
  color: var(--accent);
}

.home-stat-lbl {
  font-family: var(--font-display);
  font-size: 8px;
  color: var(--text);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  margin-top: 4px;
}

.digest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 8px;
}

/* ===== Responsive ===== */

@media (max-width: 720px) {
  .tracker-layout { flex-direction: column; }
  .tracker-rail { width: 100%; max-height: 260px; overflow-y: auto; }

  /* Stack the node image on top, full-width and centred, instead of a
     cramped left thumbnail — reads far better on a narrow screen. */
  .timeline-node-card.has-img { flex-direction: column; }
  .timeline-node-thumb {
    width: 100%;
    height: 180px;
    align-self: auto;
  }
  .timeline-node-card.has-img .timeline-node-content { padding: 12px 14px; }

  .suggest-grid { grid-template-columns: 1fr; }
  .manage-form-row { flex-direction: column; }
  .manage-row { flex-wrap: wrap; }
}

@media (max-width: 640px) {
  .page-wrap { padding: 20px 16px 48px; }

  /* The nav's phone layout lives in tokens.css (map.html loads that but not
     this file, and the bar has to fit there too). */

  .page-title { font-size: 22px; }
  .tracker-toolbar { gap: 8px; }
}

/* ===== Progressive reveal + render performance ===== */

/* Mirrors the personal site's scroll reveal: reveal.js adds .visible as each
   block scrolls into view, so sections appear one at a time instead of all
   at once. */
.animate-on-scroll {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .5s ease, transform .5s ease;
  will-change: opacity, transform;
}

.animate-on-scroll.visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .animate-on-scroll {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Let the browser skip layout/paint for off-screen items in the long lists,
   so a big feed or timeline doesn't render everything up front.
   contain-intrinsic-size reserves an approximate height to keep scrolling
   stable before an item is rendered. */
.rest-row     { content-visibility: auto; contain-intrinsic-size: 0 46px; }
.feed-card    { content-visibility: auto; contain-intrinsic-size: 0 96px; }
.timeline-node{ content-visibility: auto; contain-intrinsic-size: 0 130px; }
.story-tile   { content-visibility: auto; contain-intrinsic-size: 0 220px; }

/* Timeline progressive-load indicator — shown at the bottom of the thread
   while renderMain() streams the remaining nodes in, removed on the last
   chunk. Signals "still loading", so a big story never looks frozen. */
.timeline-loading {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 4px;
  color: var(--text);
  font-size: 0.85rem;
}
.timeline-loading-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  animation: timeline-loading-pulse 1s ease-in-out infinite;
}
@keyframes timeline-loading-pulse {
  0%, 100% { opacity: 0.25; transform: scale(0.8); }
  50%      { opacity: 1;    transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .timeline-loading-dot { animation: none; opacity: 0.6; }
}

/* ── Source picker ───────────────────────────────────────────
   The per-story source multi-select: on the timeline header, in the
   Add-story form, and in the inline editor. Everything is selected by
   default, so the control's real job is showing what's OFF and flagging a
   typed-in source we couldn't place — hence the muted chips and the one
   alarm colour on `not-found`. No new hues: --accent for selected,
   --accent2 for the one thing that needs attention. */
.source-picker {
  margin-top: 12px;
  border-top: 1px solid var(--border2);
  padding-top: 10px;
}
.manage-edit .source-picker,
.manage-form .source-picker { margin-top: 4px; }

.source-picker-head {
  font-family: var(--font-display);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text2);
  opacity: .75;
  margin-bottom: 6px;
}

.source-picker-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.source-picker-count {
  font-size: 12px;
  color: var(--text2);
}
.source-picker-count strong { color: var(--text); }

/* The only loud thing in the control: a user-typed source that needs a look. */
.source-flag {
  font-family: var(--font-display);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .8px;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  color: var(--accent2);
  border: 1px solid var(--accent2);
}

.source-picker-body { margin-top: 10px; }

.source-picker-tools {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}
.source-filter,
.source-add-input {
  flex: 1;
  min-width: 0;
  padding: 7px 10px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--text);
  background: var(--bg1);
  border: 1px solid var(--border2);
  border-radius: var(--radius-sm);
}
.source-filter:focus,
.source-add-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.source-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-height: 210px;
  overflow-y: auto;
  padding: 2px;
}

/* Off is the state worth reading at a glance, so selected chips stay quiet
   and deselected ones go flat + struck through. */
.source-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-body);
  font-size: 12px;
  padding: 4px 9px;
  border-radius: 999px;
  border: 1px solid var(--border2);
  background: var(--bg1);
  color: var(--text2);
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s, opacity .12s;
}
.source-chip:hover { border-color: var(--accent); }
.source-chip.on {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--text);
}
.source-chip:not(.on) {
  opacity: .5;
  text-decoration: line-through;
}
.source-chip-tick {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  border: 1px solid var(--border2);
  flex: none;
  position: relative;
}
.source-chip.on .source-chip-tick {
  background: var(--accent);
  border-color: var(--accent);
}
.source-chip.on .source-chip-tick::after {
  content: '';
  position: absolute;
  left: 3px;
  top: 0;
  width: 3px;
  height: 7px;
  border: solid var(--on-accent);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.source-chip.flash { animation: source-chip-flash .6s ease 2; }
@keyframes source-chip-flash {
  0%, 100% { box-shadow: 0 0 0 0 var(--accent-soft); }
  50%      { box-shadow: 0 0 0 4px var(--accent-soft); }
}

.source-add {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-top: 10px;
}

.source-added-list:not(:empty) { margin-top: 8px; display: grid; gap: 5px; }
.source-added {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  padding: 6px 9px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border2);
  background: var(--bg1);
}
.source-added-name { color: var(--text); font-weight: 600; }
.source-added-note { color: var(--text2); opacity: .8; }
.source-added-x {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--text2);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  padding: 0 2px;
}
.source-added-x:hover { color: var(--accent2); }
.source-added.corrected { border-color: var(--accent); }
.source-added.corrected .source-added-note { color: var(--accent); opacity: 1; }
/* Couldn't be found at all — the one case the user has to act on. Distinct
   from a real source that simply had no news, which is never flagged. */
.source-added.not-found {
  border-color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 7%, transparent);
}
.source-added.not-found .source-added-note { color: var(--accent2); opacity: 1; }
.source-added.not-found .source-added-name { text-decoration: line-through; }

.source-picker-note {
  margin-top: 8px;
  font-size: 11px;
  color: var(--text2);
  opacity: .65;
}

@media (max-width: 620px) {
  .source-picker-tools,
  .source-add { flex-wrap: wrap; }
  .source-chips { max-height: 160px; }
}

/* ── Source picker: category groups ──────────────────────────────────
   250+ sources across ~18 categories, so the chip list is grouped and
   collapsed by default — the common case is "leave it alone", and the
   summary line already says how many are on. YouTube groups carry a badge
   because those sources behave differently in one visible way: their videos
   never appear on the Feed, only inside a story. */
.source-groups {
  max-height: 330px;
  overflow-y: auto;
  border: 1px solid var(--border2);
  border-radius: var(--radius-sm);
  background: var(--bg1);
}

.source-group + .source-group { border-top: 1px solid var(--border2); }

.source-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  cursor: pointer;
  user-select: none;
}
.source-group-head:hover { background: var(--bg2); }

.source-group-caret {
  font-size: 10px;
  color: var(--text2);
  opacity: .7;
  width: 10px;
  flex: none;
}

.source-group-badge {
  font-family: var(--font-display);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: .8px;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 3px;
  border: 1px solid var(--border2);
  color: var(--text2);
  opacity: .8;
  flex: none;
}

.source-group-name {
  font-size: 13px;
  color: var(--text);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.source-group-count {
  font-family: var(--font-mono, monospace);
  font-size: 11px;
  color: var(--text2);
  opacity: .7;
  flex: none;
}
/* Something inside is switched off — the one thing worth spotting while
   the group is collapsed. */
.source-group-count[data-partial] {
  color: var(--accent2);
  opacity: 1;
  font-weight: 600;
}

.source-group-acts { display: flex; gap: 4px; flex: none; opacity: 0; transition: opacity .12s; }
.source-group-head:hover .source-group-acts,
.source-group.open .source-group-acts { opacity: 1; }

.source-group-btn {
  font-family: var(--font-display);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 3px;
  border: 1px solid var(--border2);
  background: var(--card);
  color: var(--text2);
  cursor: pointer;
}
.source-group-btn:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

.source-group .source-chips {
  max-height: none;
  padding: 2px 10px 10px 28px;
}

@media (max-width: 620px) {
  .source-groups { max-height: 260px; }
  .source-group-acts { opacity: 1; }
  .source-group .source-chips { padding-left: 12px; }
}

/* ── YouTube beats on a story timeline ───────────────────────────────
   A video reaches the site only by being matched onto a story, so when one
   does show up it should read as a video and not as another wire headline. */
.timeline-node .feed-card .origin-tag.origin-youtube::before {
  content: '▶ ';
  letter-spacing: 0;
}

/* ── Source picker: search mode ──────────────────────────────────────
   Typing in the search box turns the grouped list into a plain flat list of
   matching tags. The chips keep their place in the DOM (so reading the
   selection back is unaffected) — the group chrome just gets out of the way,
   which is what makes it read as one list of results rather than eighteen
   categories opening and closing. */
.source-picker.searching .source-group-head { display: none; }
.source-picker.searching .source-group + .source-group { border-top: none; }
.source-picker.searching .source-group .source-chips { padding: 0 10px; }
.source-picker.searching .source-groups { padding: 8px 0; }
/* Select all / Clear all act on the visible matches while searching, so the
   pair reads as "select everything I just searched for". */
.source-picker.searching .source-group:first-child .source-chips { padding-top: 2px; }

.source-search-note {
  margin: -2px 0 8px;
  font-size: 11px;
  color: var(--text2);
  opacity: .7;
}
.source-search-note.empty { color: var(--accent2); opacity: 1; }
