:root {
  --bg: #F3F4F6;
  --card: rgba(255,255,255,0.55);
  --card-border: rgba(255,255,255,0.6);
  --text: #12151A;
  --muted: #767C87;
  --amber: #E08A1E;
  --teal: #0F8F76;
  --danger: #D64545;
  --gold: #E0A21E;
  --silver: #9AA3AF;
  --bronze: #B87139;
  --display: 'Space Grotesk', sans-serif;
  --body: 'Inter', sans-serif;
  --mono: 'JetBrains Mono', monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  color: var(--text);
  font-family: var(--body);
  min-height: 100vh;
  background:
    radial-gradient(circle at 8% 12%, rgba(47,111,237,0.16), transparent 42%),
    radial-gradient(circle at 92% 8%, rgba(15,143,118,0.16), transparent 40%),
    radial-gradient(circle at 25% 90%, rgba(224,138,30,0.14), transparent 45%),
    radial-gradient(circle at 85% 85%, rgba(120,90,230,0.12), transparent 42%),
    var(--bg);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
}

.hidden { display: none !important; }
.frame { max-width: 1180px; margin: 0 auto; padding: 1.75rem 2rem 4rem; }

.wordmark { font-family: var(--display); font-weight: 700; font-size: 2rem; }

/* ---------- Sign-in ---------- */
#signin-screen { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
.signin-card {
  width: min(380px, 90vw);
  padding: 3rem 2.25rem;
  border-radius: 20px;
  text-align: center;
  background: rgba(255,255,255,0.55);
  border: 1px solid rgba(255,255,255,0.65);
  backdrop-filter: blur(22px) saturate(1.4);
  -webkit-backdrop-filter: blur(22px) saturate(1.4);
  box-shadow: 0 30px 60px -30px rgba(20,25,35,0.35), inset 0 1px 0 rgba(255,255,255,0.6);
}
.tagline { color: var(--muted); margin: 0.35rem 0 2rem; font-size: 0.92rem; min-height: 1.3em; transition: opacity 0.3s ease; }
.tagline.fading { opacity: 0; }
.signin-status { color: var(--muted); font-family: var(--mono); font-size: 0.78rem; min-height: 1.2em; margin-top: 1.1rem; }

.btn-primary {
  width: 100%; padding: 0.85rem 1rem; border: none; border-radius: 10px;
  background: linear-gradient(135deg, #12151A, #2b3140);
  color: #fff; font-weight: 600; font-size: 0.95rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
}
.signal-dot { width: 8px; height: 8px; border-radius: 50%; background: #FFC658; }

/* Shared pill style for utility/action buttons (close, back, resolve,
   decline, search, grab, report-type picker, modal tabs) — the same look as
   .monitor-pill, just clickable. Header icon buttons (.icon-btn) and the
   mobile FABs (.fab) keep their own distinct treatment, deliberately not
   converted to this. */
.pill-btn {
  display: inline-flex; align-items: center; gap: 0.4rem; font-family: var(--mono); font-size: 0.72rem;
  padding: 0.3rem 0.6rem; border-radius: 20px; background: rgba(20,25,35,0.04); border: 1px solid rgba(20,25,35,0.08);
  color: var(--text); cursor: pointer; transition: background 0.15s ease, border-color 0.15s ease;
}
.pill-btn:hover:not(:disabled) { background: rgba(20,25,35,0.08); border-color: rgba(20,25,35,0.15); }
.pill-btn:disabled { opacity: 0.55; cursor: default; }
/* Icon-only close buttons (✕) — same soft pill background/border, but round
   rather than an elongated dot+label pill, since there's no label to pair a
   status dot with. */
.pill-btn-icon { width: 30px; height: 30px; padding: 0; justify-content: center; border-radius: 50%; }

/* ---------- Hero ---------- */
.hero {
  position: relative; border-radius: 22px; overflow: hidden; height: 220px; margin-bottom: 1.25rem;
  background: linear-gradient(120deg, #3d4c63, #1c2531 65%);
  box-shadow: 0 24px 48px -28px rgba(20,25,35,0.4);
}
.hero::before { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at 78% 25%, rgba(255,255,255,0.15), transparent 55%); }
/* Cycling backdrop banner — sits behind .hero-top/.hero-mid (those have
   z-index:1, this doesn't, so it always stays underneath regardless of DOM
   order) using .hero's own dark gradient as the base/fallback until the
   first image loads. The ::after scrim keeps header text readable over
   whatever photo is showing, same idea as .hero's own gradient. */
.hero-bg { position: absolute; inset: 0; overflow: hidden; }
.hero-bg-slide {
  position: absolute; inset: 0; background-size: cover; background-position: center;
  opacity: 0; transition: opacity 1.4s ease;
}
.hero-bg-slide.active { opacity: 1; }
.hero-bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(120deg, rgba(30,37,49,0.9), rgba(20,25,35,0.78) 65%);
}
.hero-featured-tag {
  font-family: var(--mono); font-size: 0.68rem; color: rgba(255,255,255,0.75);
  margin-top: 0.3rem; cursor: pointer; display: flex; align-items: center; gap: 0.3rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.hero-featured-tag:hover { color: #fff; }
.hero-top { display: flex; justify-content: space-between; align-items: center; padding: 1.25rem 1.5rem; position: relative; z-index: 1; color: #fff; }
.logo { font-family: var(--display); font-weight: 700; font-size: 1.4rem; color: #fff; }
.logo-tag { font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.04em; color: rgba(255,255,255,0.55); text-transform: uppercase; margin-top: 1px; }
.hero-icons { display: flex; gap: 0.6rem; align-items: center; }
/* Admin hero masthead's Disk Space section — dark background, so the shared
   .now-title/.now-meta/.disk-donut-track tokens (tuned for light cards)
   need overriding here; .disk-donut-fill's amber/danger stay as-is, already
   visible on dark. */
.hero-diskspace { position: relative; z-index: 1; padding: 0 1.5rem 1.25rem; }
.hero-diskspace-label { font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.04em; color: rgba(255,255,255,0.55); text-transform: uppercase; margin: 0 0 0.6rem; font-weight: 500; }
.hero-diskspace .now-title { color: #fff; }
.hero-diskspace .now-meta { color: rgba(255,255,255,0.7); }
.hero-diskspace .disk-donut-track { stroke: rgba(255,255,255,0.18); }
.hero-diskspace .empty-state { color: rgba(255,255,255,0.7); }
/* Unlike the family dashboard's hero, admin's has no cycling banner image
   needing a fixed box — height:220px + overflow:hidden was clipping the
   Disk Space section whenever volumes wrapped to more rows than fit
   (confirmed live on mobile: 3 volumes stacked single-column, 3rd cut off). */
.hero-admin { height: auto; }
.icon-btn {
  width: 36px; height: 36px; border-radius: 50%; background: rgba(255,255,255,0.18);
  border: 1px solid rgba(255,255,255,0.3); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  display: flex; align-items: center; justify-content: center; color: #fff; font-size: 1rem;
  cursor: pointer; line-height: 1; text-decoration: none;
}
.icon-btn:hover { background: rgba(255,255,255,0.28); }
/* An SVG child (admin-link-btn) defaults to inline, which leaves a few px of
   baseline whitespace under it even inside a flex-centered parent — block
   avoids that so it centers exactly like the text-glyph icon buttons do. */
.icon-btn svg { display: block; }

.app-footer { text-align: center; font-family: var(--mono); font-size: 0.7rem; color: var(--muted); padding: 1.5rem 0 5rem; }

/* Floating request button — mobile only (see the media query below); desktop
   keeps the plain header icon button (.icon-btn above) instead. Styled to match
   the header's avatar-chip (dark glass pill + gradient circle badge). */
.fab {
  display: none;
  position: fixed; bottom: 1.75rem; right: 1.75rem; z-index: 2147483647;
  align-items: center; gap: 0.6rem; text-align: left;
  padding: 0.4rem 1rem 0.4rem 0.4rem; border-radius: 999px; border: 1px solid rgba(255,255,255,0.16);
  background: rgba(20,21,26,0.6); backdrop-filter: blur(16px) saturate(1.4); -webkit-backdrop-filter: blur(16px) saturate(1.4);
  color: #fff; cursor: pointer;
  box-shadow: 0 14px 30px -12px rgba(20,25,35,0.55);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.fab:hover { transform: translateY(-2px); box-shadow: 0 18px 36px -12px rgba(20,25,35,0.65); }
.fab:active { transform: translateY(0); }
.fab-icon {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  background: linear-gradient(135deg,#FFD98A,var(--amber));
  display: flex; align-items: center; justify-content: center;
  font-size: 1.15rem; font-weight: 600; color: #12151A; line-height: 1;
}
/* Report FAB reuses .fab's shape/glass styling but on the opposite side, with a
   teal icon badge instead of amber to read as the secondary action next to the
   amber request FAB. */
.fab-left { right: auto; left: 1.75rem; }
.fab-icon-teal { background: linear-gradient(135deg,#7EE0C9,var(--teal)); color: #0b1f1a; }
.fab-text { display: flex; flex-direction: column; }
.fab-title { font-family: var(--display); font-size: 0.82rem; font-weight: 600; line-height: 1.2; }
.fab-subtitle { font-size: 0.66rem; opacity: 0.75; line-height: 1.2; }
@media (max-width: 900px) {
  .fab { display: flex; bottom: 1.25rem; right: 1.25rem; }
  .fab-left { right: auto; left: 1.25rem; }
  .hero-icons #search-btn { display: none; }
  /* The report icon button in the header is desktop-only — .fab-left below is
     its mobile equivalent, mirroring the request FAB on the opposite side. */
  .hero-icons #report-search-btn { display: none; }
  /* Admin link has no FAB equivalent (it's low-frequency, doesn't warrant a
     third floating button) — stays in the header icon row on mobile too,
     alongside the avatar chip and logout button which are already there. */
}
.avatar-chip {
  display: flex; align-items: center; gap: 0.55rem; padding: 0.35rem 0.7rem 0.35rem 0.35rem;
  border-radius: 999px; background: rgba(255,255,255,0.18); border: 1px solid rgba(255,255,255,0.3);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); color: #fff;
  cursor: pointer; transition: background 0.15s ease;
}
.avatar-chip:hover { background: rgba(255,255,255,0.28); }
.avatar { width: 26px; height: 26px; border-radius: 50%; background: linear-gradient(135deg,#FFD98A,var(--amber)); flex-shrink: 0; }
.avatar-name { font-size: 0.78rem; font-weight: 600; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.avatar-role { font-size: 0.66rem; opacity: 0.8; }
.hero-mid { position: relative; z-index: 1; padding: 0.5rem 1.75rem; display: flex; justify-content: space-between; align-items: flex-end; height: 112px; color: #fff; }
.hero-headline { font-family: var(--display); font-size: 1.55rem; font-weight: 600; line-height: 1.2; }
.hero-sub { opacity: 0.8; font-size: 0.8rem; margin-top: 0.35rem; font-family: var(--mono); }
.date-pill { text-align: right; }
.date-num { font-family: var(--display); font-size: 1.5rem; font-weight: 700; }
.date-txt { font-size: 0.72rem; opacity: 0.75; font-family: var(--mono); }

/* Owner-scheduled announcement — a persistent broadcast-style alert, not a
   toast, since a maintenance notice should stay visible the whole scheduled
   window rather than auto-dismiss after a few seconds. */
.notice-banner {
  display: flex; align-items: center; gap: 0.65rem; padding: 0.8rem 1.1rem;
  background: rgba(224,138,30,0.12); border: 1px solid rgba(224,138,30,0.35);
  border-radius: 12px; color: var(--text); font-size: 0.85rem; margin-bottom: 1.1rem;
}
.notice-banner-icon { flex-shrink: 0; color: var(--amber); }

/* ---------- Bento grid ---------- */
.bento { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.1rem; }
.span2 { grid-column: span 2; } .span4 { grid-column: span 4; }

.card {
  background: var(--card); border: 1px solid var(--card-border); border-radius: 18px; padding: 1.15rem;
  backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4);
  box-shadow: 0 16px 36px -22px rgba(20,25,35,0.28), inset 0 1px 0 rgba(255,255,255,0.5);
  display: flex; flex-direction: column;
}
.card-label {
  font-family: var(--mono); font-size: 0.72rem; color: var(--muted); letter-spacing: 0.02em;
  margin-bottom: 0.75rem; display: flex; align-items: center; gap: 0.5rem;
}
.chan {
  font-family: var(--mono); font-size: 0.68rem; color: var(--amber);
  border: 1px solid rgba(224,138,30,0.35); background: rgba(224,138,30,0.08);
  padding: 0.15rem 0.45rem; border-radius: 5px;
}
.chan-teal { color: var(--teal); border-color: rgba(15,143,118,0.35); background: rgba(15,143,118,0.08); }
.empty-state { color: var(--muted); font-size: 0.85rem; margin: 0.5rem 0; }

/* Now Playing rows */
.now-row { display: flex; align-items: center; gap: 0.9rem; padding: 0.65rem 0; border-bottom: 1px solid rgba(20,25,35,0.06); cursor: pointer; }
.now-row:last-child { border-bottom: none; }
.now-row:hover { opacity: 0.85; }
.thumb { width: 46px; height: 46px; border-radius: 10px; object-fit: cover; background: #E2E6EC; flex-shrink: 0; }
.now-title { font-size: 0.88rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.now-meta { font-size: 0.74rem; color: var(--muted); font-family: var(--mono); margin-top: 2px; display: flex; align-items: center; gap: 0.4rem; }
.state-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--teal); display: inline-block; flex-shrink: 0; }
.state-dot.paused { background: var(--muted); }
.state-dot.danger { background: var(--danger); }
.bar { height: 5px; background: rgba(20,25,35,0.08); border-radius: 3px; margin-top: 7px; overflow: hidden; }
.bar-fill { height: 100%; background: var(--amber); }

/* Now Playing only — elapsed/total + ETA above the bar. Scoped to .now-row
   (not a global .bar override) since Download Queue/torrent file rows reuse
   the same .bar/.bar-fill classes and shouldn't be affected. */
.now-eta { font-family: var(--mono); font-size: 0.64rem; color: var(--muted); text-align: right; line-height: 1.15; white-space: nowrap; }
.now-eta .sep { opacity: 0.5; margin: 0 0.3em; }
.now-eta .eta-val { color: var(--text); font-weight: 500; }
.now-row .bar { margin-top: 2px; }
/* Smooths the once-a-second interpolated width updates into a continuously
   advancing bar instead of a visible 1%-per-tick stutter. Scoped to Now
   Playing only — Download Queue's bar-fill isn't driven by that ticker. */
.now-row .bar-fill { transition: width 1s linear; }

/* Disk Space panel's per-volume gauge — same amber/danger semantics as
   .bar-fill/.state-dot.danger, just circular instead of linear. pathLength="100"
   on both circles lets stroke-dasharray take a plain percentage directly,
   no circumference math needed. */
.disk-donut { width: 40px; height: 40px; flex-shrink: 0; transform: rotate(-90deg); }
.disk-donut-track { fill: none; stroke: rgba(20,25,35,0.08); stroke-width: 3.5; }
.disk-donut-fill { fill: none; stroke: var(--amber); stroke-width: 3.5; stroke-linecap: round; transition: stroke-dasharray 0.3s ease; }
.disk-donut-fill.danger { stroke: var(--danger); }

/* Volumes side by side instead of stacked — a plain vertical list reads odd
   for just 2-3 items that aren't really a sequence. Wraps on narrow widths
   instead of the list-divider look .dl-row uses elsewhere (Download Queue/
   Issues), which stays untouched since those genuinely are lists. */
#diskspace-body { display: flex; flex-wrap: wrap; gap: 1.25rem; }
#diskspace-body .dl-row { flex: 1 1 200px; padding: 0; border-bottom: none; }

/* Recently Watched reuses the Now Playing row markup (.now-row/.thumb/.now-meta),
   minus the progress bar. flex:1 on a flex-column .card fills whatever height the row's tallest
   sibling (Now Playing) naturally wants — grid stretch then matches Now Playing's
   card to it — so the two stay the same height dynamically either direction,
   with overflow-y:auto for whichever has more content than fits. min-height is
   just a floor so it doesn't collapse to almost nothing if Now Playing is empty. */
#recently-watched-body { flex: 1; min-height: 9rem; overflow-y: auto; }

/* Download queue rows — same shape as Now Playing rows but not clickable */
.dl-row { display: flex; align-items: center; gap: 0.9rem; padding: 0.65rem 0; border-bottom: 1px solid rgba(20,25,35,0.06); }
.dl-row:last-child { border-bottom: none; }
.dl-row-clickable { cursor: pointer; }
.dl-row-body { flex: 1; min-width: 0; }

/* Top of the Month leaderboard — #1 gets the big medal frame, #2/#3 are compact
   ranked rows underneath, each color-coded gold/silver/bronze. */
.top-month-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2rem; align-items: stretch; }
/* Flex column so .medal-rows (mt-auto below) can be pinned to the bottom of the
   card — grid's align-items:stretch already equalizes card height per row, this
   makes the runner-up list actually sit at that shared bottom edge instead of
   trailing the artwork, which is what let a short avatar's rows sit higher than
   a taller poster's rows in the same row of cards. */
.top-month-tile { display: flex; flex-direction: column; text-align: center; }
.top-month-frame {
  position: relative; width: 100%; max-width: 92px; aspect-ratio: 2/3; border-radius: 12px; overflow: hidden; margin: 0 auto;
  background: linear-gradient(160deg,#E4E9F2,#CBD6E8);
  box-shadow: 0 0 0 2px rgba(224,162,30,0.6), 0 10px 20px -12px rgba(224,162,30,0.6);
}
/* Same reserved box height as the poster frames (aspect-ratio: 2/3, unchanged)
   so the label/title/plays rows below start at the same vertical offset in
   every column — only the avatar itself becomes circular and centered within
   that box, rather than the box itself getting shorter. A shorter box here is
   exactly what threw off "347 plays"'s alignment against the other three. */
.top-month-tile.user .top-month-frame {
  background: none; box-shadow: none;
  display: flex; align-items: center; justify-content: center;
}
.top-month-tile.user .top-month-img {
  width: 92px; height: 92px; border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(224,162,30,0.6), 0 10px 20px -12px rgba(224,162,30,0.6);
}
.top-month-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.top-month-medal {
  display: block; font-size: 1.3rem; margin-bottom: 0.3rem;
  filter: drop-shadow(0 2px 3px rgba(224,162,30,0.45));
}
.top-month-label {
  font-family: var(--mono); font-size: 0.64rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em;
  margin-top: 0.65rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Fixed to one line so a long title doesn't push that tile's medal rows down
   relative to the other three — otherwise the silver/bronze rows drift out of
   alignment across the row of tiles. */
.top-month-title {
  font-size: 0.85rem; font-weight: 600; margin-top: 0.2rem; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.top-month-plays { font-family: var(--mono); font-size: 0.7rem; color: var(--gold); margin-top: 0.2rem; }

/* Silver and bronze share a single grid (not two independent flex rows), so the
   badge/name/plays columns are sized once across both rows and land at
   identical widths — two separate rows each auto-sizing off their own content
   is what made them look out of place relative to each other. */
.medal-rows { display: grid; grid-template-columns: 1.1em 1fr auto; align-items: center; column-gap: 0.4rem; row-gap: 0.4rem; font-size: 0.72rem; margin-top: auto; padding-top: 0.5rem; text-align: left; }
.medal-badge { font-size: 0.85rem; text-align: center; }
.medal-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.medal-plays { font-family: var(--mono); color: var(--muted); }
.medal-name.silver { color: var(--silver); }
.medal-name.bronze { color: var(--bronze); }

@media (max-width: 900px) {
  .top-month-grid { grid-template-columns: repeat(2, 1fr); row-gap: 1.5rem; }
}

/* ---------- My Stats tab ---------- */
.stat-hero { text-align: center; padding: 0.4rem 0.5rem 1.1rem; border-bottom: 1px solid rgba(20,25,35,0.08); margin-bottom: 1rem; }
.stat-hero-num {
  font-family: var(--display); font-weight: 700; font-size: 2.5rem; line-height: 1; letter-spacing: -0.01em;
}
.stat-hero-unit { font-size: 1.05rem; font-weight: 500; color: var(--muted); margin-left: 0.15rem; }
.stat-hero-cap { font-family: var(--mono); font-size: 0.68rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 0.4rem; }
.stat-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.6rem; margin-bottom: 1.2rem; }
.mystats-tile {
  background: rgba(20,25,35,0.03); border: 1px solid rgba(20,25,35,0.06); border-radius: 12px;
  padding: 0.65rem 0.55rem; text-align: center;
}
.stat-tile-val { font-family: var(--display); font-weight: 600; font-size: 1.15rem; color: var(--teal); }
.stat-tile-val.amber { color: var(--amber); }
.stat-tile-lbl { font-family: var(--mono); font-size: 0.6rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 0.25rem; }

/* Most Watched — text-only, all three ranks in one .medal-rows grid (reusing
   Top of the Month's own gold/silver/bronze styling above) instead of a big
   poster tile for #1. medal-rows-full just gives the standalone list a touch
   more room than the compact runner-up-only version Top of the Month uses. */
.medal-rows-full { row-gap: 0.65rem; font-size: 0.85rem; padding: 0.2rem 0 0.3rem; }
.medal-rows-full .medal-badge { font-size: 1.05rem; }
.medal-name.gold { color: var(--gold); font-weight: 600; }

/* Watch Activity — day-of-week and hour-of-day breakdowns adapted from
   Tautulli's own Graphs page, scoped to this user, Movies/TV only (this
   deployment has no Live TV sessions, so that series is dropped rather than
   shipped as a permanently-empty legend entry). Bar heights are set inline
   per row (see renderActivityBars in app.js), scaled to that chart's own
   tallest bucket rather than a fixed max. */
.chart-sub { font-size: 0.74rem; color: var(--muted); margin: 0 0 0.7rem; }
.chart-legend {
  display: flex; gap: 1.1rem; align-items: center; margin-bottom: 1rem;
  font-family: var(--mono); font-size: 0.68rem; color: var(--muted);
}
.chart-legend .dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; margin-right: 0.4rem; vertical-align: -1px; }
.chart-legend .dot.movies { background: var(--teal); }
.chart-legend .dot.tv { background: var(--amber); }
.chart-title { font-family: var(--display); font-weight: 600; font-size: 0.86rem; margin-bottom: 0.6rem; }

.dow-chart {
  display: flex; align-items: flex-end; gap: 0.55rem; height: 108px;
  border-bottom: 1px solid rgba(20,25,35,0.12);
}
.dow-col {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  height: 100%; cursor: default; transition: filter 0.15s ease;
}
.dow-col:hover { filter: brightness(1.08); }
.dow-seg { width: 100%; max-width: 24px; border-radius: 3px 3px 0 0; }
.dow-seg.tv { background: var(--amber); }
.dow-seg.movies { background: var(--teal); border-radius: 0; }
.dow-labels { display: flex; gap: 0.55rem; margin-top: 0.4rem; }
.dow-labels span { flex: 1; text-align: center; font-family: var(--mono); font-size: 0.62rem; color: var(--muted); }

.hod-chart {
  display: flex; align-items: flex-end; gap: 2px; height: 84px;
  border-bottom: 1px solid rgba(20,25,35,0.12);
}
.hod-col {
  flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%;
  border-radius: 2px 2px 0 0; overflow: hidden; cursor: default; transition: filter 0.15s ease;
}
.hod-col:hover { filter: brightness(1.15); }
.hod-seg { width: 100%; }
.hod-seg.tv { background: var(--amber); }
.hod-seg.movies { background: var(--teal); }
.hod-labels { display: flex; gap: 2px; margin-top: 0.35rem; }
.hod-labels span { flex: 1; text-align: center; font-family: var(--mono); font-size: 0.52rem; color: var(--muted); }

/* Admin: Seeding — just a handful of numbers, no file list. Wraps to 2x2 on
   narrow cards instead of squeezing four values onto one line. */
.stat-pair { display: flex; flex-wrap: wrap; gap: 1rem 0.5rem; text-align: center; }
.stat-tile { flex: 1 1 35%; min-width: 90px; }
.stat-value { font-family: var(--display); font-size: 1.4rem; font-weight: 600; color: var(--teal); white-space: nowrap; }
.stat-label {
  font-family: var(--mono); font-size: 0.64rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em; margin-top: 0.2rem;
}

/* Owner-only status panel */
.ups-status { padding-bottom: 0.85rem; margin-bottom: 0.85rem; border-bottom: 1px solid rgba(20,25,35,0.06); }
.monitor-pills { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.monitor-pill {
  display: inline-flex; align-items: center; gap: 0.4rem; font-family: var(--mono); font-size: 0.72rem;
  padding: 0.3rem 0.6rem; border-radius: 20px; background: rgba(20,25,35,0.04); border: 1px solid rgba(20,25,35,0.08);
}

/* ---------- Poster grids ---------- */
.poster-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 0.85rem; }
.poster-grid-scroll { display: flex; overflow-x: auto; gap: 0.9rem; padding-bottom: 0.3rem; }
.poster-grid-scroll .poster-card { flex: 0 0 130px; }

.poster-card { cursor: pointer; }
.poster-frame {
  position: relative; width: 100%; aspect-ratio: 2/3; border-radius: 11px; overflow: hidden;
  background: linear-gradient(160deg,#E4E9F2,#CBD6E8);
  box-shadow: 0 10px 20px -14px rgba(20,25,35,0.4);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.poster-card:hover .poster-frame { transform: translateY(-3px); box-shadow: 0 16px 28px -14px rgba(20,25,35,0.5); }
.poster-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.poster-overlay { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,0.7) 100%); display: flex; align-items: flex-end; padding: 0.55rem; }
.poster-overlay-text { font-size: 0.74rem; font-weight: 600; line-height: 1.25; color: #fff; }
.poster-badge {
  position: absolute; top: 0.45rem; left: 0.45rem; font-family: var(--mono); font-size: 0.6rem;
  padding: 0.18rem 0.4rem; border-radius: 5px; background: rgba(255,255,255,0.85); color: #12151A;
}
.poster-meta { font-size: 0.72rem; color: var(--muted); font-family: var(--mono); margin-top: 0.4rem; }
.subsection-label {
  font-family: var(--display); font-size: 0.85rem; font-weight: 600; color: var(--text);
  margin: 0 0 0.6rem; padding-top: 0.2rem;
}
.subsection-label:not(:first-child) { margin-top: 0.5rem; padding-top: 1rem; border-top: 1px solid rgba(20,25,35,0.06); }

@media (max-width: 900px) {
  .span2, .span4 { grid-column: span 4; }
  .bento { grid-template-columns: 1fr; }
  .frame { padding: 1.25rem; }
  /* Cards stack into one column here, so Now Playing and Recently Watched are no
     longer row-siblings to stretch-match — flex:1 alone would collapse this to
     ~0 height with nothing to fill against, so cap it to roughly 8 rows instead. */
  #recently-watched-body { flex: none; min-height: 0; max-height: 34rem; overflow-y: auto; }
  /* Airing Today stays a wrapping grid on desktop (it's usually a handful of
     items) but scrolls horizontally on mobile like Recently Added/Releasing
     Soon, rather than wrapping into a tall multi-row block. */
  #airing-today-body { display: flex; overflow-x: auto; gap: 0.9rem; padding-bottom: 0.3rem; }
  #airing-today-body .poster-card { flex: 0 0 130px; }
}

/* ---------- Modals ---------- */
.modal {
  position: fixed; inset: 0; background: rgba(20,25,35,0.35); backdrop-filter: blur(4px);
  display: flex; align-items: flex-start; justify-content: center; padding-top: 8vh; z-index: 20;
}
.modal-card {
  width: min(540px, 90vw);
  /* .modal-card is a flex item of .modal — without this, a flex item's default
     min-width:auto lets wide/unstyled content inside (e.g. an unstyled input)
     force it past the width cap above instead of wrapping/shrinking, which is
     what pushed the report modal off the right edge on mobile. */
  min-width: 0;
  background: rgba(255,255,255,0.75); border: 1px solid rgba(255,255,255,0.7);
  border-radius: 16px; padding: 1.4rem; max-height: 78vh; display: flex; flex-direction: column;
  backdrop-filter: blur(24px) saturate(1.5); -webkit-backdrop-filter: blur(24px) saturate(1.5);
  box-shadow: 0 40px 80px -30px rgba(20,25,35,0.45);
}
.modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.85rem; }
.modal-head h3 { font-family: var(--display); margin: 0; font-weight: 600; }

.modal-tabs { display: flex; gap: 0.5rem; margin-bottom: 0.85rem; }
.modal-tab .state-dot { background: var(--muted); }
.modal-tab.active .state-dot { background: var(--teal); }
.modal-tab.active { color: var(--text); border-color: rgba(15,143,118,0.35); background: rgba(15,143,118,0.08); }

/* All tab panes (plus the season picker, shown in place of whichever tab
   triggered it) sit in the same flex-column .modal-card, so each needs to be
   a flex column itself (min-height:0 so overflow-y:auto below actually kicks
   in instead of the card just growing past its max-height). */
#search-tab, #myrequests-tab, #mystats-tab, #season-picker, #report-search-view, #report-browse-view, #report-form-view {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
}

/* Report modal's browse view (show -> seasons -> episodes) — plain clickable
   navigation rows, distinct from .season-row's checkbox-select rows in the
   request modal's season picker. */
.browse-row { display: flex; align-items: center; gap: 0.85rem; padding: 0.6rem 0; cursor: pointer; }
.browse-row + .browse-row { border-top: 1px solid rgba(20,25,35,0.08); }
.browse-row:hover { opacity: 0.85; }
.browse-row-thumb { width: 42px; height: 42px; border-radius: 8px; object-fit: cover; background: #E2E6EC; flex-shrink: 0; }
.browse-row-name { font-size: 0.85rem; font-weight: 600; }
.browse-row-index { font-size: 0.75rem; color: var(--muted); font-family: var(--mono); }

#search-input, #report-search-input, #library-search-input {
  width: 100%; padding: 0.75rem 0.95rem; background: rgba(255,255,255,0.7);
  border: 1px solid rgba(20,25,35,0.1); border-radius: 9px; color: var(--text);
  font-family: var(--body); font-size: 0.9rem; margin-bottom: 0.85rem;
}
#search-input:focus, #report-search-input:focus { outline: none; border-color: var(--teal); }
.discover-label {
  font-family: var(--mono); font-size: 0.68rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em; margin: -0.35rem 0 0.5rem;
}
.search-results { flex: 1; overflow-y: auto; }

.my-requests-list { flex: 1; overflow-y: auto; }
#mystats-body { flex: 1; overflow-y: auto; }
.my-request-row { display: flex; gap: 0.85rem; padding: 0.65rem 0; align-items: center; }
.my-request-row + .my-request-row { border-top: 1px solid rgba(20,25,35,0.08); }
.my-request-status { font-size: 0.74rem; font-family: var(--mono); color: var(--muted); margin-top: 2px; display: flex; align-items: center; gap: 0.4rem; }
.my-request-status.available { color: var(--teal); }
.my-request-status.downloading { color: var(--amber); }
.my-request-status.declined { color: var(--danger); }
.my-request-status .status-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; display: inline-block; }
.result-item { display: flex; gap: 0.85rem; padding: 0.65rem 0; align-items: center; cursor: pointer; }
.result-item + .result-item { border-top: 1px solid rgba(20,25,35,0.08); }
.result-poster { width: 42px; height: 60px; object-fit: cover; border-radius: 6px; background: #E2E6EC; }
.result-info { flex: 1; }
.result-title { font-size: 0.87rem; font-weight: 600; }
.result-year { font-size: 0.75rem; color: var(--muted); font-family: var(--mono); }
/* Season picker — shown instead of requesting a whole TV show outright */
.season-picker.hidden { display: none; }
.season-picker #season-picker-back { margin-bottom: 0.6rem; }
.season-picker h4 { font-family: var(--display); margin: 0 0 0.75rem; font-weight: 600; }
.season-list { overflow-y: auto; max-height: 40vh; margin-bottom: 1rem; }
.season-row { display: flex; align-items: center; gap: 0.7rem; padding: 0.55rem 0; font-size: 0.85rem; }
.season-row + .season-row { border-top: 1px solid rgba(20,25,35,0.08); }
.season-row input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--teal); flex-shrink: 0; }
.season-row-name { flex: 1; }
.season-row-episodes { font-size: 0.75rem; color: var(--muted); font-family: var(--mono); }
.season-row-status { font-size: 0.72rem; font-family: var(--mono); color: var(--muted); }
.season-row.unavailable .season-row-name { color: var(--muted); }

/* Media info modal */
.info-card { width: min(620px, 92vw); padding: 0; overflow: hidden; position: relative; }
.info-close { position: absolute; top: 0.85rem; right: 0.85rem; z-index: 2; }
.info-layout { display: flex; gap: 1.5rem; padding: 1.5rem; max-height: 78vh; overflow-y: auto; }
.info-poster { width: 150px; flex-shrink: 0; border-radius: 12px; object-fit: cover; aspect-ratio: 2/3; background: #E2E6EC; box-shadow: 0 16px 32px -14px rgba(20,25,35,0.4); }
/* Reserves permanent clearance under .info-close (absolutely positioned over
   the top-right corner) so the CH.NN badge (first thing in the body) never
   sits underneath it. */
.info-body { min-width: 0; padding-right: 1.75rem; }
.info-body h3 { font-family: var(--display); font-size: 1.3rem; font-weight: 600; margin: 0.6rem 0 0.3rem; }
.info-meta { color: var(--muted); font-family: var(--mono); font-size: 0.78rem; margin: 0 0 0.85rem; }
.info-overview { font-size: 0.9rem; line-height: 1.55; color: #2b3140; margin: 0; }

/* On a real phone width, the 150px poster + gap left too little room for the
   text column even with the padding-right reservation above — the CH.NN
   badge was wrapping onto two lines instead. Stacking poster-then-text
   gives the column the full card width, and the close button ends up
   floating over the poster's top-right corner instead of competing with
   text for space — a normal, non-colliding pattern. */
@media (max-width: 480px) {
  .info-layout { flex-direction: column; }
  .info-poster { width: 140px; margin: 0 auto 0.5rem; }
  .info-body { padding-right: 0; }
}
.info-stream {
  margin: 0.9rem 0 0; padding-top: 0.75rem; border-top: 1px solid rgba(20,25,35,0.1);
  display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0.75rem;
  font-family: var(--mono); font-size: 0.76rem;
}
.info-stream.hidden { display: none; }
.info-stream dt { color: var(--muted); }
.info-stream dd { margin: 0; color: #2b3140; }

/* Torrent details modal (Download Queue -> click a torrent row) */
.torrent-details-card { width: min(560px, 92vw); }
.torrent-details-layout { padding: 1.5rem; max-height: 78vh; overflow-y: auto; }
.torrent-details-layout h3 { font-family: var(--display); font-size: 1.1rem; font-weight: 600; margin: 0 1.75rem 0.75rem 0; }
.torrent-files { margin-top: 1rem; padding-top: 0.9rem; border-top: 1px solid rgba(20,25,35,0.1); }
.torrent-file-row { padding: 0.4rem 0; }
.torrent-file-row .now-title { white-space: normal; overflow-wrap: anywhere; }

/* "Available now" toast — pushed via SSE when Overseerr reports a request has
   finished downloading. Stacks above everything, including the request modal,
   same z-index trick as .fab since this is a small dashboard and correctness
   here matters more than a tidy stacking context. */
#toast-container {
  position: fixed; bottom: 1.75rem; left: 1.75rem; z-index: 2147483647;
  display: flex; flex-direction: column-reverse; gap: 0.6rem;
  pointer-events: none;
}
.toast {
  display: flex; gap: 0.75rem; align-items: center; width: 300px; max-width: calc(100vw - 3.5rem);
  padding: 0.7rem; background: rgba(20,21,26,0.75); backdrop-filter: blur(20px) saturate(1.5);
  -webkit-backdrop-filter: blur(20px) saturate(1.5); border: 1px solid rgba(255,255,255,0.1);
  border-radius: 12px; box-shadow: 0 20px 40px -16px rgba(0,0,0,0.5);
  pointer-events: auto; cursor: pointer;
  animation: toast-in 0.25s ease;
}
.toast.leaving { animation: toast-out 0.2s ease forwards; }
.toast-poster { width: 38px; height: 54px; object-fit: cover; border-radius: 6px; background: #3a3d47; flex-shrink: 0; }
.toast-body { min-width: 0; }
.toast-eyebrow { font-family: var(--mono); font-size: 0.65rem; color: var(--teal); text-transform: uppercase; letter-spacing: 0.05em; }
.toast-title { font-size: 0.85rem; font-weight: 600; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(8px); } }

/* Admin panel (owner only) — recent sign-ins roster + pending Overseerr requests
   with inline approve/decline. */
.admin-subhead {
  font-family: var(--mono); font-size: 0.68rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em; margin: 0.9rem 0 0.4rem;
}
.admin-subhead:first-of-type { margin-top: 0; }

.login-row { display: flex; align-items: center; gap: 0.7rem; padding: 0.5rem 0; }
.login-row + .login-row { border-top: 1px solid rgba(20,25,35,0.06); }
.login-avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; background: #E2E6EC; flex-shrink: 0; }
.login-name { font-size: 0.85rem; font-weight: 600; }
.login-time { font-size: 0.72rem; color: var(--muted); font-family: var(--mono); }

.pending-row { display: flex; gap: 0.85rem; align-items: center; padding: 0.65rem 0; }
.pending-row + .pending-row { border-top: 1px solid rgba(20,25,35,0.08); }
.pending-requester { display: flex; align-items: center; gap: 0.35rem; margin-top: 3px; font-size: 0.74rem; color: var(--muted); font-family: var(--mono); }
.pending-requester img { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; background: #E2E6EC; }
.pending-actions { display: flex; gap: 0.5rem; flex-shrink: 0; }
/* Wanted/Missing, Search Library, and Open Issues rows act on click anywhere
   in the row — pending requests and import issues only act on their explicit
   buttons. */
#wanted-body .pending-row, #library-search-body .pending-row, #admin-issues-body .pending-row { cursor: pointer; }

/* "Request" — shown on the info modal when opened from a search/discover
   result (not yet in the library), so a title/poster click leads to details
   first rather than committing to a request immediately. */
#info-request-section { margin-top: 1rem; }

/* "Report an issue" — inline sub-form on the media info modal. */
#info-report-section { margin-top: 1rem; padding-top: 0.9rem; border-top: 1px solid rgba(20,25,35,0.1); }
.report-form { margin-top: 0.75rem; }
.report-type-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem; margin-bottom: 0.6rem; }
.report-type-btn .state-dot { background: var(--muted); }
.report-type-btn.selected .state-dot { background: var(--teal); }
.report-type-btn.selected { color: var(--teal); border-color: rgba(15,143,118,0.35); background: rgba(15,143,118,0.08); }
#report-message {
  width: 100%; box-sizing: border-box; padding: 0.6rem 0.75rem; background: rgba(255,255,255,0.7);
  border: 1px solid rgba(20,25,35,0.1); border-radius: 9px; color: var(--text);
  font-family: var(--body); font-size: 0.85rem; resize: vertical; margin-bottom: 0.6rem;
}
#report-message:focus { outline: none; border-color: var(--teal); }
#report-submit-btn:disabled { opacity: 0.5; cursor: default; }
.report-status { font-size: 0.78rem; color: var(--muted); margin: 0.5rem 0 0; }
.report-status.ok { color: var(--teal); }
.report-status.error { color: var(--danger); }

/* Open issues admin rows — reuses .pending-row/.pending-requester/.pending-actions
   from the pending-requests section above; only the reported-message line is new. */
.issue-message { font-size: 0.78rem; color: var(--text); margin-top: 0.3rem; font-style: italic; }

/* Release search modal (owner only) — list of indexer results for resolving
   an issue directly, without leaving the dashboard for Sonarr/Radarr. */
#release-list { flex: 1; min-height: 0; overflow-y: auto; }
.release-row { display: flex; gap: 0.85rem; align-items: center; padding: 0.65rem 0; }
.release-row + .release-row { border-top: 1px solid rgba(20,25,35,0.08); }
.release-row.rejected { opacity: 0.6; }
.release-info { flex: 1; min-width: 0; }
.release-title {
  font-size: 0.82rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.release-meta { font-size: 0.72rem; color: var(--muted); font-family: var(--mono); margin-top: 2px; }
.release-rejections { font-size: 0.7rem; color: var(--danger); margin-top: 2px; }
.grab-btn { flex-shrink: 0; }

/* Post-grab tracking — a grabbed release row keeps evolving in place
   (Grabbed -> Downloading -> Importing -> Done/Failed) instead of freezing
   at "Grabbed ✓". Reuses the plain .bar/.bar-fill classes (not the .now-row-
   scoped ones) since this isn't Now Playing. */
.track-stage {
  font-family: var(--mono); font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); display: flex; align-items: center; gap: 0.4rem; margin-top: 4px;
}
.track-stage .state-dot.amber { background: var(--amber); }
.track-stage .state-dot.danger { background: var(--danger); }
.track-stage .state-dot.pulse { animation: track-pulse 1.4s ease-in-out infinite; }
@keyframes track-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
.track-meta { font-size: 0.76rem; color: var(--muted); margin-top: 3px; }
.track-result-ok { color: var(--teal); font-weight: 600; }
.track-result-fail { color: var(--danger); }
.release-row .bar { margin-top: 6px; }
.fix-it-btn { border-color: rgba(214,69,69,0.35); color: var(--danger); margin-top: 0.5rem; }

/* Settings modal (owner only) — service health grid + per-service/deployment
   edit popups. Saving anything restarts the container (see routes/settings.js),
   since Node only loads .env once at process start. */
.settings-card { width: min(780px, 94vw); overflow-x: hidden; }
.settings-body { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; margin: 0.5rem 0; }
/* .discover-label's shared -0.35rem top margin is tuned to tuck it under a
   search input (its original use) — here it's the first thing in a
   scrollable settings tab with nothing above it, so that negative margin
   was clipping the top of the text against the scroll container's edge. */
#notice-status-text { margin-top: 0; }
/* wraps instead of squeezing "Run Health Check"/"Edit Deployment Settings"
   into a cramped two-line button next to the heading on narrow screens */
.settings-section-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem 0.85rem; }
.settings-section-row .admin-subhead { margin: 0; min-width: 0; }
.settings-section-row .pill-btn { white-space: nowrap; }
.settings-section-row-spaced { margin-top: 1.4rem; }
.settings-status { font-size: 0.8rem; text-align: center; margin: 0.5rem 0 0; }
.settings-status.error { color: var(--danger); }
.settings-status.ok { color: var(--teal); }

/* Service health cards */
.service-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.85rem; margin: 0.75rem 0; }
@media (max-width: 700px) { .service-grid { grid-template-columns: repeat(2, 1fr); } }
/* Two columns of cards (each with a version string, latency, and Edit button)
   is too cramped on an actual phone width — confirmed live, cards were
   overflowing their column and getting clipped by the modal's own edge. */
@media (max-width: 480px) { .service-grid { grid-template-columns: 1fr; } }
.service-card {
  min-width: 0; border: 1px solid rgba(20,25,35,0.08); border-radius: 12px;
  padding: 0.85rem; background: rgba(255,255,255,0.5);
}
.service-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 0.5rem; margin-bottom: 0.5rem; }
/* Same shrink priority as the foot row below — the name truncates before
   the badge (which is short and fixed either way: Online/Unconfigured/Error)
   is ever allowed to shrink, so every card's badge stays visually uniform. */
.service-card-name {
  font-weight: 600; font-size: 0.88rem; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.service-badge {
  font-family: var(--mono); font-size: 0.64rem; padding: 0.2rem 0.5rem; border-radius: 20px;
  border: 1px solid; white-space: nowrap; flex-shrink: 0;
}
.service-badge.online { color: var(--teal); border-color: rgba(15,143,118,0.35); background: rgba(15,143,118,0.08); }
.service-badge.unconfigured { color: var(--muted); border-color: rgba(20,25,35,0.12); background: rgba(20,25,35,0.04); }
.service-badge.error { color: var(--danger); border-color: rgba(214,69,69,0.35); background: rgba(214,69,69,0.08); }
.service-card-meta {
  font-family: var(--mono); font-size: 0.72rem; color: var(--muted); min-height: 1.1em;
  display: flex; align-items: center; gap: 0.35rem;
}
.service-card-foot { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; margin-top: 0.6rem; }
/* A long version string (e.g. Plex's) must truncate before the Edit button
   ever shrinks/wraps — confirmed live, the button was squeezing into a
   two-line "Edit / ✎" shape instead. */
.service-card-foot .pill-btn { flex-shrink: 0; white-space: nowrap; }
.service-card-status-text {
  font-family: var(--mono); font-size: 0.72rem; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}

/* Settings edit popup — one field per row, full-width input, stacked */
.settings-field-block + .settings-field-block { margin-top: 0.9rem; }
.settings-field-block-label { font-size: 0.85rem; font-weight: 600; display: block; margin-bottom: 0.35rem; }
.notice-preset-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; }
.notice-preset-row-fun { opacity: 0.75; }
.settings-input-full {
  width: 100%; padding: 0.6rem 0.75rem; background: rgba(255,255,255,0.7);
  border: 1px solid rgba(20,25,35,0.1); border-radius: 8px; color: var(--text);
  font-family: var(--mono); font-size: 0.85rem;
}
.settings-input-full:focus { outline: none; border-color: var(--teal); }
.settings-input-full:disabled { opacity: 0.5; cursor: not-allowed; }
.settings-field-block-desc { font-size: 0.75rem; color: var(--muted); margin-top: 0.3rem; }
/* Cancel and the primary action (Save Changes / OK) share the same shape and
   size — Cancel keeps its own lighter fill/border, just resized/reshaped to
   match rather than sitting next to it as a small pill. Applies everywhere
   this row is used: the settings edit popup and the confirm dialog. */
.settings-edit-actions { display: flex; gap: 0.6rem; margin-top: 0.5rem; }
.settings-edit-actions .pill-btn,
.settings-edit-actions .btn-primary {
  flex: 1; border-radius: 10px; padding: 0.85rem 1rem;
  font-size: 0.95rem; font-weight: 600; justify-content: center;
}

/* Confirm dialog — replaces the browser's native confirm() so it matches the
   rest of the app instead of a plain unstyled OS dialog. */
.confirm-card { width: min(380px, 90vw); }
#confirm-message { font-size: 0.92rem; line-height: 1.5; margin: 0 0 1rem; }

/* Toggle switch for true/false values */
.settings-toggle { position: relative; display: inline-block; width: 40px; height: 22px; flex-shrink: 0; }
.settings-toggle input { opacity: 0; width: 0; height: 0; }
.settings-toggle-slider {
  position: absolute; inset: 0; background: rgba(20,25,35,0.15); border-radius: 22px;
  transition: background 0.15s ease; cursor: pointer;
}
.settings-toggle-slider::before {
  content: ''; position: absolute; width: 16px; height: 16px; left: 3px; top: 3px;
  background: #fff; border-radius: 50%; transition: transform 0.15s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.settings-toggle input:checked + .settings-toggle-slider { background: var(--teal); }
.settings-toggle input:checked + .settings-toggle-slider::before { transform: translateX(18px); }
.settings-toggle input:disabled + .settings-toggle-slider { opacity: 0.5; cursor: not-allowed; }
