/* =========================================================================
   OpenReadr — design system
   Goal: editorial, calm, dark by default. No Bootstrap-default look.
   ========================================================================= */

/* ---------- Design tokens ----------------------------------------------- */
:root {
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: .75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 16px;
  --radius-pill: 999px;

  --font-sans: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-serif: "Source Serif 4", "Source Serif Pro", "Charter", Georgia, "Times New Roman", serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --shadow-sm: 0 1px 2px rgba(15, 12, 8, .06);
  --shadow: 0 6px 18px rgba(15, 12, 8, .08), 0 2px 4px rgba(15, 12, 8, .04);
  --shadow-lg: 0 18px 40px rgba(15, 12, 8, .14), 0 6px 12px rgba(15, 12, 8, .06);

  --transition-fast: 120ms cubic-bezier(.2, .8, .2, 1);
  --transition: 200ms cubic-bezier(.2, .8, .2, 1);
}

/* ---------- Light theme ------------------------------------------------- */
[data-bs-theme="light"] {
  --bg: #faf8f4;
  --bg-soft: #f3f0e9;
  --surface: #ffffff;
  --surface-2: #f7f4ee;
  --surface-hover: #f1ede5;
  --border: #e8e3d7;
  --border-strong: #d4cdbb;
  --text: #1c1917;
  --text-muted: #6b675f;
  --text-subtle: #9a958a;
  --accent: #d97706;
  --accent-strong: #b45309;
  --accent-soft: #fef3c7;
  --accent-text: #92400e;
  --danger: #c0392b;
  --warning: #b45309;
  --success: #15803d;
  --info: #0369a1;
  --mark-bg: rgba(217, 119, 6, .22);

  /* --- Glass tokens (light) --------------------------------------------
     Glass on light backgrounds uses a near-white frost with very subtle
     warm-grey hints. The amber identity is carried by the accent color
     on borders, buttons and the brand-mark — the glass surfaces stay
     neutral so they don't look "yellow". */
  --glass-fill:
    linear-gradient(140deg,
      rgba(255, 255, 255, .78) 0%,
      rgba(252, 250, 246, .60) 60%,
      rgba(248, 246, 240, .48) 100%);
  --glass-fill-strong:
    linear-gradient(140deg,
      rgba(255, 255, 255, .88) 0%,
      rgba(250, 248, 244, .72) 100%);
  --glass-border-top: rgba(255, 255, 255, .9);
  --glass-border-bottom: rgba(120, 110, 95, .25);
  --glass-blur: 22px;
  --glass-blur-strong: 32px;
  --glass-shadow:
    0 24px 60px rgba(60, 50, 35, .12),
    0 8px 20px rgba(60, 50, 35, .07),
    0 1px 0 rgba(255, 255, 255, .9) inset;
  --glass-specular:
    radial-gradient(120% 80% at 0% 0%,
      rgba(255, 255, 255, .55) 0%,
      rgba(255, 255, 255, .12) 28%,
      transparent 60%);
  --glass-shine: rgba(255, 255, 255, .65);
  --glass-input-bg: rgba(255, 255, 255, .55);
  --glass-input-border: rgba(80, 75, 65, .20);
  --glass-input-border-focus: var(--accent);
  /* Solid-enough fill for popovers (dropdown menus, offcanvas drawers,
     modals): the glass-fill tokens are too translucent for floating UI
     that needs to MASK content underneath rather than float over it.
     96% opacity keeps the frosted feel but stops page content showing
     through. */
  --popover-fill: rgba(252, 250, 246, .96);
  --glass-orb-1: radial-gradient(circle, rgba(245, 158, 11, .55), transparent 60%);
  --glass-orb-2: radial-gradient(circle, rgba(251, 191, 36, .45), transparent 65%);
  --glass-orb-3: radial-gradient(circle, rgba(254, 215, 170, .65), transparent 70%);
  --glass-bg-gradient:
    radial-gradient(120% 80% at 100% 0%, #fef3c7 0%, transparent 55%),
    radial-gradient(80% 60% at 0% 100%, #fed7aa 0%, transparent 60%),
    linear-gradient(160deg, #fffaf0 0%, #fdf4e3 40%, #f7e8d0 100%);
  /* Subtler version for the in-app shell: less saturated so it doesn't
     fight the content. Light mode uses warm-grey hints rather than full
     amber/peach — the bg already carries the brand warmth via --bg. */
  --app-bg-gradient:
    radial-gradient(70% 45% at 100% 0%, rgba(245, 158, 11, .06) 0%, transparent 65%),
    radial-gradient(55% 40% at 0% 100%, rgba(160, 130, 90, .07) 0%, transparent 70%),
    var(--bg);
  --app-orb-1: radial-gradient(circle, rgba(200, 170, 130, .14), transparent 70%);
  --app-orb-2: radial-gradient(circle, rgba(170, 145, 110, .10), transparent 72%);
}

/* ---------- Dark theme (default) --------------------------------------- */
[data-bs-theme="dark"] {
  --bg: #0d0c0a;
  --bg-soft: #15130f;
  --surface: #181613;
  --surface-2: #211e19;
  --surface-hover: #25221c;
  --border: #2a2620;
  --border-strong: #3b362c;
  --text: #f3efe7;
  --text-muted: #a8a299;
  --text-subtle: #6e695f;
  --accent: #f59e0b;
  --accent-strong: #fbbf24;
  --accent-soft: #2a1c08;
  --accent-text: #fcd34d;
  --danger: #f87171;
  --warning: #fbbf24;
  --success: #4ade80;
  --info: #38bdf8;
  --mark-bg: rgba(245, 158, 11, .25);

  /* --- Glass tokens (dark) ---------------------------------------------
     Glass on dark backgrounds uses a low-alpha white fill plus a subtle
     amber tint at the bottom, mimicking warm light passing through.
     Whites kept intentionally low so cards don't read as "grey film"
     over the dark bg — the gradient border + specular highlight do the
     visual lifting. */
  --glass-fill:
    linear-gradient(140deg,
      rgba(255, 255, 255, .07) 0%,
      rgba(255, 255, 255, .035) 55%,
      rgba(245, 158, 11, .05) 100%);
  --glass-fill-strong:
    linear-gradient(140deg,
      rgba(255, 255, 255, .10) 0%,
      rgba(245, 158, 11, .07) 100%);
  --glass-border-top: rgba(255, 255, 255, .28);
  --glass-border-bottom: rgba(245, 158, 11, .18);
  --glass-blur: 24px;
  --glass-blur-strong: 36px;
  --glass-shadow:
    0 30px 80px rgba(0, 0, 0, .55),
    0 10px 24px rgba(0, 0, 0, .35),
    0 1px 0 rgba(255, 255, 255, .12) inset;
  --glass-specular:
    radial-gradient(120% 80% at 0% 0%,
      rgba(255, 255, 255, .25) 0%,
      rgba(255, 255, 255, .06) 30%,
      transparent 60%);
  --glass-shine: rgba(255, 255, 255, .35);
  --glass-input-bg: rgba(255, 255, 255, .05);
  --glass-input-border: rgba(255, 255, 255, .14);
  --glass-input-border-focus: var(--accent);
  /* Solid-enough fill for popovers (dropdown menus, offcanvas drawers,
     modals): translucent glass-fill is too see-through when floating
     UI must MASK content underneath. Near-opaque dark with a hint of
     warmth. */
  --popover-fill: rgba(24, 22, 19, .96);
  --glass-orb-1: radial-gradient(circle, rgba(245, 158, 11, .55), transparent 60%);
  --glass-orb-2: radial-gradient(circle, rgba(217, 70, 0, .42), transparent 65%);
  --glass-orb-3: radial-gradient(circle, rgba(180, 83, 9, .55), transparent 65%);
  --glass-bg-gradient:
    radial-gradient(120% 80% at 100% 0%, #2a1c08 0%, transparent 55%),
    radial-gradient(80% 60% at 0% 100%, #1a0f04 0%, transparent 60%),
    linear-gradient(160deg, #110d07 0%, #1a130a 40%, #0a0807 100%);
  /* Subtler version for the in-app shell: less saturated so it doesn't
     fight the content. */
  --app-bg-gradient:
    radial-gradient(80% 50% at 100% 0%, #1a0f04 0%, transparent 60%),
    radial-gradient(60% 45% at 0% 100%, #1a130a 0%, transparent 70%),
    var(--bg);
  --app-orb-1: radial-gradient(circle, rgba(245, 158, 11, .18), transparent 65%);
  --app-orb-2: radial-gradient(circle, rgba(217, 119, 6, .14), transparent 70%);
}

/* ---------- Flat theme ------------------------------------------------
   Truly flat — no depth cues at all.  Hierarchy comes from spacing,
   colour and typography; not from shadows or 3D effects.  Borders are
   as subtle as possible — barely a hairline against the surface.

   - Zero blur, zero shadows, zero gradients, zero specular highlights
   - Borders use the lightest available token (--border, not --border-strong)
   - Cards distinguish themselves from the page through a tiny surface
     vs. bg colour contrast — no extra outline needed
   - Popovers (dropdown / offcanvas / modal) are fully opaque solid
     fills, separated from content by their own background only
   ===================================================================== */
[data-theme="flat"][data-bs-theme="light"] {
  --glass-fill: var(--surface);
  --glass-fill-strong: var(--surface);
  --glass-border-top: var(--border);
  --glass-border-bottom: var(--border);
  --glass-blur: 0px;
  --glass-blur-strong: 0px;
  --glass-shadow: none;
  --glass-specular: none;
  --glass-shine: transparent;
  --glass-input-bg: var(--bg-soft);
  --glass-input-border: var(--border);
  --popover-fill: var(--surface);
  --glass-orb-1: transparent;
  --glass-orb-2: transparent;
  --glass-orb-3: transparent;
  --glass-bg-gradient: var(--bg);
  --app-bg-gradient: var(--bg);
  --app-orb-1: transparent;
  --app-orb-2: transparent;
}
[data-theme="flat"][data-bs-theme="dark"] {
  --glass-fill: var(--surface);
  --glass-fill-strong: var(--surface);
  --glass-border-top: var(--border);
  --glass-border-bottom: var(--border);
  --glass-blur: 0px;
  --glass-blur-strong: 0px;
  --glass-shadow: none;
  --glass-specular: none;
  --glass-shine: transparent;
  --glass-input-bg: var(--surface-2);
  --glass-input-border: var(--border);
  --popover-fill: var(--surface);
  --glass-orb-1: transparent;
  --glass-orb-2: transparent;
  --glass-orb-3: transparent;
  --glass-bg-gradient: var(--bg);
  --app-bg-gradient: var(--bg);
  --app-orb-1: transparent;
  --app-orb-2: transparent;
}

/* Hide every ambient/atmospheric pseudo-element. */
[data-theme="flat"] .auth-shell::before,
[data-theme="flat"] .auth-shell::after,
[data-theme="flat"] .app-shell::before,
[data-theme="flat"] .app-shell::after,
[data-theme="flat"] .auth-card::after,
[data-theme="flat"] .article-tile::before,
[data-theme="flat"] .ai-digest-hero::before {
  display: none !important;
}

/* Component-level shadow strip — these have HARDCODED box-shadows in
   their base CSS that override the --glass-shadow token, so we kill
   each one explicitly under flat. */
[data-theme="flat"] .topbar,
[data-theme="flat"] .dropdown-menu,
[data-theme="flat"] .offcanvas,
[data-theme="flat"] .modal-content,
[data-theme="flat"] .article-card,
[data-theme="flat"] .article-card:hover,
[data-theme="flat"] .article-tile,
[data-theme="flat"] .article-tile:hover,
[data-theme="flat"] .feed-card,
[data-theme="flat"] .feed-card:hover,
[data-theme="flat"] .stat-card,
[data-theme="flat"] .stat-card:hover,
[data-theme="flat"] .recent-card,
[data-theme="flat"] .recent-card:hover,
[data-theme="flat"] .ai-digest-hero,
[data-theme="flat"] .surface,
[data-theme="flat"] .alert,
[data-theme="flat"] .reader-detail,
[data-theme="flat"] .article-overlay,
[data-theme="flat"] .pagination .page-link:hover,
[data-theme="flat"] .nav-pills-app a.active,
[data-theme="flat"] .view-toggle a.active,
[data-theme="flat"] .admin-tab.active,
[data-theme="flat"] .sidebar-item.active,
[data-theme="flat"] .ai-digest-hero-head:hover {
  box-shadow: none !important;
}

/* Buttons: solid amber, no gradient/glow, no lift-on-hover. */
[data-theme="flat"] .btn-primary,
[data-theme="flat"] .btn-primary:focus,
[data-theme="flat"] .btn-primary:focus-visible,
[data-theme="flat"] .btn-primary:active,
[data-theme="flat"] .btn-primary:hover {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: none;
  filter: none;
  transform: none;
}
[data-theme="flat"] .btn-primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}
[data-theme="flat"] .btn-outline-secondary:hover,
[data-theme="flat"] .btn-outline-primary:hover {
  box-shadow: none;
  transform: none;
}
[data-theme="flat"] .brand-mark {
  background: var(--accent);
  box-shadow: none;
}

/* Article-card unread indicator: keep the amber side-bar (it's a useful
   signal, not a depth cue). */
[data-theme="flat"] .article-card:hover,
[data-theme="flat"] .article-tile:hover,
[data-theme="flat"] .feed-card:hover,
[data-theme="flat"] .stat-card:hover,
[data-theme="flat"] .recent-card:hover {
  transform: none;
}

/* =====================================================================
   Colorful (flat) theme
   ---------------------------------------------------------------------
   Same structural rules as Flat (no blur, no shadow, no orbs), but with
   a deliberately playful palette inspired by a Bauhaus-flavoured mobile
   mockup: dusty mauve canvas behind cream cards, with coral as the
   primary action colour and a teal accent for secondary highlights.

   Selector specificity reuses the flat reset for shadows/blur by
   *not* re-listing every component — we duplicate the structural
   overrides so colorful works standalone (without depending on flat
   being applied at the same time).
   ===================================================================== */
[data-theme="colorful"][data-bs-theme="light"] {
  /* Canvas: warm cream/sand — the dominant surface on the mockup.
     The coral is used SPARINGLY as accent (buttons, brand panel,
     unread strip) so it punctuates the cream rather than drowning it.
     Sage green sits alongside as a cool counterpoint. */
  --bg: #fbe8d4;             /* cream/sand canvas */
  --bg-soft: #f5dcc0;
  --surface: #fff7ec;        /* near-white cream cards */
  --surface-2: #fde2cf;      /* slight peach for secondary surfaces */
  --surface-hover: #f9d6b9;
  --border: #ecd5bc;
  --border-strong: #d9b896;
  --text: #2a1f1c;
  --text-muted: #6b4a3d;
  --text-subtle: #9a7a6a;
  --accent: #ef7a59;         /* coral primary — used as accent only */
  --accent-strong: #d8624a;
  --accent-soft: #ffe1d5;
  --accent-text: #8a3520;
  --danger: #c0392b;
  --warning: #d97706;
  --success: #5b9279;        /* sage green from the mockup leaves */
  --info: #4f9a93;           /* muted teal counterpoint */
  --mark-bg: rgba(239, 122, 89, .25);

  /* Glass tokens neutralised — colorful is a flat theme. */
  --glass-fill: var(--surface);
  --glass-fill-strong: var(--surface-2);
  --glass-border-top: var(--border);
  --glass-border-bottom: var(--border);
  --glass-blur: 0px;
  --glass-blur-strong: 0px;
  --glass-shadow: none;
  --glass-specular: none;
  --glass-shine: transparent;
  --glass-input-bg: #fff1e1;
  --glass-input-border: var(--border);
  --popover-fill: var(--surface);
  --glass-orb-1: transparent;
  --glass-orb-2: transparent;
  --glass-orb-3: transparent;
  --glass-bg-gradient: var(--bg);
  --app-bg-gradient: var(--bg);
  --app-orb-1: transparent;
  --app-orb-2: transparent;
}
[data-theme="colorful"][data-bs-theme="dark"] {
  /* Canvas: deep petrol/teal — the dominant dark colour on the
     onboarding mockup.  Mustard-orange is the primary accent (buttons,
     CTAs), with mint green + bright blue as playful secondary pops.
     Text is warm cream so it feels editorial, not techy. */
  --bg: #0e3540;             /* deep petrol teal */
  --bg-soft: #0f3e4b;
  --surface: #164554;         /* slightly lifted teal for cards */
  --surface-2: #1c5161;
  --surface-hover: #235d6e;
  --border: #2c6b7c;
  --border-strong: #3f8497;
  --text: #fbe8d4;            /* cream text for warmth */
  --text-muted: #c5b69e;
  --text-subtle: #8a7e6b;
  --accent: #f0a13a;          /* mustard-orange primary — bright CTA */
  --accent-strong: #f5b341;
  --accent-soft: #3d2a14;
  --accent-text: #ffd089;
  --danger: #e85f3f;          /* coral red still works on teal */
  --warning: #f2c528;         /* bright yellow from the mockup */
  --success: #65c89e;         /* mint green pop */
  --info: #2da4cb;            /* bright blue pop */
  --mark-bg: rgba(240, 161, 58, .28);

  --glass-fill: var(--surface);
  --glass-fill-strong: var(--surface-2);
  --glass-border-top: var(--border);
  --glass-border-bottom: var(--border);
  --glass-blur: 0px;
  --glass-blur-strong: 0px;
  --glass-shadow: none;
  --glass-specular: none;
  --glass-shine: transparent;
  --glass-input-bg: var(--surface-2);
  --glass-input-border: var(--border);
  --popover-fill: var(--surface);
  --glass-orb-1: transparent;
  --glass-orb-2: transparent;
  --glass-orb-3: transparent;
  --glass-bg-gradient: var(--bg);
  --app-bg-gradient: var(--bg);
  --app-orb-1: transparent;
  --app-orb-2: transparent;
}

/* Hide ambient/atmospheric pseudo-elements (same as flat). */
[data-theme="colorful"] .auth-shell::before,
[data-theme="colorful"] .auth-shell::after,
[data-theme="colorful"] .app-shell::before,
[data-theme="colorful"] .app-shell::after,
[data-theme="colorful"] .auth-card::after,
[data-theme="colorful"] .article-tile::before,
[data-theme="colorful"] .ai-digest-hero::before {
  display: none !important;
}

/* Strip hardcoded shadows on every surface that has one. */
[data-theme="colorful"] .topbar,
[data-theme="colorful"] .dropdown-menu,
[data-theme="colorful"] .offcanvas,
[data-theme="colorful"] .modal-content,
[data-theme="colorful"] .article-card,
[data-theme="colorful"] .article-card:hover,
[data-theme="colorful"] .article-tile,
[data-theme="colorful"] .article-tile:hover,
[data-theme="colorful"] .feed-card,
[data-theme="colorful"] .feed-card:hover,
[data-theme="colorful"] .stat-card,
[data-theme="colorful"] .stat-card:hover,
[data-theme="colorful"] .recent-card,
[data-theme="colorful"] .recent-card:hover,
[data-theme="colorful"] .ai-digest-hero,
[data-theme="colorful"] .surface,
[data-theme="colorful"] .alert,
[data-theme="colorful"] .reader-detail,
[data-theme="colorful"] .article-overlay,
[data-theme="colorful"] .pagination .page-link:hover,
[data-theme="colorful"] .nav-pills-app a.active,
[data-theme="colorful"] .view-toggle a.active,
[data-theme="colorful"] .admin-tab.active,
[data-theme="colorful"] .sidebar-item.active,
[data-theme="colorful"] .ai-digest-hero-head:hover {
  box-shadow: none !important;
}

/* Buttons: solid coral with the brighter shade on hover. */
[data-theme="colorful"] .btn-primary,
[data-theme="colorful"] .btn-primary:focus,
[data-theme="colorful"] .btn-primary:focus-visible,
[data-theme="colorful"] .btn-primary:active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: none;
  filter: none;
  transform: none;
}
[data-theme="colorful"] .btn-primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  box-shadow: none;
  transform: none;
}
[data-theme="colorful"] .btn-outline-secondary:hover,
[data-theme="colorful"] .btn-outline-primary:hover {
  box-shadow: none;
  transform: none;
}
[data-theme="colorful"] .brand-mark {
  background: var(--accent);
  color: #fff;
  box-shadow: none;
}
[data-theme="colorful"] .article-card:hover,
[data-theme="colorful"] .article-tile:hover,
[data-theme="colorful"] .feed-card:hover,
[data-theme="colorful"] .stat-card:hover,
[data-theme="colorful"] .recent-card:hover {
  transform: none;
}

/* Teal as a secondary accent — only used where we already render an
   "info" colour, so dropping --info on this theme is enough.  Links
   keep the warm coral so the brand identity stays consistent. */
[data-theme="colorful"] .status-pill.ok {
  color: var(--success);
  background: color-mix(in srgb, var(--success) 16%, var(--surface));
}
[data-theme="colorful"] a {
  color: var(--accent-strong);
}
[data-theme="colorful"] a:hover {
  color: var(--accent);
}

/* =====================================================================
   Colorful LIGHT — mint accents
   ---------------------------------------------------------------------
   The mockup is built on a three-colour rhythm: cream canvas + coral
   accents + a soft mint green that catches the eye between them.  We
   bring that mint into three load-bearing places:
     1. The AI digest hero — already "the special one" on the dashboard
     2. Empty thumbnails — when an article has no image, the placeholder
        gets a mint wash instead of plain peach
     3. Hover backgrounds — feed/article/recent cards lift to mint on
        hover for a playful interaction signal
   The mint is exposed as a token so we can tweak it in one place.
   ===================================================================== */
[data-theme="colorful"][data-bs-theme="light"] {
  --colorful-mint: #94d0ce;
  --colorful-mint-soft: #c2e3e2;
  --colorful-mint-strong: #6cb9b6;
}

/* AI digest hero — solid mint surface, dark text, coral wash on top */
[data-theme="colorful"][data-bs-theme="light"] .ai-digest-hero {
  background: var(--colorful-mint-soft);
  border-color: var(--colorful-mint-strong);
}
[data-theme="colorful"][data-bs-theme="light"] .ai-digest-hero::before {
  /* Replace the amber wash with a soft coral one so the third colour
     still nudges in. */
  background:
    radial-gradient(60% 80% at 100% 0%,
      color-mix(in srgb, var(--accent) 22%, transparent),
      transparent 55%);
  display: block !important;       /* re-enable; flat-reset hides it */
}

/* Empty-thumbnail placeholder = mint wash with subtle gradient.
   Real <img class="tile-image"> overrides this automatically. */
[data-theme="colorful"][data-bs-theme="light"] .article-tile .tile-image {
  background:
    linear-gradient(135deg,
      var(--colorful-mint-soft) 0%,
      var(--colorful-mint) 100%);
  color: #2a4d4c;
}

/* Hover backgrounds — cards lift to mint to advertise interactivity */
[data-theme="colorful"][data-bs-theme="light"] .article-card:hover,
[data-theme="colorful"][data-bs-theme="light"] .article-tile:hover,
[data-theme="colorful"][data-bs-theme="light"] .feed-card:hover,
[data-theme="colorful"][data-bs-theme="light"] .stat-card:hover,
[data-theme="colorful"][data-bs-theme="light"] .recent-card:hover {
  background: var(--colorful-mint-soft);
  border-color: var(--colorful-mint-strong);
}
[data-theme="colorful"][data-bs-theme="light"] .sidebar-item:hover {
  background: var(--colorful-mint-soft);
}
[data-theme="colorful"][data-bs-theme="light"] .dropdown-item:hover,
[data-theme="colorful"][data-bs-theme="light"] .dropdown-item:focus {
  background: var(--colorful-mint-soft);
  color: var(--text);
}

/* ---------- Reset & base ------------------------------------------------ */
* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-feature-settings: "cv11", "ss01";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: -.005em;
  overflow-x: hidden;            /* defensief — voorkom horizontale scroll-overflow */
}

img { max-width: 100%; height: auto; }

/* Lange titels / URLs nooit horizontaal laten uitsteken. */
.article-title,
.article-meta,
.feed-url,
.surface a {
  overflow-wrap: anywhere;
  word-break: break-word;
}

a { color: var(--accent); text-decoration: none; transition: color var(--transition-fast); }
a:hover { color: var(--accent-strong); }

code, pre, kbd { font-family: var(--font-mono); font-size: .9em; }
code {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: .1em .35em;
  color: var(--accent-text);
}

mark {
  background: var(--mark-bg);
  color: inherit;
  padding: 0 .15em;
  border-radius: 3px;
  font-weight: 600;
}

::selection { background: var(--accent); color: white; }

/* ---------- App shell --------------------------------------------------- */
.app-shell {
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  min-height: 100vh;
  /* Prevents horizontal scroll when an overwide row sneaks in — extra
     safety net on top of the per-row min-width:0 fixes. */
  overflow-x: hidden;
  background: var(--app-bg-gradient);
  isolation: isolate;
}
/* Two large blurred orbs in the background for glass cards to refract
   against.  Subtler than the auth shell — this is for daily use, not
   a first impression.  position: fixed so they don't move when the
   page scrolls. */
.app-shell::before,
.app-shell::after {
  content: "";
  position: fixed;
  width: 48vmax;
  height: 48vmax;
  border-radius: 50%;
  filter: blur(90px);
  opacity: .9;
  pointer-events: none;
  z-index: 0;
}
.app-shell::before {
  top: -18vmax;
  right: -14vmax;
  background: var(--app-orb-1);
}
.app-shell::after {
  bottom: -22vmax;
  left: -14vmax;
  background: var(--app-orb-2);
}
/* App-shell children live on the layer above the orbs.
   IMPORTANT: do NOT apply this to .offcanvas — Bootstrap renders the
   drawer with position:fixed (off-screen until opened). Overriding it
   to position:relative pulls the entire drawer into the flow and pushes
   main content far below the fold.
   The topbar gets its own rule with z-index 1080 because the
   `.app-shell > header` selector has higher CSS specificity than
   `.topbar` and would otherwise force the topbar to z-index 1, putting
   it below dashboard cards whose backdrop-filter creates their own
   stacking contexts. */
.app-shell > main,
.app-shell > footer { position: relative; z-index: 1; }
.app-shell > header { position: relative; z-index: 1080; }

.topbar {
  position: sticky;
  top: 0;
  /* Bumped above dashboard items (which can create their own stacking
     contexts via transform/isolation) so the account dropdown menu
     always renders above page content. Matches Bootstrap's
     dropdown/popover layer level. */
  z-index: 1080;
  /* Heavy glass — this is the most visible "floating" surface in the app. */
  background: var(--glass-fill-strong);
  backdrop-filter: saturate(180%) blur(22px);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  border-bottom: 1px solid transparent;
  /* Gradient bottom border via background-image trick. Keeps the seam
     between topbar and page from looking flat. */
  background-image:
    linear-gradient(to bottom,
      transparent calc(100% - 1px),
      var(--glass-border-bottom) 100%),
    var(--glass-fill-strong);
  /* Soft shadow underneath so the topbar lifts off the page bg. */
  box-shadow: 0 4px 24px rgba(0, 0, 0, .12);
  /* iOS PWA: viewport-fit=cover lets content slide under the notch.
     Push the topbar down so it sits below the status bar. */
  padding-top: env(safe-area-inset-top);
}

.topbar-inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  max-width: 1600px;
  margin: 0 auto;
  /* Respect notch/dynamic-island side-insets in landscape. */
  padding-left: max(var(--space-5), env(safe-area-inset-left));
  padding-right: max(var(--space-5), env(safe-area-inset-right));
}

/* Bottom safe-area voor de home-indicator (iPhone X+). */
.app-shell > main {
  padding-bottom: env(safe-area-inset-bottom);
}

/* iOS PWA fullscreen: de offcanvas-drawer staat fixed met top:0 en
   overlapt anders het dynamic island / de notch. Header krijgt zelfde
   safe-area-top als de topbar; left/right voor landscape-orientatie. */
.offcanvas {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}
.offcanvas-header {
  padding-top: max(var(--space-3), env(safe-area-inset-top));
}
.offcanvas-body {
  padding-bottom: max(var(--space-3), env(safe-area-inset-bottom));
}

/* Brand */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -.01em;
  color: var(--text);
  margin-right: var(--space-2);
}
.brand:hover { color: var(--text); }
.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--accent), var(--accent-strong));
  color: #1c1209;
  font-size: .9rem;
  /* Match the auth-card brand-mark: a touch of glow + inset shine so it
     reads as an actual glowing object on the frosted topbar. */
  box-shadow:
    0 4px 12px rgba(245, 158, 11, .35),
    0 1px 0 rgba(255, 255, 255, .35) inset;
}
.brand-name { display: inline-flex; align-items: baseline; gap: .15em; }
.brand-name .accent { color: var(--accent); }

/* Nav links — glass pill container with a frosted active state. */
.nav-pills-app {
  display: inline-flex;
  gap: var(--space-1);
  padding: 4px;
  background: var(--glass-input-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius-pill);
}
.nav-pills-app a {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  padding: .35rem .85rem;
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  font-size: .875rem;
  font-weight: 500;
  transition: all var(--transition-fast);
}
.nav-pills-app a:hover { color: var(--text); background: var(--glass-input-bg); }
.nav-pills-app a.active {
  background: var(--glass-fill-strong);
  color: var(--text);
  box-shadow:
    0 4px 12px rgba(245, 158, 11, .18),
    0 1px 0 var(--glass-border-top) inset;
}

/* Topbar tools */
.topbar-spacer { flex: 1; }

.topbar-search {
  flex: 0 1 320px;
  position: relative;
}
.topbar-search input {
  width: 100%;
  background: var(--glass-input-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius-pill);
  color: var(--text);
  padding: .45rem 1rem .45rem 2.25rem;
  font-size: .875rem;
  transition: all var(--transition-fast);
}
.topbar-search input::placeholder { color: var(--text-subtle); }
.topbar-search input:focus {
  outline: none;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--glass-input-bg) 80%, var(--accent-soft));
  box-shadow:
    0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent),
    0 1px 0 rgba(255, 255, 255, .1) inset;
}
.topbar-search .icon {
  position: absolute;
  left: .85rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-subtle);
  pointer-events: none;
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  flex: 0 0 36px;                  /* nooit krimpen in flex-container */
  padding: 0;
  border-radius: var(--radius);
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-muted);
  cursor: pointer;
  position: relative;
  transition: all var(--transition-fast);
}

.icon-btn img,
.avatar-circle {
  display: block;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.avatar-circle {
  /* Bv. in topbar-dropdown trigger.  Inline width/height blijven werken. */
  aspect-ratio: 1 / 1;
}
.icon-btn:hover {
  background: var(--glass-input-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--text);
}
.icon-btn .badge-dot {
  position: absolute;
  top: 6px; right: 6px;
  width: 16px; height: 16px;
  background: var(--danger);
  color: #fff;
  border-radius: var(--radius-pill);
  font-size: .65rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  border: 2px solid var(--bg);
}

/* ---------- Page container --------------------------------------------- */
.page {
  max-width: 1600px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
}
.page-narrow { max-width: 880px; }
.page-reading { max-width: 720px; }

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid transparent;
  /* Gradient bottom border — fades in from the page edges, brightest in
     the middle. Subtle but tells the user "header ends here". */
  background-image: linear-gradient(
    to right,
    transparent,
    var(--glass-border-bottom) 20%,
    var(--glass-border-bottom) 80%,
    transparent
  );
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: bottom;
}
.page-title {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -.02em;
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.page-subtitle {
  color: var(--text-muted);
  font-size: .9rem;
  margin-top: var(--space-1);
}

/* ---------- Surfaces (frosted glass) ----------------------------------
   The default .surface used by most form-cards and panels.  Same glass
   recipe as .auth-card but with less aggressive blur because there can
   be many surfaces on one page (settings, admin lists, etc.) and blur
   is GPU-expensive. */
.surface {
  position: relative;
  background: var(--glass-fill);
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
  isolation: isolate;
}
.surface::before {
  /* Gradient border — same trick as .auth-card. */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    150deg,
    var(--glass-border-top) 0%,
    rgba(255, 255, 255, .04) 50%,
    var(--glass-border-bottom) 100%
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 1;
}
.surface > * { position: relative; z-index: 2; }
.surface-body { padding: var(--space-5); }
.surface-head {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--glass-border-bottom);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
}
.surface-head h2 {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
  letter-spacing: -.01em;
}

/* ---------- Buttons (overrides Bootstrap) ------------------------------ */
.btn {
  border-radius: var(--radius);
  font-weight: 500;
  padding: .5rem 1rem;
  transition: all var(--transition-fast);
  border: 1px solid transparent;
  letter-spacing: -.005em;
}
.btn-sm { padding: .35rem .7rem; font-size: .825rem; }

/* Bootstrap has a lot of button states that all need to be forced to our
   accent so its default blues don't sneak back in on :active/:focus.
   Primary is a glowing amber gradient — the "bright object" on every
   frosted surface. */
.btn-primary,
.btn-primary:focus,
.btn-primary:focus-visible,
.btn-primary:active,
.btn-primary.active,
.btn-primary:not(:disabled):not(.disabled):active,
.btn-primary:not(:disabled):not(.disabled).active,
.show > .btn-primary.dropdown-toggle {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-strong) 100%);
  border: 1px solid color-mix(in srgb, var(--accent) 50%, transparent);
  color: #1c1209;
  font-weight: 600;
  box-shadow:
    0 6px 18px rgba(245, 158, 11, .28),
    0 1px 0 rgba(255, 255, 255, .35) inset,
    0 -1px 0 rgba(0, 0, 0, .12) inset;
  --bs-btn-active-bg: var(--accent-strong);
  --bs-btn-active-border-color: var(--accent-strong);
  --bs-btn-hover-bg: var(--accent-strong);
  --bs-btn-hover-border-color: var(--accent-strong);
  --bs-btn-focus-shadow-rgb: 217, 119, 6;
}
.btn-primary:hover {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-strong) 100%);
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
  color: #1c1209;
  transform: translateY(-1px);
  filter: brightness(1.04);
  box-shadow:
    0 10px 26px rgba(245, 158, 11, .4),
    0 1px 0 rgba(255, 255, 255, .45) inset,
    0 -1px 0 rgba(0, 0, 0, .15) inset;
}
.btn-primary:focus,
.btn-primary:focus-visible {
  outline: none;
  box-shadow:
    0 6px 18px rgba(245, 158, 11, .28),
    0 0 0 4px color-mix(in srgb, var(--accent) 28%, transparent),
    0 1px 0 rgba(255, 255, 255, .35) inset;
}
.btn-primary:active,
.btn-primary:not(:disabled):not(.disabled):active {
  background: linear-gradient(135deg, var(--accent-strong) 0%, var(--accent) 100%);
  color: #1c1209;
  transform: translateY(0);
  filter: brightness(.96);
}

/* Outline buttons → frosted glass tile. */
.btn-outline-secondary,
.btn-outline-primary,
.btn-outline-danger,
.btn-outline-secondary:focus,
.btn-outline-primary:focus,
.btn-outline-danger:focus {
  background: var(--glass-input-bg);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--text);
  border-color: var(--glass-input-border);
  --bs-btn-active-bg: var(--glass-fill-strong);
  --bs-btn-active-color: var(--text);
  --bs-btn-active-border-color: var(--glass-border-top);
  --bs-btn-focus-shadow-rgb: 168, 162, 153;
}
.btn-outline-secondary:hover,
.btn-outline-primary:hover {
  background: var(--glass-fill-strong);
  border-color: var(--glass-border-top);
  color: var(--text);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .12);
}
.btn-outline-secondary:active,
.btn-outline-primary:active,
.btn-outline-secondary:not(:disabled):not(.disabled):active,
.btn-outline-primary:not(:disabled):not(.disabled):active {
  background: var(--glass-fill-strong);
  border-color: var(--glass-border-top);
  color: var(--text);
}
.btn-outline-secondary:focus-visible,
.btn-outline-primary:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

.btn-outline-danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 40%, var(--border));
  --bs-btn-active-bg: color-mix(in srgb, var(--danger) 14%, var(--surface));
  --bs-btn-active-color: var(--danger);
  --bs-btn-active-border-color: var(--danger);
}
.btn-outline-danger:hover,
.btn-outline-danger:active,
.btn-outline-danger:not(:disabled):not(.disabled):active {
  background: color-mix(in srgb, var(--danger) 12%, var(--surface));
  border-color: var(--danger);
  color: var(--danger);
}
.btn-outline-danger:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 30%, transparent);
}

/* Iconen-knoppen in de topbar mogen ook geen Bootstrap-blauw vangen. */
.icon-btn:focus,
.icon-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* ---------- Dashboard-layout ------------------------------------------- */
.dashboard-grid {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-4);
}
@media (max-width: 991.98px) {
  .dashboard-grid { grid-template-columns: 1fr; }
}

/* ---------- Dashboard editorial layout --------------------------------
   Greeting + inline stat ribbon (instead of the old block of stat-cards),
   an AI-digest hero, and themed section headers above each tile-row. */
.dashboard-header .stat-ribbon {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: .875rem;
  color: var(--text-muted);
}
.dashboard-header .stat-ribbon strong {
  color: var(--text);
  font-weight: 700;
  font-feature-settings: "tnum";  /* tabular numerals so digits align */
  margin-right: .25rem;
}
.dashboard-header .stat-ribbon .dot {
  color: var(--text-subtle);
}

/* AI digest hero: bigger, more prominent than the old collapsible panel.
   The body uses the same markdown styling but at a comfortable reading
   size so it reads like an editor's morning brief.
   Uses <details> so the user can collapse it without JS. */
.ai-digest-hero {
  position: relative;
  margin-bottom: var(--space-6);
  padding: var(--space-5) var(--space-6);
  border-radius: 20px;
  background: var(--glass-fill-strong);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid var(--glass-border-top);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
  isolation: isolate;
}
.ai-digest-hero::before {
  /* A soft amber wash in the top-right so it reads as "the special one"
     among the dashboard sections. */
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(60% 80% at 100% 0%,
      color-mix(in srgb, var(--accent) 14%, transparent),
      transparent 55%);
  pointer-events: none;
  z-index: 0;
}
.ai-digest-hero > * { position: relative; z-index: 1; }

.ai-digest-hero-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  cursor: pointer;
  user-select: none;
  list-style: none;  /* hide the default <summary> triangle */
}
.ai-digest-hero-head::-webkit-details-marker { display: none; }
.ai-digest-hero-head::marker { content: ""; }

.ai-digest-hero-kicker {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
}
.ai-digest-hero-head h2 {
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -.02em;
  margin: .15rem 0 0;
  color: var(--text);
}
.ai-digest-hero-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}
.ai-digest-hero-chev {
  color: var(--text-muted);
  font-size: 1rem;
  transition: transform var(--transition-fast);
}
details.ai-digest-hero[open] .ai-digest-hero-chev { transform: rotate(180deg); }
/* When collapsed the head doesn't need its bottom margin. */
details.ai-digest-hero:not([open]) .ai-digest-hero-head { margin-bottom: 0; }

.ai-digest-hero-body {
  font-size: .95rem;
  line-height: 1.6;
  color: var(--text);
}
.ai-digest-hero-body > p:first-child { margin-top: 0; }
.ai-digest-hero-body > p:last-child  { margin-bottom: 0; }

/* Dashboard sections: Featured / Favorites. Headers have a kicker style
   so they stand apart from .surface-head (which is for boxed surfaces). */
.dashboard-section {
  margin-bottom: var(--space-6);
}
.dashboard-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-2);
}
.dashboard-section-head h2 {
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -.01em;
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: .4em;
}
.dashboard-section-head .small { font-size: .825rem; color: var(--text-muted); }

/* Featured + Favorites grids: explicit column counts per breakpoint so
   the layout always fills cleanly (no orphan tile in an empty row).
   Featured = 8 items in 4 cols → 2 rows. Favorites = 4 items in 4 cols
   → 1 row.  Both divide evenly at every breakpoint. */
.dashboard-featured.article-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4);
}
@media (max-width: 1199.98px) {
  .dashboard-featured.article-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 575.98px) {
  .dashboard-featured.article-grid { grid-template-columns: 1fr; }
}

.dashboard-favorites.article-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4);
}
@media (max-width: 1199.98px) {
  .dashboard-favorites.article-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 575.98px) {
  .dashboard-favorites.article-grid { grid-template-columns: 1fr; }
}

/* Recent-articles grid: 2-col on wide, 1-col on narrow.  Each row is a
   compact card with a small thumbnail (left) + title + meta.  Richer
   than text rows but lighter than the Featured tiles, providing a
   third density tier on the dashboard. */
.recent-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}
@media (max-width: 991.98px) {
  .recent-grid { grid-template-columns: 1fr; }
}

.recent-card {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-3);
  border-radius: 14px;
  background: var(--glass-fill);
  backdrop-filter: blur(12px) saturate(150%);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  border: 1px solid var(--glass-input-border);
  text-decoration: none;
  color: inherit;
  transition: background var(--transition-fast),
              border-color var(--transition-fast),
              transform var(--transition-fast),
              box-shadow var(--transition-fast);
  position: relative;
}
.recent-card:hover {
  background: var(--glass-fill-strong);
  border-color: var(--glass-border-top);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .12);
  color: inherit;
}
.recent-card.is-unread::before {
  content: "";
  position: absolute;
  left: -1px;
  top: 14px;
  bottom: 14px;
  width: 3px;
  background: var(--accent);
  border-radius: var(--radius-pill);
}

.recent-card-thumb {
  width: 84px;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface-2);
  flex-shrink: 0;
}
.recent-card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.recent-card-thumb-fallback {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
  font-size: 1.5rem;
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--text-muted) 10%, var(--surface-2)),
      var(--surface-2));
}

.recent-card-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.recent-card-title {
  font-weight: 600;
  font-size: .92rem;
  line-height: 1.35;
  letter-spacing: -.005em;
  color: var(--text);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.recent-card.is-read .recent-card-title { color: var(--text-muted); font-weight: 500; }
.recent-card:hover .recent-card-title { color: var(--accent); }
.recent-card-meta {
  font-size: .75rem;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: .35em;
  flex-wrap: wrap;
}
.recent-card-meta .dot { color: var(--text-subtle); }

/* ---------- Stat cards (dashboard) ------------------------------------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.stat-card {
  background: var(--glass-fill);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  position: relative;
  overflow: hidden;
  transition: all var(--transition);
  box-shadow: 0 6px 16px rgba(0, 0, 0, .12);
}
.stat-card:hover {
  border-color: var(--glass-border-top);
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(0, 0, 0, .18);
}
.stat-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--stat-color, var(--accent));
}
.stat-card .stat-label {
  color: var(--text-muted);
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.stat-card .stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  margin-top: var(--space-1);
  letter-spacing: -.02em;
}
.stat-card .stat-icon {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  font-size: 1.25rem;
  color: var(--stat-color, var(--accent));
  opacity: .25;
}

/* ---------- Two-column reader layout ----------------------------------
   The Articles page uses the same layout whether showing the list/grid
   or showing a single article: [filter sidebar | swappable content].
   When an article is selected (?article=ID or via JS click), the
   article-fragment replaces the list/grid in the right column — the
   filters sidebar stays put. */
.reader {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: var(--space-5);
  align-items: start;
}
@media (max-width: 900px) {
  .reader { grid-template-columns: 1fr; }
}

.reader-sidebar {
  position: sticky;
  top: 80px;
  max-height: calc(100vh - 100px);
  overflow-y: auto;
  padding-right: var(--space-2);
}
.reader-sidebar h3 {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text-subtle);
  font-weight: 700;
  margin: var(--space-5) 0 var(--space-2);
  padding-left: var(--space-3);
}
.reader-sidebar h3:first-child { margin-top: 0; }

/* ---------- Alerts: category groups -----------------------------------
   Each category renders as a <details> with a header that shows the
   group's overall status and a bulk pause/resume button. */
.alert-group {
  border-bottom: 1px solid var(--glass-border-bottom);
}
.alert-group:last-of-type { border-bottom: none; }
.alert-group-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  cursor: pointer;
  user-select: none;
  list-style: none;
  background: var(--glass-input-bg);
}
.alert-group-head::-webkit-details-marker { display: none; }
.alert-group-head::marker { content: ""; }
.alert-group-head:hover { background: var(--glass-fill-strong); }
.alert-group-chev {
  color: var(--text-subtle);
  font-size: .85em;
  transition: transform var(--transition-fast);
}
details.alert-group:not([open]) .alert-group-chev { transform: rotate(-90deg); }
.alert-group-name {
  font-weight: 600;
  font-size: .95rem;
  color: var(--text);
}
.alert-group-count {
  font-size: .75rem;
  color: var(--text-muted);
  font-feature-settings: "tnum";
}
.alert-group-bulk {
  margin-left: auto;
}

/* ---------- Admin tab bar ----------------------------------------------
   Sits at the top of every admin page (rendered by admin/_base.html)
   so the five admin sections (Users / Settings / Mail / Scheduler /
   Audit) feel like one page with tabs.  Real <a href> links — clicking
   navigates fully so each tab's data is fetched on demand instead of
   loading all five at once. */
.admin-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: 4px;
  margin-bottom: var(--space-5);
  background: var(--glass-input-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius-pill);
  width: fit-content;
  max-width: 100%;
  overflow-x: auto;
}
.admin-tab {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .45rem .95rem;
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  font-size: .85rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: all var(--transition-fast);
}
.admin-tab:hover { color: var(--text); background: var(--glass-input-bg); }
.admin-tab.active {
  background: var(--glass-fill-strong);
  color: var(--text);
  font-weight: 600;
  box-shadow:
    0 4px 10px rgba(245, 158, 11, .14),
    0 1px 0 var(--glass-border-top) inset;
}

/* Collapsible sidebar groups (Categories, Feeds, Tags, Auto-labels).
   Native <details>/<summary>, no JS. Chevron rotates on open.
   Every group gets the same top margin so the spacing between View →
   Categories is the same as between Feeds → Tags → Auto-labels. */
.sidebar-group { margin-top: var(--space-5); }
.sidebar-group-head {
  display: flex;
  align-items: center;
  gap: .5em;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text-subtle);
  font-weight: 700;
  padding: var(--space-1) var(--space-3);
  cursor: pointer;
  user-select: none;
  list-style: none;  /* hide default disclosure triangle */
  border-radius: 8px;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.sidebar-group-head::-webkit-details-marker { display: none; }
.sidebar-group-head::marker { content: ""; }
.sidebar-group-head:hover { background: var(--glass-input-bg); color: var(--text-muted); }
.sidebar-group-head .sidebar-group-chev {
  font-size: .85em;
  color: var(--text-subtle);
  transition: transform var(--transition-fast);
}
details.sidebar-group:not([open]) .sidebar-group-chev { transform: rotate(-90deg); }
.sidebar-group-head .sidebar-group-count {
  margin-left: auto;
  font-size: .65rem;
  background: var(--glass-input-bg);
  color: var(--text-muted);
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}
.sidebar-group-body { margin-top: var(--space-1); }

/* ---------- Article overlay (slide-over reader) -----------------------
   When the user clicks an article in the list, this overlay slides in
   from the right and covers the article-body area — but NOT the page-
   header (filter title + view-toggle + mark-all-as-read).  The list
   underneath is hidden via display:none on .article-list/.article-grid
   so its text doesn't bleed through the glass.

   Structure in list.html:
     <section #content-pane>
       <button.filters-trigger>          (mobile only)
       <div.page-header>                  (always visible)
       <div #content-body>                (positioning context for overlay)
         <div.article-list/grid>          (display:none when overlay open)
         <nav.pagination>                  (display:none when overlay open)
         <div.article-overlay>             (slides in from right)
       </div>
     </section>
*/
#content-body { position: relative; }

/* When the overlay is open, content-body grows to match the article's
   natural height (set by JS via ResizeObserver). The CSS fallback
   min-height covers the no-JS path. */
#content-body.has-overlay {
  min-height: calc(100vh - 220px);
}
/* Hide the list/grid/pagination while the overlay is on so the article
   text isn't competing with bleed-through from the background. */
#content-body.has-overlay > .article-list,
#content-body.has-overlay > .article-grid,
#content-body.has-overlay > nav {
  display: none;
}

/* Closing animation: when the overlay starts sliding out, the list/grid
   underneath gets re-shown.  Without help it would pop in instantly.
   The .is-closing class is set by JS for the duration of the slide-out
   and triggers a synchronised fade-in + tiny lift-up on the list. */
@keyframes openreadr-list-restore {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
#content-body.is-closing > .article-list,
#content-body.is-closing > .article-grid,
#content-body.is-closing > nav {
  animation: openreadr-list-restore 340ms cubic-bezier(.22, .82, .25, 1);
}
@media (prefers-reduced-motion: reduce) {
  #content-body.is-closing > .article-list,
  #content-body.is-closing > .article-grid,
  #content-body.is-closing > nav {
    animation: none;
  }
}

.article-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* Min-height so short articles still look like a full card; no max
     and no overflow so the article renders in its natural height and
     the page scrolls if it's taller than the viewport. JS keeps
     #content-body sized so the section fits. */
  min-height: calc(100vh - 220px);

  background: var(--glass-fill-strong);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  border: 1px solid var(--glass-border-top);
  border-radius: 20px;
  box-shadow:
    0 30px 80px rgba(0, 0, 0, .45),
    0 12px 28px rgba(0, 0, 0, .25);
  padding: var(--space-6);
  z-index: 10;
  /* Sit just off-screen to the right until .is-open is toggled. The
     +40px buffer covers the box-shadow so no edge bleeds.
     Slide AND opacity share the same duration + easing so open/close
     feel exactly symmetric. */
  transform: translateX(calc(100% + 40px));
  transition: transform 340ms cubic-bezier(.22, .82, .25, 1),
              opacity   340ms cubic-bezier(.22, .82, .25, 1);
  opacity: 0;
  will-change: transform, opacity;
  pointer-events: none;
}
.article-overlay.is-open {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}

/* The reading-view inside the overlay uses a comfortable column width. */
.article-overlay .reading-view {
  max-width: 820px;
  margin: 0 auto;
}

/* Respect reduced-motion preference: no slide, just fade. */
@media (prefers-reduced-motion: reduce) {
  .article-overlay {
    transform: none;
    transition: opacity 120ms ease;
  }
}

.sidebar-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: .55rem var(--space-3);
  border-radius: 10px;
  color: var(--text-muted);
  font-size: .875rem;
  font-weight: 500;
  text-decoration: none;
  margin-bottom: 2px;
  transition: all var(--transition-fast);
}
.sidebar-item:hover {
  background: var(--glass-input-bg);
  color: var(--text);
}
.sidebar-item.active {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--accent) 18%, transparent),
    color-mix(in srgb, var(--accent) 6%, transparent));
  color: var(--accent-text);
  font-weight: 600;
  box-shadow:
    0 4px 12px rgba(245, 158, 11, .14),
    0 1px 0 var(--glass-border-top) inset;
}
.sidebar-item .count {
  margin-left: auto;
  font-size: .75rem;
  background: var(--glass-input-bg);
  color: var(--text-muted);
  padding: 1px 8px;
  border-radius: var(--radius-pill);
}
.sidebar-item.active .count { background: rgba(0,0,0,.08); color: var(--accent-text); }
[data-bs-theme="dark"] .sidebar-item.active .count { background: rgba(255,255,255,.12); }

/* ---------- Article cards ---------------------------------------------- */
.article-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.article-card {
  /* Light frost — many rows on a page, so we use a smaller blur and
     skip backdrop-filter on hover so scrolling stays snappy. */
  background: var(--glass-fill);
  backdrop-filter: blur(10px) saturate(150%);
  -webkit-backdrop-filter: blur(10px) saturate(150%);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  display: grid;
  /* minmax(0, 1fr) instead of 1fr — otherwise the first column gets
     implicit min-width:auto and a long URL/title pushes the card past
     the viewport. */
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: start;
  transition: border-color var(--transition-fast), background var(--transition-fast), box-shadow var(--transition-fast);
  position: relative;
}
/* Text cell may shrink + long strings (URLs, hashes) may break at any position. */
.article-card > div:first-child { min-width: 0; }
.article-card:hover {
  border-color: var(--glass-border-top);
  background: var(--glass-fill-strong);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .14);
}
.article-card.is-unread::before {
  content: "";
  position: absolute;
  left: -1px;
  top: 20px;
  bottom: 20px;
  width: 3px;
  background: var(--accent);
  border-radius: var(--radius-pill);
}
.article-card.is-read .article-title { color: var(--text-muted); font-weight: 500; }

.article-title {
  font-size: 1.0625rem;
  font-weight: 600;
  margin: 0;
  letter-spacing: -.01em;
  line-height: 1.35;
  display: block;
  color: var(--text);
  /* RSS-feeds bevatten regelmatig titels met lange URLs of CVE-IDs zonder
     spaties. Zonder break-word duwt zo'n string de hele kaart te wijd. */
  overflow-wrap: anywhere;
  word-break: break-word;
}
.article-title:hover { color: var(--accent); }

.article-meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-2);
  font-size: .8rem;
  color: var(--text-muted);
  flex-wrap: wrap;
}
.article-meta .dot { color: var(--text-subtle); }

.article-snippet {
  margin-top: var(--space-2);
  color: var(--text-muted);
  font-size: .875rem;
  line-height: 1.55;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.article-actions {
  display: inline-flex;
  flex-direction: column;
  gap: var(--space-1);
}
.article-actions button {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-subtle);
  padding: 6px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all var(--transition-fast);
  width: 32px; height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.article-actions button:hover {
  color: var(--text);
  background: var(--surface-hover);
}
.article-actions .starred { color: var(--accent); }

/* ---------- Fancy grid-weergave (article tiles) ------------------------ */
.article-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-4);
}

.article-tile {
  /* Strong frost — grid tiles are big and benefit from real glass.
     Specular highlight applied via ::before so the corner shines. */
  background: var(--glass-fill);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: all var(--transition);
  position: relative;
  text-decoration: none;
  color: inherit;
  min-width: 0;
  box-shadow: 0 8px 22px rgba(0, 0, 0, .14);
}
.article-tile::before {
  /* Subtle top-left specular wash on the whole tile. */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--glass-specular);
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 1;
  opacity: .6;
}
.article-tile > * { position: relative; z-index: 2; }
.article-tile:hover {
  border-color: var(--glass-border-top);
  transform: translateY(-2px);
  box-shadow: 0 16px 36px rgba(0, 0, 0, .22);
  color: inherit;
}
.article-tile.is-read { opacity: .72; }
.article-tile.is-unread::after {
  content: "";
  position: absolute;
  top: 12px; left: 12px;
  width: 8px; height: 8px;
  background: var(--accent);
  border-radius: 50%;
  box-shadow: 0 0 0 2px var(--surface);
}

/* Wrapper rondom de tile-image — laat ons labels als overlay positioneren
   in de rechterbovenhoek zonder dat ze de tile-uitlijning verstoren. */
.article-tile .tile-image-wrap {
  position: relative;
  width: 100%;
}
.article-tile .tile-image {
  aspect-ratio: 16 / 9;
  width: 100%;
  /* Neutral tonal gradient — used as a fallback when an article has no
     thumbnail. Previously this was an amber-tinted gradient which gave
     the whole grid a yellow cast in the preview. Real thumbnails are
     served via <img class="tile-image"> and override this background. */
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--text-muted) 10%, var(--surface-2)),
      var(--surface-2));
  object-fit: cover;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
  font-size: 2.5rem;
  border-bottom: 1px solid var(--glass-border-bottom);
}
.article-tile img.tile-image { display: block; }

/* Labels als overlay rechtsboven over de afbeelding.  Achtergrond met
   subtiele backdrop-blur zodat ze leesbaar blijven over elke afbeelding. */
.article-tile .tile-labels {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: calc(100% - 16px);
  z-index: 1;
}
.article-tile .tile-labels .label-pill {
  background: color-mix(in srgb, var(--bg) 75%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-color: color-mix(in srgb, var(--text) 25%, transparent);
  font-size: .7rem;
  padding: 2px 8px;
}

.article-tile .tile-body {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
}
.article-tile .tile-title {
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.35;
  letter-spacing: -.005em;
  color: var(--text);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
}
.article-tile.is-read .tile-title { color: var(--text-muted); font-weight: 500; }
.article-tile .tile-meta {
  font-size: .75rem;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: .35em;
  flex-wrap: wrap;
  margin-top: auto;
}

/* View-toggle in article-list — glass segmented control. */
.view-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--glass-input-bg);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius);
}
.view-toggle a {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  padding: .25rem .65rem;
  border-radius: calc(var(--radius) - 4px);
  color: var(--text-muted);
  font-size: .8rem;
  font-weight: 500;
}
.view-toggle a:hover { color: var(--text); }
.view-toggle a.active {
  background: var(--glass-fill-strong);
  color: var(--text);
  box-shadow:
    0 4px 10px rgba(245, 158, 11, .14),
    0 1px 0 var(--glass-border-top) inset;
}

/* ---------- Reading view (article detail) ------------------------------ */
.reading-view {
  max-width: 720px;
  margin: 0 auto;
}
.reading-view .article-content {
  font-family: var(--font-serif);
  font-size: 1.125rem;
  line-height: 1.75;
  color: var(--text);
}
.reading-view .article-content p { margin: 0 0 var(--space-4); }
.reading-view .article-content h1,
.reading-view .article-content h2,
.reading-view .article-content h3 {
  font-family: var(--font-sans);
  font-weight: 700;
  letter-spacing: -.02em;
  margin-top: var(--space-6);
  margin-bottom: var(--space-3);
}
.reading-view .article-content img {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  margin: var(--space-5) 0;
}
.reading-view .article-content a {
  color: var(--accent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.reading-view .article-content a:hover {
  border-bottom-color: var(--accent);
}
.reading-view .article-content blockquote {
  border-left: 3px solid var(--accent);
  padding-left: var(--space-4);
  color: var(--text-muted);
  font-style: italic;
  margin: var(--space-5) 0;
}

.reading-header {
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--border);
}
.reading-header h1 {
  font-family: var(--font-sans);
  font-size: 2rem;
  line-height: 1.2;
  letter-spacing: -.025em;
  font-weight: 800;
  margin: var(--space-2) 0 var(--space-3);
}
.reading-header .kicker {
  color: var(--accent);
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
}

/* ---------- Labels / pills --------------------------------------------- */
.label-pill {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: .75rem;
  font-weight: 500;
}
.label-pill .swatch {
  width: 8px; height: 8px;
  border-radius: 50%;
  display: inline-block;
}

/* ---------- Category filter chips -------------------------------------- */
.category-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.category-chip {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .35rem .85rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  font-size: .825rem;
  font-weight: 500;
  transition: all var(--transition-fast);
}
.category-chip:hover {
  background: var(--surface-hover);
  border-color: var(--border-strong);
  color: var(--text);
}
.category-chip.active {
  background: var(--accent-soft);
  color: var(--accent-text);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
}
.category-chip .count {
  background: rgba(0,0,0,.06);
  color: inherit;
  padding: 0 8px;
  border-radius: var(--radius-pill);
  font-size: .75em;
}
[data-bs-theme="dark"] .category-chip .count { background: rgba(255,255,255,.08); }

/* ---------- Collapsible category groups in sidebar -------------------- */
.cat-group { margin-bottom: var(--space-2); }
.cat-group-header {
  display: flex;
  align-items: center;
  gap: .35em;
  padding: .35rem var(--space-3);
  border-radius: var(--radius);
  color: var(--text-muted);
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  cursor: pointer;
  user-select: none;
  background: transparent;
  border: none;
  width: 100%;
  text-align: left;
  transition: color var(--transition-fast);
}
.cat-group-header:hover { color: var(--text); }
.cat-group-header .chev {
  transition: transform var(--transition-fast);
  font-size: .85em;
}
.cat-group.collapsed .cat-group-header .chev { transform: rotate(-90deg); }
.cat-group.collapsed .cat-group-body { display: none; }
.cat-group-count {
  margin-left: auto;
  font-size: .85em;
  background: var(--surface-2);
  color: var(--text-muted);
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  font-weight: 600;
}

/* ---------- Feed cards (grid) ------------------------------------------ */
.feed-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--space-4);
}
.feed-card {
  background: var(--glass-fill);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius);
  padding: var(--space-5);
  position: relative;
  transition: all var(--transition);
  box-shadow: 0 6px 16px rgba(0, 0, 0, .12);
}
.feed-card:hover {
  border-color: var(--glass-border-top);
  transform: translateY(-2px);
  box-shadow: 0 14px 32px rgba(0, 0, 0, .2);
}
.feed-card.is-error::before {
  content: "";
  position: absolute;
  left: 0; top: 12px; bottom: 12px;
  width: 3px;
  background: var(--danger);
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}
.feed-card .feed-title {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -.01em;
  margin: 0;
}
.feed-card .feed-url {
  color: var(--text-subtle);
  font-size: .8rem;
  margin: var(--space-1) 0 var(--space-3);
  word-break: break-all;
}
.feed-card .feed-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  color: var(--text-muted);
  font-size: .8rem;
  margin-bottom: var(--space-3);
}
.feed-card .feed-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-3);
}

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  font-size: .75rem;
  font-weight: 500;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
}
.status-pill.ok { color: var(--success); background: color-mix(in srgb, var(--success) 12%, var(--surface-2)); }
.status-pill.warn { color: var(--warning); background: color-mix(in srgb, var(--warning) 12%, var(--surface-2)); }
.status-pill.err { color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, var(--surface-2)); }
.status-pill.muted { color: var(--text-muted); }

/* PRO badge — small premium marker rendered next to features that are
   reserved for paid plans.  Subtle gold gradient so it reads as
   aspirational, not aggressive. */
.pro-badge {
  display: inline-flex;
  align-items: center;
  gap: .3em;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  color: #7a5a00;
  background: linear-gradient(135deg, #fff1c2 0%, #ffd166 100%);
  border: 1px solid rgba(184, 134, 11, .35);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .6);
  white-space: nowrap;
}
.pro-badge i { font-size: .85em; }
[data-bs-theme="dark"] .pro-badge {
  color: #2a1d00;
  background: linear-gradient(135deg, #ffe69e 0%, #ffbe3d 100%);
  border-color: rgba(255, 200, 80, .55);
}
/* Flat-theme variant: keep it visible but tone down the glow. */
[data-theme="flat"] .pro-badge {
  background: #fff3cd;
  color: #856404;
  border-color: #ffe69c;
  box-shadow: none;
}

/* ---------- Forms (frosted) -------------------------------------------- */
.form-control, .form-select {
  background: var(--glass-input-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--glass-input-border);
  color: var(--text);
  border-radius: var(--radius);
  padding: .5rem .75rem;
  transition: all var(--transition-fast);
  font-size: .9rem;
}
.form-control:focus, .form-select:focus {
  background: color-mix(in srgb, var(--glass-input-bg) 80%, var(--accent-soft));
  color: var(--text);
  border-color: var(--accent);
  box-shadow:
    0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent),
    0 1px 0 rgba(255, 255, 255, .1) inset;
  outline: none;
}
.form-control::placeholder { color: var(--text-subtle); }

.form-label {
  font-weight: 500;
  font-size: .875rem;
  margin-bottom: var(--space-2);
  color: var(--text);
}

.form-check-input {
  background-color: var(--surface);
  border-color: var(--border-strong);
  cursor: pointer;
}
.form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}
.form-check-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.form-check-label { font-size: .9rem; color: var(--text-muted); }

/* ---------- Provider grid (channel-edit integraties) ------------------- */
.provider-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3);
}
.provider-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  background: var(--glass-input-bg);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--glass-input-border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.provider-tile:hover { border-color: var(--glass-border-top); background: var(--glass-fill-strong); }
.provider-tile input[type="radio"] {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
}
.provider-tile:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
}
.provider-tile__icon { font-size: 1.6rem; color: var(--accent); }
.provider-tile__name { font-weight: 600; }
.provider-tile__desc { line-height: 1.4; }
/* Admin globally disabled this provider: render the tile as unavailable. */
.provider-tile--disabled {
  opacity: .55;
  cursor: not-allowed;
  filter: grayscale(.35);
}
.provider-tile--disabled:hover { border-color: var(--glass-input-border); background: var(--glass-input-bg); }

/* ---------- AI summary / digest panel --------------------------------- */
/* Built op <details>/<summary> zodat de gebruiker hem kan in-/uitklappen
   zonder JS. Default-state is open zodat verse output meteen zichtbaar is. */
.ai-summary-panel {
  margin-top: var(--space-4);
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.ai-summary-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  font-weight: 600;
  font-size: .9rem;
  cursor: pointer;
  user-select: none;
  list-style: none;            /* verberg de default-driehoek */
}
.ai-summary-head::-webkit-details-marker { display: none; }
.ai-summary-head::marker { content: ""; }
.ai-summary-head:hover { background: color-mix(in srgb, var(--accent) 15%, transparent); }
details[open] .ai-summary-head {
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 20%, var(--border));
}
.ai-summary-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 400;
}
.ai-summary-chev {
  transition: transform var(--transition-fast);
  color: var(--text-muted);
}
details[open] .ai-summary-chev { transform: rotate(180deg); }

.ai-summary-body {
  padding: var(--space-5);
  line-height: 1.7;
  color: var(--text);
  font-size: .94rem;
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* Markdown-output binnen het AI-panel. */
.ai-summary-body > p:first-child { margin-top: 0; }
.ai-summary-body p { margin: 0 0 var(--space-3); }
.ai-summary-body p:last-child { margin-bottom: 0; }

.ai-summary-body ul,
.ai-summary-body ol {
  margin: 0;
  padding-left: 0;
  list-style: none;
}
.ai-summary-body li {
  position: relative;
  margin-bottom: var(--space-4);    /* extra ademruimte tussen bullets */
  padding-left: var(--space-4);
  line-height: 1.6;
}
.ai-summary-body li:last-child { margin-bottom: 0; }
/* Custom bullet — past beter bij de accent-stijl dan de default-disc. */
.ai-summary-body li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .65em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  opacity: .7;
}
.ai-summary-body li > p:first-child { margin-top: 0; }
.ai-summary-body li > p:last-child { margin-bottom: 0; }

.ai-summary-body strong { color: var(--text); font-weight: 600; }

/* Topic-bold aan het begin van een bullet → eigen regel, accent-kleur,
   uppercase-vibe. Maakt de digest scanbaar. */
.ai-summary-body li > strong:first-child {
  display: block;
  color: var(--accent);
  margin-bottom: var(--space-2);
  font-size: .82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.ai-summary-body h1,
.ai-summary-body h2,
.ai-summary-body h3 {
  font-size: 1rem;
  font-weight: 600;
  margin: var(--space-3) 0 var(--space-2);
  color: var(--text);
}
.ai-summary-body h1:first-child,
.ai-summary-body h2:first-child,
.ai-summary-body h3:first-child { margin-top: 0; }

.ai-summary-body code {
  background: color-mix(in srgb, var(--accent) 10%, var(--surface-2));
  padding: 1px 5px;
  border-radius: 4px;
  font-size: .85em;
}
.ai-summary-body a { color: var(--accent); text-decoration: underline; }
.ai-summary-body a:hover { text-decoration: none; }
.ai-summary-body em { font-style: italic; }

/* ---------- Smart-search ---------------------------------------------- */
.smart-toggle {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  font-size: .85rem;
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
  padding: .25rem .5rem;
  border-radius: var(--radius);
}
.smart-toggle:hover { background: var(--surface-hover); color: var(--text); }
.smart-toggle .form-check-input { cursor: pointer; }

.smart-translation {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  padding: .5em .75em;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: .85rem;
  overflow-wrap: anywhere;
  word-break: break-word;
  max-width: 100%;
}
.smart-translation code {
  background: transparent;
  padding: 0;
  color: var(--text);
  font-weight: 500;
}

/* ---------- Alerts list ----------------------------------------------- */
.alert-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
  min-width: 0;
}
.alert-row:last-child { border-bottom: none; }
.alert-row__main {
  min-width: 0;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.alert-row__title-line {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.alert-row__name { font-weight: 600; }
.alert-row__query {
  display: inline-block;
  background: var(--surface-2);
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  font-size: .85em;
  overflow-wrap: anywhere;
  word-break: break-word;
  align-self: flex-start;
}
.alert-row__channels {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-size: .8rem;
  color: var(--text-muted);
}
.alert-row__meta {
  font-size: .75rem;
  color: var(--text-subtle);
}
.alert-row__actions {
  display: flex;
  gap: var(--space-1);
  flex-shrink: 0;
  align-items: center;
}

/* Uniforme icon-button: 32x32, geen tekst, dezelfde uitlijning ongeacht
   of het een <a>, <button>, of <form><button> is. Zet 'm overal in de
   app neer voor consistentie. */
.icon-btn--danger {
  color: var(--danger);
}
.icon-btn--danger:hover {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: var(--danger);
}

@media (max-width: 640px) {
  .alert-row {
    flex-direction: column;
    padding: var(--space-3) var(--space-4);
  }
  .alert-row__actions {
    align-self: flex-end;
  }
}

/* Compactere "aside row" voor de groups + channels-kolom rechts. */
.aside-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border);
  min-width: 0;
}
.aside-row:last-child { border-bottom: none; }
.aside-row__main {
  min-width: 0;
  flex: 1 1 auto;
}
.aside-row__title {
  display: flex;
  align-items: center;
  gap: .4em;
  flex-wrap: wrap;
}
.aside-row__target {
  font-size: .75rem;
  color: var(--text-muted);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.aside-row__actions {
  display: flex;
  gap: var(--space-1);
  flex-shrink: 0;
}

/* ---------- Notification list ----------------------------------------- */
.notif-list { display: block; }
.notif-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
  /* Kritisch: zonder min-width:0 expandeert een flex-item naar z'n
     content-breedte (zoals een lange URL) en duwt de rest van de pagina
     buiten het viewport. */
  min-width: 0;
}
.notif-row:last-child { border-bottom: none; }
.notif-row__main {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-width: 0;       /* idem — zorgt dat de tekst-kolom mag krimpen */
  flex: 1 1 auto;
}
.notif-row__body {
  min-width: 0;
  flex: 1 1 auto;
  /* Lange URL's en CVE-IDs zonder spaties laten breken in plaats van
     het hele rij-element te verbreden. */
  overflow-wrap: anywhere;
  word-break: break-word;
}
.notif-row__title { font-weight: 500; line-height: 1.4; }
.notif-row__meta { margin-top: 2px; }
.notif-row__excerpt { margin-top: var(--space-2); color: var(--text-muted); }
.notif-row__action {
  flex-shrink: 0;
  display: inline-flex;
  gap: var(--space-1);
  align-items: flex-start;
}

.notif-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  margin-top: 8px; flex-shrink: 0;
}
.notif-dot.is-read { background: transparent; }

.notif-new-pill {
  background: var(--accent-soft);
  color: var(--accent-text);
  margin-right: .4em;
}

@media (max-width: 640px) {
  .notif-row { padding: var(--space-3) var(--space-3); }
  /* Action-knop een tikje kleiner zodat 'ie niet visueel domineert. */
  .notif-row__action .btn { padding: .25rem .45rem; }
}

/* Checkbox-lijst — vervangt <select multiple> voor velden waar je
   zero-or-more wilt kunnen selecteren (alert-kanalen, kanaalgroepen, etc.). */
.checkbox-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  max-height: 240px;
  overflow-y: auto;
}
.checkbox-group .form-check { margin: 0; padding-left: 1.75em; }
.checkbox-group .form-check-label {
  color: var(--text);
  cursor: pointer;
  user-select: none;
}

.form-text { color: var(--text-muted); font-size: .825rem; }

/* ---------- Auth screens (glassmorphism) ------------------------------
   The auth shell is the only place where we lean into "glass": layered
   gradient backdrop with blurred amber/warm orbs floating behind a
   heavily frosted card. The shell uses ::before/::after pseudo-elements
   for the orbs so we don't have to add wrapper divs to the HTML. */
.auth-shell {
  position: relative;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-5);
  background: var(--glass-bg-gradient);
  overflow: hidden;
  isolation: isolate;
}
/* Two large blurred orbs in the background to give the glass card
   something interesting to refract.  position: fixed so they stay put
   when the viewport scrolls on tall forms.  z-index: 0 keeps them
   below the card (which sits on auto/0 inside the auth-shell stacking
   context). */
.auth-shell::before,
.auth-shell::after {
  content: "";
  position: fixed;
  width: 56vmax;
  height: 56vmax;
  border-radius: 50%;
  filter: blur(80px);
  opacity: .85;
  pointer-events: none;
  z-index: 0;
  animation: glassOrb 22s ease-in-out infinite alternate;
}
.auth-shell::before {
  top: -18vmax;
  right: -14vmax;
  background: var(--glass-orb-1);
}
.auth-shell::after {
  bottom: -22vmax;
  left: -14vmax;
  background: var(--glass-orb-2);
  animation-duration: 28s;
  animation-delay: -8s;
}
@keyframes glassOrb {
  0%   { transform: translate(0, 0) scale(1); }
  50%  { transform: translate(4vmax, -3vmax) scale(1.08); }
  100% { transform: translate(-3vmax, 4vmax) scale(.95); }
}
@media (prefers-reduced-motion: reduce) {
  .auth-shell::before,
  .auth-shell::after { animation: none; }
}

/* Footer in the auth-shell sits above the orbs but below the card. */
.auth-shell > * { position: relative; z-index: 1; }

/* ---------- Glass card -------------------------------------------------
   The glass card is the centerpiece.  Structure:
   - .auth-card itself: gradient fill + backdrop-blur + outer shadow
   - ::before: the gradient border, painted via mask-composite so it sits
     on the very edge with no extra DOM
   - ::after: a top-left specular highlight (the "shine" you get when
     light hits real glass)
*/
.auth-card {
  position: relative;
  width: 100%;
  max-width: 440px;
  padding: var(--space-7);
  border-radius: 24px;
  background: var(--glass-fill);
  backdrop-filter: blur(var(--glass-blur)) saturate(160%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(160%);
  box-shadow: var(--glass-shadow);
  z-index: 2;
  /* Border baseline; the gradient border is painted on top via ::before */
  border: 1px solid transparent;
  overflow: hidden;
  isolation: isolate;
}
.auth-card::before {
  /* Gradient border: paint a 1px-thick frame using mask-composite.
     This works in all modern browsers (Safari 15.4+, Chrome 120+,
     Firefox 120+) and is the cleanest way to do gradient borders. */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    150deg,
    var(--glass-border-top) 0%,
    rgba(255, 255, 255, .04) 50%,
    var(--glass-border-bottom) 100%
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 1;
}
.auth-card::after {
  /* Specular highlight: a soft white wash in the top-left corner that
     mimics a light source bouncing off the glass surface. */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--glass-specular);
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 1;
}
/* All real content sits above the pseudo-element overlays. */
.auth-card > * { position: relative; z-index: 2; }

.auth-card .brand {
  font-size: 1.25rem;
  margin-bottom: var(--space-5);
}
.auth-card .brand-mark {
  /* Lift the brand-mark out of the surface palette and onto its own
     mini glass tile so it pops on the frosted background. */
  background: linear-gradient(135deg, var(--accent), var(--accent-strong));
  color: #1c1209;
  box-shadow:
    0 6px 18px rgba(245, 158, 11, .35),
    0 1px 0 rgba(255, 255, 255, .35) inset;
}
.auth-card h1 {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -.02em;
  margin: 0 0 var(--space-2);
  /* Explicit — Bootstrap 5.3's --bs-heading-color can render h1 in a
     lighter tone on some surfaces. Forcing var(--text) keeps the title
     crisp on the frosted card. */
  color: var(--text);
}
.auth-card p.lead {
  color: var(--text-muted);
  font-size: .925rem;
  margin-bottom: var(--space-5);
}

/* ---------- Glass inputs (scoped to .auth-card) -----------------------
   Inputs inside the glass card need their own frosted look so they read
   as "inside" the glass surface rather than as opaque controls. */
.auth-card .form-control,
.auth-card input[type="text"],
.auth-card input[type="email"],
.auth-card input[type="password"],
.auth-card input[type="number"] {
  background: var(--glass-input-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--glass-input-border);
  border-radius: 12px;
  color: var(--text);
  padding: .7rem .9rem;
  transition: border-color var(--transition), box-shadow var(--transition), background var(--transition);
}
.auth-card .form-control::placeholder { color: var(--text-subtle); }
.auth-card .form-control:focus,
.auth-card input:focus {
  outline: none;
  border-color: var(--glass-input-border-focus);
  background: color-mix(in srgb, var(--glass-input-bg) 80%, var(--accent-soft));
  box-shadow:
    0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent),
    0 1px 0 rgba(255, 255, 255, .1) inset;
}
.auth-card .form-label {
  font-size: .825rem;
  font-weight: 500;
  color: var(--text-muted);
  margin-bottom: .35rem;
}
.auth-card .form-check-input {
  background-color: var(--glass-input-bg);
  border-color: var(--glass-input-border);
}
.auth-card .form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}
.auth-card .form-check-label { color: var(--text-muted); font-size: .875rem; }

/* ---------- Glass primary button (scoped to .auth-card) ---------------
   The submit button gets an amber gradient with a soft glow so it
   feels like the bright object on the frosted surface. */
.auth-card .btn-primary {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-strong) 100%);
  border: 1px solid color-mix(in srgb, var(--accent) 50%, transparent);
  color: #1c1209;
  font-weight: 600;
  padding: .75rem 1rem;
  border-radius: 14px;
  box-shadow:
    0 8px 24px rgba(245, 158, 11, .35),
    0 1px 0 rgba(255, 255, 255, .35) inset,
    0 -1px 0 rgba(0, 0, 0, .12) inset;
  transition: transform var(--transition-fast), box-shadow var(--transition), filter var(--transition);
}
.auth-card .btn-primary:hover,
.auth-card .btn-primary:focus-visible {
  transform: translateY(-1px);
  filter: brightness(1.04);
  box-shadow:
    0 12px 32px rgba(245, 158, 11, .45),
    0 1px 0 rgba(255, 255, 255, .45) inset,
    0 -1px 0 rgba(0, 0, 0, .15) inset;
}
.auth-card .btn-primary:active {
  transform: translateY(0);
  filter: brightness(.96);
}

/* Links inside the glass card get a subtle hover glow.
   We need the !important here because most of these links live inside
   a wrapping `<div class="small text-muted">`, and .text-muted carries
   `color: var(--text-muted) !important` from Bootstrap — without the
   override below the "Create account" / "Sign in" links would render
   as low-contrast grey instead of the amber accent. */
.auth-card a,
.auth-card .text-muted a,
.auth-card .small a {
  color: var(--accent) !important;
  text-decoration: none;
  transition: color var(--transition);
}
.auth-card a:hover,
.auth-card .text-muted a:hover,
.auth-card .small a:hover {
  color: var(--accent-strong) !important;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Flash messages floating in the auth-shell get the glass treatment too. */
.auth-shell .alert {
  background: var(--glass-fill-strong);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid var(--glass-border-top);
  border-radius: 14px;
  box-shadow: var(--glass-shadow);
  color: var(--text);
}

/* ---------- Alerts (flash messages) — frosted tinted glass ------------ */
.alert {
  border-radius: 14px;
  border-width: 1px;
  border-style: solid;
  padding: var(--space-3) var(--space-4);
  font-size: .9rem;
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .1);
}
.alert-success {
  background: color-mix(in srgb, var(--success) 18%, var(--glass-input-bg));
  border-color: color-mix(in srgb, var(--success) 50%, var(--glass-border-top));
  color: var(--success);
}
.alert-danger {
  background: color-mix(in srgb, var(--danger) 18%, var(--glass-input-bg));
  border-color: color-mix(in srgb, var(--danger) 50%, var(--glass-border-top));
  color: var(--danger);
}
.alert-warning {
  background: color-mix(in srgb, var(--warning) 18%, var(--glass-input-bg));
  border-color: color-mix(in srgb, var(--warning) 50%, var(--glass-border-top));
  color: var(--warning);
}
.alert-info {
  background: color-mix(in srgb, var(--info) 18%, var(--glass-input-bg));
  border-color: color-mix(in srgb, var(--info) 50%, var(--glass-border-top));
  color: var(--info);
}

/* ---------- Tables (kept mostly flat for legibility) ------------------- */
.table {
  --bs-table-bg: transparent;
  color: var(--text);
}
.table > :not(caption) > * > * {
  background: transparent;
  color: inherit;
  border-bottom-color: var(--glass-border-bottom);
}
.table thead th {
  background: var(--glass-input-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom-color: var(--glass-border-top);
  font-weight: 600;
}
.table tbody tr:hover > * { background: var(--glass-input-bg); }

/* ---------- Pagination (glass pills) ----------------------------------- */
.pagination .page-link {
  background: var(--glass-input-bg);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-color: var(--glass-input-border);
  color: var(--text);
  border-radius: var(--radius-sm);
  margin: 0 2px;
  transition: all var(--transition-fast);
}
.pagination .page-link:hover {
  background: var(--glass-fill-strong);
  border-color: var(--glass-border-top);
  transform: translateY(-1px);
}
.pagination .page-item.disabled .page-link { background: transparent; color: var(--text-subtle); }
.pagination .page-item.active .page-link {
  background: linear-gradient(135deg, var(--accent), var(--accent-strong));
  border-color: transparent;
  color: #1c1209;
  font-weight: 600;
  box-shadow: 0 4px 12px rgba(245, 158, 11, .3);
}

/* ---------- Empty states ----------------------------------------------- */
.empty-state {
  text-align: center;
  padding: var(--space-8) var(--space-5);
  color: var(--text-muted);
}
.empty-state .empty-icon {
  font-size: 2.5rem;
  color: var(--text-subtle);
  margin-bottom: var(--space-3);
  opacity: .6;
}
.empty-state h3 {
  font-size: 1.1rem;
  color: var(--text);
  font-weight: 600;
  margin: 0 0 var(--space-2);
}
.empty-state p { font-size: .9rem; max-width: 420px; margin: 0 auto var(--space-4); }

/* ---------- Misc -------------------------------------------------------- */
hr { border: none; border-top: 1px solid var(--border); margin: var(--space-5) 0; }

.text-muted, .text-body-secondary { color: var(--text-muted) !important; }
.text-subtle { color: var(--text-subtle); }
.small { font-size: .825rem; }

.kbd {
  display: inline-block;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  padding: 2px 6px;
  font-family: var(--font-mono);
  font-size: .75rem;
  color: var(--text-muted);
}

/* Toolbar inside cards */
.toolbar {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
}

/* Scrollbar */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
*::-webkit-scrollbar-track { background: transparent; }

/* ---------- App footer -------------------------------------------------- */
.app-footer {
  border-top: 1px solid var(--border);
  background: var(--bg-soft);
  margin-top: var(--space-7);
}
.app-footer-inner {
  max-width: 1600px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-5);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  color: var(--text-muted);
  font-size: .825rem;
}
.app-footer-meta { flex: 0 0 auto; }
.app-footer-meta strong { color: var(--text); font-weight: 600; }
.app-footer-meta a { color: inherit; text-decoration: none; }
.app-footer-meta a:hover { color: var(--accent); }
/* Right-side cluster: links + version share the same horizontal spacing
   and sit together on the right of the footer. */
.app-footer-right {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-left: auto;
}
.app-footer-links {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.app-footer-links a {
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  gap: .35em;
  transition: color var(--transition-fast);
}
.app-footer-links a:hover { color: var(--accent); }
.app-footer-version { flex: 0 0 auto; }

.auth-shell .app-footer {
  background: transparent;
  border-top: none;
  margin-top: var(--space-6);
}

/* Markdown-content (release notes etc.) */
.prose {
  font-size: .95rem;
  line-height: 1.65;
  color: var(--text);
}
.prose h1, .prose h2, .prose h3 {
  font-weight: 700;
  letter-spacing: -.015em;
  margin: var(--space-5) 0 var(--space-2);
  color: var(--text);
}
.prose h1 { font-size: 1.5rem; margin-top: 0; }
.prose h2 { font-size: 1.2rem; padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.prose h3 { font-size: 1rem; }
.prose p { margin: 0 0 var(--space-3); }
.prose ul, .prose ol { padding-left: var(--space-5); margin-bottom: var(--space-3); }
.prose li { margin-bottom: var(--space-1); }
.prose code { font-size: .85em; }
.prose pre {
  background: var(--surface-2);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  overflow-x: auto;
}
.prose pre code { background: transparent; border: none; color: var(--text); padding: 0; }
.prose hr { margin: var(--space-5) 0; }
.prose blockquote {
  border-left: 3px solid var(--border-strong);
  padding-left: var(--space-3);
  color: var(--text-muted);
  margin: var(--space-3) 0;
}
.prose table { border-collapse: collapse; margin-bottom: var(--space-3); }
.prose th, .prose td {
  border: 1px solid var(--border);
  padding: var(--space-2) var(--space-3);
  text-align: left;
}
.prose th { background: var(--surface-2); }

/* ---------- Top-loader (toont bij langlopende acties) ------------------ */
.top-loader {
  position: fixed;
  top: 0; left: 0;
  height: 3px;
  width: 100%;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--accent) 20%,
    var(--accent-strong) 50%,
    var(--accent) 80%,
    transparent 100%);
  z-index: 9999;
  animation: top-loader-slide 1.2s ease-in-out infinite;
}
@keyframes top-loader-slide {
  0% { background-position: -100% 0; }
  100% { background-position: 200% 0; }
}

button.is-busy { opacity: .85; }
button.is-busy .busy-text { font-size: .85em; }
.spinner-border-sm { width: 0.95rem; height: 0.95rem; border-width: .15em; }

/* ---------- Hamburger / mobile menu trigger ---------------------------- */
/* Default verborgen; alleen op mobiel/tablet getoond via media-query verderop.
   ``!important`` om zeker te zijn dat geen Bootstrap-utility 'm overschrijft. */
.icon-btn.menu-toggle { display: none !important; }

/* ---------- Offcanvas drawer (popover-opaque) -------------------------- */
.offcanvas {
  background: var(--popover-fill);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  color: var(--text);
  border-color: var(--glass-border-top);
  box-shadow: -20px 0 50px rgba(0, 0, 0, .35);
}
.offcanvas-header {
  border-bottom: 1px solid var(--glass-border-bottom);
  padding: var(--space-4);
}
.offcanvas-body { padding: var(--space-4); }
.offcanvas .btn-close { filter: invert(0.7); }
/* Backdrop behind the offcanvas — slightly tinted instead of pure black. */
.offcanvas-backdrop.show { opacity: .55; }

/* ---------- Dropdown menu (popover-opaque) -----------------------------
   Uses --popover-fill (near-opaque) instead of --glass-fill-strong so
   the dropdown actually masks the dashboard content underneath rather
   than letting it show through the glass. The blur stays as a polish
   effect but isn't relied on to hide content. */
.dropdown-menu {
  background: var(--popover-fill) !important;
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--glass-border-top) !important;
  border-radius: 14px;
  box-shadow:
    0 16px 40px rgba(0, 0, 0, .35),
    0 1px 0 var(--glass-border-top) inset;
  padding: var(--space-2);
}
.dropdown-item {
  border-radius: 8px;
  color: var(--text);
  padding: .45rem .75rem;
  font-size: .9rem;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.dropdown-item:hover,
.dropdown-item:focus {
  background: var(--glass-input-bg);
  color: var(--text);
}
.dropdown-item.active {
  background: linear-gradient(135deg, var(--accent), var(--accent-strong));
  color: #1c1209;
  font-weight: 600;
}
.dropdown-divider {
  border-color: var(--glass-border-bottom) !important;
  margin: var(--space-2) 0;
}

/* ---------- Modal (popover-opaque) ------------------------------------ */
.modal-content {
  background: var(--popover-fill);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  border: 1px solid var(--glass-border-top);
  border-radius: 18px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
  color: var(--text);
}
.modal-header {
  border-bottom: 1px solid var(--glass-border-bottom);
  padding: var(--space-4) var(--space-5);
}
.modal-body { padding: var(--space-5); }
.modal-footer {
  border-top: 1px solid var(--glass-border-bottom);
  padding: var(--space-4) var(--space-5);
}
.modal-backdrop.show { opacity: .55; }
.modal-content .btn-close { filter: invert(0.7); }

/* ---------- Responsive: tablet (<992px) -------------------------------- */
@media (max-width: 991.98px) {
  .topbar-inner { padding: var(--space-3) var(--space-4); gap: var(--space-3); }

  /* Verberg de nav-pills + search uit de topbar; ze leven in de drawer. */
  .topbar .nav-pills-app,
  .topbar .topbar-search { display: none; }

  /* Houd alleen brand + bell + theme + hamburger. */
  .icon-btn.menu-toggle { display: inline-flex !important; }

  .page { padding: var(--space-4) var(--space-4); }
  .page-header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
  }
  .page-title { font-size: 1.25rem; }

  .stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
  }
  .stat-card { padding: var(--space-3) var(--space-4); }
  .stat-card .stat-value { font-size: 1.4rem; }
}

/* ---------- Responsive: phone (<640px) --------------------------------- */
@media (max-width: 640px) {
  .topbar-inner {
    padding: var(--space-2) var(--space-3);
    gap: var(--space-2);
  }
  .brand-name { display: none; }  /* alleen het logo-blokje */

  .page { padding: var(--space-4) var(--space-3); }
  .surface-body { padding: var(--space-4); }
  .surface-head { padding: var(--space-3) var(--space-4); }

  /* Bootstrap's .row heeft negatieve margins die net naast 't viewport-grens
     vallen bij smalle padding.  Halen we hier op de mobiele schermen weg. */
  .row { margin-left: 0; margin-right: 0; }
  .row > [class^="col"] { padding-left: 0; padding-right: 0; }
  .row > [class^="col"] + [class^="col"] { margin-top: var(--space-3); }

  .article-card {
    grid-template-columns: 1fr;
    padding: var(--space-3) var(--space-4);
  }
  .article-card .article-actions {
    flex-direction: row;
    justify-content: flex-end;
    margin-top: var(--space-2);
  }
  .article-title { font-size: 1rem; }
  .article-snippet { -webkit-line-clamp: 3; }

  .feed-grid { grid-template-columns: 1fr; }

  .reading-header h1 { font-size: 1.5rem; }
  .reading-view .article-content { font-size: 1rem; }

  .auth-card { padding: var(--space-5); }
  .auth-shell { padding: var(--space-3); }
}

/* ---------- Reader sidebar collapses to drawer on mobile/tablet ------- */
@media (max-width: 991.98px) {
  /* Op kleinere schermen wordt de sidebar via een drawer (offcanvas) getoond.
     De template plaatst dezelfde inhoud in een offcanvas; de inline aside
     verbergen we hier. */
  .reader { display: block; }
  .reader > .reader-sidebar.inline-sidebar { display: none; }

  .filters-trigger {
    display: inline-flex;
    align-items: center;
    gap: .35em;
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text);
    padding: .35rem .85rem;
    border-radius: var(--radius-pill);
    font-size: .825rem;
    margin-bottom: var(--space-3);
    cursor: pointer;
  }
  .filters-trigger:hover { background: var(--surface-hover); border-color: var(--border-strong); }
}

@media (min-width: 992px) {
  /* Op desktop: drawer + trigger nooit zichtbaar — sidebar is inline. */
  .filters-trigger,
  #filtersDrawer { display: none !important; }
}
