/* Reset, sidebar, top bar, content frame, and the home page components:
   greeting, folder status, segmented pill, quick links, hero, shelves, cards.

   Split out of app.css; load order is the cascade, so this file expects to
   come after the ones before it in index.html. */

html {
  overflow: hidden;
}

body {
  overflow: hidden;
}

button,
a {
  font: inherit;
}

button {
  border: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

#app {
  min-block-size: 100dvb;
  background: var(--page);
}

.sidebar {
  position: fixed;
  inset-block: 0 var(--player-h);
  inset-inline-start: 0;
  display: flex;
  flex-direction: column;
  inline-size: var(--sidebar-w);
  block-size: auto;
  overflow: hidden;
  background: rgba(37, 37, 38, 0.4);
  border-inline-end: 1px solid rgba(255, 255, 255, 0.06);
}

.sidebar__brand-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 16px 22px;
}

.sidebar__wordmark {
  display: flex;
  align-items: center;
  gap: 14px;
  min-inline-size: 0;
}

.sidebar__mark {
  flex: 0 0 auto;
  inline-size: 28px;
  block-size: 28px;
  fill: var(--text);
}

.sidebar__brand {
  color: var(--text);
  font-size: 18.4px;
  font-weight: 600;
  line-height: 27.6px;
  letter-spacing: -0.03em;
}

.icon-button {
  display: grid;
  place-items: center;
  inline-size: 28px;
  block-size: 28px;
  padding: 0;
  border-radius: var(--radius-art-sm);
  color: var(--text-muted);
  background: transparent;
  cursor: pointer;
  position: relative;
}

/* Desktop hit-areas reach 44px without growing the visuals. The 28px
   collapse control gains 8px per side; the bar and avatar below gain
   what each needs. Scoped to the rail layout: below 768px the controls
   themselves are already 44px (responsive.css). */
@media (min-width: 768px) {
  .icon-button::after {
    content: "";
    position: absolute;
    inset: -8px;
  }
}

.icon-button:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
}

.icon-button svg,
.sidebar__nav-item svg {
  inline-size: 20px;
  block-size: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: 1.6px;
  margin-inline: 12px;
}

.sidebar__nav-item {
  display: flex;
  align-items: center;
  gap: 14px;
  block-size: 43px;
  padding-inline: 14px;
  border-radius: var(--radius-row);
  color: var(--text-muted);
  font-size: 15px;
  font-weight: 500;
  line-height: 22.5px;
}

.sidebar__nav-item:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
}

.sidebar__nav-item--selected {
  background: var(--surface-3);
  color: var(--text);
}

.sidebar__nav-item--selected svg {
  color: var(--accent);
}

/* The selected row's own background sits later in this file than the hover
   rule and at the same weight, so it won every time and hovering the page you
   are already on did nothing at all. */
.sidebar__nav-item--selected:hover {
  background: color-mix(in srgb, var(--surface-3) 82%, #fff);
}

.sidebar__rule {
  block-size: 1px;
  margin: 12px 20px;
  background: rgba(255, 255, 255, 0.08);
}

.sidebar__pinned {
  display: flex;
  flex: 1 1 auto;
  min-block-size: 0;
  flex-direction: column;
}

.sidebar__section-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: 16px 14px;
  margin-block-end: 8px;
}

.sidebar__section-row h2 {
  margin: 0;
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  letter-spacing: 0.8px;
  text-transform: uppercase;
}

.sidebar__pinned-list {
  display: flex;
  flex: 1 1 auto;
  min-block-size: 0;
  flex-direction: column;
  gap: 4px;
  padding-inline: 12px;
  overflow: auto;
}

.pinned-item {
  display: flex;
  align-items: center;
  gap: 12px;
  min-block-size: 56px;
  padding-inline: 8px 12px;
  border-radius: var(--radius-tile);
}

.pinned-item:hover {
  background: rgba(255, 255, 255, 0.06);
}

.pinned-item__art {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  inline-size: 40px;
  block-size: 40px;
  overflow: hidden;
  border-radius: var(--radius-art-sm);
  background:
    radial-gradient(circle at 32% 28%, var(--text) 0 12%, transparent 13%),
    linear-gradient(135deg, var(--accent) 0%, var(--accent-soft) 34%, var(--bg-glow-2) 100%);
}

.pinned-item__art svg {
  inline-size: 20px;
  block-size: 20px;
  fill: var(--text);
  stroke: none;
}

.pinned-item__copy {
  display: flex;
  min-inline-size: 0;
  flex-direction: column;
  gap: 3px;
}

.pinned-item__title,
.pinned-item__meta {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pinned-item__title {
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
}

.pinned-item__meta {
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 400;
  line-height: 16px;
}

.sidebar__footer {
  flex: 0 0 auto;
  margin-block: 0 16px;
  padding-block-start: 16px;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 400;
  line-height: 16px;
  text-align: center;
}

/* ── Main area (right of sidebar) ─────────────────────────── */

.main {
  position: fixed;
  inset-block: 0 var(--player-h);
  inset-inline-start: var(--sidebar-w);
  inset-inline-end: 0;
  display: flex;
  flex-direction: column;
}

/* ── Top bar ──────────────────────────────────────────────── */

.top-bar {
  flex: 0 0 var(--top-bar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: 24px;
  background: transparent;
}

.top-bar__left,
.top-bar__right {
  display: flex;
  align-items: center;
  gap: 6px;
}

.top-bar__btn {
  display: grid;
  place-items: center;
  inline-size: 32px;
  block-size: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  color: var(--text-muted);
  background: transparent;
  cursor: pointer;
  position: relative;
  transition: color 0.15s var(--ease-out), background 0.15s var(--ease-out);
}

.top-bar__btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.06);
}

.top-bar__btn svg {
  inline-size: 20px;
  block-size: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.top-bar__right .top-bar__btn {
  inline-size: 40px;
  block-size: 40px;
}

.top-bar__avatar {
  display: grid;
  place-items: center;
  inline-size: 40px;
  block-size: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: linear-gradient(135deg, #f472b6, #ec4899, #db2777);
  color: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  position: relative;
  transition: opacity 0.15s var(--ease-out);
}

@media (min-width: 768px) {
  .top-bar__btn::after {
    content: "";
    position: absolute;
    inset: -6px;
  }
  .top-bar__avatar::after {
    content: "";
    position: absolute;
    inset: -2px;
    border-radius: 50%;
  }
}

.top-bar__avatar:hover {
  opacity: 0.85;
}

.top-bar__avatar svg {
  inline-size: 20px;
  block-size: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── Content frame ────────────────────────────────────────── */

.content-frame {
  flex: 1 1 0;
  min-block-size: 0;
  overflow-y: auto;
  scrollbar-width: none;
  /* Their .scroll-area: two accent washes bled in from the top corners at
     7% and 4%, held in place while the content scrolls over them. This is
     most of why the reference's black never reads as flat. */
  background-image:
    radial-gradient(900px 520px at 12% -6%,
      color-mix(in oklab, var(--accent) 7%, transparent), transparent 60%),
    radial-gradient(720px 540px at 105% 2%,
      color-mix(in oklab, var(--accent) 4%, transparent), transparent 55%);
  background-repeat: no-repeat;
  background-attachment: local;
}

.content-frame::-webkit-scrollbar {
  display: none;
}

.content-frame__inner {
  padding-inline: 32px;
}

/* ── Greeting row ─────────────────────────────────────────── */

.greeting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: 24px;
  margin-block-end: 8px;
}

.greeting-row__text {
  margin: 0;
  color: var(--text);
  font-size: 36px;
  font-weight: 700;
  line-height: 40px;
  letter-spacing: -0.9px;
}

/* ── Folder status (greeting row) ─────────────────────────── */

.greeting-row__status {
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  white-space: nowrap;
}

.greeting-row__status:empty {
  display: none;
}

.reconnect-btn {
  padding: 6px 14px;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-muted);
  font-size: 13px;
  line-height: 20px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s var(--ease-out), color 0.15s var(--ease-out);
}

.reconnect-btn:hover {
  background: rgba(255, 255, 255, 0.14);
  color: var(--text);
}

/* ── Segmented pill ───────────────────────────────────────── */

.seg-pill {
  display: flex;
  align-items: center;
  block-size: 34px;
  padding: 2px;
  border-radius: var(--radius-pill);
}

/* This sort control sat on the button as six inline styles, which meant no
   stylesheet could touch it — including the phone rules, which is how it
   stayed 32px tall on touch. */
.seg-pill__seg--sort {
  margin-inline-start: 12px;
  block-size: 32px;
  padding: 0 16px;
  border-radius: 99px;
  font-size: 13px;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  position: relative;
}

/* The label stays exactly "Sort: <name>" for the count-line honesty checks;
   the arrow is paint only, so textContent never sees it. */
.seg-pill__seg--sort::after {
  content: "▾";
  margin-inline-start: 6px;
  font-size: 10px;
  line-height: 1;
  color: var(--text-muted);
}

/* Library sort menu: the same popover shape as the overflow menus, anchored
   to the sort wrap. Rows are menuitemradios; the checked one carries weight
   plus a check that is paint only, so textContent stays the sort name. */
.lib-sort-wrap {
  position: relative;
}

/* Specificity note: .seg-pill__seg below sets block-size:100%, which would
   otherwise win over the sort's 32px on equal specificity by source order. */
#lib-sort-btn {
  block-size: 32px;
}

#lib-sort-menu {
  min-inline-size: 188px;
}

#lib-sort-menu .np-menu__item::before {
  content: "✓";
  flex: 0 0 16px;
  opacity: 0;
}

#lib-sort-menu .np-menu__item[aria-checked="true"] {
  color: var(--text);
  font-weight: 700;
}

#lib-sort-menu .np-menu__item[aria-checked="true"]::before {
  opacity: 1;
}

/* Desktop hit-areas reach 44px without growing the visuals, the same
   ::after/::before expansion P2 used for the top bar. The pill segments
   keep their 34px look (inset -7px covers the ~30px row), the 32px sort
   control its own (inset -6px); its arrow already owns ::after, so the hit
   area takes ::before instead. Scoped to desktop: below 768px the controls
   themselves are already 44px (responsive.css). */
@media (min-width: 768px) {
  #lib-filter-pill .seg-pill__seg::after {
    content: "";
    position: absolute;
    inset: -7px -4px;
  }
  #lib-sort-btn::before {
    content: "";
    position: absolute;
    inset: -6px -4px;
  }
}

.seg-pill__seg {
  block-size: 100%;
  padding-inline: 14px;
  border: 0;
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  color: var(--text-muted);
  background: transparent;
  cursor: pointer;
  position: relative;
  transition: color 0.15s var(--ease-out), background 0.15s var(--ease-out);
}

.seg-pill__seg:hover {
  color: var(--text);
}

.seg-pill__seg--active {
  background: var(--accent);
  color: #fff;
}

/* This rule used to set the colour the active segment already had, which
   reads as a hover state and behaves as nothing. */
.seg-pill__seg--active:hover {
  background: color-mix(in srgb, var(--accent) 86%, #fff);
}

/* ── Quick-links grid ─────────────────────────────────────── */

.quick-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  margin-block-end: 32px;
}

.quick-card {
  display: flex;
  align-items: center;
  block-size: 56px;
  border-radius: var(--radius-tile);
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
  transition: background 0.15s var(--ease-out);
}

.quick-card:hover {
  background: rgba(255, 255, 255, 0.10);
}

.quick-card__cover {
  background-size: cover;
  background-position: center;
  flex: 0 0 56px;
  inline-size: 56px;
  block-size: 56px;
}

/* Monogram fallback: artless CARD slots show the album's initial over the
   hue gradient, so a library with no art reads as records rather than a
   wall of blank blocks. Ambient slots (now-playing, headers, hero) keep
   pure gradients. JS clears the letter wherever real artwork resolves. */
.shelf-card__cover,
.quick-card__cover {
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.92);
  font-weight: 700;
  user-select: none;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
}

.shelf-card__cover {
  font-size: 64px;
  line-height: 1;
}

.quick-card__cover {
  font-size: 22px;
  line-height: 1;
}

.quick-card__label {
  padding-inline: 12px;
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Hero card ────────────────────────────────────────────── */

.hero-card {
  display: flex;
  align-items: center;
  gap: 24px;
  min-block-size: 220px;
  padding: 24px;
  border-radius: var(--radius-art);
  background: linear-gradient(135deg, #1a1028 0%, #0d0a14 40%, #120e1c 100%);
  margin-block-end: 40px;
}

.hero-card__cover {
  flex: 0 0 172px;
  inline-size: 172px;
  block-size: 172px;
  border-radius: var(--radius-art);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}

.hero-card__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-inline-size: 0;
}

.hero-card__eyebrow {
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  letter-spacing: 1.2px;
}

.hero-card__title {
  margin: 0;
  color: var(--text);
  /* The reference's lead card title measures 36px/700 with -0.9px tracking; ours
     was 48px, which read as a different level of heading entirely. */
  font-size: 36px;
  font-weight: 700;
  line-height: 40px;
  letter-spacing: -0.9px;
}

.hero-card__artist {
  color: var(--text-muted);
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  margin-block-end: 8px;
}

.hero-card__actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-block-start: 4px;
}

.hero-card__play {
  display: flex;
  align-items: center;
  gap: 8px;
  block-size: 48px;
  padding-inline: 22px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: #fff;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 0.15s var(--ease-out);
}

.hero-card__play:hover {
  opacity: 0.9;
}

.hero-card__play svg {
  inline-size: 18px;
  block-size: 18px;
}

.hero-card__share {
  display: grid;
  place-items: center;
  inline-size: 48px;
  block-size: 48px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.15s var(--ease-out), color 0.15s var(--ease-out);
}

.hero-card__share:hover {
  background: rgba(255, 255, 255, 0.14);
  color: var(--text);
}

/* Press acknowledgment, matching the icon-btn flash family. */
.hero-card__share.is-flashed {
  color: var(--accent);
}

.hero-card__share svg {
  inline-size: 20px;
  block-size: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── Shelf component ──────────────────────────────────────── */

.shelf {
  margin-block-end: 40px;
}

.shelf__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-block-end: 16px;
}

.shelf__head-left {
  min-inline-size: 0;
}

.shelf__title-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.shelf__icon {
  flex: 0 0 auto;
  inline-size: 20px;
  block-size: 20px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.shelf__title {
  margin: 0;
  color: var(--text);
  font-size: 24px;
  font-weight: 700;
  line-height: 32px;
  letter-spacing: -0.6px;
}

.shelf__chevron {
  flex: 0 0 auto;
  inline-size: 20px;
  block-size: 20px;
  fill: none;
  stroke: var(--text-muted);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.shelf__subtitle {
  margin: 4px 0 0;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
}

.shelf__see-all {
  flex: 0 0 auto;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  white-space: nowrap;
  padding-block-start: 6px;
  transition: color 0.15s var(--ease-out);
}

.shelf__see-all:hover {
  color: var(--text);
}

/* ── Card row (horizontal scroll) ─────────────────────────── */

.shelf__row {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  scroll-behavior: smooth;
  padding-block-end: 4px;
}

.shelf__row::-webkit-scrollbar {
  display: none;
}

/* ── Individual card ──────────────────────────────────────── */

.shelf-card {
  flex: 0 0 176px;
  display: flex;
  flex-direction: column;
  transition: transform 0.15s var(--ease-out);
}

.shelf-card:hover {
  transform: translateY(-2px);
}

.shelf-card__cover {
  inline-size: 176px;
  /* Square by ratio rather than by a fixed height: in the library grid the
     column is 1fr, so a fixed height leaves the box wider than it is tall. */
  aspect-ratio: 1 / 1;
  block-size: auto;
  border-radius: var(--radius-art);
  margin-block-end: 12px;
  /* Set here rather than alongside each background-image assignment, so
     artwork that is not square is cropped to fill instead of letterboxed
     against black. Real libraries are full of odd aspect ratios. */
  background-size: cover;
  background-position: center;
}

/* In the library the columns are 1fr, so the card fills its track rather than
   sitting at the shelf's fixed 176px. */
.lib-grid--albums .shelf-card,
.lib-grid--albums .shelf-card__cover {
  inline-size: 100%;
}

.shelf-card__title {
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shelf-card__artist {
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 400;
  line-height: 20px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Home loading skeleton ──────────────────────────────────
   Shown in the shelves, quick grid and hero while a scan/index run puts
   words on the page. Opacity pulse only — no layout motion — on the motion
   tokens, and static under reduced motion. Deliberately bar-shaped: this
   must read as "not yet" rather than as artwork (rule 5). */

@keyframes skel-pulse {
  from { opacity: 0.45; }
  to   { opacity: 1; }
}

.skel-line {
  display: block;
  block-size: 12px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.10);
  animation: skel-pulse 1.1s var(--ease-in-out) infinite alternate;
}

.skel-line--short {
  inline-size: 55%;
}

.hero-card__title .skel-line {
  block-size: 30px;
  inline-size: min(320px, 60%);
  border-radius: 8px;
}

.hero-card__artist .skel-line {
  block-size: 16px;
  inline-size: 180px;
  margin-block-end: 8px;
}

.hero-card.is-loading .hero-card__cover {
  animation: skel-pulse 1.1s var(--ease-in-out) infinite alternate;
}

.shelf-card.is-skeleton .shelf-card__cover,
.quick-card.is-skeleton .quick-card__cover {
  background: rgba(255, 255, 255, 0.08);
  animation: skel-pulse 1.1s var(--ease-in-out) infinite alternate;
}

.shelf-card.is-skeleton .shelf-card__title,
.shelf-card.is-skeleton .shelf-card__artist {
  overflow: visible;
}

.shelf-card.is-skeleton .skel-line {
  margin-block-start: 12px;
}

.quick-card.is-skeleton .skel-line {
  flex: 1;
  margin-inline-end: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .skel-line,
  .hero-card.is-loading .hero-card__cover,
  .shelf-card.is-skeleton .shelf-card__cover,
  .quick-card.is-skeleton .quick-card__cover {
    animation: none;
    opacity: 0.7;
  }
}

/* ── Index notice ───────────────────────────────────────────
   The scan finished but some files could not be read. The library is
   smaller than the folder, and without this nothing says so. Dismisses
   for the session; the next scan with failures raises it again. */

.index-notice {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-block-end: 24px;
  padding: 12px 12px 12px 20px;
  border-radius: var(--radius-tile);
  font-size: 14px;
  line-height: 20px;
  color: var(--text);
}

#index-notice[hidden] {
  display: none;
}

#index-notice-text {
  flex: 1;
  min-inline-size: 0;
}

.index-notice__dismiss {
  flex: 0 0 auto;
  min-block-size: 44px;
  padding-inline: 18px;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.10);
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  line-height: 20px;
  cursor: pointer;
  transition: background 0.15s var(--ease-out);
}

.index-notice__dismiss:hover {
  background: rgba(255, 255, 255, 0.16);
}


/* ── Idle: nothing playing ────────────────────────────────── */

/* The reference shows no player bar until a track starts. Zeroing the height
   here also releases the space the sidebar and content frame reserve for it,
   since both size themselves against this property. */
#app.is-idle {
  --player-h: 0px;
}

#app.is-idle .player {
  display: none;
}

/* Artist page sections. The reference puts its first section title at x=304, y=571
   with 24px/700, -0.6px tracking and a 32px line box. */
.artist-content {
  padding-inline: 16px !important;
  padding-block: 36px 32px !important;
}

.artist-section__title {
  letter-spacing: -0.6px;
  line-height: 32px;
}


/* ─ Toast ─────────────────────────────────────────────────── */

.toast {
  position: fixed;
  inset-block-end: calc(var(--player-h) + 20px);
  /* Centre on the content area, not the viewport; the sidebar shifts the
     optical centre by half its width. */
  inset-inline-start: calc(50% + var(--sidebar-w) / 2);
  translate: -50% 8px;
  max-inline-size: min(560px, calc(100vw - 64px));
  padding: 13px 20px;
  border-radius: var(--radius-pill);
  font-size: 14px;
  line-height: 20px;
  color: var(--text);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s var(--ease-out), translate 0.2s var(--ease-out);
  /* Above the now-playing sheet, which sits at 1000. At 300 every toast the
     sheet itself raises — Share, Like, the overflow menu — was drawn behind
     it, so the confirmation for the thing you just did was the one you could
     not see. */
  z-index: 1100;
}

.toast.is-open {
  opacity: 1;
  translate: -50% 0;
}

/* The glass-strong fill is translucent, so over light artwork the text had
   nothing guaranteed behind it. An opaque base coat sits under the blur —
   invisible on dark, readable everywhere else. Doubled specificity so it
   holds whatever order the stylesheets load in. */
.toast.glass-strong {
  background-color: #101219;
}


/* ─ Entrance ──────────────────────────────────────────────────
   Their own keyframe and curve, read from the stylesheet they serve:
   rise 0.4s cubic-bezier(.16, 1, .3, 1). Content lifts 14px into place
   rather than appearing. */

@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.shelf,
.hero-card,
.quick-grid,
.album-header,
.artist-header,
.liked-header {
  animation: rise 0.4s var(--ease-out) both;
}

@media (prefers-reduced-motion: reduce) {
  .shelf,
  .hero-card,
  .quick-grid,
  .album-header,
  .artist-header,
  .liked-header {
    animation: none;
  }
}

/* ─ View entrance ─────────────────────────────────────────────
   The seven views toggle display with no transition, so switching views
   snapped. An animation (not a transition) restarts every time display
   flips from none, giving a short opacity + lift crossfade. Timing only:
   no colour, size, spacing, or layout change. */
@keyframes view-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

[id^="view-"] {
  animation: view-in 170ms var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
  [id^="view-"] {
    animation: none;
  }
}


/* ─ Marquee ───────────────────────────────────────────────────
   Their keyframe: still for the first 12% and the last 12%, travelling
   between, offset by the container's own width. Only applied when the
   text genuinely overflows. */

@keyframes marquee {
  0%, 12%   { transform: translateX(0); }
  88%, 100% { transform: translateX(calc(-100% + var(--marquee-w, 0px))); }
}

.marquee__inner {
  display: inline-block;
  white-space: nowrap;
}

.is-scrolling .marquee__inner {
  animation: marquee var(--marquee-time, 10s) var(--ease-in-out) infinite;
}

/* An ellipsis and a marquee cannot both be right; scrolling text shows all
   of itself, so the truncation is dropped while it runs. */
.is-scrolling {
  text-overflow: clip;
}

@media (prefers-reduced-motion: reduce) {
  .is-scrolling .marquee__inner { animation: none; }
  .is-scrolling { text-overflow: ellipsis; }
}

/* The hidden attribute is only a default of display:none, so any rule setting
   display outranks it and the element stays on screen while reporting itself
   hidden. Three separate bugs here have been exactly that. This makes the
   attribute mean what it says, everywhere. */
[hidden] {
  display: none !important;
}


/* ─ Empty home ────────────────────────────────────────────────
   What a first run actually shows, in place of a page of albums that
   do not exist. */

.home-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 96px 32px 120px;
  animation: rise 0.4s var(--ease-out) both;
}

.home-empty[hidden] {
  display: none;
}

.home-empty__icon {
  inline-size: 56px;
  block-size: 56px;
  color: var(--text-dim);
  margin-block-end: 20px;
}

.home-empty__title {
  margin: 0 0 10px;
  font-size: 24px;
  line-height: 32px;
  font-weight: 700;
  letter-spacing: -0.6px;
  color: var(--text);
}

.home-empty__body {
  margin: 0 0 28px;
  max-inline-size: 44ch;
  font-size: 14px;
  line-height: 20px;
  color: var(--text-muted);
}


/* ─ Collapsed sidebar ─────────────────────────────────────── */

#app.is-sidebar-collapsed {
  --sidebar-w: 0px;
}

#app.is-sidebar-collapsed .sidebar {
  transform: translateX(-100%);
}

.sidebar {
  transition: transform 0.25s var(--ease-out);
}

/* The expand control only exists while the sidebar is away. */
.top-bar__btn--expand {
  display: none;
}

#app.is-sidebar-collapsed .top-bar__btn--expand {
  display: grid;
}


/* ─ Keyboard shortcut list ────────────────────────────────── */

.shortcuts {
  position: fixed;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  translate: -50% -50%;
  inline-size: min(420px, calc(100vw - 64px));
  max-block-size: calc(100vh - 96px);
  overflow-y: auto;
  padding: 24px 26px;
  border-radius: var(--radius-art-lg);
  z-index: 400;
  animation: rise 0.25s var(--ease-out) both;
}

.shortcuts__title {
  margin: 0 0 16px;
  font-size: 20px;
  line-height: 28px;
  font-weight: 700;
  letter-spacing: -0.5px;
  color: var(--text);
}

.shortcuts__row {
  display: grid;
  grid-template-columns: 128px 1fr;
  gap: 14px;
  align-items: baseline;
  padding-block: 7px;
  border-block-start: 1px solid rgba(255, 255, 255, 0.07);
  font-size: 14px;
  line-height: 20px;
  color: var(--text);
}

.shortcuts__row:first-of-type {
  border-block-start: 0;
}

.shortcuts kbd {
  font-family: var(--font);
  font-size: 12px;
  line-height: 16px;
  font-weight: 600;
  color: var(--text-muted);
}
