:root {
  --bg: #ffffff;
  --bg-soft: #f4f4f5;
  --fg: #18181b;
  --fg-muted: #71717a;
  --border: #e4e4e7;
  --accent: #2563eb;
  --selected: #eff6ff;
  --selected-edge: #2563eb;
  --warn-bg: #fef3c7;
  --warn-edge: #d97706;
  --warn-fg: #92400e;
  --topbar-h: 3.25rem;
  --list-w: 24rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #18181b;
    --bg-soft: #232327;
    --fg: #e8e8ea;
    --fg-muted: #a1a1aa;
    --border: #34343a;
    --accent: #7aa5f7;
    --selected: #1e2637;
    --selected-edge: #7aa5f7;
    --warn-bg: #3a2c12;
    --warn-edge: #d99a2b;
    --warn-fg: #f0c674;
  }
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
  overflow: hidden;              /* the two panes scroll, the page does not */
}

a { color: var(--accent); }
.muted { color: var(--fg-muted); }

/* ---------- top bar ---------- */

.topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  height: var(--topbar-h);
  padding: 0 1rem;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

.topbar h1 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  flex: 0 0 auto;
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.78rem;
  margin-left: auto;
  flex: 0 0 auto;
}

.topbar button {
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg-muted);
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1;
  padding: 0.3rem 0.5rem;
}

.topbar button:hover { color: var(--fg); border-color: var(--fg-muted); }
.topbar button[aria-busy="true"] { opacity: 0.5; pointer-events: none; }
.topbar button:disabled { opacity: 0.35; pointer-events: none; }

/* The audit view is deliberately conspicuous: reading rejected articles and mistaking them
   for the feed would be an easy and confusing error. */
#dropped[aria-pressed="true"] {
  background: var(--warn-bg);
  border-color: var(--warn-edge);
  color: var(--warn-fg);
  font-weight: 600;
}

body.viewing-dropped .list li.item { border-left-color: var(--warn-edge); }
body.viewing-dropped .list li.item[aria-selected="true"] { background: var(--warn-bg); }
body.viewing-dropped .preview-pane { border-top: 3px solid var(--warn-edge); }

/* ---------- category tabs ---------- */

.tabs {
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
  scrollbar-width: none;
  min-width: 0;
}

.tabs::-webkit-scrollbar { display: none; }

.tabs button {
  flex: 0 0 auto;
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--fg-muted);
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  padding: 0.2rem 0.7rem;
  white-space: nowrap;
}

.tabs button:hover { background: var(--bg-soft); color: var(--fg); }

.tabs button[aria-selected="true"] {
  background: var(--bg-soft);
  border-color: var(--border);
  color: var(--fg);
  font-weight: 600;
}

/* Every tab carries its count, so the number needs to read as a subordinate detail rather
   than as part of the label. A pill keeps it from running into the next tab's name. */
.tabs .count {
  background: var(--bg-soft);
  border-radius: 999px;
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  margin-left: 0.4rem;
  opacity: 0.75;
  padding: 0.05rem 0.35rem;
}

.tabs button[aria-selected="true"] .count {
  background: var(--bg);
  opacity: 0.9;
}

/* In the dropped view, a feed with no audit companion has nothing to show. Dimmed rather
   than hidden: tabs that come and go make the strip jump under the reader's thumb. */
.tabs button.unavailable { opacity: 0.4; }

/* ---------- split ---------- */

.split {
  display: grid;
  grid-template-columns: var(--list-w) 1fr;
  height: calc(100% - var(--topbar-h));
}

.pane { overflow-y: auto; overscroll-behavior: contain; }

.list-pane { border-right: 1px solid var(--border); }
.list-pane:focus-within { outline: none; }

.preview-pane { padding: 1.5rem 2rem 4rem; }

/* ---------- list ---------- */

.list { list-style: none; margin: 0; padding: 0; }
.list:focus { outline: none; }

/* ---------- day groups ---------- */

.list li.day-group { border-bottom: none; cursor: default; display: block; padding: 0; }

.day-header {
  background: var(--bg-soft);
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--fg-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  gap: 0.5rem;
  padding: 0.45rem 0.9rem;
  text-align: left;
  width: 100%;
  -webkit-tap-highlight-color: rgba(127, 127, 127, 0.25);
}

.day-header:hover { color: var(--fg); }

.day-header .chevron {
  display: inline-block;
  transition: transform 150ms ease;
}

.day-group[aria-expanded="false"] .chevron { transform: rotate(-90deg); }

.day-header .day-count {
  font-variant-numeric: tabular-nums;
  margin-left: auto;
  opacity: 0.7;
}

.day-items { list-style: none; margin: 0; padding: 0; }
.day-group[aria-expanded="false"] .day-items { display: none; }

.list li.item {
  border-bottom: 1px solid var(--border);
  border-left: 3px solid transparent;
  /* cursor:pointer is load-bearing on iOS Safari, not decoration: without it Safari can
     decline to synthesise click events on a plain non-interactive element. */
  cursor: pointer;
  display: flex;
  gap: 0.65rem;
  padding: 0.7rem 0.9rem;
  scroll-margin: 0.5rem;
  -webkit-tap-highlight-color: rgba(127, 127, 127, 0.25);
}

.list .thumb {
  background: var(--bg-soft);
  border-radius: 6px;
  flex: none;
  height: 3.6rem;
  object-fit: cover;
  width: 3.6rem;
}

.list .text { min-width: 0; }

.list li.item:hover { background: var(--bg-soft); }

.list li.item[aria-selected="true"] {
  background: var(--selected);
  border-left-color: var(--selected-edge);
}

.list .t {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 0.9rem;
  line-height: 1.35;
  margin-bottom: 0.25rem;
}

.list .m { color: var(--fg-muted); font-size: 0.72rem; }
.list .m .sep { opacity: 0.5; margin: 0 0.35rem; }

/* Which category a row came from, shown only in the All view — inside a single category it
   would be the same word on every row. */
.list .m .origin {
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-right: 0.45rem;
  padding: 0.05rem 0.3rem;
  text-transform: uppercase;
}

/* ---------- preview ---------- */

.preview-pane .hero {
  border-radius: 8px;
  display: block;
  margin-bottom: 1rem;
  max-height: 22rem;
  max-width: 44rem;
  object-fit: cover;
  width: 100%;
}

.preview-pane h2 {
  font-size: 1.35rem;
  line-height: 1.3;
  margin: 0 0 0.4rem;
  max-width: 44rem;
}

.preview-pane h2 a { text-decoration: none; }
.preview-pane h2 a:hover { text-decoration: underline; }

.preview-meta {
  color: var(--fg-muted);
  font-size: 0.8rem;
  margin-bottom: 1.25rem;
}

.preview-meta .sep { opacity: 0.5; margin: 0 0.4rem; }

.tag {
  background: var(--bg-soft);
  border-radius: 4px;
  padding: 0.05rem 0.35rem;
  margin-right: 0.25rem;
}

.preview-actions {
  align-items: center;
  display: flex;
  gap: 0.9rem;
  margin-top: 1.5rem;
}

.open-link { font-size: 0.85rem; }

.send-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--fg-muted);
  cursor: pointer;
  font: inherit;
  font-size: 0.8rem;
  padding: 0.3rem 0.7rem;
}

.send-btn:hover { color: var(--fg); border-color: var(--fg-muted); }

/* Feed-supplied description: ours is <p>, <ul>/<ol>, <a>, <small>, <strong>, <table>. */
.body { font-size: 0.97rem; max-width: 44rem; }
.body p:first-child { margin-top: 0; }
.body ul, .body ol { padding-left: 1.3rem; margin: 0.6rem 0; }
.body li { margin: 0.3rem 0; }
.body small { color: var(--fg-muted); }
.body img { max-width: 100%; height: auto; }

/* meta.xml ships tables; keep a wide one from stretching the pane. */
.body .table-scroll { overflow-x: auto; }
.body table { border-collapse: collapse; font-size: 0.85rem; }
.body th, .body td { border: 1px solid var(--border); padding: 0.25rem 0.6rem; text-align: left; white-space: nowrap; }
.body th { background: var(--bg-soft); }

.state { color: var(--fg-muted); padding: 2.5rem 1rem; text-align: center; font-size: 0.9rem; }
.state.error { color: #dc2626; }
@media (prefers-color-scheme: dark) { .state.error { color: #f87171; } }

/* ---------- keyboard help ---------- */

.help {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, 0.5);
}

.help[hidden] { display: none; }

.help-panel {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  max-width: 23rem;
  padding: 1.25rem 1.5rem;
  width: calc(100% - 2rem);
}

.help-panel h2 { font-size: 0.95rem; margin: 0 0 0.85rem; }
.help-panel dl { display: grid; grid-template-columns: auto 1fr; gap: 0.45rem 1rem; margin: 0; font-size: 0.85rem; }
.help-panel dt { text-align: right; white-space: nowrap; }
.help-panel dd { margin: 0; color: var(--fg-muted); }

kbd {
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 4px;
  font: inherit;
  font-size: 0.75rem;
  padding: 0.05rem 0.3rem;
}

/* Mobile only — see the media query below. */
.back-btn {
  display: none;
  background: none;
  border: none;
  color: var(--fg);
  cursor: pointer;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0.2rem 0.5rem 0.2rem 0;
}

#position { font-variant-numeric: tabular-nums; }

/* ---------- narrow screens: two screens, not two panes ---------- */

@media (max-width: 800px) {
  /* The document scrolls, not the body.
     The base rules give html and body `height: 100%` and hand scrolling to the two panes.
     Overriding only `overflow` left body as a viewport-height scroll container with the
     page scrolling inside it — and a `position: sticky` child of that arrangement is
     unreliable, which is how the tab strip ended up scrolling out of view. Releasing the
     height as well puts scrolling back on the viewport, where sticky is well defined. */
  html, body { height: auto; overflow: visible; }

  .topbar {
    position: sticky;
    top: 0;
    z-index: 3;
    flex-wrap: wrap;
    height: auto;
    padding: 0.5rem 0.75rem;
    gap: 0.5rem;
  }

  .tabs { order: 3; width: 100%; padding-top: 0.25rem; }

  .split { display: block; height: auto; }
  .pane { overflow: visible; }
  .list-pane { border-right: none; }
  .list li.item { scroll-margin-top: 4rem; padding: 0.85rem 0.9rem; }
  .list .t { -webkit-line-clamp: 4; line-clamp: 4; font-size: 0.95rem; }

  .preview-pane { padding: 1.25rem 1rem 4rem; }

  /* One screen at a time: the list, or a single article. Showing both stacked puts the
     article below the fold, so tapping an item appeared to do nothing. */
  body:not(.reading) .preview-pane { display: none; }
  body.reading .list-pane { display: none; }
  body.reading .tabs { display: none; }

  body.reading .back-btn { display: inline-flex; }

  /* Swiping is horizontal; let the browser keep vertical panning to itself. */
  body.reading .preview-pane { touch-action: pan-y; }

  /* ---------- swipe transitions ----------
     The article follows the finger, then either springs home or is pushed off-screen while
     the next one arrives from the far edge. Durations are mirrored in app.js (OUT_MS/IN_MS)
     and the two must stay in step.

     `clip` rather than `hidden`: hidden would turn .split into a scroll container and take
     the page's vertical scrolling with it, which is the one thing that must keep working. */
  body.reading .split { overflow-x: clip; }

  body.reading .preview-pane {
    /* Decelerating: quick away from the edge, gentle into place. */
    transition: transform 260ms cubic-bezier(0.22, 0.61, 0.36, 1),
                opacity   260ms ease-out;
    will-change: transform, opacity;
  }

  /* Under the finger there is no transition — the pane IS the finger position. */
  body.reading .preview-pane.swipe-drag { transition: none; }

  /* Leaving: faster than arriving, and eased *in*, so it reads as being pushed away. */
  body.reading .preview-pane.swipe-out-left,
  body.reading .preview-pane.swipe-out-right {
    transition: transform 170ms cubic-bezier(0.55, 0, 0.85, 0.4),
                opacity   170ms ease-in;
    opacity: 0;
  }

  body.reading .preview-pane.swipe-out-left  { transform: translate3d(-100%, 0, 0); }
  body.reading .preview-pane.swipe-out-right { transform: translate3d(100%, 0, 0); }

  /* The start frame for the incoming article. app.js paints this, then drops the class so
     the base transition above carries it home. Transitions are off here, or the jump to the
     edge would animate too and the article would fly out before flying back in. */
  body.reading .preview-pane.swipe-from-left,
  body.reading .preview-pane.swipe-from-right {
    transition: none;
    opacity: 0;
  }

  body.reading .preview-pane.swipe-from-left  { transform: translate3d(-100%, 0, 0); }
  body.reading .preview-pane.swipe-from-right { transform: translate3d(100%, 0, 0); }

  /* Hide the desktop-only affordances that have no touch equivalent. */
  #help-toggle { display: none; }
}

/* Reduced motion is a request for no motion, not for less of it. app.js skips the animation
   entirely; this makes sure a class left behind by an interrupted swipe cannot strand the
   article off-screen. */
@media (prefers-reduced-motion: reduce) {
  body.reading .preview-pane,
  body.reading .preview-pane.swipe-drag,
  body.reading .preview-pane.swipe-out-left,
  body.reading .preview-pane.swipe-out-right,
  body.reading .preview-pane.swipe-from-left,
  body.reading .preview-pane.swipe-from-right {
    transition: none;
    transform: none;
    opacity: 1;
  }
}

/* A pointer-less device has no hover state to reveal anything, so make the tap target
   obviously tappable rather than relying on the hover highlight. */
@media (hover: none) {
  .list li.item:active { background: var(--bg-soft); }
}
