@import 'tokens.css';

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  height: 100%;
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
}

body {
  height: 100%;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* ---- Layout shell ---- */
.pg-shell {
  display: grid;
  /* NOTE: .pg-main spans `grid-column: 2 / -1`, so the split between columns 2 and 3 here is never
     observable — the list/reader widths come from .pg-main's own grid in MainLayout.razor.css.
     Column 2's value is kept only so the track list stays readable. */
  grid-template-columns: 240px 340px 1fr;
  grid-template-rows: 100vh;
  overflow: hidden;
}

/* Tablet (768–1199): sidebar + reader, list hidden via pg-main's own grid (unchanged). */
@media (min-width: 768px) and (max-width: 1199px) {
  .pg-shell {
    grid-template-columns: 240px 1fr;
  }
}

/* Collapsed sidebar: shrink to a narrow strip so the article list + reader get more room.
   The strip keeps the header (with the expand button), everything else is hidden via the
   sidebar's own scoped CSS. Collapse is a desktop/tablet (≥768px) affordance only — on mobile
   the sidebar is the full-screen root pane and the collapse button is hidden. */
@media (min-width: 768px) {
  html.pg-sidebar-collapsed .pg-shell {
    grid-template-columns: 48px 340px 1fr;
  }
}

@media (min-width: 768px) and (max-width: 1199px) {
  html.pg-sidebar-collapsed .pg-shell {
    grid-template-columns: 48px 1fr;
  }
}

/* Expand strip only exists while collapsed (razor-gated); participates only ≥768px. */
.pg-sidebar-expand-strip { display: none; }

@media (min-width: 768px) {
  /* Collapsed sidebar inner: keep header + collapse toggle, hide the rest. */
  html.pg-sidebar-collapsed .pg-sidebar { overflow: hidden; position: relative; }
  /* The whole collapsed rail is the expand target — invisible button over everything,
     so a click anywhere (not just the corner icon) expands. Global, not scoped: the
     html.pg-sidebar-collapsed gate lives outside the component. */
  html.pg-sidebar-collapsed .pg-sidebar-expand-strip {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 5;
    padding: 0;
    border: none;
    background: transparent;
    cursor: pointer;
  }
  html.pg-sidebar-collapsed .pg-sidebar:hover { background: var(--color-surface-2); }
  html.pg-sidebar-collapsed .pg-sidebar:hover .pg-sidebar-collapse-btn { color: var(--color-text); }
  /* The header is the rail's only visible child: let it fill the full height and center
     the expand icon in the bar, with no separator line under it. */
  html.pg-sidebar-collapsed .pg-sidebar-header {
    padding: var(--space-2);
    justify-content: center;
    align-items: center;
    border-bottom: none;
    flex: 1;
  }
  html.pg-sidebar-collapsed .pg-sidebar-logo,
  html.pg-sidebar-collapsed .pg-sidebar-add-btn:not(.pg-sidebar-collapse-btn),
  html.pg-sidebar-collapsed .pg-sidebar-nav,
  html.pg-sidebar-collapsed .pg-sidebar-section-label,
  html.pg-sidebar-collapsed .pg-sidebar-tags,
  html.pg-sidebar-collapsed .pg-sidebar-footer,
  html.pg-sidebar-collapsed .pg-feed-filter,
  html.pg-sidebar-collapsed .pg-folder-new-row {
    display: none !important;
  }
}

/* Panes: transparent wrapper so list+reader participate directly in pg-main's grid (desktop). */
.pg-panes { display: contents; }

/* Mobile-only chrome (top nav bar, bottom action bar, action sheet) is hidden on
   desktop/tablet; the ≤767px block below reveals it. Declared before that block so the
   equal-specificity !important rules resolve by source order. */
.pg-mobile-only { display: none !important; }

/* ====================================================================== */
/* Mobile (≤767px) — native push-navigation (Feeds → Articles → Reader)   */
/* ====================================================================== */
@media (max-width: 767px) {
  html, body { overflow: hidden; }
  /* That lock is for the reader's sliding panes. Pages without them (the landing, login,
     register) scroll the window as on desktop: html visible, so body's overflow-x passes
     to the viewport (a scrolling body would also break the landing's sticky nav).
     Without this they could not scroll at all on phones (2026-10-13). */
  html:has(.pg-landing, .pg-auth-shell) { overflow: visible; }
  html:has(.pg-landing, .pg-auth-shell) body { overflow: visible; overflow-x: hidden; }

  /* The reader shell becomes a 300vw horizontal track of three full-width panes; data-mview
     (emitted on .pg-panes by Home.razor) slides it 0 / -100vw / -200vw. pg-main is a
     pass-through so the sidebar and the panes row are the shell's direct flex children.
     Gated on :has(.pg-panes) so other MainLayout pages (e.g. /not-found) aren't turned into
     a 300vw track that hides their content off-screen. */
  .pg-shell:has(.pg-panes) {
    display: flex;
    grid-template-columns: none;
    width: 300vw;
    height: 100dvh;
    overflow: hidden;
    transform: translateX(0);
    transition: transform 0.34s var(--ease-ios);
    will-change: transform;
  }
  .pg-shell:has(.pg-panes[data-mview="list"])   { transform: translateX(-100vw); }
  .pg-shell:has(.pg-panes[data-mview="reader"]) { transform: translateX(-200vw); }

  /* Non-reader MainLayout pages (no .pg-panes): plain scrollable column, no slide track. */
  .pg-shell:not(:has(.pg-panes)) {
    display: flex;
    flex-direction: column;
    grid-template-columns: none;
    height: 100dvh;
    overflow-y: auto;
  }

  .pg-panes {
    display: flex;
    flex-direction: row;
    flex: 0 0 200vw;
    width: 200vw;
    height: 100dvh;
    min-height: 0;
  }

  /* Mobile-only chrome (top nav bar, bottom action bar, action sheet) is present in the DOM
     at all sizes (base rule below hides it); revealed here. */
  .pg-mobile-only { display: flex !important; }
  /* The collapse button is meaningless on mobile (sidebar is the full-screen root). */
  .pg-sidebar-collapse-btn { display: none !important; }

  /* Settings on mobile: drop the feed sidebar entirely. .pg-shell has no .pg-panes here, so it
     lays out as a column and the sidebar — which is `flex: 0 0 100vw` for the reader's slide
     track — became a full-viewport-tall block you had to scroll past before reaching a single
     setting. Settings carries its own section index and its own way back to the reader, so the
     sidebar was only in the way. Global, not scoped: the gate is an ancestor of PgSidebar's root,
     which a scoped selector can never reach. */
  .pg-shell:has(.pg-settings-main) .pg-sidebar { display: none; }
  .pg-shell:has(.pg-settings-main) { display: block; overflow-y: auto; }
}

/* Home-screen PWA (standalone) chin-gap fix. With viewport-fit=cover, iOS shifts the app up
   behind the status bar but does NOT grow the viewport to compensate — so any full-height
   container measured as 100% or 100dvh ends up ~59px (the status-bar height) short, exposing
   the page bg below the action bar (the "chin gap"). Only 100vh reports the true full-screen
   height; everything in the standalone height chain must use it, html/body included, or the
   short ancestor re-introduces the gap. Scoped to standalone so the in-tab path keeps dvh.
   Ref: r/PWA "fighting the chin gap" (Edit 2) — confirmed on-device here (innerH 793 vs
   screenH 852, safeTop 59). Do NOT add apple-mobile-web-app-status-bar-style=black-translucent. */
@media (max-width: 767px) and (display-mode: standalone) {
  html, body { height: 100vh; }
  .pg-shell:has(.pg-panes),
  .pg-shell:not(:has(.pg-panes)),
  .pg-panes { height: 100vh; }
}

/* --- Pull-to-refresh / pull-to-back affordance (mobile) --- */
/* A circular progress ring that fills as the pane is pulled down from its top, with the icon
   centred inside and a label below (managed by pull.js). Absolutely positioned in the pane root
   (which must be positioned — see the panes' rules); offset below the status bar so it never
   hides under the notch. pull.js drives the vertical offset + opacity + ring fill + state class. */
.pg-pull {
  position: absolute;
  top: calc(env(safe-area-inset-top) + var(--space-2));
  left: 50%;
  transform: translateX(-50%) translateY(0);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  /* Translucent rounded-square backdrop (app bg colour @ ~82%) so the ring + label stay
     readable over article thumbnails/text; light blur lifts it off busy content. */
  padding: var(--space-2) var(--space-3);
  background: color-mix(in srgb, var(--color-bg) 82%, transparent);
  border-radius: 12px;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  box-shadow: var(--shadow-sm);
  opacity: 0;
  pointer-events: none;
  font-family: var(--font-body);
  z-index: 60;
}
.pg-pull-dial { position: relative; width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; }
.pg-pull-ring { position: absolute; inset: 0; width: 30px; height: 30px; transform: rotate(-90deg); overflow: visible; }
.pg-pull-ring-track { fill: none; stroke: var(--color-border); stroke-width: 3; }
.pg-pull-ring-fill {
  fill: none; stroke: var(--color-text-faint); stroke-width: 3; stroke-linecap: round;
  stroke-dasharray: 94.25; stroke-dashoffset: 94.25; transition: stroke 0.16s ease;
}
.pg-pull-ico {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.9rem; color: var(--color-text-faint); transition: color 0.16s ease;
}
.pg-pull-ico svg { width: 1em; height: 1em; display: block; }
.pg-pull-label {
  font-size: 0.625rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--color-text-faint); transition: color 0.16s ease;
}
/* Armed — past threshold, release to fire. */
.pg-pull--ready .pg-pull-ring-fill { stroke: var(--color-secondary); }
.pg-pull--ready .pg-pull-ico,
.pg-pull--ready .pg-pull-label { color: var(--color-secondary); }
.pg-pull--ready .pg-pull-ico { animation: pg-pull-pop 0.26s var(--ease-out); }
/* Active — indeterminate spinner while the refresh runs. */
.pg-pull-ring-fill--spin {
  stroke: var(--color-secondary); stroke-dasharray: 24 70.25;
  transform-origin: 18px 18px; animation: pg-pull-spin 0.8s linear infinite;
}
.pg-pull--active .pg-pull-ico { color: var(--color-secondary); }
/* Done — brief success flash (refresh only). */
.pg-pull--done .pg-pull-ring-fill { stroke: var(--color-success); }
.pg-pull--done .pg-pull-ico,
.pg-pull--done .pg-pull-label { color: var(--color-success); }
@keyframes pg-pull-spin { to { transform: rotate(360deg); } }
@keyframes pg-pull-pop { 0% { transform: scale(1); } 45% { transform: scale(1.24); } 100% { transform: scale(1); } }

/* The refresh spinner is a genuine loading indicator — keep it rotating (slowly) even when the
   user prefers reduced motion, which the global guard above would otherwise freeze. */
@media (prefers-reduced-motion: reduce) {
  .pg-pull-ring-fill--spin { animation-duration: 1s !important; animation-iteration-count: infinite !important; }
  .pg-pull--ready .pg-pull-ico { animation: none !important; }
}

/* --- Mobile top nav bar (back / feed title) --- */
.pg-mnav {
  align-items: center;
  gap: var(--space-1);
  /* 48px bar + the status-bar inset on top, with matching padding so the back button
     never sits under the Dynamic Island / notch. */
  height: calc(48px + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) var(--space-1) 0;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
  flex-shrink: 0;
  position: relative;
  z-index: 46;
}
.pg-mnav-back {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  height: 40px;
  padding: 0 var(--space-2) 0 var(--space-1);
  border: none;
  background: none;
  /* Flat/paper guideline — neutral text, not link-blue. */
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
  border-radius: var(--radius-sm);
  -webkit-tap-highlight-color: transparent;
  max-width: 42%;
}
.pg-mnav-back .pg-ico { font-size: 1.35rem; flex-shrink: 0; color: var(--color-text-muted); }
.pg-mnav-back span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-mnav-back:active { background: var(--color-surface-2); }
.pg-mnav-title {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-xs);
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 var(--space-1);
}
.pg-mnav-spacer { min-width: 40px; flex-shrink: 0; }

/* --- Reader bottom action bar (primary actions) --- */
.pg-reader-bottombar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  /* Include the safe-area inset in the height (not just as padding): with box-sizing
     border-box a bare --pg-bottombar-h would absorb the inset and shove the 44px buttons
     up off-centre in iOS standalone (home-indicator) mode. Here the button row stays a
     centred --pg-bottombar-h and the inset is filler below. */
  height: calc(var(--pg-bottombar-h) + env(safe-area-inset-bottom));
  padding: 0 var(--space-3);
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  z-index: 46;
  /* Auto-hide on scroll: initReaderProgress (App.razor) toggles .pg-reader-bottombar--hidden by
     scroll direction. translateY(125%) slides it fully below the pane (overflow:hidden on
     .pg-reader clips it), reclaiming the reading area; it slides back on scroll-up / near top. */
  transition: transform 0.28s var(--ease-ios), opacity 0.2s ease;
}
.pg-reader-bottombar.pg-reader-bottombar--hidden {
  transform: translateY(125%);
  opacity: 0;
  pointer-events: none;
}
/* Slide the bar up the first time it appears (article opened from the list) instead of popping
   in — same motion as the scroll-up reveal. The animation only runs when the element is first
   inserted (list → reader), so switching between already-open articles doesn't replay it. */
@media (max-width: 767px) {
  .pg-reader-bottombar { animation: pg-bottombar-in 0.28s ease; }
}
@keyframes pg-bottombar-in {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
.pg-bbar-group {
  display: flex;
  align-items: center;
  gap: 2px;
}
.pg-bbar-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  background: none;
  color: var(--color-text-muted);
  font-size: 1.25rem;
  border-radius: var(--radius-md);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.12s ease, background-color 0.12s ease;
}
.pg-bbar-btn:disabled { opacity: 0.3; cursor: not-allowed; }
.pg-bbar-btn--active { color: var(--color-secondary); }
.pg-bbar-btn:active:not(:disabled) { background: var(--color-surface-2); }
/* In-place busy glyph for the bar's full-text button — matches the desktop toolbar's
   .pg-reader-spinner, sized for the 44px touch target. Keyframes are declared here rather than
   borrowed from the reader's scoped sheet: keyframe names are global either way, but depending on
   another component's stylesheet makes this break silently if that file ever moves. */
.pg-bbar-spinner {
  font-size: 1rem;
  line-height: 1;
  color: var(--color-text-muted);
  animation: pg-pulse 1s ease-in-out infinite;
  will-change: opacity;
}
@keyframes pg-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { .pg-bbar-spinner { animation: none; } }

/* --- Bottom action sheet (overflow actions) — absolute within the reader pane --- */
.pg-sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 150;
  animation: pg-fade-in 0.2s ease-out;
}
.pg-sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 151;
  background: var(--color-surface);
  border-radius: 16px 16px 0 0;
  padding: var(--space-1) var(--space-2) calc(var(--space-4) + env(safe-area-inset-bottom));
  box-shadow: 0 -6px 28px rgba(0, 0, 0, 0.18);
  animation: pg-sheet-up 0.3s var(--ease-ios);
  max-height: 82dvh;
  overflow-y: auto;
}
@keyframes pg-sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
.pg-sheet-grip { width: 36px; height: 4px; border-radius: 2px; background: var(--color-border-strong); margin: var(--space-2) auto var(--space-1); }
.pg-sheet-title { font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-faint); padding: var(--space-2) var(--space-3) var(--space-1); }
.pg-sheet-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  border: none;
  background: none;
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--color-text);
  text-decoration: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.pg-sheet-row .pg-ico { font-size: 1.3rem; color: var(--color-text-muted); flex-shrink: 0; }
.pg-sheet-row:active { background: var(--color-surface-2); }
.pg-sheet-row:disabled { opacity: 0.5; cursor: not-allowed; }
.pg-sheet-row--active,
.pg-sheet-row--active .pg-ico { color: var(--color-secondary); }
.pg-sheet-row .pg-tag-dot { width: 12px; height: 12px; }
.pg-sheet-row-tail { margin-left: auto; color: var(--color-text-faint); display: inline-flex; }
.pg-popover-empty { font-size: var(--text-xs); color: var(--color-text-faint); padding: var(--space-2) var(--space-3); }

/* ---- Typography ---- */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.25;
}

a {
  color: var(--color-link);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

code, pre, kbd {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

/* ---- Article reading typography ---- */
.pg-article-body {
  font-family: var(--reader-font, var(--font-body));
  font-size: calc(var(--reader-font-scale, 1) * var(--text-md));
  line-height: 1.75;
  max-width: var(--reader-body-w, 680px);
  margin: 0 auto;
  overflow-wrap: break-word;
  word-break: break-word;
  min-width: 0;
  overflow-x: hidden;
}

.pg-article-body p { margin-bottom: var(--space-4); }
.pg-article-body h2, .pg-article-body h3 { margin: var(--space-6) 0 var(--space-2); }
.pg-article-body img, .pg-article-body video, .pg-article-body iframe, .pg-article-body embed, .pg-article-body object { max-width: 100%; height: auto; border-radius: var(--radius-sm); }
/* RSS feeds often wrap content in divs/figures with explicit widths — force them to fit */
.pg-article-body div, .pg-article-body figure, .pg-article-body section, .pg-article-body aside { max-width: 100%; }
.pg-article-body blockquote {
  border-left: 3px solid var(--color-border-strong);
  padding-left: var(--space-4);
  color: var(--color-text-muted);
  margin: var(--space-4) 0;
}
.pg-article-body pre {
  overflow-x: auto;
  max-width: 100%;
  white-space: pre;
}
.pg-article-body table {
  display: block;
  overflow-x: auto;
  max-width: 100%;
}
.pg-article-body td, .pg-article-body th {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
}

/* ---- Scrollbar ---- */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--color-border-strong); }

/* ---- Accessibility ---- */
/* Visible keyboard-focus ring. :focus-visible only matches keyboard/AT focus, so mouse
   clicks don't get an outline. Inputs/textareas/selects keep their own focus border+ring. */
a:focus-visible,
button:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ---- Motion ---- */
/* Animations are opt-out via prefers-reduced-motion (see the guard at the end of this section). */
@keyframes pg-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes pg-modal-in {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 8px)); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}

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

/* ---- Shared component primitives ---- */

/* Buttons */
.pg-btn {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 500;
  font-family: var(--font-body);
  cursor: pointer;
  border: 1px solid transparent;
  line-height: 1.5;
  display: inline-flex;
  align-items: center;
  touch-action: manipulation;
  transition: background-color 0.12s ease, border-color 0.12s ease, opacity 0.12s ease, color 0.12s ease;
}

.pg-btn:active:not(:disabled) { transform: translateY(0.5px); }

.pg-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.pg-btn--primary { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.pg-btn--primary:hover:not(:disabled) { opacity: 0.88; }

.pg-btn--secondary { background: var(--color-surface); color: var(--color-text); border-color: var(--color-border); }
.pg-btn--secondary:hover:not(:disabled) { background: var(--color-surface-2); }

.pg-btn--ghost { background: none; color: var(--color-text-muted); border-color: transparent; }
.pg-btn--ghost:hover:not(:disabled) { background: var(--color-surface-2); color: var(--color-text); }

.pg-btn--danger { background: var(--color-danger); color: #fff; border-color: var(--color-danger); }
.pg-btn--danger:hover:not(:disabled) { opacity: 0.88; }

.pg-btn--sm { padding: var(--space-1) var(--space-3); font-size: var(--text-2xs); }

@media (max-width: 767px) {
  .pg-btn--sm { padding: var(--space-2) var(--space-3); }
}

/* Modal */
.pg-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 100;
  animation: pg-fade-in 0.16s ease-out;
}

.pg-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 101;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  width: 420px;
  max-width: 90vw;
  animation: pg-modal-in 0.18s ease-out;
}

.pg-modal--sm { width: 360px; }
.pg-modal--lg { width: min(600px, 95vw); }

.pg-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.pg-modal-title { font-size: var(--text-sm); font-weight: 600; }

.pg-modal-close {
  border: none;
  background: none;
  cursor: pointer;
  color: var(--color-text-muted);
  font-size: 0.875rem;
  padding: 2px;
  border-radius: var(--radius-sm);
  min-width: 32px;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
}

.pg-modal-close:hover { color: var(--color-text); }
.pg-modal-body { padding: var(--space-4); }
.pg-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
}

/* Form primitives */
.pg-form-label {
  display: block;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

.pg-form-input {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: var(--text-xs);
  font-family: var(--font-body);
  outline: none;
}

.pg-form-input:focus { border-color: var(--color-border-strong); box-shadow: 0 0 0 2px var(--color-focus); }

/* iOS Safari auto-zooms the page when a focused text control's font-size is under 16px,
   and the zoom sticks after blur — hiding the bottom bar until the user pinches out.
   Enforce the 16px floor on every text-entry control on phones (!important so it beats
   the per-class sizes, which stay smaller on desktop). */
@media (max-width: 767px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="color"]),
  textarea,
  select { font-size: 16px !important; }
}
.pg-form-input--error { border-color: var(--color-danger); }

.pg-form-error { margin-top: var(--space-2); font-size: var(--text-2xs); color: var(--color-danger); }

select.pg-form-input { appearance: auto; }
.pg-form-input--mono { font-family: var(--font-mono); }
.pg-form-input--sm   { width: auto; }

/* Data table (API keys, invitation codes) */
.pg-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-xs);
  margin-top: var(--space-3);
}
.pg-table th,
.pg-table td {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}
.pg-table th {
  font-weight: 600;
  color: var(--color-text-muted);
  border-bottom-color: var(--color-border-strong);
}
.pg-table tbody tr:last-child td { border-bottom: none; }
.pg-table tbody tr:hover { background: var(--color-surface-2); }

/* Form section (fieldset) */
.pg-form-section {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  /* Keep form fields readable even when the settings panel is wide (tables use full width). */
  max-width: 720px;
}
.pg-form-section[disabled] { opacity: 0.6; pointer-events: none; }

.pg-form-legend {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text-muted);
  padding: 0 var(--space-2);
}

.pg-form-hint {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: calc(-1 * var(--space-2));
}

.pg-form-check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  cursor: pointer;
}

/* Alert banners */
.pg-alert {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  border: 1px solid transparent;
}
.pg-alert--success {
  background: rgba(22, 163, 74, 0.08);
  border-color: rgba(22, 163, 74, 0.25);
  color: var(--color-success);
}
.pg-alert--error {
  background: rgba(220, 38, 38, 0.08);
  border-color: rgba(220, 38, 38, 0.25);
  color: var(--color-danger);
}
.pg-alert--sm { padding: var(--space-2) var(--space-3); }

/* Auth */
.pg-auth-shell {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-6);
}
.pg-auth-main { width: min(420px, 100%); }
.pg-auth-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  box-shadow: var(--shadow-sm);
}
.pg-auth-title { margin: 0; font-size: var(--text-lg); }
.pg-auth-subtitle { margin: 0; color: var(--color-text-muted); font-size: var(--text-sm); }
.pg-auth-form { display: flex; flex-direction: column; gap: var(--space-3); }

/* Settings section — shared across all settings child components */
.pg-settings-section {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
/* Separates a sub-section that follows another within the same settings tab. */
.pg-settings-section--divided {
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: 1px solid var(--color-border);
}
.pg-settings-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.pg-settings-section-title { margin: 0; font-size: 1.125rem; font-weight: 600; }
.pg-settings-section-actions { display: flex; gap: var(--space-2); }

/* Utility */
.pg-text-muted { color: var(--color-text-muted); font-size: var(--text-xs); }

/* Filter Rules */
.pg-filter-rules { display: flex; flex-direction: column; gap: var(--space-4); }
.pg-section-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
/* On a phone the title and its action buttons cannot share a row: the title wraps to two lines and
   the buttons squeeze to unreadable. Stack them and let the actions sit full width. */
@media (max-width: 767px) {
  .pg-section-header { flex-direction: column; align-items: stretch; gap: var(--space-2); }
  .pg-section-header > :not(.pg-section-title) { display: flex; flex-wrap: wrap; gap: var(--space-2); }
}
.pg-section-title { margin: 0; font-size: 1.125rem; font-weight: 600; }
.pg-filter-rule-form { padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); }
.pg-form-heading { margin: 0 0 var(--space-2); font-size: 1rem; font-weight: 600; }
.pg-form-row { display: flex; flex-direction: column; gap: var(--space-1); }
.pg-form-row--inline { flex-direction: row; align-items: flex-end; gap: var(--space-3); flex-wrap: wrap; }
.pg-form-row--grow { flex: 1; display: flex; flex-direction: column; gap: var(--space-1); }
.pg-form-checkbox-row { display: flex; flex-direction: column; gap: var(--space-1); }
.pg-form-actions { display: flex; gap: var(--space-2); justify-content: flex-end; }
.pg-filter-rule-preview { font-size: var(--text-sm); color: var(--color-text-muted); margin: 0; }
.pg-filter-rule-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.pg-filter-rule-item { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-3); background: var(--color-surface-2); border-radius: var(--radius-md); border: 1px solid var(--color-border); }
.pg-filter-rule-item--inactive { opacity: 0.5; }
.pg-filter-rule-info { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.pg-filter-rule-name { font-weight: 600; font-size: var(--text-sm); }
.pg-filter-rule-desc { font-size: var(--text-xs); color: var(--color-text-muted); word-break: break-word; }
.pg-filter-rule-feed { font-style: italic; }
.pg-filter-rule-actions { display: flex; gap: var(--space-2); flex-shrink: 0; }
.pg-input--sm { width: 5rem; }

/* Tags */
/* User management (admin) */
.pg-user-list { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.pg-user-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); }
.pg-user-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pg-user-email { font-size: var(--text-sm); color: var(--color-text); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.pg-user-meta { font-size: var(--text-xs); color: var(--color-text-faint); }
.pg-user-tag { font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; padding: 1px 6px; border-radius: var(--radius-pill); background: var(--color-surface-2); color: var(--color-text-muted); }
.pg-user-tag--admin { background: var(--color-accent); color: #fff; }
.pg-user-tag--disabled { background: var(--color-danger); color: #fff; }
.pg-user-actions { display: flex; gap: var(--space-1); flex-wrap: wrap; flex-shrink: 0; }
.pg-user-reset { display: flex; gap: var(--space-2); align-items: center; width: 100%; margin-top: var(--space-2); }
.pg-user-reset .pg-form-input { margin: 0; }

.pg-tag-settings { display: flex; flex-direction: column; gap: var(--space-4); }
.pg-tag-form { padding: var(--space-4); }
.pg-tag-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.pg-tag-list-item { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-2) var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); }
.pg-tag-chip { display: inline-flex; align-items: center; padding: 2px 10px; border-radius: var(--radius-pill); font-size: var(--text-xs); font-weight: 600; color: #fff; border: 2px solid transparent; cursor: pointer; transition: opacity .15s; white-space: nowrap; }
.pg-tag-chip--outline { background: transparent !important; }
.pg-tag-chip--selected { opacity: 1; }
.pg-tag-chip:hover { opacity: 0.8; }
.pg-tag-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.pg-article-tags { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-3); }
.pg-color-input { width: 3rem; height: 2rem; padding: 2px; border: 1px solid var(--color-border); border-radius: var(--radius-md); cursor: pointer; }
.pg-sidebar-tags { padding-bottom: var(--space-2); }
.pg-sidebar-tags .pg-tag-dot { margin-right: var(--space-2); }
.pg-nav-icon { font-size: 12px; color: var(--color-text-faint); flex-shrink: 0; }
.pg-nav-item-action .pg-ico { font-size: 12px; }

/* Collapsible article-list search: a lens button in .pg-list-tools opens a popover input.
   Global — not scoped — because the leading .pg-search-icon is rendered by the <PgIcon>
   child component, whose span would not inherit PgArticleList's scoped-CSS attribute. */
/* The wrap stays static so the popover anchors to the right edge of .pg-list-tools
   (the pane's right content edge) rather than the lens — which is the leftmost tool,
   so anchoring there would push a 300px popover off the left of the 340px pane and
   .pg-list's overflow:hidden would clip it. */
.pg-search-wrap { display: inline-flex; }
.pg-search-popover { position: absolute; top: calc(100% + 6px); right: 0; z-index: 70; display: flex; align-items: center; gap: var(--space-2); width: min(300px, calc(100vw - 2 * var(--space-4))); height: 38px; padding: 0 var(--space-2) 0 var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md); box-shadow: var(--shadow-popover); animation: pg-search-pop 0.14s ease-out; }
@keyframes pg-search-pop { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
.pg-search-icon { color: var(--color-text-faint); font-size: 0.95rem; flex-shrink: 0; pointer-events: none; }
.pg-search-input { flex: 1; min-width: 0; height: 100%; border: none; background: none; padding: 0; font-size: var(--text-xs); font-family: var(--font-body); color: var(--color-text); outline: none; }
.pg-search-input::placeholder { color: var(--color-text-faint); }
.pg-search-input::-webkit-search-cancel-button { -webkit-appearance: none; }
/* (16px mobile input floor lives in the global anti-zoom rule near .pg-form-input.) */
.pg-search-clear { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: none; background: none; border-radius: var(--radius-sm); color: var(--color-text-faint); font-size: 0.9rem; cursor: pointer; flex-shrink: 0; }
.pg-search-clear:hover { background: var(--color-surface-2); color: var(--color-text); }
/* 16px input on mobile so iOS Safari doesn't zoom the viewport on focus. */

/* Feed Health */
.pg-feed-health { display: flex; flex-direction: column; gap: var(--space-4); }
.pg-health-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.pg-health-table th { text-align: left; padding: var(--space-2) var(--space-3); font-weight: 600; color: var(--color-text-muted); border-bottom: 1px solid var(--color-border); white-space: nowrap; }
.pg-health-table td { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--color-border); vertical-align: middle; }
.pg-health-row--error td { background: color-mix(in srgb, var(--color-danger) 5%, transparent); }
.pg-health-row--inactive { opacity: 0.55; }
.pg-health-title { max-width: 18rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-health-date { white-space: nowrap; color: var(--color-text-muted); }
.pg-health-errors-col { text-align: center; }
.pg-health-errors--nonzero { color: var(--color-danger); font-weight: 700; }
.pg-health-badge { display: inline-block; padding: 1px 8px; border-radius: var(--radius-pill); font-size: var(--text-xs); font-weight: 600; }
.pg-health-badge--ok { background: color-mix(in srgb, var(--color-success) 15%, transparent); color: var(--color-success); }
.pg-health-badge--error { background: color-mix(in srgb, var(--color-danger) 15%, transparent); color: var(--color-danger); }
.pg-health-badge--inactive { background: var(--color-surface-2); color: var(--color-text-muted); }
.pg-health-row--error-detail td { background: color-mix(in srgb, var(--color-danger) 5%, transparent); padding-top: 0; border-bottom: 1px solid var(--color-border); }
.pg-health-error-msg { font-size: var(--text-xs); color: var(--color-danger); font-family: var(--font-mono); word-break: break-all; }
.pg-health-actions-col { width: 1%; white-space: nowrap; text-align: right; }
.pg-health-edit-row td { background: var(--color-surface-2); }
.pg-health-edit { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3); }
.pg-health-edit-field { display: flex; flex-direction: column; gap: 2px; font-size: var(--text-xs); color: var(--color-text-muted); }
.pg-health-edit-field .pg-form-input { margin: 0; }
.pg-health-edit-field span { font-size: var(--text-xs); }
.pg-health-edit-check { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-sm); color: var(--color-text); }
.pg-health-edit-actions { display: flex; gap: var(--space-2); margin-left: auto; }

/* Digest tag filter */
.pg-digest-tag-filter { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.pg-digest-tag-chip { display: inline-flex; align-items: center; padding: 2px 10px; border-radius: var(--radius-pill); font-size: var(--text-xs); font-weight: 600; border: 2px solid; cursor: pointer; color: #fff; transition: opacity .15s; }
.pg-digest-tag-chip:not(.pg-digest-tag-chip--selected) { background: transparent !important; }
.pg-digest-tag-chip--selected { opacity: 1; }
.pg-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }

/* Loading skeletons */
.pg-skeleton {
  background: var(--color-surface-2);
  border-radius: var(--radius-sm);
  position: relative;
  overflow: hidden;
}
.pg-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--color-text) 7%, transparent), transparent);
  animation: pg-shimmer 1.4s ease-in-out infinite;
}
@keyframes pg-shimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .pg-skeleton::after { display: none; } }
.pg-skeleton-line { height: 0.7rem; }
.pg-skeleton-thumb { width: 56px; height: 56px; flex-shrink: 0; border-radius: var(--radius-sm); }

/* Empty states (article list + reader) */
.pg-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-6);
  max-width: 360px;
  margin: 0 auto;
  animation: pg-fade-in 0.24s ease-out;
}
.pg-empty-icon { font-size: 2.5rem; line-height: 1; opacity: 0.85; color: var(--color-text-faint); }
.pg-empty-title { font-family: var(--font-display); font-size: var(--text-md); font-weight: 600; color: var(--color-text); margin: 0; }
.pg-empty-hint { font-size: var(--text-xs); color: var(--color-text-muted); margin: 0; line-height: 1.6; }
.pg-empty-hint .pg-kbd { margin: 0 1px; }

/* Keyboard shortcut cheatsheet */
.pg-shortcuts { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; }
.pg-shortcut-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.pg-shortcut-row dt { display: flex; gap: var(--space-1); flex-shrink: 0; }
.pg-shortcut-row dd { margin: 0; font-size: var(--text-xs); color: var(--color-text-muted); text-align: right; }
.pg-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  padding: 2px 6px;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--color-text);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  line-height: 1;
}

/* ---- Icon primitive (unified Lucide icon system) ----
   Lucide line icon rendered as a currentColor mask. Vendor the SVGs into
   wwwroot/icons/lucide/<name>.svg (same-origin → mask works, inherits color
   and 1em sizing). Use via the <PgIcon Name="bookmark" /> component.
   See ICON-SYSTEM.md for the full glyph → name mapping. */
.pg-ico {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask: var(--ico) center / contain no-repeat;
          mask: var(--ico) center / contain no-repeat;
}

/* Sidebar collapsible section caret — PgIcon is a child component, so it is sized from here
   (scoped PgSidebar.razor.css never reaches it). */
.pg-sidebar-section-toggle .pg-ico { width: 12px; height: 12px; }
.pg-feed-menu-tail .pg-ico { width: 14px; height: 14px; }

/* Generic anchored popover menu (reader ⋯ / tag picker, list mark-read). Shared by several
   components, so it lives here; menu.js positions it (fixed) and handles dismissal. */
.pg-popover {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 80;
  min-width: 220px;
  max-width: min(320px, calc(100vw - 16px));
  display: flex;
  flex-direction: column;
  padding: var(--space-1);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-popover);
  text-align: left;
}
.pg-popover-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 400;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.pg-popover-item:hover:not(:disabled),
.pg-popover-item:focus-visible { background: var(--color-surface-2); }
.pg-popover-item:disabled { opacity: 0.5; cursor: not-allowed; }
.pg-popover-item--muted { color: var(--color-text-muted); }
.pg-popover-item .pg-ico { width: 16px; height: 16px; flex-shrink: 0; }
.pg-popover-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.pg-popover-check { margin-left: auto; display: inline-flex; color: var(--color-unread); }
.pg-popover-kbd { margin-left: auto; }
.pg-popover-label {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-faint);
}
.pg-popover-divider { height: 1px; margin: var(--space-1) var(--space-2); background: var(--color-border); }

/* Reader tag row: applied chips (click removes) + one picker trigger. */
.pg-article-tags { align-items: center; }
.pg-tag-chip-x { margin-left: 6px; opacity: 0.75; font-weight: 400; }
.pg-tag-picker { position: relative; }
.pg-tag-add {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 10px;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
}
.pg-tag-add:hover { color: var(--color-text); border-color: var(--color-text-faint); }
.pg-tag-add .pg-ico { width: 12px; height: 12px; }

/* Reader typeface (Aa › Font): --reader-font is set inline on .pg-reader. Body text only;
   titles keep the display face. */
.pg-reader-fonts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-1); }
.pg-reader-fonts-groups { display: flex; flex-direction: column; gap: var(--space-1); }
.pg-reader-fonts-group {
  margin-top: var(--space-1);
  font-size: var(--text-2xs);
  color: var(--color-text-faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.pg-reader-fonts .pg-reader-prefs-btn { text-align: left; padding: 6px 8px; font-size: var(--text-sm); }
.pg-feed-snoozed .pg-ico { width: 12px; height: 12px; }
