/* Groove Bank — everything that isn't a token. Tokens live in theme.css.
   Mobile-first at 375px (iPhone 13 mini). No hardcoded greys. */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--text-body);
  line-height: 1.4;
  -webkit-text-size-adjust: 100%;
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* Room for the fixed tab bar plus the iOS home indicator */
  padding-bottom: calc(var(--tap-min) + 18px + env(safe-area-inset-bottom, 0px));
}

/* ---------- App header ---------- */

.app-header {
  display: flex;
  align-items: baseline;
  gap: var(--gap-sm);
  /* The top inset matters only once this is installed to a home screen: the
     status bar is translucent there and the page starts under the clock, so
     without it "Groove Bank" sits behind the time. Zero in a browser tab. */
  padding: calc(var(--gap) + env(safe-area-inset-top, 0px)) var(--gap) var(--gap-sm);
}

.app-header h1 {
  margin: 0;
  font-size: var(--text-xl);
  letter-spacing: -0.02em;
}

.app-header .tagline {
  color: var(--text-dim);
  font-size: var(--text-small);
}

/* ---------- Screens ---------- */

.screen { display: none; padding: 0 var(--gap) var(--gap); }
.screen.is-active { display: block; }

.screen h2 {
  font-size: var(--text-lg);
  margin: var(--gap-sm) 0 var(--gap);
}

/* ---------- Cards — one card family, used everywhere ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--gap);
  margin-bottom: var(--gap);
}

.card-title {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
}

.card-sub {
  color: var(--text-dim);
  font-size: var(--text-small);
  margin: var(--gap-xs) 0 0;
}

/* Rows inside a card: a pattern under its rhythm */
.row {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  min-height: var(--tap-min);
  padding: var(--gap-sm) 0;
  border-top: 1px solid var(--border);
}

.row:first-of-type { border-top: 0; }
.row .row-name { flex: 1 1 auto; min-width: 0; }
.row .row-meta { color: var(--text-dim); font-size: var(--text-small); flex: 0 0 auto; }

/* ---------- Pills — the one badge component ---------- */

.pill {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--text-small);
  font-weight: 600;
  background: var(--surface-raised);
  color: var(--text-dim);
  border: 1px solid var(--border);
  white-space: nowrap;
}

.pill.kind-groove { color: var(--cell-hit); border-color: var(--cell-hit); }
.pill.kind-break  { color: var(--accent);   border-color: var(--accent); }
.pill.kind-intro  { color: var(--text);     border-color: var(--text-dim); }

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-sm);
  min-height: var(--tap-min);
  padding: 0 var(--gap);
  font-family: inherit;
  font-size: var(--text-body);
  font-weight: 600;
  color: var(--text);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.btn:active { background: var(--border); }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--bg); }
.btn-primary:active { background: var(--accent-dim); border-color: var(--accent-dim); color: var(--text); }
.btn-danger { color: var(--danger); border-color: var(--danger); }
.btn-block { display: flex; width: 100%; }

/* ---------- Notices ---------- */

.notice {
  background: var(--surface-raised);
  border: 1px solid var(--accent);
  border-left-width: 4px;
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: var(--gap-sm) var(--gap);
  margin: 0 var(--gap) var(--gap);
  /* Body size, not --text-small: a notice only ever appears when something is
     wrong, and it has to be readable at arm's length in a car. */
  font-size: var(--text-body);
}

/* Same component, no alarm. For the one notice that is good news - the screen
   really is being held awake - which would otherwise read as a warning. */
.notice.is-quiet {
  border-color: var(--border);
  color: var(--text-dim);
}

.notice[hidden] { display: none; }

.empty-state {
  color: var(--text-dim);
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: var(--gap-lg) var(--gap);
  text-align: center;
}

.placeholder {
  color: var(--text-dim);
  font-size: var(--text-small);
}

/* ---------- Tab bar ---------- */

.tabbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  z-index: 10;
}

.tab {
  flex: 1 1 0;
  min-height: var(--tap-min);
  padding: var(--gap-sm) 0;
  font-family: inherit;
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--text-dim);
  background: transparent;
  border: 0;
  border-top: 3px solid transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.tab[aria-selected="true"] { color: var(--accent); border-top-color: var(--accent); }

/* ---------- Button grids ---------- */

.btn-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-sm);
  margin-bottom: var(--gap);
}

.btn-grid .btn { flex: 1 1 45%; }
.btn-grid .btn-block { flex: 1 1 100%; }

/* ---------- Sliders and labelled fields ---------- */

.field { display: block; margin-bottom: var(--gap); }

.field-label {
  display: block;
  font-size: var(--text-small);
  color: var(--text-dim);
  margin-bottom: var(--gap-xs);
}

/* An explanation that belongs to the option above it, not a label for the one
   below. Body size rather than --text-small: it is prose to be read, and this
   is still a screen read at arm's length. */
.field-help {
  margin: calc(-1 * var(--gap-sm)) 0 var(--gap);
  color: var(--text-dim);
  font-size: var(--text-body);
}

/* Tall enough to grab one-handed, and the thumb is deliberately oversized -
   this gets dragged while the app is playing, often in a moving car. */
.slider {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: var(--tap-min);
  margin: 0;
  background: transparent;
  cursor: pointer;
}

.slider::-webkit-slider-runnable-track {
  height: 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  border: 1px solid var(--border);
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 34px;
  height: 34px;
  margin-top: -13px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg);
}

.slider::-moz-range-track {
  height: 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  border: 1px solid var(--border);
}

.slider::-moz-range-thumb {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg);
}

.btn[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
}

/* ---------- Library: rhythm cards ---------- */

/* The whole header is the expand/collapse control - a thumb-sized target with
   the name and the part count inside it, rather than a small chevron to hit. */
.rhythm-head {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--gap-sm) 0;
  font-family: inherit;
  font-size: var(--text-body);
  color: var(--text);
  text-align: left;
  background: transparent;
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.rhythm-head .chev {
  flex: 0 0 auto;
  width: 22px;
  color: var(--accent);
  font-size: var(--text-lg);
  line-height: 1;
}

.rhythm-text { flex: 1 1 auto; min-width: 0; }
.rhythm-text .card-title { display: block; }
.rhythm-text .card-sub { display: block; }

.rhythm-body {
  border-top: 1px solid var(--border);
  margin-top: var(--gap-sm);
  padding-top: var(--gap);
}

/* A pattern is an info line plus its own actions, boxed so a long list of them
   still reads as separate parts rather than one wall of buttons. */
.pattern {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--gap-sm) var(--gap);
  margin-bottom: var(--gap);
}

.pattern .row { border-top: 0; }
.pattern .btn-grid { margin-bottom: var(--gap-sm); }
.pattern .btn { background: var(--surface); }
.pattern .btn-primary { background: var(--accent); }

/* "Delete rhythm" is two words and has to stay on one line at 375px. */
.rhythm-tools { margin-top: var(--gap); margin-bottom: 0; }
.rhythm-tools .btn { padding: 0 var(--gap-sm); }

/* ---------- Inline forms (no browser prompts anywhere) ---------- */

.text-input {
  display: block;
  width: 100%;
  min-height: var(--tap-min);
  padding: 0 var(--gap-sm);
  font-family: inherit;
  font-size: var(--text-body);
  color: var(--text);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.text-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Three equal buttons on one line - used for the groove/break/intro choice. */
.seg { display: flex; gap: var(--gap-sm); }
.seg .btn { flex: 1 1 0; min-width: 0; padding: 0 var(--gap-sm); }

/* ---------- Inline delete confirmation ---------- */

.confirm {
  border: 1px solid var(--danger);
  border-radius: var(--radius-sm);
  padding: var(--gap-sm) var(--gap);
  margin-top: var(--gap-sm);
}

.confirm-text { margin: 0 0 var(--gap-sm); font-size: var(--text-small); }
.confirm .btn-grid { margin-bottom: 0; }

/* Full width, stacked. Nested four levels deep there is not enough room for two
   readable labels side by side, and a destructive choice is the last place to
   put two small targets next to each other. */
.confirm .btn-grid .btn { flex: 1 1 100%; }

/* ---------- Editor: fields ---------- */

/* A native picker earns its place here and not for `kind`: the number of rhythms
   is unbounded, and a wheel beats an ever-growing row of buttons. */
.select-input {
  display: block;
  width: 100%;
  min-height: var(--tap-min);
  padding: 0 var(--gap-sm);
  font-family: inherit;
  font-size: var(--text-body);
  color: var(--text);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.select-input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Minus, the count, plus. The number is not typeable on purpose - a keyboard
   opening over the grid to set "3" is worse than two taps. */
.stepper { display: flex; align-items: center; gap: var(--gap-sm); }
.stepper .btn { flex: 0 0 var(--tap-min); padding: 0; font-size: var(--text-lg); }

.stepper-value {
  flex: 1 1 auto;
  text-align: center;
  font-size: var(--text-body);
  font-weight: 600;
}

.field-value { color: var(--text); font-weight: 600; }

/* ---------- Editor: transport ---------- */

/* Sticky so Play and the tempo stay reachable however far down the grid you
   have scrolled. Full-bleed background, or the grid shows through the gap. */
.transport {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--gap);
  margin: 0 calc(var(--gap) * -1);
  padding: var(--gap-sm) var(--gap);
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

.transport .btn { flex: 1 1 auto; }

.transport-meta {
  flex: 0 0 auto;
  color: var(--text-dim);
  font-size: var(--text-small);
  text-align: right;
}

/* Stop is a filled red: it is the button you reach for without looking. */
.btn-danger-solid {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--text);
}

/* ---------- Editor: the step grid ---------- */

/* The app's one horizontal scroller, and it goes full-bleed: every pixel of the
   375px viewport is a pixel of pattern. */
.grid-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  margin: var(--gap) calc(var(--gap) * -1) var(--gap-sm);
  -webkit-overflow-scrolling: touch;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.grid { width: max-content; min-width: 100%; }
.grid-row { display: flex; align-items: stretch; }
/* position:relative so a cell's offsetLeft is its distance along the strip.
   followPlayhead() depends on that. */
.grid-cells { display: flex; position: relative; }

/* The label is the mute toggle: the biggest target in the row, and it says what
   it is rather than hiding behind a speaker icon. */
.grid-label {
  position: sticky;
  left: 0;
  z-index: 2;
  flex: 0 0 var(--grid-label-w);
  width: var(--grid-label-w);
  min-height: var(--cell-h);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: 0 var(--gap-sm);
  font-family: inherit;
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--text);
  text-align: left;
  background: var(--surface);
  border: 0;
  border-right: 2px solid var(--border);
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.grid-label-name { display: block; }

.grid-label-state {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--danger);
  min-height: 15px;
}

.grid-row.is-muted .grid-label { color: var(--text-dim); }
.grid-row.is-muted .cell::after { opacity: 0.3; }

/* The corner above the labels, and the bar-number ruler beside it. */
.grid-ruler-row .grid-label,
.grid-ruler-row .tick {
  min-height: var(--ruler-h);
  height: var(--ruler-h);
}

.grid-ruler-row .grid-corner {
  cursor: default;
  background: var(--surface);
}

.tick {
  flex: 0 0 var(--cell-w);
  width: var(--cell-w);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
}

/* ---------- Editor: cells ---------- */

/* Circles, not squares: empty ring for off, small and blue for a hit, large and
   amber for an accent. This is the signature visual (DESIGN.md §6). */
.cell {
  position: relative;
  flex: 0 0 var(--cell-w);
  width: var(--cell-w);
  height: var(--cell-h);
  padding: 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: pan-x pan-y;
}

.cell::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--cell-off);
  background: transparent;
}

.cell.is-hit::after {
  width: 18px;
  height: 18px;
  background: var(--cell-hit);
  border-color: var(--cell-hit);
}

.cell.is-accent::after {
  width: 26px;
  height: 26px;
  background: var(--cell-accent);
  border-color: var(--cell-accent);
}

/* Beats 2 and 4 of every bar are shaded, so you can count without reading the
   ruler. */
.cell.is-offbeat, .tick.is-offbeat { background: var(--beat-shade); }

/* Bar lines as an inset shadow rather than a border - a border would widen the
   cell and walk the grid out of step with the ruler above it. */
.cell.is-bar-start, .tick.is-bar-start { box-shadow: inset 2px 0 0 var(--border); }

/* Last, so it wins over the beat shading. */
.cell.is-now, .tick.is-now { background: var(--playhead); }

.grid-hint {
  color: var(--text-dim);
  font-size: var(--text-small);
  margin: 0 0 var(--gap);
}

/* Wider than a phone: spend the room on bigger steps, not on more of them -
   the pattern stays as readable, the targets get easier. */
@media (min-width: 480px) {
  :root { --cell-w: 44px; --grid-label-w: 120px; }
}

/* ---------- Flashcards ---------- */

/* The card face. Everything about it is sized to be read from further away than
   the rest of the app: this is the one screen where the phone is propped up and
   you are holding sticks rather than the handset. */
.flashcard { text-align: center; }

.flashcard-pos {
  margin: 0 0 var(--gap);
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--text-dim);
}

/* The rhythm is context; the part name is the question. Hence the size gap. */
.flashcard-rhythm {
  margin: 0 0 var(--gap-xs);
  font-size: var(--text-lg);
  color: var(--text-dim);
}

.flashcard-name {
  margin: 0 0 var(--gap);
  font-size: var(--text-huge);
  line-height: 1.15;
  font-weight: 800;
  color: var(--text);
  overflow-wrap: anywhere;   /* a long part name wraps rather than widening the page */
}

.flashcard-meta {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--gap-sm);
  margin-bottom: var(--gap-lg);
}

/* Play is the primary action until it is playing, at which point Stop should not
   be the brightest thing on screen. */
.flashcard .btn-block { margin-bottom: var(--gap-sm); }
.flashcard .btn-grid { margin-bottom: 0; }

/* The revealed grid is a picture, not a control: same circles, no pointer, no
   press feedback. Its labels are divs, so they lose the mute affordance too. */
.cell.is-static,
.flashcard-grid .grid-label { cursor: default; }

/* ---------- Share: export / import feedback ---------- */

/* Two one-line messages under the Share buttons - what just saved, or why a
   file could not be read. Same size as .card-sub; only the colour differs. */
.share-status,
.share-error {
  margin: var(--gap-sm) 0 0;
  font-size: var(--text-small);
  line-height: 1.4;
}

.share-status { color: var(--text-dim); }
.share-error  { color: var(--danger); font-weight: 600; }

/* ---------- Practice: the sticky head ---------- */

/* The Editor's transport is a row; this one is a stack. It carries the status
   line, what the app is doing right now, and the one button you reach for
   without looking - all of it pinned to the top, because in a car the phone is
   glanced at, not read. */
.practice-head {
  flex-direction: column;
  align-items: stretch;
  gap: var(--gap-sm);
  padding-top: var(--gap);
  padding-bottom: var(--gap);
}

/* Big, because "3 / 8 — Samba Reggae — Gopher Break" is read at arm's length
   from a phone in a cradle. Wraps rather than truncating: which part is playing
   is the whole point of the line. */
.practice-status {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1.25;
}

.practice-phase {
  margin: 0;
  font-size: var(--text-small);
  color: var(--text-dim);
}

.practice-meta {
  margin: 0;
  font-size: var(--text-small);
  color: var(--text-dim);
  text-align: right;
}

/* ---------- Practice: the playlist ---------- */

.practice-item { position: relative; }
.practice-item .row { border-top: 0; }
.practice-item .card-sub { margin-bottom: var(--gap-xs); }
.practice-item .btn-grid { margin-bottom: 0; }
.practice-item .field:last-of-type { margin-bottom: var(--gap-sm); }

/* The list position, so "3 / 8" on the status line finds its row at a glance. */
.practice-num {
  flex: 0 0 auto;
  min-width: 26px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

/* The item playing right now. A left edge rather than a fill: the card is mostly
   controls, and a filled card would fight the Stop button for attention. */
.practice-item.is-now {
  border-color: var(--accent);
  box-shadow: inset 4px 0 0 var(--accent);
}

.practice-item.is-now .practice-num { color: var(--accent); }

/* A control that cannot do anything yet - Play with an empty list, Move up on
   the first item. Dimmed rather than hidden, so the row does not reflow. */
.btn[disabled] {
  opacity: 0.4;
  cursor: default;
}
