:root {
  --bg: #f6f7fb;
  --card-bg: #ffffff;
  --ink: #16233f;
  --muted: #6b7280;
  --accent: #16233f;
  --border: #e5e7eb;
}

:root[data-theme="dark"] {
  --bg: #0f1420;
  --card-bg: #161d2e;
  --ink: #e8ebf3;
  --muted: #8b93a7;
  --accent: #e8ebf3;
  --border: #262f45;
}

/* Thin, theme-matched scrollbar: not hidden outright (that removes the
   only visual cue a panel scrolls, a real usability regression, especially
   on Windows where there's no other scroll affordance by default). Instead
   replaces the default chunky Windows scrollbar with a slim, subtle one
   that tracks light/dark via the --border/--muted tokens above. Firefox
   uses scrollbar-width/scrollbar-color; everything Chromium/WebKit-based
   (most Windows browsers) uses ::-webkit-scrollbar. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

*::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

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

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

*::-webkit-scrollbar-thumb:hover {
  background: var(--muted);
}

.material-symbols-outlined {
  font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
  font-size: 20px;
  vertical-align: middle;
  user-select: none;
}

* {
  box-sizing: border-box;
}

/* Native checkboxes/radios otherwise render with the browser's default blue
   fill regardless of theme: clashes with the site's dark palette and
   doesn't match the monochrome --ink/--bg look everything else uses for an
   "active" state (nav-item[aria-current], .btn-primary, ...). One shared
   rule since they show up in several unrelated places (region/language/home
   region filters, the profile newsletter toggle, the video modal's autoplay
   toggle) that shouldn't each need their own override. */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--ink);
}

body {
  font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
  background: var(--bg);
  color: var(--ink);
  margin: 0;
}

/* Mobile-only branding bar (hidden at desktop widths further down):
   .sidebar-brand is the desktop equivalent, sized for the sidebar's ~190px
   column instead of the full page width. */
header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  max-width: 1600px;
  margin: 0 auto;
  /* Symmetric, not the top-heavy 2rem/1rem this used to be: that split
     made sense when header just sat at the top of a scrolling page (extra
     top breathing room from the viewport edge), but reads as visibly
     unbalanced now that it's a persistent fixed bar with its own
     background (see the max-width:860px block below). Keep in lockstep
     with that block's main padding-top comment if this changes again. */
  padding: 1rem 1.25rem;
}

/* Wraps the logo/title so the whole header, like .sidebar-brand, links home;
   its own flex/gap reproduces what a plain sibling img+div used to give
   header's own flex for free, since header now only has two children
   (the hamburger and this link). */
.header-brand {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  text-decoration: none;
  color: inherit;
}

header img {
  width: 36px;
  height: 36px;
}

header h1 {
  font-size: 1.4rem;
  margin: 0;
}

header p {
  margin: 0.15rem 0 0;
  color: var(--muted);
  font-size: 0.9rem;
}

.hamburger {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--ink);
  cursor: pointer;
  flex-shrink: 0;
}

.app-body {
  display: flex;
  align-items: flex-start;
  max-width: 1600px;
  margin: 0 auto;
}

/* Full viewport height + sticky, not just "grows with content": that's
   what lets .sidebar-footer sit pinned at the bottom (via .sidebar-scroll
   taking the remaining flex:1 space above it) instead of just being last
   in the document flow, which wouldn't reach the bottom on a short page.
   z-index matters here too: position: sticky always creates a stacking
   context, so without an explicit z-index .auth-panel's own z-index (45)
   only ever competes against its siblings *inside* that context, never
   against main's content. A hovered .video card gets its own stacking
   context too (via transform), and since main comes after .sidebar in the
   DOM, it would win the paint order and cover the panel regardless of the
   panel's own z-index. Below 200 (.video-modal-backdrop): this only needs
   to beat auto-level main content, not the video modal. */
.sidebar {
  width: 190px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  height: 100vh;
  position: sticky;
  top: 0;
  z-index: 40;
}

.sidebar-brand {
  display: none;
  align-items: center;
  gap: 0.5rem;
  padding: 1.5rem 1rem 1rem;
  text-decoration: none;
  color: inherit;
}

.sidebar-brand img {
  width: 28px;
  height: 28px;
}

.sidebar-brand h1 {
  font-size: 1.1rem;
  margin: 0;
}

.sidebar-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

/* The full-width mobile <header> and .sidebar-brand are two separate
   renders of the same branding (see the comment on renderHeader): only
   one is ever visible at a time. Hiding the header here also removes the
   gap it used to leave above main on desktop; main now starts at the true
   top of the page (see main's own padding-top override below). */
@media (min-width: 861px) {
  header {
    display: none;
  }

  .sidebar-brand {
    display: flex;
  }

  main {
    padding-top: 2rem;
  }

  /* Mobile-only nav destination: desktop's side column already shows
     headlines alongside the shelf stack at all times (see index.html),
     so this would just be a redundant link there. `a.nav-item-headlines`
     (not just `.nav-item-headlines`) is deliberate: the general `.nav-item`
     rule below has equal specificity and comes later in the file, so an
     equal-specificity override here would lose on source order alone. */
  a.nav-item-headlines {
    display: none;
  }
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  border-radius: 8px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.88rem;
  font-weight: 600;
}

.nav-item:hover {
  background: var(--card-bg);
}

.nav-item[aria-current="page"] {
  background: var(--ink);
  color: var(--bg);
}

.sidebar-filters {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.filters-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.4rem 0.7rem;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font-size: 0.8rem;
  cursor: pointer;
  text-align: left;
}

.filters-toggle:hover {
  background: var(--card-bg);
}

.filters-summary {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.filters-chevron {
  flex-shrink: 0;
  font-size: 18px;
  transition: transform 0.15s ease;
}

.filters-toggle[aria-expanded="true"] .filters-chevron {
  transform: rotate(180deg);
}

.filters-body {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-top: 0.3rem;
}

/* Author `display: flex` above otherwise beats the UA [hidden] rule: same
   recurring gap this project has hit before (see .quick-action-btn etc.). */
.filters-body[hidden] {
  display: none;
}

.filter-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.7rem;
  font-size: 0.85rem;
  color: var(--ink);
  cursor: pointer;
}

/* type="radio" is Home region's single-select list (see renderRadioGroup in
   helpers.js): same sizing as the checkbox groups above it. */
.filter-option input[type="checkbox"],
.filter-option input[type="radio"] {
  width: 15px;
  height: 15px;
  cursor: pointer;
  flex-shrink: 0;
}

.sidebar-footer {
  flex-shrink: 0;
  margin: 1rem 1rem 0;
  padding: 1rem 0 1.5rem;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.drawer-backdrop {
  display: none;
}

.sidebar-close {
  display: none;
}

@media (max-width: 860px) {
  .hamburger {
    display: inline-flex;
  }

  /* At mobile widths, hamburger + logo + title still leaves too little
     room for the subtitle on one line next to "Daily Anchor": drop it
     rather than let it wrap into a cramped stack. (auth-btn/theme-toggle
     used to compete for this same space too, before they moved into the
     sidebar footer; see renderSidebar.) */
  header p {
    display: none;
  }

  /* Fixed + slide-away-on-scroll-down/reveal-on-scroll-up (see
     initHeaderAutoHide in helpers.js): otherwise the header just sits in
     normal document flow and scrolls off for good, leaving no way back to
     the hamburger without scrolling all the way back to the top. z-index
     15: above ordinary content (auto/0) so it doesn't scroll under it, but
     below .drawer-backdrop (20)/.sidebar (30) so opening the drawer dims
     the header along with everything else instead of leaving it stranded
     on top of the dimming overlay. */
  header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 15;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    transform: translateY(0);
    transition: transform 0.25s ease;
  }

  header.header-hidden {
    transform: translateY(-100%);
  }

  /* Header is now removed from flow (position: fixed above): main needs
     its own top offset to clear it instead of the two just naturally
     stacking. 68px is header's own height (1rem+1rem vertical padding +
     its 36px-tall content row, `header p`'s subtitle already hidden above);
     keep this in lockstep with header's padding/content if either
     changes, or main's first row ends up under the fixed header.
     `body main`, not bare `main`: the base `main{padding:...}` rule further
     down the file has equal specificity and would otherwise win this on
     source order alone regardless of this media query matching. */
  body main {
    padding-top: calc(68px + 1rem);
  }

  .sidebar-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 1rem;
    right: 1rem;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--card-bg);
    color: var(--ink);
    cursor: pointer;
  }

  .app-body {
    display: block;
  }

  .sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: 230px;
    height: auto;
    background: var(--bg);
    border-right: 1px solid var(--border);
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    z-index: 30;
  }

  /* .sidebar-brand is desktop-only (see the base rule): on mobile the top
     header already shows branding, so .sidebar-scroll needs its own top
     padding here instead of getting it for free below .sidebar-brand. */
  .sidebar-scroll {
    padding-top: 5rem;
  }

  .sidebar.open {
    transform: translateX(0);
  }

  .drawer-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
    z-index: 20;
  }

  .drawer-backdrop.open {
    opacity: 1;
    pointer-events: auto;
  }
}

/* display: flex + flex:1 on .auth-btn below is what lets the button fill
   the sidebar-footer row (sized by .theme-toggle's fixed 36px + the row's
   own width): .auth-panel is position: absolute so it doesn't
   participate in this and isn't affected. */
.auth-control {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
}

.auth-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 36px;
  flex: 1;
  min-width: 0;
  padding: 0 0.85rem;
  border-radius: 18px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}

.auth-btn .material-symbols-outlined {
  flex-shrink: 0;
  font-size: 20px;
}

/* text-overflow: ellipsis doesn't reliably render on native <button>
   elements (a long-standing cross-browser quirk): the box truncates
   correctly but silently drops the "…" indicator, reading as broken text
   rather than an intentional truncation. Applying it to this inner span
   instead of the button itself renders correctly everywhere. */
.auth-btn-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.auth-btn:hover {
  background: var(--border);
}

/* left: 0, not right: 0: .auth-control now lives in the ~190px sidebar
   near the viewport's left edge. Anchoring the panel's right edge to
   auth-control's right edge (the old header-era positioning) would push
   most of this 260px-wide panel off-screen to the left; anchoring its left
   edge instead expands it rightward into the main content area, which has
   room.
   bottom, not top: .auth-control now sits at the very bottom of the
   sidebar footer, so opening downward (the old header-era positioning,
   where there was a whole page below it) pushed the panel past the
   viewport's bottom edge. Opening upward has the sidebar's full height
   available instead. */
.auth-panel {
  display: none;
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  width: 260px;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  z-index: 45;
}

.auth-panel.open {
  display: block;
}

/* In-flow, not absolutely positioned in the corner: the email/code input
   sits flush against the panel's own padding, so an overlaid button there
   would sit on top of it. */
.auth-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin: -0.2rem -0.3rem 0.75rem 0;
}

.auth-panel-title {
  font-size: 0.88rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.auth-panel-close {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.auth-panel-close:hover {
  background: var(--border);
  color: var(--ink);
}

.auth-panel-close .material-symbols-outlined {
  font-size: 18px;
}

.auth-panel form {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.auth-panel input[type="email"],
.auth-panel input[type="text"] {
  padding: 0.6rem 0.8rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--ink);
  font-size: 0.9rem;
  width: 100%;
}

.auth-panel form button {
  padding: 0.55rem 0.8rem;
  border-radius: 8px;
  border: none;
  background: var(--ink);
  color: var(--bg);
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
}

.auth-panel form button:hover {
  opacity: 0.9;
}

.auth-panel form button:disabled {
  opacity: 0.6;
  cursor: default;
}

.btn-secondary {
  display: block;
  width: 100%;
  margin-top: 0.6rem;
  padding: 0.5rem 0.7rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  font-size: 0.82rem;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
}

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

.btn-secondary:disabled {
  opacity: 0.6;
  cursor: default;
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--ink);
  cursor: pointer;
}

.theme-toggle:hover {
  background: var(--border);
}

main {
  flex: 1;
  min-width: 0;
  padding: 1rem 1.25rem 3rem;
}

.page-heading {
  font-size: 1.3rem;
  margin: 0 0 0.3rem;
}

.page-sub {
  color: var(--muted);
  font-size: 0.92rem;
  margin: 0 0 1.5rem;
}

#status {
  color: var(--muted);
  padding: 2rem 0;
}

/* auto-fill + minmax instead of a fixed column count, so column count
   responds to actual available width (narrower inside a split layout like
   the homepage's videos/headlines columns, wider on courses.html/course.html
   which don't share the row), rather than one hardcoded breakpoint. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1.25rem;
}

/* Shared "course preview" component: courses.html (one per course in a
   department) and path.html (one per course in a path) both render a
   course's heading/description above a capped, horizontally-scrolling row
   of its videos, closed out by a .video-cta tile. Single row, not a
   wrapping grid, so vertical space per course stays constant regardless of
   how many are listed on the page, no reflow needed at narrow widths. */
.course-preview {
  margin-bottom: 1.75rem;
}

.course-preview:last-child {
  margin-bottom: 0;
}

.course-preview-head {
  margin-bottom: 0.85rem;
}

.course-preview-head .title {
  font-size: 1.05rem;
  font-weight: 700;
  margin: 0 0 0.3rem;
}

.course-preview-head .desc {
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.4;
  margin: 0;
  max-width: 620px;
}

.course-preview .card-grid {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-bottom: 0.35rem;
}

.course-preview .card-grid > .video,
.course-preview .card-grid > .video-cta {
  flex: 0 0 200px;
}

@media (max-width: 860px) {
  .course-preview .card-grid > .video,
  .course-preview .card-grid > .video-cta {
    flex: 0 0 165px;
  }
}

.course-preview-message {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0;
}

/* Closes out a course's preview row as its own tile. Deliberately not
   styled like the video cards beside it (no thumbnail) so it reads as an
   action, not a 6th piece of content. Uses --bg/--ink the same direction
   the .video cards' own background does (card-bg is a step off --bg in
   both themes), rather than .btn-primary's inverted fill, because a stark
   white-on-dark tile stood out as looking broken, not accented, once it
   was one repeated tile among many rather than a single button. */
.video-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0.15rem;
  background: var(--bg);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 10px;
  text-decoration: none;
  padding: 1rem 0.75rem;
  transition: border-color 0.15s ease;
}

.video-cta:hover {
  border-color: var(--muted);
}

/* --cta-pct (set inline per course, from real watched/count data) draws a
   conic-gradient progress ring in the tile's own ink color against a
   dimmed version of itself as the track. */
.video-cta-icon {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.5rem;
  background: conic-gradient(var(--ink) var(--cta-pct, 0%), color-mix(in srgb, var(--ink) 25%, transparent) 0);
}

.video-cta-icon-inner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--bg);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
}

.video-cta-icon-inner .material-symbols-outlined {
  font-size: 1.1rem;
}

.video-cta-label {
  font-weight: 700;
  font-size: 0.85rem;
}

.video-cta-more {
  font-size: 0.72rem;
  opacity: 0.75;
}

/* Small tier-name pill, used by path.html's per-step badge and paths.html's
   tier-span badge (e.g. "Fundamental → Advanced"). */
.tier-pill {
  flex-shrink: 0;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.25rem 0.65rem;
}

.video {
  position: relative;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.video:hover {
  box-shadow: 0 6px 18px rgba(22, 35, 63, 0.1);
  transform: translateY(-2px);
}

/* Clipping lives here (not on .video) so the card's own rounded corners
   still clip the thumbnail image, without also clipping the card-menu
   dropdown below, which needs to extend past the card's box when open. */
.video .thumb {
  display: block;
  position: relative;
  cursor: pointer;
  border: none;
  padding: 0;
  width: 100%;
  background: none;
  overflow: hidden;
  border-radius: 10px 10px 0 0;
}

/* Ordered courses' episode number, shared by course.html's full lesson
   list and courses.html's preview grid: top-left, since .duration/
   .card-menu-wrap/.viewed-badge all live top-right. */
.episode-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  background: rgba(0, 0, 0, 0.75);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 4px;
}

.duration {
  position: absolute;
  bottom: 6px;
  right: 6px;
  background: rgba(0, 0, 0, 0.75);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 1px 5px;
  border-radius: 4px;
}

/* Top-right, alongside .card-menu-wrap (also top-right, further out at
   right:6px); sits just to that button's left, "before" it in reading
   order. Both only ever show for a signed-in user, so there's no case
   where this renders without the button also being present. */
.viewed-badge {
  position: absolute;
  top: 6px;
  right: 34px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  /* Same green as .toast.success, since "watched" reuses the site's existing
     success color instead of introducing a new one. */
  background: #1a9c5f;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 1;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}

.viewed-badge .material-symbols-outlined {
  font-size: 16px;
}

.viewed-badge:hover {
  background: #157f45;
}

/* Resume-progress sliver, flush along .thumb's bottom edge. Black/white
   like .duration rather than --ink/--border, since it sits over a photo. */
.thumb-progress {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: rgba(0, 0, 0, 0.35);
}

.thumb-progress > span {
  display: block;
  height: 100%;
  background: #fff;
}

/* Grid-card "more options" menu: top-right corner, outermost, with
   .viewed-badge sitting just to its left (see that rule) when present.
   Positioned relative to .video (not .thumb, which clips; see the
   comment on .video .thumb) even though it's a sibling of .thumb, same
   reasoning as .viewed-badge's placement. course.html's .episode-badge
   (defined there) uses the top-left corner instead, so no collision. */
.card-menu-wrap {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
}

.card-menu-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  cursor: pointer;
}

.card-menu-btn.visible {
  display: flex;
}

.card-menu-btn:hover {
  background: rgba(0, 0, 0, 0.8);
}

.card-menu-btn .material-symbols-outlined {
  font-size: 16px;
}

.card-menu {
  top: calc(100% + 4px);
  right: 0;
  min-width: 170px;
}

.video img,
.video iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
  background: #dfe3ea;
  border: none;
}

.play-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
}

.play-badge::after {
  content: "";
  border-style: solid;
  border-width: 8px 0 8px 13px;
  border-color: transparent transparent transparent #fff;
  margin-left: 3px;
}

.thumb:hover .play-badge {
  background: rgba(0, 0, 0, 0.85);
}

.video-body {
  padding: 0.85rem 1rem 1rem;
}

.video-body .title {
  color: var(--ink);
  font-weight: 600;
  font-size: 0.9rem;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--muted);
  font-size: 0.8rem;
  margin-top: 0.5rem;
}

.last-updated {
  color: var(--muted);
  font-size: 0.78rem;
  margin: 0.2rem 0 0.85rem;
}

/* Lives inside <main> (a direct sibling of the shelf/content stack, right
   before </main>) precisely so it inherits main's own horizontal padding
   and its offset past the sidebar: a plain page-level sibling of
   .app-body instead (the old placement) spans the full viewport width,
   including the space the sidebar column occupies, so its content doesn't
   line up with everything above it. No max-width/centering of its own
   needed here since main is already width-bound by .app-body. */
footer {
  margin-top: 1.5rem;
}

.subscribe {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  justify-content: space-between;
}

.subscribe-copy strong {
  display: block;
  font-size: 1rem;
  margin-bottom: 0.2rem;
}

.subscribe-copy p {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
  max-width: 420px;
}

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.6rem 1.1rem;
  border-radius: 8px;
  border: none;
  background: var(--ink);
  color: var(--bg);
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  text-decoration: none;
}

.btn-primary:hover {
  opacity: 0.9;
}

.btn-primary:disabled {
  opacity: 0.6;
  cursor: default;
}

/* Generic icon+text+button horizontal CTA - used by the feedback card's
   signed-out state (helpers.js's renderFeedbackCard) on both index.html and
   profile.html. */
.signin-prompt {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.9rem;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1rem 1.2rem;
}

.signin-prompt .material-symbols-outlined {
  font-size: 1.6rem;
  color: var(--muted);
  flex-shrink: 0;
}

.signin-prompt strong {
  display: block;
  font-size: 0.92rem;
}

.signin-prompt p {
  margin: 0.2rem 0 0;
  font-size: 0.82rem;
  color: var(--muted);
}

.signin-prompt .btn-primary {
  margin-left: auto;
  flex-shrink: 0;
}

@media (max-width: 860px) {
  .signin-prompt .btn-primary {
    margin-left: 0;
    width: 100%;
    justify-content: center;
  }
}

/* Shared feedback card (helpers.js's renderFeedbackCard/loadFeedbackCard),
   used standalone on profile.html and paired with the newsletter CTA in
   index.html's own .home-footer-row layout (that pairing's height-matching
   overrides live there, page-specific; this is just the card itself). */
.feedback-section {
  margin-top: 0;
}

.feedback-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1.2rem 1.3rem;
}

.feedback-card-copy strong {
  display: block;
  font-size: 0.92rem;
}

.feedback-card-copy p {
  margin: 0.2rem 0 0.9rem;
  font-size: 0.82rem;
  color: var(--muted);
}

.feedback-form {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

.feedback-form textarea {
  width: 100%;
  min-height: 80px;
  padding: 0.65rem 0.8rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-size: 0.88rem;
  resize: vertical;
}

.feedback-form .btn-primary {
  align-self: flex-end;
}

.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--card-bg);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.75rem 1.1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.88rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 100;
}

.toast.visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.toast.success .material-symbols-outlined {
  color: #1a9c5f;
}

.toast.error .material-symbols-outlined {
  color: #d1453b;
}

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

@keyframes shimmer {
  0% {
    background-position: -400px 0;
  }
  100% {
    background-position: 400px 0;
  }
}

.skeleton {
  background-image: linear-gradient(90deg, var(--border) 25%, var(--card-bg) 50%, var(--border) 75%);
  background-size: 400px 100%;
  animation: shimmer 1.4s linear infinite;
}

.video .thumb.skeleton {
  display: block;
  aspect-ratio: 16 / 9;
}

.skeleton.line {
  height: 0.85rem;
  border-radius: 4px;
}

.section-heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.play-all-btn {
  padding: 0.45rem 0.85rem;
  font-size: 0.82rem;
}

/* .btn-primary's own `display: inline-flex` otherwise beats the UA [hidden]
   rule (same gap as .video-modal-error/.quick-action-btn further
   down), so the button was visible (and clickable, before the click
   listener was even attached) during the initial load window. Scoped to
   .play-all-btn rather than .btn-primary itself since no other .btn-primary
   usage toggles `hidden`. */
.play-all-btn[hidden] {
  display: none;
}

/* Modal/theater video player, replacing the old inline-card iframe swap.
   z-index sits above both .toast (100) and .auth-panel (45). */
.video-modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  z-index: 200;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

.video-modal-backdrop.open {
  display: flex;
}

.video-modal {
  width: min(1000px, 100%);
  max-height: calc(100vh - 3rem);
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.video-modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1.1rem;
  border-bottom: 1px solid var(--border);
}

.video-modal-titles {
  min-width: 0;
}

.video-modal-breadcrumb {
  display: block;
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 0.15rem;
}

.video-modal-titles h3 {
  margin: 0;
  font-size: 1rem;
  line-height: 1.35;
}

.video-modal-meta {
  margin-top: 0.25rem;
  font-size: 0.8rem;
  color: var(--muted);
}

/* Right side of the header: window chrome (watch-link/minimize/close) on
   line 1, watched/bookmark quick actions on line 2, both right-aligned.
   Deliberately plain buttons rather than a menu so both states are visible
   and clickable without an extra tap. On mobile, .video-modal-quick-actions
   is moved out of here entirely (see positionQuickActions in player.js);
   this wrapper just falls back to holding .video-modal-actions alone. */
.video-modal-header-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
  flex-shrink: 0;
}

.video-modal-quick-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}

/* Mobile position: a row of its own between the player and Up Next,
   skewed right rather than stretched full-width. */
@media (max-width: 860px) {
  .video-modal-body > .video-modal-quick-actions {
    padding: 0.6rem 1.1rem 0;
  }
}

.quick-action-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.4rem 0.7rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

.quick-action-btn:hover {
  background: var(--border);
}

/* Same green as .viewed-badge / .toast.success, reused for any "on" state
   (watched, bookmarked) rather than introducing a new color. */
.quick-action-btn.is-active {
  border-color: #1a9c5f;
  color: #1a9c5f;
}

.quick-action-btn[hidden] {
  display: none;
}

/* Smaller on desktop, where these sit stacked under the header's actions
   row rather than getting a full-width row to themselves like on mobile.
   .video-modal-watch-link matches so the two rows read as one size scale. */
@media (min-width: 861px) {
  .quick-action-btn,
  .video-modal-watch-link {
    padding: 0.3rem 0.55rem;
    gap: 0.25rem;
    font-size: 0.72rem;
  }

  .quick-action-btn .material-symbols-outlined,
  .video-modal-watch-link .material-symbols-outlined {
    font-size: 16px;
  }
}

/* Shared by the grid card's overflow ("more options") menu (see
   .card-menu-wrap further down). Same anchored-dropdown convention as
   .auth-panel: toggled via a class, not the hidden attribute, so it
   doesn't need a defensive [hidden] rule. */
.menu-panel {
  display: none;
  position: absolute;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.35rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  z-index: 20;
}

.menu-panel.open {
  display: block;
}

.menu-item {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
  white-space: nowrap;
}

.menu-item:hover {
  background: var(--border);
}

.menu-item.is-active {
  color: #1a9c5f;
}

.video-modal-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.video-modal-watch-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.4rem 0.7rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.video-modal-watch-link:hover {
  background: var(--border);
}

.video-modal-close,
.video-modal-minimize {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  flex-shrink: 0;
}

.video-modal-close:hover,
.video-modal-minimize:hover {
  background: var(--border);
}

.video-modal-body {
  display: grid;
  grid-template-columns: 1fr 280px;
  min-height: 0;
  overflow: hidden;
}

.video-modal-player-area {
  position: relative;
  background: #000;
  aspect-ratio: 16 / 9;
  max-width: 100%;
  /* Without this, the grid's default align-items: stretch forces this box
     to match the row's height (which the auto-sized track would otherwise
     take from whichever column is taller, i.e. a long Up Next list),
     squashing/cropping the embedded video instead of letting aspect-ratio
     govern its size. Sized purely from its own width; player.js then
     measures the result and imposes it as the Up Next column's explicit
     height (see the ResizeObserver in ensureModal), so the two columns
     match with no leftover gap and no chance of feeding back into this
     element's own size. */
  align-self: start;
}

.video-modal-player-mount,
.video-modal-player-mount iframe {
  width: 100%;
  height: 100%;
  border: none;
  display: block;
}

/* Same [hidden]-vs-author-display gap as .video-modal-error/.quick-action-btn
   above: onError() sets playerMountEl.hidden to swap in the error overlay,
   but without this the mount (and its stale last-frame iframe) kept
   rendering underneath it. */
.video-modal-player-mount[hidden] {
  display: none;
}

.video-modal-error {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  color: #fff;
  text-align: center;
  padding: 1.5rem;
}

/* Author `display: flex` above otherwise beats the UA [hidden] rule, so the
   (empty, transparent) error box stayed laid out over the player even while
   "hidden", an invisible layer that silently ate every click meant for the
   iframe's own play/pause/settings controls. Needs the extra specificity
   here (class + attribute) to actually win. */
.video-modal-error[hidden] {
  display: none;
}

.video-modal-error .video-modal-watch-link {
  border-color: rgba(255, 255, 255, 0.3);
  color: #fff;
}

.video-modal-upnext {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-left: 1px solid var(--border);
}

.video-modal-upnext-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 0.9rem;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}

.video-modal-autoplay {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  cursor: pointer;
}

.video-modal-upnext-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.video-modal-upnext-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem;
  border: 1px solid transparent;
  border-radius: 8px;
  background: none;
  cursor: pointer;
  text-align: left;
  color: var(--ink);
}

.video-modal-upnext-item:hover {
  background: var(--border);
}

.video-modal-upnext-item.active {
  border-color: var(--ink);
  background: var(--border);
}

.video-modal-upnext-item img {
  width: 88px;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 6px;
  flex-shrink: 0;
  background: #dfe3ea;
}

.video-modal-upnext-info {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.video-modal-upnext-title {
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.video-modal-upnext-duration {
  font-size: 0.72rem;
  color: var(--muted);
}

@media (max-width: 860px) {
  .video-modal-backdrop {
    padding: 0;
  }

  .video-modal {
    width: 100%;
    height: 100%;
    max-height: none;
    border-radius: 0;
  }

  /* Side-by-side titles + actions get cramped next to a long title at
     narrow widths, so stack them instead, actions on top. column-reverse
     flips the visual order without reordering the DOM (titles stay first
     for source order / screen readers). */
  .video-modal-header {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: 0.6rem;
  }

  .video-modal-actions {
    justify-content: flex-end;
  }

  /* 3 rows, not 2: player.js's positionQuickActions() reparents
     .video-modal-quick-actions in here (between player-area and upnext)
     at this same breakpoint, since CSS alone can't move it out of the
     header. */
  .video-modal-body {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    overflow-y: auto;
  }

  .video-modal-upnext {
    border-left: none;
    border-top: 1px solid var(--border);
  }
}

/* Minimized ("mini player") mode: the video keeps playing in a small
   corner box while the backdrop stops dimming/blocking the rest of the
   page, so the user can keep scrolling/browsing elsewhere. Declared after
   the max-width: 860px block (and using the .minimized class, one level
   more specific than .video-modal alone) so it wins at every viewport
   width, including the mobile full-bleed layout above. */
.video-modal-backdrop.minimized {
  background: transparent;
  pointer-events: none;
  align-items: flex-end;
  justify-content: flex-end;
  padding: 1rem;
}

.video-modal.minimized {
  width: min(320px, calc(100vw - 2rem));
  height: auto;
  max-height: none;
  border-radius: 10px;
  pointer-events: auto;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
}

.video-modal.minimized .video-modal-header,
.video-modal.minimized .video-modal-upnext {
  display: none;
}

.video-modal.minimized .video-modal-body {
  display: block;
}

.video-modal.minimized .video-modal-player-area {
  width: 100%;
  max-width: 100%;
  aspect-ratio: 16 / 9;
}

.video-modal-mini-controls {
  display: none;
  position: absolute;
  top: 6px;
  right: 6px;
  gap: 0.3rem;
  z-index: 1;
}

.video-modal.minimized .video-modal-mini-controls {
  display: flex;
}

.video-modal-mini-controls button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  cursor: pointer;
}

.video-modal-mini-controls button:hover {
  background: rgba(0, 0, 0, 0.85);
}

.video-modal-mini-controls .material-symbols-outlined {
  font-size: 16px;
}
