/* ==========================================================================
   12F — stylesheet
   Direction: WSJ front-page density meets Soar/flysoar.ai futurism.
   Light mode is the default (WSJ-paper feel); dark mode is a toggle
   (data-theme="dark" on <html>). The sticky ticker and the cover-art
   illustration tiles are intentionally always-dark "chrome" — a fixed
   signature accent that doesn't flip with the theme, same as a printed
   masthead's ever-black nameplate rule. The logo swaps per theme (see
   #logoMasthead / #logoFooter in the markup + the script at the bottom).
   ========================================================================== */

:root{
    /* ---- Paper (light, default) ---- */
    --bg:#FFFFFF;
    --bg-elev:#F5F4EF;
    --surface:#F7F6F1;
    --surface-2:#EFEDE6;
    --border:rgba(20,18,15,0.11);
    --border-strong:rgba(20,18,15,0.20);
    --text:#17171B;
    --text-dim:#54525A;
    --text-faint:#84828A;

    --accent:#2F5FDB;        --accent-rgb:47,95,219;
    --accent-dim:rgba(47,95,219,0.10);
    --brass:#96731E;         --brass-rgb:150,115,30;
    --live:#D93636;          --live-rgb:217,54,54;
    --up:#1E9A61;            --up-rgb:30,154,97;
    --down:#D64545;          --down-rgb:214,69,69;
    --shadow-rgb:20,18,15;
    --cat-glyph-rgb:0,0,0;

    --radius:16px;
    color-scheme: light;
}

:root[data-theme="dark"]{
    /* ---- Night terminal (toggle) ---- */
    --bg:#0A0A0E;
    --bg-elev:#111114;
    --surface:#15151A;
    --surface-2:#1B1B21;
    --border:rgba(255,255,255,0.08);
    --border-strong:rgba(255,255,255,0.14);
    --text:#F3F2ED;
    --text-dim:#9C9CA3;
    --text-faint:#66666E;

    --accent:#5B8CFF;        --accent-rgb:91,140,255;
    --accent-dim:rgba(91,140,255,0.14);
    --brass:#C9A961;         --brass-rgb:201,169,97;
    --live:#FF4B4B;          --live-rgb:255,75,75;
    --up:#3DD68C;            --up-rgb:61,214,140;
    --down:#FF6B6B;          --down-rgb:255,107,107;
    --shadow-rgb:0,0,0;
    --cat-glyph-rgb:255,255,255;

    color-scheme: dark;
}

html{transition:none;}
body, .icon-btn, .briefing, .cover-art{
  transition:background-color .2s ease, border-color .2s ease, color .2s ease;
}

*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
  background:
    radial-gradient(1200px 600px at 15% -10%, rgba(var(--accent-rgb),0.10), transparent 60%),
    radial-gradient(1000px 500px at 90% 0%, rgba(var(--brass-rgb),0.08), transparent 55%),
    var(--bg);
  color:var(--text);
  font-family:'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  -webkit-font-smoothing:antialiased;
  min-height:100vh;
}
a{color:inherit; text-decoration:none;}
.mono{font-family:'IBM Plex Mono', monospace;}
.serif{font-family:'Newsreader', serif;}

/* ---------- Ticker — shared building blocks for the Live Feed ----------
   The site's only live feed is .live-feed, defined further down -- it sits
   directly below the hero (not inside it) on every breakpoint, docking to
   the top of the viewport via position:sticky once a visitor scrolls past
   it. These base .ticker-tag / .ticker-dot / .ticker-track / .ticker-item
   rules supply its shared layout (flex, gap, animation) and are then
   re-themed/scoped by the .live-feed rules below. There is no separate
   always-on band inside the hero anymore, and no duplicate band at the top
   of the header either. */
.ticker-tag{
  flex:0 0 auto;
  display:flex;
  align-items:center;
  gap:6px;
  padding:0 16px;
  height:100%;
  background:rgba(var(--live-rgb),0.10);
  border-right:1px solid var(--border);
  color:var(--live);
  font-size:11.5px;
  font-weight:700;
  letter-spacing:.08em;
}
.ticker-dot{width:6px;height:6px;border-radius:50%;background:var(--live);box-shadow:0 0 8px var(--live); animation:pulse 1.6s infinite;}
@keyframes pulse{0%,100%{opacity:1;}50%{opacity:.35;}}
.ticker-track{
  display:flex;
  gap:52px;
  padding-left:24px;
  animation:scroll 40s linear infinite;
}
@keyframes scroll{
  0%{transform:translateX(0);}
  100%{transform:translateX(-50%);}
}
.ticker-item{font-size:13px; color:var(--text-dim); display:flex; gap:8px; align-items:center; cursor:pointer;}
.ticker-item:hover{color:var(--text);}
.ticker-item:hover .src{color:var(--accent);}
.ticker-item b{color:var(--text); font-weight:600;}
.ticker-item .src{color:var(--text-faint); font-size:11px;}

/* ---------- Masthead — WSJ-style centered nameplate ---------- */
.masthead-utility{
  display:flex; align-items:center; justify-content:space-between;
  padding:10px clamp(16px,4vw,48px);
  border-bottom:1px solid var(--border);
  font-size:12px; color:var(--text-faint);
}
.masthead-utility .utility-left{display:flex; align-items:center; gap:8px; white-space:nowrap;}
.masthead-utility .utility-right{display:flex; align-items:center; gap:18px;}
.masthead-utility .utility-right a{color:var(--text-dim); font-weight:500; white-space:nowrap;}
.masthead-utility .utility-right a:hover{color:var(--text);}
.masthead-utility .icon-btn{width:30px; height:30px; flex:0 0 auto;}
@media (max-width:640px){
  .masthead-utility .utility-right a{display:none;}
  .masthead-utility .utility-right{gap:10px;}
}

/* Restored WSJ-style centered nameplate (the site's original masthead
   treatment, from before the A24-style cinematic hero existed). Sits
   between the utility row and the rule/nav-chip row below it. Swapped
   between the light/dark logo PNGs by applyTheme() in index.html, same
   asset pair the footer's own adaptive logo already uses. */
.masthead-logo{text-align:center; padding:26px 16px 22px;}
.masthead-logo img{
  height:112px; width:auto; margin:0 auto; display:inline-block;
  filter:drop-shadow(0 0 34px rgba(var(--accent-rgb),0.18));
}
@media (max-width:840px){
  .masthead-logo{padding:20px 16px 16px;}
  .masthead-logo img{height:74px;}
}

.masthead-rule{
  height:1px; margin:0 clamp(16px,4vw,48px);
  background:linear-gradient(90deg, transparent, var(--border-strong) 12%, var(--border-strong) 88%, transparent);
}

.icon-btn{
  width:36px;height:36px;border-radius:10px;
  border:1px solid var(--border);
  background:none; appearance:none; font:inherit; padding:0; margin:0;
  display:flex;align-items:center;justify-content:center;
  color:var(--text-dim); cursor:pointer;
}
.icon-btn:hover{border-color:var(--border-strong); color:var(--text);}
.btn-primary{
  background:var(--text); color:var(--bg); font-weight:600; font-size:12.5px;
  padding:7px 14px; border-radius:9px; border:none; cursor:pointer;
}

/* ---------- Chip row — the site's single category nav, under the masthead ---------- */
.search-band{padding:16px clamp(16px,4vw,48px) 0; text-align:center;}

.nav-row{
  display:flex; align-items:center; justify-content:center; gap:14px; flex-wrap:wrap;
}

.chip-row{
  display:flex; gap:8px; justify-content:center; flex-wrap:wrap;
  max-width:900px;
}
.chip{
  padding:7px 14px; border-radius:100px; border:1px solid var(--border);
  font-size:13px; color:var(--text-dim); cursor:pointer; font-weight:500;
  transition:.15s;
}
.chip:hover{border-color:var(--border-strong); color:var(--text);}
.chip.active{background:var(--text); color:var(--bg); border-color:var(--text);}

/* mobile — chips become a horizontally-scrollable strip instead of wrapping */
@media (max-width:640px){
  .search-band{padding:14px 0 0;}
  .nav-row{flex-wrap:nowrap; gap:8px; padding:0 16px;}
  .chip-row{
    flex-wrap:nowrap; justify-content:flex-start; max-width:none; flex:1; min-width:0;
    overflow-x:auto; scroll-snap-type:x proximity;
    -webkit-overflow-scrolling:touch; scrollbar-width:none;
    -webkit-mask-image:linear-gradient(to right, black calc(100% - 28px), transparent 100%);
    mask-image:linear-gradient(to right, black calc(100% - 28px), transparent 100%);
    padding-right:28px;
  }
  .chip-row::-webkit-scrollbar{display:none;}
  .chip{white-space:nowrap; flex:0 0 auto; scroll-snap-align:start;}
}

/* ---------- Kickers (small-caps section labels, WSJ dept-head style) ---------- */
.kicker{
  font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.1em;
  color:var(--brass); margin-bottom:12px; display:flex; align-items:center; gap:7px;
}
.kicker.live{color:var(--live);}
.kicker.live .dot{width:6px;height:6px;border-radius:50%;background:var(--live);box-shadow:0 0 8px var(--live); animation:pulse 1.6s infinite;}

.kicker-row{
  display:flex; align-items:baseline; justify-content:space-between; gap:12px;
  font-size:12px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--text);
  padding-bottom:10px; margin-bottom:22px; border-bottom:1px solid var(--border-strong);
}
.kicker-row .sort{font-size:11px; font-weight:500; letter-spacing:0; text-transform:none; color:var(--text-faint);}
.kicker-row .sort b{color:var(--text-dim); font-weight:600; cursor:pointer;}

/* shown when a category filter (chip click) matches nothing in a column */
.empty-note{
  font-size:12.5px; font-style:italic; color:var(--text-faint);
  padding:6px 0 2px; letter-spacing:0; text-transform:none; font-weight:400;
}

/* ---------- Front page — 3-column WSJ-density grid ---------- */
.front-page{
  max-width:1320px; margin:0 auto; padding:44px clamp(16px,4vw,48px) 0;
  display:grid; grid-template-columns:232px 1fr 300px; column-gap:0;
}
.rail{padding:0 26px;}
.rail-left{padding-left:0; border-right:1px solid var(--border);}
.lead-col{padding:0 30px; border-right:1px solid var(--border);}
.rail-right{padding-right:0;}

@media (max-width:1100px){
  .front-page{grid-template-columns:1fr; row-gap:40px;}
  .rail-left, .lead-col{border-right:none; padding:0; border-bottom:1px solid var(--border); padding-bottom:32px;}
  .rail-right{padding:0;}
  /* "Browse more" rail panels exist purely to fill leftover vertical space
     next to the (much taller) desktop lead column. Once the layout stacks
     to a single column there's no gap to fill, so hide them entirely. */
  .rail-explore{display:none !important;}
}

/* compact AI briefing card, sits atop the left rail */
.briefing{
  background:linear-gradient(180deg, var(--surface), var(--bg-elev));
  border:1px solid var(--border);
  border-radius:var(--radius);
  padding:20px 20px 18px;
  position:relative;
  margin-bottom:26px;
}
.briefing::before{
  content:"";
  position:absolute; inset:0; border-radius:var(--radius);
  padding:1px;
  background:linear-gradient(135deg, rgba(var(--accent-rgb),0.5), rgba(var(--brass-rgb),0.25), transparent 60%);
  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  pointer-events:none;
}
.briefing-head{display:flex; flex-direction:column; align-items:flex-start; gap:3px; margin-bottom:14px;}
.briefing-title{display:flex; align-items:center; gap:7px; font-weight:600; font-size:14px; white-space:nowrap;}
.briefing-title .dot{width:6px;height:6px;border-radius:50%;background:var(--up); box-shadow:0 0 8px var(--up); animation:pulse 2s infinite; flex:0 0 auto;}
.briefing-time{font-size:9.5px; color:var(--text-faint); text-transform:uppercase; letter-spacing:.08em; white-space:nowrap;}
.briefing ul{list-style:none; margin:0; padding:0;}
.briefing ul li{margin:0 0 9px; padding:0; color:var(--text); font-size:13px; font-weight:400; line-height:1.55;}
.briefing ul li:last-child{margin-bottom:0;}
.briefing ul b{font-weight:600;}
/* Block-level (not inline-block) so the source badge always sits on its
   own line below the excerpt, matching .digest-list li .meta's pattern --
   inline-block let it wrap up into the last line of a long excerpt
   whenever that line still had a little horizontal room left. */
.briefing .cite{
  display:block; margin-top:5px;
  font-size:9.5px; color:var(--text-faint);
  white-space:nowrap; max-width:100%; overflow:hidden; text-overflow:ellipsis;
}
.briefing .cite a{color:var(--text-faint);}
.briefing .cite a:hover{color:var(--accent); text-decoration:underline;}

/* dense digest list — What's News, WSJ style */
.digest-list{list-style:none; margin:0; padding:0;}
.digest-list li{padding:12px 0; border-bottom:1px solid var(--border); font-size:12.5px; line-height:1.5; color:var(--text-dim);}
.digest-list li:last-child{border-bottom:none;}
.digest-list li b{color:var(--text); font-weight:600;}
.digest-list li .meta{display:block; margin-top:5px; font-size:10px; color:var(--text-faint); letter-spacing:.02em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.digest-list li .meta a{color:var(--text-faint);}
.digest-list li .meta a:hover{color:var(--accent); text-decoration:underline;}

/* lead column — big story, then dense hairline-divided stories */
.lead-story h2{
  font-family:'Newsreader', serif; font-weight:700; text-transform:capitalize;
  font-size:clamp(30px,3.4vw,46px); line-height:1.1; margin:10px 0 12px; letter-spacing:-0.01em;
}
.lead-story .dek{font-size:14.5px; color:var(--text-dim); line-height:1.62; margin-bottom:12px;}
.byline{display:flex; gap:8px; align-items:center; flex-wrap:wrap; font-size:11px; color:var(--text-faint);}
.byline .source-tag{font-size:10.5px;}
.byline a{color:var(--text-faint);}
.byline a:hover{color:var(--accent); text-decoration:underline;}

/* real, hotlinked article photography — pulled from the original publisher.
   Every image frame on the site is strictly height-bounded (max-height,
   not just aspect-ratio) so a tall or oddly-shaped source photo can never
   push the page's vertical rhythm around, and every frame shares the same
   subtle 4px corner radius -- boxy, not pill-shaped -- across the whole
   site. object-fit:cover always fills its frame (no letterbox bars), and
   the frame's own background is a soft card tone rather than a stark
   black/white fill, so nothing shows through the edges while an image is
   still loading. The Top Stories hero anchors to the literal top of the
   photo (object-position:center top) since it's the one slot big enough
   that a bad crop is most noticeable; every other photo frame uses a
   gentler 20%-from-top bias, enough to keep most faces/subjects in frame
   without looking like a hard top-crop on every card. Neither is real
   subject/face detection -- portrait-oriented source photos are instead
   routed to a thumbnail layout entirely rather than cropped into a wide
   box; see is_portrait_image() in fetch_news.py and .cg-hero-fallback
   below. */
.media{margin:10px 0 4px;}
.cover-photo{
  width:100%; max-height:360px; aspect-ratio:16/9; border-radius:4px;
  object-fit:cover; object-position:center top;
  display:block;
  border:1px solid rgba(255,255,255,0.10);
  background:var(--surface-2);
}
/* .flat-item-photo/.cover-art below are no longer emitted by fetch_news.py
   (replaced by the .cg-hero-medium/.cg-hero-split cards further down), but
   browse.html's own JS still renders both classes for its archive cards --
   kept intact so that page's layout doesn't shift. */
.flat-item-photo .cover-photo{aspect-ratio:3/2;}

.story-row{display:flex; gap:16px; align-items:flex-start; margin-top:2px;}
.story-row .story-col{flex:1; min-width:0;}
.thumb{
  width:96px; height:72px; flex-shrink:0; object-fit:cover; object-position:50% 20%; border-radius:4px;
  border:1px solid var(--border); background:var(--surface-2);
}
/* Mobile: thumbnails lock to a flat 80x80 square (rather than 96x72) so a
   dense list stays tidy and predictable at narrow widths -- see the
   768px breakpoint at the bottom of this file for the rest of the mobile
   framing rules (hero max-height, touch padding, etc). */
@media (max-width:768px){ .thumb{width:80px; height:80px;} }

.cover-art{
  width:100%; aspect-ratio:16/9; border-radius:10px; margin:10px 0 4px;
  position:relative; overflow:hidden;
  display:flex; align-items:center; justify-content:center;
  border:1px solid rgba(255,255,255,0.10);
}
.cover-art svg{width:34%; height:34%; position:relative; z-index:1; opacity:.85;}
.cover-art::after{
  content:""; position:absolute; inset:0;
  background-image:
    radial-gradient(circle at 1px 1px, rgba(255,255,255,0.05) 1px, transparent 0);
  background-size:14px 14px;
  mix-blend-mode:overlay;
}
.cover-art--geo{background:linear-gradient(135deg, #16233d, #0A0A0E 70%);}
.cover-art--geo svg{color:var(--accent);}
.cover-art--markets{background:linear-gradient(135deg, #241d0f, #0A0A0E 70%);}
.cover-art--markets svg{color:var(--brass);}
.cover-art--christian{background:linear-gradient(135deg, #241017, #0A0A0E 70%);}
.cover-art--christian svg{color:var(--live);}
/* Matches .flat-item-photo .cover-photo's ratio above -- a card's image-box
   height shouldn't visibly jump depending on whether that particular
   article happened to have a real photo or fell back to this placeholder
   art. */
.flat-item-photo .cover-art{aspect-ratio:3/2;}

.photo-credit{font-size:9.5px; color:var(--text-faint); margin:0 0 10px; letter-spacing:.02em;}

.markets-note{font-size:10.5px; color:var(--text-faint); margin:-16px 0 28px; letter-spacing:.01em;}

.hr{height:1px; background:var(--border); margin:24px 0;}

.sub-story h3{
  font-family:'Newsreader', serif; font-weight:700; text-transform:capitalize; font-size:23px; line-height:1.24; margin:6px 0 8px;
}
.sub-story p{font-size:13px; color:var(--text-dim); line-height:1.58; margin:0 0 10px;}

.source-tag{
  font-size:11px; font-weight:700; letter-spacing:.05em; color:var(--brass);
  text-transform:uppercase;
}
.lean{
  font-size:9.5px; font-weight:600; letter-spacing:.04em; text-transform:uppercase;
  padding:2px 7px; border-radius:5px; color:var(--text-faint); border:1px solid var(--border);
}

/* ============ Political-lean indicator — AllSides-style gradient track + dot ============
   Applied per-article. Rating reflects the SOURCE's general editorial lean (not the
   individual story). Scale: Left · Lean Left · Center · Lean Right · Right. */
.bias-L  { --bias-color:#5B8CFF; }
.bias-CL { --bias-color:#8FB3FF; }
.bias-C  { --bias-color:#ADADB6; }
.bias-CR { --bias-color:#FF9A8F; }
.bias-R  { --bias-color:#FF6B6B; }

.bias-meter{
  display:inline-flex; align-items:center; gap:6px; margin-left:auto;
  font-family:'IBM Plex Mono', monospace; font-size:8.5px; font-weight:600;
  letter-spacing:.05em; text-transform:uppercase; color:var(--bias-color, var(--text-faint));
  cursor:default;
}
.bias-track{
  position:relative; width:30px; height:4px; border-radius:3px; flex:none;
  background:linear-gradient(90deg,#5B8CFF 0%,#5B8CFF 14%,rgba(140,138,148,0.45) 42%,rgba(140,138,148,0.45) 58%,#FF6B6B 86%,#FF6B6B 100%);
}
.bias-dot{
  position:absolute; top:50%; width:7px; height:7px; border-radius:50%;
  background:var(--bg); border:1.5px solid var(--bias-color, var(--text-dim));
  box-shadow:0 0 5px var(--bias-color, transparent);
  transform:translate(-50%,-50%);
}
.bias-meter.bias-L  .bias-dot{ left:14%; }
.bias-meter.bias-CL .bias-dot{ left:32%; }
.bias-meter.bias-C  .bias-dot{ left:50%; }
.bias-meter.bias-CR .bias-dot{ left:68%; }
.bias-meter.bias-R  .bias-dot{ left:86%; }
.bias-label{color:inherit;}

/* compact dot-only variant for dense rows (digest list, ticker, live wire, briefing cites) */
.bias-dot-mini{
  display:inline-block; width:6px; height:6px; border-radius:50%; margin-left:5px;
  vertical-align:middle; flex:none;
  background:var(--bias-color, var(--text-faint));
  box-shadow:0 0 4px var(--bias-color, transparent);
}

/* right rail — compact markets + live wire */
.mini-markets{display:flex; flex-direction:column;}
.mm-row{display:flex; justify-content:space-between; align-items:center; padding:9px 0; border-bottom:1px solid var(--border); font-size:12.5px;}
.mm-row:last-child{border-bottom:none;}
.mm-row .name{color:var(--text-dim); font-weight:600;}
.mm-row .val{font-family:'IBM Plex Mono', monospace; text-align:right;}
.mm-row .val .chg{display:block; font-size:10.5px; margin-top:2px;}

.live-wire-viewport{
  height:280px; overflow:hidden; position:relative;
  -webkit-mask-image:linear-gradient(to bottom, transparent, black 8%, black 92%, transparent);
  mask-image:linear-gradient(to bottom, transparent, black 8%, black 92%, transparent);
}
.live-wire-track{display:flex; flex-direction:column; animation:wirescroll 24s linear infinite;}
.live-wire-viewport:hover .live-wire-track{animation-play-state:paused;}
@keyframes wirescroll{
  0%{transform:translateY(0);}
  100%{transform:translateY(-50%);}
}
.wire-item{display:block; padding:11px 0; border-bottom:1px solid var(--border); font-size:12px; line-height:1.45; cursor:pointer;}
.wire-item:hover .txt{color:var(--text);}
.wire-item .t{font-family:'IBM Plex Mono', monospace; font-size:10px; color:var(--live); margin-right:7px;}
.wire-item .txt{color:var(--text-dim); transition:color .15s;}

.up{color:var(--up);} .down{color:var(--down);}

/* dedicated per-category grid, hidden inside the lead column until that
   category's nav chip is active (see the category-filtering script) --
   single column since the lead column is much narrower than the full-width
   below-fold sections that also use .flat-grid. */
.lead-col .flat-grid.cat-grid{display:none; grid-template-columns:1fr;}

/* homepage-only "browse more" rail panels (left: Sports & Culture, right:
   Tech & World) -- reuse .digest-list styling, just a different kicker. */
.rail-explore .digest-list li:first-child{padding-top:0;}

/* ---------- Below-fold sections — dense flat lists, no cards ---------- */
.section-flat{max-width:1320px; margin:0 auto; padding:52px clamp(16px,4vw,48px) 0;}
.flat-grid{display:grid; grid-template-columns:1fr 1fr; column-gap:44px;}
@media (max-width:760px){ .flat-grid{grid-template-columns:1fr;} }
.flat-item{padding:18px 0; border-bottom:1px solid var(--border);}
.flat-item h3{
  font-family:'Newsreader', serif; font-weight:700; text-transform:capitalize; font-size:21px; margin:6px 0 7px; line-height:1.26;
}
.flat-item p{margin:0; font-size:13px; color:var(--text-dim); line-height:1.55;}
/* Unscoped (not .flat-item .meta) on purpose: the .cg-* category-grid
   blocks below use this same "time · source-link" meta line but aren't
   .flat-item elements, and without the flex/gap here the two collapse
   together with no visual space between them (e.g. "Wedthehill.com"). */
.meta{display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:11px; color:var(--text-faint); margin-top:10px;}
.meta a{color:var(--text-faint);}
.meta a:hover{color:var(--accent); text-decoration:underline;}

/* WSJ-style featured "main article" treatment for the first (photo) card --
   browse.html's own JS still renders .flat-item-photo for its archive
   cards, so this stays in place for that page even though fetch_news.py's
   category grids now use the procedural .cg-* block layouts below instead
   of a single repeated "photo first, list after" template. */
.flat-grid > .flat-item-photo:first-child{
  grid-column:1 / -1;
  padding-top:0;
  border-bottom:1px solid var(--border-strong);
  margin-bottom:8px;
}
.flat-grid > .flat-item-photo:first-child .cover-photo{aspect-ratio:3/2;}
.flat-grid > .flat-item-photo:first-child h3{
  font-size:clamp(24px,2.4vw,34px); line-height:1.16; margin:10px 0 10px;
}
.flat-grid > .flat-item-photo:first-child p{font-size:14.5px; line-height:1.6; color:var(--text-dim); max-width:68ch;}

/* ---------- Procedural per-category layout blocks ----------
   Politics / Markets / Christian World News (Group A): medium hero ->
   3-across text strip -> 2-column mixed grid. World / Tech / Sports /
   Culture (Group B): split hero (headline left, photo right) -> dense
   headline list with occasional small thumbnails. Each .cg-block spans the
   full width of the outer .flat-grid so its own internal columns/flex
   layout are independent of the outer grid's 1 or 2 columns. */
.cg-block{grid-column:1 / -1;}

.cg-hero-medium{padding-top:0; padding-bottom:18px; border-bottom:1px solid var(--border-strong); margin-bottom:8px;}
.hero-medium-photo{
  width:100%; max-height:360px; aspect-ratio:16/9; border-radius:4px;
  object-fit:cover; object-position:50% 20%; display:block;
  border:1px solid rgba(255,255,255,0.10); background:var(--surface-2);
}
.cg-hero-medium h3{
  font-family:'Newsreader', serif; font-weight:700; text-transform:capitalize;
  font-size:clamp(22px,2vw,28px); line-height:1.2; margin:10px 0 8px;
}
.cg-hero-medium p{font-size:14px; line-height:1.58; color:var(--text-dim); max-width:66ch; margin:0;}

.cg-strip{display:flex; gap:28px; padding:20px 0; border-bottom:1px solid var(--border); flex-wrap:wrap;}
.cg-strip-item{flex:1 1 0; min-width:190px;}
.cg-strip-item h4{
  font-family:'Newsreader', serif; font-weight:700; font-size:16.5px; line-height:1.3; margin:6px 0 8px;
}
@media (max-width:760px){
  .cg-strip{flex-direction:column; gap:0; padding:0;}
  .cg-strip-item{padding:16px 0; border-bottom:1px solid var(--border); min-width:0;}
}

.cg-mixed2{display:grid; grid-template-columns:1fr 1fr; column-gap:44px;}
@media (max-width:760px){ .cg-mixed2{grid-template-columns:1fr;} }
.cg-mixed-item{padding:16px 0; border-bottom:1px solid var(--border);}
.cg-mixed-item h3{
  font-family:'Newsreader', serif; font-weight:700; text-transform:capitalize;
  font-size:18.5px; margin:6px 0 7px; line-height:1.28;
}
.cg-mixed-item p{margin:0; font-size:12.5px; color:var(--text-dim); line-height:1.5;}

.cg-hero-split{
  display:flex; gap:28px; align-items:flex-start;
  padding-bottom:20px; border-bottom:1px solid var(--border-strong); margin-bottom:8px;
}
.cg-hero-split .chs-text{flex:1 1 0; min-width:0;}
.cg-hero-split .chs-text h3{
  font-family:'Newsreader', serif; font-weight:700;
  font-size:clamp(22px,2vw,28px); line-height:1.18; margin:10px 0 10px;
}
.cg-hero-split .chs-text p{font-size:14px; line-height:1.58; color:var(--text-dim); margin:0;}
.chs-media{flex:0 0 46%; max-width:46%;}
.hero-split-photo{
  width:100%; max-height:360px; aspect-ratio:16/9; border-radius:4px;
  object-fit:cover; object-position:50% 20%; display:block;
  border:1px solid rgba(255,255,255,0.10); background:var(--surface-2);
}
@media (max-width:760px){
  .cg-hero-split{flex-direction:column; gap:12px;}
  .chs-media{flex:none; max-width:100%;}
}

.cg-dense-item{display:flex; gap:14px; align-items:flex-start; padding:13px 0; border-bottom:1px solid var(--border);}
.cg-dense-item--text{display:block;}
.cg-dense-col{flex:1; min-width:0;}
.cg-dense-item h4{
  font-family:'Newsreader', serif; font-weight:700; font-size:15.5px; line-height:1.32; margin:4px 0 0;
}
.thumb-sm{width:64px; height:48px;}

/* Portrait-image fallback (see is_portrait_image() in fetch_news.py): used
   in place of .cg-hero-medium/.cg-hero-split whenever the chosen photo is
   taller than it is wide -- a left-aligned thumbnail next to the full
   headline/dek instead of forcing a tall image into a wide 16:9 box. */
.cg-hero-fallback{
  display:flex; gap:16px; align-items:flex-start;
  padding-bottom:18px; border-bottom:1px solid var(--border-strong); margin-bottom:8px;
}
.cg-hero-fallback .chf-col{flex:1; min-width:0;}
.cg-hero-fallback .chf-col h3{
  font-family:'Newsreader', serif; font-weight:700; text-transform:capitalize;
  font-size:clamp(19px,1.6vw,23px); line-height:1.24; margin:8px 0 6px;
}
.cg-hero-fallback .chf-col p{font-size:13.5px; line-height:1.55; color:var(--text-dim); margin:0;}
.cg-hero-thumb{width:120px; height:120px;}

/* ---------- Mobile framing (<=768px): every hero photo caps shorter than
   its desktop max-height, so a big top image never pushes headline text
   below the fold on a phone; thumbnails/touch targets adjust alongside it.
   ---------- */
@media (max-width:768px){
  .cover-photo, .hero-medium-photo, .hero-split-photo{ max-height:240px; }
  .cg-hero-thumb{ width:80px; height:80px; }

  /* Touch-friendly chip nav: taller tap target, a bit more breathing room
     than the desktop pill so a thumb can hit it reliably. */
  .chip{ padding:10px 16px; min-height:40px; display:inline-flex; align-items:center; }

  /* A little extra vertical room between stacked cards so consecutive
     tap targets (headline link, source link, chip) don't sit edge-to-edge. */
  .flat-item, .cg-strip-item, .cg-mixed-item, .cg-dense-item{ padding-top:16px; padding-bottom:16px; }
}

/* compact markets strip used at top of Markets section */
.markets{
  display:grid; grid-template-columns:repeat(5,1fr); gap:1px;
  background:var(--border); border:1px solid var(--border); border-radius:14px; overflow:hidden;
  margin-bottom:28px;
}
@media (max-width:760px){ .markets{grid-template-columns:repeat(2,1fr);} }
.mkt-cell{background:var(--surface); padding:16px 18px;}
.mkt-cell .name{font-size:11.5px; color:var(--text-faint); font-weight:600; letter-spacing:.03em;}
.mkt-cell .price{font-family:'IBM Plex Mono', monospace; font-size:17px; font-weight:500; margin:6px 0 2px;}
.mkt-cell .chg{font-family:'IBM Plex Mono', monospace; font-size:12px; font-weight:500;}

/* ---------- Footer ---------- */
footer{
  margin-top:80px; border-top:1px solid var(--border);
  background:var(--bg-elev);
  padding:44px clamp(16px,4vw,48px) 30px;
  color:var(--text-faint); font-size:12.5px; line-height:1.7;
}
footer .foot-inner{max-width:1320px; margin:0 auto;}

footer .foot-top{
  display:flex; align-items:center; justify-content:space-between; gap:20px 28px;
  flex-wrap:wrap; margin-bottom:28px;
}
footer .foot-brand{display:flex; align-items:center; gap:14px;}
footer .foot-brand img.logo{height:38px; width:auto; display:block;}
footer .foot-tagline{
  font-size:10.5px; font-weight:600; color:var(--text-dim); text-transform:uppercase;
  letter-spacing:.1em; padding-left:14px; border-left:1px solid var(--border-strong);
}
footer .foot-links{display:flex; gap:22px; flex-wrap:wrap;}
footer .foot-links a{color:var(--text-dim); font-weight:500; font-size:12.5px;}
footer .foot-links a:hover{color:var(--accent);}

footer .foot-rule{height:1px; background:var(--border); margin:0 0 26px;}

footer .foot-disclaimer{max-width:860px; margin:0 0 28px; font-size:11.5px; color:var(--text-faint); line-height:1.7;}

footer .foot-bottom{
  display:flex; align-items:center; justify-content:space-between; gap:10px 20px; flex-wrap:wrap;
  padding-top:18px; border-top:1px solid var(--border);
  font-size:11px; color:var(--text-faint); letter-spacing:.02em;
}
footer .foot-copyright{font-weight:600; color:var(--text-dim);}

@media (max-width:640px){
  footer{padding:36px 16px 26px;}
  footer .foot-top{flex-direction:column; align-items:flex-start; gap:14px; margin-bottom:22px;}
  footer .foot-brand{flex-direction:column; align-items:center; gap:10px; width:100%; text-align:center;}
  footer .foot-brand img.logo{height:32px;}
  footer .foot-tagline{padding-left:0; border-left:none;}
  footer .foot-links{gap:14px 18px;}
  footer .foot-rule{margin-bottom:20px;}
  footer .foot-disclaimer{margin-bottom:20px;}
  footer .foot-bottom{flex-direction:column; align-items:flex-start; gap:8px;}
}

/* ---------- Category-switch loader ----------
   Full-screen 3s takeover that plays every time a chip is clicked, before the
   filtered articles are revealed. Uses theme CSS custom properties throughout
   so it's automatically light-themed in light mode and dark-themed in dark
   mode — never hardcode literal colors here. */
.cat-loader{
  position:fixed; inset:0; z-index:999;
  display:flex; align-items:center; justify-content:center; flex-direction:column;
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .5s ease, visibility 0s linear .5s;
  background:
    radial-gradient(ellipse 60% 40% at 28% 68%, rgba(var(--accent-rgb),0.10), transparent 60%),
    radial-gradient(ellipse 50% 35% at 76% 24%, rgba(var(--brass-rgb),0.09), transparent 60%),
    var(--bg);
  background-size:220% 220%, 220% 220%, 100% 100%;
  animation:catDrift 9s ease-in-out infinite alternate;
}
.cat-loader.active{opacity:1; visibility:visible; pointer-events:all; transition:opacity .5s ease, visibility 0s linear 0s;}
@keyframes catDrift{
  0%{background-position:15% 65%, 82% 18%, 0 0;}
  100%{background-position:38% 42%, 58% 42%, 0 0;}
}
.cat-loader canvas{position:absolute; inset:0; width:100%; height:100%; display:block;}
.cat-loader-content{position:absolute; z-index:1; left:50%; top:50%; transform:translateX(-50%); display:flex; flex-direction:column; align-items:center; transition:top .2s ease;}
.cat-loader-caption{
  font-size:12px; font-weight:500; letter-spacing:.24em; text-transform:uppercase;
  color:var(--text-dim); text-align:center;
  text-shadow:0 0 10px rgba(var(--cat-glyph-rgb),0.35);
  animation:catCaptionPulse 1.7s ease-in-out infinite;
}
@keyframes catCaptionPulse{0%,100%{opacity:.5;}50%{opacity:1;}}
.cat-loader-caption .scan-word{margin-right:.5em;}
@media (max-width:640px){
  .cat-loader-caption{font-size:10.5px; letter-spacing:.17em; padding:0 30px;}
  /* Force "Scanning" and the category name onto their own lines -- on a
     narrow phone width a long category name (e.g. "Christian World News")
     would otherwise soft-wrap mid-phrase and strand a single word alone. */
  .cat-loader-caption .scan-word,
  .cat-loader-caption .scan-cat{display:block; margin-right:0;}
  .cat-loader-caption .scan-cat{margin-top:6px;}
}

/* ==========================================================================
   Archive / search overlay + "browse older stories" link
   ========================================================================== */
.browse-archive-link{
  display:block; margin:18px auto 4px; background:none; border:1px solid var(--border);
  border-radius:100px; padding:7px 16px; font-size:12px; color:var(--text-dim); cursor:pointer;
}
.browse-archive-link:hover{border-color:var(--border-strong); color:var(--text);}

.archive-overlay{
  position:fixed; inset:0; background:rgba(10,10,10,0.55);
  display:flex; align-items:flex-start; justify-content:center;
  padding:8vh 16px; z-index:200;
}
.archive-overlay[hidden]{display:none;}
.archive-panel{
  background:var(--bg); border:1px solid var(--border-strong); border-radius:10px;
  width:100%; max-width:720px; max-height:78vh; display:flex; flex-direction:column;
  box-shadow:0 20px 60px rgba(0,0,0,0.35); overflow:hidden;
}
.archive-panel-head{
  display:flex; align-items:center; gap:10px; padding:14px 16px; border-bottom:1px solid var(--border);
  flex:0 0 auto;
}
.archive-search-icon{color:var(--text-faint); flex:0 0 auto;}
.archive-input{
  flex:1; min-width:0; background:transparent; border:none; outline:none;
  color:var(--text); font-size:15px; font-family:inherit;
}
.archive-input::placeholder{color:var(--text-faint);}
.archive-cat-filter{
  background:var(--bg-elev); border:1px solid var(--border); border-radius:6px;
  color:var(--text-dim); font-size:12px; padding:5px 8px; font-family:inherit; flex:0 0 auto;
}
.archive-results{overflow-y:auto; flex:1 1 auto; min-height:120px;}
.archive-result{
  display:block; padding:12px 16px; border-bottom:1px solid var(--border);
  text-decoration:none; color:inherit;
}
.archive-result:hover{background:var(--bg-elev);}
.archive-result .r-source{font-size:11px; font-weight:600; color:var(--accent); text-transform:uppercase; letter-spacing:.03em;}
.archive-result .r-title{font-family:'Newsreader', serif; font-size:16px; line-height:1.3; margin:3px 0; color:var(--text);}
.archive-result .r-meta{font-size:11px; color:var(--text-faint);}
.archive-panel-foot{
  display:flex; align-items:center; justify-content:center; gap:10px; padding:10px 16px;
  border-top:1px solid var(--border); flex:0 0 auto;
}
.archive-load-more{
  background:var(--bg-elev); border:1px solid var(--border); border-radius:100px; padding:7px 16px;
  font-size:12px; color:var(--text-dim); cursor:pointer;
}
.archive-load-more:hover{border-color:var(--border-strong); color:var(--text);}
.archive-status{font-size:11px; color:var(--text-faint);}
@media (max-width:640px){
  .archive-overlay{padding:0;}
  .archive-panel{max-width:none; width:100%; height:100%; max-height:none; border-radius:0;}
  .archive-cat-filter{display:none;}
}

/* ==========================================================================
   Browse page (browse.html) — full archive, filterable by section, with
   real photo cards (replaces the old bare-text search overlay + dropdown).
   ========================================================================== */
.browse-hero{max-width:900px; margin:0 auto; padding:36px clamp(16px,4vw,48px) 8px; text-align:center;}
.browse-hero h1{font-size:clamp(28px,4vw,38px); margin:0 0 8px; font-weight:700;}
.browse-sub{font-size:12.5px; color:var(--text-faint); margin:0 0 22px;}
.browse-search{
  width:100%; max-width:520px; margin:0 auto 18px; display:block;
  background:var(--surface); border:1px solid var(--border); border-radius:100px;
  padding:11px 18px; font-size:14px; color:var(--text); text-align:center;
}
.browse-search::placeholder{color:var(--text-faint);}
.browse-search:focus{outline:none; border-color:var(--border-strong);}

/* Live Feed -- the always-on sticky ticker band at the very top of the
   page. Docks to the top of the viewport via native position:sticky the
   moment a visitor scrolls down into the main site feed, with a fully
   opaque theme-matched background (solid white in light mode, solid
   near-black in dark mode via var(--bg)) so sticky content actually reads
   as "docked" instead of letting content behind it show through. Reuses
   the .ticker-tag / .ticker-dot / .ticker-track / .ticker-item styles
   above (already theme-aware via CSS custom properties), just scoped here
   for the sticky bar's own layout. */
.live-feed{
  position:sticky; top:0; z-index:60;
  display:flex; align-items:center;
  height:42px;
  padding:0 clamp(16px,4vw,40px) 0 0;
  overflow:hidden;
  white-space:nowrap;
  background:var(--bg);
  border-bottom:1px solid var(--border-strong);
  box-shadow:0 12px 24px -12px rgba(var(--shadow-rgb),0.35);
}
.live-feed .ticker-item:hover{color:var(--accent);}

/* Pause the live feed's horizontal scroll the moment a visitor is actually
   reading it -- mouse hover on desktop (:hover), and press-and-hold on
   touch (the `.ticker-touching` class, toggled by Pointer Event listeners
   in the hero script since touch devices don't get a real, sustained
   :hover from a press-and-hold). Resuming is just removing whichever
   state applied -- animation-play-state:paused freezes a CSS animation at
   its exact current position, so play picks back up seamlessly with no JS
   position-tracking needed. */
.live-feed:hover .ticker-track,
.live-feed.ticker-touching .ticker-track{
  animation-play-state:paused;
}

@media (max-width:900px){
  .live-feed{height:38px;}
  .live-feed .ticker-item{font-size:11px;}
  .live-feed .ticker-item .src{display:none;}
}
