Files
esh-pfi-infrastructure/stacks/news-digest/templates/style.css
T
vh aeb5c18ca3 news-digest: multi-tenant deploy + masthead overlap fix
Two pieces:

1) Multi-tenant onboarding via scripts/add-digest-user.sh

   Shared miniflux + per-user digest stack. Onboarding a teammate
   takes one command (plus a one-time sudo for dir creation):

     scripts/add-digest-user.sh <username>

   What the script does:
     - Reads miniflux admin creds from ana-docker
     - Allocates next free port (scans existing digest-*/.env)
     - Generates a random password (or accepts one as 2nd arg)
     - Creates the miniflux user via the admin API
     - Materializes a per-user .env at /opt/docker/compose/digest-<user>/
       (inherits NEWS_DIGEST_TAG from the canonical stack so all
       tenants run the same image)
     - Brings up `docker compose -p digest-<user> up -d`
     - Seeds default world/local feeds in the new user's miniflux
     - Triggers a first digest run

   compose.yaml now uses ${DIGEST_PROJECT:-news-digest} to namespace
   container_name + homepage labels. Default keeps backward-compat
   for the singleton install — existing stacks unaffected.

2) Masthead overlap on phone widths

   Desktop CSS pinned .masthead-edition to grid-row 1, which collided
   with .masthead-brand once the mobile media query collapsed both
   to grid-column 1. Result: "MORNING EDITION" badge stacked on top
   of the "DAILY DIGEST" hero. Reset grid-row to `auto` for all
   three masthead children in the ≤720 px breakpoint so they
   auto-flow vertically.
2026-04-28 13:53:26 -07:00

1047 lines
27 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* -------------------------------------------------------------------------
news-digest — daily intel briefing.
Aesthetic: operations-center chrome (Australis cool-mono palette,
JetBrains Mono UPPERCASE eyebrows, mdi-glyph anchor) wrapping
editorial-serif news content (Fraunces). Two type families that
wouldn't normally meet, intentionally combined: the chrome says
"filed at 0800 from the bridge"; the headlines say "this is news,
read it like news."
Type:
Display (headlines): Fraunces — variable serif w/ optical sizes
Body (summaries): Inter — same as Australis, optimized for screens
Mono (chrome): JetBrains Mono — eyebrows / metadata / chips
Color: pure Australis Ice + Sea + Aurora. Score chips lean
--aus-blue (info); release tags --aus-green; drama --aus-red
(sparingly). No warm accents — Australis is cool-only by design.
------------------------------------------------------------------------- */
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700;9..144,800&display=swap');
:root {
/* Australis palette — vendored subset */
--aus-black: #222531;
--aus-white: #a9bcc3;
--aus-bright-white: #cce7ec;
--aus-bright-black: #373b46;
--aus-dark-30: #414751;
--aus-dark-40: #565f69;
--aus-dark-50: #6e7882;
--aus-dark-60: #86929d;
--aus-bright-70: #9daeb6;
--aus-bright-80: #b3cbcf;
--aus-blue: #6388d8;
--aus-bright-blue: #a4c4ff;
--aus-cyan: #00b1a8;
--aus-bright-cyan: #42dcd1;
--aus-green: #16b866;
--aus-bright-green: #51e08a;
--aus-yellow: #e1c631;
--aus-bright-yellow:#ffe14e;
--aus-red: #ff491a;
--aus-magenta: #9d78ff;
--bg: var(--aus-black);
--bg-elev: var(--aus-bright-black);
--surface: #2a2e3a;
--surface-hi: var(--aus-dark-30);
--rule: var(--aus-dark-30);
--rule-bright: var(--aus-dark-40);
--fg: var(--aus-bright-white);
--fg-dim: var(--aus-bright-70);
--fg-faint: var(--aus-dark-50);
--accent: var(--aus-bright-cyan);
--accent-dim: var(--aus-cyan);
/* Type families */
--font-display: "Fraunces", "Times New Roman", Georgia, serif;
--font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
/* Scale + chrome */
--pad-x: 24px;
--content-w: 880px;
}
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body {
margin: 0;
background: var(--bg);
color: var(--fg);
font: 16px/1.55 var(--font-sans);
font-feature-settings: "ss01" 1;
-webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent); }
/* -------------------------------------------------------------------------
masthead — full-width banner; the "I am a news edition" statement
------------------------------------------------------------------------- */
.masthead {
border-bottom: 1px solid var(--rule);
background: var(--bg-elev);
}
.masthead-inner {
max-width: var(--content-w);
margin: 0 auto;
padding: 36px var(--pad-x) 24px;
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: auto auto;
column-gap: 32px;
row-gap: 16px;
align-items: end;
}
.masthead-brand {
grid-column: 1 / 2;
grid-row: 1 / 2;
display: flex;
align-items: baseline;
gap: 10px;
font-family: var(--font-mono);
/* Brand wraps an <a href="index.html"> so the hero is clickable to
the latest edition from any archived page. Strip default link
styling so the visual is unchanged. */
color: inherit;
text-decoration: none;
transition: opacity 0.15s ease;
}
.masthead-brand:hover {
opacity: 0.85;
}
.masthead-brand:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 4px;
border-radius: 2px;
}
.brand-glyph {
font-size: 32px;
line-height: 1;
color: var(--accent);
text-shadow: 0 0 12px rgba(66, 220, 209, .35);
}
.brand-mark {
font-size: 14px;
font-weight: 700;
letter-spacing: .18em;
color: var(--fg);
}
.masthead-edition {
grid-column: 2 / 3;
grid-row: 1 / 2;
display: flex;
align-items: baseline;
justify-content: flex-end;
gap: 18px;
font-family: var(--font-mono);
}
.archive-link {
color: var(--fg-dim);
font-size: 11px;
letter-spacing: .14em;
border-bottom: 1px dotted var(--fg-faint);
padding-bottom: 1px;
}
.archive-link:hover { color: var(--accent); border-color: var(--accent-dim); }
.edition-num {
color: var(--fg-faint);
font-size: 11px;
letter-spacing: .14em;
}
.edition-stamp {
/* THE statement piece — large mono in Australis yellow.
"This is the morning/evening brief, filed officially." */
font-family: var(--font-mono);
font-size: 22px;
font-weight: 700;
letter-spacing: .14em;
color: var(--aus-yellow);
padding: 4px 10px;
border: 1px solid var(--aus-yellow);
border-radius: 2px;
}
.masthead-meta {
grid-column: 1 / 3;
grid-row: 2 / 3;
display: grid;
grid-template-columns: repeat(3, auto);
gap: 4px 32px;
align-items: baseline;
font-family: var(--font-mono);
padding-top: 16px;
border-top: 1px solid var(--rule);
}
.meta-row {
display: flex;
align-items: baseline;
gap: 10px;
font-size: 11px;
letter-spacing: .08em;
}
.meta-label {
color: var(--fg-faint);
text-transform: uppercase;
letter-spacing: .14em;
}
.meta-value {
color: var(--fg);
font-variant-numeric: tabular-nums;
}
/* The aurora glow rule — sanctioned Australis gradient, used once. */
.aurora-rule {
height: 2px;
background: linear-gradient(90deg,
transparent 0%,
var(--aus-bright-blue) 25%,
var(--aus-bright-cyan) 50%,
var(--aus-bright-green) 75%,
transparent 100%);
opacity: .65;
}
/* -------------------------------------------------------------------------
jump nav
------------------------------------------------------------------------- */
.jumpnav {
position: sticky;
top: 0;
z-index: 5;
background: rgba(34, 37, 49, 0.92);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-bottom: 1px solid var(--rule);
}
.jumpnav ol {
list-style: none;
margin: 0;
padding: 12px var(--pad-x);
max-width: var(--content-w);
margin-inline: auto;
display: flex;
gap: 28px;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: .14em;
text-transform: uppercase;
}
.jumpnav a {
color: var(--fg-dim);
display: inline-flex;
align-items: baseline;
gap: 8px;
}
.jumpnav a:hover { color: var(--fg); }
.jump-num { color: var(--fg-faint); font-size: 10px; }
/* -------------------------------------------------------------------------
main brief
------------------------------------------------------------------------- */
.brief {
max-width: var(--content-w);
margin: 0 auto;
padding: 32px var(--pad-x) 64px;
}
.desk + .desk { margin-top: 64px; }
.desk-head {
display: grid;
/* num | title | count | collapse — explicit columns so the
collapse button doesn't auto-flow into the desk-sub row. */
grid-template-columns: auto 1fr auto auto;
align-items: baseline;
column-gap: 16px;
padding-bottom: 14px;
margin-bottom: 24px;
border-bottom: 1px solid var(--rule-bright);
}
.desk-num {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 700;
letter-spacing: .14em;
color: var(--aus-yellow);
padding-top: 4px; /* aligns visually with the serif baseline */
}
.desk-title {
font-family: var(--font-display);
font-size: 38px;
font-weight: 700;
letter-spacing: -0.015em;
margin: 0;
color: var(--fg);
font-variation-settings: "opsz" 144;
}
.desk-sub {
/* Sub-line hugs the title baseline */
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: .08em;
color: var(--fg-faint);
text-transform: uppercase;
grid-column: 2 / 3;
align-self: end;
padding-bottom: 6px;
padding-left: 12px;
}
.desk-count {
font-family: var(--font-mono);
font-size: 11px;
font-variant-numeric: tabular-nums;
letter-spacing: .08em;
color: var(--fg-dim);
text-transform: uppercase;
}
/* Per-desk collapse toggle. Sits on the right edge of the desk header,
clicking collapses the body to a single-line summary. State is
persisted in localStorage by section id (#world, #local, etc), so
each device remembers per-user preference. */
.desk-collapse {
grid-column: 4 / 5;
grid-row: 1 / 2;
align-self: center;
background: transparent;
border: 1px solid transparent;
border-radius: 4px;
padding: 4px 8px;
font-family: var(--font-mono);
font-size: 14px;
line-height: 1;
color: var(--fg-dim);
cursor: pointer;
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease, transform 0.18s ease;
}
.desk-collapse:hover,
.desk-collapse:focus-visible {
color: var(--accent);
border-color: var(--rule-bright);
background: var(--bg-elev);
outline: none;
}
.desk.is-collapsed .desk-collapse {
transform: rotate(-90deg);
}
/* When collapsed, hide everything inside the desk EXCEPT the header.
The .desk-head is direct child; .source / .headlines-list etc are
the rest of the body. */
.desk.is-collapsed > :not(.desk-head) {
display: none;
}
/* Tighten the header bottom-border in collapsed state — no items below
means the heavy underline looks weird floating alone. */
.desk.is-collapsed .desk-head {
margin-bottom: 0;
padding-bottom: 8px;
border-bottom: 1px dashed var(--rule);
}
/* -------------------------------------------------------------------------
per-source block
------------------------------------------------------------------------- */
.source + .source { margin-top: 36px; }
.source-head {
display: flex;
align-items: baseline;
gap: 14px;
margin-bottom: 14px;
}
.source-name {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 700;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--accent);
border-bottom: 1px dashed var(--accent-dim);
padding-bottom: 1px;
}
.source-name:hover { color: var(--aus-bright-cyan); }
.source-count {
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: .14em;
color: var(--fg-faint);
font-variant-numeric: tabular-nums;
}
/* -------------------------------------------------------------------------
item — the news card
------------------------------------------------------------------------- */
.items {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 24px;
}
.item {
display: grid;
grid-template-columns: 96px 1fr;
column-gap: 24px;
padding-bottom: 24px;
border-bottom: 1px solid var(--rule);
}
.item:last-child {
border-bottom: 0;
padding-bottom: 0;
}
.item-rail {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 6px;
padding-top: 6px;
}
.chip {
font-family: var(--font-mono);
font-size: 10.5px;
font-weight: 600;
letter-spacing: .06em;
padding: 2px 8px;
border: 1px solid currentColor;
border-radius: 2px;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.chip-score { color: var(--aus-blue); }
.chip-comments { color: var(--fg-dim); border-color: var(--rule-bright); }
.chip-tag {
font-size: 9.5px;
letter-spacing: .14em;
text-transform: uppercase;
}
.chip-tag-news { color: var(--aus-bright-blue); }
.chip-tag-tutorial { color: var(--aus-bright-cyan); }
.chip-tag-release { color: var(--aus-bright-green); }
.chip-tag-discussion{ color: var(--fg-dim); }
.chip-tag-question { color: var(--aus-yellow); }
.chip-tag-showcase { color: var(--aus-magenta); }
.chip-tag-drama { color: var(--aus-red); }
.chip-tag-meme { color: var(--fg-faint); }
.item-body {
min-width: 0;
}
.item-title {
margin: 0 0 8px;
font-family: var(--font-display);
font-size: 22px;
line-height: 1.2;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--fg);
font-variation-settings: "opsz" 36;
}
.item-title a { color: inherit; }
.item-title a:hover { color: var(--accent); }
.item-tldr {
margin: 0 0 12px;
font-size: 15px;
line-height: 1.55;
color: var(--fg-dim);
}
.item-foot {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 16px;
font-family: var(--font-mono);
font-size: 10.5px;
letter-spacing: .06em;
color: var(--fg-faint);
}
/* Action chips — explicit "go here" buttons. Reddit items get TWO of
these when the post links externally: SOURCE (the linked content)
and REDDIT THREAD (the discussion). Non-Reddit items get just SOURCE. */
.item-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.action {
display: inline-flex;
align-items: baseline;
gap: 6px;
padding: 4px 10px;
border: 1px solid var(--rule-bright);
border-radius: 2px;
font-family: var(--font-mono);
font-size: 10.5px;
font-weight: 600;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--fg-dim);
transition: border-color 120ms, color 120ms, background 120ms;
}
.action-arrow {
color: var(--accent-dim);
font-size: 11px;
}
.action-label { color: var(--fg); }
.action-domain {
color: var(--fg-dim);
font-weight: 500;
letter-spacing: .04em;
text-transform: lowercase;
}
.action:hover {
border-color: var(--accent);
background: rgba(66, 220, 209, 0.08);
}
.action:hover .action-arrow,
.action:hover .action-label,
.action:hover .action-domain { color: var(--accent); }
.action-source { /* default styling above is "source" */ }
.action-thread .action-arrow { color: var(--aus-bright-blue); }
.action-thread:hover { border-color: var(--aus-bright-blue); background: rgba(164, 196, 255, 0.08); }
.action-thread:hover .action-arrow,
.action-thread:hover .action-label { color: var(--aus-bright-blue); }
.item-meta-row {
display: flex;
align-items: baseline;
gap: 8px;
margin-left: auto; /* pushes timestamp to the right */
}
.item-meta { font-variant-numeric: tabular-nums; color: var(--fg-faint); }
.sep { color: var(--rule-bright); }
/* Visual de-emphasize purely meme/skip items if any slip through. */
.item[data-tag="meme"] { opacity: .58; }
.item[data-tag="other"] { opacity: .85; }
/* -------------------------------------------------------------------------
headlines — dense list (world / local).
Different rhythm from .item cards: one row per story, source on the
right, title-led. Volume-driven sections where the deliverable is
"what happened" not "what someone said about what happened".
------------------------------------------------------------------------- */
.headlines-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0;
}
.headline {
position: relative; /* anchors the absolute-positioned × button */
display: grid;
grid-template-columns: 1fr auto auto;
grid-template-rows: auto auto;
align-items: baseline;
column-gap: 16px;
row-gap: 4px;
padding: 14px 0;
border-bottom: 1px solid var(--rule);
}
.headline-title { grid-column: 1 / 2; grid-row: 1; }
.headline-source { grid-column: 2 / 3; grid-row: 1; }
.headline-time { grid-column: 3 / 4; grid-row: 1; }
.headline-tldr {
grid-column: 1 / 4;
grid-row: 2;
margin: 4px 0 2px;
font-family: var(--font-sans);
font-size: 14px;
line-height: 1.5;
color: var(--fg-dim);
}
.headline:last-child {
border-bottom: 0;
}
.headline-title {
margin: 0;
font-family: var(--font-display);
font-size: 17px;
line-height: 1.35;
font-weight: 500;
letter-spacing: -0.005em;
color: var(--fg);
font-variation-settings: "opsz" 24;
/* Title takes the leftmost column; allow it to wrap on narrow screens. */
min-width: 0;
}
.headline-title a { color: inherit; }
.headline-title a:hover { color: var(--accent); }
.headline-source {
font-family: var(--font-mono);
font-size: 10px;
font-weight: 600;
letter-spacing: .12em;
color: var(--fg-faint);
text-transform: uppercase;
white-space: nowrap;
}
.headline-time {
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--fg-faint);
font-variant-numeric: tabular-nums;
min-width: 32px;
text-align: right;
}
/* Hidden state — same mechanism as .item, applies to .headline too. */
.headline.is-hidden { display: none; }
/* -------------------------------------------------------------------------
archive page — list of all editions
------------------------------------------------------------------------- */
.archive-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0;
border: 1px solid var(--rule);
border-radius: 2px;
overflow: hidden;
}
.archive-item + .archive-item { border-top: 1px solid var(--rule); }
.archive-row {
display: grid;
grid-template-columns: 1fr auto auto;
align-items: center;
gap: 24px;
padding: 16px 20px;
background: var(--surface);
transition: background 120ms;
}
.archive-row:hover {
background: var(--surface-hi);
color: var(--fg);
}
.archive-date {
font-family: var(--font-display);
font-size: 17px;
font-weight: 500;
letter-spacing: -0.005em;
color: var(--fg);
font-variation-settings: "opsz" 36;
}
.archive-edition {
font-family: var(--font-mono);
font-size: 10.5px;
font-weight: 700;
letter-spacing: .14em;
padding: 3px 9px;
border: 1px solid currentColor;
border-radius: 2px;
}
.archive-edition-am { color: var(--aus-yellow); }
.archive-edition-pm { color: var(--aus-bright-cyan); }
.archive-arrow {
font-family: var(--font-mono);
color: var(--fg-faint);
font-size: 14px;
}
.archive-row:hover .archive-arrow { color: var(--accent); }
/* -------------------------------------------------------------------------
empty state + colophon
------------------------------------------------------------------------- */
.desk.empty {
text-align: center;
padding: 72px 24px;
border: 1px dashed var(--rule);
border-radius: 6px;
}
.empty-msg {
font-family: var(--font-mono);
font-size: 13px;
color: var(--fg-faint);
line-height: 1.6;
}
.empty-msg code {
color: var(--fg-dim);
font-family: inherit;
background: var(--surface);
padding: 2px 6px;
border-radius: 2px;
}
.colophon {
border-top: 1px solid var(--rule);
background: var(--bg-elev);
padding: 24px var(--pad-x);
margin-top: 64px;
}
.colophon-inner {
max-width: var(--content-w);
margin: 0 auto;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
font-family: var(--font-mono);
font-size: 10.5px;
letter-spacing: .08em;
}
.colophon-block {
display: flex;
flex-direction: column;
gap: 4px;
}
.colophon-block .meta-label { color: var(--fg-faint); font-size: 10px; }
.colophon-block .meta-value { color: var(--fg-dim); font-size: 11px; }
/* -------------------------------------------------------------------------
per-item × button + hidden tray
------------------------------------------------------------------------- */
/* The .item layout is a 2-column grid; the × button absolute-positions
over its top-right corner so it doesn't disturb the rail/body grid. */
.item {
position: relative;
}
.item-hide {
position: absolute;
top: -2px;
right: -4px;
width: 22px;
height: 22px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
background: transparent;
border: 1px solid transparent;
border-radius: 4px;
color: var(--fg-faint);
font-family: var(--font-sans);
font-size: 18px;
line-height: 1;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s ease, color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.item:hover .item-hide,
.item:focus-within .item-hide,
.item-hide:focus-visible {
opacity: 1;
}
.item-hide:hover,
.item-hide:focus-visible {
color: var(--aus-red);
border-color: var(--rule-bright);
background: var(--bg-elev);
outline: none;
}
/* Hidden state — display:none rather than animate, since we re-render
pre-paint from the API set on every page load. Keeping it simple. */
.item.is-hidden {
display: none;
}
/* When every item in a .source (or every source in a .desk) is hidden,
collapse the whole header out — no point rendering "r/homelab (0)".
app.js applies .is-empty when the visible-count drops to zero. */
.source.is-empty,
.desk.is-empty {
display: none;
}
.hidden-tray {
margin-top: 24px;
padding: 14px 18px;
background: var(--bg-elev);
border: 1px solid var(--rule);
border-radius: 6px;
}
.hidden-tray-head {
margin: 0;
}
.hidden-tray-toggle {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 4px 6px;
margin-left: -6px;
background: transparent;
border: none;
color: var(--fg-dim);
font-family: var(--font-sans);
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
cursor: pointer;
border-radius: 4px;
transition: color 0.15s ease, background 0.15s ease;
}
.hidden-tray-toggle:hover,
.hidden-tray-toggle:focus-visible {
color: var(--fg);
background: var(--surface);
outline: none;
}
.tray-glyph {
display: inline-block;
font-size: 11px;
transition: transform 0.18s ease;
}
.hidden-tray.is-expanded .tray-glyph {
transform: rotate(180deg);
}
.tray-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 22px;
padding: 0 6px;
background: var(--surface);
color: var(--fg);
border-radius: 10px;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0;
}
.hidden-tray-list {
margin: 12px 0 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 4px;
}
.hidden-tray-item {
display: grid;
grid-template-columns: 1fr auto;
align-items: baseline;
gap: 12px;
padding: 6px 8px;
border-radius: 4px;
transition: background 0.12s ease;
}
.hidden-tray-item:hover {
background: var(--surface);
}
.hidden-tray-title {
font-family: var(--font-display);
font-weight: 400;
font-size: 14px;
color: var(--fg-dim);
line-height: 1.35;
/* Truncate with ellipsis on long titles so the row stays one line. */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hidden-tray-restore {
padding: 2px 8px;
background: transparent;
border: 1px solid var(--rule-bright);
border-radius: 4px;
color: var(--fg-dim);
font-family: var(--font-sans);
font-size: 12px;
cursor: pointer;
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.hidden-tray-restore:hover,
.hidden-tray-restore:focus-visible {
color: var(--accent);
border-color: var(--accent);
background: var(--bg);
outline: none;
}
/* -------------------------------------------------------------------------
responsive — two breakpoints:
≤ 720 px : tablet / narrow desktop. Soften the layout but still
room for two-column-ish.
≤ 480 px : true phone. Strip the rail, hide section numbers, tighten
everything for a single-thumb scroll.
------------------------------------------------------------------------- */
@media (max-width: 720px) {
:root { --pad-x: 16px; }
.masthead-inner {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto;
padding: 24px var(--pad-x) 16px;
row-gap: 12px;
}
/* Desktop pins brand+edition both to grid-row 1, which collapses
them on top of each other once we go single-column. Reset row
placement so they auto-flow vertically. */
.masthead-brand,
.masthead-edition,
.masthead-meta {
grid-column: 1 / 2;
grid-row: auto;
}
.masthead-edition { justify-content: flex-start; flex-wrap: wrap; gap: 12px; }
.edition-stamp { font-size: 16px; padding: 3px 8px; }
.masthead-meta {
grid-template-columns: 1fr;
gap: 6px;
padding-top: 12px;
}
.meta-row { font-size: 10.5px; flex-wrap: wrap; }
.jumpnav ol { gap: 18px; padding: 10px var(--pad-x); }
/* Desk header: num / title row 1, sub spans below, count + collapse
pushed to row 1 right-side. */
.desk-head {
grid-template-columns: auto 1fr auto;
row-gap: 6px;
}
.desk-num { padding-top: 2px; }
.desk-title { font-size: 26px; }
.desk-sub {
grid-column: 1 / 4;
padding-left: 0;
padding-bottom: 0;
align-self: start;
}
.desk-count { font-size: 10px; align-self: center; }
.desk-collapse {
grid-column: 3 / 4;
grid-row: 1 / 2;
margin-left: 4px;
/* Bigger touch target for thumbs. */
padding: 6px 10px;
font-size: 16px;
}
/* Items — keep the rail but compact it. */
.item { grid-template-columns: 60px 1fr; column-gap: 12px; }
.item-rail { padding-top: 4px; gap: 4px; }
.chip { font-size: 9.5px; padding: 1px 6px; }
.item-title { font-size: 18px; }
.item-tldr { font-size: 14px; }
.colophon-inner { grid-template-columns: 1fr; }
/* On touch screens there's no hover, so the × always shows.
Slightly bigger tap target too (44×44 minimum is the WCAG ask;
28 is the visible bullseye, padded to ~44 effective via the
parent .item gutter). */
.item-hide { opacity: 1; width: 30px; height: 30px; font-size: 20px; }
.hidden-tray { padding: 12px 14px; }
/* Headlines: stack title / source / time / tldr vertically. */
.headline {
grid-template-columns: 1fr;
gap: 4px;
padding: 12px 0;
}
.headline-title,
.headline-source,
.headline-time,
.headline-tldr {
grid-column: 1 / 2;
grid-row: auto;
}
.headline-time { text-align: left; }
.headline-tldr { font-size: 14px; }
}
@media (max-width: 480px) {
:root { --pad-x: 14px; }
/* Masthead: shrink the brand strip, drop the edition number badge. */
.masthead-inner { padding: 18px var(--pad-x) 12px; }
.brand-glyph { font-size: 26px; }
.brand-mark { font-size: 12px; letter-spacing: .14em; }
.edition-num { display: none; }
.archive-link { font-size: 10px; }
.jumpnav ol {
gap: 14px;
font-size: 10.5px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
flex-wrap: nowrap;
white-space: nowrap;
}
.jumpnav { -webkit-overflow-scrolling: touch; }
/* Phone: drop the section number badge AND the rail. Chips for
reddit cards float into the body inline (the "▲ 234" stays
useful but the column itself is wasteful at this width). */
.desk-num { display: none; }
.desk-head { grid-template-columns: 1fr auto; column-gap: 10px; }
.desk-title { font-size: 22px; }
.desk-sub { grid-column: 1 / 3; }
.desk-count { display: none; }
.desk-collapse { grid-column: 2 / 3; padding: 8px 12px; }
/* Strip the rail entirely; chips go inline above the title. Keeps
score/comment context but reclaims the 60px gutter for content. */
.item {
grid-template-columns: 1fr;
column-gap: 0;
row-gap: 6px;
padding-bottom: 18px;
}
.item-rail {
flex-direction: row;
align-items: center;
align-self: start;
gap: 6px;
padding-top: 0;
}
.chip { font-size: 9px; padding: 1px 5px; }
.item-title { font-size: 17px; line-height: 1.3; }
.item-tldr { font-size: 13.5px; }
.item-foot { gap: 10px; font-size: 10px; }
.action { padding: 4px 8px; font-size: 10px; }
.item-meta-row { margin-left: 0; flex-basis: 100%; }
.colophon { padding: 18px var(--pad-x); margin-top: 32px; }
.colophon-block .meta-value { font-size: 10.5px; word-break: break-word; }
.brief { padding: 20px var(--pad-x) 40px; }
.desk + .desk { margin-top: 40px; }
}
/* Belt-and-suspenders: never let a long URL or unbroken token push
horizontal overflow. */
@media (max-width: 720px) {
body { overflow-x: hidden; }
.item-title, .item-tldr, .headline-title, .headline-tldr {
overflow-wrap: anywhere;
word-break: break-word;
}
}