/* page.css — shared styles for frontpage and article templates.
   Loaded statically so the page is styled on first paint, before page.js runs. */

@font-face {
  font-family: 'Fira Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/fira-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Fira Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/fira-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Injection points for dynamic content (page.js); hidden until filled so
   they never add gaps — also covers noscript, where they stay empty. */
[data-slot]:empty { display: none; }

/* Topic theme colors — kept in sync with TOPICS in harvester/config.py.
   Related topics share a hue family so the UI reads coherently. */
:root {
  /* Shared by #site-header and the popup backdrop, which offsets itself by
     this much so popups open below the header instead of over it. */
  --header-height: 3rem;

  /* Topic colors. Deep and saturated so they hold up as small bold text on the
     light-gray .meta pill, the only place they were ever shown: each clears
     4.5:1 against it. Kept for reuse — the pill's labels currently read gray and
     are told apart by their emoji instead. Related topics share a hue family;
     the grouping mirrors harvester/config.py's TOPICS list. */
  --topic-politics: #2760f9;
  --topic-international_relations: #066bdd;
  --topic-economy: #037e52;
  --topic-business: #037d65;
  --topic-technology: #893efa;
  --topic-science: #ae06f5;
  --topic-environment: #037a87;
  --topic-health: #037c70;
  --topic-conflict: #de060d;
  --topic-crime: #d22e05;
  --topic-law: #dc063a;
  --topic-culture: #ab5804;
  --topic-entertainment: #8b6904;
  --topic-education: #996304;
  --topic-religion: #c0460a;
  --topic-sports: #d4057d;
  --topic-other: #59708f;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: "Fira Sans", Verdana, Geneva, sans-serif;
  background: #fff; color: #222; line-height: 1.33;
}

#site-header {
  display: flex; align-items: center; gap: 2rem;
  height: var(--header-height);
  padding: 0 2rem;
  background: #111; color: #fff;
}
.site-brand { color: #fff; font-size: 1.2rem; font-weight: bold; text-decoration: none; display: flex; align-items: center; }
.site-logo { height: 2.25rem; width: auto; display: block; }
#site-nav a { color: #ccc; text-decoration: none; font-size: 0.9rem; }
#site-nav a:hover { color: #fff; }

.lang-switch { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; }
.lang-flag { display: flex; line-height: 0; text-decoration: none; opacity: 0.55; }
.lang-flag img { height: 1.4rem; width: auto; display: block; }
.lang-flag:hover { opacity: 1; }
.lang-flag.active { opacity: 1; }

/* Menu button, injected by page.js right after the flags. Sits in the header's
   right-aligned group with its own margin instead of the header's 2rem gap, so
   it reads as part of the flag row. The generic button rules further down would
   give it a dark pill; reset those here. */
.menu-button {
  display: flex; align-items: center;
  margin-left: 1rem;
  padding: 0;
  border: 0; border-radius: 0;
  background: none;
  line-height: 0;
  opacity: 0.55;
  cursor: pointer;
}
.menu-button:hover { background: none; opacity: 1; }
.menu-button img { height: 1.4rem; width: auto; display: block; }

article {
  max-width: 720px; margin: 2.5rem auto; padding: 0 1rem;
}
article h1 { font-size: 1.4rem; line-height: 1.3; margin-bottom: 0.5rem; }
article p { margin-bottom: 1.25rem; }
article nav { margin-top: 2rem; font-size: 0.9rem; }
article nav a { display: block; color: #0066cc; margin-bottom: 0.4rem; }
article footer { margin-top: 2rem; color: #555; font-size: 0.85rem; }

/* Keywords close the article, below the copy/search/follow rows page.js anchors
   above them. Light gray and small — they are classification rather than reading
   matter — but shown, not hidden: the same terms are served to crawlers in
   news_keywords and article:tag, and text a crawler reads while the reader
   can't is exactly the pattern a keyword-stuffing penalty looks for. */
#article-keywords {
  margin-top: 1.5rem;
  color: #999;
  font-size: 0.75rem;
}

.headline {
  font-size: 1rem;
  font-weight: bold;
  text-decoration: none;
  color: inherit;
}
.headline:hover { text-decoration: underline; }

/* Both the frontpage list and the article page carry the section emoji, the time
   and their topics on one line below the headline. No background and no indent,
   so the line reads as a caption hanging off the headline: the emoji that leads
   it sits flush with the headline's first letter, and gray keeps the text
   quieter than the story. */
.meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  color: #888;
  font-size: 0.8rem;
  margin-top: 0.15rem;
}
.meta .section { color: inherit; }
/* On an article the pill is followed by the story itself, so it needs the gap
   the bare <time> used to carry. Frontpage items get theirs from .articles
   article, and #article is the article page's own <article> — not a list item. */
#article .meta { margin-bottom: 1.5rem; }

/* Separator and topic emoji in one pseudo-element — the markup has no room for
   two, so the middot that divides the items also carries the emoji that leads
   each label. The middot stays gray so it separates without competing;
   --topic-emoji comes from the per-topic rules below, with an empty fallback so
   an unrecognized topic still gets its separator. */
.meta .topic-bar-item::before {
  content: "·\00a0" var(--topic-emoji, "");
  color: #aaa;
  font-weight: normal;
  margin: 0 0.45em;
}

/* The section, as an emoji leading the .meta line. It keeps the meta line's own
   size rather than shrinking further — at 0.8rem it is already a marker beside
   the gray text, and the flex parent centers it against the date it precedes. */
.section-emoji {
  line-height: 1;
  margin-right: 0.4em;
}
/* The glyph itself, keyed by the section's i18n key which the markup carries as
   a class — the same arrangement as the topic emoji above, and for the same
   reason: the frontpage repeats the section once per article, so it belongs in
   one stylesheet rather than in every generated page. Keep in step with SECTIONS
   in harvester/config.py; a section with no rule here still gets its span, and
   shows as a gap where the emoji would be. Pages generated before this moved to
   CSS carry the emoji inline and no section class, so they keep rendering as
   they were until they are next regenerated. */
.section-emoji.section_ai::before { content: "🤖"; }
.section-emoji.section_world_news::before { content: "🌐"; }

.topic-bar-item { text-transform: capitalize; font-weight: bold; }

/* Tighter spacing between articles in the frontpage list. */
.articles article { margin: 1.25rem auto; }

/* Each frontpage headline is wrapped in an <h2> so the list has real structure
   for a screen reader. That wrapper is semantic only: the browser default h2
   size and margins are cleared here so the link keeps looking exactly as it did
   when it was a bare <a>, with .headline still supplying the size and weight. */
.articles h2 { font-size: inherit; font-weight: inherit; margin: 0; }

/* The frontpage's own <h1>. Deliberately visible rather than screen-reader-only:
   a heading the crawler reads and the reader cannot see is the same pattern the
   keyword note above avoids. Sized close to the headlines it introduces so it
   labels the list without becoming the loudest thing on the page. */
#frontpage-heading {
  font-size: 1.1rem;
  margin: 0 0 1rem;
}

/* Per-topic color and emoji, keyed by the topic name the markup adds as a class.
   The emoji is drawn by the .meta rule above; the color is kept here but is
   currently overridden (see below). Emoji that have a text presentation carry an
   explicit U+FE0F so they never render as gray glyphs. */
.topic-bar-item.politics { color: var(--topic-politics); --topic-emoji: "🏛\FE0F"; }
.topic-bar-item.international_relations { color: var(--topic-international_relations); --topic-emoji: "🤝"; }
.topic-bar-item.economy { color: var(--topic-economy); --topic-emoji: "📈"; }
.topic-bar-item.business { color: var(--topic-business); --topic-emoji: "💰"; }
.topic-bar-item.technology { color: var(--topic-technology); --topic-emoji: "💡"; }
.topic-bar-item.science { color: var(--topic-science); --topic-emoji: "🔬"; }
.topic-bar-item.environment { color: var(--topic-environment); --topic-emoji: "🌱"; }
.topic-bar-item.health { color: var(--topic-health); --topic-emoji: "🩺"; }
.topic-bar-item.conflict { color: var(--topic-conflict); --topic-emoji: "⚔\FE0F"; }
.topic-bar-item.crime { color: var(--topic-crime); --topic-emoji: "🚓"; }
.topic-bar-item.law { color: var(--topic-law); --topic-emoji: "⚖\FE0F"; }
.topic-bar-item.culture { color: var(--topic-culture); --topic-emoji: "🎭"; }
.topic-bar-item.entertainment { color: var(--topic-entertainment); --topic-emoji: "🍿"; }
.topic-bar-item.education { color: var(--topic-education); --topic-emoji: "🎓"; }
.topic-bar-item.religion { color: var(--topic-religion); --topic-emoji: "🕊️"; }
.topic-bar-item.sports { color: var(--topic-sports); --topic-emoji: "🏆"; }
.topic-bar-item.other { color: var(--topic-other); --topic-emoji: "🗂\FE0F"; }

/* "other" says nothing a reader can use, so it stays out of the pill — dropped
   as a whole flex item, which takes its separator with it. It remains in the
   markup, where it still carries the classification. */
.topic-bar-item.other { display: none; }

/* For now the labels read in the pill's own gray, like the time beside them, so
   the emoji carries the topic's identity and the bar stays quiet. Last so it
   wins over the equally specific per-topic colors above, which stay defined. */
.meta .topic-bar-item { color: inherit; }

/* The frontpage stacks many of these lines under each other, where a column of
   topic emoji competes with the section emoji leading each headline — so there
   the meta line runs on labels alone. Emptying --topic-emoji rather than hiding
   the pseudo-element keeps the middot that separates the labels. Article pages
   show one meta line and keep their emoji. Last, and equally specific to the
   per-topic rules above, so it wins over the emoji they define. */
.articles .topic-bar-item { --topic-emoji: ""; }

#site-footer {
  max-width: 720px;
  margin: 4rem auto 2rem;
  padding: 0 1rem;
  text-align: center;
  color: #888;
  font-size: 0.8rem;
}
#site-footer hr {
  border: 0;
  border-top: 1px solid #ddd;
  margin: 0 0 1rem;
}

/* AI disclaimer, injected by page.js into the before-frontpage / before-article
   slot. Same column as the content below it, but quiet: small and gray so it
   informs without pulling attention from the story. */
.ai-disclaimer {
  max-width: 720px;
  margin: 1.5rem auto 0;
  padding: 0 1rem;
  color: #888;
  font-size: 0.8rem;
  line-height: 1.5;
}
/* Divider between the disclaimer and the content it precedes. A pseudo-element
   rather than a border on the <p>: it sits inside the content box, so the rule is
   inset by the 1rem padding and lines up with the text — and with #site-footer hr,
   which is inset the same way by its own container's padding. */
.ai-disclaimer::after {
  content: "";
  display: block;
  border-top: 1px solid #ddd;
  margin-top: 1rem;
}

/* Where the story came from: the original reports a search found for this event,
   each carrying its own headline, listed between the body and the rows below.
   Plain list, no pills — provenance a reader can check rather than a call to
   action. One source per line, since a real headline is long enough to wrap. */
.sources {
  margin: 2rem 0 0;
  font-size: 0.9rem;
}
.sources p {
  margin: 0 0 0.4rem;
  color: #666;
}
.sources ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
.sources li { margin: 0 0 0.4rem; line-height: 1.4; }
.sources a { color: #0066cc; }
/* The publisher, trailing the headline it belongs to. Gray and small so the
   headline stays the thing being read, but present: which outlet reported it is
   half of what makes a source worth listing. */
.source-host {
  margin-left: 0.4rem;
  color: #888;
  font-size: 0.75rem;
  white-space: nowrap;
}

/* The three rows page.js closes an article with — copy link, related search,
   follow — injected after the sources (or above the keywords when there are
   none). Quiet by design: they follow the story rather than compete with it.
   The first carries the gap from the article; the rest sit tight under it so
   the three read as one block. */
.article-share {
  margin: 2rem 0 0;
  font-size: 0.9rem;
}
.related-search {
  margin: 1.25rem 0 0;
  font-size: 0.9rem;
}
.related-search-label {
  margin: 0 0 0.4rem;
  color: #666;
}
.related-search-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.5rem;
  margin: 0;
}
/* Each engine is a pill, same shape as the .meta bar but light blue instead of
   gray — a destination to click rather than a line to read. */
.related-search-links a {
  padding: 0.2em 0.9em;
  border-radius: 9999px;
  background: #e8f1fb;
  color: #0066cc;
  font-size: 0.75rem;
  font-weight: bold;
  text-decoration: none;
}
.related-search-links a:hover { background: #d6e6f8; }

/* Follow box, injected by page.js directly below the related-news row. The only
   framed row of the three: the box is the one that asks something of the reader,
   so it is set apart and centered rather than run on with the left-aligned rows
   above it. Same hairline and radius as .newsletter-cta, which makes the same
   kind of ask. */
.retention-box {
  margin: 1.5rem 0 0;
  padding: 0.9rem 1rem;
  border: 1px solid #ddd;
  border-radius: 10px;
  font-size: 0.9rem;
  text-align: center;
}
.retention-label {
  margin: 0 0 0.5rem;
  color: #666;
}
.action-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.5rem;
  margin: 0;
}
/* A flex row ignores the box's inherited text-align, so its pills need centering
   of their own. */
.retention-box .action-row { justify-content: center; }
/* One rule for every pill outside the search row, whether it is a <button> that
   acts on the page or an <a> that leaves for Google or Bluesky. Same shape as
   the blue search pills, green instead: those go looking for the story
   elsewhere, these stay with us. The font and border declarations are what a
   button needs to stop looking like a form control. */
.action-pill {
  margin: 0;
  padding: 0.2em 0.9em;
  border: 0;
  border-radius: 9999px;
  background: #e6f2ea;
  color: #16704a;
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: bold;
  line-height: inherit;
  text-decoration: none;
  cursor: pointer;
}
.action-pill:hover { background: #d6e9dd; }
/* The two seconds after a successful copy. */
.action-pill.copied {
  background: #16704a;
  color: #fff;
  cursor: default;
}
.action-pill[disabled] {
  opacity: 0.6;
  cursor: default;
}

/* A line or two of plain instructions — the body of the bookmark and iOS
   install popups, where the browser gives JavaScript nothing to do but explain
   the steps. */
.popup-hint {
  margin: 0;
  line-height: 1.5;
}

/* Newsletter call-to-action, injected by page.js into the after-article slot. */
.newsletter-cta {
  max-width: 720px;
  margin: 3rem auto;
  padding: 1.75rem 1rem;
  border: 1px solid #ddd;
  border-radius: 10px;
  background: #fafafa;
  text-align: center;
}
.newsletter-cta-text {
  margin-bottom: 1rem;
  font-size: 1.1rem;
  font-weight: bold;
  color: #222;
}
.newsletter-cta-button {
  display: inline-block;
  padding: 0.6rem 1.5rem;
  background: #111;
  color: #fff;
  text-decoration: none;
  border-radius: 9999px;
  font-size: 0.95rem;
}
.newsletter-cta-button:hover { background: #333; }

/* Form controls (e.g. the newsletter signup): rounded, inheriting the body font
   so they match surrounding text. login.html keeps its own .auth-scoped form
   styles, which win on specificity, so these don't affect it. */
input, button {
  font: inherit;
  border-radius: 6px;
}
input {
  padding: 0.5rem 0.7rem;
  border: 1px solid #ccc;
  background: #fff;
  color: #222;
}
input:focus { outline: none; border-color: #0066cc; }
button {
  padding: 0.55rem 1.3rem;
  border: 0;
  background: #111;
  color: #fff;
  cursor: pointer;
}
button:hover { background: #333; }
button:disabled { opacity: 0.5; cursor: default; }

/* Popups (createPopup() in page.js) ─────────────────────────────────────────
   Generic modal chrome: a dimming backdrop that fills the viewport with the
   panel anchored to its top-right corner. Only the panel's contents are
   popup-specific, so any future popup gets the backdrop, the close button and
   the placement for free. */
.popup-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  /* The top margin clears the header, so the menu button that opened the
     popup stays visible behind it rather than being covered. */
  padding: calc(var(--header-height) + 0.5rem) 1rem 1rem;
  background: rgba(0, 0, 0, 0.6);
}
/* display:flex above would otherwise beat the browser's [hidden] rule. */
.popup-backdrop[hidden] { display: none; }

.popup {
  position: relative;
  min-width: 220px;
  max-width: min(90vw, 420px);
  /* Against the backdrop's content box, so the panel scrolls internally
     instead of running past the bottom of the viewport. */
  max-height: 100%;
  overflow-y: auto;
  padding: 3rem 2rem 2rem;   /* top padding clears the close button */
  border-radius: 10px;
  background: #fff;
  color: #222;
}

/* Close button, top-left of the panel. Overrides the generic button styling
   below so it reads as a quiet icon rather than a dark action button. */
.popup-close {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: #888;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}
.popup-close:hover { background: #f0f0f0; color: #222; }

/* Stop the page behind the backdrop from scrolling while a popup is open. */
body.popup-open { overflow: hidden; }

/* A stacked list of links — the menu popup's body, and the default shape for
   any later popup that is just a set of destinations. */
.popup-menu { display: flex; flex-direction: column; gap: 0.9rem; }
.popup-menu a {
  color: #0066cc;
  text-decoration: none;
  font-size: 1.05rem;
}
.popup-menu a:hover { text-decoration: underline; }

/* Newsletter signup form (pages/<lang>/newsletter.json -> #newsletter-form).
   Submit is handled by attachNewsletter() in page.js. */
#newsletter-form input[type="email"] { width: 100%; max-width: 360px; }
.newsletter-status { min-height: 1.2rem; font-size: 0.9rem; color: #555; }
.newsletter-status.error { color: #c0392b; }
