:root {
  --bg: #0c0d10;
  --surface: #14171c;
  --surface-2: #1c2027;
  --border: #23262d;
  --text: #f2ede4;
  --text-muted: #a9adb6;
  --accent: #E8B34A;
  --accent-hover: #f0c264;
  --success: #4caf7d;
  --danger: #e0607a;
  --radius: 8px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Space Grotesk', -apple-system, "Segoe UI", Roboto, sans-serif;
  line-height: 1.5;
}

h1,
h2,
h3 {
  font-weight: 700;
  letter-spacing: 0.01em;
}

/* Section/component titles (card headers, dialog headers, movie titles) are
   all h3 - gold by default here rather than per-component, so nothing new
   nested inside a card/dialog can silently pick up (or fail to pick up) the
   accent color depending on how deep it's nested. h1/h2 (page titles) stay
   neutral text color. */
h3 {
  color: var(--accent);
}

.eyebrow {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 0.35rem;
}

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

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

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 0.75rem 1.5rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  font-size: .9rem;
  font-weight: 600;
  text-transform: uppercase;
}

.nav-container {
  display: flex;
  justify-content: space-between;
}

.nav-brand {
  margin-right: auto;
  display: flex;
  align-items: center;
  gap: 2.5rem;
}

.nav-brand a {
  font-family: 'Archivo', sans-serif;
  font-size: 1.4rem;
  font-weight: 800;
  color: #fff;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

.nav-user,
.nav-manage {
  position: relative;
}

.nav-user summary,
.nav-manage summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  cursor: pointer;
}

.nav-user summary::-webkit-details-marker,
.nav-manage summary::-webkit-details-marker {
  display: none;
}

.nav-manage summary {
  color: var(--accent);
}

.nav-manage summary:hover,
.nav-manage[open] summary {
  color: var(--accent-hover);
}

.nav-manage summary::after {
  content: "▾";
  font-size: 0.7rem;
}

.nav-user-dropdown,
.nav-manage-dropdown {
  position: absolute;
  right: 0;
  top: 100%;
  margin-top: 0.5rem;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
  min-width: 140px;
  padding: 0.6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  z-index: 20;
}

.nav-manage-dropdown {
  right: auto;
  left: 0;
  white-space: nowrap;
}

.nav-user img {
  width: 28px;
  height: 28px;
  border-radius: 50%;
}

.container {
  width: 50%;
  margin: 0 auto;
  padding: 1.5rem;
}

.site-footer {
  position: fixed;
  right: 0.75rem;
  bottom: 0.5rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--text-muted);
  font-size: 0.75rem;
}

.site-footer a {
  color: var(--text-muted);
  text-decoration: underline;
}

.provider-attribution {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.provider-attribution-logo {
  height: 0.85rem;
  width: auto;
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  margin-bottom: 1rem;
}

.card h2 {
  margin: 0;
  color: var(--accent);
}

.card h3 {
  margin: 0;
}

/* A <details class="card"> uses its own <summary> as the collapse toggle -
   same disclosure-arrow language as .entry-summary, but laid out as a
   title/action row (server name + "View server watch list") instead of a
   poster-thumb row. */
summary.card-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  cursor: pointer;
  list-style: none;
}

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

summary.card-summary::marker {
  content: "";
}

summary.card-summary::before {
  content: "\25b8";
  color: var(--text-muted);
  flex-shrink: 0;
  margin-right: 0.5rem;
  transition: transform 0.15s ease;
}

details[open]>summary.card-summary::before {
  transform: rotate(90deg);
}

summary.card-summary h3 {
  flex: 1;
}

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.25rem;
}

.section-header h4 {
  margin: 0;
}

.lazy-panel {
  margin-top: 1.25rem;
}

.lazy-panel-summary {
  display: flex;
  align-items: center;
  cursor: pointer;
  list-style: none;
}

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

.lazy-panel-summary::marker {
  content: "";
}

.lazy-panel-summary::before {
  content: "\25b8";
  color: var(--text-muted);
  flex-shrink: 0;
  margin-right: 0.5rem;
  transition: transform 0.15s ease;
}

details[open]>.lazy-panel-summary::before {
  transform: rotate(90deg);
}

/* Reschedule/Extend in-place edit: the display and the form are same-slot
   siblings inside a [data-inline-edit] cell, toggled via [hidden] from
   app.js - only one is ever visible, so the row never grows/deforms.
   The [hidden] override is required, not decorative: an author-stylesheet
   `display: flex` would otherwise beat the UA default `[hidden] { display:
   none }` regardless of the attribute's presence, since author rules always
   outrank UA rules - re-declaring [hidden] here at higher specificity
   restores correct hiding. */
[data-inline-edit] .js-inline-form {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}

[data-inline-edit] .js-inline-form[hidden] {
  display: none;
}

.lazy-panel-summary h4 {
  margin: 0;
}

.lazy-panel>div {
  margin-top: 0.75rem;
}

.title-edit-form {
  display: flex;
  align-items: flex-end;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.title-edit-form label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  flex: 1;
  max-width: 24rem;
}

.table-scroll {
  overflow-x: auto;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
}

.admin-table th {
  text-align: left;
  padding: 0.5rem 0.75rem;
  background: var(--surface-2);
  color: var(--text-muted);
  font-weight: 400;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.admin-table th a.sortable {
  color: inherit;
  white-space: nowrap;
}

.admin-table th a.sortable:hover {
  color: var(--text);
}

.admin-table td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.admin-table-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  justify-content: flex-end;
}

/* A button that reads as a link (e.g. the manage table's movie name opening
   its management modal) - resets the button chrome the way
   button.entry-row-trigger does. */
.link-button,
.link-button:hover {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--accent);
  cursor: pointer;
  text-align: left;
}

.link-button:hover {
  text-decoration: underline;
}

.manage-attendees {
  list-style: none;
  padding: 0;
  margin: 0 0 0.75rem;
}

.manage-attendees li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.25rem 0;
  border-bottom: 1px solid var(--border);
}

.manage-attendees li:last-child {
  border-bottom: none;
}

.manage-event-form {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin: 0.5rem 0;
}

.event-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-top: 0.75rem;
}

#welcome h2 {
  color: var(--accent);
}

.quote {
  font-style: italic;
  color: var(--text-muted);
  margin: 0.5rem 0 0;
}

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

.carousel-wrap {
  margin-bottom: 1rem;
}

.carousel {
  position: relative;
  overflow: hidden;
  /* A single explicit height tied to viewport width, clamped - .carousel-item
     and .carousel-track both size off a percentage of THIS height, so it
     needs to be a definite value, not derived from aspect-ratio-of-width
     (which resolves to auto in a flex/percentage chain like this and lets
     the track render outside the intended box) or a fixed px value (doesn't
     scale with the page). */
  height: clamp(18rem, 24vw, 37.5rem);
  display: flex;
  align-items: center;
}

.carousel-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border-radius: 50%;
  background: rgba(12, 13, 16, 0.7);
  color: var(--text);
  border: 1px solid var(--border);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.carousel-nav:hover {
  background: rgba(12, 13, 16, 0.9);
}

.carousel-nav-prev {
  left: 1rem;
}

.carousel-nav-next {
  right: 1rem;
}

.carousel-track {
  display: flex;
  align-items: center;
  height: 100%;
  transition: transform 0.6s ease;
}

.carousel-item {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  height: 55%;
  aspect-ratio: 2 / 3;
  /* Negative - inactive items are scale(0.7), which shrinks the visible
     poster but not its flex box footprint, so a positive/zero margin here
     still leaves a large visible gap between posters. Pulling the boxes
     together with a negative margin makes the shrunk posters overlap. */
  margin: 0 -2.5rem;
  /* Dim inactive posters by darkening rather than fading (opacity) - they
     now overlap their neighbors (see the negative margin above), and a
     transparent poster would let the one behind it show through. */
  transform: scale(calc(100 / 75));
  filter: brightness(0.45) blur(1px);
  transition: transform 0.4s ease, filter 0.4s ease;
}

.carousel-item.is-active {
  z-index: 2;
  /* .carousel-item's own height is 55% of the track's (i.e. the carousel's)
     height - scale by 100/55 so the active poster's rendered height reaches
     exactly the full height of that containing div, not an arbitrary
     emphasis factor. */
  transform: scale(calc(100 / 55));
  filter: brightness(1) blur(0);
}

/* .poster-trigger (the base rule below) never sets its own height - other
   poster grids don't need it to, since their img sizes itself via
   aspect-ratio off its own width. The carousel's img instead needs
   height:100% (to exactly fill .carousel-item's already-definite box via
   object-fit:cover), which only resolves if .poster-trigger itself has a
   definite height to be a percentage of - hence this. */
.carousel-item .poster-trigger {
  height: 100%;
}

/* Scoped to .poster-trigger (not just .carousel-item) - the movie-detail
   modal's <dialog> is also a DOM descendant of .carousel-item (it's a
   sibling of .poster-trigger, not rendered elsewhere), so a bare
   ".carousel-item img" selector was matching the modal's poster/backdrop
   images too and overriding their own width/height rules. */
.carousel-item .poster-trigger img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--surface-2);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
}

.carousel-item.is-active .poster-trigger img {
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.6);
}

.movie-card {
  text-align: center;
}

.movie-card img {
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--surface-2);
}

.movie-card .title {
  font-size: 0.9rem;
  margin-top: 0.4rem;
}

.watchlist-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1rem;
}

.watchlist-list {
  display: none;
}

.watchlist-results-view.is-list-view .watchlist-grid {
  display: none;
}

.watchlist-results-view.is-list-view .watchlist-list {
  display: block;
}

.list-toolbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}

.toolbar-actions {
  display: flex;
  /* .filter-tabs a has padding-bottom (room for the active-tab underline)
     with no matching top padding, so its box is taller below the text than
     above it - align-items: center would center the trigger against that
     lopsided box and visibly push it down past the tab labels. Top-aligning
     both instead lines the trigger up with where the tab text actually
     starts. */
  align-items: flex-start;
  gap: 0.75rem;
}

button.add-movie-trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
  border-radius: var(--radius);
}

.view-toggle {
  display: flex;
  gap: 0.25rem;
}

.view-toggle-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  font-size: 1rem;
  line-height: 1;
  border-radius: var(--radius);
  background: none;
  color: var(--text-muted);
}

.view-toggle-btn.active {
  background: var(--surface-2);
  color: var(--text);
}

.filter-tabs {
  display: flex;
  gap: 1.25rem;
}

.filter-tabs a {
  color: var(--text-muted);
  padding-bottom: 0.5rem;
  border-bottom: 2px solid transparent;
}

.filter-tabs a.active {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.sort-select {
  margin-bottom: 0.5rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.sort-select select {
  padding: 0.35rem 0.5rem;
  font-size: 0.85rem;
}

.more-filters {
  position: relative;
}

.more-filters summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  font-size: 1rem;
  line-height: 1;
  border-radius: var(--radius);
  background: var(--accent);
  color: #10111a;
}

.more-filters summary:hover {
  background: var(--accent-hover);
}

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

.more-filters-panel {
  position: absolute;
  z-index: 10;
  top: calc(100% + 0.25rem);
  right: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 14rem;
  max-height: 20rem;
  overflow-y: auto;
  padding: 0.6rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.more-filters-panel input[type=search] {
  width: 100%;
}

.genre-filter-options {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-height: 10rem;
  overflow-y: auto;
}

.genre-filter-options label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  white-space: nowrap;
}

/* Manage/Profile provider picker - a few hundred entries once channel
   add-ons are unpacked, so it lays out in columns instead of one long
   vertical list. .provider-option (rather than a plain `label` type
   selector) and the [type=checkbox] attribute both exist to out-specificity
   .field label / .profile-fields input further down this file - otherwise
   whichever rule happens to be declared later wins and either blanks out
   the option text or stretches the checkbox to the row's full width. */
.provider-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: 0.3rem 1rem;
  max-height: 16rem;
  overflow-y: auto;
}

.provider-grid .provider-option {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text);
}

.provider-grid .provider-option input[type="checkbox"] {
  width: auto;
  flex-shrink: 0;
}

.provider-channel-group {
  margin-top: 0.5rem;
}

.provider-channel-group summary {
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.provider-channel-group .provider-grid {
  margin-top: 0.4rem;
  max-height: 12rem;
}

.poster-cell {
  position: relative;
}

.poster-trigger {
  display: block;
  width: 100%;
  padding: 0;
  background: none;
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  position: relative;
}

.poster-trigger img {
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--surface-2);
  display: block;
}

.status-flag {
  position: absolute;
  z-index: 2;
  top: 0.4rem;
  right: 0.4rem;
  background: rgba(0, 0, 0, 0.6);
  color: var(--accent);
  border-radius: 999px;
  width: 1.6rem;
  height: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.9rem;
}

.rank-badge {
  position: absolute;
  z-index: 2;
  top: 0.4rem;
  left: 0.4rem;
  background: rgba(0, 0, 0, 0.6);
  color: var(--accent);
  border-radius: 999px;
  padding: 0.15rem 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
}

.poster-title {
  position: absolute;
  z-index: 2;
  top: 0;
  left: 0;
  right: 0;
  padding: 0.5rem 0.5rem 1.25rem;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.85), transparent);
  color: var(--text);
  font-size: 0.8rem;
  text-align: center;
  line-height: 1.3;
  border-radius: var(--radius) var(--radius) 0 0;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.poster-cell:hover .poster-title,
.poster-cell.no-poster .poster-title {
  opacity: 1;
}

/* The placeholder "poster" is a flat gray block, not a photo - the base
   gradient (opaque only near the top, fading out) isn't guaranteed to cover
   a long, wrapped title. Use a solid background instead so contrast holds
   regardless of title length. A long title's natural (unclamped) height can
   still reach down into where .poster-actions centers itself though, so this
   is capped to 3 lines with an ellipsis - a bounded height .poster-actions
   (anchored to the bottom below, for no-poster cards) can reliably clear. */
.poster-cell.no-poster .poster-title {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  padding: 0.5rem;
  background: rgba(0, 0, 0, 0.85);
  border-radius: var(--radius) var(--radius) 0 0;
}

.poster-actions {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.75rem;
  background: rgba(12, 13, 16, 0.8);
  border-radius: var(--radius);
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

.poster-cell:hover .poster-actions {
  opacity: 1;
}

.poster-cell.no-poster .poster-actions {
  justify-content: flex-end;
}

.poster-actions form {
  display: contents;
}

.poster-actions button {
  width: 100%;
  padding: 0.35rem 0.4rem;
  font-size: 0.8rem;
  pointer-events: auto;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  margin-top: 1.25rem;
}

dialog.movie-modal {
  position: fixed;
  inset: 0;
  margin: auto;
  max-width: min(40rem, calc(100vw - 2rem));
  width: 100%;
  max-height: calc(100vh - 4rem);
  height: fit-content;
  overflow-y: auto;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
}

dialog.movie-modal::backdrop {
  background: rgba(0, 0, 0, 0.6);
}

/* Movie-detail dialogs wrap their content in .movie-modal-scroll instead of
   scrolling the dialog itself, so .modal-close (a direct child of the
   dialog, outside that wrapper) stays fixed in the corner - pinned in view
   - while only the content beneath it scrolls. Padding/scrolling move from
   the dialog onto the wrapper; :has() targets only dialogs that use it, so
   the simpler content-only dialogs (signup-rejected, set-favorite-movie)
   keep scrolling/padding the dialog itself, unaffected. */
dialog.movie-modal:has(.movie-modal-scroll) {
  padding: 0;
  overflow: hidden;
}

.movie-modal-scroll {
  max-height: calc(100vh - 4rem);
  overflow-y: auto;
  padding: 1.5rem;
}

dialog.movie-modal textarea {
  width: 100%;
  resize: vertical;
}

/* position: absolute (not float+sticky) so it overlays the backdrop image
   instead of pushing it down - float/sticky both keep the button in normal
   flow, which forces following siblings (the backdrop) to either squeeze
   beside it or clear below it; only taking it fully out of flow lets the
   backdrop render underneath it. */
.modal-close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  color: var(--text);
  border: none;
  border-radius: 999px;
  width: 2rem;
  height: 2rem;
  line-height: 1;
  cursor: pointer;
}

.detail-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}

.date-watched-form {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.discover-reason {
  font-size: 0.8rem;
  margin: 0.4rem 0 0;
  text-align: center;
}

.entry-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}

.entry-row .entry-row-trigger {
  flex: 1;
  min-width: 0;
}

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

.rsvp-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

.rsvp-btn {
  width: 4.5rem;
  text-align: center;
}

.rsvp-count {
  font-size: 0.75rem;
  white-space: nowrap;
}

/* Backdrop-banner watch-event cards on the dashboard - same negative-margin +
   gradient-fade "image bleeds into the panel" layering as
   .movie-detail-backdrop-wrap (below), but the image itself is positioned
   differently: a fixed aspect-ratio box with an absolutely-positioned,
   full-width/auto-height <img> instead of object-fit: cover. A real backdrop
   (already roughly this shape) needs no visible cropping under this setup;
   a portrait poster (the fallback when a movie has no real backdrop) shows
   its full width unstretched, with the excess height simply clipped by
   overflow:hidden and softened by the same gradient - a fade-out rather than
   a zoomed-in crop. */
.watch-events-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.watch-event-card {
  position: relative;
}

.watch-event-trigger,
.watch-event-trigger:hover {
  display: block;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.watch-event-backdrop-wrap {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  margin-bottom: -3.5rem;
  border-radius: var(--radius);
  background: var(--surface-2);
}

.watch-event-backdrop {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  display: block;
}

.watch-event-backdrop-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 45%, var(--surface) 100%);
}

.watch-event-info {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0 1rem;
}

.watch-event-info .entry-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--accent);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.watch-event-tagline {
  font-family: 'Newsreader', serif;
  font-size: 0.9rem;
  font-style: italic;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.watch-event-meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.watch-event-info .muted {
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.watch-event-info .status-badge {
  font-size: 0.75rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-muted);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
  text-align: center;
  white-space: nowrap;
}

/* Event rows on the events page (the list-view watchlist rows dropped their
   badge, so this only styles event schedule pills now). */
.entry-row .status-badge {
  font-size: 0.75rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-muted);
  text-align: center;
  white-space: nowrap;
  flex-shrink: 0;
}

.watch-event-card .rsvp-block {
  position: absolute;
  z-index: 2;
  right: 1rem;
  top: 0.75rem;
}

.watch-event-card .rsvp-count {
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.entry-row .poster-thumb {
  width: 40px;
  height: 60px;
  object-fit: cover;
  border-radius: 4px;
  background: var(--surface-2);
}

/* Fixed-width Added/Watched columns - flex can't align variable-width items
   across separate rows, so each gets a set width; long "by <name>" text
   truncates with an ellipsis rather than shifting its neighbor. A watched
   date doubles as the watched-status indicator (the old status badge was
   dropped as redundant: interactive rows show status via their toggle
   buttons, read-only rows via this column). */
.entry-row .entry-col {
  font-size: 0.75rem;
  flex-shrink: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}

.entry-row .entry-col-rank {
  width: 3rem;
}

.entry-row .entry-col-added {
  width: 13rem;
}

.entry-row .entry-col-watched {
  width: 8.5rem;
}

.entry-toggle {
  width: 100%;
}

.entry-summary {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  cursor: pointer;
  list-style: none;
}

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

.entry-summary::marker {
  content: "";
}

.entry-summary::before {
  content: "\25b8";
  color: var(--text-muted);
  flex-shrink: 0;
  transition: transform 0.15s ease;
}

details[open]>.entry-summary::before {
  transform: rotate(90deg);
}

.entry-summary .entry-title {
  flex: 1;
}

/* Modifier for a .entry-summary that's a real <button> opening a modal
   (not a <details> disclosure) - resets the generic button/button:hover
   chrome (which would otherwise paint near-black text invisible against the
   card background, only "fixed" by hover's accent-colored flash) and drops
   the disclosure caret, which would be misleading here. */
button.entry-row-trigger,
button.entry-row-trigger:hover {
  background: none;
  color: inherit;
  border: none;
  padding: 0;
  text-align: left;
  font: inherit;
}

.entry-row-trigger::before {
  content: none;
}

.entry-expanded {
  width: 100%;
  padding: 0.75rem 0 0.25rem;
}

.entry-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
  padding-left: calc(40px + 0.75rem);
}

.movie-detail-panel {
  width: 100%;
}

/* Negative margin pulls .movie-detail-header up to overlap the bottom of the
   backdrop (the ::after gradient fades the image into the panel's own
   background so the poster/title read as sitting "on" the backdrop, the
   same layering Letterboxd's movie pages use). */
.movie-detail-backdrop-wrap {
  position: relative;
  margin-bottom: -3.5rem;
  border-radius: var(--radius);
  overflow: hidden;
}

.movie-detail-backdrop {
  display: block;
  width: 100%;
  object-fit: cover;
  background: var(--surface-2);
}

.movie-detail-backdrop-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 45%, var(--surface) 100%);
}

.movie-detail-header {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

.movie-detail-header.has-backdrop {
  position: relative;
  z-index: 1;
}

.movie-detail-poster {
  width: 33%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--surface-2);
  flex-shrink: 0;
}

.movie-detail-header.has-backdrop .movie-detail-poster {
  border: 3px solid var(--surface);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}

.movie-detail-heading h3 {
  margin: 0 0 0.35rem;
}

.movie-detail-tagline {
  font-family: 'Newsreader', serif;
  font-size: 1.05rem;
  margin: 0.4rem 0 0;
}

.movie-detail-body {
  width: 100%;
  min-width: 0;
  line-height: 1.7;
  margin-top: 1rem;
}

.movie-detail-body p {
  margin: 0 0 0.85rem;
}

.movie-detail-body figure {
  margin: 0;
}

.movie-detail-body ul {
  margin: 0.25rem 0;
  padding-left: 1.25rem;
}

button,
.btn {
  display: inline-block;
  background: var(--accent);
  color: #10111a;
  border: none;
  border-radius: var(--radius);
  padding: 0.45rem 0.9rem;
  font-size: 0.9rem;
  cursor: pointer;
}

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

button.secondary,
.btn.secondary {
  background: var(--surface-2);
  color: var(--text);
}

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

input[type=text],
input[type=number],
input[type=datetime-local],
select,
textarea {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius);
  padding: 0.4rem 0.6rem;
  font-size: 0.9rem;
}

.field {
  margin-bottom: 0.75rem;
}

.field label {
  display: block;
  margin-bottom: 0.25rem;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.profile-layout {
  display: flex;
  gap: 1.25rem;
  align-items: flex-start;
  flex-wrap: wrap;
}

.profile-fields {
  flex: 0 1 320px;
  min-width: 0;
}

.profile-fields input,
.profile-fields textarea {
  width: 100%;
  resize: vertical;
}

.favorite-movie-field {
  width: 160px;
  flex-shrink: 0;
}

.favorite-movie-title {
  margin: 0 0 0.4rem;
  font-size: 0.9rem;
  font-weight: 600;
}

.save-button {
  display: block;
  margin: 1.25rem auto 0;
}

.messages {
  list-style: none;
  padding: 0;
  margin: 0;
  position: fixed;
  top: 1rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  pointer-events: none;
}

.messages li {
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius);
  background: var(--surface-2);
  border-top: 3px solid var(--accent);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transform: translateY(-0.5rem);
  transition: opacity 0.3s ease, transform 0.3s ease;
  pointer-events: auto;
}

.messages li.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.messages li.error {
  border-top-color: var(--danger);
}

.messages li.success {
  border-top-color: var(--success);
}

.muted {
  color: var(--text-muted);
}

.poll-option {
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}

.poll-option .entry-summary {
  width: 100%;
}

.poll-bar {
  height: 6px;
  background: var(--accent);
  border-radius: 3px;
}

.poll-bar-track {
  background: var(--surface-2);
  border-radius: 3px;
  flex: 1;
  min-width: 60px;
}