/* ---------------------------------------------------------------------------
   base.css — Global reset, html/body, and :root CSS custom properties
   (--blur-color-*, --blur-opacity, --accent-color) set from JS.
   Load FIRST: other files rely on the box-sizing reset and these variables.
   --------------------------------------------------------------------------- */

* { box-sizing: border-box; }
html {
  height: 100%;
  overflow: hidden;
}
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: #121214;
  color: #e8e8ea;
  /* 100dvh intentionally NOT used here (and no longer overrides 100vh
     below) — per the documented iOS PWA cold-start bug: on standalone
     launch, 100dvh can report a stale/short value until a geometry change
     (rotation) forces WebKit to recompute it, which is exactly the
     "gap between the nav bar and the bottom of the screen" symptom this
     was causing. 100vh is the value that's reliably correct from cold
     start in standalone mode (see responsive.css's mobile override and
     layout-init.js's viewport recheck for the full explanation/fallback
     chain). Desktop/non-PWA browser quirks around 100vh (toolbar height)
     don't apply here since standalone mode has no toolbar. */
  height: 100vh;
  overflow: hidden;
}
:root {
  --blur-color-1: #2a2a2e;
  --blur-color-2: #2a2a2e;
  --blur-color-3: #2a2a2e;
  --blur-color-4: #2a2a2e;
  --blur-opacity: 0;
  /* Drives #playerBar's own fallback fill opacity on mobile (see
     responsive.css) - 1 (fully opaque) whenever there's no real mosaic to
     show behind it (matches --blur-opacity: 0's default here), 0 once a
     track's art/colors have actually been extracted (see applyBlurColors
     in playback.js, the only place this and --blur-opacity change
     together). Keeping this default in sync with --blur-opacity's default
     matters: both start meaning "nothing to show yet". */
  --player-fallback-opacity: 1;
  --accent-color: #e8e8ea;
  --bottom-nav-h: 0px;
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-top: env(safe-area-inset-top, 0px);
}
/* Prevent double-tap zoom / tap highlight so the UI reads as an app,
   not a webpage. Text stays selectable where it already was (inputs,
   textareas), everything else opts out of the browser's touch chrome. */
* { -webkit-tap-highlight-color: transparent; }
button, .file-row, .queue-item, .playlist-item, .tab-btn, .bottom-nav-btn,
.crumb-label, .album-card, a { touch-action: manipulation; }

/* ---------------------------------------------------------------------------
   Minimal text input / textarea style — shared, global base. Previously
   there was no single foundation: .modal input and .search-input each had
   their own visible bordered-box look, and other fields (the entire
   metadata editor, for one) had no styling at all and rendered as raw,
   unstyled browser defaults. This is the one shared minimal look the whole
   app now builds on: no border/box at rest, a background only barely
   distinct from the surrounding surface, and just a thin bottom line that
   brightens to the accent color on focus — nothing to look at until you
   actually interact with a field. Per-context files (topbar.css,
   context-menu-modal.css, metadata-editor.css, settings.css) may still set
   layout (width, margin, font-size) but no longer need to (and mostly no
   longer do) redeclare the visual appearance itself.
   --------------------------------------------------------------------------- */
input[type="text"],
input[type="number"],
input[type="search"],
input[type="email"],
input[type="password"],
textarea {
  background: rgba(255, 255, 255, 0.03);
  border: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 4px 4px 0 0;
  color: #e8e8ea;
  padding: 6px 8px;
  outline: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}
input[type="text"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
textarea:focus {
  border-bottom-color: var(--accent-color);
  background: rgba(255, 255, 255, 0.05);
}
input[type="text"]::placeholder,
input[type="number"]::placeholder,
input[type="search"]::placeholder,
textarea::placeholder {
  color: #6a6a70;
}
/* Native spinner arrows on number inputs looked visually inconsistent
   against the app's minimal style — removed rather than restyled, since
   they're notoriously unreliable to reskin consistently across browsers.
   Typing a value or using the up/down arrow keys still works exactly the
   same; only the visible clickable arrows are gone. */
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}

/* Minimal checkbox: same resting background as text inputs, a faint
   accent-tinted border (brightening on hover/focus), and an accent-
   colored checkmark — nothing fills solid, staying consistent with the
   "outline + color accent" look used everywhere else rather than a
   filled block. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border: 1.5px solid color-mix(in srgb, var(--accent-color) 45%, transparent);
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.03);
  cursor: pointer;
  position: relative;
  vertical-align: middle;
  transition: border-color 0.15s ease, background 0.15s ease;
}
input[type="checkbox"]:hover { border-color: color-mix(in srgb, var(--accent-color) 70%, transparent); }
input[type="checkbox"]:checked {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--accent-color);
}
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  /* The checkmark's rotated 4x8 box isn't visually centered within itself
     — the corner (the actual visual "point" of the check) sits toward the
     bottom-left of that box, so centering the box geometrically in the
     16px checkbox would look off-center to the eye. Nudged right/up from
     true geometric center to compensate. */
  left: 5px;
  top: 2.5px;
  width: 4px;
  height: 7px;
  border: solid var(--accent-color);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
input[type="checkbox"]:indeterminate {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--accent-color);
}
input[type="checkbox"]:indeterminate::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 7px;
  width: 8px;
  height: 2px;
  background: var(--accent-color);
}

/* ---------------------------------------------------------------------------
   Generic overflow marquee — reusable site-wide marquee for independent,
   unrelated pieces of text (queue items, file names, playlist names, the
   player bar's own artist/album lines, etc). The player bar's title+path
   pair has its own separate hand-built version (see startMarquee() in
   playback.js) for reasons noted there.

   No CSS animation here on purpose — every overflowing+visible element on
   screen must scroll in lockstep (start together, hold together, and only
   loop back to start once every one of them has finished), which a
   per-element CSS @keyframes animation can't express since each element's
   scroll distance/duration naturally differs. Driven entirely by
   runMarqueeClock() in layout-init.js instead: a single shared
   requestAnimationFrame loop sets transform directly on every active
   element using one shared cycle length (the slowest element's duration),
   so they all move and pause as one group regardless of individual text
   length. This class just establishes the clipping box + the span that
   gets a transform written to it every frame.

   Desktop: scrolls on hover only (a static ellipsis at rest is calmer for
   a page with many rows, and hover is a natural "I want to read this"
   signal that touch devices don't have) — gated in JS via
   activeMarqueeEntries()'s matchMedia('(hover: hover)') check
   (layout-init.js), not a CSS media query.
   Mobile: scrolls automatically after a brief pause, since there's no
   hover — the same check above simply includes every overflowing element
   unconditionally when hover isn't supported.
--------------------------------------------------------------------------- */
.marquee-generic { overflow: hidden; white-space: nowrap; position: relative; display: block; }
.marquee-generic > span { display: inline-block; will-change: transform; }
/* wrapWhole:true targets that also carry their own static
   text-overflow:ellipsis (e.g. .mini-player-title, .fp-title,
   .fp-upcoming-title, .lib-name, .file-name, .album-card-name - see their
   definitions in responsive.css/filelist.css) have that ellipsis on the
   CONTAINER itself (.marquee-generic is added directly to them, same
   element) - a different element from the span runMarqueeClock actually
   transforms (see layout-init.js: the span is created fresh as a child of
   the container). .marquee-active is added to BOTH the container and the
   span while active (see runMarqueeClock) specifically so this rule and
   the wrapWhole:false rule below can each target their own element -
   .marquee-generic.marquee-active neutralizes the container's own
   ellipsis while its child span is scrolling, since text-overflow:ellipsis
   is evaluated at the container's own rendering step independent of a
   child's position/transform. */
.marquee-generic.marquee-active { text-overflow: clip; }
/* wrapWhole:false case (e.g. .file-title wrapping .cell-text): the
   container becomes the clipping box, exactly like .marquee-generic above.
   Its child (.cell-text) has its own overflow:hidden/text-overflow:
   ellipsis for the static, non-hovered/non-active state — but once it
   becomes the actively-scrolling "span" for a marquee, self-clipping and
   showing "…" instead of the real text made the translate() applied to it
   invisible (an element can't visibly scroll past its own overflow:hidden
   boundary). marquee-active neutralizes that for exactly the moment it's
   actually being animated. */
.marquee-generic-inline { overflow: hidden; white-space: nowrap; position: relative; display: block; }
.marquee-generic-inline > .marquee-active {
  display: inline-block;
  overflow: visible;
  text-overflow: clip;
  max-width: none;
  will-change: transform;
}