/* All Day — one app for the wire, Reddit, video, podcasts and Wikipedia.

   THEMING. Light is the base palette on :root, because a stylesheet whose
   only complete definition lives inside a media query breaks the moment
   someone toggles. Dark is redefined twice: once under prefers-color-scheme
   for phones set to dark, guarded so an explicit "light" choice still wins,
   and once under [data-theme="dark"] so the toggle wins in both directions.
   Never give a colour its only definition inside a media block.

   TYPE. Sized up from the first pass. Section headings are real headings —
   a 0.68rem uppercase mono label is a caption, not a title, and people with
   ordinary eyesight were squinting at them.

   COLOUR. Local items carry their own hue, so the thing that makes this a
   Burlington paper is visible while scrolling rather than only readable.
   Thumbnails are never desaturated: a greyed-out photo reads as broken. */

:root {
  color-scheme: light;

  --ground: #FBF9F5;
  --sunk: #F1EDE5;
  --panel: #FFFFFF;
  --panel-2: #F7F4EE;
  --panel-hover: #F1ECE2;
  --edge: rgba(26, 22, 14, .13);
  --edge-soft: rgba(26, 22, 14, .07);

  --ink: #16191D;
  --ink-soft: #4A5058;
  --ink-faint: #767D86;

  --accent: #9A5B00;             /* passes on paper */
  --accent-bright: #C87E12;
  --accent-soft: rgba(200, 126, 18, .13);
  --local: #16704E;
  --local-soft: rgba(22, 112, 78, .07);
  /* text that sits ON --local as a fill — flips with the theme, because dark
     mode's --local is a light mint and white on mint is unreadable */
  --local-ink: #FFFFFF;
  --good: #16704E;
  --warn: #A33B2A;

  --t-local: #12704B;   --t-news: #9A5B00;      --t-politics: #A3372B;
  --t-business: #7A5A00; --t-tech: #1B5E93;      --t-science: #5B3E9B;
  --t-sports: #9C4B14;   --t-culture: #9C2F6B;   --t-gaming: #6A3B9E;
  --t-newsletters: #106B6B;
  --rail-track: #DCDCD6;
  --rail-thumb: #9A9A92;
  --shadow-1: 0 1px 2px rgba(20, 16, 8, .06), 0 2px 8px rgba(20, 16, 8, .05);
  --shadow-2: 0 8px 30px rgba(20, 16, 8, .14);
  --scrim: rgba(28, 24, 16, .42);
  --dock-bg: rgba(255, 253, 249, .86);
  /* the tab bar is a control, not chrome — it gets its own, more solid set */
  --tabbar-bg: rgba(255, 253, 249, .97);
  --tabbar-edge: rgba(20, 16, 8, .12);
  --tabbar-lip: rgba(255, 255, 255, .9);
  --tab-on-ink: #FFFFFF;
  --shadow-3: 0 10px 34px rgba(20, 16, 8, .20), 0 2px 8px rgba(20, 16, 8, .10);

  --radius: 16px;
  --radius-sm: 11px;
  --radius-lg: 22px;
  --pill: 999px;

  /* Instrument Serif read spindly on the wire's pick cards and Tonight's
     pick — hairline strokes with no middle weight. Lora is the serif the
     rest of the site already sets its headlines in, and it has real weights. */
  --serif: "Lora", Georgia, serif;
  /* DM Sans read thin on a phone — headlines at 500 looked washed out
     against the serif display face. Archivo is a sturdier grotesque with
     real weight at small sizes, which is what a wire of two thousand
     headlines needs. */
  --ui: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;

  --tabbar-h: 78px;   /* measured: the nine-pill bar is 84-88px tall */
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
  --mast-h: 56px;
  --nav-h: 0px;
  /* Where the app's own chrome may start. Added to the home screen the page
     runs edge to edge (viewport-fit=cover + black-translucent), so y=0 is
     BEHIND the iOS clock — the shared bar pads itself past the inset, and its
     measured height therefore already clears it. --nav-h is 0 until nav.js
     lands and stays 0 if it never does, so the inset has to be the floor or
     the masthead spends that first load under the status bar. */
  --top-h: max(var(--nav-h), var(--safe-t));
}

/* phones set to dark — unless the reader explicitly chose light */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ground: #06080A;
    --sunk: #040608;
    --panel: rgba(255, 255, 255, .062);
    --panel-2: rgba(255, 255, 255, .038);
    --panel-hover: rgba(255, 255, 255, .11);
    --edge: rgba(255, 255, 255, .13);
    --edge-soft: rgba(255, 255, 255, .07);
    --ink: #F2F0EA;
    --ink-soft: rgba(242, 240, 234, .72);
    --ink-faint: rgba(242, 240, 234, .48);
    --accent: #E8A33D;
    --accent-bright: #F2B44F;
    --accent-soft: rgba(232, 163, 61, .17);
    --local: #6FD79E;
    --local-soft: rgba(111, 215, 158, .075);
    --local-ink: #06251A;
    --good: #6FD79E;
    --warn: #F08A72;
    --t-local: #6FD79E;   --t-news: #E8A33D;      --t-politics: #F08A72;
    --t-business: #E3BE5C; --t-tech: #6BB6E8;      --t-science: #B79BEE;
    --t-sports: #F0A06A;   --t-culture: #EE87BE;   --t-gaming: #B79BEE;
    --t-newsletters: #6ED2CE;
    --t-local: #6FD79E;   --t-news: #E8A33D;      --t-politics: #F08A72;
  --t-business: #E3BE5C; --t-tech: #6BB6E8;      --t-science: #B79BEE;
  --t-sports: #F0A06A;   --t-culture: #EE87BE;   --t-gaming: #B79BEE;
  --t-newsletters: #6ED2CE;
  --rail-track: #2C3134;
    --rail-thumb: #7E868A;
    --shadow-1: none;
    --shadow-2: 0 12px 40px rgba(0, 0, 0, .55);
    --scrim: rgba(0, 0, 0, .66);
    --dock-bg: rgba(14, 17, 22, .88);
    --tabbar-bg: rgba(22, 26, 33, .98);
    --tabbar-edge: rgba(255, 255, 255, .14);
    --tabbar-lip: rgba(255, 255, 255, .07);
    --tab-on-ink: #0B0E13;
    --shadow-3: 0 12px 40px rgba(0, 0, 0, .62), 0 2px 10px rgba(0, 0, 0, .45);
  }
}

/* the toggle, which must beat the phone in both directions */
:root[data-theme="dark"] {
  color-scheme: dark;
  --ground: #06080A;
  --sunk: #040608;
  --panel: rgba(255, 255, 255, .062);
  --panel-2: rgba(255, 255, 255, .038);
  --panel-hover: rgba(255, 255, 255, .11);
  --edge: rgba(255, 255, 255, .13);
  --edge-soft: rgba(255, 255, 255, .07);
  --ink: #F2F0EA;
  --ink-soft: rgba(242, 240, 234, .72);
  --ink-faint: rgba(242, 240, 234, .48);
  --accent: #E8A33D;
  --accent-bright: #F2B44F;
  --accent-soft: rgba(232, 163, 61, .17);
  --local: #6FD79E;
  --local-soft: rgba(111, 215, 158, .075);
  --local-ink: #06251A;
  --good: #6FD79E;
  --warn: #F08A72;
  --rail-track: #2C3134;
  --rail-thumb: #7E868A;
  --shadow-1: none;
  --shadow-2: 0 12px 40px rgba(0, 0, 0, .55);
  --scrim: rgba(0, 0, 0, .66);
  --dock-bg: rgba(14, 17, 22, .88);
  --tabbar-bg: rgba(22, 26, 33, .98);
  --tabbar-edge: rgba(255, 255, 255, .14);
  --tabbar-lip: rgba(255, 255, 255, .07);
  --tab-on-ink: #0B0E13;
  --shadow-3: 0 12px 40px rgba(0, 0, 0, .62), 0 2px 10px rgba(0, 0, 0, .45);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
/* A class rule carrying an explicit `display` outranks the UA sheet's
   [hidden] { display: none }, which silently un-hides anything styled
   display:flex/grid. Settle it once, here. */
[hidden] { display: none !important; }
/* 100dvh, not 100%. On iOS `height: 100%` resolves against the LARGE
   viewport — the one with Safari's toolbar collapsed — so with the toolbar
   showing, the bottom of the app sits below what you can see and the dock
   drifts as the toolbar animates in and out. dvh tracks the visible viewport.
   The vh line stays first as the fallback for anything that lacks dvh. */
html { height: 100vh; height: 100dvh; -webkit-text-size-adjust: 100%; }
body {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; text-align: left; }
a { color: inherit; text-decoration: none; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
::selection { background: var(--accent-soft); }

/* ============================================================ top chrome */
/* nav.js prepends the shared BTown bar in normal flow, which is right for a
   scrolling page and wrong for a fixed shell. Pinned here, it is the one
   thing that travels with you down the page — the way out to the rest of the
   network is always one tap away. --nav-h stays 0 if the bar never loads. */
.btnav { position: fixed; top: 0; left: 0; right: 0; z-index: 41;
  /* The status-bar strip belongs to the bar: it paints its own navy up
     behind the clock instead of being hidden under it. nav.js sets the same
     padding for every property that runs full-bleed; this repeats it so the
     app is right on its own the moment the CSS loads. */
  padding-top: var(--safe-t);
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px); }

.mast {
  position: fixed; top: var(--top-h); left: 0; right: 0; z-index: 40;
  background: var(--dock-bg);
  backdrop-filter: saturate(140%) blur(18px);
  -webkit-backdrop-filter: saturate(140%) blur(18px);
  border-bottom: 1px solid var(--edge-soft);
  transition: transform .26s ease;
}
/* The masthead belongs to the page, not to the chrome: it goes up out of the
   way as you read and comes back the moment you scroll up. -100% is exactly
   its own height, which puts its bottom edge level with the bottom of the
   shared bar — and the bar paints above it (z 41 > 40), so it is fully
   hidden without needing to know how tall the bar is. */
body.chrome-away .mast { transform: translateY(-100%); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .mast, .dock-inner { background: var(--ground); }
}
.mast-in {
  display: flex; align-items: center; gap: 10px;
  max-width: 880px; margin: 0 auto; padding: 9px 14px 10px;
}
.brand { min-width: 0; flex: 1; }
.wordmark {
  display: block; font-family: var(--serif); font-size: 1.62rem;
  line-height: 1.05; letter-spacing: .005em;
}
.tagline {
  display: block; font-family: var(--mono); font-size: .625rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-faint);
  margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mast-acts { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }

/* a 44px hit target with a 22px glyph — the old 18px star was unreadable */
.iconbtn {
  width: 42px; height: 42px; border-radius: 13px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-soft); position: relative;
  transition: background .15s ease, color .15s ease;
}
.iconbtn:hover { background: var(--panel-hover); color: var(--ink); }
.iconbtn svg { width: 23px; height: 23px; fill: none; stroke: currentColor;
  stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.iconbtn.on { color: var(--accent); }

/* the old top-right control read as a record button. It is a link to the
   full-screen headline board, so it says so. */
.boardlink {
  display: inline-flex; align-items: center; gap: 6px; height: 34px;
  padding: 0 11px 0 9px; border-radius: var(--pill);
  border: 1px solid var(--accent-soft); background: var(--accent-soft);
  color: var(--accent); font-family: var(--mono); font-size: .64rem;
  font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
}
.boardlink svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; }

.stale {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  padding: 6px 14px; text-align: center;
}
.stale[hidden] { display: none; }

/* how far down the wire you are — pulse.html has this and it is missed */
.readbar { height: 2px; background: transparent; }
.readbar i { display: block; height: 100%; width: 0; background: var(--accent);
  transition: width .1s linear; }

/* ============================================================ panels */
.app { position: absolute; inset: 0; }
.panel {
  position: absolute; inset: 0; overflow-y: auto; overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0 14px calc(var(--tabbar-h) + 34px + var(--safe-b));
}
/* The room the fixed masthead needs is a flow spacer, not top padding.
   A sticky child measures its offset from the scroll container's PADDING
   box, so top padding here silently added itself to every sticky `top` —
   the control band came to rest 150px down the screen with headlines
   scrolling above it. As a spacer it costs the same pixels and leaves
   sticky offsets meaning what they say. */
.panel::before {
  content: ""; display: block;
  height: calc(var(--top-h) + var(--mast-h) + 16px);
}
body.has-player .panel { padding-bottom: calc(var(--tabbar-h) + 92px + var(--safe-b)); }
.panel[hidden] { display: none; }
@media (min-width: 760px) {
  .panel { padding-left: max(16px, calc((100% - 880px) / 2));
           padding-right: max(16px, calc((100% - 880px) / 2)); }
}

/* ============================================================ headings */
/* A section heading is a heading. The mono line above it is the caption. */
.eyebrow {
  font-family: var(--mono); font-size: .66rem; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--accent);
}
.h-sec { margin: 4px 0 12px; }
.h-sec h2 {
  font-family: var(--serif); font-size: 1.72rem; line-height: 1.12;
  font-weight: 400; letter-spacing: .003em; margin-top: 1px;
}
.h-sec .sub { font-size: .875rem; color: var(--ink-soft); margin-top: 3px; line-height: 1.45; }
.h-sec .count { color: var(--ink-faint); }
.h-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }

.card { background: var(--panel); border: 1px solid var(--edge);
  border-radius: var(--radius); box-shadow: var(--shadow-1); overflow: hidden; }
.empty { color: var(--ink-faint); text-align: center; padding: 46px 12px; font-size: 1rem; }
.loading { color: var(--ink-faint); font-family: var(--mono); font-size: .72rem;
  letter-spacing: .1em; text-transform: uppercase; text-align: center; padding: 38px 12px; }
.errbox { background: color-mix(in srgb, var(--warn) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  border-radius: var(--radius); padding: 18px; color: var(--ink-soft);
  font-size: .96rem; line-height: 1.55; }
.errbox b { color: var(--ink); }

/* ============================================================ chips */
.chips { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 8px;
  scrollbar-width: none; -webkit-overflow-scrolling: touch; }
/* the same grey bar a rail gets, for chip rows and the Wander trail — a row
   that runs off the edge with no sign of it reads as a row that ends there */
.hint-bar { position: relative; height: 4px; border-radius: 3px; margin: 0 0 14px;
  background: var(--rail-track); overflow: hidden;
  opacity: 0; transition: opacity .3s ease; }
.hint-bar.is-live { opacity: 1; }
.hint-bar i { position: absolute; top: 0; bottom: 0; left: 0; border-radius: 3px;
  background: var(--rail-thumb); transition: left .12s linear, width .12s linear; }
@media (hover: hover) and (pointer: fine) {
  .hint-bar { cursor: pointer; height: 6px; }
  .hint-bar:hover i { background: var(--ink-soft); }
  /* a mouse has no other way to move a chip row, so keep it reachable */
  .hint-bar:hover, .chips:hover + .hint-bar, .trail:hover + .hint-bar { opacity: 1; }
}
.trail { padding-bottom: 8px; }
.trail + .hint-bar { margin-bottom: 16px; }
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: 0 0 auto; font-size: .8rem; font-weight: 500; letter-spacing: .01em;
  color: var(--ink-soft); background: var(--panel); border: 1px solid var(--edge);
  border-radius: var(--pill); padding: 8px 15px; min-height: 38px;
  display: inline-flex; align-items: center; gap: 6px;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.chip:hover { background: var(--panel-hover); }
.chip.on { color: var(--accent); background: var(--accent-soft); border-color: transparent; font-weight: 600; }
.chip .n { font-family: var(--mono); font-size: .72rem; opacity: .75; }
/* the local switch is a real action, not a state label — people read the wire
   as already-local and never pressed it */
.chip.local-switch { border-color: color-mix(in srgb, var(--local) 40%, transparent); color: var(--local); }
.chip.local-switch.on { background: var(--local-soft); color: var(--local); border-color: transparent; }

/* ============================================================ carousel */
/* Nobody swipes a rail they cannot tell is a rail. Every horizontal strip
   gets dots (one per PAGE, not per card) and a live progress bar. */
.rail-wrap { position: relative; margin-bottom: 8px; }
.rail {
  display: grid; grid-auto-flow: column; gap: 11px;
  /* NO grid-auto-columns here: each card type sets its own width, and a
     blanket column width makes wider cards overlap each other. */
  overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: 10px; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  scroll-padding-left: 0;
}
.rail::-webkit-scrollbar { display: none; }
.rail > * { scroll-snap-align: start; }
/* The affordance under every rail: a full-width track with a grey thumb that
   is ALWAYS drawn, then page dots centred beneath it. Always-drawn matters —
   a bar that only appears once you already know to scroll teaches nobody. On
   a rail short enough to fit, the thumb simply fills the track, which is a
   true statement rather than a hidden control. */
/* a mouse gets a grab cursor and a usable scrollbar; a finger needs neither */
@media (hover: hover) and (pointer: fine) {
  .rail { cursor: grab; }
  .rail.is-panning { cursor: grabbing; scroll-behavior: auto; }
  .rail.is-panning * { pointer-events: none; }
  .rail-bar { cursor: pointer; height: 7px; }
  .rail-bar:hover i { background: var(--ink-soft); }
}
.rail-nav { display: flex; flex-direction: column; gap: 7px; padding: 2px 0; }
.rail-expand {
  align-self: center; display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: .6rem; font-weight: 500; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-faint);
  border: 1px solid var(--edge); border-radius: var(--pill); padding: 5px 12px;
  background: var(--panel); min-height: 30px;
}
.rail-expand:hover { color: var(--accent); border-color: var(--accent-soft); }
/* the same control at the top, so a tall list can be closed from where you
   opened it */
.rail-collapse-top {
  display: block; margin: 0 auto 10px; font-family: var(--mono); font-size: .6rem;
  font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint); border: 1px solid var(--edge); border-radius: var(--pill);
  padding: 5px 12px; background: var(--panel); min-height: 30px;
}
.rail-collapse-top:hover { color: var(--accent); border-color: var(--accent-soft); }
.rail-collapse-top[hidden] { display: none; }

/* Opened out: the same cards, stacked, no sideways anything. */
.rail.is-open {
  grid-auto-flow: row; grid-auto-columns: auto; grid-template-columns: 1fr;
  overflow-x: visible; scroll-snap-type: none; gap: 10px; cursor: default;
}
.rail.is-open > * { width: auto !important; }
.rail.is-open .v img, .rail.is-open .doorcard img { aspect-ratio: 16/9; }
/* One strip carries both: the track spans it, the dots sit on top. The
   track only shows while the rail is moving and fades a moment after it
   stops, the way an overlay scrollbar does — the dots are the part that
   stays, and they are what tells you there is more. */
.rail-strip { position: relative; height: 14px; display: flex;
  align-items: center; justify-content: center; }
.rail-bar { position: absolute; left: 0; right: 0; top: 50%; margin-top: -2px;
  height: 4px; border-radius: 3px; background: var(--rail-track);
  overflow: hidden; opacity: 0; transition: opacity .3s ease; }
.rail-bar.is-live { opacity: 1; }
.rail-bar i { position: absolute; top: 0; bottom: 0; left: 0; border-radius: 3px;
  background: var(--rail-thumb); transition: left .12s linear, width .12s linear; }
.rail-dots { position: relative; display: flex; gap: 6px; align-items: center;
  padding: 0 9px; background: var(--ground); border-radius: var(--pill); }
.rail-dots i { width: 6px; height: 6px; border-radius: 50%;
  background: var(--rail-track); transition: background .2s ease, width .2s ease; }
.rail-dots i.on { background: var(--accent); width: 15px; border-radius: var(--pill); }
.rail-strip.is-idle .rail-dots { display: none; }
.card .rail-dots { background: var(--panel); }
/* opened out, there is nothing sideways left to indicate */
.rail-wrap.is-open .rail-bar { display: none; }

/* ============================================================ feed rows */
/* The wire and Reddit read as a page of headlines, not a stack of cards in a
   box: no outer panel, just hairlines between rows. The picks carousel keeps
   its cards — those ARE buttons, not a feed. */
.feed { overflow: hidden; }
.feed .fi { background: var(--ground); }
.feed .fi:first-child { border-top: 1px solid var(--edge-soft); }
.feed .fi { padding-left: 2px; padding-right: 2px; }
.fi {
  position: relative; display: grid;
  /* Title, then ONE meta line carrying outlet, age and the upvote. The star
     lives in its own column on the right, the way the old app had it — a
     dedicated row for two controls was a line of height for nothing. */
  grid-template-columns: 1fr auto auto;
  grid-template-areas: "body star thumb" "foot star thumb";
  gap: 1px 10px; align-items: start;
  padding: 10px 12px; border-bottom: 1px solid var(--edge-soft);
  background: var(--panel);
  transform: translateX(var(--swipe, 0)); touch-action: pan-y;
}
.fi-body { grid-area: body; }
.fi-foot { grid-area: foot; display: flex; align-items: center; gap: 9px; margin-top: 3px; min-width: 0; }
.fi-foot .spacer { flex: 1 1 auto; min-width: 4px; }
.fi > .act { grid-area: star; align-self: center; }
.fi-foot .spacer { flex: 1; }
.fi-thumb { grid-area: thumb; align-self: center; }
.fi:last-child { border-bottom: 0; }
.fi.is-swiping { transition: none; }
.fi:not(.is-swiping) { transition: transform .18s cubic-bezier(.2, .8, .3, 1); }
.fi-body { min-width: 0; display: block; }
.fi-title {
  display: block; font-size: .97rem; line-height: 1.34; font-weight: 600;
  letter-spacing: -.006em; color: var(--ink);
}
.fi-meta {
  display: flex; align-items: center; gap: 6px; min-width: 0; flex-shrink: 1;
  overflow: hidden; white-space: nowrap;
  font-family: var(--mono); font-size: .625rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint);
}
.fi-meta > * { flex: 0 0 auto; }
.fi-src { color: var(--ink-soft); font-weight: 600; letter-spacing: .09em;
  overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto; }
.fi-src.t-local { color: var(--t-local); }
.fi-src.t-news { color: var(--t-news); }
.fi-src.t-politics { color: var(--t-politics); }
.fi-src.t-business { color: var(--t-business); }
.fi-src.t-tech { color: var(--t-tech); }
.fi-src.t-science { color: var(--t-science); }
.fi-src.t-sports { color: var(--t-sports); }
.fi-src.t-culture { color: var(--t-culture); }
.fi-src.t-gaming { color: var(--t-gaming); }
.fi-src.t-newsletters { color: var(--t-newsletters); }
.fi-thumb {
  width: 70px; height: 70px; object-fit: cover; border-radius: 9px;
  background: var(--panel-2); flex: 0 0 auto;
}
.fi.is-read { opacity: .55; }
/* scrolled off the top while you were looking at it — same shade Pulse uses */
.fi.passed .fi-title { color: var(--ink-faint); font-weight: 500; }
.fi.passed .fi-thumb { opacity: .6; }
.fi.is-read .fi-title { font-weight: 500; }

/* local is the whole point of the paper — make it visible while scrolling */
.fi.is-local { background: linear-gradient(90deg, var(--local-soft), transparent 38%); }
.fi.is-local::before {
  content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px;
  border-radius: 2px; background: var(--local); opacity: .8;
}
/* the rule needs room of its own, or it sits against the first letter */
.feed .fi.is-local { padding-left: 13px; }
.tag-local {
  color: var(--local); font-weight: 600;
  background: var(--local-soft); border-radius: 4px; padding: 1px 5px;
  letter-spacing: .08em;
}
.tag-new { color: var(--good); font-weight: 600; }

/* the swipe label sits under the row and does not travel with it */
.fi-hint {
  position: absolute; top: 0; bottom: 0; display: flex; align-items: center;
  font-family: var(--mono); font-size: .68rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; pointer-events: none;
  transform: translateX(calc(var(--swipe, 0) * -1));
}
.fi-hint[data-side="left"] { left: 16px; color: var(--good); }
.fi-hint[data-side="right"] { right: 16px; color: var(--warn); }
.fi.will-commit .fi-hint { font-size: .74rem; }

/* ============================================================ actions */
.act {
  width: 34px; height: 34px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-faint); flex: 0 0 auto;
  transition: color .14s ease, background .14s ease, transform .14s ease;
}
.act:hover { background: var(--panel-hover); color: var(--ink-soft); }
.act:active { transform: scale(.9); }
.act svg { width: 21px; height: 21px; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.act.on { color: var(--accent); }
.act.on svg { fill: currentColor; stroke: currentColor; }

/* upvote — a count that reads as a count */
.vote {
  display: inline-flex; align-items: center; gap: 3px; height: 22px;
  padding: 0 7px 0 5px; border-radius: var(--pill); border: 1px solid var(--edge);
  color: var(--ink-faint); font-family: var(--mono); font-size: .72rem;
  font-weight: 600; letter-spacing: .02em; flex: 0 0 auto;
  transition: all .14s ease;
}
.vote svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round; }
.vote:hover { background: var(--panel-hover); color: var(--ink-soft); }
.vote.on { color: var(--accent); border-color: transparent; background: var(--accent-soft); }
.vote.on svg { fill: currentColor; }
.vote:active { transform: scale(.93); }
.vote .n { min-width: 8px; text-align: left; }
.vote.is-zero .n { opacity: .5; }

.more {
  width: 100%; padding: 16px; margin-top: 14px; text-align: center;
  font-family: var(--mono); font-size: .7rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--accent);
  border: 1px solid var(--edge); border-radius: var(--radius); background: var(--panel);
}
.more:hover { background: var(--panel-hover); }
.caught { text-align: center; color: var(--good); padding: 22px 12px;
  font-family: var(--mono); font-size: .7rem; letter-spacing: .12em; text-transform: uppercase; }

/* ============================================================ picks */
.pick {
  display: flex; flex-direction: column; width: min(300px, 78vw); height: 100%;
  border-radius: var(--radius); border: 1px solid var(--edge);
  background: var(--panel); box-shadow: var(--shadow-1);
}
.pick-hit { display: block; padding: 15px 15px 12px; flex: 1 1 auto; }
.pick-foot { display: flex; align-items: center; gap: 8px; padding: 0 11px 9px; }
.pick-title { display: block; font-family: var(--serif); font-size: 1.3rem;
  font-weight: 500; line-height: 1.16; margin: 8px 0 7px; }
.pick-why { display: block; font-size: .88rem; line-height: 1.45; color: var(--ink-soft); }
.pick.is-local { border-color: color-mix(in srgb, var(--local) 38%, transparent); }
.picks-when { display: flex; gap: 8px; align-items: center; }

/* ============================================================ video */
.vgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 11px; }
.rail .v { width: 168px; }
/* A column, so the card's spare height goes to the TEXT and the buttons stay
   on the floor. Cards in a rail stretch to the tallest one; without this the
   slack opened up as a band of nothing between the reason and the buttons,
   which is what "a lot of the room taken up by empty white space" was. */
.v { position: relative; border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--edge); background: var(--panel); box-shadow: var(--shadow-1);
  display: flex; flex-direction: column; }
.v-hit { display: block; width: 100%; flex: 1 1 auto; }
.v-shot { position: relative; }
.v img { width: 100%; aspect-ratio: 16/9; object-fit: cover; background: var(--panel-2); }
.v-dur { position: absolute; right: 7px; bottom: 7px; font-family: var(--mono);
  font-size: .62rem; font-weight: 600; background: rgba(0, 0, 0, .78); color: #fff;
  padding: 2px 6px; border-radius: 5px; letter-spacing: .02em; }
.v-live { position: absolute; left: 7px; top: 7px; display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: .6rem; font-weight: 700; letter-spacing: .1em;
  background: #C8321E; color: #fff; padding: 3px 7px; border-radius: 5px; }
.v-live i { width: 5px; height: 5px; border-radius: 50%; background: #fff; animation: blink 1.6s ease infinite; }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
.v-body { padding: 7px 9px 7px; }
.v-title { font-size: .88rem; line-height: 1.3; font-weight: 500;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.v-meta { display: block; font-family: var(--mono); font-size: .6rem; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: 4px; }
.v-why { font-size: .8rem; line-height: 1.38; color: var(--ink-soft); margin-top: 5px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* The reason IS the product — "around fifty videos, each with a reason".
   Hiding it on the shelves left a wall of thumbnails like every other video
   app. Two lines then cut it in the middle of the sentence: the reasons run
   57 to 87 characters and a 168px card fits about 25 to a line, so two lines
   lost a third of every one of them. Four shows all of them whole, and the
   space it takes was already there and empty. */
.rail .v-why { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden; font-size: .75rem; line-height: 1.36; margin-top: 5px; }
.vgrid .v-why { -webkit-line-clamp: 4; }
.hero-body .v-why { display: -webkit-box; -webkit-line-clamp: 4; }
.v-acts { display: flex; align-items: center; gap: 1px; padding: 2px 4px 4px;
  flex: 0 0 auto; border-top: 1px solid var(--edge-soft); margin-top: 6px; }
.v-acts .act { width: 29px; height: 29px; }
.v-acts .act svg { width: 18px; height: 18px; }
.v-acts .vote { height: 28px; padding: 0 7px 0 5px; font-size: .68rem; }
.v-acts .spacer { flex: 1; }
.v.is-seen { opacity: .5; }

/* the hero: tonight's one pick, and it says who chose it */
.hero-card { margin-bottom: 16px; }
.hero { display: block; width: 100%; text-align: left; }
.hero img { width: 100%; aspect-ratio: 16/9; max-height: 140px; object-fit: cover; }
.hero-body { display: block; padding: 10px 13px 12px; }
.hero-title { display: block; font-family: var(--serif); font-size: 1.26rem; font-weight: 500; line-height: 1.1; margin: 3px 0 4px; }

/* A shelf label is a label. Legible — far bigger than the .68rem caption it
   replaces — but a serif headline block per shelf costs a whole video row
   every time, and this tab is for browsing video, not reading titles. */
.shelf-head { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; margin: 2px 0 9px; }
.shelf-head.has-more { flex-wrap: nowrap; }
/* the title keeps its line and the subtitle gives way — a two-line shelf name
   beside a button reads as a wrap, not a heading */
.shelf-head.has-more .t { white-space: nowrap; flex: 0 0 auto; }
.shelf-head.has-more .s { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.shelf-more { flex: 0 0 auto; align-self: center; font: inherit; font-size: .74rem;
  font-weight: 600; letter-spacing: .01em; color: var(--local);
  background: color-mix(in srgb, var(--local) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--local) 30%, transparent);
  border-radius: 999px; padding: 4px 10px; cursor: pointer; }
.shelf-more:hover { background: color-mix(in srgb, var(--local) 20%, transparent); }
/* the bench reveal sits under its shelf's rail, not in the shelf head */
.bench-more { display: block; margin: -4px 0 16px; }
/* a bench video that stepped in for one the reader hid says so */
.v-next { display: block; font-family: var(--mono); font-size: .58rem; letter-spacing: .07em;
  text-transform: uppercase; color: var(--local); margin-bottom: 3px; }
.shelf-head .t { font-size: 1.06rem; font-weight: 600; letter-spacing: -.004em; }
.shelf-head .s { font-size: .84rem; color: var(--ink-faint); }

/* Live cams are ambient. A lake at night is not competing with a
   documentary for attention: small enough to skim past, big enough to
   actually see, and no action row — you do not rate a webcam. */
.livestrip { margin-bottom: 18px; }
.livestrip .rail { gap: 7px; }
.livestrip .rail .v { width: 96px; border-radius: 9px; }
.livestrip .v-body { padding: 4px 6px 6px; }
.livestrip .v-title { font-size: .66rem; line-height: 1.2; -webkit-line-clamp: 2; }
.livestrip .v-meta, .livestrip .v-acts, .livestrip .v-dur, .livestrip .v-why { display: none; }
.livestrip .v-live { left: 4px; top: 4px; font-size: .48rem; padding: 1px 4px; gap: 3px; }
.livestrip .v-live i { width: 4px; height: 4px; }
.livestrip .shelf-head { margin-bottom: 6px; }
.livestrip .shelf-head .t { font-size: .92rem; }
.livestrip .shelf-head .s { font-size: .76rem; }

/* ============================================================ listen */
/* The Brief's own show still leads, but as a banner rather than a page. It
   cost 352px, and the Spotify embed another 352, which put the first actual
   podcast a full screen below the fold. */
.feature { display: grid; grid-template-columns: 76px 1fr; gap: 12px; padding: 11px; margin-bottom: 10px; }
.feature img { border-radius: 9px; align-self: start; }
.feature h2 { font-family: var(--serif); font-size: 1.18rem; line-height: 1.1; margin: 2px 0 3px; font-weight: 400; }
.feature p { font-size: .83rem; line-height: 1.42; color: var(--ink-soft);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.btns { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 9px; }
.feature .btn { padding: 7px 13px; font-size: .82rem; min-height: 36px; }

/* Browsing every episode is a deliberate act, not the default view. */
.embed-toggle { width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 11px 14px; margin-bottom: 12px; font-size: .92rem; font-weight: 500;
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--radius);
  box-shadow: var(--shadow-1); }
.embed-toggle .chev { display: inline-flex; color: var(--ink-faint); transition: transform .2s ease; }
.embed-toggle .chev svg { width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.embed-toggle[aria-expanded="true"] .chev { transform: rotate(180deg); }
/* the embed plays the newest episode; the back catalogue lives behind this */
.embed-all { display: block; text-align: center; margin: -6px 0 16px;
  font-family: var(--mono); font-size: .64rem; font-weight: 500; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent); padding: 5px; }
.embed { width: 100%; height: 352px; border: 0; border-radius: var(--radius);
  margin-bottom: 14px; background: var(--panel); }
.shows { display: grid; gap: 10px; }
/* A show is a card with its newest episodes already in it. The accordion
   put every episode behind a tap, so a tab full of podcasts showed no
   podcasts. */
.show { overflow: hidden; }
.show-head { display: grid; grid-template-columns: 46px 1fr auto; align-items: center;
  gap: 11px; padding: 10px 12px 10px 10px; width: 100%;
  border-bottom: 1px solid var(--edge-soft); }
.show-out { color: var(--ink-faint); font-size: 1rem; padding: 6px 4px; }
.show-out:hover { color: var(--accent); }
.show .eps { margin: 0; }
/* One line per episode: play, title, age, save. The generic feed row put
   the buttons on a line of their own, which made every episode 135px tall. */
.ep { display: grid; grid-template-columns: 34px 1fr auto auto; align-items: center;
  gap: 10px; padding: 9px 11px; border-bottom: 1px solid var(--edge-soft); }
.ep:last-child { border-bottom: 0; }
.ep-go { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid var(--edge); display: inline-flex; align-items: center;
  justify-content: center; color: var(--ink); background: var(--panel-2); }
.ep-go svg { width: 15px; height: 15px; margin-left: 1px; }
.ep-go:hover { border-color: var(--accent); color: var(--accent); }
.ep.is-playing .ep-go { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.ep-meta { min-width: 0; }
.ep-title { display: block; font-size: .93rem; line-height: 1.32; font-weight: 500;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.ep-age { display: block; font-family: var(--mono); font-size: .6rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: 3px; }
.ep .vote { height: 28px; padding: 0 7px 0 5px; font-size: .68rem; }
.ep .act { width: 32px; height: 32px; }
.ep .act svg { width: 19px; height: 19px; }
.show-more { width: 100%; padding: 11px; text-align: center;
  font-family: var(--mono); font-size: .66rem; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent); border-top: 1px solid var(--edge-soft); }
.show-more:hover { background: var(--panel-hover); }
.show-head img { width: 46px; height: 46px; border-radius: 9px; object-fit: cover; }
.noart { width: 46px; height: 46px; border-radius: 9px; background: var(--accent-soft);
  display: grid; place-items: center; font-size: 1.15rem; }
.show-meta { min-width: 0; }
.show-name { display: block; font-size: .95rem; font-weight: 600; line-height: 1.25; }
.eps { margin-top: 8px; }
.ep-play { width: 100%; }
.ep-bar { height: 3px; border-radius: 2px; background: var(--edge); margin-top: 8px; overflow: hidden; }
.ep-bar i { display: block; height: 100%; background: var(--accent); }

/* ============================================================ wander */
.door { text-align: center; padding: 4px 4px 2px; }
.door h1 { font-family: var(--serif); font-size: 2rem; line-height: 1.05; font-weight: 400; margin-bottom: 6px; }
.door p { color: var(--ink-soft); font-size: .92rem; line-height: 1.45; max-width: 30rem; margin: 0 auto 12px; }
.search { position: relative; max-width: 30rem; margin: 0 auto 12px; }
.search input {
  width: 100%; background: var(--panel); border: 1px solid var(--edge);
  border-radius: var(--pill); padding: 13px 18px 13px 44px; color: var(--ink);
  font: inherit; font-size: 1rem; box-shadow: var(--shadow-1);
}
.search input::placeholder { color: var(--ink-faint); }
.search .mag { position: absolute; left: 16px; top: 50%; transform: translateY(-50%);
  width: 19px; height: 19px; color: var(--ink-faint); fill: none; stroke: currentColor; stroke-width: 2; }
.suggest {
  position: absolute; left: 0; right: 0; top: calc(100% + 7px); z-index: 20;
  background: var(--panel); border: 1px solid var(--edge); border-radius: var(--radius);
  overflow: hidden; text-align: left; box-shadow: var(--shadow-2);
}
.suggest[hidden] { display: none; }
.suggest button { display: block; width: 100%; padding: 11px 16px; font-size: .96rem;
  border-bottom: 1px solid var(--edge-soft); }
.suggest button:last-child { border-bottom: 0; }
.suggest button:hover { background: var(--panel-hover); }
.suggest .d { display: block; font-size: .8rem; color: var(--ink-faint); margin-top: 2px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  background: var(--accent-soft); color: var(--accent); border: 1px solid transparent;
  border-radius: var(--pill); padding: 11px 20px; font-size: .95rem; font-weight: 600;
  min-height: 44px;
}
.btn:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.btn-big { font-size: 1.06rem; padding: 15px 30px; }
.btn-quiet { background: var(--panel); color: var(--ink-soft); border-color: var(--edge); font-weight: 500; }
.door-out { display: inline-flex; margin-top: 10px; font-size: .88rem; padding: 9px 17px; min-height: 40px; }
/* the shuffle glyph read as something else entirely — use the word */
.pool-shuffle { font-family: var(--mono); font-size: .6rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint);
  border: 1px solid var(--edge); border-radius: var(--pill); padding: 6px 11px;
  white-space: nowrap; }
.pool-shuffle:hover { color: var(--accent); border-color: var(--accent-soft); }
.btn-quiet:hover { background: var(--panel-hover); }

/* a pool door opens in place — it does not throw you somewhere */
.pool { margin-bottom: 4px; }
.pool-head { display: grid; grid-template-columns: 32px minmax(0, 1fr) auto auto;
  align-items: center; gap: 10px; padding: 10px 2px 8px; width: 100%; min-width: 0; }
.pool-head > * { min-width: 0; }
.pool-emoji { font-size: 1.25rem; text-align: center; }
.pool-name { display: block; font-size: 1.02rem; font-weight: 600; }
.pool-sub { display: block; font-size: .8rem; color: var(--ink-faint); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pool-body { padding: 0 0 4px; }
.doorbox {
  display: flex; flex-direction: column; width: min(250px, 72vw); height: 100%;
  border-radius: var(--radius-sm); border: 1px solid var(--edge); background: var(--panel-2);
}
.doorcard {
  display: block; width: 100%; flex: 1 1 auto; padding: 11px 12px 8px;
  text-align: left; background: none; border: 0; font: inherit; color: inherit; cursor: pointer;
}
.door-foot { display: flex; align-items: center; gap: 8px; padding: 0 9px 8px; }
.pool-body .rail .doorbox, .rail .doorbox { flex: 0 0 auto; }
.doorcard .d { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; }
.doorcard .t { display: block; font-size: .93rem; font-weight: 600; line-height: 1.28;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; display: -webkit-box; overflow: hidden; }
.doorcard .d { font-size: .82rem; line-height: 1.4; color: var(--ink-soft); margin-top: 4px; }
/* a handful of articles genuinely have no short description on Wikipedia —
   the card should close up rather than reserve a line for nothing */
.doorcard .d:empty { display: none; }
.doorcard img { width: 100%; aspect-ratio: 16/9; object-fit: cover;
  border-radius: 8px; margin-bottom: 9px; }

.trail { display: flex; gap: 7px; overflow-x: auto; padding-bottom: 14px; scrollbar-width: none; }
.trail::-webkit-scrollbar { display: none; }
.tchip { flex: 0 0 auto; font-size: .78rem; color: var(--ink-soft); border: 1px solid var(--edge);
  border-radius: var(--pill); padding: 7px 13px; background: var(--panel); max-width: 210px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-height: 36px; }
.tchip.here { color: var(--accent); border-color: transparent; background: var(--accent-soft); font-weight: 600; }

/* ============================================================ reader */
.reader { line-height: 1.75; font-size: 1.09rem; color: var(--ink-soft); }
.reader h1, .reader h2, .reader h3, .reader h4 {
  font-family: var(--serif); color: var(--ink); line-height: 1.16;
  margin: 1.5em 0 .4em; font-weight: 400;
}
.reader h1 { font-size: 2.1rem; margin-top: .1em; }
.reader h2 { font-size: 1.5rem; }
.reader h3 { font-size: 1.22rem; }
.reader p { margin: .95em 0; }
.reader ul, .reader ol { margin: .95em 0 .95em 1.35em; }
.reader li { margin: .4em 0; }
.reader a { color: var(--accent); text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent); text-underline-offset: 3px; }
.reader .plain-link { color: var(--ink-soft); }
.reader img { border-radius: var(--radius-sm); margin: 1.1em auto; height: auto; }
.reader figcaption { font-size: .84rem; color: var(--ink-faint); margin-top: 7px; }
.reader .table-wrap { overflow-x: auto; margin: 1.2em 0; border: 1px solid var(--edge); border-radius: var(--radius-sm); }
.reader table { border-collapse: collapse; font-size: .88rem; min-width: 100%; }
.reader th, .reader td { padding: 8px 11px; border-bottom: 1px solid var(--edge-soft); text-align: left; vertical-align: top; }
.reader th { color: var(--ink); font-weight: 600; white-space: nowrap; }
.reader .wikinote { font-size: .64em; color: var(--ink-faint); vertical-align: super; }
.reader blockquote { margin: 1.2em 0; padding-left: 16px; border-left: 3px solid var(--accent-soft); }
.reader-hero { width: 100%; max-height: 46vh; object-fit: cover; border-radius: var(--radius); margin-bottom: 18px; }
@media (min-width: 760px) { .reader-hero { max-height: 38vh; } }
.reader-acts { display: flex; flex-wrap: wrap; gap: 9px; margin: 28px 0 30px; }
.srcline { font-family: var(--mono); font-size: .66rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-faint); padding: 14px 0 4px; }
.srcline a { color: var(--accent); }

/* ============================================================ peek */
/* press and hold anything and read it right there — his favourite gesture */
.peek {
  position: fixed; z-index: 95; left: 12px; right: 12px;
  bottom: calc(var(--tabbar-h) + var(--safe-b) + 14px);
  max-width: 560px; margin: 0 auto; max-height: 62vh;
  background: var(--panel); border: 1px solid var(--edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-2);
  display: flex; flex-direction: column; overflow: hidden;
  animation: peekin .18s cubic-bezier(.2, .9, .3, 1);
}
@keyframes peekin { from { transform: translateY(14px); opacity: 0; } }
.peek-head { display: flex; align-items: center; gap: 10px; padding: 13px 8px 11px 15px;
  border-bottom: 1px solid var(--edge-soft); }
.peek-head .t { flex: 1; min-width: 0; font-size: .96rem; font-weight: 600; line-height: 1.3; }
.peek-body { overflow-y: auto; padding: 14px 15px 16px; font-size: 1rem; line-height: 1.62;
  color: var(--ink-soft); -webkit-overflow-scrolling: touch; }
.peek-body img { border-radius: var(--radius-sm); margin: 0 0 12px; width: 100%;
  aspect-ratio: 16/9; object-fit: cover; }
.peek-foot { display: flex; gap: 8px; padding: 0 15px 15px; }

/* ============================================================ sheet */
.sheet { position: fixed; inset: 0; z-index: 90; }
.sheet[hidden] { display: none; }
.sheet-scrim { position: absolute; inset: 0; background: var(--scrim); }
.sheet-card {
  position: absolute; left: 0; right: 0; bottom: 0; max-height: 86vh;
  background: var(--ground); border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg); border-top: 1px solid var(--edge);
  display: flex; flex-direction: column;
  animation: sheetin .22s cubic-bezier(.2, .9, .3, 1);
}
@keyframes sheetin { from { transform: translateY(24px); opacity: .4; } }
@media (min-width: 620px) {
  .sheet-card { left: 50%; right: auto; transform: translateX(-50%);
    width: 520px; bottom: 0; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
}
.sheet-head { display: flex; align-items: center; gap: 10px; padding: 16px 10px 13px 18px;
  border-bottom: 1px solid var(--edge-soft); }
.sheet-head h2 { flex: 1; font-family: var(--serif); font-size: 1.4rem; font-weight: 400; }
.sheet-body { overflow-y: auto; padding: 12px 14px calc(24px + var(--safe-b)); }
.sheet-row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 12px;
  padding: 13px 6px; border-bottom: 1px solid var(--edge-soft); }
.sheet-row:last-child { border-bottom: 0; }
.sheet-row .t { font-size: 1rem; font-weight: 500; }
.sheet-row .d { font-size: .83rem; color: var(--ink-faint); margin-top: 2px; }

/* iOS-style switch, because a checkbox reads as a form */
.sw { width: 50px; height: 30px; border-radius: var(--pill); background: var(--edge);
  position: relative; flex: 0 0 auto; transition: background .18s ease; }
.sw::after { content: ""; position: absolute; top: 3px; left: 3px; width: 24px; height: 24px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3); transition: transform .18s ease; }
.sw.on { background: var(--good); }
.sw.on::after { transform: translateX(20px); }

.seg { display: flex; gap: 3px; padding: 3px; background: var(--panel-2);
  border: 1px solid var(--edge); border-radius: var(--pill); }
.seg button { flex: 1; text-align: center; padding: 9px 10px; border-radius: var(--pill);
  font-size: .84rem; font-weight: 600; color: var(--ink-faint); min-height: 40px;
  /* three labels on a 390px phone: one line each, or the control doubles in
     height and the first video leaves the screen */
  white-space: nowrap; }
@media (max-width: 400px) { .seg button { font-size: .8rem; padding: 9px 7px; } }
.seg button.on { background: var(--panel); color: var(--accent); box-shadow: var(--shadow-1); }

/* ============================================================ dock */
/* fixed, not absolute: it must be anchored to the viewport, not to a
   container whose height iOS recalculates while you scroll. */
.dock { position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  padding: 0 10px calc(var(--safe-b) + 8px); pointer-events: none; }
.dock > * { pointer-events: auto; }
.tabbar {
  display: grid;
  /* Eight equal columns and one that sizes to its own label: the centre
     button keeps the words "What Now" on ONE line while the other eight
     share what is left. Nine equal columns forced it to wrap, which made
     the centrepiece look like a mistake rather than a choice. */
  grid-template-columns: repeat(4, 1fr) auto repeat(4, 1fr); gap: 2px;
  max-width: 470px; margin: 0 auto; padding: 6px;
  background: var(--dock-bg); border: 1px solid var(--edge);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-2);
  backdrop-filter: saturate(150%) blur(18px);
  -webkit-backdrop-filter: saturate(150%) blur(18px);
}
.tabbar button {
  position: relative; display: grid; justify-items: center; gap: 3px;
  padding: 7px 1px 6px; border-radius: 14px; min-height: 48px;
  color: var(--ink-faint); font-size: .6rem; font-weight: 600; letter-spacing: .005em;
  line-height: 1.2;
  transition: color .16s ease, background .16s ease;
}
.tabbar button svg { width: 21px; height: 21px; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.tabbar button:hover { color: var(--ink-soft); }
.tabbar button > span { white-space: nowrap; }
.tabbar button.on { color: var(--accent); background: var(--accent-soft); }
.tabbar button.on svg { stroke-width: 2.1; }
.tabdot { position: absolute; top: 6px; right: 50%; margin-right: -16px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

/* ============================================================ player */
.player {
  max-width: 470px; margin: 0 auto 8px; display: flex; align-items: center; gap: 8px;
  background: var(--dock-bg); border: 1px solid var(--edge); border-radius: var(--radius);
  box-shadow: var(--shadow-2); padding: 14px 8px 9px; position: relative; overflow: hidden;
  backdrop-filter: saturate(150%) blur(18px); -webkit-backdrop-filter: saturate(150%) blur(18px);
}
.player[hidden] { display: none; }
/* four transport controls and a title on a 320px phone: the shell default of
   42px would leave the title 25px of room, so the player runs its own size */
.player .iconbtn { width: 36px; height: 36px; border-radius: 11px; }
.player .iconbtn svg { width: 21px; height: 21px; }
.p-open { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; text-align: left; }
.p-art { width: 42px; height: 42px; border-radius: 11px; object-fit: cover; flex: 0 0 auto; }
.p-meta { flex: 1; min-width: 0; }
.p-title { display: block; font-size: .9rem; font-weight: 600; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.p-sub { display: flex; align-items: baseline; gap: 7px; margin-top: 2px; }
.p-show { font-family: var(--mono); font-size: .62rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.p-time { flex: 0 0 auto; font-family: var(--mono); font-size: .62rem; letter-spacing: .04em;
  color: var(--ink-faint); font-variant-numeric: tabular-nums; }
@media (max-width: 360px) {
  .player { gap: 6px; }
  .player .iconbtn { width: 34px; height: 34px; }
  /* the row cannot hold five controls this narrow. Close moves into the
     Now playing sheet, where there is room to label it. */
  #p-close { display: none; }
}

/* The bar was two pixels of decoration. It is the seek control now: a 16px
   strip of hit area across the top of the player drawing a 3px line. An hour
   of live radio is unusable without one. */
.p-bar { position: absolute; left: 0; right: 0; top: 0; height: 16px; z-index: 2;
  cursor: pointer; touch-action: none; }
.p-bar::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: var(--edge); }
.p-bar i { position: absolute; left: 0; top: 0; height: 3px; width: 0; background: var(--accent); }
.p-bar i::after { content: ""; position: absolute; right: -5px; top: -3px; width: 9px; height: 9px;
  border-radius: 50%; background: var(--accent); opacity: 0; transition: opacity .15s ease; }
.p-bar:hover i::after, .p-bar.scrubbing i::after, .p-bar:focus-visible i::after { opacity: 1; }
.p-bar.scrubbing::before, .p-bar.scrubbing i { height: 4px; }

/* A sheet is z 90 and the dock is z 45, so pressing play on an artist put the
   player behind the sheet you pressed it in — which read as "it did not play".

   Raising the PLAYER is not enough: .dock is positioned with a z-index, so it
   opens a stacking context and any z-index on a child is resolved inside it.
   The dock itself has to come up. The tab bar comes with it, so it is hidden
   for as long as the sheet is open — it was already behind the sheet card and
   unreachable through the scrim, so nothing is lost and the player keeps the
   position it always has, above where the bar was. */
body.sheet-open .dock { z-index: 91; }
/* display, not visibility: the bar has to leave the layout so the player
   drops to the bottom edge, which is both where a mini player belongs and
   the only position the sheet's own bottom padding can be sized against. */
body.sheet-open .tabbar { display: none; }
body.sheet-open.has-player .sheet-body { padding-bottom: calc(92px + var(--safe-b)); }

/* ======================================================= now playing sheet */
.np-head { display: flex; gap: 14px; align-items: center; margin-bottom: 18px; }
.np-art { width: 76px; height: 76px; border-radius: var(--radius-sm); object-fit: cover;
  flex: 0 0 auto; }
.np-meta { min-width: 0; }
.np-title { font-size: 1.05rem; font-weight: 600; line-height: 1.3; }
.np-show { font-family: var(--mono); font-size: .64rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: 5px; }
.np-bar { position: relative; height: 22px; cursor: pointer; touch-action: none; }
.np-bar::before { content: ""; position: absolute; left: 0; right: 0; top: 9px; height: 4px;
  border-radius: 2px; background: var(--edge); }
.np-bar i { position: absolute; left: 0; top: 9px; height: 4px; border-radius: 2px; width: 0;
  background: var(--accent); }
.np-bar i::after { content: ""; position: absolute; right: -7px; top: -5px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--accent); box-shadow: var(--shadow-1); }
.np-times { display: flex; justify-content: space-between; font-family: var(--mono);
  font-size: .68rem; color: var(--ink-faint); font-variant-numeric: tabular-nums;
  margin: 2px 0 18px; }
.np-transport { display: flex; align-items: center; justify-content: center; gap: 22px;
  margin-bottom: 18px; }
.np-btn { display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  color: var(--ink); border-radius: var(--pill); }
.np-skip { position: relative; width: 52px; height: 52px; }
.np-skip svg { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round; }
.np-skip span { position: absolute; left: 0; right: 0; bottom: 6px; text-align: center;
  font-family: var(--mono); font-size: .55rem; color: var(--ink-faint); }
.np-flip svg { transform: scaleX(-1); }
/* the app never fills with accent — but a transport button must read as THE
   button. --ground inverts with the theme, so the glyph is near-white on the
   brown and near-black on the amber; an invented token would only fail in one. */
.np-big { width: 68px; height: 68px; background: var(--accent); color: var(--ground);
  box-shadow: var(--shadow-2); }
/* fill:none + a stroke, the same way .iconbtn does it: ICON.play carries its
   own inline fill and wins, ICON.pause is two stroked lines and would render
   as an empty circle under a fill-only rule. It did. */
.np-big svg { width: 30px; height: 30px; fill: none; stroke: currentColor;
  stroke-width: 2.6; stroke-linecap: round; }
.np-big:active { transform: scale(.95); }
.np-foot { display: flex; gap: 10px; align-items: center; }
.np-foot .np-btn { flex: 1; min-height: 44px; border: 1px solid var(--edge);
  background: var(--panel-2); font-size: .86rem; font-weight: 600; }
.np-stop { flex: 0 0 46px !important; color: var(--ink-faint); }
.np-stop svg { width: 17px; height: 17px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; }
.np-rate { font-variant-numeric: tabular-nums; }
.np-open svg { width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* a play button anywhere in the app turns into a pause while it is the thing
   playing — including inside a sheet, where the dock cannot be seen */
[data-pk].is-playing { color: var(--accent); }

/* ============================================================ video modal */
.vbox { position: fixed; inset: 0; z-index: 92; display: flex; align-items: center;
  justify-content: center; background: var(--scrim); padding: 16px; }
.vbox[hidden] { display: none; }
.vbox-card { width: 100%; max-width: 660px; background: var(--ground);
  border: 1px solid var(--edge); border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: var(--shadow-2); }
.vbox-head { display: flex; align-items: center; gap: 10px; padding: 12px 8px 12px 16px; }
.vbox-head .t { flex: 1; min-width: 0; font-size: .96rem; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vbox-frame { aspect-ratio: 16/9; background: #000; }
.vbox-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.vbox-foot { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 11px 16px 14px; }
.vbox-hint { font-size: .8rem; color: var(--ink-faint); }

/* ============================================================ toast */
/* ------------------------------------------------------------- welcome */
.welcome { position: fixed; inset: 0; z-index: 98; display: grid; place-items: center;
  padding: 22px; background: var(--scrim); backdrop-filter: blur(3px); }
.welcome-card { width: 100%; max-width: 360px; background: var(--ground);
  border: 1px solid var(--edge); border-radius: var(--radius-lg); padding: 24px 22px 20px;
  box-shadow: var(--shadow-2); animation: sheetin .24s cubic-bezier(.2,.9,.3,1); }
.welcome-card h2 { font-family: var(--serif); font-size: 1.62rem; font-weight: 400;
  line-height: 1.1; margin: 4px 0 16px; }
.welcome-list { list-style: none; display: grid; gap: 11px; margin: 0 0 16px; padding: 0; }
.welcome-list li { display: grid; grid-template-columns: 88px 1fr; align-items: baseline; gap: 10px; }
.welcome-list b { font-size: .95rem; font-weight: 600; color: var(--accent); }
.welcome-list span { font-size: .88rem; line-height: 1.4; color: var(--ink-soft); }
.welcome-local { display: flex; align-items: center; gap: 8px; font-size: .87rem;
  color: var(--ink-soft); padding: 10px 0 18px; border-top: 1px solid var(--edge-soft); }
.welcome-local i { width: 10px; height: 10px; border-radius: 3px; background: var(--local); flex: 0 0 auto; }
.welcome .btn { width: 100%; }

.toast {
  position: fixed; bottom: calc(var(--tabbar-h) + var(--safe-b) + 20px);
  left: 50%; transform: translateX(-50%); z-index: 96;
  background: var(--ink); color: var(--ground); border-radius: var(--pill);
  padding: 11px 20px; font-size: .9rem; font-weight: 500;
  box-shadow: var(--shadow-2); max-width: 88vw; text-align: center;
}
.toast[hidden] { display: none; }

.confirm { position: fixed; inset: 0; z-index: 97; display: grid; place-items: center;
  padding: 20px; background: var(--scrim); }
.confirm[hidden] { display: none; }
.confirm-card { width: 100%; max-width: 340px; background: var(--ground);
  border: 1px solid var(--edge); border-radius: var(--radius-lg); padding: 22px;
  box-shadow: var(--shadow-2); text-align: center; }
.confirm-card h3 { font-family: var(--serif); font-size: 1.35rem; font-weight: 400; margin-bottom: 7px; }
.confirm-card p { font-size: .93rem; color: var(--ink-soft); line-height: 1.5; margin-bottom: 18px; }
.confirm-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }

body.chrome-away .fresh { top: calc(var(--top-h) + 12px); }
.fresh {
  position: fixed; top: calc(var(--top-h) + var(--mast-h) + 12px); left: 50%;
  transform: translateX(-50%); z-index: 44;
  transition: top .26s ease;
  background: var(--accent); color: #fff; border-radius: var(--pill);
  padding: 9px 18px; font-family: var(--mono); font-size: .66rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; box-shadow: var(--shadow-2);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
}

.sheet-note { font-size: .82rem; color: var(--ink-faint); margin: -2px 0 12px; }

/* ================================================ wire header controls */

/* Search is a chip now, not a band. It is the one chip with a colour of its
   own so it reads as a verb among nouns. */
.chip.search-chip {
  color: var(--accent); border-color: color-mix(in srgb, var(--accent) 34%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, var(--panel));
  font-weight: 600;
}
.chip.search-chip.on { background: var(--accent-soft); border-color: transparent; }
.chip.search-chip svg { width: 15px; height: 15px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; flex: 0 0 auto; }

/* Outlets: locals on the first line, the wire on the second. Smaller than a
   topic chip — there are forty of them and they are a second-order control. */
.chips.srcbar { gap: 6px; padding-top: 0; padding-bottom: 6px; }
.chips.srcbar .chip {
  font-size: .74rem; padding: 5px 11px; min-height: 30px; border-radius: var(--pill);
  font-weight: 500;
}
.chips.srcbar .chip.on { font-weight: 600; }
.chips.srcbar + .hint-bar { margin-bottom: 8px; }
.srcbar.is-local .chip { border-color: color-mix(in srgb, var(--local) 26%, var(--edge)); }
.srcchip.c-local { color: var(--t-local); }
.srcchip.c-news { color: var(--t-news); }
.srcchip.c-politics { color: var(--t-politics); }
.srcchip.c-business { color: var(--t-business); }
.srcchip.c-tech { color: var(--t-tech); }
.srcchip.c-science { color: var(--t-science); }
.srcchip.c-sports { color: var(--t-sports); }
.srcchip.c-culture { color: var(--t-culture); }
.srcchip.c-gaming { color: var(--t-gaming); }
.srcchip.c-newsletters { color: var(--t-newsletters); }

/* Layout switch and Focus, out where they can be found. Focus used to live
   three taps deep in Settings, which is the same as not existing. */
.toolrow { display: flex; align-items: center; gap: 8px; margin: 4px 0 12px; flex-wrap: wrap; }
.toolseg { display: inline-flex; border: 1px solid var(--edge); border-radius: var(--pill);
  overflow: hidden; background: var(--panel); }
.toolbtn { font: inherit; font-size: .76rem; font-weight: 500; color: var(--ink-soft);
  background: none; border: 0; padding: 7px 13px; min-height: 32px; cursor: pointer;
  transition: background .15s ease, color .15s ease; }
.toolseg .toolbtn + .toolbtn { border-left: 1px solid var(--edge); }
.toolbtn:hover { background: var(--panel-hover); }
.toolbtn.on { color: var(--accent); background: var(--accent-soft); font-weight: 600; }
/* a toolbtn that is an <a>, not a <button> — dressed like the focus pill */
.toollink { display: inline-flex; align-items: center; text-decoration: none;
  border: 1px solid var(--edge); border-radius: var(--pill); background: var(--panel); }
/* The Wire's layout switch leads the chip band, so it has to survive a flex
   row that stretches its children and scrolls sideways: hold its own width,
   centre against a 38px chip rather than growing to match it, and come in a
   size down so it reads as the switch and not as another topic. */
.chips > .toolseg { flex: 0 0 auto; align-self: center; }
.toolseg-sm .toolbtn { font-size: .72rem; padding: 6px 11px; min-height: 30px; }
.focus-btn { border: 1px solid var(--edge); border-radius: var(--pill); background: var(--panel); }
.focus-btn.on { border-color: transparent; }

/* Temperature, lake, sunset. Three numbers, one line, no card. */
/* One line on a phone. Three readings plus a link is exactly enough to
   overflow 390px and drop the link onto a row of its own, right-aligned,
   which reads as a layout bug rather than a footnote. */
.wxstrip { display: flex; align-items: stretch; gap: 4px;
  margin: 0 0 16px; border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--local) 22%, var(--edge));
  background: color-mix(in srgb, var(--local) 6%, var(--panel));
  overflow: hidden; }
.wxstrip-in { display: flex; align-items: baseline; gap: 10px; flex: 1 1 auto;
  min-width: 0; padding: 9px 2px 9px 12px; }
/* Lake and Sunset are four characters each and must never truncate. Only the
   conditions line gives way — "70° Partly Cloudy" can lose its adjective and
   still be a temperature. */
.wxstrip .wx-bit { flex: 0 0 auto; white-space: nowrap; }
.wxstrip .wx-bit:first-child { flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; }
.wx-x { flex: 0 0 auto; width: 28px; font: inherit; font-size: 1rem;
  line-height: 1; color: var(--ink-faint); background: none; border: 0;
  cursor: pointer; }
.wx-x:hover { color: var(--ink); }
.wxstrip-in:hover { background: color-mix(in srgb, var(--local) 8%, transparent); }
.wx-bit { display: inline-flex; align-items: baseline; gap: 6px; }
.wx-k { font-family: var(--mono); font-size: .6rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-faint); }
.wx-v { font-size: .86rem; font-weight: 600; }
.wx-go { margin-left: auto; padding-left: 2px; font-size: .72rem; color: var(--local);
  font-weight: 600; flex: 0 0 auto; white-space: nowrap; }

/* ==================================================== by-source layout */
.srcgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 14px; }
.srcsec { border: 1px solid var(--edge); border-radius: var(--radius-sm);
  background: var(--panel); padding: 11px 12px 4px; }
.srcsec.is-local { border-color: color-mix(in srgb, var(--local) 34%, transparent); }
.srcsec-head { display: flex; align-items: baseline; gap: 8px; margin: 0 0 6px;
  padding-bottom: 7px; border-bottom: 1px solid var(--edge); }
.srcsec-name { font: inherit; font-size: .84rem; font-weight: 700; letter-spacing: .01em;
  background: none; border: 0; padding: 0; cursor: pointer; color: var(--ink); }
.srcsec-name:hover { text-decoration: underline; }
.srcsec-name.c-local { color: var(--t-local); }
.srcsec-name.c-news { color: var(--t-news); }
.srcsec-name.c-politics { color: var(--t-politics); }
.srcsec-name.c-business { color: var(--t-business); }
.srcsec-name.c-tech { color: var(--t-tech); }
.srcsec-name.c-science { color: var(--t-science); }
.srcsec-name.c-sports { color: var(--t-sports); }
.srcsec-name.c-culture { color: var(--t-culture); }
.srcsec-name.c-gaming { color: var(--t-gaming); }
.srcsec-name.c-newsletters { color: var(--t-newsletters); }
.srcsec-tag { margin-left: auto; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); }
.feed-tight .fi { padding-top: 8px; padding-bottom: 8px; }
.feed-tight .fi-title { font-size: .9rem; line-height: 1.3; }

.welcome-swipe { display: grid; gap: 5px; margin: 12px 0 0; padding-top: 12px;
  border-top: 1px solid var(--edge); font-size: .82rem; color: var(--ink-soft); }
.welcome-swipe em { font-style: normal; font-weight: 600; color: var(--ink);
  font-size: .78rem; }
.welcome-swipe b { display: inline-block; width: 1.2em; font-size: 1rem;
  color: var(--accent); font-weight: 700; }

/* The same lesson, once, on the wire itself — for everyone who tapped past
   the intro card, and for everyone who was already here before it existed. */
.tipbar { display: flex; align-items: center; gap: 10px; margin: 0 0 14px;
  padding: 9px 12px; border-radius: var(--radius-sm);
  border: 1px dashed var(--edge); background: var(--panel-2);
  font-size: .8rem; color: var(--ink-soft); }
.tipbar b { color: var(--ink); font-weight: 600; }
/* The subscribe tip is the one tipbar holding a link; it has to read as the
   action, not vanish into the hint text around it. */
.tipbar a { color: var(--accent-bright); font-weight: 600; text-decoration: none;
  white-space: nowrap; }
.tipbar a:hover { text-decoration: underline; }
.tipbar .x { margin-left: auto; flex: 0 0 auto; font: inherit; font-size: 1rem;
  line-height: 1; background: none; border: 0; color: var(--ink-faint);
  cursor: pointer; padding: 2px 4px; }
.tipbar .x:hover { color: var(--ink); }

/* One line under each tab's title: when this tab's content last changed. */
.updated { display: inline-flex; align-items: center; gap: 5px; }
.updated i { width: 5px; height: 5px; border-radius: 50%; background: var(--local);
  flex: 0 0 auto; }
.updated.is-old i { background: var(--ink-faint); }

.tabstamp { display: flex; align-items: baseline; gap: 10px;
  margin: 0 0 12px; font-family: var(--mono); font-size: .62rem;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-faint); }
/* one line: a wrapped stamp puts a right-aligned orphan under the left one,
   which reads as a layout bug rather than a caption */
.tabstamp-what { margin-left: auto; text-align: right; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tabstamp .updated { flex: 0 0 auto; }


/* ==================================================== btown tv masthead */
.tvmast { margin: 0 0 14px; }
.tvmast-pre { font-family: var(--mono); font-size: .58rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-faint); margin: 0 0 1px; }
.tvmast h1 { font-family: var(--serif); font-size: 1.72rem; line-height: 1;
  letter-spacing: -.018em; margin: 0 0 4px; }
/* The Play button is a requested, deliberate ~50px. The rest of the block
   pays for it rather than the page growing: the tagline tightens and the
   note under the button rides close. */
.tvmast-sub { font-size: .79rem; line-height: 1.36; color: var(--ink-soft);
  margin: 0; max-width: 46ch; }

/* ===================================================== the local switch */
/* The one control the whole app is about. Full width, first thing on every
   tab, and green enough that you find it without reading it. */
.localsw { margin: 0 0 14px; }
/* The switch and the jar share a line. The switch takes the room it needs and
   the jar sits on the end of it — one row, not a second band. */
.localsw-row { display: flex; align-items: stretch; gap: 8px; }
.localsw-row .localsw-bar { flex: 1 1 auto; min-width: 0; }

/* ============================================================ music tools */
.m-tools { display: flex; align-items: stretch; gap: 8px; margin: 0 0 14px; }
.m-mixlink { flex: 1 1 auto; display: inline-flex; align-items: center; justify-content: center;
  gap: 7px; min-height: 44px; padding: 0 14px; border-radius: var(--pill);
  border: 1px solid var(--edge); background: var(--panel); color: var(--ink-soft);
  font-size: .84rem; font-weight: 600; box-shadow: var(--shadow-1); }
.m-mixlink:hover { color: var(--accent); border-color: var(--accent); }
.m-mixlink.on { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }

/* ========================================================= venue calendar */
/* Text and a date. A show is a name, a night and a room, and a wall of
   posters is worse at all three. */
.cal-rooms { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 0 0 14px; }
.cal-room { display: grid; gap: 3px; text-align: left; padding: 12px 13px;
  border: 1px solid var(--edge); border-radius: var(--radius); background: var(--panel);
  box-shadow: var(--shadow-1); }
.cal-room:hover { border-color: var(--accent); }
.cal-room-name { font-size: .95rem; font-weight: 600; }
.cal-room-n { font-family: var(--mono); font-size: .6rem; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-faint); }
@media (max-width: 360px) { .cal-rooms { grid-template-columns: 1fr; } }

.cal-list { margin-top: 4px; }
/* Deliberately NOT sticky. A sticky child of .panel measures its offset from
   the panel's padding box, so `top: 0` parks it under the pinned nav rather
   than below it — the same trap the wire's control band hit. A date header
   every few rows does not need to follow you down the page. */
.cal-day { margin: 16px 0 6px; padding: 6px 0 5px;
  font-family: var(--mono); font-size: .63rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent); }
.cal-day:first-child { margin-top: 0; }
.cal-day-rows { border: 1px solid var(--edge); border-radius: var(--radius);
  background: var(--panel); overflow: hidden; }
.cal-row { display: grid; grid-template-columns: 62px 1fr auto; align-items: baseline;
  gap: 10px; padding: 11px 13px; border-bottom: 1px solid var(--edge-soft);
  color: inherit; text-decoration: none; }
.cal-row:last-child { border-bottom: 0; }
.cal-row:hover { background: var(--panel-hover); }
.cal-when { font-family: var(--mono); font-size: .68rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.cal-what { min-width: 0; }
/* Listing titles run to 120 characters and venue names to 45 — a full bill
   ("PUNK ROCK QUEER PROM at the LIGHT CLUB LAMP SHOP! ft. ...") is five lines
   of a compact list otherwise. Two lines, and the sheet is not the place to
   read the whole bill; the link is. */
.cal-title { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; font-size: .92rem; line-height: 1.32; font-weight: 500; }
.cal-where { display: block; font-size: .76rem; color: var(--ink-faint); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-run { display: block; font-family: var(--mono); font-size: .6rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: 3px; }
.cal-price { font-family: var(--mono); font-size: .68rem; color: var(--ink-faint);
  white-space: nowrap; }
.cal-free { color: var(--good); font-weight: 600; }
.cal-back { display: inline-flex; align-items: center; min-height: 40px; padding: 0 4px;
  margin-bottom: 4px; font-size: .84rem; font-weight: 600; color: var(--ink-faint); }
.cal-back:hover { color: var(--accent); }
.cal-site { width: 100%; margin-top: 14px; }
.cal-site svg { width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* ================================================================== jar */
.jarbtn {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  padding: 0 13px; min-height: 44px; border-radius: var(--pill);
  border: 1px solid var(--edge); background: var(--panel);
  color: var(--ink-soft); font-size: .78rem; font-weight: 600;
  box-shadow: var(--shadow-1); white-space: nowrap;
  transition: color .15s ease, border-color .15s ease;
}
.jarbtn:hover { color: var(--accent); border-color: var(--accent); }
.jarbtn svg { width: 18px; height: 18px; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
@media (max-width: 360px) { .jarbtn span { display: none; } .jarbtn { padding: 0 11px; } }

/* on the tabs with no Local switch to sit beside */
.jarrow { display: flex; justify-content: flex-end; margin: -4px 0 12px; }

.jar-lede { color: var(--ink-soft); margin: 0 0 16px; }
.jar-form { display: grid; gap: 12px; }
.jar-form label { display: grid; gap: 6px; font-size: .84rem; font-weight: 600;
  color: var(--ink-soft); }
.jar-form textarea, .jar-form input {
  width: 100%; font: inherit; font-size: .95rem; font-weight: 400; color: var(--ink);
  background: var(--panel-2); border: 1px solid var(--edge);
  border-radius: var(--radius-sm); padding: 11px 12px; resize: vertical;
}
.jar-form textarea:focus, .jar-form input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.jar-who { font-weight: 500; }
.jar-err { color: var(--warn); font-size: .85rem; margin: 0; }

/* The tip half, set apart so it reads as an offer rather than a toll on the
   suggestion above it. */
.jar-tip { margin: 22px 0 0; padding: 16px 16px 18px; border-radius: var(--radius-sm);
  background: var(--accent-soft); }
.jar-tip-lede { margin: 0 0 13px; font-size: .92rem; line-height: 1.5; color: var(--ink); }
.jar-kofi { width: 100%; }
.jar-kofi svg { width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.jar-fine { margin: 16px 0 0; font-size: .82rem; line-height: 1.5; color: var(--ink-faint); }


.localsw-bar {
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px; padding: 4px;
  border-radius: var(--pill); background: var(--panel-2);
  border: 1px solid var(--edge);
  transition: background .2s ease, border-color .2s ease;
}
.localsw-half {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 40px; padding: 8px 10px; border: 0; border-radius: var(--pill);
  font: inherit; font-size: .92rem; font-weight: 600; letter-spacing: -.005em;
  color: var(--ink-faint); background: none; cursor: pointer;
  transition: background .18s ease, color .18s ease, box-shadow .18s ease;
}
.localsw-half:hover { color: var(--ink); }
.localsw-half.on { background: var(--panel); color: var(--ink); box-shadow: var(--shadow-1); }

/* the leaf is the recognisable part — it stays coloured even when Local is
   the half you are NOT on, so the control reads as "local lives here" */
.localsw-leaf { font-size: 1.02rem; line-height: 1; filter: saturate(1.1); }

/* switched on, the whole control commits to it */
.localsw.is-on .localsw-bar {
  background: color-mix(in srgb, var(--local) 12%, var(--panel-2));
  border-color: color-mix(in srgb, var(--local) 34%, transparent);
}
.localsw.is-on .localsw-half.on {
  background: var(--local); color: var(--local-ink);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--local) 40%, transparent);
}
.localsw.is-on .localsw-half:not(.on) { color: var(--local); }
/* the leaf reads as a silhouette on the deep-green fill; in dark mode the
   fill is a light mint and it needs no help */
.localsw.is-on .localsw-half.on .localsw-leaf { filter: brightness(1.55) saturate(1.3); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .localsw.is-on .localsw-half.on .localsw-leaf { filter: none; }
}
:root[data-theme="dark"] .localsw.is-on .localsw-half.on .localsw-leaf { filter: none; }

.localsw-note { margin: 7px 2px 0; font-size: .76rem; color: var(--ink-faint);
  text-align: center; }
.localsw-note b { color: var(--ink-soft); font-weight: 600;
  font-variant-numeric: tabular-nums; }
.localsw.is-on .localsw-note b { color: var(--local); }
.localsw-of { opacity: .7; }

@media (prefers-reduced-motion: reduce) {
  .localsw-bar, .localsw-half { transition: none; }
}

/* ================================================== the control band */
/* Topic chips and the view switch, pinned under the masthead. Scrolling two
   thousand headlines and then having to scroll all the way back to change
   topic is the single most annoying thing a feed can do.

   It floats rather than bleeding edge to edge: the panel's horizontal
   padding is a max() expression on wide screens, so there is no honest
   negative margin to cancel it. A rounded bar with a shadow reads as a
   deliberate floating control instead of a sticky element with gaps. */
.ctlband {
  position: sticky;
  top: calc(var(--top-h) + var(--mast-h) + 6px);
  z-index: 12;
  margin: 0 -6px 12px;
  padding: 8px 8px 2px;
  border-radius: var(--radius);
  background: var(--dock-bg);
  backdrop-filter: saturate(150%) blur(18px);
  -webkit-backdrop-filter: saturate(150%) blur(18px);
  border: 1px solid var(--edge-soft);
  box-shadow: var(--shadow-1);
  transition: top .26s ease;
}
/* the masthead slides away as you read; the band follows it up */
/* the masthead has gone; the shared bar has not */
body.chrome-away .ctlband { top: calc(var(--top-h) + 6px); }
.ctlband .chips { padding-bottom: 6px; }
.ctlband .hint-bar { margin-bottom: 8px; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ctlband { background: var(--ground); }
}

/* ================================================ the tab bar, louder */
/* It was a pale row of small grey glyphs on a translucent ground — on first
   landing it read as a caption, not as the way between five feeds. It now
   sits up off the page: opaque, a lit top edge, a real shadow under it, and
   an active tab that is a filled pill rather than a faint tint. */
.tabbar {
  background: var(--tabbar-bg);
  border-color: var(--tabbar-edge);
  box-shadow: var(--shadow-3), inset 0 1px 0 var(--tabbar-lip);
}
.tabbar button { color: var(--ink-soft); }
.tabbar button svg { stroke-width: 1.9; }
.tabbar button:active { transform: translateY(1px); }
.tabbar button.on {
  color: var(--tab-on-ink);
  background: var(--accent);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 45%, transparent);
}
.tabbar button.on svg { stroke-width: 2.3; }
/* No marker above the pill: it sits outside the bar's padding box and reads
   as a stray sliver rather than an indicator. The filled pill is the signal. */
.tabbar button.on .tabdot { background: var(--tab-on-ink); }
@media (prefers-reduced-motion: reduce) {
  .tabbar button:active { transform: none; }
  .ctlband { transition: none; }
}


/* ============================================ play the edition on a TV */
/* Every night's page is published as its own YouTube playlist. Casting the
   whole edition is the best thing this tab does and it was a quiet outline
   button underneath six shelves — filled, and up in the masthead, the way
   BTown TV had it. */
.tvmast-row { display: flex; align-items: center; gap: 5px 11px; flex-wrap: wrap; margin-top: 10px; }
.tv-play {
  background: var(--accent); color: var(--tab-on-ink); border-color: transparent;
  padding: 10px 17px; font-size: .89rem; min-height: 42px;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 42%, transparent);
}
.tv-play:hover { background: color-mix(in srgb, var(--accent) 88%, #000); }
.tv-play svg { width: 15px; height: 15px; fill: currentColor; stroke: none; flex: 0 0 auto; }
.tvmast-n { font-size: .74rem; line-height: 1.2; color: var(--ink-faint); }

/* the newsletter edition a hand-written pick came from */
.pick-from { display: block; margin-top: 9px; font-family: var(--mono);
  font-size: .58rem; letter-spacing: .11em; text-transform: uppercase;
  color: var(--local); }
.pick-why { display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical;
  overflow: hidden; }
/* ============================================================ music */
/* Cover art is the whole point of a music surface, so it leads everything and
   is never desaturated. Where an artist has no art, the initials sit on their
   own stable hue rather than a grey box — a grey box reads as a failed image,
   which is the one thing this tab cannot look like. */

.m-noart {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--ui); font-weight: 700; letter-spacing: -.02em;
  color: hsl(var(--hue, 30) 62% 32%);
  background: hsl(var(--hue, 30) 58% 90%);
  box-shadow: inset 0 0 0 1px hsl(var(--hue, 30) 40% 80%);
}
:root[data-theme="dark"] .m-noart,
:root:not([data-theme="light"]) .m-noart {
  color: hsl(var(--hue, 30) 70% 78%);
  background: hsl(var(--hue, 30) 34% 22%);
  box-shadow: inset 0 0 0 1px hsl(var(--hue, 30) 30% 34%);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .m-noart {
    color: hsl(var(--hue, 30) 62% 32%);
    background: hsl(var(--hue, 30) 58% 90%);
    box-shadow: inset 0 0 0 1px hsl(var(--hue, 30) 40% 80%);
  }
}

/* ---- the shelf of people playing soon ---- */
/* Its own width, set here rather than on .rail — a column size on the shared
   rail class makes wider cards from other tabs overlap each other. */
/* 152px, not 216 — at the larger size one card filled a phone screen and the
   shelf showed one and a bit, which is a list wearing a carousel's clothes.
   This is the video card's width, and the shelf reads as a row you push. */
.m-gig {
  width: 152px; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--edge);
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-1);
}
.m-gig-hit { display: block; text-align: left; width: 100%; }
.m-gig-art { display: block; width: 100%; aspect-ratio: 1; object-fit: cover;
  background: var(--panel-2); font-size: 1.45rem; }
.m-gig-body { display: block; padding: 8px 9px 3px; }
.m-gig-when { display: block; font-family: var(--mono); font-size: .58rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--accent); font-weight: 600; }
.m-gig-name { display: block; font-size: .9rem; font-weight: 600; line-height: 1.22;
  margin-top: 2px; letter-spacing: -.004em; }
.m-gig-where { display: block; font-size: .74rem; color: var(--ink-soft); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.m-gig-foot { display: flex; align-items: center; gap: 4px; padding: 2px 6px 6px; }

/* ---- the directory ---- */
.m-list { border-top: 1px solid var(--edge-soft); }
.m-row { display: flex; align-items: center; gap: 8px;
  padding: 8px 2px; border-bottom: 1px solid var(--edge-soft); }
.m-row-hit { flex: 1; min-width: 0; display: flex; align-items: center; gap: 11px;
  text-align: left; }
.m-row-art { width: 54px; height: 54px; flex: 0 0 auto; border-radius: 10px;
  object-fit: cover; background: var(--panel-2); font-size: .95rem; }
.m-row-meta { min-width: 0; display: block; }
.m-row-name { display: block; font-size: .99rem; font-weight: 600; line-height: 1.25;
  letter-spacing: -.004em; }
.m-row-tags { display: block; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); margin-top: 2px; }
.m-soon { color: var(--accent); font-weight: 700; }
.m-row-why { display: block; font-size: .82rem; color: var(--ink-soft); line-height: 1.35;
  margin-top: 3px; overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.m-row-acts { display: flex; align-items: center; gap: 3px; flex: 0 0 auto; }

/* One play button, used in the shelf, the rows and the sheet. */
.m-play { width: 34px; height: 34px; flex: 0 0 auto; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--accent); background: var(--accent-soft); }
.m-play svg { width: 17px; height: 17px; fill: currentColor; stroke: none; }
.m-play:active { transform: scale(.92); }

/* ---- the artist sheet ---- */
.m-sheet-head { display: flex; gap: 13px; align-items: center; margin-bottom: 12px; }
.m-sheet-art { width: 86px; height: 86px; flex: 0 0 auto; border-radius: 13px;
  object-fit: cover; background: var(--panel-2); font-size: 1.5rem; }
.m-sheet-meta h3 { font-family: var(--serif); font-size: 1.5rem; font-weight: 400;
  line-height: 1.12; }
.m-sheet-sub { font-family: var(--mono); font-size: .62rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: 4px; }
.m-why-big { font-size: 1rem; line-height: 1.5; color: var(--ink-soft);
  margin: 0 0 16px; }

.m-shows { display: grid; gap: 7px; margin-bottom: 16px; }
.m-show { display: block; padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--panel-2); border: 1px solid var(--edge-soft); }
.m-show-when { display: block; font-family: var(--mono); font-size: .64rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--accent); font-weight: 600; }
.m-show-where { display: block; font-size: .93rem; font-weight: 600; margin-top: 3px; }
.m-show-what { display: block; font-size: .82rem; color: var(--ink-faint); margin-top: 2px; }

.m-session { display: flex; align-items: center; gap: 11px; margin-bottom: 16px;
  padding: 10px 12px; border-radius: var(--radius-sm); background: var(--panel-2);
  border: 1px solid var(--edge-soft); }
.m-session-meta { min-width: 0; }
.m-session-title { display: block; font-size: .88rem; font-weight: 500; line-height: 1.3;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.m-session-date { display: block; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); margin-top: 2px; }

/* Bandcamp's own player. It is the only embed here that plays a whole track
   to someone who is not logged in, and it is built once, in the sheet. */
.m-bc { width: 100%; height: 120px; border: 0; border-radius: var(--radius-sm);
  margin-bottom: 14px; background: var(--panel-2); }
.m-links { margin-bottom: 4px; }

.m-credit { font-size: .78rem; color: var(--ink-faint); line-height: 1.5;
  margin: 18px 2px 4px; }
.m-credit a { color: var(--ink-soft); text-decoration: underline; }

/* ---- the mixtape ---- */
.m-theme { padding: 12px 14px; border-radius: var(--radius); margin-bottom: 13px;
  background: var(--local-soft); border: 1px solid var(--edge-soft); }
.m-theme .eyebrow { color: var(--local); }
.m-theme-title { display: block; font-family: var(--serif); font-size: 1.3rem;
  line-height: 1.15; margin-top: 3px; }
.m-theme p { font-size: .87rem; color: var(--ink-soft); margin-top: 5px; line-height: 1.45; }
.m-add { width: 100%; margin-bottom: 16px; }

.m-tracks { display: block; }
.m-track { display: flex; align-items: center; gap: 10px; padding: 10px 2px;
  border-bottom: 1px solid var(--edge-soft); }
.m-track-body { flex: 1; min-width: 0; }
.m-track-song { display: block; font-size: .98rem; font-weight: 600; line-height: 1.25; }
.m-track-by { display: block; font-size: .84rem; color: var(--ink-soft); margin-top: 1px; }
.m-local { color: var(--local); font-weight: 700; font-size: .74rem;
  background: var(--local-soft); border-radius: 4px; padding: 1px 5px; }
.m-track-why { display: block; font-size: .82rem; color: var(--ink-soft);
  line-height: 1.4; margin-top: 4px; }
.m-track-who { display: block; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); margin-top: 3px; }
.m-track-go { flex: 0 0 auto; font-size: .74rem; font-weight: 600; color: var(--accent);
  background: var(--accent-soft); border-radius: var(--pill); padding: 6px 11px;
  white-space: nowrap; }

/* ---- the form ---- */
.m-form-intro { font-size: .9rem; color: var(--ink-soft); line-height: 1.5;
  margin-bottom: 14px; }
.m-form { display: grid; gap: 11px; }
.m-form label { display: block; font-size: .8rem; font-weight: 600; color: var(--ink-soft); }
.m-form input[type=url], .m-form input[type=text], .m-form textarea {
  display: block; width: 100%; margin-top: 5px; padding: 11px 12px;
  font: inherit; font-size: 1rem; color: var(--ink);
  background: var(--panel-2); border: 1px solid var(--edge);
  border-radius: var(--radius-sm); min-height: 44px;
}
.m-form input:focus, .m-form textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.m-form-two { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.m-form-check { display: flex; align-items: center; gap: 8px; font-weight: 500;
  color: var(--ink); }
.m-form-check input { width: 20px; height: 20px; }
.m-form details summary { font-size: .84rem; color: var(--ink-faint); cursor: pointer;
  padding: 4px 0; }
.m-form details label { margin-top: 9px; }

/* A rail sitting on the app ground needs its dots to mask the track behind
   them in the same colour, or the pill shows as a patch of the wrong shade. */
.m-gig .rail-dots { background: var(--ground); }

/* The card classes below set `display: block`, and they are declared after
   .m-noart, so they were quietly winning and stranding the initials in the
   top-left corner. Same specificity, so source order settles it — this has to
   stay at the end of the file. */
.m-noart { display: grid; place-items: center; }

/* Every music product that shows a lot of sleeves draws a hairline inside the
   artwork — Apple Music, Spotify, Bandcamp and Last.fm each ship their own
   version. Without it a white sleeve dissolves into a light page and a black
   one into a dark page. Bandcamp's is the cheapest: an outline pulled inward,
   which follows border-radius and costs no layout.

   Dark needs roughly twice the alpha of light. On a dark ground a pale sleeve
   is already separated by luminance; the case the ring has to win is a BLACK
   sleeve, and it has to out-contrast that. Apple ships 10% black / 25% white
   for exactly this reason. */
.m-gig-art, .m-row-art, .m-sheet-art {
  outline: 1px solid rgba(20, 16, 8, .11);
  outline-offset: -1px;
}
:root[data-theme="dark"] .m-gig-art,
:root[data-theme="dark"] .m-row-art,
:root[data-theme="dark"] .m-sheet-art,
:root:not([data-theme="light"]) .m-gig-art,
:root:not([data-theme="light"]) .m-row-art,
:root:not([data-theme="light"]) .m-sheet-art {
  outline-color: rgba(255, 255, 255, .22);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .m-gig-art,
  :root:not([data-theme="dark"]) .m-row-art,
  :root:not([data-theme="dark"]) .m-sheet-art {
    outline-color: rgba(20, 16, 8, .11);
  }
}

/* Dates, times and prices line up column-wise down a list of shows. */
.m-gig-when, .m-show-when, .m-row-tags, .m-session-date { font-variant-numeric: tabular-nums; }

/* ------------------------------------------- nine tabs, every label */
/* Nine labels DO fit — but only with the two longest shortened and the type
   brought down. Measured across 320/375/390, all nine visible:

     "Wikipedia" + "Instagram"   overflows the page at 320, 375 AND 390
     "Wiki" + "Insta" @ .6rem    fits 375 and 390, overflows 320
     "Wiki" + "Insta" @ .52rem   fits 320 too, once gaps and padding tighten

   Hiding the labels was the earlier answer and it made eight of nine tabs a
   guess. Shortening two words costs less than that. What Now's column is
   `auto`, so it takes the room its label needs and the other eight share the
   rest — which is why it can keep two words while they get one. */
@media (max-width: 374px) {
  .tabbar { gap: 1px; padding: 3px; }
  .tabbar button { font-size: .52rem; padding-left: 1px; padding-right: 1px; }
  .tabbar button svg { width: 20px; height: 20px; }
  .tabbar #tab-whatnow > span { padding: 0 4px; }
}

/* ============================================================ photos */
/* Tonight first, because "is it worth going out" is the question that turns a
   reader into a photographer. Asking for photos does not get photos. */
.ph-tonight {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 14px 16px; border-radius: var(--radius); margin-bottom: 6px;
  background: linear-gradient(105deg,
    color-mix(in srgb, var(--accent) 15%, transparent),
    color-mix(in srgb, var(--accent) 4%, transparent));
  border: 1px solid var(--edge-soft);
}
.ph-t-left { min-width: 0; }
.ph-t-time { display: block; font-family: var(--serif); font-size: 2rem; line-height: 1.05;
  margin-top: 2px; font-variant-numeric: tabular-nums; }
.ph-t-sub { display: block; font-size: .92rem; color: var(--ink-soft); margin-top: 2px; }
.ph-t-note { font-size: .78rem; color: var(--ink-faint); line-height: 1.5;
  margin: 0 2px 14px; }
.ph-t-note a { color: var(--ink-soft); text-decoration: underline; }

/* The number, on a ring that fills with the score. */
.ph-score {
  flex: 0 0 auto; width: 72px; height: 72px; border-radius: 50%;
  display: grid; place-items: center; position: relative;
  background: conic-gradient(var(--accent) var(--fill, 0%), var(--edge-soft) 0);
}
.ph-score::after {
  content: ""; position: absolute; inset: 5px; border-radius: 50%;
  background: var(--ground);
}
.ph-score-n { position: relative; z-index: 1; font-family: var(--serif);
  font-size: 1.5rem; line-height: 1; font-variant-numeric: tabular-nums; }
.ph-score-of { position: relative; z-index: 1; font-family: var(--mono);
  font-size: .55rem; letter-spacing: .08em; color: var(--ink-faint); margin-top: 1px; }

/* Where to stand — its own width, never a column size on the shared rail. */
.ph-spot {
  width: 208px; display: flex; flex-direction: column; gap: 3px;
  padding: 11px 12px; background: var(--panel); border: 1px solid var(--edge);
  border-radius: var(--radius); box-shadow: var(--shadow-1);
}
.ph-spot-name { font-size: .98rem; font-weight: 600; line-height: 1.2; }
.ph-spot-meta { font-family: var(--mono); font-size: .58rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent); font-variant-numeric: tabular-nums; }
.ph-spot-why { font-size: .8rem; color: var(--ink-soft); line-height: 1.35; margin-top: 2px;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
.ph-spot-map { font-size: .76rem; font-weight: 600; color: var(--accent); margin-top: 4px; }

/* The wall. Square tiles, because a grid of mixed aspect ratios reads as a
   mess and every one of these is somebody's photo of the same lake. */
.ph-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
  gap: 6px; margin-top: 2px;
}
.ph-tile { position: relative; }
.ph-tile-hit { display: block; width: 100%; }
.ph-img {
  display: block; width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: 10px; background: var(--panel-2);
  outline: 1px solid rgba(20, 16, 8, .11); outline-offset: -1px;
}
:root[data-theme="dark"] .ph-img,
:root:not([data-theme="light"]) .ph-img { outline-color: rgba(255, 255, 255, .22); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .ph-img { outline-color: rgba(20, 16, 8, .11); }
}
.ph-tile-hit:active .ph-img { transform: scale(.97); }
.ph-img, .ph-tile-hit:active .ph-img { transition: transform .1s ease; }

/* The opened photo. Full width, its own caption, credit always present. */
.ph-full { display: block; width: 100%; border-radius: var(--radius-sm);
  background: var(--panel-2); margin-bottom: 12px; }
.ph-cap { font-size: 1rem; line-height: 1.5; margin-bottom: 6px; }
.ph-meta { font-family: var(--mono); font-size: .62rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 4px; }
.ph-credit-line { font-size: .84rem; color: var(--ink-soft); margin-bottom: 12px; }
.ph-form-intro { font-size: .9rem; color: var(--ink-soft); line-height: 1.5; margin-bottom: 14px; }
.ph-form-note { font-size: .78rem; color: var(--ink-faint); line-height: 1.45; }
.ph-err { font-size: .84rem; color: var(--warn); margin-top: 8px; }
.m-form select, .m-form input[type=file] {
  display: block; width: 100%; margin-top: 5px; padding: 11px 12px;
  font: inherit; font-size: 1rem; color: var(--ink);
  background: var(--panel-2); border: 1px solid var(--edge);
  border-radius: var(--radius-sm); min-height: 44px;
}
.ph-spot .rail-dots, .ph-grid .rail-dots { background: var(--ground); }

/* Wikipedia's picture of the day. Ambient and openly not local, so it is
   small and it sits at the bottom — the house rule the webcams follow on
   Watch. Landscape, not square, so it never reads as one of ours. */
.ph-potd { display: flex; gap: 11px; align-items: flex-start; margin-top: 2px;
  padding-bottom: 4px; }
.ph-potd-hit { flex: 0 0 auto; width: 116px; }
.ph-potd-img { display: block; width: 100%; aspect-ratio: 4/3; object-fit: cover;
  border-radius: 9px; background: var(--panel-2);
  outline: 1px solid rgba(20, 16, 8, .11); outline-offset: -1px; }
:root[data-theme="dark"] .ph-potd-img,
:root:not([data-theme="light"]) .ph-potd-img { outline-color: rgba(255, 255, 255, .22); }
.ph-potd-meta { min-width: 0; }
.ph-potd-cap { display: block; font-size: .84rem; line-height: 1.4; color: var(--ink-soft);
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
.ph-potd-by { display: block; font-family: var(--mono); font-size: .58rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); margin-top: 4px; }

/* ============================================ the What Now centrepiece */
/* One tab is not like the others. What Now answers the question the whole
   paper exists to answer — what should I do, right now — so it sits dead
   centre of the nine and wears its own colour, warm red against the app's
   amber. It keeps its label at every width while the others go icon-only,
   because a button nobody can name is not a centrepiece.

   The red is defined on :root and redefined in BOTH dark blocks. The topic
   hues in this file are only defined in one of the two, which is why they
   stay light-mode values under the dark toggle — a bug worth not copying. */
:root {
  --now: #C0362C;
  --now-soft: rgba(192, 54, 44, .13);
  --now-ink: #FFFFFF;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --now: #F0705E;
    --now-soft: rgba(240, 112, 94, .18);
    --now-ink: #1A0F0D;
  }
}
:root[data-theme="dark"] {
  --now: #F0705E;
  --now-soft: rgba(240, 112, 94, .18);
  --now-ink: #1A0F0D;
}

.tabbar #tab-whatnow { color: var(--now); }
.tabbar #tab-whatnow svg { stroke-width: 2; }
/* The filled disc is the centrepiece: it reads as a button on a bar of
   glyphs. It stays filled whether or not the tab is active — active adds the
   soft tint behind it, the same way every other pill signals itself. */
.tabbar #tab-whatnow svg {
  background: var(--now); color: var(--now-ink);
  border-radius: 50%; padding: 4px; width: 25px; height: 25px;
  /* border-box, or the disc's padding makes this button taller than the other
     eight, which grows the whole grid row and eats the panel's bottom padding
     — at 430px that clearance was down to 1.9px, so the last event in the
     list sat under the dock. */
  box-sizing: border-box;
  /* No glow. The colour alone is enough to make it the centre — a drop shadow
     on a 25px disc reads as a notification badge, not a button. */
}
.tabbar #tab-whatnow.on { color: var(--now); background: var(--now-soft); }
.tabbar #tab-whatnow.on svg { stroke-width: 2.2; }
.tabbar #tab-whatnow > span { font-weight: 700; letter-spacing: -.01em;
  white-space: nowrap; padding: 0 5px; }

/* ============================================================ what now */
/* The sky is the point. The standalone app painted the whole page with a
   gradient built from the real sun times, and that is what made it feel like
   Burlington rather than a list — so it came across with the engine.

   It is scoped to THIS PANEL and nothing else. All Day is a light app with
   eight other tabs, and a night sky leaking onto the wire would be a bug, not
   a mood. The panel paints itself and sets its own ink; every colour below
   comes from that local set rather than the app's tokens, which is why this
   block does not follow the usual light/dark pattern — the sky IS the theme
   here, and it is the same at 2pm in either mode. */
/* NO `position: relative` HERE. #panel-whatnow is itself a .panel — which is
   already `position: absolute; inset: 0`, so it is already a containing block
   for the sky. An id outranks a class, so adding relative here beat that: the
   panel stopped being pinned to the shell and grew to its own 7,700px of
   content, leaving clientHeight === scrollHeight. It could not scroll at all.
   A desktop browser hid it by scrolling the page instead; on iOS, where the
   shell is fixed at 100dvh, there is nothing to fall back to — the tab simply
   would not move on iPad, and dragged a 7,700px layer on iPhone. */
.wn-sky {
  position: absolute; left: 0; right: 0; top: 0; z-index: 0;
  /* height is set in JS from the last sky-lit element — see whatnow.js */
  height: 420px; pointer-events: none;
  /* The fade used to start at 68% of that height, so everything in the bottom
     third — the spin buttons, the pool line, the path chips — sat in sky-lit
     ink on a near-white wash. JS sizes the sky as (last lit element + 90px),
     so holding the colour to calc(100% - 90px) puts the whole fade in that
     tail and every lit element keeps a real ground under it. */
  background: linear-gradient(180deg, var(--sky-top) 0%, var(--sky-mid) 46%,
    var(--sky-low) calc(100% - 90px), transparent 100%);
}
#panel-whatnow > *:not(.wn-sky) { position: relative; z-index: 1; }

/* THE INK FOLLOWS THE SKY.
   Every phase used to run a dark top into a pale bottom and print white on all
   of it, which meant the lower half — "Nah, spin again", the pool line, the
   path chips — was white on #F5C173 at 1.5:1. A gradient that crosses from
   dark to light cannot carry one ink, so each phase now stays on one side of
   that line: night and dusk are dark skies with white ink, the four daylight
   phases are bright skies with dark ink. Nothing below is a bare colour — the
   glass, the pills and the shadows all come from the set, so a phase switches
   as a whole and cannot half-invert. Worst contrast measured: 8.5:1. */
#panel-whatnow {
  --sky-top: #93BAD6; --sky-mid: #A9C7DE; --sky-low: #C6DCEA;
  /* dark ink, for a bright sky */
  --sky-ink: #1A1206;
  --sky-soft: rgba(26,18,6,.8);
  --sky-faint: rgba(26,18,6,.62);
  --sky-glass: rgba(255,255,255,.3);
  --sky-glass-line: rgba(255,255,255,.5);
  --sky-pill: rgba(255,255,255,.34);
  --sky-pill-line: rgba(255,255,255,.55);
  --sky-solid: rgba(28,20,8,.9);
  --sky-solid-ink: #FFF6E8;
  --sky-tshadow: none;
  --sky-cardshadow: 0 10px 26px rgba(26,40,60,.16);
}
#panel-whatnow[data-phase="dawn"]    { --sky-top:#F0A98E; --sky-mid:#EE9C82; --sky-low:#F7CDB2; }
#panel-whatnow[data-phase="morning"] { --sky-top:#9BBFD8; --sky-mid:#B0CBE0; --sky-low:#CBDFEC; }
#panel-whatnow[data-phase="day"]     { --sky-top:#93BAD6; --sky-mid:#A9C7DE; --sky-low:#C6DCEA; }
#panel-whatnow[data-phase="golden"]  { --sky-top:#E8A75C; --sky-mid:#F0BB73; --sky-low:#F8D9A6; }
/* the two dark phases keep the white ink the panel was designed around */
#panel-whatnow[data-phase="night"],
#panel-whatnow[data-phase="dusk"] {
  --sky-ink: #FFFFFF;
  --sky-soft: rgba(255,255,255,.82);
  --sky-faint: rgba(255,255,255,.68);
  --sky-glass: rgba(255,255,255,.14);
  --sky-glass-line: rgba(255,255,255,.22);
  --sky-pill: rgba(255,255,255,.16);
  --sky-pill-line: rgba(255,255,255,.24);
  --sky-solid: rgba(255,255,255,.92);
  --sky-solid-ink: #1A1206;
  --sky-tshadow: 0 1px 3px rgba(0,0,0,.35);
  --sky-cardshadow: 0 12px 34px rgba(6,10,20,.28);
}
#panel-whatnow[data-phase="night"] { --sky-top:#05070D; --sky-mid:#0A1226; --sky-low:#14213A; }
#panel-whatnow[data-phase="dusk"]  { --sky-top:#232B55; --sky-mid:#4A3560; --sky-low:#6E3A43; }

.wn-strip {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--sky-soft); margin: 2px 2px 12px;
  text-shadow: var(--sky-tshadow);
}

/* window picker — three words, not a segmented control; it is a sentence */
.wn-modes { display: flex; gap: 7px; margin-bottom: 14px; }
.wn-mode {
  flex: 1; padding: 9px 8px; min-height: 40px; border-radius: var(--pill);
  font-size: .88rem; font-weight: 600; color: var(--sky-soft);
  background: var(--sky-glass); border: 1px solid var(--sky-glass-line);
}
.wn-mode.on { color: var(--sky-solid-ink); background: var(--sky-solid); border-color: transparent; }

/* the answer — a glass card on the sky */
.wn-answer {
  padding: 20px 19px 18px; border-radius: var(--radius-lg); margin-bottom: 12px;
  background: var(--sky-glass); border: 1px solid var(--sky-glass-line);
  /* No backdrop-filter. The only thing behind these is the sky, and the sky is
     a linear gradient — blurring a linear gradient returns the same linear
     gradient, so the four filters here cost a full-frame backdrop repaint on
     every scroll tick and bought nothing visible. */
  box-shadow: var(--sky-cardshadow);
  transition: opacity .18s ease, transform .18s ease;
}
.wn-answer.is-spinning { opacity: .35; transform: scale(.985); }
.wn-kicker {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .11em;
  text-transform: uppercase; color: var(--sky-faint); margin-bottom: 7px;
}
.wn-title {
  font-family: var(--serif); font-size: 2rem; line-height: 1.08; font-weight: 400;
  color: var(--sky-ink); letter-spacing: -.008em; text-wrap: balance;
  text-shadow: var(--sky-tshadow);
}
.wn-meta { font-size: .95rem; color: var(--sky-soft); margin-top: 7px; line-height: 1.4; }
.wn-blurb { font-size: .92rem; color: var(--sky-soft); margin-top: 9px; line-height: 1.5; }
.wn-go {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 13px;
  padding: 10px 17px; border-radius: var(--pill); min-height: 42px;
  background: var(--sky-solid); color: var(--sky-solid-ink); font-weight: 600; font-size: .92rem;
}
.wn-go svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }

.wn-acts { display: flex; gap: 9px; margin-bottom: 9px; }
.wn-again, .wn-going {
  flex: 1; min-height: 52px; border-radius: var(--pill); font-weight: 700;
  /* the global reset is `button { text-align: left }`, so these two have to
     say otherwise — they are labels on a control, not lines of copy */
  text-align: center;
  display: flex; align-items: center; justify-content: center;
  padding: 8px 12px; line-height: 1.18;
  font-size: .95rem; letter-spacing: -.005em;
}
/* "Whoa, that's interesting" is a sentence, not a word — let it take two
   lines on a narrow phone rather than shrink below reading size. */
@media (max-width: 380px) { .wn-again, .wn-going { font-size: .87rem; } }
.wn-again { color: var(--sky-ink); background: var(--sky-pill);
  border: 1px solid var(--sky-pill-line); }
/* white on #C0362C is 5.3:1 — the one control that reads the same on any sky */
.wn-going { color: #fff; background: var(--now); box-shadow: 0 4px 14px rgba(192,54,44,.4); }
.wn-pool {
  font-family: var(--mono); font-size: .6rem; letter-spacing: .07em;
  color: var(--sky-faint); margin: 0 2px 14px; line-height: 1.5;
}
.wn-paths { margin-bottom: 4px; }
/* the chips sit on the sky, so they cannot use the app's panel colours */
.wn-paths .chip { background: var(--sky-glass); border-color: var(--sky-glass-line);
  color: var(--sky-soft); }
.wn-paths .chip.on { background: var(--sky-solid); color: var(--sky-solid-ink); border-color: transparent; }

/* below the fold the sky has faded out and the app's own colours resume */
.wn-list { border-top: 1px solid var(--edge-soft); }
.wn-row { display: flex; align-items: baseline; gap: 11px; padding: 10px 2px;
  border-bottom: 1px solid var(--edge-soft); text-align: left; width: 100%; }
.wn-row-time { flex: 0 0 62px; font-family: var(--mono); font-size: .64rem;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-faint);
  font-variant-numeric: tabular-nums; }
.wn-row-body { min-width: 0; flex: 1; }
.wn-row-t { display: block; font-size: .97rem; font-weight: 500; line-height: 1.3; }
.wn-row-v { display: block; font-size: .8rem; color: var(--ink-faint); margin-top: 2px; }
.wn-day { margin: 18px 0 2px; font-family: var(--mono); font-size: .64rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); }

.wn-doors { display: grid; gap: 9px; margin: 18px 0 6px; }
.wn-door { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 15px; border-radius: var(--radius); background: var(--panel-2);
  border: 1px solid var(--edge); }
.wn-door-t { display: block; font-size: 1rem; font-weight: 600; }
.wn-door-s { display: block; font-size: .82rem; color: var(--ink-faint); margin-top: 2px; }
.wn-door svg { width: 20px; height: 20px; flex: 0 0 auto; fill: none;
  stroke: currentColor; stroke-width: 1.8; color: var(--ink-faint); }

@media (prefers-reduced-motion: reduce) { .wn-answer { transition: none; } }

/* ========================================================== instagram */
/* A grid in posted order. No ranking, no engagement count, nothing that
   would make this a second algorithm — which is the entire reason it exists. */
.ig-note { font-size: .8rem; color: var(--ink-faint); line-height: 1.5; margin: 0 2px 12px; }
/* The caption is the announcement — a spot, a time, a lineup — so it sits on
   the card next to the picture rather than behind a tap. Picture left at a
   fixed square, text right and clamped to four lines. */
.ig-feed { display: grid; gap: 2px; border-top: 1px solid var(--edge-soft); }
.ig-post { border-bottom: 1px solid var(--edge-soft); }
.ig-hit { display: flex; gap: 12px; align-items: flex-start; width: 100%;
  text-align: left; padding: 11px 2px; }
.ig-shot { position: relative; flex: 0 0 auto; width: 96px; }
@media (max-width: 359px) {
  .ig-shot, .ig-img { width: 78px; }
  .ig-img { height: 78px; }
  .ig-hit { gap: 9px; }
}
.ig-img { display: block; width: 96px; height: 96px; object-fit: cover;
  border-radius: 10px; background: var(--panel-2);
  outline: 1px solid rgba(20, 16, 8, .11); outline-offset: -1px; }
:root[data-theme="dark"] .ig-img,
:root:not([data-theme="light"]) .ig-img { outline-color: rgba(255, 255, 255, .2); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .ig-img { outline-color: rgba(20, 16, 8, .11); }
}
/* when the signed URL has aged out, the row becomes text and closes the gap */
.ig-post.no-art .ig-shot { display: none; }
.ig-vid { position: absolute; top: 6px; right: 6px; width: 17px; height: 17px;
  border-radius: 50%; background: rgba(0,0,0,.55); display: grid; place-items: center; }
.ig-vid::after { content: ""; border-left: 5px solid #fff;
  border-top: 3.5px solid transparent; border-bottom: 3.5px solid transparent; margin-left: 1px; }
.ig-body { min-width: 0; flex: 1; display: block; }
.ig-who { display: flex; align-items: baseline; gap: 7px; font-family: var(--mono);
  font-size: .6rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent); font-weight: 600; }
.ig-when { color: var(--ink-faint); font-weight: 400; letter-spacing: .06em; }
.ig-text { display: -webkit-box; margin-top: 5px; font-size: .89rem; line-height: 1.42;
  color: var(--ink); -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
  white-space: pre-wrap;
  /* Captions carry bare URLs and 30-character hashtags. Without this an
     unbreakable token sets a minimum width the row cannot shrink under, and
     the whole panel scrolls sideways at 320px — measured, not theoretical. */
  overflow-wrap: anywhere; }
.ig-nocap { color: var(--ink-faint); font-style: italic; }
.ig-full { display: block; width: 100%; border-radius: var(--radius-sm);
  background: var(--panel-2); margin-bottom: 12px; }
.ig-cap { font-size: .95rem; line-height: 1.55; white-space: pre-wrap; margin-bottom: 12px; }
.ig-handle { font-family: var(--mono); font-size: .68rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 8px; }

/* ------------------------------------------------- the Brief's episodes */
/* Spotify's show embed plays the newest episode at any height, so the archive
   is a list here and each row opens that episode's own embed. */
.l-eps { margin-bottom: 18px; }
.l-ep-list { border-top: 1px solid var(--edge-soft); }
.l-ep { border-bottom: 1px solid var(--edge-soft); }
.l-ep-hit { display: block; width: 100%; text-align: left; padding: 11px 2px; }
.l-ep-t { display: block; font-size: .97rem; font-weight: 600; line-height: 1.3; }
.l-ep-m { display: block; font-family: var(--mono); font-size: .6rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint); margin-top: 3px;
  font-variant-numeric: tabular-nums; }
.l-ep-b { display: -webkit-box; margin-top: 5px; font-size: .85rem; line-height: 1.42;
  color: var(--ink-soft); -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere; }
.l-ep.is-open .l-ep-t { color: var(--accent); }
.l-ep-embed { width: 100%; height: 152px; border: 0; border-radius: var(--radius-sm);
  margin: 4px 0 10px; background: var(--panel-2); }
/* The wall before anyone has filled it: an invitation, not a blank. */
.m-mix-empty { font-size: .95rem; line-height: 1.55; color: var(--ink-soft);
  padding: 14px 15px; border-radius: var(--radius); background: var(--panel-2);
  border: 1px dashed var(--edge); }
.m-plerr { color: var(--warn); }

/* Thirty-one account chips is a long sideways push. Opened out they become a
   plain wrapped list — the same escape the rails offer. */
.ig-accts.is-open .chips {
  display: flex; flex-wrap: wrap; overflow: visible; gap: 7px; padding-bottom: 4px;
}
.ig-accts.is-open .chips .chip { flex: 0 0 auto; }
.ig-accts-btn {
  display: block; margin: 2px auto 14px; font-family: var(--mono); font-size: .6rem;
  font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint); border: 1px solid var(--edge); border-radius: var(--pill);
  padding: 5px 12px; background: var(--panel); min-height: 30px;
}
.ig-accts-btn:hover { color: var(--accent); border-color: var(--accent-soft); }

/* ------------------------------------------- the SEE wall (Instagram half two)

   Its own class family rather than a reuse of Photos' .ph-grid. The layout is
   nearly the same and the temptation is real, but Photos is a submission queue
   with credits and a sunset score, and coupling the two means every change to
   one has to be checked against the other. These share the tokens, not the
   selectors.

   Tiles are bigger than Photos'. That wall is thumbnails of one lake from one
   angle, where density is the point; this one is 250-odd accounts making
   different things, and at 108px a creemee and a DJ set look identical. */
.igw-wall {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 7px; margin-top: 2px;
}
@media (max-width: 380px) {
  .igw-wall { grid-template-columns: repeat(2, 1fr); gap: 6px; }
}
.igw-tile { position: relative; }
.igw-hit {
  display: block; width: 100%; border-radius: 11px;
  overflow: hidden; background: var(--panel-2); text-align: left;
}
/* The picture's own box: the handle scrim and the play badge anchor here,
   so a caption below never pushes them off the image. */
.igw-shot { position: relative; display: block; }
.igw-img {
  display: block; width: 100%; aspect-ratio: 1; object-fit: cover;
  background: var(--panel-2);
  outline: 1px solid rgba(20, 16, 8, .11); outline-offset: -1px;
}
:root[data-theme="dark"] .igw-img,
:root:not([data-theme="light"]) .igw-img { outline-color: rgba(255, 255, 255, .22); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .igw-img { outline-color: rgba(20, 16, 8, .11); }
}
.igw-hit:active .igw-img { transform: scale(.97); }

/* Two clamped lines of the caption, quiet. Rows share the height of their
   tallest card, so a short caption leaves a sliver of panel below it —
   cheaper to accept than a masonry layout, and barely visible at this size. */
.igw-cap {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; padding: 6px 9px 0; margin-bottom: 8px;
  font-size: .72rem; line-height: 1.4; color: var(--ink-soft);
  overflow-wrap: break-word;
}
.igw-img { transition: transform .1s ease; }

/* The handle rides on the picture. A scrim rather than a bar: half these
   photographs are a bright sky and half are a dark room, and one flat colour
   loses the text against one of them. Fixed white on the scrim for the same
   reason — the ink token flips with the theme, the photograph underneath
   does not. */
.igw-foot {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: baseline; gap: 6px;
  padding: 16px 8px 6px;
  background: linear-gradient(to top, rgba(0, 0, 0, .72), rgba(0, 0, 0, 0));
  pointer-events: none;
}
.igw-who {
  font-size: .68rem; font-weight: 600; color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.igw-when {
  flex: 0 0 auto; font-family: var(--mono); font-size: .54rem; letter-spacing: .06em;
  color: rgba(255, 255, 255, .78); text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
}
/* Same play triangle DO uses, moved to the corner a thumbnail wants it in. */
.igw-vid {
  position: absolute; top: 7px; right: 7px; width: 20px; height: 20px;
  border-radius: 50%; background: rgba(0, 0, 0, .55); pointer-events: none;
}
.igw-vid::after {
  content: ""; position: absolute; top: 5px; left: 7px;
  border-left: 7px solid #fff; border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
}

/* The games pointer on What Now. It is a door to sports.html, not a tab. */
.wn-sport { margin: 16px 0 4px; }
.wn-sport-hit { display: block; padding: 14px 15px; border-radius: var(--radius);
  background: var(--panel-2); border: 1px solid var(--edge); }
.wn-sport-k { display: block; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .11em; text-transform: uppercase; color: var(--accent);
  font-weight: 600; margin-bottom: 7px; }
.wn-sport-g { display: block; font-size: .92rem; line-height: 1.4; color: var(--ink-soft);
  margin-top: 3px; }
.wn-sport-g b { color: var(--ink); font-variant-numeric: tabular-nums; }
.wn-sport-g i { color: var(--ink-faint); font-style: normal; }
.wn-sport-more { display: inline-flex; align-items: center; gap: 5px; margin-top: 9px;
  font-size: .84rem; font-weight: 600; color: var(--accent); }
.wn-sport-more svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }

/* The arcade board sits directly under sports and borrows its whole card, so
   the two read as one pair of pointers rather than two unrelated boxes. Three
   rows, and the name allowed to run out with an ellipsis — the count is the
   part that has to stay readable. */
.wn-arcade { margin: 10px 0 4px; }
.wn-arcade-hit { display: block; padding: 14px 15px; border-radius: var(--radius);
  background: var(--panel-2); border: 1px solid var(--edge); }
.wn-arcade-k { display: block; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .11em; text-transform: uppercase; color: var(--accent);
  font-weight: 600; margin-bottom: 7px; }
.wn-arcade-g { display: block; font-size: .92rem; line-height: 1.4; color: var(--ink-soft); }
.wn-arcade-g b { color: var(--ink); }
.wn-arcade-rows { display: block; margin-top: 8px; }
.wn-arcade-r { display: flex; align-items: baseline; gap: 8px;
  font-size: .88rem; line-height: 1.9; color: var(--ink-soft); }
.wn-arcade-r i { font-style: normal; flex: 0 0 auto; }
.wn-arcade-r em { font-style: normal; color: var(--ink); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wn-arcade-r b { margin-left: auto; flex: 0 0 auto; color: var(--ink-soft);
  font-size: .8rem; font-weight: 500; font-variant-numeric: tabular-nums; }
.wn-arcade-more { display: inline-flex; align-items: center; gap: 5px; margin-top: 9px;
  font-size: .84rem; font-weight: 600; color: var(--accent); }
.wn-arcade-more svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }
