/*
 * Video browse ("Quiet Gallery") and watch ("Single Focus") surfaces.
 *
 * Static design-prototype pass for #309/#316 (see routes.swift for the
 * TEMPORARY fixture-backed routes this styles). Scoped to .videos-*,
 * .watch-*, and .video-tile* classes — deliberately not reusing
 * .course-card/.course-grid from site.css/library.css (tracked duplication
 * in #328; those are fixed 160px-height thumbnails, not aspect-ratio-locked,
 * and not a clean base for a 16:9 video card). The video player itself
 * reuses the existing .video-frame chrome from video-player.css — do not
 * duplicate that here.
 *
 * The browse-grid card class is named .video-tile, not .video-card:
 * video-player.css already defines .video-card as the single-player
 * wrapper (padding/max-width/centering for one player, not a grid item).
 * An earlier pass named the grid card .video-card too, and the two rules
 * silently combined — the player wrapper's `margin-left/right: auto`
 * pulled every grid tile out of stretch alignment into shrink-to-fit
 * sizing, producing uneven card widths across a row. Do not reintroduce
 * that name here; grep Public/css/*.css for a class name before adding it.
 *
 * All colors reference the CSS custom properties defined in site.css :root
 * so both surfaces track light/dark mode automatically.
 */

/* ---------------------------------------------------------------------
   Shared
   --------------------------------------------------------------------- */

.watch-container {
  max-width: 888px; /* matches video-player.css's .video-card 500px-tall 16:9 cap */
  margin: 0 auto;
  padding: 40px 20px 64px;
}

/* .videos-container renders nested inside the library sidebar shell's
   .main-content (issue #412 prep) — .main-content already supplies its own
   horizontal padding and .library-container already caps the shell's
   overall width to 1200px. An outer max-width/margin:auto/full horizontal
   padding here would stack on top of that and double the insets, the same
   way library/course.leaf's own content relies on .main-content alone
   rather than bringing its own outer container padding. Keep only the
   vertical breathing room the grid still needs (.videos-header already
   has its own bottom margin, so the top value here is modest). */
.videos-container {
  padding: 0 0 24px;
}

/* ---------------------------------------------------------------------
   Browse page — "Quiet Gallery"
   --------------------------------------------------------------------- */

.videos-header {
  text-align: center;
  margin-bottom: 40px;
}

.videos-eyebrow {
  margin: 0 0 8px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-primary);
}

.videos-header h1 {
  margin: 0 0 12px;
  color: var(--text-primary);
  font-size: 2.25rem;
}

.videos-subhead {
  margin: 0 auto;
  max-width: 46em;
  color: var(--text-secondary);
}

/* Column count follows the grid's own available width (auto-fill/minmax),
   not fixed viewport-width breakpoints — issue #412 prep nests this grid
   inside the library sidebar shell, where a fixed 240px sidebar (desktop
   only, library.css) eats into the viewport width the old breakpoints
   assumed was fully available. Matches .course-grid's existing
   container-relative pattern in library.css, the established precedent
   for a grid living inside this same shell. */
.videos-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 28px;
}

.video-tile {
  display: block;
  /* Matches .video-frame's 20px corner radius (video-player.css) so
     primary cards and the player read as the same shape language. The
     thumbnail's top corners follow automatically via this overflow:
     hidden — no separate radius needed on .video-tile-thumb. */
  border-radius: 20px;
  overflow: hidden;
  background: var(--bg-secondary);
  border: 1px solid var(--border-color);
  color: inherit;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.video-tile:hover,
.video-tile:focus-visible {
  transform: translateY(-4px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}

.video-tile:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .video-tile {
    transition: box-shadow 0.2s ease;
  }

  .video-tile:hover,
  .video-tile:focus-visible {
    transform: none;
  }
}

.video-tile-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
}

.video-tile-thumb-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A little hue variety across the grid so cards without real thumbnails
   don't read as one repeated tile. Purely decorative — colors still derive
   from the light/dark-aware custom properties above.

   Excludes .has-poster: once a video has a real thumbnail (#316), this
   hue-rotate would shift the actual poster image's colors, which is a
   real regression, not a decorative flourish — the filter is only for
   the gradient-placeholder cards that have no thumbnail to distort.

   Cycles on nth-child(4n+...) rather than 3n so the pattern lines up
   row-to-row when the fluid auto-fill grid above happens to land on 4
   columns (a 3n cycle against a 4-wide grid drifts diagonally by one card
   per row, which reads as a bug rather than a design choice). At other
   column counts the cycle is purely decorative variety, not meant to
   align row-to-row. Three variants plus the unmodified base gives four
   distinct looks per cycle. */
.videos-grid .video-tile:nth-child(4n+2) .video-tile-thumb:not(.has-poster) {
  filter: hue-rotate(20deg);
}

.videos-grid .video-tile:nth-child(4n+3) .video-tile-thumb:not(.has-poster) {
  filter: hue-rotate(-18deg);
}

.videos-grid .video-tile:nth-child(4n+4) .video-tile-thumb:not(.has-poster) {
  filter: hue-rotate(42deg);
}

.video-tile-duration {
  position: absolute;
  right: 8px;
  bottom: 8px;
  padding: 2px 7px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.75);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.video-tile-body {
  padding: 14px 16px 16px;
}

.video-tile-title {
  margin: 0 0 6px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--text-primary);
}

.video-tile-date {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.videos-empty-state {
  text-align: center;
  padding: 64px 20px;
  border: 1px dashed var(--border-color);
  border-radius: 14px;
}

.videos-empty-heading {
  margin: 0 0 8px;
  font-size: 1.1rem;
  color: var(--text-primary);
}

.videos-empty-subtext {
  margin: 0;
  color: var(--text-secondary);
}

.videos-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin: 40px 0 0;
}

.videos-pagination-link {
  color: var(--accent-primary);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
}

.videos-pagination-link:hover,
.videos-pagination-link:focus-visible {
  text-decoration: underline;
}

.videos-pagination-info {
  color: var(--text-secondary);
  font-size: 0.9rem;
}

/* ---------------------------------------------------------------------
   Watch page — "Single Focus"
   --------------------------------------------------------------------- */

.watch-eyebrow {
  margin: 0 0 10px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-primary);
}

/* partials/watch-shell always renders the <p class="watch-eyebrow">
   wrapper (issue #444 third follow-up) — the caller-side #if that
   decides whether there's anything to show (e.g. video.hasDescription)
   can only make eyebrowText empty, not remove the wrapper itself (the
   shell can't test an imported value in its own #if, see that
   partial's doc comment). Collapse the empty case here instead. */
.watch-eyebrow:empty {
  display: none;
}

.watch-title {
  margin: 0 0 12px;
  color: var(--text-primary);
  line-height: 1.25;
  font-size: clamp(1.6rem, 1.15rem + 1.8vw, 2.5rem);
  /* No serif/editorial heading stack exists elsewhere in the codebase
     (site.css defines only `sans-serif` on body). Scoped system-serif
     stack here per the spec's request for editorial treatment — not
     introducing this globally, and not loading a web font. */
  font-family: Georgia, 'Iowan Old Style', 'Palatino Linotype', Palatino, serif;
  font-weight: 600;
}

.watch-meta {
  margin: 0 0 24px;
  color: var(--text-secondary);
  font-size: 0.9rem;
}

.watch-meta-sep {
  margin: 0 8px;
}

.watch-player-wrap {
  margin-bottom: 28px;
}

.watch-description {
  position: relative;
}

.watch-description-clamp {
  position: relative;
  max-height: 4.8em; /* ~3 lines at line-height 1.6 */
  overflow: hidden;
  color: var(--text-secondary);
  line-height: 1.6;
  transition: max-height 0.25s ease;
}

.watch-description-clamp p {
  margin: 0 0 1em;
}

.watch-description-clamp p:last-child {
  margin-bottom: 0;
}

.watch-description-clamp:not(.is-expanded)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2.4em;
  background: linear-gradient(to bottom, transparent, var(--bg-primary));
  pointer-events: none;
}

.watch-description-clamp.is-expanded {
  max-height: 2000px;
}

.watch-description-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding: 4px 0;
  border: none;
  background: none;
  color: var(--accent-primary);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}

.watch-description-toggle:hover {
  text-decoration: underline;
}

.watch-description-toggle:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}

.watch-description-toggle-icon {
  font-size: 0.8rem;
}

/* ---------------------------------------------------------------------
   Watch page — "Up Next" panel (issue #412)
   --------------------------------------------------------------------- */

/* Two-column layout: player+description on the left, an always-visible
   "Up Next" panel on the right, YouTube-style. `.watch-container` itself
   is untouched (see this file's own header comment and #412's own
   requirement — share.leaf depends on its current standalone 888px-cap
   shape) — it's simply nested as this grid's left-hand item here. Both
   overrides below are scoped to that nesting only, so share.leaf's own
   `.watch-container` usage (centered in a full-width page, no grid) is
   completely unaffected. */
.watch-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  align-items: start;
  gap: 32px;
}

.watch-layout > .watch-container {
  min-width: 0;
  /* `.watch-container`'s base rule sets `margin: 0 auto` to center itself
     in its standalone (share.leaf) usage. Inside this grid, a `width: auto`
     item with inline-axis auto margins does NOT stretch to fill its track
     (CSS Grid's default `justify-self: stretch`) — the auto margins
     absorb the leftover space instead and the item shrinks to its content
     width, confirmed via getBoundingClientRect()/getComputedStyle(): a
     772px track rendering a 246.5px-wide box with ~263px margins on each
     side. Overriding to `margin: 0` here lets the item stretch to fill
     the track normally, as grid's stretch default intends. */
  margin: 0;
  /* Same reasoning for the base rule's own `max-width: 888px` (issue #444
     final visual pass): left in place, it would still cap this column at
     888px inside the now-uncapped `.library-container--watch` grid below,
     leaving a dead gap between the video and the 300px aside instead of
     the column actually growing with the window. `.watch-layout` only
     ever renders here (partials/watch-shell) — share.leaf's own standalone
     `.watch-container` usage never nests inside it, so this is safe. */
  max-width: none;
}

/* Two real columns only have room to breathe above this breakpoint;
   below it, the panel stacks under the player instead of squeezing both
   into an illegibly narrow player. A proper fix for the available width
   itself is #435's job (full-width/theater mode), sequenced after this
   work, not this task's. */
@media (max-width: 1024px) {
  .watch-layout {
    grid-template-columns: 1fr;
  }
}

/* "No card" wide layout for the merged watch page (video route + lesson
   route, both partials/watch-shell — issue #444 final visual pass).
   Modeled on a YouTube watch-page reference: the player/metadata/aside
   should sit directly on the page's own background with no visible card
   edge and no centered-box side margins on a normal desktop window.
   Scoped via `.library-container--watch` (LibraryContext.isWatchLayout,
   set by VideosController.watch and LibraryController.showLesson,
   applied in library/layout.leaf) — every other page sharing that shell
   (library index/course/locked, the videos grid) never sets the flag and
   keeps library.css's default card look untouched.

   Positioned in this file specifically so it loads after library.css's
   own `@media (prefers-color-scheme: dark) { .library-container {...} }`
   block (base.leaf links video-surfaces.css after library.css) — an
   unconditional override here wins the equal-specificity tie in both
   light and dark mode without needing a second dark-mode-scoped copy of
   this rule. */
.library-container--watch {
  background-color: transparent;
  box-shadow: none;
  border-radius: 0;
  /* Dropped entirely rather than raised to a larger fixed number —
     `.main-content`'s own 48px horizontal padding (library.css) becomes
     the only gutter, the same "small edge margins, otherwise fills the
     window" feel the YouTube reference has. */
  max-width: none;
}

/* `.main-content`'s own flat background (library.css) would otherwise
   still read as a visible rectangle against `.library-page`'s gradient
   background once the card above it goes transparent — same override
   position/reasoning as `.library-container--watch` itself. */
.library-container--watch .main-content {
  background: transparent;
}

/* Theater mode (issue #435) — the video spans full width at the top;
   below it, metadata (left) and Up Next (right, at its original ~300px
   width) sit side by side — Up Next is a rail for "everything under the
   video," not a full-width block stacked beneath it. `display: contents`
   on .watch-container (below) lets its children (.watch-player-wrap,
   .watch-metadata) become direct grid participants without touching the
   default (non-theater) layout at all.

   Row-gap is 0, not the shared 32px column-gap: .watch-player-wrap
   already carries its own margin-bottom: 28px (below), and a nonzero
   row-gap here would double that spacing between the video row and the
   meta/upnext row.

   max-width/margin/padding here — not on .video-frame — cap the whole
   grid (video row AND meta/upnext row) at the same width, centered: the
   video's own height cap (aspect-ratio 16/9 at up to 70vh,
   video-player.css) is almost always the narrower constraint than the
   full window, so capping only the video left the row below it
   stretched to the full window width instead, with dead space next to
   the metadata text before Up Next began. Both rows sharing one capped
   container is what keeps them the same width. */
.library-page.theater-mode .watch-layout {
  grid-template-columns: minmax(0, 1fr) 300px;
  grid-template-areas:
    "video video"
    "meta  upnext";
  gap: 0 32px;
  max-width: calc(70vh * 16 / 9);
  margin: 0 auto;
  padding: 0 20px;
}

.library-page.theater-mode .watch-container {
  display: contents;
}

.library-page.theater-mode .watch-player-wrap {
  grid-area: video;
}

/* .watch-container's own padding (base rule above) disappears for
   layout purposes once it's display: contents — its children render
   with no inherited padding at all. .watch-layout's own padding (above)
   now provides that gutter for both .watch-metadata and the aside
   equally, so only the top value is still needed here — adding a
   horizontal value too would double it on this column only, offsetting
   .watch-metadata from the aside instead of aligning them. */
.library-page.theater-mode .watch-metadata {
  grid-area: meta;
  padding: 24px 0 0;
}

/* Both aside shapes the shell's asideHTML slot renders (watch.leaf's
   .watch-upnext and library/lesson.leaf's .watch-sidebar-tabs, #444)
   must map to the same named grid area here — an item with neither
   class assigned would fall through to implicit auto-placement instead
   of sitting beside .watch-metadata, since grid-template-areas above
   already fills both explicit rows. */
.library-page.theater-mode .watch-upnext,
.library-page.theater-mode .watch-sidebar-tabs {
  grid-area: upnext;
}

.watch-upnext-heading {
  margin: 0 0 16px;
  color: var(--text-primary);
  font-size: 1.1rem;
  font-weight: 600;
}

.watch-upnext-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Same .video-tile card the browse grid uses (see this file's header
   comment on why — not a second, near-duplicate card shape for the same
   data) — a vertical list of them reads fine at the panel's ~300px width
   without further overrides, apart from trimming the title down slightly
   for the narrower column. */
.watch-upnext-tile .video-tile-title {
  font-size: 0.95rem;
}

.watch-upnext-empty {
  margin: 0;
  padding: 20px;
  border: 1px dashed var(--border-color);
  border-radius: 14px;
  color: var(--text-secondary);
  font-size: 0.9rem;
  text-align: center;
}

/* ---------------------------------------------------------------------
   Lesson page — Lessons/Links/Resources sidebar tabs (issue #444)
   --------------------------------------------------------------------- */

/* partials/lesson-sidebar-tabs replaces .watch-upnext in the aside slot
   on library/lesson.leaf only — videos/watch.leaf keeps "Up Next" above
   unchanged. Vanilla-JS tab switching toggles .is-active on both the tab
   buttons and their panels (see that partial's own inline <script>). */
.watch-sidebar-tablist {
  display: flex;
  gap: 4px;
  margin: 0 0 16px;
  border-bottom: 1px solid var(--border-color);
}

.watch-sidebar-tab {
  flex: 1 1 auto;
  padding: 10px 4px;
  margin-bottom: -1px; /* sit on top of .watch-sidebar-tablist's border */
  border: none;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--text-secondary);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}

.watch-sidebar-tab:hover {
  color: var(--text-primary);
}

.watch-sidebar-tab.is-active {
  color: var(--accent-primary);
  border-bottom-color: var(--accent-primary);
}

.watch-sidebar-tabpanel {
  display: none;
}

.watch-sidebar-tabpanel.is-active {
  display: block;
}

.watch-sidebar-tab-empty {
  margin: 0;
  padding: 20px;
  border: 1px dashed var(--border-color);
  border-radius: 14px;
  color: var(--text-secondary);
  font-size: 0.9rem;
  text-align: center;
}

/* Lessons tab list — mirrors .sidebar-nav-item's active-state visual
   language (the orange gradient in library-sidebar.css) rather than
   reusing that class directly: .sidebar-nav-item's base colors (white
   text, hover/active backgrounds tuned for the permanent dark .sidebar
   panel) would be illegible against this aside's light/theme-var
   surface. Only the "current" treatment is mirrored, not the whole
   class, since this list has no non-current hover/lock-icon states to
   match. */
.lesson-tab-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.lesson-tab-item a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  color: var(--text-primary);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 500;
  transition: background-color 0.15s ease;
}

.lesson-tab-item a:hover {
  background: var(--bg-secondary);
}

.lesson-tab-item .lesson-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  background: var(--bg-secondary);
  border-radius: 6px;
  font-size: 0.8rem;
  font-weight: 600;
  flex-shrink: 0;
}

.lesson-tab-item.is-current a {
  background: linear-gradient(135deg, #d97706 0%, #b45309 100%);
  color: #fff;
  box-shadow: 0 4px 12px rgba(217, 119, 6, 0.4);
}

.lesson-tab-item.is-current .lesson-number {
  background: rgba(255, 255, 255, 0.25);
}
