:root {
  --bg: #14151a;
  --surface: #1c1e26;
  --surface-hover: #262933;
  --border: #2c2f3a;
  --text: #f2f2f5;
  --subtext: #9497a6;
  --accent: #3b6ef6;
  --accent-dim: #1f3566;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* #coverflow/#cf-background-stage are deliberately widened beyond the
     viewport while receding/advancing (see cfSetRecedeWidth in app.js), so
     there's a scale() applied to bring them back to the same on-screen
     footprint - without this, that temporary extra width would otherwise
     show up as a horizontal scrollbar. */
  overflow-x: hidden;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 24px;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  background: var(--bg);
  z-index: 10;
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 18px;
}
.brand-logo {
  height: 28px;
  width: auto;
  display: block;
  /* The image's own artwork is centered within its file, but flexbox
     centers it against the text's full line-box (which reserves descender
     space most fonts don't visually use), so it reads as sitting a touch
     high without this nudge. */
  margin-top: 3px;
}
.section-tabs {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Lets the search box drop to its own line on a narrow screen instead of
     being squeezed or overflowing - row-gap plus a real bottom padding
     (not just top) means that second line always ends with genuine
     breathing room before #content starts, however tall this row ends up
     being. That matters because #cf-back-btn floats *up* out of #content
     (see .cf-back-btn) - without a guaranteed gap here, it could end up
     overlapping whatever this row wrapped to, however the search box
     was previously) - which is exactly what the search box collided with
     before this row was resized. */
  flex-wrap: wrap;
  gap: 16px;
  row-gap: 10px;
  padding: 14px 24px;
}
.section-tab-group {
  display: flex;
  gap: 4px;
}
.section-tab {
  background: none;
  border: none;
  color: var(--subtext);
  font-size: 14px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: 16px;
  cursor: pointer;
}
.section-tab:hover { background: var(--surface); color: var(--text); }
.section-tab.active { background: var(--accent-dim); color: var(--text); }

.search-wrap input {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 8px 16px;
  color: var(--text);
  font-size: 14px;
  width: 240px;
  max-width: 100%;
  outline: none;
}
.search-wrap input:focus { border-color: var(--accent); }

.content {
  /* Extra top clearance (vs. the 20px elsewhere) - #cf-back-btn floats up
     out of #content (see .cf-back-btn), and on a narrow screen .section-tabs
     wraps to two lines (tabs + search), which can otherwise leave too little
     real gap between the wrapped search box and that floated button. */
  padding: 40px 24px 48px;
}

.cf-hint {
  text-align: center;
  font-size: 15px;
  font-weight: 500;
  color: #c7c9d4;
  margin-bottom: 4px;
  /* Always exactly one line - the different hint strings between views
     (Artists vs Albums/Stories) must never wrap differently from each
     other, or cfComputeLayout()'s height budget (and so the whole
     coverflow's size) would shift between views. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cf-hint-row {
  /* The back button is positioned absolutely (see .cf-back-btn) so it never
     takes up flow height - .cf-hint-row's own height is always exactly
     .cf-hint's, meaning the hint/coverflow/artwork below never shift when
     the button is shown or hidden. */
  position: relative;
}
.cf-back-btn {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 6px;
  background: none;
  border: 1px solid var(--border);
  color: var(--subtext);
  font-size: 13px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: 16px;
  cursor: pointer;
  white-space: nowrap;
}
.cf-back-btn:hover {
  background: var(--surface);
  color: var(--text);
}

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

.cf-viewport {
  position: relative;
  /* Break out of .content's horizontal padding so the row of covers reaches
     the actual edges of the viewport, rather than stopping short of them. */
  margin: 0 -24px;
  width: calc(100% + 48px);
}

.coverflow {
  position: relative;
  /* height and the custom properties below are set by cfComputeLayout() in
     app.js so exactly CF_VISIBLE_COUNT covers fill the real screen width. */
  height: 400px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  perspective: var(--cf-perspective, 1200px);
  user-select: none;
  touch-action: pan-y;
  overflow: hidden;
  transition: transform 0.45s cubic-bezier(0.22, 0.68, 0, 1), opacity 0.45s ease;
}

.cf-stage {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}

.cf-stage.dragging .cf-item { transition: none; }

/* Drilling from Artists into an artist's Albums (and back): rather than an
   instant cut, the outgoing view recedes/flies off and the incoming one
   animates in from the same spot - see cfDrillIn()/cfDrillOut() in app.js.
   Applied to #coverflow itself (which is what declares `perspective` for
   its 3D contents), not to .cf-stage inside it - by the time .coverflow's
   own transform is resolved, its 3D children are already fully rendered/
   composited into one flat picture (perspective only shapes how a
   *parent's* 3D children are drawn, not how the element with `transform`
   is itself placed in its own parent's - ordinary 2D - space). So this
   really is just resizing/fading an already-finished picture, like scaling
   a photo: every cover keeps its exact angle and position relative to the
   others, with zero reprocessing, reflattening, or repositioning. */
.coverflow.cf-receded {
  transform: scale(0.55);
  opacity: 0.4;
}
.coverflow.cf-flying-off {
  transform: scale(1.8);
  opacity: 0;
}

.cf-background-stage {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  /* Static (no transition) - this is a frozen backdrop that just sits at
     the same "receded" look the whole time it's shown, matching
     .coverflow.cf-receded above exactly. */
  transform: scale(0.55);
  opacity: 0.4;
}

.cf-item {
  /* No margin-left/top here - the JS transform's translate(-50%, -50%)
     already centers on left/top; adding a margin offset too would double it
     (that was a real bug once already - see updateCfItemTransform). */
  position: absolute;
  left: 50%;
  top: 34%;
  width: var(--cf-item-size, 168px);
  height: var(--cf-item-size, 168px);
  cursor: pointer;
  transition: transform 0.4s cubic-bezier(0.22, 0.68, 0, 1.01);
  /* Hints the browser to keep these on their own GPU-composited layer ahead
     of time, rather than promoting them on demand - without this, a whole
     batch of covers freshly mounted at once (e.g. switching views) can
     render one flat/unrotated frame before the 3D transform is composited,
     which looks like a brief "rectangles, then they angle" pop. */
  will-change: transform;
}

.cf-face {
  width: 100%;
  height: 100%;
  border-radius: 4px;
  overflow: hidden;
  background: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(var(--cf-item-size, 168px) * 0.262);
  font-weight: 700;
  color: #14151a;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55);
}
.cf-face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
  -webkit-user-drag: none;
}
.cf-item.focused .cf-face {
  box-shadow: 0 22px 50px rgba(59, 110, 246, 0.35), 0 14px 34px rgba(0, 0, 0, 0.65);
}

.cf-reflection {
  position: absolute;
  left: 0;
  top: 100%;
  width: 100%;
  height: 100%;
  border-radius: 4px;
  overflow: hidden;
  transform: scaleY(-1);
  pointer-events: none;
}
.cf-reflection img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Dimmed via brightness, not alpha - stays fully opaque so it occludes
     whatever sits behind it instead of letting it show through. */
  filter: brightness(0.55);
}

.cf-reflection-fade {
  position: absolute;
  left: 0;
  top: 100%;
  width: 100%;
  /* Taller than it needs to be, on purpose: a 3D-transformed, GPU-composited
     element shows a faint seam at its own bottom edge even when the fill
     matches the page background exactly. Making the box much taller pushes
     that edge safely past the coverflow container's own overflow:hidden clip
     (a clean cut, no seam) - the actual visible fade distance is controlled
     separately below via a fixed-relative-to-cover-size gradient stop, so
     this doesn't change how the fade looks, just where the box truly ends. */
  height: 400%;
  border-radius: 4px;
  pointer-events: none;
  /* Not flipped like .cf-reflection - fades the (opaque) reflection into the
     solid page background top-to-bottom, rather than into transparency. */
  background: linear-gradient(to bottom, transparent 0, var(--bg) calc(var(--cf-item-size, 168px) * 0.68));
}

.cf-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 50;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: rgba(28, 30, 38, 0.85);
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cf-arrow:hover { background: var(--surface-hover); }
.cf-arrow:disabled { opacity: 0.25; cursor: default; pointer-events: none; }
.cf-arrow-prev { left: 12px; }
.cf-arrow-next { right: 12px; }

.cf-caption {
  position: relative;
  text-align: center;
  /* margin-top is set in JS (cfComputeLayout) - it pulls the caption up to
     sit just below the focused cover's shadow instead of the coverflow
     container's own much-taller bottom edge. */
  padding: 0 56px;
}
.cf-title {
  font-size: 16px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cf-subtitle {
  font-size: 13px;
  line-height: 16px;
  min-height: 16px;
  color: var(--subtext);
  margin-top: 4px;
}

.preview-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
}
.preview-modal-inner {
  width: 100%;
  max-width: 480px;
  height: 100%;
  max-height: 780px;
  background: var(--bg);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
}
.preview-modal-header {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  padding: 8px;
}
.preview-modal-close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.preview-modal-close:hover { background: var(--surface-hover); }
.preview-modal-frame {
  flex: 1;
  width: 100%;
  border: none;
}

.hidden { display: none !important; }

.empty-state {
  color: var(--subtext);
  font-size: 14px;
  padding: 40px 0;
  text-align: center;
}

.cta-button {
  display: inline-block;
  flex-shrink: 0;
  background: var(--accent);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  padding: 10px 20px;
  border-radius: 20px;
  box-shadow: 0 6px 18px rgba(59, 110, 246, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.cta-button:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(59, 110, 246, 0.45);
}
.cta-button:active {
  transform: translateY(0);
}
