/* Type follows the Lotus Garden (Natural Way of Living) members area: a lowercase Poppins wordmark
   beside the lotus mark, Work Sans for text. The colours are the Pale sky palette (light and dark). */
@font-face {
  font-family: "Work Sans";
  src: url("fonts/work-sans-latin-wght.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("fonts/poppins-latin-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

:root {
  --bg: #f3f4f8;
  --surface: #ffffff;
  --text: #5a5c69;
  --strong: #1a202c;
  --muted: #858796;
  --line: #e3e6f0;
  --accent: #137da8;
  --accent-deep: #0f6a8f;
  --logo: #4198e4;
  --accent-ink: #ffffff;
  --accent-soft: #e5f4fa;
  --link: #8fadc9;
  --danger: #c4516a;
  --danger-soft: #fbeef1;
  --alert-soft: #fde8ec;
  --alert-line: #d1607a;
  --alert-ink: #7a1f36;
  --fav: #cf5e95;
  --radius: 12px;
  color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #16181d;
    --surface: #1f2228;
    --text: #d6d8e0;
    --strong: #f1f2f6;
    --muted: #9a9cab;
    --line: #30343d;
    --accent: #74c5e7;
    --accent-deep: #9ad6ef;
    --logo: #74c5e7;
    --accent-ink: #0b2230;
    --accent-soft: #223339;
    --link: #a3c0da;
    --danger: #e58a9d;
    --danger-soft: #3a2429;
    --alert-soft: #3f2229;
    --alert-line: #e58a9d;
    --alert-ink: #ffc2cf;
    --fav: #f0a0c8;
  }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 "Work Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
button, input, select { font: inherit; color: inherit; }
.muted { color: var(--muted); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Header: white bar, like the members area */
.top { background: var(--surface); border-bottom: 1px solid var(--line); padding-top: env(safe-area-inset-top); }
.top-inner, main { max-width: 760px; margin: 0 auto; padding: 0 16px; }
.top-inner { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 60px; }
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
h1 { font: 400 1.45rem/1 "Poppins", "Work Sans", sans-serif; margin: 0; color: var(--logo); white-space: nowrap; }

main { padding-top: 8px; padding-bottom: 210px; }
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); margin: 14px 0; }

button { cursor: pointer; border: 0; border-radius: 8px; padding: 10px 18px; background: var(--accent); color: var(--accent-ink); font-weight: 600; }
button:disabled { opacity: .55; cursor: default; }
button.ghost { background: transparent; color: var(--accent-deep); border: 1px solid var(--accent); padding: 6px 12px; }

/* Collapsible panels share one look: the add panel and each playlist */
details > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 10px; -webkit-tap-highlight-color: transparent; } /* iOS paints a grey tap box over the touched summary, and leaves it at the row's old place while a playlist is dragged */
details > summary::-webkit-details-marker { display: none; }
.chev { flex: none; color: var(--muted); transition: transform .15s; }
details[open] > summary .chev { transform: rotate(90deg); }

.add { padding: 0; }
.add summary { padding: 14px 16px; font-weight: 600; color: var(--strong); }
.add form { padding: 0 16px 16px; }
.add-head { margin: 0; padding: 0 16px 6px; font-size: .85rem; font-weight: 600; color: var(--strong); }
.row { display: flex; gap: 8px; }
input[type=url] {
  flex: 1; min-width: 0; padding: 10px 12px; border-radius: 8px;
  border: 1px solid #acb5bd; background: var(--surface); color: var(--strong); font-size: .95rem;
}
input[type=url]:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }

.add-files { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 16px 16px; }
.file-btn { display: inline-block; cursor: pointer; border-radius: 8px; border: 1px solid var(--accent); color: var(--accent-deep); padding: 6px 12px; font-weight: 600; }
.file-btn[hidden] { display: none; } /* the class's display would otherwise beat the hidden attribute */
.add-files input:focus-visible + .file-btn { outline: 2px solid var(--accent); outline-offset: 1px; }
.add-files input:disabled + .file-btn { opacity: .55; cursor: default; }

#job { padding: 0 16px 16px; }
.add details:not([open]) + #job { padding-top: 0; }
.job-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-weight: 600; color: var(--strong); }
.job-head span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#job-dismiss { flex: none; width: 44px; height: 44px; margin: -10px -12px -10px 0; }
#job-dismiss[hidden] { display: none; } /* button.icon's display would otherwise beat the hidden attribute */
#job-dismiss svg { width: 20px; height: 20px; }
progress { width: 100%; height: 8px; margin-top: 8px; accent-color: var(--accent); }
#job-status { margin: 6px 0 0; font-size: .9rem; }
#job-status.error { color: var(--danger); }

/* Listen | Manage: a segmented switch under the header. Manage-only things are hidden in Listen by one rule. */
.tabs { display: flex; gap: 2px; padding: 2px; margin: 12px 0 0; background: var(--line); border-radius: 9px; }
.tabs button { flex: 1; min-height: 44px; padding: 6px 0; border-radius: 7px; background: none; color: var(--strong); font-weight: 500; }
.tabs button[aria-selected="true"] { background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / 12%); font-weight: 600; }
.tabs button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
body[data-tab="listen"] .manage-only { display: none; }
.manage-hint { margin: 10px 2px 0; font-size: .85rem; }

/* Library */
.coll { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); margin: 14px 0; overflow: hidden; }
.coll > summary { padding: 14px 12px 14px 16px; align-items: center; }
.coll-head { flex: 1; min-width: 0; }
.coll-title { font-weight: 700; color: var(--strong); }
.coll-meta { font-size: .85rem; }
button.del {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  background: transparent; color: var(--danger); border: 1px solid transparent;
  padding: 6px 10px; font-weight: 500; font-size: .9rem;
}
button.del svg { width: 16px; height: 16px; fill: currentColor; }
.coll-warn { margin: 0 16px 12px; padding: 10px 12px; border-radius: 8px; background: var(--accent-soft); font-size: .9rem; display: flex; gap: 10px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.coll-warn button { padding: 7px 12px; }

.coll-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 0 16px 12px; }
.export-box { display: flex; align-items: flex-start; gap: 8px; margin: 0 16px 12px; padding: 10px 12px; border-radius: 8px; background: var(--accent-soft); font-size: .9rem; }
.export-body { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.export-box a.save { display: inline-block; border-radius: 8px; padding: 10px 18px; background: var(--accent); color: var(--accent-ink); font-weight: 600; text-decoration: none; overflow-wrap: anywhere; }
.export-box a.save:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.export-note, .export-error { margin: 0; }
.export-error { color: var(--danger); }
button.icon.export-close { flex: none; width: 44px; height: 44px; margin: -8px -8px 0 0; } /* beats button.icon's 46 px size */
button.icon.export-close svg { width: 20px; height: 20px; }
.tracks { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.tracks li { display: flex; align-items: stretch; border-bottom: 1px solid var(--line); }
.tracks li:last-child { border-bottom: 0; }
.track { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; text-align: left; background: none; color: var(--text); border-radius: 0; padding: 12px 4px 12px 4px; font-weight: 400; position: relative; }
.track.current { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.track.current .t-name { color: var(--accent-deep); font-weight: 600; }
/* In Listen a row has no handle, so the track button starts the row and needs the left margin the handle's column gave it. */
.tracks li > .track:first-child { padding-left: 16px; }
.t-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.t-meta { font-size: .85rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.t-done { color: var(--accent-deep); }
/* The drag handle: only it starts a drag, so touching anywhere else in the row still scrolls the page. pan-y, not none: a quick swipe
   that starts on the handle scrolls too, and app.js picks the item up only after a short hold, then blocks scrolling for that drag. */
.drag { flex: none; display: grid; place-items: center; width: 44px; min-height: 44px; color: var(--muted); cursor: grab; touch-action: pan-y; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; }
.drag svg { width: 20px; height: 20px; fill: currentColor; }
.tracks li:has(.track.current) { background: var(--accent-soft); }
.tracks li.dragging { position: relative; z-index: 1; background: var(--accent-soft); transform: scale(1.02); box-shadow: 0 8px 22px rgb(58 59 69 / 30%); } /* lifted: bigger, with a deeper shadow */
.tracks li.dragging .drag { cursor: grabbing; }
/* While a row is being dragged it is moved in the DOM; scroll anchoring would then scroll the page under the finger and the row would
   overshoot. Only then: otherwise it is what keeps the list steady when something above it changes height. The class is on the
   section whose rows are moving: #library, or #favorites for a drag inside Favorites. */
#library.reordering, #favorites.reordering { overflow-anchor: none; }
.coll > summary.has-drag { padding-left: 0; } /* the 44px handle column then lines up with the track handles below */
.coll.dragging { position: relative; z-index: 1; background: var(--accent-soft); transform: scale(1.02); box-shadow: 0 8px 22px rgb(58 59 69 / 30%); } /* lifted: bigger, with a deeper shadow */
.coll.dragging .drag { cursor: grabbing; }
/* A track row's delete icon in Manage (and the remove-from-Favorites icon): the heart's box, in the danger colour. */
.row-del { flex: none; display: grid; place-items: center; width: 44px; min-height: 44px; padding: 0; border-radius: 0;
  background: none; color: var(--danger); touch-action: manipulation; }
.row-del svg { width: 20px; height: 20px; fill: currentColor; }
/* The heart: its own button at the right of a track row, so a tap never plays or drags. */
.fav-btn { flex: none; display: grid; place-items: center; width: 44px; min-height: 44px; padding: 0; border-radius: 0;
  background: none; color: var(--muted); touch-action: manipulation; }
.fav-btn[aria-pressed="true"] { color: var(--fav); }
.fav-btn svg { width: 22px; height: 22px; fill: currentColor; }
.coll.fav > summary { padding-left: 0; } /* the heart takes the 44px column the drag dots use */
.fav-mark { flex: none; display: grid; place-items: center; width: 44px; min-height: 44px; color: var(--fav); }
.fav-mark svg { width: 20px; height: 20px; fill: currentColor; }
/* The heart after "with Love" in About: the Favorites heart (same path, same --fav), sized to the text. Decorative: the words carry the meaning. */
.love { width: 1em; height: 1em; margin-left: .3em; vertical-align: -.125em; fill: var(--fav); }
/* Recently Deleted: a section under the playlists, shown in Manage only. */
.trash-mark { flex: none; display: grid; place-items: center; width: 44px; min-height: 44px; color: var(--muted); }
.trash-mark svg { width: 20px; height: 20px; fill: currentColor; }
.coll.trash > summary { padding-left: 0; } /* the mark takes the 44px column the drag dots use */
.trash-note { margin: 0; padding: 0 16px 12px; font-size: .85rem; }
.trash-tools { display: flex; padding: 0 16px 12px; }
.trash-item { display: flex; align-items: center; gap: 10px; padding-left: 16px; }
.trash-ic { flex: none; width: 20px; height: 20px; fill: var(--muted); }
.trash-text { flex: 1; min-width: 0; }
.trash-name { color: var(--strong); overflow-wrap: anywhere; }
.act { flex: none; width: 52px; display: flex; flex-direction: column; align-items: center; gap: 0; background: none; padding: 6px 0; border-radius: 0; font-size: .7rem; font-weight: 500; line-height: 1.2; touch-action: manipulation; }
.act svg { width: 22px; height: 22px; fill: currentColor; }
.act.rec { color: var(--accent-deep); }
.act.del-forever { color: var(--danger); }
button.ghost.danger { color: var(--danger); border-color: var(--danger); }
.t-prog { position: absolute; left: 0; bottom: 0; height: 3px; background: var(--accent); opacity: .8; }

.empty { text-align: center; margin-top: 40px; }

/* Player */
#player {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 10;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
  box-shadow: 0 -4px 18px rgb(58 59 69 / 6%);
  padding: 12px 16px calc(10px + env(safe-area-inset-bottom));
}
#player > * { max-width: 728px; margin-left: auto; margin-right: auto; }
.np-title { font-weight: 600; color: var(--strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.np-sub { font-size: .85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* touch-action: none: a drag on the slider is the slider's, so a slightly diagonal drag on a phone is never taken as a page scroll. */
#seek { display: block; width: 100%; margin-top: 8px; accent-color: var(--accent); touch-action: none; }
.times { display: flex; justify-content: space-between; font-size: .8rem; font-variant-numeric: tabular-nums; }
.controls { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 2px; }
/* The five transport buttons stay centred under the seek bar: the Continuous play toggle sits in the right-hand slot (.spacer) and an
   empty slot of the same width balances it on the left, so the play button is the exact middle. */
.controls::before { content: ""; }
.controls::before, .controls .spacer { width: 72px; flex: none; }
.controls .spacer { display: grid; place-items: center; }
/* Continuous play: on = tinted disc, off = crossed out. Never a hover tint: this id rule outranks the hover rules, so a mouse
   hovering a switched-off button doesn't show the disc either. */
#continuous { background: none; color: var(--text); }
#continuous[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent-deep); }
#continuous[aria-pressed="true"] .slash { display: none; }
button.icon { background: none; color: var(--text); padding: 6px; width: 46px; height: 46px; display: grid; place-items: center; border-radius: 50%; }
button.icon svg { width: 26px; height: 26px; fill: currentColor; }
button.icon text { font: 700 6.5px "Work Sans", system-ui, sans-serif; fill: currentColor; }
button.icon.big { width: 58px; height: 58px; background: var(--accent); color: var(--accent-ink); }
button.icon.big svg { width: 30px; height: 30px; }

@media (max-width: 420px) {
  .controls { gap: 2px; }
  .controls::before, .controls .spacer { width: 60px; }
  button.icon { width: 42px; height: 42px; }
  button.del .del-label { display: none; }
  h1 { font-size: 1.2rem; }
}

/* Crash notice */
.notice { padding: 14px 16px; border-color: var(--danger); background: var(--danger-soft); }
.notice p { margin: 0 0 10px; color: var(--strong); font-size: .95rem; }
.update { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 16px; border-color: var(--accent); background: var(--accent-soft); color: var(--strong); }
.update[hidden] { display: none; } /* the class's display would otherwise beat the hidden attribute */
.update button { padding: 7px 14px; }
/* Loud on purpose: people who ignore it end up using a browser tab, which can lose their downloads. */
.install-card { padding: 14px 16px; border: 2px solid var(--alert-line); background: var(--alert-soft); }
.install-card p { margin: 0 0 8px; color: var(--strong); font-size: .95rem; }
.install-card .install-title { font-size: 1.05rem; color: var(--alert-ink); }
.install-card button.ghost { color: var(--alert-ink); border-color: var(--alert-line); }

/* Player: full or minimized to one line. Only one set of controls shows at a time. */
.np { display: flex; align-items: flex-start; gap: 8px; }
.np-text { flex: 1; min-width: 0; }
#minimize { width: 44px; height: 44px; margin: -8px 0 0 -9px; }
.mini { display: none; align-items: center; gap: 4px; }
.mini-title { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; min-height: 44px; text-align: left; background: none; color: var(--strong); padding: 0 4px 0 0; font-weight: 600; border-radius: 8px; }
.mini-chev { flex: none; width: 26px; height: 26px; fill: currentColor; color: var(--text); }
/* Marker in the minimized bar while Continuous play is off: the crossed-out list icon, small, not a button, never wider than 16px. */
.mini-once { flex: none; display: grid; place-items: center; width: 16px; height: 16px; color: var(--text); }
.mini-once[hidden] { display: none; }
.mini-once svg { width: 16px; height: 16px; fill: currentColor; }
.mini-title span { display: block; flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#player.is-mini { padding-top: 6px; padding-bottom: calc(6px + env(safe-area-inset-bottom)); }
#player.is-mini .mini { display: flex; }
#player.is-mini > :not(.mini) { display: none; }
#mini-play { background: var(--accent); color: var(--accent-ink); }
body.player-mini main { padding-bottom: 90px; }
/* Manage hides the playback bar by CSS only: the audio, the lock screen and #player's hidden attribute (nothing loaded) are untouched. */
body[data-tab="manage"] #player { display: none; }
body[data-tab="manage"] main { padding-bottom: 40px; }

/* Menu: a button with aria-expanded that shows a group of ordinary buttons (the disclosure pattern, not role="menu"). */
.menu { position: relative; flex: none; }
#menu-btn { width: 44px; height: 44px; } /* an id rule, so it also beats the 42px phone size of button.icon */
.menu-list {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 190px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 24px rgb(0 0 0 / .18); padding: 6px; display: flex; flex-direction: column;
}
.menu-list[hidden] { display: none; } /* the class's display would otherwise beat the hidden attribute */
.menu-list button { width: 100%; text-align: left; background: transparent; color: var(--strong); font-weight: 500; min-height: 44px; padding: 10px 12px; }
.menu-list button[hidden] { display: none; }
.menu-list button:focus-visible, #menu-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Dialogs: Send feedback, About, Install app */
dialog {
  width: min(560px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow: auto;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); color: var(--text); padding: 16px;
}
dialog::backdrop { background: rgb(0 0 0 / .45); }
dialog h2 { margin: 0 0 10px; font-size: 1.15rem; color: var(--strong); }
dialog p { margin: 0 0 10px; }
dialog ul { margin: 0 0 10px; padding-left: 20px; }
dialog li { margin-bottom: 6px; }
dialog .row { margin-top: 6px; }
#fb-form label { display: block; margin: 12px 0 4px; font-weight: 600; color: var(--strong); }
#fb-message, #fb-email, #fb-platform {
  display: block; width: 100%; padding: 10px 12px; border-radius: 8px;
  border: 1px solid #acb5bd; background: var(--surface); color: var(--strong); font: inherit; font-size: 1rem; /* under 16px, iOS zooms the page into the field on focus */
}
#fb-message { resize: vertical; }
#fb-message:focus, #fb-email:focus, #fb-platform:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
#fb-form label.fb-check { display: flex; align-items: center; gap: 10px; min-height: 44px; margin: 8px 0 0; font-weight: 500; }
.fb-check input { width: 20px; height: 20px; flex: none; accent-color: var(--accent); }
dialog .fb-hint { margin: 2px 0 12px; font-size: .85rem; }
#fb-status { margin: 0 0 10px; }
#fb-status.error { color: var(--danger); }
#fb-status:empty { display: none; }

/* Hover tints, only where a real hover exists. iOS Safari leaves :hover applied after a tap until the next tap elsewhere, which
   made a just-tapped button look stuck pressed. A phone reports (hover: none), so it never gets these; a mouse keeps them.
   (pointer: fine) as well keeps a touch-first device with a stray mouse from getting them as its primary input.
   Hover-only: keep every :hover rule in this block (tests/unit/touch-ui.test.mjs fails on one outside it). :focus-visible stays outside. */
@media (hover: hover) and (pointer: fine) {
  button:hover { background: var(--accent-deep); }
  button.ghost:hover { background: var(--accent-soft); }
  .file-btn:hover { background: var(--accent-soft); }
  button.del:hover { background: var(--danger-soft); border-color: var(--danger); }
  .track:hover { background: var(--accent-soft); }
  .fav-btn:hover { background: var(--accent-soft); }
  .tabs button:not([aria-selected="true"]):hover { background: color-mix(in srgb, var(--surface) 50%, transparent); }
  .row-del:hover, .act.del-forever:hover, button.ghost.danger:hover { background: var(--danger-soft); }
  .act.rec:hover { background: var(--accent-soft); }
  button.icon:hover { background: var(--accent-soft); }
  button.icon.big:hover { background: var(--accent-deep); }
  .install-card button.ghost:hover { background: color-mix(in srgb, var(--alert-line) 20%, var(--alert-soft)); }
  .mini-title:hover { background: var(--accent-soft); }
  #mini-play:hover { background: var(--accent-deep); }
  .menu-list button:hover { background: var(--accent-soft); }
}
