/* ============================================================
   SLR Harvester Web — stylesheet
   All colours via CSS custom properties. No hardcoded values.
   ============================================================ */

/* ----------------------------------------------------------
   1. Custom properties — dark theme (default)
   ---------------------------------------------------------- */
:root,
[data-theme="dark"] {
  --bg:           #0d1117;
  --surface:      #161b22;
  --surface-2:    #21262d;
  --surface-3:    #2d333b;
  --border:       #30363d;
  --border-subtle:#21262d;
  --text:         #e6edf3;
  --text-muted:   #8b949e;
  --text-faint:   #484f58;
  /* Brand color: turquoise, base #31E6D3. Bright/light by nature (relative
     luminance ~0.62) — great as text/icon/border color on dark backgrounds,
     but text painted ON a solid fill of it needs dark ink, not white; see
     --accent-ink below. accent-hover is a brighter pop for hover feedback. */
  --accent:       #31e6d3;
  --accent-hover: #5ff0e0;
  --accent-subtle:#123330;
  --accent-ink:   #06231f;
  --green:        #3fb950;
  --green-subtle: #1a2e1a;
  --danger:       #f85149;
  --danger-subtle:#2d1a1a;
  --warning:      #d29922;
  /* World-map choropleth scale: low (no/few publications, blends toward
     background) -> high (most publications, stands out). Direction is
     deliberately inverted vs. light theme so "more data" always reads as
     more visually prominent, not as invisible-dark-on-dark. */
  --heat-low:     #17322e;
  --heat-high:    #5ff0e0;
  /* "SLR Harvester Web" (.app-name) is the widest label the sidebar ever
     has to fit, so this is sized off that: 17px header padding + 22px logo
     + 13px logo-gap + ~117px text + ~18px breathing room + 30px toggle
     button + 10px padding ≈ 227px, rounded up a touch as a safety margin
     for font metrics running wider on some systems (was 256px — ~30px/
     0.8cm of pure dead space past the toggle button on desktop). The
     900px/640px breakpoints below already override this tighter on their
     own (216px / 56px icon-rail) and don't need adjusting. */
  --sidebar-w:    228px;
  --sidebar-collapsed-w: 56px;
  --topbar-h:     52px;
  /* Shared left inset for the sidebar's icon column (logo, nav icons,
     copyright ©) so everything lines up on the same vertical axis. A single
     source of truth — override this one variable per breakpoint instead of
     re-tuning padding in three different places. */
  --sidebar-icon-pad-left: 19px;
  --transition:   200ms ease;
  --radius-sm:    4px;
  --radius:       6px;
  --radius-md:    8px;
  --radius-lg:    10px;
  --shadow:       0 1px 3px rgba(0,0,0,.4);
  --shadow-lg:    0 4px 16px rgba(0,0,0,.5);
}

[data-theme="light"] {
  --bg:           #f6f8fa;
  --surface:      #ffffff;
  --surface-2:    #f6f8fa;
  --surface-3:    #eaeef2;
  --border:       #d0d7de;
  --border-subtle:#eaeef2;
  --text:         #1f2328;
  --text-muted:   #636c76;
  --text-faint:   #b0b8c1;
  /* Same brand hue as dark theme (~173°) but deepened for contrast against a
     white page — the literal bright #31E6D3 fails WCAG AA as text/borders
     here. accent-ink is white: this darker fill has enough contrast for it. */
  --accent:       #0a7a6e;
  --accent-hover: #086159;
  --accent-subtle:#d7f7f2;
  --accent-ink:   #ffffff;
  --green:        #1a7f37;
  --green-subtle: #d1fae5;
  --danger:       #cf222e;
  --danger-subtle:#fde8e8;
  --warning:      #9a6700;
  /* Classic light-to-dark choropleth direction: low data is a pale tint,
     high data is deep and saturated — reads correctly on a light page. */
  --heat-low:     #d7f7f2;
  --heat-high:    #0a4a42;
  --shadow:       0 1px 3px rgba(0,0,0,.12);
  --shadow-lg:    0 4px 16px rgba(0,0,0,.15);
}

/* ----------------------------------------------------------
   2. Reset & base
   ---------------------------------------------------------- */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  height: 100%;
  overflow: hidden;
  /* Defense in depth for #app's position:fixed below: if any sub-pixel
     rounding ever leaves a sliver at the viewport edge, it reads as the
     app's own background instead of the browser's default white canvas. */
  background: var(--bg);
  /* Opt out of mobile browsers' automatic text-inflation ("font boosting").
     Without this, rotating a phone to landscape can grow text (e.g. article
     titles) and rotating back to portrait doesn't shrink it back — the
     browser's heuristic only recomputes for the wider layout it last saw.
     Every font-size in this file is a fixed px value already, so there's
     nothing here that actually needs auto-adjusting. */
  text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

input, select, textarea {
  font: inherit;
  color: inherit;
}

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

a:hover {
  text-decoration: underline;
  color: var(--accent-hover);
}

/* ----------------------------------------------------------
   3. App shell — sidebar + main
   ---------------------------------------------------------- */
#app {
  /* position:fixed + inset:0 (not height:100vh/100dvh) so the app is pinned
     to the ACTUAL visual viewport rectangle at all times. vh/dvh are values
     computed once per layout pass; on some mobile browsers (observed: Edge
     Android) that computed height can lag behind the real available height
     as the browser's own UI chrome shows/hides, leaving a gap at the bottom
     edge filled by the page background instead of the app. Fixed positioning
     has no such lag — the browser places it against the current visual
     viewport directly, which is what "reaches every screen edge, in every
     browser, at every moment" actually requires. */
  position: fixed;
  inset: 0;
  display: flex;
  overflow: hidden;
}

/* iOS notch/home-indicator safe-area baseline.
   The bottom inset is handled on .sidebar-nav directly (see below) via
   max(), not here — keeping it in one place avoids stacking it twice. */
@supports (padding: env(safe-area-inset-top)) {
  #topbar {
    padding-top: env(safe-area-inset-top);
    height: calc(var(--topbar-h) + env(safe-area-inset-top));
  }
}

/* ----------------------------------------------------------
   4. Sidebar
   ---------------------------------------------------------- */
.sidebar {
  display: flex;
  flex-direction: column;
  width: var(--sidebar-w);
  min-width: var(--sidebar-w);
  min-height: 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  overflow: hidden;
  flex-shrink: 0;
  /* Overlay, not a column that squeezes the page. Expanding used to be a
     layout change: the sidebar was an in-flow flex child, so every view next
     to it got ~170px narrower and reflowed — tables re-wrapped, grids
     dropped a column, charts resized. Now it is lifted out of flow and
     simply covers what is underneath, with .sidebar-backdrop dimming the
     content behind it. #main keeps a fixed margin for the collapsed rail, so
     the page geometry never moves.
     position:fixed also makes z-index apply, which the sidebar needs anyway
     so its opaque background paints over the Home view's viewport-fixed
     particle canvas instead of the other way around. */
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 250;
  /* Promotes the sidebar to its own compositing layer, isolated from the
     Home view's continuously-animating position:fixed particle canvas next
     to it. Without this, observed on Edge: the flex-stretched sidebar-nav
     goes stale and leaves a gap under the footer that only clears on a
     forced reflow (e.g. toggling fullscreen) — a compositor invalidation
     bug where an adjacent animating fixed layer prevents this one from
     repainting at its correct height. */
  transform: translateZ(0);
}

@media (prefers-reduced-motion: no-preference) {
  .sidebar {
    transition: width var(--transition), min-width var(--transition);
  }
}

.sidebar.collapsed {
  width: var(--sidebar-collapsed-w);
  min-width: var(--sidebar-collapsed-w);
}

.sidebar.collapsed .sidebar-label {
  display: none;
}

.sidebar.collapsed .app-name {
  display: none;
}

/* Hide entire logo block in collapsed state; center the toggle button */
.sidebar.collapsed .app-logo {
  display: none;
}

/* Sidebar header */
.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Left padding chosen so the logo icon's horizontal center (17 + 22/2 = 28px)
     matches the nav icons' center below it (padding-left 19 + 18/2 = 28px). */
  padding: 12px 10px 12px 17px;
  border-bottom: 1px solid var(--border);
  height: var(--topbar-h);
  flex-shrink: 0;
}

.sidebar.collapsed .sidebar-header {
  justify-content: center;
  padding: 12px 0;
  gap: 0;
}

.app-logo {
  display: flex;
  align-items: center;
  gap: 13px;
  overflow: hidden;
}

.logo-icon {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--accent);
  width: 22px;
  height: 22px;
}

.logo-icon svg {
  width: 22px;
  height: 22px;
}

.app-name {
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--text);
}

/* "Web" sits raised and shrunk like an exponent — up and to the right of
   "Harvester" — rather than running inline with it, so it reads as a
   superscript tag, not part of the name itself. */
.title-web {
  font-weight: 400;
  font-size: 0.62em;
  vertical-align: super;
  letter-spacing: -.01em;
}

/* Sidebar toggle button */
.icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--radius);
  color: var(--text-muted);
  flex-shrink: 0;
}

.icon-btn:hover {
  background: var(--surface-2);
  color: var(--text);
}

.icon-btn svg {
  width: 16px;
  height: 16px;
}

/* Unlike .nav-icon/.theme-switch-icon, this wrapper span had no rules of its
   own — a plain inline span lets its svg child sit on the text baseline
   instead of centering, which reads as "not quite vertically centered"
   inside the button. Flex-centering the wrapper itself fixes that. */
#sidebar-toggle-icon {
  display: flex;
  align-items: center;
  justify-content: center;
}

.sidebar.collapsed #sidebar-toggle {
  margin: 0 auto;
}

/* The chevron direction is set by swapping the icon in JS (app-ui.js:
   setSidebarCollapsed/injectIcons use chevronLeft/chevronRight directly) — do
   NOT also rotate it here via CSS, that cancels the swap and points it the
   wrong way. */

/* Sidebar nav */
.sidebar-nav {
  flex: 1;
  min-height: 0; /* Safari: a scrollable flex:1 child needs this to actually fill (not just wrap) available height */
  padding: 8px 0 max(8px, env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

/* Fixed height + identical padding-left in both collapsed and expanded modes:
   icons must not move at all when the sidebar-label appears/disappears.
   height (not padding) sets the vertical size so the row can't grow/shrink
   with label line-height, and padding-left is the same 19px used to center
   the 18px icon in the 56px collapsed sidebar — kept identical when expanded
   so the icon never shifts horizontally either. */
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 34px;
  padding: 0 10px 0 var(--sidebar-icon-pad-left);
  border-radius: 0;
  color: var(--text-muted);
  white-space: nowrap;
  position: relative;
  text-align: left;
}

.nav-item:hover {
  background: var(--surface-2);
  color: var(--text);
}

.nav-item.active {
  background: var(--accent-subtle);
  color: var(--accent-hover);
}

.nav-item.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  background: var(--accent);
  border-radius: 0 2px 2px 0;
}

/* Onboarding hint: flashes the suggested "next step" nav item twice */
@keyframes navHintPulse {
  0%, 100% { background: transparent; color: var(--text-muted); }
  50%      { background: var(--accent-subtle); color: var(--accent-hover); }
}

.nav-item.nav-hint-pulse {
  animation: navHintPulse .7s ease-in-out 2;
}

.nav-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
}

.nav-icon svg {
  width: 18px;
  height: 18px;
}

.nav-label {
  font-size: 13.5px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Groups nav items without subheadings — same inset on both sides as the
   icon column so it reads as a deliberate divider rather than a stray
   line, unchanged by collapse/expand since it doesn't touch .sidebar-label
   at all. */
.nav-separator {
  height: 1px;
  margin: 6px var(--sidebar-icon-pad-left);
  background: var(--border);
  flex-shrink: 0;
}

/* ----------------------------------------------------------
   5. Main + topbar
   ---------------------------------------------------------- */
#main {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
  min-height: 0;
  /* Always inset by the collapsed rail width — deliberately NOT tied to the
     sidebar's state: expanding must not change the content's width, or it
     gets squeezed instead of covered. */
  margin-left: var(--sidebar-collapsed-w);
}

/* Dims the content whenever the sidebar is expanded. Must be a child of
   #app: #app is position:fixed and therefore forms its own stacking context
   — sitting next to it in <body>, this element's z-index would be compared
   against #app as a whole and it would land ON TOP of the sidebar, which
   would then look greyed out and swallow every click.
   Visibility rides entirely on the sibling selector below, so it needs no
   JS state of its own. */
.sidebar-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .5);
  backdrop-filter: saturate(.55) blur(1px);
  -webkit-backdrop-filter: saturate(.55) blur(1px);
  z-index: 240;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition);
}

.sidebar:not(.collapsed) ~ .sidebar-backdrop {
  opacity: 1;
  pointer-events: auto;
}

#topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  height: var(--topbar-h);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
  gap: 12px;
  /* Same fix as .sidebar above, same reason: without an explicit stacking
     context the Home view's viewport-fixed particle canvas (z-index:-1
     inside .welcome-view's own isolated context) can still show through
     this otherwise-opaque bar on some browsers/GPUs — observed on desktop,
     not mobile, which is exactly the kind of compositor-layering quirk
     position:relative + z-index + a promoted layer forecloses outright,
     the same way it already does for the sidebar. Never let the header
     read as transparent in any view, on any device. */
  position: relative;
  z-index: 100;
  transform: translateZ(0);
}

.view-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

/* Account entry point — Home screen only, top-right of the view's own box
   (not the global topbar; see renderWelcome in views.js). */
.welcome-account-wrap {
  position: absolute;
  top: 20px;
  right: 24px;
  z-index: 10;
}

.welcome-account-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  box-shadow: var(--shadow);
}

.welcome-account-btn:hover {
  background: var(--surface-3);
  color: var(--text);
}

.welcome-account-btn svg {
  width: 22px;
  height: 22px;
}

.welcome-account-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 220px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  z-index: 200;
}

.welcome-account-menu[hidden] {
  display: none;
}

.account-menu-email {
  padding: 8px 10px 7px;
  font-size: 12px;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: 4px;
  word-break: break-all;
}

.account-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--text);
}

.account-menu-item:hover {
  background: var(--surface-2);
}

.account-menu-item--danger {
  color: var(--danger);
}

.account-menu-item--danger:hover {
  background: var(--danger-subtle);
}

/* Now a real button (was a plain span) — height matches .theme-switch (the
   toggles it sits next to in .topbar-right) so it reads as part of the same
   row of controls instead of a differently-sized label. */
.project-badge {
  display: inline-flex;
  align-items: center;
  height: 28px;
  box-sizing: border-box;
  background: var(--accent-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 14px;
  padding: 0 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 260px;
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--transition), border-color var(--transition);
}

.project-badge:hover {
  background: color-mix(in srgb, var(--accent) 24%, var(--accent-subtle));
  border-color: var(--accent);
}

.project-badge:empty {
  display: none;
}

.topbar-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--radius);
  color: var(--text-muted);
  border: 1px solid var(--border);
  background: var(--surface-2);
  flex-shrink: 0;
  transition: background .15s, border-color .15s, color .15s;
}

.topbar-icon-btn:hover {
  color: var(--text);
  border-color: var(--accent);
  background: var(--surface-3);
}

.topbar-icon-btn svg {
  width: 14px;
  height: 14px;
}

.fullscreen-switch {
  width: 52px;
}

.fullscreen-switch .theme-switch-knob {
  width: 20px;
  height: 20px;
}

.fullscreen-switch.is-active {
  background: var(--accent-subtle);
  border-color: var(--accent);
}

.fullscreen-switch.is-active .theme-switch-knob {
  transform: translateX(24px);
  background: #ffffff;
}

.fullscreen-switch .theme-switch-icon svg {
  width: 11px;
  height: 11px;
}

/* Theme toggle switch — always top-right in topbar */
.theme-switch {
  position: relative;
  width: 52px;
  height: 28px;
  border-radius: 14px;
  background: var(--surface-3);
  border: 1px solid var(--border);
  cursor: pointer;
  flex-shrink: 0;
  transition: background .2s, border-color .2s;
  padding: 0;
}

.theme-switch:hover {
  border-color: var(--accent);
}

/* Light mode: slide knob to the right */
.theme-switch.is-light {
  background: var(--accent-subtle);
  border-color: var(--accent);
}

.theme-switch-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow);
  transition: transform .2s ease, background .2s;
}

.theme-switch.is-light .theme-switch-knob {
  transform: translateX(24px);
  background: #ffffff;
}

.theme-switch-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  pointer-events: none;
}

.theme-switch-icon svg {
  width: 12px;
  height: 12px;
}

.theme-switch.is-light .theme-switch-icon {
  color: #F6A623;
}

/* View container */
#view-container {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  background: var(--bg);
  -webkit-overflow-scrolling: touch;
}

/* Mobile baseline hardening (iOS-first) */
@media (max-width: 900px) {
  :root {
    --sidebar-w: 216px;
  }

  #topbar {
    height: auto;
    min-height: var(--topbar-h);
    padding: 8px 12px;
    align-items: center;
    gap: 8px;
  }

  /* Fullscreen/theme toggles always stay on the same row as the title —
     the title/badge shrink and truncate instead of wrapping the toggles
     down to a second line. */
  .topbar-left {
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
  }

  .view-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .topbar-right {
    flex-shrink: 0;
    justify-content: flex-end;
  }

  .project-badge {
    max-width: 32vw;
  }

  .sidebar-header,
  .nav-item,
  .icon-btn,
  .btn-primary,
  .btn-secondary,
  .btn-danger,
  .articles-action-btn,
  .tag-add-btn,
  .tag-card-btn {
    min-height: 40px;
  }

  /* Keep the list toolbar height-consistent in compressed desktop windows */
  .list-toolbar .search-input,
  .list-toolbar .filter-select,
  .list-toolbar .year-input,
  .list-toolbar .articles-action-btn,
  .list-filter-toggle {
    height: 32px;
    min-height: 32px;
  }

  /* Same deal for Auto-Tag Rules' "Add a keyword" row — .autotag-add-btn
     also carries .btn-secondary, whose 40px touch-target min-height above
     would otherwise make it visibly taller than the input beside it. */
  .autotag-add-input,
  .autotag-add-btn {
    height: 30px;
    min-height: 30px;
  }

  /* Side-padding overrides for this breakpoint live at the very end of this
     file, not here — several of the classes below (.databases-view,
     .viz-view, etc.) have their base/desktop rule defined later in this
     file than this block, and CSS gives source order the tie-break for
     equal specificity regardless of which one is inside a media query, so
     a same-specificity override placed here would silently lose to a later
     unconditional rule. Keeping every side-padding override together at
     the end guarantees it always wins. */

  .theme-switch,
  .fullscreen-switch {
    min-height: 0;
    height: 28px;
  }
}

@media (max-width: 640px) {
  :root {
    /* Was 56px here — for BOTH states, which is what made the sidebar
       effectively un-expandable on phones: the toggle ran, but the expanded
       width stayed that of the icon rail, and the labels were forced to
       display:none on top of that. 56px now applies only to the collapsed
       rail (via --sidebar-collapsed-w); expanded is 216px, labels and all. */
    --sidebar-w: 216px;
    /* In the collapsed rail the nav icons and the footer's © are the only
       remaining alignment targets. */
    --sidebar-icon-pad-left: 17px;
  }

  /* Only the collapsed state is the icon-only rail. */
  .sidebar.collapsed .sidebar-header {
    justify-content: center;
    padding: 10px 0;
  }

  .sidebar.collapsed .nav-item {
    padding: 10px 0 10px var(--sidebar-icon-pad-left);
  }

  .sidebar .nav-item::before {
    left: 0;
  }
}

/* ----------------------------------------------------------
   6. Welcome view
   ---------------------------------------------------------- */
.welcome-view {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100%;
  padding: 40px 20px;
  text-align: center;
  gap: 32px;
}

/* Fixed to the true viewport (not absolute within .welcome-view) so its
   coordinate space is completely independent of the sidebar's width — the
   sidebar collapsing/expanding must not stretch or reflow the pattern.
   The sidebar (given a real stacking position below) simply paints over
   its own portion, exactly like an overlay. */
.hero-particles-canvas {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100dvh;
  z-index: -1;
  pointer-events: none;
}

.welcome-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.welcome-logo {
  width: 112px;
  height: 112px;
  color: var(--accent);
  opacity: .9;
}

.welcome-logo svg {
  width: 112px;
  height: 112px;
}

.welcome-hero h1 {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -.3px;
  color: var(--text);
}

.welcome-hero p {
  font-size: 15px;
  color: var(--text-muted);
  max-width: 420px;
  line-height: 1.6;
}

.welcome-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.welcome-auth-row {
  display: flex;
  gap: 10px;
}

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 6px 18px;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
  box-shadow: var(--shadow);
  border: 1px solid transparent;
}

.btn-primary:hover {
  background: var(--accent-hover);
}

.btn-primary svg {
  width: 16px;
  height: 16px;
}

.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--surface-2);
  color: var(--text);
  padding: 6px 16px;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
  border: 1px solid var(--border);
}

.btn-secondary:hover {
  background: var(--surface-3);
}

.btn-secondary svg {
  width: 16px;
  height: 16px;
}

.projects-add-btn {
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 500;
}

/* Add Tag (Tags view) reuses this class for sizing only — keep it muted,
   unlike the turquoise New Project button below. */
.projects-add-btn.tag-add-btn {
  color: var(--text-muted);
}

.projects-add-btn.tag-add-btn:hover {
  color: var(--text);
}

/* New Project, empty-projects state: extra emphasis so it's unmissable
   the first time a folder has no projects yet. */
.projects-add-btn--emphasize {
  box-shadow: 0 0 0 4px var(--accent-subtle), var(--shadow);
}

.btn-danger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--danger-subtle);
  color: var(--danger);
  padding: 9px 20px;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 600;
  border: 1px solid var(--danger);
}

.btn-danger:hover {
  background: var(--danger);
  color: #fff;
}

.btn-danger svg {
  width: 16px;
  height: 16px;
}

.welcome-compat-notice[hidden] {
  display: none;
}

.welcome-compat-notice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--danger-subtle);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  padding: 12px 16px;
  max-width: 460px;
  margin-top: 14px;
  /* Body text reads as plain/neutral, not alarm-red — var(--text) rather
     than a literal white so light theme (whose --danger-subtle is a pale
     pink) doesn't go illegible; this still lands near-white in dark
     theme, matching what was asked for. The icon keeps --danger below. */
  color: var(--text);
  font-size: 13px;
  font-weight: 400;
  text-align: left;
}

.welcome-compat-notice strong {
  font-weight: 400;
}

.welcome-compat-notice svg {
  color: var(--danger);
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 1px;
}

/* Points at the sidebar's About nav-item, where the onboarding walkthrough
   that used to live here as static text now lives instead (see
   #about-first-time in renderAbout). Vertically aligned to that button by
   JS (renderWelcome); horizontal tracking is free — this is positioned
   relative to .welcome-view, whose own left edge already sits flush
   against the sidebar regardless of collapsed/expanded width. */
.welcome-firsttime-hint {
  position: absolute;
  left: 14px;
  top: 50%;
  display: flex;
  align-items: center;
  gap: 6px;
  z-index: 5;
}

@media (prefers-reduced-motion: no-preference) {
  .welcome-firsttime-hint {
    animation: firsttimeHintNudge 2.4s ease-in-out infinite;
  }
}

@keyframes firsttimeHintNudge {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(-4px); }
}

.welcome-firsttime-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px 8px 10px;
  background: var(--accent-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 20px;
  color: var(--accent);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  box-shadow: var(--shadow);
}

.welcome-firsttime-btn:hover {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: var(--accent-hover);
}

.welcome-firsttime-btn svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

.welcome-firsttime-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-faint);
  flex-shrink: 0;
}

.welcome-firsttime-close:hover {
  background: var(--surface-3);
  color: var(--text-muted);
}

.welcome-firsttime-close svg {
  width: 11px;
  height: 11px;
}

/* Bottom-center of the Home view, independent of the (vertically centered)
   content group above it — absolute rather than a flex child so it stays
   pinned to the viewport edge regardless of how tall the content above it
   gets. */
.welcome-copyright {
  position: absolute;
  left: 0;
  right: 0;
  bottom: max(16px, env(safe-area-inset-bottom, 0px));
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: 11px;
  /* --text-faint fails WCAG AA against --bg in both themes (~2:1) — this
     line should read as quiet, not be functionally invisible. */
  color: var(--text-muted);
  user-select: none;
}

.welcome-copyright-icon {
  font-size: 13px;
}

/* ----------------------------------------------------------
   7. Projects view
   ---------------------------------------------------------- */
.projects-view {
  padding: 24px;
}

.projects-view h2 {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 6px;
}

.projects-subtitle {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 24px;
}

.projects-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 14px;
}

.project-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
  cursor: pointer;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-shadow: var(--shadow);
}

.project-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-lg);
}

.project-card.active {
  border-color: var(--accent);
  background: var(--accent-subtle);
}

.project-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}

.project-card-header-main {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* Rounded-corner icon frame, top-left of the card — click to pick an emoji,
   symbol, or 1-2 letter abbreviation (openProjectIconPicker in views.js).
   Falls back to auto-derived initials when nothing's been chosen. */
.project-icon-frame {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 11px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.project-icon-frame:hover {
  border-color: var(--accent);
  background: var(--surface-3);
}

.project-icon-frame svg {
  width: 19px;
  height: 19px;
}

.project-icon-frame.is-text {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--accent);
  background: var(--accent-subtle);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}

.project-card-titles {
  min-width: 0;
}

.project-card-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.35;
}

/* text-muted (not text-faint) — the previous color read as barely-visible
   against both the plain card background and the active card's accent
   tint. */
.project-card-date {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--text-muted);
  white-space: nowrap;
  margin-top: 2px;
}

/* Top-right dot on a project card: marks it as "currently in progress" */
.project-pin-dot {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 2px solid var(--border);
  background: transparent;
  flex-shrink: 0;
  margin-top: 2px;
  transition: background var(--transition), border-color var(--transition), transform var(--transition);
}

.project-pin-dot:hover {
  border-color: var(--accent);
  transform: scale(1.15);
}

.project-pin-dot.pinned {
  background: var(--accent);
  border-color: var(--accent);
}

.project-card-stats {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}

.project-card-stats-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 14px;
  margin-top: auto;
}

/* No flex `gap` — same reasoning as .corpus-banner-stat above: the markup
   is an icon followed by "<strong>N</strong> label text", and gap would
   double up with the text's own leading space. */
.stat-chip {
  display: flex;
  align-items: center;
  font-size: 12px;
  color: var(--text-muted);
}

.stat-chip svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  margin-right: 5px;
}

.stat-chip.selected {
  color: var(--accent);
}

.stat-chip.corpus {
  color: var(--green);
}

.project-card-desc {
  font-size: 12px;
  color: var(--text-faint);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
}

.project-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  align-self: flex-start;
}

.open-project-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--radius);
  font-size: 12px;
  font-weight: 500;
  background: var(--accent-subtle);
  color: var(--accent-hover);
  border: 1px solid var(--accent);
}

.open-project-btn:hover {
  background: var(--accent);
  color: var(--accent-ink);
}

.open-project-btn svg {
  width: 12px;
  height: 12px;
}

/* Opens that card's info inline in the Projects tab (see openProjectDetail
   in app.js) — this replaced the old standalone "Project Info" nav item. */
.project-info-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--radius);
  font-size: 12px;
  font-weight: 500;
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

.project-info-btn:hover {
  background: var(--surface-3);
  color: var(--text);
  border-color: var(--accent);
}

.project-info-btn svg {
  width: 12px;
  height: 12px;
}

.project-detail-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px 6px 10px;
  margin-bottom: 20px;
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 500;
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

.project-detail-back:hover {
  background: var(--surface-3);
  color: var(--text);
  border-color: var(--accent);
}

.project-detail-back svg {
  width: 14px;
  height: 14px;
}

/* Project icon picker — floating popup opened from .project-icon-frame
   (openProjectIconPicker in views.js). Same fixed-position/click-outside
   pattern as .toolbar-popup-menu, just with a richer body (two choice
   grids + a text input) instead of a plain item list. */
.project-icon-picker {
  position: fixed;
  z-index: 1001;
  width: 248px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
  padding: 12px;
}

.project-icon-picker-section + .project-icon-picker-section {
  margin-top: 12px;
}

.project-icon-picker-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--text-faint);
  margin-bottom: 6px;
}

.project-icon-picker-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 4px;
}

.project-icon-picker-item {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 7px;
  border: 1px solid transparent;
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 15px;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.project-icon-picker-item:hover {
  background: var(--surface-3);
  border-color: var(--accent);
  color: var(--text);
}

.project-icon-picker-item svg {
  width: 14px;
  height: 14px;
}

.project-icon-picker-letters {
  display: flex;
  gap: 6px;
}

.project-icon-picker-input {
  flex: 1;
  min-width: 0;
  height: 30px;
  padding: 0 10px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  text-align: center;
}

.project-icon-picker-input:focus {
  outline: none;
  border-color: var(--accent);
}

.project-icon-picker-set {
  height: 30px;
  padding: 0 12px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
}

.project-icon-picker-set:hover {
  background: var(--surface-3);
  color: var(--text);
  border-color: var(--accent);
}

.project-icon-picker-reset {
  width: 100%;
  margin-top: 12px;
  padding: 7px 10px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.project-icon-picker-reset:hover {
  background: var(--surface-2);
  color: var(--text);
  border-color: var(--accent);
}

/* ----------------------------------------------------------
   8. Articles view
   ---------------------------------------------------------- */
.articles-view {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* Wraps the stats banner + toolbar + "Showing N of M" line as a single
   collapsible unit above the article list. Plain block on desktop (its
   .collapsible-inner children just stack normally); on mobile/tablet
   (see the max-width:900px rule below) it becomes the collapse/reveal
   target for wireListHeaderCollapse's scroll listener in views.js. */
.list-header-collapsible {
  flex-shrink: 0;
}

/* List toolbar — shared by Articles / Selected / Corpus.
   Row 1: Filter toggle + Fetch/Tag/Export action buttons. Row 2-3 (only
   when the filter toggle is active): tag chips, year range. Row 4:
   search + sort, always visible. */
.list-toolbar {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.list-toolbar-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Search input */
.search-input-wrap {
  position: relative;
  flex: 1;
  min-width: 160px;
  max-width: 340px;
}

.search-input-wrap svg {
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 14px;
  color: var(--text-faint);
  pointer-events: none;
}

.search-input {
  width: 100%;
  height: 32px;
  box-sizing: border-box;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0 10px 0 28px;
  line-height: 1;
  font-size: 13px;
  color: var(--text);
  outline: none;
}

.search-input:focus {
  border-color: var(--accent);
  background: var(--surface);
}

.search-input::placeholder {
  color: var(--text-faint);
}

/* "Fields" multi-select trigger next to the search box — the popup itself
   (.search-fields-popup) reuses .toolbar-popup-menu for its box chrome and
   .settings-category-item for each checkbox row. */
.search-fields-toggle {
  flex-shrink: 0;
}

.search-fields-popup {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.search-fields-item {
  padding: 6px 10px;
  border-radius: 5px;
}

.search-fields-item:hover {
  background: var(--surface-2);
}

/* Filter controls */
.filter-select {
  height: 32px;
  box-sizing: border-box;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0 10px;
  line-height: 1;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  outline: none;
  max-width: 170px;
}

.filter-select:focus {
  border-color: var(--accent);
}

.filter-year-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--text-muted);
  flex-shrink: 0;
}

.year-input {
  width: 60px;
  height: 32px;
  box-sizing: border-box;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0 7px;
  line-height: 1;
  font-size: 13px;
  color: var(--text);
  outline: none;
  text-align: center;
}

.year-input:focus {
  border-color: var(--accent);
}

/* Stats row */
.articles-stats {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 6px 24px;
  font-size: 12px;
  color: var(--text-muted);
  background: var(--surface);
  border-bottom: 1px solid var(--border-subtle);
  flex-shrink: 0;
  flex-wrap: wrap;
}

.articles-stats strong {
  color: var(--text);
}

.stats-sep {
  color: var(--border);
}

/* Article list. No top padding: the first card's left tag-color stripe
   should sit flush against the toolbar above it, not float below a gap. */
.article-list {
  flex: 1;
  overflow-y: auto;
  padding: 0 0 8px;
}

.article-list-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 20px;
  gap: 12px;
  color: var(--text-faint);
}

.article-list-empty svg {
  width: 40px;
  height: 40px;
  opacity: .4;
}

.article-list-empty p {
  font-size: 14px;
}

/* Shared "no project open" notice — same text, same top-left placement,
   same Projects-navigating button, used by every view's empty state
   instead of each view inventing its own wording/position. */
.no-project-notice {
  padding: 24px;
  color: var(--text-faint);
}

.no-project-notice p {
  font-size: 14px;
  margin-bottom: 12px;
}

/* Swipe right selects an article then adds it to corpus; swipe left undoes
   the same steps in reverse — mirrors .history-item-swipe. Touch only;
   mouse/pen keep the tap-visible badge buttons in .article-tag-row-actions
   instead (see wireArticleSwipeGestures in views.js). */
.article-item-swipe {
  position: relative;
  overflow: hidden;
  touch-action: pan-y;
}

.article-swipe-action {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 96px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  cursor: pointer;
}

.article-swipe-action svg {
  width: 20px;
  height: 20px;
}

.article-swipe-reveal-left {
  right: 0;
}

.article-swipe-reveal-right {
  left: 0;
}

.article-swipe-danger {
  background: var(--danger);
}

.article-swipe-accent {
  background: var(--accent);
  color: var(--bg);
}

/* Article item */
.article-item {
  border-left: 3px solid var(--tag-color, transparent);
  padding: 10px 24px 10px 21px;
  border-bottom: 1px solid var(--border-subtle);
  cursor: pointer;
  position: relative;
  background: var(--bg);
}

.article-item:hover {
  background: var(--surface);
}

.article-item.expanded {
  background: var(--surface);
}

.article-item-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

/* .article-badges comes before .article-main in the DOM (see
   articleItemHTML in views.js) so that at the narrow-phone breakpoint
   below, floating it lets the title wrap around it. This "order" pair
   restores the original visual left-title/right-badges arrangement for
   every flex-based layout (i.e. everywhere above that breakpoint). */
.article-main {
  flex: 1;
  min-width: 0;
  order: 1;
}

.article-title {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text);
  line-height: 1.4;
}

.article-badges {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  flex-shrink: 0;
  margin-top: 1px;
  order: 2;
}

.article-badges-row {
  display: flex;
  align-items: center;
  gap: 4px;
}

.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 5px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
}

.badge svg {
  width: 13px;
  height: 13px;
  pointer-events: none;
}

.badge-selected {
  background: var(--accent-subtle);
  color: var(--accent-hover);
  border: 1px solid var(--accent);
}

.badge-corpus {
  background: var(--green-subtle);
  color: var(--green);
  border: 1px solid var(--green);
}

/* Source database badge (Scopus / arXiv / PubMed / Semantic Scholar) */
.badge-source {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  height: 20px;
  padding: 0 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .02em;
  white-space: nowrap;
  flex-shrink: 0;
}

.badge-source-scopus {
  background: rgba(230, 115, 0, .12);
  color: #e07020;
  border: 1px solid rgba(230, 115, 0, .35);
}

.badge-source-arxiv {
  background: rgba(179, 27, 27, .12);
  color: #e05555;
  border: 1px solid rgba(179, 27, 27, .35);
}

.badge-source-pubmed {
  background: rgba(50, 101, 153, .12);
  color: #6faad4;
  border: 1px solid rgba(50, 101, 153, .35);
}

.badge-source-s2 {
  background: rgba(24, 87, 164, .12);
  color: #7aaee8;
  border: 1px solid rgba(24, 87, 164, .35);
}

.badge-source-openalex {
  background: rgba(58, 176, 158, .12);
  color: #3ab09e;
  border: 1px solid rgba(58, 176, 158, .35);
}

/* Document-type pills must be content-sized, not forced into square icon badges. */
.badge-doctype {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  min-width: 0;
  height: 20px;
  padding: 0 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .02em;
  white-space: nowrap;
  flex-shrink: 0;
  background: rgba(165, 172, 184, .10);
  color: var(--text-muted);
  border: 1px solid rgba(165, 172, 184, .25);
}

.badge-doctype-preprint    { background: rgba(180, 148, 247, .15); color: #9d71e8; border: 1px solid rgba(180, 148, 247, .4); }
.badge-doctype-article     { background: rgba(165, 172, 184, .10); color: var(--text-muted); border: 1px solid rgba(165, 172, 184, .25); }
.badge-doctype-journal-issue{ background: rgba(122, 166, 217, .16); color: #87b5e6; border: 1px solid rgba(122, 166, 217, .45); }
.badge-doctype-book        { background: rgba(139, 115, 85,  .15); color: #a07850; border: 1px solid rgba(139, 115, 85,  .4); }
.badge-doctype-other       { background: rgba(165, 172, 184, .10); color: var(--text-muted); border: 1px solid rgba(165, 172, 184, .25); }
.badge-doctype-chapter     { background: rgba(139, 115, 85,  .10); color: #987040; border: 1px solid rgba(139, 115, 85,  .3); }
.badge-doctype-dataset     { background: rgba(0,   170,  85,  .12); color: #00aa55; border: 1px solid rgba(0,   170,  85,  .4); }
.badge-doctype-review      { background: rgba(100, 168, 255, .12); color: #64a8ff; border: 1px solid rgba(100, 168, 255, .4); }
.badge-doctype-dissertation{ background: rgba(244, 162,  97,  .15); color: #d08030; border: 1px solid rgba(244, 162,  97,  .4); }
.badge-doctype-report      { background: rgba(165, 172, 184, .15); color: #8a9aaa; border: 1px solid rgba(165, 172, 184, .4); }

.article-meta {
  display: flex;
  align-items: center;
  gap: 0;
  margin-top: 3px;
  font-size: 12px;
  color: var(--text-muted);
  flex-wrap: wrap;
  line-height: 1.6;
}

.meta-sep {
  margin: 0 5px;
  color: var(--text-faint);
}

/* Phone portrait: flex can't make text "wrap around a box for N lines,
   then take the full width back" — a float can, since a float only
   constrains the line boxes that overlap its own height, and everything
   after that flows at full width automatically. Switching the header to
   block layout and floating .article-badges (naturally ~3 title-lines
   tall, from its own two badge rows) gets exactly that: the title wraps
   beside the badges for as many lines as they take, then any further
   title lines — and .article-meta below, always, via clear — use the
   full row width instead of being squeezed for the article's whole
   height. (.article-badges must precede .article-main in the DOM for
   this wrap to happen at all; the order:1/2 pair above puts them back
   the other way round for every wider, flex-based layout.) */
@media (max-width: 480px) {
  .article-item-header {
    display: block;
  }

  .article-badges {
    float: right;
    margin: 1px 0 6px 10px;
  }

  .article-meta {
    clear: both;
  }
}

.abstract-indicator {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  opacity: 0.55;
}

.affiliation-indicator {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  opacity: 0.55;
}

.article-tag-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 5px;
}

.article-tag-row-indicators {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

.article-tag-row-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.abstract-indicator svg {
  width: 13px;
  height: 13px;
}

.affiliation-indicator svg {
  width: 13px;
  height: 13px;
}

.abstract-indicator.has-abstract {
  color: var(--accent);
  opacity: 0.7;
}

.affiliation-indicator.available {
  color: var(--accent);
  opacity: 0.7;
}

.affiliation-indicator.fetchable {
  color: var(--text-muted);
  opacity: 0.55;
}

.affiliation-indicator.unavailable {
  color: var(--text-faint);
  opacity: 0.35;
}

.abstract-indicator.no-abstract {
  color: var(--text-faint);
  opacity: 0.4;
}

.article-item:hover .affiliation-indicator {
  opacity: 1;
}

.article-item:hover .abstract-indicator {
  opacity: 1;
}

/* Third indicator, next to abstract/affiliation — a <button> (not a <span>
   like the other two) only when a citation network actually exists for
   this article, so it's clickable exactly when there's something to show. */
.network-indicator {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  opacity: 0.55;
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
}

.network-indicator svg {
  width: 13px;
  height: 13px;
}

.network-indicator.available {
  color: var(--accent);
  opacity: 0.7;
  cursor: pointer;
}

.network-indicator.available:hover {
  opacity: 1;
  color: var(--accent-hover);
}

.network-indicator.unavailable {
  color: var(--text-faint);
  opacity: 0.35;
  cursor: default;
}

.article-item:hover .network-indicator {
  opacity: 1;
}

.article-tag-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 8px;
  border-radius: 20px;
  font-size: 11px;
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
  width: fit-content;
  cursor: pointer;
}

.article-tag-pill:hover {
  background: var(--surface-3);
  border-color: var(--accent);
  color: var(--text);
}

/* Same look as .corpus-tag-chip: neutral grey border/background/text — only
   the .tag-dot carries the tag's actual color, not the whole pill. */

.article-tag-pill.tag-pill-unset {
  opacity: 0.45;
}

.article-tag-pill.tag-pill-unset:hover {
  opacity: 1;
}

.tag-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--tag-color, var(--border));
  flex-shrink: 0;
}

.tag-dot.tag-dot-empty {
  background: var(--border);
  opacity: 0.5;
}

/* Article expanded section */
.article-detail {
  display: none;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border-subtle);
}

.article-item.expanded .article-detail {
  display: block;
}

.article-abstract {
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.65;
}

/* One block per kind of information inside an expanded card — abstract,
   affiliation countries, affiliations. They carry different content but they
   are peers, so they share one heading style and one spacing rhythm instead
   of each bringing its own. Previously the abstract had a title while the
   affiliations used an inline bold "Label:" prefix, at a different size,
   with different gaps above and below. */
.article-detail-block + .article-detail-block {
  margin-top: 14px;
}

.article-detail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 20px;
  margin-bottom: 6px;
}

.article-detail-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* Beide Knoepfe im Kartenkopf — Read aloud und der Affiliations-Umschalter —
   teilen sich ein Grundmass. Sie standen vorher zufaellig gleich da, weil
   zwei getrennte Regeln zufaellig dieselben Werte trugen; dem Umschalter
   fehlten allerdings nowrap und flex-shrink, so dass seine Beschriftung
   unter Platzmangel in eine zweite Zeile rutschte und der Text aus dem
   20px hohen Kasten nach unten heraushing — genau das "etwas tiefer
   gestellt", das mal auffiel und mal nicht, je nach Fensterbreite und
   Anzahl der Affiliations. Ein gemeinsames Grundmass kann nicht mehr
   auseinanderlaufen. */
.article-speak-btn,
.article-affiliations-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  height: 20px;
  padding: 0 7px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.article-speak-btn svg {
  width: 11px;
  height: 11px;
}

.article-speak-btn:hover {
  border-color: var(--accent);
  color: var(--text);
}

.article-speak-btn.is-speaking {
  border-color: var(--accent);
  background: var(--accent-subtle);
  color: var(--accent-hover);
}

@media (max-width: 560px) {
  /* On a phone the label is the first thing that can go — the icon carries
     the meaning well enough there. */
  .article-speak-label { display: none; }
  .article-speak-btn { padding: 0 6px; }
}

.article-abstract.no-abstract {
  color: var(--text-faint);
  font-style: italic;
}

.article-detail-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  font-size: 12px;
  color: var(--text-muted);
}

.article-affiliation-countries {
  align-items: flex-start;
}

.article-detail-value {
  font-size: 12.5px;
  color: var(--text-muted);
  line-height: 1.6;
}

.article-country-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.article-country-flag {
  font-size: 12px;
  line-height: 1;
  font-family: 'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', emoji;
}

.article-country-sep {
  color: var(--text-faint);
}

/* Der Abstract-Kopf schiebt seinen Vorlese-Knopf bewusst an den rechten
   Kartenrand: dort ist er erwartbar, weil er zum ganzen Abschnitt gehoert.
   Der Affiliations-Umschalter gehoert dagegen zum Wort davor — er sagt,
   wie viele Namen sich hinter der Zeile verbergen. Mit
   justify-content: space-between landete er auf breiten Fenstern ueber
   tausend Pixel neben seiner Beschriftung und war schlicht nicht mehr zu
   finden. Also bleibt er hier direkt daneben stehen, so wie vor dem Umbau
   auf Abschnittsueberschriften. */
.article-detail-head-inline {
  justify-content: flex-start;
}

/* Raw affiliation names (Fetch Affiliations) — collapsed behind a toggle
   since a full institution list can run much longer than the country
   summary above it. */
/* Sits in .article-detail-head next to the section title, so it is sized to
   match the Read aloud button rather than the old inline label row. */
.article-affiliations-toggle:hover {
  background: var(--surface-3);
  color: var(--text);
  border-color: var(--accent);
}

.article-affiliations-toggle svg {
  width: 11px;
  height: 11px;
  transition: transform var(--transition);
}

.article-affiliations-toggle[aria-expanded="true"] svg {
  transform: rotate(90deg);
}

.article-affiliations-list {
  list-style: none;
  margin: 6px 0 0 0;
  padding: 8px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 12px;
  color: var(--text-muted);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.article-affiliations-list[hidden] {
  display: none;
}

/* Teilen-Blatt — nur dort zu sehen, wo der Browser kein eigenes hat
   (navigator.share fehlt, praktisch: Firefox am Rechner). Es haengt an
   <body>, nicht in der Karte: die Karte liegt in mehreren Behaeltern mit
   overflow, in denen ein Menue am unteren Rand abgeschnitten wuerde. */
.share-popup {
  position: fixed;
  z-index: 400;
  min-width: 208px;
  max-width: min(280px, calc(100vw - 24px));
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .28);
}

.share-popup-title {
  font-size: 11.5px;
  color: var(--text-faint);
  margin: 0;
  padding: 6px 8px 8px;
  border-bottom: 1px solid var(--border);
  line-height: 1.4;
}

.share-popup-items {
  display: flex;
  flex-direction: column;
  padding-top: 4px;
}

.share-popup-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition);
}

.share-popup-item:hover {
  background: var(--surface-3);
}

.share-popup-icon {
  display: inline-flex;
  color: var(--text-muted);
  flex-shrink: 0;
}

.share-popup-icon svg {
  width: 15px;
  height: 15px;
}

.share-popup-item:hover .share-popup-icon {
  color: var(--accent);
}

/* Identifier row — DOI and EID as linked items with copy buttons */
.article-id-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin-top: 10px;
  font-size: 12px;
}

.article-id-item {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--text-muted);
}

.article-id-label {
  display: flex;
  align-items: center;
  gap: 3px;
  font-weight: 600;
  color: var(--text-faint, var(--text-muted));
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .03em;
  flex-shrink: 0;
}

.article-id-label svg {
  width: 11px;
  height: 11px;
  flex-shrink: 0;
}

.article-id-item a {
  color: var(--accent);
  text-decoration: none;
  word-break: break-all;
}

.article-id-item a:hover {
  text-decoration: underline;
}

.article-id-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 2px 3px;
  cursor: pointer;
  color: var(--text-faint, var(--text-muted));
  flex-shrink: 0;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.article-id-copy svg {
  width: 12px;
  height: 12px;
  pointer-events: none;
}

.article-id-copy:hover {
  background: var(--surface-2);
  color: var(--text);
  border-color: var(--border);
}

.article-id-copy.copied {
  color: var(--green, #3fb950);
  border-color: var(--green, #3fb950);
}

.detail-field {
  display: flex;
  align-items: center;
  gap: 5px;
}

.detail-field svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  color: var(--text-faint);
}

.article-comment {
  margin-top: 8px;
  padding: 7px 10px;
  background: var(--surface-2);
  border-radius: var(--radius);
  font-size: 12px;
  color: var(--text-muted);
  border-left: 2px solid var(--warning);
}

/* ----------------------------------------------------------
   9. History view
   ---------------------------------------------------------- */
.history-view {
  padding: 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.history-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.hist-tabs {
  display: flex;
  gap: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 3px;
}

.hist-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: none;
  background: transparent;
  border-radius: calc(var(--radius) - 2px);
  color: var(--text-muted);
  font-size: 13px;
  cursor: pointer;
  transition: background .15s, color .15s;
}

.hist-tab svg {
  width: 14px;
  height: 14px;
}

.hist-tab:hover {
  color: var(--text);
}

.hist-tab.active {
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(0,0,0,.15);
}

.hist-tab-count {
  font-size: 11px;
  color: var(--text-faint);
}

.history-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.history-item-swipe {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  touch-action: pan-y;
}

.hist-swipe-action {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 96px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  cursor: pointer;
}

.hist-swipe-action svg {
  width: 20px;
  height: 20px;
}

.hist-swipe-reveal-left {
  right: 0;
}

.hist-swipe-reveal-right {
  left: 0;
}

.hist-swipe-danger {
  background: var(--danger);
}

.hist-swipe-accent {
  background: var(--accent);
  color: var(--bg);
}

.history-item {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

/* Die Kopfzeile ist jetzt zweistoeckig: oben die Zeile aus Pfeil, Text und
   Bubbles, darunter die Tag-Leiste ueber die ganze Kartenbreite. Vorher
   steckte die Leiste mit im Textblock und endete dort, wo die Bubbles
   anfingen - auf dem Telefon nach 93 von 310 Pixeln. */
.history-item-header {
  display: block;
  padding: 16px 20px;
  cursor: pointer;
  user-select: none;
}

.history-item-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
}

.history-item-header:hover {
  background: var(--surface-2);
}

.history-item.expanded .history-item-header {
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}

.history-chevron {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--text-faint);
  width: 16px;
  height: 16px;
  margin-top: 2px;
  transition: transform var(--transition);
}

.history-chevron svg {
  width: 16px;
  height: 16px;
}

.history-item.expanded .history-chevron {
  transform: rotate(90deg);
}

.history-meta {
  flex: 1;
  min-width: 0;
}

/* Action icons + source/count bubbles stacked in a column, right-aligned —
   mirrors .article-badges/.article-badges-row so both stay out of the
   .history-meta row and let the tag-distribution bar stretch much closer
   to the card's edge instead of being squeezed by everything on the right. */
/* Quelle oben, Anzahl darunter - untereinander statt nebeneinander. Zu
   zweit in einer Zeile brauchten sie 131 der 310 Pixel einer Telefonkarte
   und liessen der Query 93 uebrig, von der dann noch 13 Prozent zu sehen
   waren. */
.history-badges {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
  flex-shrink: 0;
}

.history-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.history-timestamp {
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
  margin-bottom: 4px;
}

.history-query-preview {
  font-size: 14px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: ui-monospace, 'Cascadia Code', monospace;
}

/* Tag distribution bar inside history card header. Lives in .history-meta
   (flex:1), so it already stretches to fill whatever width is left of the
   fixed-width action icons/bubbles on the right — the fix for "reaching
   to the end of the card" is really those bubbles taking less room (see
   .history-count below) plus this bar being visually thicker. */
.hist-tag-bar {
  display: flex;
  height: 6px;
  border-radius: 3px;
  overflow: hidden;
  margin-top: 10px;
  gap: 1px;
}

.hist-tag-seg {
  display: block;
  height: 100%;
  border-radius: 1px;
  min-width: 2px;
  transition: opacity 0.15s;
}

.hist-tag-seg-none {
  background: var(--surface-3) !important;
}

.hist-tag-seg:hover,
.hist-tag-bar:hover .hist-tag-seg {
  opacity: 0.8;
}

/* Dieselbe Pille wie das Quellenabzeichen darueber - gleiche Hoehe, gleiche
   Schrift, gleiche Rundung. Und eine Mindestbreite, damit "19 results" und
   "1000 results" nicht unterschiedlich breit untereinander stehen: die
   beiden Bubbles bilden eine Spalte, und eine Spalte mit zwei verschieden
   breiten Pillen sieht aus wie ein Versehen. */
/* Eine feste Breite fuer beide Pillen, auf allen Fenstern gleich: sonst ist
   die Karte mit 19 Treffern schmaler bepillt als die mit 1000, und die
   Spalte rechts franst ueber die Liste hinweg aus. 86px fasst auch
   "10000 results". */
.history-badges .badge-source,
.history-count {
  min-width: 86px;
}

.history-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 20px;
  padding: 0 8px;
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .02em;
  color: var(--text-muted);
  white-space: nowrap;
  flex-shrink: 0;
  box-sizing: border-box;
}

.hist-copy-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  color: var(--text-muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s, background .15s, color .15s;
}

.hist-copy-btn svg {
  width: 15px;
  height: 15px;
}

.history-item-header:hover .hist-copy-btn {
  opacity: 1;
}

.hist-copy-btn:hover {
  background: var(--surface-2);
  color: var(--accent);
}

.hist-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  color: var(--text-muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s, background .15s, color .15s;
}

.hist-action-btn svg {
  width: 15px;
  height: 15px;
}

.history-item-header:hover .hist-action-btn {
  opacity: 1;
}

.hist-action-btn:hover {
  background: var(--surface-2);
  color: var(--accent);
}

.hist-action-btn.hist-action-danger:hover {
  color: var(--danger);
}

.history-query-full {
  display: none;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}

.history-item.expanded .history-query-full {
  display: block;
}

.history-query-full pre {
  font-family: ui-monospace, 'Cascadia Code', monospace;
  font-size: 12px;
  color: var(--text-muted);
  white-space: pre-wrap;
  word-break: break-all;
  line-height: 1.55;
}

.history-results-list {
  display: none;
  padding: 8px 0;
  max-height: 360px;
  overflow-y: auto;
}

.history-item.expanded .history-results-list {
  display: block;
}

.history-result-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 7px 16px;
  border-bottom: 1px solid var(--border-subtle);
}

.history-result-item:last-child {
  border-bottom: none;
}

.history-result-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--tag-color, var(--border));
  flex-shrink: 0;
  margin-top: 5px;
}

.history-result-title {
  font-size: 13px;
  color: var(--text);
  flex: 1;
  min-width: 0;
  line-height: 1.4;
}

.history-result-year {
  font-size: 11px;
  color: var(--text-faint);
  flex-shrink: 0;
  margin-top: 2px;
}

/* ----------------------------------------------------------
   10. Project info (inline detail panel inside .projects-view)
   ---------------------------------------------------------- */
.project-info-cols {
  display: grid;
  grid-template-columns: minmax(300px, 1fr) minmax(260px, 1fr);
  gap: 24px;
  align-items: start;
}

@media (max-width: 700px) {
  .project-info-cols {
    grid-template-columns: 1fr;
  }
}

.project-meta-edit {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 28px;
}

.project-name-input {
  font-size: 20px;
  font-weight: 700;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--text);
  padding: 4px 8px;
  width: 100%;
  transition: border-color .15s, background .15s;
}

.project-name-input:hover,
.project-name-input:focus {
  background: var(--surface);
  border-color: var(--border);
  outline: none;
}

.project-name-input:focus {
  border-color: var(--accent);
}

.project-desc-input {
  font-size: 14px;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: 4px 8px;
  width: 100%;
  resize: vertical;
  font-family: inherit;
  transition: border-color .15s, background .15s, color .15s;
}

.project-desc-input:hover,
.project-desc-input:focus {
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
  outline: none;
}

.project-desc-input:focus {
  border-color: var(--accent);
}

.info-section {
  margin-bottom: 28px;
}

.info-section h3 {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--text-faint);
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

.info-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 10px;
}

.info-field {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 14px;
}

.info-field label {
  display: block;
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--text-faint);
  margin-bottom: 4px;
}

.info-field span {
  font-size: 14px;
  color: var(--text);
}

.tag-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tag-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 3px 10px 3px 7px;
  font-size: 12px;
  color: var(--text-muted);
}

.tag-legend-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* ----------------------------------------------------------
   11. Loading & error states
   ---------------------------------------------------------- */
.loading-spinner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 60px 20px;
  color: var(--text-muted);
}

.spinner-ring {
  width: 32px;
  height: 32px;
  animation: spin .8s linear infinite;
}

.spinner-ring-track,
.spinner-ring-arc {
  fill: none;
  stroke-width: 3;
}

.spinner-ring-track {
  stroke: var(--border);
}

.spinner-ring-arc {
  stroke: var(--accent);
  stroke-linecap: round;
  /* circumference = 2*pi*18 ≈ 113 — show ~28% of it as the moving arc */
  stroke-dasharray: 32 200;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.error-notice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--danger-subtle);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin: 20px;
  color: var(--danger);
  font-size: 13px;
}

.error-notice svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 1px;
}

/* ----------------------------------------------------------
   12. Scrollbar styling
   ---------------------------------------------------------- */
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 3px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--text-faint);
}

/* Database quick-link items — reuse nav-item; override color & text-decoration */
a.db-link {
  text-decoration: none;
  color: var(--text-muted);
}

a.db-link:visited {
  color: var(--text-muted);
}

.db-link:hover {
  color: var(--text);
}

/* Small colored letter badge instead of SVG icon */
.nav-icon.db-badge {
  background: var(--db-color, #888);
  color: #fff;
  border-radius: 4px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: -.2px;
  width: 18px;
  height: 18px;
  line-height: 1;
  flex-shrink: 0;
}

/* Collapsed sidebar: show only the badge, hide label (already hidden via sidebar-label) */
.sidebar.collapsed .nav-icon.db-badge {
  margin: 0 auto;
}



/* ----------------------------------------------------------
   14. Corpus view
   ---------------------------------------------------------- */
.corpus-banner {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 14px 24px;
  background: var(--green-subtle);
  border-bottom: 1px solid var(--green);
  flex-wrap: wrap;
  flex-shrink: 0;
}

/* No flex `gap` here on purpose: each stat's markup is an icon followed by
   "<strong>N</strong> label text" — a raw text node as a flex item gets
   wrapped in its own anonymous flex box, so `gap` would add its full value
   AGAIN between the <strong> and that trailing text on top of the single
   space already in the text, reading as a doubled gap. Spacing the icon off
   via its own margin instead keeps the number-to-label gap a plain single
   space, everywhere this markup is used. */
.corpus-banner-stat {
  display: flex;
  align-items: center;
  font-size: 13px;
  color: var(--green);
}

.corpus-banner-stat svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  margin-right: 7px;
}

.corpus-banner-stat strong {
  font-weight: 700;
}

.corpus-tag-breakdown {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 10px 24px 12px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--surface);
  flex-shrink: 0;
}

.corpus-tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 20px;
  font-size: 11.5px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-muted);
  cursor: pointer;
}

.corpus-tag-chip:hover {
  border-color: var(--accent);
  color: var(--text);
}

.corpus-tag-chip.active {
  border-color: var(--tag-color, var(--accent));
  background: var(--surface-3);
  color: var(--text);
}

.corpus-tag-chip .tag-dot {
  width: 7px;
  height: 7px;
}

.corpus-tag-chip .chip-count {
  font-size: 10.5px;
  color: var(--text-faint);
}

/* Toggle for the filter panel (tag chips + year range) below — sits inline
   in .list-toolbar-buttons-row alongside Fetch/Tag/Export, shared by
   Articles / Selected / Corpus so it looks and behaves identically
   everywhere. */
/* Ghost-button style shared by Filter/Fetch/Tag/Export — flat by default
   (no permanent fill), a soft hover tint, and color reserved for the one
   state that actually needs it: Filter while toggled on. */
.list-filter-toggle,
.articles-action-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 11px;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.list-filter-toggle svg,
.articles-action-btn svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  opacity: .8;
}

.list-filter-toggle:hover,
.articles-action-btn:hover {
  background: var(--surface-2);
  color: var(--text);
  border-color: color-mix(in srgb, var(--text-muted) 45%, var(--border));
}

.list-filter-toggle:hover svg,
.articles-action-btn:hover svg {
  opacity: 1;
}

.list-filter-toggle.active {
  background: var(--accent-subtle);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

.list-filter-toggle.active svg {
  opacity: 1;
}

/* ----------------------------------------------------------
   15. Settings view
   ---------------------------------------------------------- */
.settings-view {
  padding: 24px;
}

.settings-view h2 {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 6px;
}

/* Ein Kopfbereich fuer jeden Tab.
   Vorher fing jeder Tab anders an: About und Privacy mit einer 18px-
   Ueberschrift, Settings direkt mit blassem 13px-Text, Projects, Tags,
   History und Visualisations gleich mit einer 36-40px hohen Werkzeugleiste.
   Der Kasten sass zwar ueberall exakt 20px unter der Kopfleiste — gemessen —
   aber gelesen wurde das als drei verschiedene Abstaende, weil oben eben
   drei verschiedene Dinge standen. Jetzt beginnt jeder Tab mit demselben
   Block, und damit stimmt der Abstand nicht nur rechnerisch, sondern auch
   fuer das Auge. Der Bezugspunkt ist About, so wie gewuenscht. */
.view-head {
  margin-bottom: 28px;
}

.view-title {
  font-size: 18px;
  font-weight: 600;
  margin: 0;
}

/* Der Abstand nach oben gilt nur, wenn wirklich eine Ueberschrift darueber
   steht - und das ist nur noch in About der Fall. Ueberall sonst nennt die
   Kopfleiste den Tab bereits beim Namen; ihn darunter zu wiederholen war
   eine Zeile ohne Aussage. */
.view-subtitle {
  font-size: 13px;
  color: var(--text-muted);
  margin: 0;
}

.view-title + .view-subtitle {
  margin-top: 6px;
}

.settings-subtitle {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 28px;
}

/* ── About view ─────────────────────────────────────────────────────────── */

.about-links-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 28px;
}

.about-link-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background .15s, border-color .15s;
}

.about-link-btn:hover {
  background: var(--surface-3, var(--surface-2));
  border-color: var(--accent);
  color: var(--accent);
}

.about-link-btn svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
}

.about-link-btn--license {
  border-color: var(--accent);
  color: var(--accent);
}

.about-v2-banner {
  border-left: 4px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
  border-radius: 0 var(--radius-md, 8px) var(--radius-md, 8px) 0;
  padding: 16px 20px;
  margin-bottom: 20px;
}

.about-v2-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

.about-v2-badge {
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .05em;
  padding: 2px 7px;
  border-radius: 4px;
  flex-shrink: 0;
}

.about-v2-header strong {
  font-size: 14px;
  color: var(--text);
}

.about-v1-banner {
  border-left: 4px solid var(--border);
  background: var(--surface);
  border-radius: 0 var(--radius-md, 8px) var(--radius-md, 8px) 0;
  padding: 16px 20px;
  margin-bottom: 28px;
}

.about-v1-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

.about-v1-badge {
  background: var(--surface-3, #555);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .05em;
  padding: 2px 7px;
  border-radius: 4px;
  flex-shrink: 0;
}

.about-v1-header strong {
  font-size: 14px;
  color: var(--text-muted);
}

/* Einleitungsabsatz eines Abschnitts auf der Datenschutzseite. Vorher liefen
   die beiden obersten als .field-hint (11,5px, --text-faint) und der Rest mit
   denselben Werten inline an acht Stellen wiederholt — dasselbe Element in
   zwei Größen und zwei Graustufen, je nachdem wo man hinsah. Eine Definition,
   und der Abstand nach unten sitzt hier statt in Inline-Styles, damit
   zwischen Erklärung und der folgenden Aufzählung überall gleich viel Luft
   ist. */
.privacy-lead {
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.7;
  margin-top: 10px;
  margin-bottom: 12px;
}

.privacy-lead:first-of-type {
  margin-top: 2px;
}

.privacy-lead + .privacy-lead {
  margin-top: -2px;
}

.about-feature-list {
  list-style: none;
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.6;
  padding-left: 0;
  margin: 0;
  display: grid;
  gap: 8px;
}

.about-feature-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.about-li-icon {
  display: inline-flex;
  width: 18px;
  height: 18px;
  min-width: 18px;
  min-height: 18px;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  margin-top: 1px;
}

.about-li-icon svg {
  width: 16px;
  height: 16px;
}

.about-feature-list--muted {
  color: var(--text-faint);
}

.about-feature-list--muted .about-li-icon {
  color: var(--text-muted);
}

.about-feature-list li strong {
  color: var(--text);
}

.about-feature-list--muted li strong {
  color: var(--text-muted);
}



.settings-section {
  margin-bottom: 32px;
}

/* ── Collapsible section ──────────────────────────────────────────────
   One pattern shared by Settings, Tags and About. Built on native
   <details>/<summary>, so keyboard operation, screen-reader semantics and
   in-page find-on-this-page all come for free — and, unlike a JS accordion,
   the open state cannot desync from what is actually rendered. Which
   sections are open is persisted (see collapseSection in views.js), because
   these views re-render on almost every action and a purely in-memory state
   would snap shut under the user.

   The point of collapsing here is not to hide things: it is to make a long
   page skimmable. Every summary therefore carries a .collapse-meta with the
   gist of what is inside (a key's status, the current folder, how many tags
   exist), so a closed section still answers the question most people opened
   it for. */
.collapse-section {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: 10px;
  overflow: hidden;
}

.collapse-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  cursor: pointer;
  user-select: none;
  /* Both needed: `list-style:none` for Firefox, the ::-webkit- rule below
     for Safari, which still ignores it. */
  list-style: none;
  transition: background var(--transition);
}

.collapse-summary::-webkit-details-marker {
  display: none;
}

.collapse-summary:hover {
  background: var(--surface-2);
}

.collapse-summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.collapse-chevron {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--text-muted);
}

.collapse-chevron svg {
  width: 14px;
  height: 14px;
}

@media (prefers-reduced-motion: no-preference) {
  .collapse-chevron {
    transition: transform var(--transition);
  }
}

.collapse-section[open] > .collapse-summary .collapse-chevron {
  transform: rotate(90deg);
  color: var(--accent);
}

/* flex: 1 1 0 statt der Vorgabe 0 1 auto - und das ist hier keine
   Feinheit. Unter 560px darf die Kopfzeile umbrechen, damit der Status
   rechts eine eigene Zeile bekommt. Ein Flex-Element wandert aber in die
   naechste Zeile, sobald seine *Inhaltsbreite* nicht mehr neben den Pfeil
   passt - und eine lange Frage im Q&A ist breit. Sie landete deshalb
   komplett unter dem Pfeil, linksbuendig am Kartenrand. min-width: 0
   allein hilft nicht: es erlaubt das Schrumpfen erst, nachdem die Zeile
   schon zugeteilt ist. Mit flex-basis 0 ist die angenommene Breite null,
   die Frage bleibt neben dem Pfeil und bricht innerhalb ihres eigenen
   Kastens um - so wie es die Eintraege unter "More" ohnehin schon taten. */
.collapse-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  flex: 1 1 0;
  min-width: 0;
}

/* The at-a-glance status on the right of the header — on narrow screens it
   wraps under the title instead of squeezing it. */
.collapse-meta {
  margin-left: auto;
  padding-left: 12px;
  font-size: 12px;
  color: var(--text-muted);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
  min-width: 0;
}

.collapse-meta.is-set {
  color: var(--accent);
}

.collapse-body {
  padding: 16px;
  border-top: 1px solid var(--border-subtle);
}

/* The last field inside an open section needs no extra spacing — the border
   already provides it. */
.collapse-body > .form-field:last-child,
.collapse-body > .settings-save-row:last-child {
  margin-bottom: 0;
}

@media (max-width: 560px) {
  /* Die Aktionsknoepfe erscheinen erst beim Ueberfahren mit der Maus - auf
     einem Telefon also nie, dort wischt man stattdessen. Unsichtbar belegten
     sie trotzdem ihren Platz und nahmen ihn der Query weg. */
  .history-actions {
    display: none;
  }

  /* Zwei Zeilen statt einer: bei 310 Pixel Kartenbreite war von einer
     Suchanfrage genau ein Achtel zu lesen, danach kamen die Punkte. Der
     Umbruch bricht mitten im Wort, weil eine Query keine Woerter im Sinne
     des Zeilenumbruchs hat - eine Klammer oder ein Operator ist ein
     genauso guter Halt. */
  .history-query-preview {
    white-space: normal;
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .history-item-header {
    padding: 14px 14px;
  }

  .history-item-top {
    gap: 10px;
  }

}

@media (max-width: 560px) {
  .collapse-summary {
    flex-wrap: wrap;
    row-gap: 2px;
  }
  .collapse-meta {
    margin-left: 24px;
    padding-left: 0;
    text-align: left;
    flex-basis: 100%;
  }
}

/* Group heading above a run of sections. Adds order without introducing a
   second collapsible level — nested accordions are exactly the kind of depth
   that makes a settings page hard to scan. */
/* Passwort- und E-Mail-Änderung: eigene Blöcke innerhalb von Cloud Sync,
   durch eine Linie getrennt, damit sie nicht mit dem Anmeldestatus darüber
   verschwimmen — aber ohne eigenen Rahmen, sonst steckte wieder eine Karte
   in einer Karte. */
.account-credentials {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}

.account-credentials h4 {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 10px;
}

/* Account deletion — set apart from the rest of Cloud Sync, because it is
   the one control here that cannot be undone. */
.danger-zone {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.danger-zone h4 {
  font-size: 13px;
  font-weight: 600;
  color: var(--danger);
  margin-bottom: 8px;
}

.deletion-pending {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--danger);
  background: var(--danger-subtle);
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--text);
  line-height: 1.6;
}

.deletion-pending svg {
  width: 16px;
  height: 16px;
  color: var(--danger);
  flex-shrink: 0;
  margin-top: 2px;
}

.settings-error {
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--danger);
  line-height: 1.5;
}

.settings-error[hidden] { display: none; }

/* Workspace und Account
   ---------------------
   Beide Ansichten sind bewusst offen statt aufklappbar: sie existieren, weil
   genau diese Angaben vorher hinter einer zugeklappten Sektion lagen und
   deshalb niemand fand. Sie noch einmal zuzuklappen waere derselbe Fehler
   eine Ebene hoeher. */
.settings-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 14px 16px;
  margin-bottom: 10px;
}

/* Die aktive Wahl steht ausgeschrieben da statt nur als gesetzter
   Radioknopf - "wo liegen meine Projekte gerade" soll man lesen koennen,
   ohne erst zu pruefen, welcher Punkt angehakt ist. */
.workspace-active-card {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-subtle);
  margin-bottom: 4px;
}

.workspace-active-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--accent);
  flex-shrink: 0;
}

.workspace-active-icon svg {
  width: 17px;
  height: 17px;
}

.workspace-active-body {
  min-width: 0;
}

.workspace-active-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}

.workspace-active-name {
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
  margin: 3px 0 0;
}

.workspace-active-where {
  font-size: 12.5px;
  color: var(--text-muted);
  line-height: 1.6;
  margin: 5px 0 0;
}

.account-identity-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: 4px;
}

.account-identity-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent-subtle);
  color: var(--accent);
  flex-shrink: 0;
}

.account-identity-icon svg {
  width: 17px;
  height: 17px;
}

.account-identity-body {
  flex: 1;
  min-width: 0;
}

.account-identity-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}

.account-identity-email {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--text);
  margin: 3px 0 0;
  overflow-wrap: anywhere;
}

.settings-group-label {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  /* --text-muted, not --text-faint: faint drops to roughly 2:1 against the
     page in light theme, and this label is what tells you which group you
     are looking at. */
  color: var(--text-muted);
  margin: 24px 0 8px;
}

.settings-group-label:first-of-type {
  margin-top: 6px;
}

/* Prose inside a collapsible answer that is not part of .qa-list (currently
   only "First time here?"). */
.qa-answer {
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.7;
}

.qa-answer p + p {
  margin-top: 10px;
}

/* Closing line of About — version and copyright. Quiet by design: it is
   reference information, not something to lead with. */
.about-colophon {
  margin-top: 28px;
  padding-top: 14px;
  border-top: 1px solid var(--border-subtle);
  font-size: 12px;
  color: var(--text-faint);
  line-height: 1.7;
}

/* Q&A entries in About: same mechanism, drawn flatter — a list of
   questions, not a stack of cards. */
.qa-list .collapse-section {
  border-radius: var(--radius-sm);
  margin-bottom: 6px;
}

.qa-list .collapse-summary {
  padding: 10px 14px;
}

.qa-list .collapse-title {
  font-weight: 500;
}

.qa-list .collapse-body {
  padding: 12px 14px 14px;
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.7;
}

.qa-list .collapse-body p + p {
  margin-top: 8px;
}

.qa-list .collapse-body ul {
  padding-left: 18px;
  line-height: 1.8;
  margin-top: 6px;
}

.settings-section h3 {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--text-faint);
  margin-bottom: 14px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

/* Landing flash for a section reached via a jump-link (currently just
   #about-first-time, via Home's "First time here?" hint) — box-shadow/
   background only, so it never shifts layout the way padding would. */
@keyframes sectionHintPulse {
  0%, 100% { background-color: transparent; box-shadow: none; }
  25%, 75% { background-color: var(--accent-subtle); box-shadow: 0 0 0 8px var(--accent-subtle); }
}

.section-hint-pulse {
  border-radius: var(--radius);
  animation: sectionHintPulse 1.6s ease-in-out 1;
}

.scopus-api-notice > div {
  min-width: 0;
}

.scopus-api-notice-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  min-width: 16px;
  color: var(--accent);
  margin-top: 2px;
}

.scopus-api-notice-icon svg {
  width: 13px;
  height: 13px;
}

.form-field {
  margin-bottom: 16px;
}

.form-field label {
  display: block;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-muted);
  margin-bottom: 5px;
}

.form-field .field-hint {
  font-size: 11.5px;
  color: var(--text-faint);
  margin-top: 4px;
  line-height: 1.5;
}

.secret-input-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}

.secret-input-row .form-input {
  flex: 1;
}

.secret-toggle-btn {
  padding: 8px 12px;
  min-width: 88px;
  justify-content: center;
  flex-shrink: 0;
}

.secret-toggle-btn svg {
  width: 14px;
  height: 14px;
}

.form-input {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 12px;
  font-size: 13.5px;
  color: var(--text);
  outline: none;
}

.form-input:focus {
  border-color: var(--accent);
  background: var(--surface);
}

.form-input::placeholder {
  color: var(--text-faint);
}

.form-input.monospace {
  font-family: ui-monospace, 'Cascadia Code', monospace;
  font-size: 13px;
  letter-spacing: .03em;
}

.settings-save-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
}

/* Sits after the buttons, not between them: at opacity 0 it still occupies
   its box, and standing in the middle it pushed Save and Test visibly apart
   for no reason. */
.settings-saved-msg {
  font-size: 12.5px;
  color: var(--green);
  opacity: 0;
  margin-left: 4px;
  transition: opacity .3s;
}

/* Voice picker and its Test button on one line — going from "different voice"
   to "this is how it sounds" should be a single glance. The picker takes the
   whole remaining width: voice names are short, but the list is what the row
   is about, so the button claims only what it needs. nowrap is deliberate —
   without this rule the select (block, width:100%) and the button stacked,
   which is what made the button look like a stray control on its own line. */
.tts-voice-row {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 10px;
}

.tts-voice-row .form-input {
  flex: 1 1 auto;
  min-width: 0;
}

.tts-voice-row .tts-test-btn {
  flex: 0 0 auto;
  padding: 7px 14px;
}

.tts-rate-slider {
  width: 100%;
  accent-color: var(--accent);
}

.settings-saved-msg.visible {
  opacity: 1;
}

.backend-switch-row {
  display: flex;
  gap: 16px;
  margin-top: 6px;
}

.backend-switch-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13.5px;
  color: var(--text);
  cursor: pointer;
}

.cloud-auth-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding: 8px 12px;
  border-radius: var(--radius);
  background: var(--accent-subtle);
  color: var(--accent);
  font-size: 13px;
}

.cloud-auth-status svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

.cloud-auth-status .btn-secondary {
  margin-left: auto;
  padding: 4px 12px;
  font-size: 12.5px;
}

/* ----------------------------------------------------------
   16. Modal overlay
   ---------------------------------------------------------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .55);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

@media (prefers-reduced-motion: no-preference) {
  .modal-overlay {
    animation: fadeIn .15s ease;
  }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  width: 100%;
  max-width: 460px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

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

.modal-header h3 {
  font-size: 15px;
  font-weight: 600;
}

.modal-body {
  padding: 18px 18px 10px;
}

.modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 12px 18px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

.modal-error {
  font-size: 12.5px;
  color: var(--danger);
  padding: 0 18px 10px;
  display: none;
}

.modal-error.visible {
  display: block;
}

.modal-subtitle {
  font-size: 11.5px;
  color: var(--text-faint);
  margin-top: 2px;
}

.modal-prisma-detail {
  max-width: 640px;
}

/* ── Citation network modal ── */
.modal-network {
  max-width: 1040px;
  position: relative;
}

@media (max-width: 640px) {
  /* Near-fullscreen on phones — the diagram needs the room, and a fixed
     800px-cropped-to-viewport modal was unusable there. */
  .modal-overlay:has(.modal-network) {
    padding: 0;
  }
  .modal-network {
    max-width: 100%;
    width: 100%;
    height: 100%;
    max-height: 100%;
    border-radius: 0;
  }
}

.network-legend {
  display: flex;
  gap: 16px;
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 4px;
}

.network-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.network-legend-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}

.network-legend-dot-citing {
  background: var(--accent);
}

.network-legend-dot-citedby {
  background: var(--green, #22c55e);
}

.modal-network .modal-body {
  max-height: 72vh;
  overflow-y: auto;
}

@media (max-width: 640px) {
  .modal-network .modal-body {
    max-height: calc(100% - 60px);
  }
}

.network-canvas {
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
  overflow: hidden;
}

/* A fixed-size pannable/zoomable viewport, not a container that grows with
   content or shrinks the diagram to fit — content lives inside the inner
   .network-viewport <g>, whose own transform (translate+scale) drag/wheel/
   pinch handlers update directly; see wiring in renderArticleNetworkModal. */
.network-svg {
  display: block;
  width: 100%;
  height: 480px;
  touch-action: none;
  cursor: grab;
}

.network-svg.is-panning {
  cursor: grabbing;
}

@media (max-width: 640px) {
  .network-svg {
    height: 380px;
  }
}

.network-edge {
  stroke: var(--border);
  stroke-width: 1.5;
}

.network-edge-cites {
  stroke: color-mix(in srgb, var(--accent) 55%, var(--border));
}

.network-edge-citedby {
  stroke: color-mix(in srgb, var(--green, #22c55e) 55%, var(--border));
}

/* Marker content lives in <defs> but is still styleable by class like any
   other SVG element — each edge type references its own marker id so the
   arrowhead can be given a fixed matching fill (a shared marker can't
   inherit the referencing line's `stroke` color). */
.network-arrowhead-cites {
  fill: color-mix(in srgb, var(--accent) 55%, var(--border));
}

.network-arrowhead-citedby {
  fill: color-mix(in srgb, var(--green, #22c55e) 55%, var(--border));
}

/* paint-order:stroke + a bg-colored halo keeps every label legible
   regardless of what's behind it (edges, other nodes' circles crossing
   under the text) — plain fill alone had too little contrast, per
   feedback, especially for the faint year line. */
.network-node-label {
  font-size: 10.5px;
  fill: var(--text);
  font-weight: 600;
  paint-order: stroke fill;
  stroke: var(--bg);
  stroke-width: 3px;
  stroke-linejoin: round;
}

.network-node-year {
  font-size: 9.5px;
  fill: var(--text-muted);
  font-weight: 600;
  paint-order: stroke fill;
  stroke: var(--bg);
  stroke-width: 3px;
  stroke-linejoin: round;
}

.network-node[data-eid] {
  cursor: grab;
}

.network-node[data-eid]:active {
  cursor: grabbing;
}

/* :not(.network-node-hit) excludes the invisible larger touch-target
   circle each node also carries — only the visible circle should grow. */
.network-node[data-eid]:hover circle:not(.network-node-hit) {
  stroke: var(--accent);
  r: 15;
}

.network-node[data-eid]:hover .network-node-label {
  fill: var(--accent);
}

.network-center-label {
  font-size: 13px;
  font-weight: 700;
}

.network-empty-note,
.network-hint {
  font-size: 12px;
  color: var(--text-faint);
  text-align: center;
  margin-top: 8px;
}

.network-header-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

/* External nodes/edges — fetched from outside the project on click, so
   they're visually distinct (dashed) from the free, always-available
   in-project links. */
.network-node.network-external circle {
  stroke-dasharray: 2 2;
  opacity: 0.75;
}

.network-node.network-external .network-node-label {
  fill: var(--text-faint);
  font-style: italic;
}

.network-node.network-external {
  cursor: grab;
}

.network-node.network-external:active {
  cursor: grabbing;
}

.network-node.network-external:hover circle {
  stroke: var(--text-muted);
}

.network-node.network-external:hover .network-node-label {
  fill: var(--text-muted);
}

.network-edge-external {
  stroke-dasharray: 3 3;
  opacity: 0.7;
}

.network-load-row {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 12px;
  flex-wrap: wrap;
}

.network-load-btn {
  padding: 5px 14px;
  font-size: 12.5px;
}

.network-load-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.network-error-note {
  font-size: 12px;
  color: var(--danger);
  text-align: center;
  margin-top: 8px;
}

/* Hover card — desktop mouse only, see wiring in renderArticleNetworkModal.
   Positioned absolutely within .modal-network (which is position:relative). */
.network-tooltip {
  position: absolute;
  z-index: 20;
  max-width: 260px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 10px 12px;
  pointer-events: none;
}

.network-tooltip.hidden {
  display: none;
}

.network-tooltip-title {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.35;
  margin-bottom: 4px;
}

.network-tooltip-meta {
  font-size: 11px;
  color: var(--text-muted);
  line-height: 1.4;
  margin-bottom: 4px;
}

.network-tooltip-role {
  font-size: 10.5px;
  color: var(--accent);
  font-weight: 500;
}

.modal-body-scroll {
  max-height: 60vh;
  overflow-y: auto;
}

.prisma-modal-row {
  padding: 8px 0;
  border-bottom: 1px solid var(--border-subtle, var(--border));
}

.prisma-modal-row:last-child {
  border-bottom: none;
}

.prisma-modal-row-title {
  font-size: 13px;
  color: var(--text);
}

.prisma-modal-row-meta {
  font-size: 11.5px;
  color: var(--text-faint);
  margin-top: 2px;
}

/* ----------------------------------------------------------
   17. Projects view — new project button
   ---------------------------------------------------------- */
.projects-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
  flex-wrap: wrap;
  gap: 10px;
}

.projects-header .projects-subtitle {
  margin: 0;
}

.projects-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ----------------------------------------------------------
   18. Databases view
   ---------------------------------------------------------- */
.databases-view {
  padding: 24px;
}

.databases-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 10px;
  margin-bottom: 8px;
}

.db-card {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 8px);
  padding: 14px 16px;
  text-decoration: none;
  color: var(--text);
  cursor: pointer;
  transition: background .15s, border-color .15s, transform .1s;
  position: relative;
  border-left: 4px solid var(--db-color, var(--border));
  font: inherit;
  text-align: left;
  width: 100%;
  min-height: 88px;
  height: 100%;
}

.db-card:hover {
  background: var(--surface-2);
  border-color: var(--db-color, var(--accent));
  transform: translateY(-1px);
}

.db-card-badge {
  background: var(--db-color, #888);
  color: #fff;
  border-radius: 6px;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.db-card-badge svg {
  width: 22px;
  height: 22px;
  display: block;
}

.db-card-badge-fallback {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: -.3px;
}

.db-card-body {
  flex: 1;
  min-width: 0;
}

.db-card-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.db-card-desc {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 2px;
  line-height: 1.4;
  min-height: calc(1.4em * 2);
  max-height: calc(1.4em * 2);
  overflow: visible;
}

.db-card-arrow {
  color: var(--text-faint);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  transition: color .15s;
}

.db-card:hover .db-card-arrow {
  color: var(--db-color, var(--accent));
}

.db-card-arrow svg {
  width: 15px;
  height: 15px;
}

.databases-section {
  margin-top: 28px;
}

.databases-section:first-child {
  margin-top: 0;
}

.databases-section-heading {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.databases-section-note {
  font-size: 12px;
  color: var(--text-faint);
  margin: 0 0 10px;
}

.databases-grid {
  grid-auto-rows: 1fr;
}

.databases-section h3 {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.db-card--integrated {
  border-left-width: 4px;
}

.db-integrated-pill {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(100, 168, 255, .15);
  color: var(--accent);
  border: 1px solid rgba(100, 168, 255, .35);
  vertical-align: middle;
  margin-left: 5px;
  letter-spacing: .02em;
  text-transform: uppercase;
}

.scopus-api-notice {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  background: var(--accent-subtle);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 12px 14px;
  font-size: 13px;
  color: var(--text);
  line-height: 1.6;
}

.scopus-api-notice-warn {
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-color: var(--danger);
}

.scopus-api-notice-warn .scopus-api-notice-icon {
  color: var(--danger);
}

.scopus-api-notice-caution {
  background: color-mix(in srgb, var(--warning) 12%, transparent);
  border-color: var(--warning);
}

.scopus-api-notice-caution .scopus-api-notice-icon {
  color: var(--warning);
}

.scopus-api-notice-icon {
  flex-shrink: 0;
  color: var(--accent);
  margin-top: 1px;
}

.scopus-api-notice-icon svg {
  width: 13px;
  height: 13px;
}

.scopus-test-result {
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text);
}

.scopus-test-result a {
  color: var(--accent);
}

.scopus-test-result.scopus-test-ok {
  background: var(--green-subtle);
  border-color: var(--green);
}

.scopus-test-result.scopus-test-fail {
  background: var(--danger-subtle);
  border-color: var(--danger);
}

.link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  cursor: pointer;
  font-size: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.link-btn:hover {
  color: var(--accent-hover);
}

.settings-category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 6px 12px;
  margin-top: 8px;
}

.settings-category-item {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}

.settings-category-item input[type="checkbox"] {
  accent-color: var(--accent);
  width: 15px;
  height: 15px;
  cursor: pointer;
}

.settings-category-actions {
  display: flex;
  gap: 14px;
  margin-top: 8px;
  font-size: 12.5px;
}

/* ----------------------------------------------------------
   18b. Tags view
   ---------------------------------------------------------- */
.tags-view {
  padding: 20px 24px;
}

.tags-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.tags-auto-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 9px 10px;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.tags-auto-note svg {
  width: 14px;
  height: 14px;
  color: var(--accent);
  margin-top: 1px;
  flex-shrink: 0;
}

.tags-auto-note strong {
  color: var(--text);
}

/* Keep top spacing below header consistent across main views */
#view-container > .projects-view,
#view-container > .articles-view,
#view-container > .history-view,
#view-container > .settings-view,
#view-container > .about-view,
#view-container > .tags-view,
#view-container > .databases-view,
#view-container > .search-view,
#view-container > .viz-view {
  padding-top: 20px;
}

/* Avoid a visible spacer bar in Articles/Selected/Corpus and Search views. */
#view-container > .articles-view,
#view-container > .search-view {
  padding-top: 0;
}

.tags-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  color: var(--text-muted);
  flex-wrap: wrap;
}

.tags-summary svg {
  width: 15px;
  height: 15px;
  vertical-align: -2px;
  flex-shrink: 0;
}

.tags-section {
  margin-bottom: 28px;
}

.tags-section h3 {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  margin-bottom: 12px;
}

.tags-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 10px;
}

.tag-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px 14px;
  transition: background .15s;
}

.tag-card:hover {
  background: var(--surface-2);
}

.tag-card-swatch-wrap {
  position: relative;
  display: flex;
  flex-shrink: 0;
  cursor: pointer;
}

.tag-card-swatch-wrap:hover .tag-card-swatch {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.tag-swatch-picker {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  padding: 0;
  border: none;
}

.tag-card-swatch {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  flex-shrink: 0;
  margin-top: 2px;
}

.tag-card-body {
  flex: 1;
  min-width: 0;
}

.tag-card-name {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text);
  line-height: 1.4;
  word-break: break-word;
}

.tag-card-count {
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 3px;
}

.tag-card-actions {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
  margin-top: 1px;
}

.tag-card-btn {
  background: none;
  border: none;
  padding: 5px;
  cursor: pointer;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  transition: color .15s, background .15s;
}

.tag-card-btn:hover {
  color: var(--accent);
  background: var(--surface-2);
}

.tag-card-delete-btn:hover {
  color: var(--danger);
  background: none;
}

.tag-card-btn svg {
  width: 15px;
  height: 15px;
}

.tag-inline-input {
  width: 100%;
  font-size: 13.5px;
  font-weight: 500;
  background: var(--surface-3);
  border: 1px solid var(--accent);
  border-radius: 4px;
  padding: 2px 6px;
  color: var(--text);
  outline: none;
}

/* ----------------------------------------------------------
   Auto-Tag Rules view
   ---------------------------------------------------------- */
/* No padding of its own — .autotag-view only ever renders nested inside
   .tags-view now (Auto-Tag Rules is a section within Tags, not its own
   top-level view anymore), which already provides the page inset; adding
   a second layer of padding here was making this section sit narrower
   than Colour Schemes/Tags above it. */

/* One column, not a grid — a multi-column grid sizes every card in a row
   to the tallest one in that row, so expanding a single card used to also
   stretch its still-collapsed neighbors without actually expanding them. */
.autotag-grid {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.autotag-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px;
}

.autotag-card-header {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Only when something actually follows (i.e. the card is expanded) —
   a collapsed card's header is its whole card, no gap needed below it. */
.autotag-card-header:not(:last-child) {
  margin-bottom: 10px;
}

.autotag-card-expand-toggle {
  background: none;
  border: none;
  padding: 5px;
  cursor: pointer;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  flex-shrink: 0;
  transition: color .15s, background .15s;
}

.autotag-card-expand-toggle:hover {
  color: var(--accent);
  background: var(--surface-2);
}

.autotag-card-expand-toggle svg {
  width: 15px;
  height: 15px;
  transition: transform .15s ease;
}

.autotag-card.is-expanded .autotag-card-expand-toggle svg {
  transform: rotate(90deg);
}

.autotag-card-swatch-wrap {
  position: relative;
  display: flex;
  flex-shrink: 0;
  cursor: pointer;
}

.autotag-card-swatch-wrap:hover .autotag-card-swatch {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.autotag-swatch-picker {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  padding: 0;
  border: none;
}

.autotag-card-swatch {
  width: 14px;
  height: 14px;
  border-radius: 4px;
  flex-shrink: 0;
}

.autotag-card-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  flex: 1;
  min-width: 0;
  word-break: break-word;
}

.autotag-card-count {
  font-size: 11px;
  color: var(--text-faint, var(--text-muted));
  white-space: nowrap;
}

.autotag-kw-list {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-bottom: 10px;
}

.autotag-kw-empty {
  font-size: 11.5px;
  color: var(--text-faint, var(--text-muted));
  font-style: italic;
}

.autotag-kw-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11.5px;
  line-height: 1.4;
  padding: 3px 4px 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
}

.autotag-kw-chip svg {
  width: 11px;
  height: 11px;
  color: var(--warning);
  flex-shrink: 0;
}

.autotag-kw-chip--conflict {
  background: color-mix(in srgb, var(--warning) 16%, var(--surface));
  border-color: color-mix(in srgb, var(--warning) 50%, transparent);
}

.autotag-kw-remove {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  flex-shrink: 0;
  transition: background .15s, color .15s;
}

.autotag-kw-remove:hover {
  background: var(--danger);
  color: #fff;
}

.autotag-kw-remove svg {
  width: 10px;
  height: 10px;
}

.autotag-add-row {
  display: flex;
  gap: 6px;
}

.autotag-add-input {
  flex: 1;
  min-width: 0;
  height: 30px;
  padding: 0 10px;
  font-size: 12.5px;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  outline: none;
}

.autotag-add-input:focus {
  border-color: var(--accent);
}

.autotag-add-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 12px;
  height: 30px;
  font-size: 12px;
  white-space: nowrap;
}

.autotag-add-btn svg {
  width: 13px;
  height: 13px;
}

.tags-section-header {
  display: flex;
  align-items: center;
  margin-bottom: 12px;
}

.tags-section-header h3 {
  margin-bottom: 0;
}

.tag-add-btn {
  align-self: center;
  padding: 7px 14px;
}

.tag-add-btn:hover {
  color: var(--text);
}

.tag-add-btn svg {
  width: 13px;
  height: 13px;
}

.scheme-panel {
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-bottom: 16px;
}

.scheme-panel-intro {
  font-size: 12.5px;
  color: var(--text-muted);
  margin-bottom: 14px;
}

.scheme-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
}

.scheme-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  text-align: left;
  transition: border-color .15s, background .15s;
}

.scheme-btn:hover {
  background: var(--surface-3, var(--surface-2));
  border-color: var(--accent);
}

.scheme-dots {
  display: flex;
  gap: 4px;
}

.scheme-dots span {
  display: block;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex-shrink: 0;
}

.scheme-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  line-height: 1;
}

.scheme-desc {
  font-size: 11.5px;
  color: var(--text-muted);
  line-height: 1.3;
}

.tag-add-form {
  margin-bottom: 20px;
}

.tag-add-form-inner {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  flex-wrap: wrap;
}

.tag-color-input {
  width: 40px;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: none;
  cursor: pointer;
  padding: 2px;
}

.tag-name-input {
  flex: 1;
  min-width: 160px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--text);
  outline: none;
}

.tag-name-input:focus {
  border-color: var(--accent);
}

.btn-sm {
  padding: 5px 12px;
  font-size: 12.5px;
}

.btn-warn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: #f97316;
  color: #fff;
  padding: 10px 24px;
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
  border: 1px solid transparent;
}

.btn-warn:hover {
  background: #ea6c0a;
}

.btn-warn svg {
  width: 16px;
  height: 16px;
}

/* ----------------------------------------------------------
   19. Utilities
   ---------------------------------------------------------- */
.hidden {
  display: none !important;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* ----------------------------------------------------------
   19. Search / Scopus Query view
   ---------------------------------------------------------- */

.search-view {
  display: flex;
  height: 100%;
  overflow: hidden;
}

/* The editor is first in the DOM (see renderSearch in views.js) so that on
   mobile — where .search-view stacks into a column further down — it and
   everything in/above it (the query box, Search button, Max results) never
   move when Field Codes/Past Terms expand below it. Desktop uses `order`
   to put field-codes back on the left and past-terms on the right, the
   same 3-column layout DOM order alone used to give it for free. */
.search-editor-panel {
  order: 0;
}

/* ── Side panels ── */
.search-panel {
  width: 260px;
  min-width: 200px;
  max-width: 320px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex-shrink: 0;
  order: -1;
}

.search-panel.is-collapsed {
  display: none;
}

.search-terms-panel {
  border-right: none;
  border-left: 1px solid var(--border);
  order: 1;
}

.search-panel-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 11px 12px 10px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.search-panel-header svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

.search-panel-title {
  flex: 1;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-muted);
}

.search-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* ── Field codes ── */
.fc-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.fc-group {
  margin-bottom: 2px;
}

.fc-group-header {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  padding: 6px 2px 3px;
}

.fc-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.fc-chip {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 7px;
  font-size: 11px;
  font-family: 'Menlo', 'Consolas', 'Monaco', monospace;
  color: var(--text);
  cursor: pointer;
  line-height: 1.6;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.fc-chip:hover {
  background: var(--accent-subtle, rgba(88,166,255,.12));
  border-color: var(--accent);
  color: var(--accent);
}

/* ── Term history list ── */
.search-terms-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.search-term-row {
  display: flex;
  align-items: center;
  gap: 4px;
}

.search-term-item {
  text-align: left;
  background: none;
  border: none;
  padding: 4px 6px;
  font-size: 12.5px;
  color: var(--text);
  cursor: pointer;
  border-radius: 4px;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--transition), color var(--transition);
}

.search-term-item:hover {
  background: var(--surface-2);
  color: var(--accent);
}

.search-term-delete {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  opacity: .72;
  flex-shrink: 0;
  transition: background var(--transition), border-color var(--transition), color var(--transition), opacity var(--transition);
}

.search-term-delete svg {
  width: 12px;
  height: 12px;
  pointer-events: none;
}

.search-term-row:hover .search-term-delete,
.search-term-delete:focus-visible {
  opacity: 1;
}

.search-term-delete:hover,
.search-term-delete:focus-visible {
  background: rgba(248, 81, 73, .12);
  border-color: rgba(248, 81, 73, .35);
  color: #f85149;
}

.search-terms-empty {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.6;
  padding: 4px 2px;
}

/* ── Center editor panel ── */
.search-editor-panel {
  flex: 1;
  overflow-y: auto;
  padding: 20px 24px 32px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.search-editor-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
}

.search-mobile-toggles {
  display: none;
}

.search-mobile-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-muted);
  white-space: nowrap;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.search-mobile-toggle.active {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-subtle);
}

.search-mobile-toggle svg {
  width: 13px;
  height: 13px;
}

/* Drop the icon (keep the label) on these two buttons specifically when the
   main sidebar is collapsed to its icon-only rail — more room is freed up,
   so the icon starts to feel redundant there; expanded sidebar keeps it. */
#sidebar.collapsed ~ #main .search-mobile-toggle svg {
  display: none;
}

/* Database selector tabs in search view */
.search-db-tabs {
  display: flex;
  gap: 3px;
  flex: 1;
  flex-wrap: wrap;
}

/* Mirrors .db-card's design language (Databases view) — colored left
   border + colored abbreviation badge via the same --db-color custom
   property — just compact enough for a row of selector buttons. */
.search-db-card {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px 5px 9px;
  font-size: 12.5px;
  font-weight: 500;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--db-color, var(--border));
  border-radius: var(--radius);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
  white-space: nowrap;
}

.search-db-card-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 5px;
  background: var(--db-color, #888);
  color: #fff;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: -.2px;
  flex-shrink: 0;
}

.search-db-card.active {
  background: color-mix(in srgb, var(--db-color, var(--accent)) 14%, var(--surface));
  border-color: var(--db-color, var(--accent));
  color: var(--text);
}

.search-db-card:hover:not(.active) {
  background: var(--surface-2);
  color: var(--text);
  border-color: var(--db-color, var(--text-muted));
}

@media (max-width: 900px) {
  .search-view,
  .search-view.search-view-mobile {
    flex-direction: column;
    height: auto;
    overflow: visible;
  }

  /* Reset the desktop order override — stacked, the editor must come
     first (order:-1) with both panels after it (0/1), never before it,
     so expanding either one only ever pushes content that's already
     below, not the query box/Search button/Max results above. */
  .search-editor-panel {
    order: -1;
  }

  .search-panel {
    order: 0;
  }

  .search-terms-panel {
    order: 1;
  }

  .search-panel,
  .search-terms-panel {
    width: 100%;
    min-width: 0;
    max-width: none;
    border-left: none;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }

  .search-panel-body {
    max-height: 260px;
  }

  .search-editor-panel {
    padding: 14px 12px 20px;
  }

  .search-editor-toolbar {
    flex-wrap: wrap;
    align-items: stretch;
  }

  .search-mobile-toggles {
    display: flex;
    gap: 8px;
    width: 100%;
  }

  .search-mobile-toggle {
    flex: 1;
    min-height: 38px;
  }

  .search-db-tabs {
    width: 100%;
    flex: 0 0 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: 2px;
  }

  .search-db-tabs::-webkit-scrollbar {
    height: 4px;
  }
}

/* Hint box for free databases */
.search-db-hint {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 9px 13px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius, 6px);
  font-size: 12.5px;
  color: var(--text-muted);
  line-height: 1.55;
}

.search-db-hint code {
  font-family: 'Menlo', 'Consolas', monospace;
  font-size: 11.5px;
  background: var(--bg);
  padding: 1px 4px;
  border-radius: 3px;
  color: var(--text);
}

.search-db-hint svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--accent);
}

.search-editor-title {
  flex: 1;
  margin: 0;
  font-size: 20px;
  font-weight: 700;
}

.search-textarea {
  width: 100%;
  min-height: 220px;
  resize: vertical;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius, 6px);
  color: var(--text);
  font-family: 'Menlo', 'Consolas', 'Monaco', monospace;
  font-size: 13.5px;
  line-height: 1.65;
  padding: 10px 12px;
  box-sizing: border-box;
  overflow-y: hidden;
  transition: border-color var(--transition);
}

.search-textarea:focus {
  outline: none;
  border-color: var(--accent);
}

/* ── Actions row ── */
.search-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.search-max-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  font-size: 13px;
  color: var(--text-muted);
}

/* ── Progress ── */
.search-progress-wrap {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.search-progress-track {
  height: 4px;
  background: var(--surface-2);
  border-radius: 2px;
  overflow: hidden;
}

.search-progress-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 2px;
  transition: width .35s ease;
}

.search-progress-msg {
  font-size: 12px;
  color: var(--text-muted);
}

/* ── Fetch progress overlay ─────────────────────────────────────────────── */
.fetch-progress-overlay {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--surface);
  border-top: 2px solid var(--accent);
  padding: 10px 24px 12px;
  z-index: 9999;
  transform: translateY(100%);
  transition: transform 0.2s ease;
  display: block;
}
@media (prefers-reduced-motion: no-preference) {
  .fetch-progress-overlay { transition: transform 0.2s ease; }
  .fpo-ring-progress { transition: stroke-dashoffset 0.25s ease; }
}
.fetch-progress-overlay.visible {
  transform: translateY(0);
}

.fpo-main {
  display: flex;
  align-items: center;
  gap: 16px;
}

.fpo-circle {
  width: 48px;
  height: 48px;
  position: relative;
  flex-shrink: 0;
}

.fpo-circle-svg {
  width: 48px;
  height: 48px;
  transform: rotate(-90deg);
}

.fpo-ring-track,
.fpo-ring-progress {
  fill: none;
  stroke-width: 3;
}

.fpo-ring-track {
  stroke: var(--surface-2);
}

.fpo-ring-progress {
  stroke: var(--accent);
  stroke-linecap: round;
}

.fpo-circle-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 800;
  color: var(--accent);
  letter-spacing: .01em;
}

.fpo-text {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.fpo-header {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-muted);
}

.fpo-track {
  height: 5px;
  background: var(--surface-2);
  border-radius: 3px;
  overflow: hidden;
}

.fpo-bar {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: 3px;
  transition: width .2s ease;
}

/* ── Notice boxes ── */
.search-notice {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius, 6px);
  padding: 10px 12px;
  font-size: 13px;
  color: var(--text-muted);
}

.search-notice svg {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  margin-top: 1px;
}

.search-notice-warn {
  border-color: #e3a008;
  background: rgba(227, 160, 8, .08);
  color: var(--text);
}

.search-notice-error {
  border-color: var(--danger);
  background: rgba(248, 81, 73, .08);
  color: var(--text);
}

.search-notice-success {
  border-color: var(--green, #3fb950);
  background: rgba(63, 185, 80, .08);
  color: var(--text);
}

@media (prefers-reduced-motion: no-preference) {
  .search-progress-bar {
    transition: width .35s ease;
  }
}

/* ══════════════════════════════════════════════════════════════
   ANNOTATION ACTION BADGES  (S / C / tag swatch)
   ══════════════════════════════════════════════════════════════ */

.badge-toggle {
  cursor: pointer;
  border: 1px solid transparent;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.badge-toggle.badge-dim {
  background: var(--surface-2);
  color: var(--text-faint, var(--text-muted));
  border-color: var(--border);
}

.badge-toggle.badge-dim:hover {
  color: var(--text-muted);
  border-color: var(--text-muted);
}

.badge-tag-btn {
  padding: 0;
  width: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.badge-tag-swatch {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--badge-tag-color, var(--border));
  pointer-events: none;
}

.badge-tag-btn.badge-tag-set .badge-tag-swatch {
  background: var(--badge-tag-color);
}

/* ══════════════════════════════════════════════════════════════
   TAG PICKER POPUP
   ══════════════════════════════════════════════════════════════ */

.tag-picker-popup {
  position: fixed;
  z-index: 1000;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
  min-width: 180px;
  max-height: 280px;
  overflow-y: auto;
  padding: 4px;
}

.tag-picker-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  border-radius: 5px;
}

.tag-picker-item:hover {
  background: var(--surface-2);
}

.tag-picker-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid var(--border);
  flex-shrink: 0;
}

/* Export menu popup */
/* Generic floating choice menu — used by the Filter row's Fetch/Tag/Export
   buttons (openToolbarPopupMenu in views.js) so all three share one look
   instead of three near-duplicate popups. */
.toolbar-popup-menu {
  position: fixed;
  z-index: 1001;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
  min-width: 208px;
  padding: 4px;
}

.toolbar-popup-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 7px 10px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  border-radius: 5px;
}

.toolbar-popup-item:hover {
  background: var(--surface-2);
}

.toolbar-popup-item svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  color: var(--accent);
}

.toolbar-popup-item--warn svg {
  color: #f97316;
}

.toolbar-popup-item--warn:hover {
  background: color-mix(in srgb, #f97316 15%, transparent);
}

/* ══════════════════════════════════════════════════════════════
   ANNOTATION TOAST
   ══════════════════════════════════════════════════════════════ */

.annotation-toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 18px;
  font-size: 13px;
  color: var(--text);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .35);
  z-index: 2000;
  opacity: 0;
  transition: opacity .2s ease, transform .2s ease;
  pointer-events: none;
}

.annotation-toast.visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.annotation-toast-error {
  border-color: var(--danger);
  color: var(--danger);
}

/* ══════════════════════════════════════════════════════════════
   VISUALIZATIONS VIEW
   ══════════════════════════════════════════════════════════════ */

.viz-view {
  padding: 20px 24px 40px;
}

.viz-funnel {
  display: flex;
  align-items: stretch;
  gap: 0;
  margin-bottom: 36px;
  flex-wrap: wrap;
}

.viz-funnel-step {
  flex: 1;
  min-width: 120px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px 18px;
}

.viz-funnel-sep {
  display: flex;
  align-items: center;
  padding: 0 4px;
  color: var(--text-faint, var(--text-muted));
}

.viz-funnel-sep svg {
  width: 16px;
  height: 16px;
}

.viz-funnel-num {
  font-size: 28px;
  font-weight: 700;
  line-height: 1.1;
}

.viz-funnel-label {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 2px;
  margin-bottom: 10px;
}

.viz-funnel-bar-wrap {
  height: 4px;
  background: var(--surface-2);
  border-radius: 2px;
  overflow: hidden;
}

.viz-funnel-bar {
  height: 100%;
  border-radius: 2px;
  transition: width .4s ease;
}

.viz-bar-all      { background: var(--text-muted); }
.viz-bar-selected { background: var(--accent); }
.viz-bar-corpus   { background: var(--green, #3fb950); }

.viz-section {
  margin-top: 4px;
}

/* Controls bar: dropdowns (chart type, group-by) on the left, everything
   clickable (scope tabs, Hide None / Hide Legend / Export PNG) on the right. */
.viz-section-controls {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 10px;
}

.viz-controls-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.viz-controls-row--top {
  justify-content: space-between;
}

.viz-controls-left,
.viz-controls-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* Long option labels ("Tag Distribution — Doughnut", "Group by Document
   Type") need more room than the 170px cap every other .filter-select
   gets — otherwise the select box itself clips the chosen label. Chart
   type gets the most room since its labels are the longest of the two
   ("Screening Flow (PRISMA)"); Group by's own labels are shorter, so it
   doesn't need to match. */
.viz-chart-select {
  min-width: 260px;
  max-width: 320px;
}

.viz-groupby-select {
  min-width: 170px;
  max-width: 200px;
}

/* Chart heading sits below the controls row */
.viz-chart-heading {
  margin: 0 0 14px 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: .01em;
}

.viz-mode-tabs {
  display: flex;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  flex-shrink: 0;
}

.viz-mode-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  padding: 5px 14px;
  line-height: 1;
  font-size: 13px;
  background: transparent;
  border-right: 1px solid var(--border);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.viz-mode-tab:last-child {
  border-right: none;
}

.viz-mode-tab:hover {
  background: var(--surface-3);
  color: var(--text);
}

.viz-mode-tab.active {
  background: var(--accent);
  color: var(--accent-ink);
}

.viz-bars {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Height is user-resizable via the shared .viz-col-resize-handle below
   (inline style sets the actual px value, see getChartHeight in views.js)
   — this is just a sane first-paint fallback. */
/* No fixed height/scroll — the drag handle controls each row's own track
   thickness (--bar-track-h) instead of a container to clip within, so the
   list just takes up however much total height that produces. */
.viz-bars-scroll {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.viz-bar-row {
  display: flex;
  align-items: center;
  gap: 10px;
  transition: opacity .15s;
  cursor: default;
}

/* Sized to the longest label actually showing (--bar-label-w, set per
   render in renderBars) instead of a flat 260px regardless of content —
   that used to reserve several unnecessary cm before the bars even start
   on a chart of short labels. */
.viz-bar-label {
  width: var(--bar-label-w, 20ch);
  min-width: 60px;
  max-width: 260px;
  font-size: 12.5px;
  color: var(--text);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 0;
}

.viz-bar-track {
  flex: 1;
  height: var(--bar-track-h, 20px);
  background: var(--surface-2);
  border-radius: 4px;
  overflow: hidden;
}

.viz-bar-fill {
  height: 100%;
  border-radius: 4px;
}

@media (prefers-reduced-motion: no-preference) {
  .viz-bar-fill {
    transition: width .35s ease;
  }
}

.viz-bar-count {
  width: 80px;
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
}

.viz-bar-pct {
  color: var(--text-faint, var(--text-muted));
}

.viz-empty-bars {
  text-align: center;
  padding: 32px;
  color: var(--text-faint, var(--text-muted));
  font-size: 13px;
}

.viz-legend-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 32px;
  padding: 0 12px;
  gap: 6px;
  border-radius: 6px;
  border: 1px solid color-mix(in srgb, var(--text-muted) 34%, transparent);
  background: var(--surface);
  color: var(--text);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 1px 0 color-mix(in srgb, var(--text) 10%, transparent);
  transition: background var(--transition), color var(--transition), border-color var(--transition), opacity var(--transition), box-shadow var(--transition), transform var(--transition);
}

.viz-legend-toggle:hover:not(:disabled) {
  color: var(--text);
  border-color: var(--accent);
  background: var(--surface-2);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}

.viz-legend-toggle.is-active {
  color: var(--accent-ink);
  border-color: var(--accent);
  background: var(--accent);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--accent) 45%, transparent);
}

.viz-export-btn {
  background: color-mix(in srgb, var(--accent) 16%, var(--surface));
  border-color: color-mix(in srgb, var(--accent) 48%, transparent);
  color: var(--accent-hover);
}

.viz-export-btn:hover:not(:disabled) {
  color: var(--accent-ink);
  background: var(--accent);
}

.viz-export-btn svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
}

/* Doughnut chart — centered, legend below, same stacked layout as the
   Year and World charts instead of the old side-by-side arrangement. */
/* Height is user-resizable via the shared .viz-col-resize-handle below
   (inline style sets the actual px value, see getChartHeight in views.js)
   — this is just a sane first-paint fallback. */
.viz-doughnut-scroll {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 320px;
  /* No scrolling/clipping — .viz-doughnut-svg scales to fill this box (see
     below), so there's never overflow content to reveal by scrolling. */
  overflow: hidden;
  padding: 4px 0;
}

/* Scales with .viz-doughnut-scroll's height (set by the drag handle) —
   width follows automatically from the square viewBox's aspect ratio, so
   resizing actually shrinks/grows the graphic instead of clipping it. */
.viz-doughnut-svg {
  width: auto;
  height: 100%;
  max-width: 100%;
  flex-shrink: 0;
}

/* Sits outside .viz-doughnut-scroll on purpose — the resize handle above
   only ever resizes the graphic, never the legend (see renderDoughnut in
   views.js). Hide Legend is the only thing that removes it. */
.viz-doughnut-legend {
  width: 100%;
  margin-top: 16px;
}

.viz-world-wrap {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 16px;
  padding: 4px 0;
}

.viz-world-wrap.legend-hidden {
  justify-content: flex-start;
}

.viz-world-stage {
  width: 100%;
  min-width: 0;
  flex: none;
}

.viz-world-svg {
  width: 100%;
  height: auto;
  max-height: none;
  aspect-ratio: 1000 / 520;
  display: block;
  margin: 0 auto;
}

.viz-world-wrap.legend-hidden .viz-world-svg {
  width: 100%;
}

/* Sits between the map and the legend (map -> status -> legend) — the data-
   coverage note, the color scale, and the source credit used to float as
   overlays on top of the map itself; moved here so the map surface only
   ever shows the map. */
.viz-world-status {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 16px;
}

.viz-world-note {
  font-size: 11.5px;
  color: var(--text-muted);
}

.viz-world-legend {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 4px 16px;
  align-content: start;
  padding-top: 4px;
}

.viz-world-country {
  transition: fill .18s ease, opacity .18s ease;
}

.viz-world-country-has-data {
  cursor: pointer;
}

.viz-world-country-has-data:hover,
.viz-world-country-active {
  fill: var(--accent-hover) !important;
}

.viz-world-country-has-data:focus {
  outline: none;
  fill: var(--accent-hover) !important;
}

.viz-world-outline-fallback {
  pointer-events: none;
}

.viz-world-stage {
  position: relative;
  touch-action: none;
  cursor: grab;
  /* No background/border/overflow-clip of its own — the SVG's own ocean
     rect (rx=18, themed fill) already provides the map's sea backdrop and
     rounded edge; a second box wrapping it here would just be a redundant
     frame around the map for no visual gain (in light theme --surface-2
     equals --bg exactly, so it was invisible there anyway). */
}

.viz-world-stage.is-panning {
  cursor: grabbing;
}

.viz-world-zoom-controls {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  z-index: 2;
}

.viz-world-zoom-btn {
  width: 26px;
  height: 26px;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) 85%, transparent);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 15px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(2px);
}

.viz-world-zoom-btn:hover {
  background: var(--surface-3);
  border-color: var(--accent);
}

.viz-world-zoom-reset {
  font-size: 13px;
}

.viz-world-credit {
  font-size: 10px;
  color: var(--text-faint);
  margin-left: auto;
}

.viz-world-credit a {
  color: inherit;
  text-decoration: underline;
}

.viz-world-scale {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-muted);
}

.viz-world-scale-bar {
  display: flex;
  border-radius: 3px;
  overflow: hidden;
  border: 1px solid var(--border);
}

.viz-world-scale-swatch {
  width: 22px;
  height: 10px;
}

.viz-world-legend-item {
  cursor: pointer;
  border-radius: 6px;
  padding: 2px 4px;
  transition: background .15s ease, opacity .15s ease;
}

.viz-world-legend-item:hover {
  background: var(--surface-2);
}

.viz-world-legend-item:focus {
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}

.viz-world-empty {
  padding: 18px 0 4px;
}

#viz-chart.viz-world {
  overflow: visible;
}

.viz-doughnut-num {
  font-size: 28px;
  font-weight: 700;
  fill: var(--text);
}

.viz-doughnut-sub {
  font-size: 11px;
  fill: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .05em;
}

.viz-legend {
  flex: 1;
  min-width: 200px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 4px 16px;
  align-content: start;
  padding-top: 4px;
}

.viz-legend-item {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
}

.viz-legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex-shrink: 0;
}

.viz-legend-label {
  flex: 1;
  color: var(--text);
  font-size: 12px;
  line-height: 1.3;
  word-break: break-word;
}

.viz-legend-count {
  font-weight: 600;
  color: var(--text);
  min-width: 24px;
  text-align: right;
  font-size: 12px;
}

/* Chart interaction states */
.viz-seg {
  transition: opacity .15s;
}

.viz-seg-dim {
  opacity: 0.15;
}

.viz-row-dim {
  opacity: 0.25;
}

.viz-legend-active {
  background: var(--accent-subtle);
  border-radius: 4px;
  outline: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  outline-offset: 2px;
}

.viz-legend-active .viz-legend-label {
  color: var(--accent);
  font-weight: 600;
}

/* Year chart tag legend */
.viz-year-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  padding: 12px 4px 4px;
}

.viz-year-legend-item {
  padding: 3px 8px;
  border-radius: 4px;
  cursor: pointer;
  transition: background .15s, opacity .15s;
}

.viz-year-legend-item:hover {
  background: var(--surface-2);
}

/* ── PRISMA 2020 Screening Flow ──────────────────── */
.prisma-wrap {
  max-width: 860px;
  margin: 0 auto;
  padding: 4px 0 24px;
}

.prisma-flow-header {
  text-align: center;
  padding: 0 0 20px;
}

.prisma-flow-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
}

.prisma-flow-sub {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 4px;
}

.prisma-steps {
  display: flex;
  flex-direction: column;
}

.prisma-box {
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 20px 12px;
  border-left-width: 4px;
}

.prisma-box-stage {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.prisma-box-body {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 12px;
}

.prisma-box-n {
  font-size: 40px;
  font-weight: 700;
  color: var(--text);
  line-height: 1;
  flex-shrink: 0;
  min-width: 64px;
  text-align: right;
}

.prisma-box-text {
  flex: 1;
}

.prisma-box-desc {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.3;
}

.prisma-box-meta {
  display: block;
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 3px;
}

.prisma-pbar {
  height: 5px;
  background: var(--surface-2);
  border-radius: 3px;
  overflow: hidden;
}

.prisma-pbar-fill {
  height: 100%;
  border-radius: 3px;
  transition: width .5s ease;
}

/* Connector rows */
.prisma-step-connector {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 60px;
}

.prisma-sc-vline {
  border-right: 2px solid var(--border);
  position: relative;
}

.prisma-sc-vline::after {
  content: '';
  position: absolute;
  right: -6px;
  bottom: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 7px solid var(--border);
}

.prisma-sc-right {
  display: flex;
  align-items: center;
  padding: 8px 4px 8px 16px;
}

.prisma-sc-excl {
  position: relative;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 8px 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
}

.prisma-sc-excl::before {
  content: '';
  position: absolute;
  left: -16px;
  top: 50%;
  width: 16px;
  height: 2px;
  background: var(--border);
  transform: translateY(-50%);
}

.prisma-excl-n {
  font-size: 22px;
  font-weight: 700;
  color: var(--danger);
  flex-shrink: 0;
  min-width: 36px;
  text-align: right;
}

.prisma-excl-text {
  flex: 1;
}

.prisma-excl-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}

.prisma-excl-reason {
  display: block;
  font-size: 10.5px;
  color: var(--text-muted);
  margin-top: 2px;
}

/* Interactive PRISMA stages/connectors — click through to the matching
   article list (or a modal, for steps with no dedicated view). */
.prisma-box-clickable,
.prisma-step-connector-clickable {
  cursor: pointer;
  transition: background .15s, border-color .15s;
}

.prisma-box-clickable:hover,
.prisma-box-clickable:focus-visible {
  background: var(--surface-2);
  outline: none;
}

.prisma-step-connector-clickable:hover .prisma-sc-excl,
.prisma-step-connector-clickable:focus-visible .prisma-sc-excl {
  border-color: var(--accent);
  outline: none;
}

.prisma-hint {
  text-align: center;
  font-size: 11px;
  color: var(--text-faint);
  margin-top: 14px;
}

/* Dragging the PRISMA resize handle smaller hides secondary detail instead
   of clipping/scrolling a fixed-height box — first the exclusion/removed
   connectors, then (dragged smaller still) the per-source boxes row too.
   Dragging back out past each threshold brings it back. See
   wirePrismaResize/applyPrismaDetailLevel in views.js. */
.prisma-hide-connectors .prisma-step-connector {
  display: none;
}

.prisma-hide-sources .prisma-sources-row,
.prisma-hide-sources .prisma-sources-connector {
  display: none;
}

/* Per-source query breakdown — a row of small cards (Scopus / OpenAlex / …)
   feeding into the Identification box right below them. */
.prisma-sources-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  padding-bottom: 14px;
}

/* flex-basis:0 (not a px value) so however many source boxes there are
   split the row evenly — 50/50 for two, 33/33/33 for three, etc. — instead
   of each sizing to its own content/a fixed cap and leaving dead space.
   Only wraps onto a second line once min-width can no longer be honored
   (e.g. many sources on a narrow screen). */
.prisma-box-source {
  flex: 1 1 0;
  min-width: 150px;
  padding: 10px 14px 8px;
}

.prisma-box-source .prisma-box-n {
  font-size: 32px;
  min-width: 48px;
}

.prisma-box-source .prisma-box-body {
  margin-bottom: 8px;
}

.prisma-box-source .prisma-box-desc {
  font-size: 12px;
}

.prisma-box-source .prisma-box-meta {
  font-size: 10px;
}

/* Merges the source-boxes row down into the single Identification box. */
.prisma-sources-connector {
  width: 2px;
  height: 18px;
  background: var(--border);
  margin: 0 auto 14px;
  position: relative;
}

.prisma-sources-connector::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -1px;
  transform: translateX(-50%);
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 7px solid var(--border);
}

.prisma-source-chip {
  cursor: pointer;
  transition: opacity .15s, background .15s, border-color .15s;
}

.prisma-source-chip:hover,
.prisma-source-chip:focus-visible {
  opacity: 0.85;
  background: var(--surface-2);
  outline: none;
}

/* Year / stacked vertical column chart.
   Three parallel rows (counts / bars-area / year labels) instead of one
   self-contained column per year — this is what lets a single grid overlay
   sit behind every bar at once instead of needing per-column math. */
.viz-col-chart-wrap {
  overflow-x: auto;
  padding-bottom: 4px;
}

.viz-col-chart {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0 4px;
}

.viz-col-counts,
.viz-col-bars-area,
.viz-col-years {
  display: flex;
  gap: 6px;
  padding-left: 30px;
}

.viz-col-counts > *,
.viz-col-years > * {
  flex: 1;
  min-width: 28px;
  text-align: center;
}

/* Height is user-resizable via .viz-col-resize-handle below (inline style
   sets the actual px value) — this is just a sane first-paint fallback. */
.viz-col-bars-area {
  position: relative;
  align-items: flex-end;
  height: 320px;
}

.viz-col-bar-wrap {
  flex: 1;
  min-width: 28px;
  height: 100%;
  display: flex;
  align-items: flex-end;
}

.viz-col-bar {
  width: 100%;
  display: flex;
  flex-direction: column-reverse;
  border-radius: 4px 4px 0 0;
  overflow: hidden;
  min-height: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .viz-col-bar {
    transition: height .35s ease;
  }
}

.viz-col-seg {
  width: 100%;
  min-height: 1px;
  transition: opacity .15s;
}

.viz-col-count {
  font-size: 10px;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}

.viz-col-label {
  font-size: 10px;
  color: var(--text-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Count-axis grid — sits behind the bars, spanning the same 30px-left-gutter
   box as .viz-col-bars-area so its 0%/100% match the bars' own scale
   exactly (both are computed against the same "nice tick" top value). */
.viz-year-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.viz-year-grid-line {
  position: absolute;
  left: 30px;
  right: 4px;
  border-top: 1px dashed var(--border-subtle);
}

.viz-year-grid-label {
  position: absolute;
  left: -30px;
  top: 0;
  width: 26px;
  transform: translateY(-50%);
  text-align: right;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}

/* Drag handle: resizes .viz-col-bars-area's height (see wireYearChartResize
   in views.js) — kept outside .viz-col-chart-wrap so it never scrolls away
   horizontally with the bars. */
.viz-col-resize-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 16px;
  margin-top: 2px;
  cursor: ns-resize;
  touch-action: none;
}

.viz-col-resize-grip {
  width: 44px;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  transition: background var(--transition);
}

.viz-col-resize-handle:hover .viz-col-resize-grip,
.viz-col-resize-handle.is-dragging .viz-col-resize-grip {
  background: var(--accent);
}

/* ----------------------------------------------------------
   About view
   ---------------------------------------------------------- */
.about-view {
  max-width: none;
}

.about-hero {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  padding: 24px 28px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 20px;
}

.about-hero-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  min-height: 88px;
  border-radius: var(--radius-md);
  background: var(--accent-subtle);
  color: var(--accent);
  flex-shrink: 0;
  align-self: stretch;
}

.about-hero-icon svg {
  width: 42px;
  height: 42px;
}

.about-hero-text h2 {
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 4px;
}

.about-version-badge {
  display: inline-flex;
  font-size: 11px;
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 20px;
  padding: 2px 10px;
  margin-bottom: 10px;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.about-hero-text p {
  font-size: 13.5px;
  color: var(--text-muted);
  line-height: 1.6;
  margin: 0;
}

.about-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.about-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 13.5px;
  color: var(--text-muted);
  line-height: 1.5;
}

.about-list li svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--accent);
}

.about-list li strong {
  color: var(--text);
}

/* Muted variant for "Core Features" list */
.about-list.about-list-muted li {
  color: var(--text-faint, var(--text-muted));
  opacity: 0.75;
}

.about-list.about-list-muted li strong {
  color: var(--text-muted);
  opacity: 1;
}

.about-list.about-list-muted li svg {
  color: var(--text-faint, var(--border));
}

.about-dev-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.about-dev-info {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.about-dev-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.about-dev-sub {
  font-size: 12px;
  color: var(--text-muted);
}

.about-dev-links {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.about-github-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  text-decoration: none;
}

.about-github-btn svg {
  width: 14px;
  height: 14px;
}

/* Final spacing override for consistent header-to-content gap across views */
#view-container > .projects-view,
#view-container > .history-view,
#view-container > .settings-view,
#view-container > .about-view,
#view-container > .tags-view,
#view-container > .databases-view,
#view-container > .viz-view {
  padding-top: 20px;
}

/* Keep these views flush under topbar (no spacer bar above first row). */
#view-container > .articles-view,
#view-container > .search-view {
  padding-top: 0;
}

/* ----------------------------------------------------------
   Side-padding standardization — kept together at the very end
   of the file so it reliably wins the cascade over every view's
   own base rule, whichever section of this file that rule is
   in (equal specificity + a matching @media condition still
   loses to a later unconditional rule earlier in the file).
   Desktop: 24px everywhere. Mobile (<=900px): 12px everywhere.
   The .article-item column additionally has a 3px colored
   border, so its own left padding is 3px less than the others
   at both sizes to keep the visual inset identical all round.
   ---------------------------------------------------------- */
@media (max-width: 900px) {
  .projects-view,
  .history-view,
  .settings-view,
  .about-view,
  .tags-view,
  .databases-view,
  .viz-view,
  .list-toolbar,
  .corpus-banner,
  .corpus-tag-breakdown,
  .articles-stats,
  .search-editor-panel {
    padding-left: 12px;
    padding-right: 12px;
  }

  .article-item {
    padding-left: 9px;
    padding-right: 12px;
  }

  /* Extra headroom for "SLR Harvester Web" in the 200px tablet sidebar —
     verified comfortable in testing, but real-device font metrics can run
     wider than simulated ones, and "Web" must never be the part that gets
     silently clipped by .app-logo's overflow:hidden. */
  .app-name {
    font-size: 14px;
  }

  /* Scroll-to-collapse list header (Articles / Selected / Corpus): a phone
     or tablet screen doesn't have room for the stats banner + toolbar +
     search row AND a useful number of article cards at once, so
     wireListHeaderCollapse (views.js) toggles .is-collapsed on scroll
     direction and this grid-row trick animates the whole stack to zero
     height without needing to know its actual (variable — the filter panel
     can add rows) content height, the way a max-height transition would.
     Desktop never gets this rule at all, so the header there is always
     static/visible regardless of the class. */
  .list-header-collapsible {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows .25s ease;
  }

  .list-header-collapsible-inner {
    overflow: hidden;
    min-height: 0;
  }

  .list-header-collapsible.is-collapsed {
    grid-template-rows: 0fr;
  }
}

