/* Rank-Box -- dark, clean, mature movie-poster-grid look.
   Pure black and white/gray only, no color accent, no rounded cartoony
   shapes, no emoji. */

:root {
  --bg: #000000;
  --card: #141414;
  --border: #2a2a2a;
  --text: #f2f2f2;
  --text-dim: #8a8a8a;
  --accent: #f2f2f2; /* same white as --text -- no separate accent color */
  --topbar-height: calc(60px + env(safe-area-inset-top));
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

.hidden {
  display: none;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  /* The page itself never scrolls -- each screen owns a single inner
     .screen-scroll pane instead (see below). That's what actually fixes the
     top bar disappearing: it used to be position:fixed over a scrolling
     PAGE, and Safari's rubber-band bounce past the top/bottom of that page
     could shove a fixed element out of view along with it (confirmed with
     screenshots 2026-09-19 -- happened right at the bottom of a long list).
     Two earlier attempts (sticky -> fixed, then forcing a hardware layer)
     didn't fix that because they never addressed the actual page-level
     bounce; a third attempt (overscroll-behavior-y: none) stopped the bounce
     but killed the native bounce FEEL everywhere, which isn't what was
     wanted either. Splitting the bar out of the scrolling area entirely
     fixes the bug without giving up the bounce -- the inner pane below still
     bounces normally, it just can't drag the bar (which was never part of
     it) along for the ride. */
  overflow: hidden;
}

.screen {
  display: none;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
}

.screen.active {
  display: flex;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--topbar-height);
  flex-shrink: 0; /* never squeezed by the scroll pane below it */
  padding: 0 16px;
  padding-top: env(safe-area-inset-top);
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}

/* The ONE thing that actually scrolls on each screen -- everything below
   the top bar lives in here. Bounces normally (own native momentum +
   rubber-band), but overscroll-behavior: contain stops that bounce from
   ever reaching past this pane (nothing to chain to now anyway, since the
   page itself has overflow: hidden, but this also keeps behavior identical
   if that ever changes). */
.screen-scroll {
  flex: 1;
  min-height: 0; /* required for a flex child to be allowed to actually scroll instead of stretching the flex parent to fit it */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding-bottom: env(safe-area-inset-bottom);
}

.brand {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--text);
}

.icon-btn {
  min-width: 60px;
  min-height: 60px;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 22px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
}

.icon-btn-spacer {
  min-width: 60px;
  min-height: 60px;
}

.list-toolbar {
  display: flex;
  justify-content: flex-end;
  padding: 0 10px;
  border-bottom: 1px solid var(--border);
}

.text-btn {
  min-width: 60px;
  min-height: 56px;
  background: transparent;
  border: none;
  color: var(--accent);
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  touch-action: manipulation;
}

/* ---------- Home ---------- */

.board-list {
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.board-tile {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 22px 18px;
  min-height: 72px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  color: var(--text);
  touch-action: manipulation;
  width: 100%;
}

/* A collection row is two independent buttons (open + delete) instead of
   one -- .board-tile alone can't hold both (a <button> can't nest another
   <button>), so this wraps them and lets the delete button sit on top of
   the tile's own right edge without the two overlapping. */
.collection-row {
  position: relative;
}

.collection-row .board-tile {
  padding-right: 60px; /* room for the delete button so the name/count text never runs under it */
}

.collection-delete-btn {
  position: absolute;
  top: 0;
  right: 0;
  width: 60px;
  height: 100%;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 22px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
}

.board-tile-title {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.board-tile-count {
  font-size: 13px;
  color: var(--text-dim);
}

/* ---------- Poster grid ---------- */

.poster-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 26px;
  padding: 22px;
  flex: 1;
  align-items: start; /* don't stretch tiles to fill leftover row height */
}

/* The whole grid tile: image+badge on top, title below. Plain block flow --
   .poster-title just stacks below .poster-frame, nothing overlaps. */
.poster-item {
  position: relative;
}

/* Wraps ONLY the image + rank badge, sized tightly to the image (its one
   in-flow child) -- so .poster-rank's bottom:0 always lines up with the
   poster's real bottom edge, regardless of .poster-title's height below. */
.poster-frame {
  position: relative;
}

/* Shape-hugging: no forced aspect-ratio, so a square album cover, a wide
   game screenshot, and a tall movie poster each keep their own real shape
   instead of being cropped to fit a uniform box. Rows end up uneven as a
   result -- accepted tradeoff for not cropping anything. */
.poster-cell {
  position: relative;
  background: var(--card);
  overflow: hidden;
  touch-action: manipulation;
  border: none;
  padding: 0;
  display: block;
  border-radius: 8px;
  /* Before a lazy-loaded image finishes downloading, the browser doesn't
     know its real height yet, and "height: auto" collapses it to 0 -- an
     invisible, untappable sliver right as results first appear. This floor
     keeps every cell tappable the instant it exists; it still grows once
     the real image loads in, this just stops it from ever being nothing. */
  min-height: 140px;
}

/* In Edit mode, a tile's own touch drives the drag-to-reorder gesture
   instead of scrolling the page. */
.poster-grid.editing .poster-item {
  touch-action: none;
}

/* Makes Edit mode obviously active at a glance -- a thin outline plus a
   slight dim on every poster, no animation (keeps the mature tone rather
   than an iOS-style jiggle). */
.poster-grid.editing .poster-cell {
  border: 2px solid var(--text);
}

.poster-grid.editing .poster-cell::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  pointer-events: none;
}

.poster-item.dragging {
  z-index: 20;
}

.poster-item.dragging .poster-cell {
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
}

.poster-cell img {
  width: 100%;
  height: auto;
  display: block;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  user-drag: none;
}

/* Plain text below the poster, in normal document flow -- doesn't overlay
   the artwork at all. Extra top margin clears the rank badge's bottom half,
   which hangs below the poster in the same space. */
.poster-title {
  display: block;
  margin-top: 20px;
  padding: 0 4px;
  color: var(--text);
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Straddles the poster's bottom edge -- translateY(50%) shifts it down by
   half its own height, so its vertical center sits exactly on the edge:
   top half over the poster, bottom half hanging into the gap below.
   The button itself is just an (invisible, until Edit mode) tap target;
   .poster-rank-circle is the visible badge inside it. Doubles as the
   delete button while editing instead of a separate corner button. */
.poster-rank {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 50%);
  background: transparent;
  border: none;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
}

/* Only expand to a real 60px touch target in Edit mode -- otherwise this
   is just a label, not an interactive control, and a bigger invisible box
   would make taps on the poster right below the badge feel unresponsive. */
.poster-grid.editing .poster-rank {
  min-width: 60px;
  min-height: 60px;
}

.poster-rank-circle {
  min-width: 34px;
  height: 34px;
  padding: 0 8px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.85);
  border: 1.5px solid var(--accent);
  color: var(--text);
  font-size: 15px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.empty-state {
  display: none;
  color: var(--text-dim);
  text-align: center;
  padding: 40px 24px;
  font-size: 15px;
}

.poster-grid:empty + .empty-state {
  display: block;
}

/* ---------- Search / Add ---------- */

.search-bar {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}

.search-bar input {
  width: 100%;
  min-height: 48px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0 14px;
  font-size: 16px;
  color: var(--text);
}

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

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

/* ---------- Compare (auto-rank) ---------- */

/* Back keeps its spot in the top bar even while hidden (visibility, not
   display:none) -- otherwise removing it from the layout entirely shifts
   WHICH ONE? over to fill the gap, and it visually looks like Back "replaces"
   the title once it shows up later instead of just appearing next to it. */
#btnBackAutoRank.hidden {
  display: inline-block;
  visibility: hidden;
  pointer-events: none;
}

.compare-hint {
  text-align: center;
  color: var(--text-dim);
  font-size: 14px;
  padding: 16px 16px 4px;
  margin: 0;
}

.compare-progress {
  text-align: center;
  color: var(--text-dim);
  font-size: 12px;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin: 0;
}

.compare-row {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 24px;
}

.compare-card {
  width: 40%;
  max-width: 260px;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: none; /* no gray outline -- same as a poster on the ranked list */
  border-radius: 8px;
  overflow: hidden;
  color: var(--text);
  padding: 0;
  touch-action: manipulation;
  outline: none; /* no focus ring either -- tapping should be a true instant tap, no visual change at all */
}

.compare-card img {
  width: 100%;
  height: auto;
  display: block;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  user-drag: none;
}

.compare-title {
  padding: 12px 10px;
  font-size: 14px;
  font-weight: 600;
  text-align: center;
  /* Pure black behind the title, not the card's own dark-gray background --
     the poster above still sits on --card, this strip just doesn't. */
  background: var(--bg);
}

/* ---------- Detail screen (Movies for now) ---------- */
/* Ported from command-center's movie detail screen -- same stat tiles and
   tap-to-play trailer box, restyled in rank-box's own monochrome palette
   instead of Command Center's neon glow (this app deliberately has no
   separate accent color -- see --accent above). */

/* Stacked (poster column above trailer box) by default -- there's not
   enough width on a phone-size screen to put a poster and a 16:9 video side
   by side and still have either be worth looking at. On an iPad-width
   screen there's room, so they sit side by side instead -- same 620px
   breakpoint the stat-grid already uses to go from 2 to 4 columns. */
.detail-media-row {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 16px 20px 6px;
}

.detail-media-row .video-frame {
  margin: 0;
}

/* The poster and its "Change Trailer" button live together in one column --
   the button stays right under the poster it belongs to. The OPTIONS list
   that opens below it is NOT in here though (see #detailTrailerOptions in
   index.html) -- it needs the full screen width, not this narrow column. */
.detail-poster-column {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.detail-poster {
  display: flex;
  justify-content: center;
}

.detail-poster img {
  max-width: 220px;
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--border);
}

@media (min-width: 620px) {
  .detail-media-row {
    flex-direction: row;
    /* Stretch, not flex-start -- makes .detail-poster-column exactly as
       tall as .video-frame, so Change Trailer sits level with the video
       instead of stuck right under a much-shorter poster. */
    align-items: stretch;
  }
  .detail-poster-column {
    flex: 0 0 220px;
  }
  .detail-media-row .video-frame {
    flex: 1;
    min-width: 0;
    /* Opts back out of the row's stretch above -- keeps its own
       aspect-ratio-derived height instead of being stretched tall. */
    align-self: flex-start;
  }
}

.detail-director {
  text-align: center;
  padding: 14px 20px 0;
  color: var(--text-dim);
  font-size: 14px;
}

.detail-overview {
  padding: 10px 20px 0;
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
}

/* The iframe itself has pointer-events:none (taps pass through, can't be
   paused/rewound); a solid black button sits on top and is the ONLY
   tappable thing until the video actually starts playing -- that tap is
   what lets it autoplay WITH sound, which a plain autoplay src can't do. */
.video-frame {
  display: none;
  margin: 16px 20px;
  position: relative;
  border-radius: 8px;
  overflow: hidden;
  background: #000;
  border: 1px solid var(--border);
  aspect-ratio: 16 / 9;
}

.video-frame.loaded {
  display: block;
}

/* Shows "No trailer found for this one." (or a load error) INSIDE the
   black video-frame box itself, instead of hiding the box and leaving the
   message floating below it -- keeps the poster+trailer row looking like
   two boxes side by side even when there's no trailer to show. */
.video-frame-message {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: none;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 20px;
  color: var(--text-dim);
  font-size: 14px;
  background: #000;
  border-radius: inherit;
}

.video-frame-message.active {
  display: flex;
}

.video-frame-message a {
  color: var(--text);
}

.video-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  pointer-events: none;
}

/* Explicit z-index (not just "comes later in the page") -- without it,
   Safari can let a freshly-loading video iframe paint above this cover for
   a frame while it's warming up, flashing a peek of the video before this
   catches up. A real stacking order stops that from ever being possible. */
.video-play-overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: none;
  border-radius: inherit;
  background: #000;
  z-index: 2;
}

.video-play-overlay.hidden {
  display: none;
}

/* Stat tiles -- 2 columns on a phone-width screen, 4 across on wider
   screens like an iPad, so the info reads as a real dashboard instead of
   one cramped line of text. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  padding: 4px 20px 40px;
}

@media (min-width: 620px) {
  .stat-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

.stat-tile {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px 10px;
  text-align: center;
}

.stat-value {
  font-size: 20px;
  font-weight: 800;
  color: var(--text);
}

.stat-label {
  font-size: 12px;
  color: var(--text-dim);
  margin-top: 4px;
}

/* "Change Trailer" options list -- each real trailer/teaser TMDB has for
   this movie, tap one to switch to it. Movie/show trailer names are always
   short ("Official Trailer", "Teaser 2"), but Legos' options come straight
   from real YouTube search results (fan titles, often long, sometimes with
   emoji) -- without min-width: 0 a flex item won't shrink below its own
   text's natural width, so a long name pushed the button's edge outward
   instead of wrapping, spilling text off the visible button. */
.trailer-option {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: calc(100% - 40px);
  margin: 8px 20px 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 16px;
  color: var(--text);
  font-size: 14px;
  text-align: left;
  touch-action: manipulation;
}

.trailer-option-name {
  flex: 1;
  min-width: 0;
  overflow-wrap: break-word;
}

.trailer-option-type {
  color: var(--text-dim);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-left: 12px;
  margin-top: 1px;
  flex-shrink: 0;
}

/* ---------- Collections list / picker ---------- */

/* These empty-states aren't tied to the poster-grid:empty sibling trick
   above (.board-list and this second poster-grid aren't the ONE
   poster-grid that trick was written for) -- shown/hidden directly via
   script.js instead. The ID rules below just cancel out .empty-state's own
   default display:none for these specific elements so a plain
   classList.toggle('hidden', ...) is enough to control them. bootMessage
   (script.js's server-boot loading/error text on the Home screen) is the
   same case again. */
#collectionsEmptyState, #collectionPickerEmptyState, #bootMessage {
  display: block;
}
#collectionsEmptyState.hidden, #collectionPickerEmptyState.hidden, #bootMessage.hidden {
  display: none;
}

/* Picking existing items into a collection -- the EXACT same outline+dim
   Edit mode already uses on every poster (see .poster-grid.editing
   .poster-cell / ::after above), plus a corner checkmark badge on top so
   membership still reads clearly even though the dim alone looks the same
   as Edit mode's own poster styling. */
.poster-cell.selected {
  border: 2px solid var(--text);
}

.poster-cell.selected::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  pointer-events: none;
}

.picker-check {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.85);
  border: 1.5px solid var(--accent);
  /* Without this, the dim overlay above (a ::after, which paints after
     real children regardless of source order) would sit on top of this
     badge and wash it out. */
  z-index: 1;
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  font-size: 14px;
  font-weight: 700;
}

.poster-cell.selected .picker-check {
  display: flex;
}

/* New Collection's name field is a plain full screen now (was a small
   popup) -- reuses .search-bar's exact input styling below via the same
   markup shape as the Add screen, so no dedicated CSS of its own is
   needed here at all. */

/* ---------- Sync error banner ---------- */
/* A save to the server failed. Pinned to the very bottom of the viewport,
   above every screen (it's a body-level sibling of .screen in index.html,
   not nested inside one), so it stays visible no matter which screen was
   open when the save failed -- safe-area-inset-bottom keeps it clear of an
   iPad's home indicator. No accent color for "this is bad" -- this app has
   no color accent at all (see --accent), so the weight comes from the
   solid --text background + inverted (black-on-white) text instead, the
   strongest contrast this app's palette has. */
.sync-error-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  padding: 14px 16px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
  background: var(--text);
  color: var(--bg);
  font-size: 14px;
  font-weight: 700;
  text-align: center;
}

.sync-error-banner.hidden {
  display: none;
}

/* ---------- App footer ---------- */
/* Centered, underneath every screen's own content -- same spot and look as
   Command Center's .app-footer, not tucked in a corner. */
.app-footer {
  text-align: center;
  padding: 14px 20px 32px;
  font-size: 12px;
  color: var(--text-dim);
}

/* ---------- Front door + sign in ---------- */
/* Covers the whole app until auth.js lets someone through. Fixed and
   scrollable on its own (the page itself never scrolls in this app), so the
   form can still be reached when the iPad keyboard takes up half the screen.
   Same black-and-white look as the rest of Rank-Box. Every button and field
   is 60px tall -- big enough for a fingertip. */
#auth-gate {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--bg);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  transition: opacity 0.3s ease;
}

#auth-gate.auth-gate-hide {
  opacity: 0;
  pointer-events: none;
}

#auth-content {
  max-width: 420px;
  margin: 0 auto;
  padding: calc(60px + env(safe-area-inset-top)) 20px 60px;
  text-align: center;
}

.auth-brand {
  font-size: 22px;
  letter-spacing: 5px;
}

.auth-step {
  display: none;
}

.auth-step.active {
  display: block;
}

.auth-sub {
  color: var(--text-dim);
  font-size: 15px;
  margin: 14px 0 28px;
}

.auth-card {
  text-align: left;
}

.auth-card label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 18px 0 8px;
}

.auth-card input {
  width: 100%;
  min-height: 60px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0 16px;
  font-size: 16px; /* 16px or bigger stops iPad Safari zooming in on tap */
  color: var(--text);
}

.auth-card input::placeholder {
  color: var(--text-dim);
}

.auth-card input:focus {
  outline: none;
  border-color: var(--accent);
}

.auth-error {
  color: #ff6b6b;
  font-size: 14px;
  min-height: 20px;
  margin-top: 10px;
  margin-bottom: 16px; /* room before the button below, so the message doesn't touch it */
}

.auth-btn {
  width: 100%;
  min-height: 60px;
  background: transparent;
  border: 1px solid var(--text);
  border-radius: 4px;
  color: var(--text);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  touch-action: manipulation;
}

.auth-btn-solid {
  background: var(--text);
  color: var(--bg);
}

.auth-two-buttons {
  display: flex;
  gap: 12px;
  margin-top: 8px;
}

.auth-two-buttons .auth-btn {
  flex: 1;
  width: auto;
}

/* "Signed in as ..." line on the Home screen, just above the footer. */
.whoami {
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
  margin: 24px 0 0;
}

/* Sign out sits in the top bar -- the shared .text-btn is 56px tall, the
   house rule for touch targets is 60. */
#btnSignOut {
  min-height: 60px;
  padding: 0 4px;
}
