/* ==========================================================================
   Tuna — guitar tuner

   Visual direction: the inside of a guitar case. Deep green felt, aged nickel
   for the wire and the type, brass for anything active. The six strings along
   the bottom are drawn at true relative gauges and sit in the thumb zone,
   because on a phone that is where a hand actually reaches.
   ========================================================================== */

:root {
  /* Case interior */
  --case-deep:  #0A1618;
  --case:       #0F2224;
  --felt:       #17383A;
  --felt-edge:  #21504F;

  /* Hardware */
  --nickel:      #C6D3D0;
  --nickel-dim:  #7C918E;
  --nickel-faint:#4A5F5E;
  --brass:       #C9A227;
  --brass-bright:#E8C24E;
  --mint:        #7FE3C4;

  /* Numerals and readouts. Tabular figures are not a stylistic preference
     here: the deviation updates ~40 times a second, and proportional digits
     would make the whole line twitch sideways on every change. */
  --font-data: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  /* ui-rounded resolves to SF Pro Rounded on Apple platforms — a softer,
     instrument-dial letterform for the note itself. */
  --font-display: ui-rounded, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;

  --edge: 0 1px 0 rgba(255, 255, 255, 0.045);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--case-deep);
}

body {
  font-family: var(--font-ui);
  color: var(--nickel);
  /* Stops the double-tap-to-zoom delay and the grey flash on tap in Safari. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior: none;
  overflow: hidden;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── Shell ───────────────────────────────────────────────────────────── */

.app {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh; /* survives Safari's collapsing toolbar */
  padding:
    calc(env(safe-area-inset-top) + 0.75rem)
    calc(env(safe-area-inset-right) + 1.25rem)
    calc(env(safe-area-inset-bottom) + 0.75rem)
    calc(env(safe-area-inset-left) + 1.25rem);
  /* A soft pool of light behind the readout, like a case lid opened under a
     lamp — keeps the flat background from reading as a plain dark theme. */
  background:
    radial-gradient(120% 55% at 50% 6%, #16302F 0%, rgba(10, 22, 24, 0) 68%),
    var(--case-deep);
}

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

.topbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex: none;
}

.wordmark {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--nickel-dim);
}

.status {
  margin: 0;
  font-family: var(--font-data);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--nickel-faint);
  display: flex;
  align-items: center;
  gap: 0.5em;
}

.status::before {
  content: "";
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

[data-state="listening"] .status { color: var(--brass); }
[data-state="listening"] .status::before { animation: pulse 2s var(--ease) infinite; }
[data-state="error"] .status { color: var(--nickel-dim); }

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.25; }
}

/* ── Readout ─────────────────────────────────────────────────────────── */

.readout {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(0.5rem, 2.5vh, 1.25rem);
  min-height: 0;
}

.note {
  display: flex;
  align-items: flex-start;
  line-height: 0.82;
  color: var(--nickel);
  transition: color 260ms var(--ease);
}

.note-letter {
  font-family: var(--font-display);
  font-size: clamp(4.5rem, 24vw, 8.5rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.note-octave {
  font-family: var(--font-data);
  font-size: clamp(1rem, 4.5vw, 1.5rem);
  font-weight: 500;
  color: var(--nickel-faint);
  margin-top: 0.35em;
  margin-left: 0.15em;
}

[data-tuned="true"] .note { color: var(--mint); }
[data-signal="false"] .note-letter { color: var(--nickel-faint); }

/* ── Meter ───────────────────────────────────────────────────────────── */

.meter {
  width: 100%;
  max-width: 26rem;
  height: clamp(3.5rem, 11vh, 4.875rem);
  flex: none;
  overflow: visible;
}

.meter-ticks line {
  stroke: var(--nickel-faint);
  stroke-width: 1;
}

.meter-ticks line.major { stroke: var(--nickel-dim); }

.meter-ticks text {
  fill: var(--nickel-faint);
  font-family: var(--font-data);
  font-size: 9px;
  letter-spacing: 0.06em;
  text-anchor: middle;
}

/* The ±5 cent target. This is the thing you are aiming at, so it is drawn as a
   filled band rather than a hairline outline — at arm's length a dashed 1px
   rectangle on a dark background is invisible. */
.meter-zone {
  fill: color-mix(in srgb, var(--nickel) 9%, transparent);
  stroke: var(--nickel-dim);
  stroke-width: 1;
  transition: stroke 260ms var(--ease), fill 260ms var(--ease);
}

[data-tuned="true"] .meter-zone {
  stroke: var(--mint);
  fill: color-mix(in srgb, var(--mint) 22%, transparent);
}

.meter-centre {
  stroke: var(--nickel);
  stroke-width: 1.5;
}

.meter-marker {
  fill: var(--brass);
  stroke: var(--brass);
  stroke-width: 2;
  stroke-linecap: round;
  transition: opacity 200ms var(--ease);
}

[data-tuned="true"] .meter-marker { fill: var(--mint); stroke: var(--mint); }
[data-signal="false"] .meter-marker { opacity: 0.18; }

/* ── Guidance + data ─────────────────────────────────────────────────── */

.guidance {
  margin: 0;
  font-family: var(--font-ui);
  font-size: clamp(0.9375rem, 4vw, 1.125rem);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brass);
  text-align: center;
  min-height: 1.4em;
  transition: color 260ms var(--ease);
}

[data-tuned="true"] .guidance { color: var(--mint); }
[data-signal="false"] .guidance { color: var(--nickel-faint); }

.data {
  display: flex;
  gap: clamp(1.25rem, 7vw, 2.75rem);
  margin: 0;
  flex: none;
}

.data-cell { text-align: center; }

.data dt {
  font-family: var(--font-ui);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--nickel-faint);
  margin-bottom: 0.35em;
}

.data dd {
  margin: 0;
  font-family: var(--font-data);
  font-size: clamp(0.9375rem, 4.2vw, 1.125rem);
  font-variant-numeric: tabular-nums;
  color: var(--nickel);
}

.data dd i {
  font-style: normal;
  font-size: 0.75em;
  color: var(--nickel-faint);
}

/* ── Strings ─────────────────────────────────────────────────────────── */

.strings {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-bottom: 0.75rem;
}

.string {
  /* A row, not a pill: the string *is* the control. */
  display: grid;
  grid-template-columns: 2.5rem 1fr 2.75rem;
  align-items: center;
  gap: 0.75rem;
  /* 44px minimum target height, per Apple's guidance. */
  min-height: 2.75rem;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.string-name {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
  color: var(--nickel-faint);
  transition: color 200ms var(--ease);
}

.string-name i {
  font-family: var(--font-data);
  font-style: normal;
  font-size: 0.6em;
  font-weight: 500;
  opacity: 0.65;
  vertical-align: 0.35em;
  margin-left: 0.08em;
}

/* Gauges stay visible on every row: they are what separates the two E strings,
   and one number appearing on only the active row reads as a rendering fault. */
.string-gauge {
  font-family: var(--font-data);
  font-size: 0.625rem;
  text-align: right;
  color: var(--nickel-faint);
  opacity: 0.45;
  font-variant-numeric: tabular-nums;
  transition: color 200ms var(--ease), opacity 200ms var(--ease);
}

.string-wire {
  display: block;
  width: 100%;
  height: 1.75rem;
  overflow: visible;
}

.string-wire path {
  fill: none;
  stroke: var(--nickel-faint);
  stroke-linecap: round;
  transition: stroke 220ms var(--ease);
}

/* The winding on the three wound strings — a fine overlay, barely there,
   but it is the difference between six lines and six *strings*. */
.string-wire path.winding {
  stroke: var(--case-deep);
  stroke-dasharray: 0.8 2.2;
  opacity: 0.55;
}

/* "Active" is the string currently sounding — set by detection, whether it was
   chosen automatically or pinned. */
.string[data-active="true"] .string-name { color: var(--brass); }
.string[data-active="true"] .string-gauge { opacity: 1; }
.string[data-active="true"] .string-wire path { stroke: var(--brass); }

/* "Pressed" is the string the user pinned, which is a different thing: it stays
   marked while silent, so the dot answers "why is it only listening for D?" */
.string[aria-pressed="true"] .string-name {
  color: var(--brass);
  position: relative;
}

.string[aria-pressed="true"] .string-name::before {
  content: "";
  position: absolute;
  left: -0.75rem;
  top: 50%;
  width: 0.3125rem;
  height: 0.3125rem;
  margin-top: -0.15625rem;
  border-radius: 50%;
  background: currentColor;
}

.string[data-tuned="true"] .string-name,
.string[data-tuned="true"] .string-gauge { color: var(--mint); }
.string[data-tuned="true"] .string-wire path { stroke: var(--mint); }

.string:focus-visible {
  outline: 2px solid var(--brass-bright);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ── Controls ────────────────────────────────────────────────────────── */

.controls {
  flex: none;
  display: flex;
  gap: 0.625rem;
}

.btn {
  min-height: 3rem;
  padding: 0 1.5rem;
  border-radius: 0.625rem;
  border: 1px solid transparent;
  font-family: var(--font-ui);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: background-color 180ms var(--ease), color 180ms var(--ease),
              border-color 180ms var(--ease), transform 120ms var(--ease);
}

.btn:active { transform: scale(0.98); }

.btn:focus-visible {
  outline: 2px solid var(--brass-bright);
  outline-offset: 2px;
}

.btn-primary {
  flex: 1;
  background: var(--brass);
  color: #16120A;
  box-shadow: var(--edge);
}

.btn-ghost {
  flex: none;
  background: var(--felt);
  border-color: var(--felt-edge);
  color: var(--nickel-dim);
}

.btn-ghost[aria-pressed="true"] {
  color: var(--brass);
  border-color: color-mix(in srgb, var(--brass) 45%, transparent);
  background: color-mix(in srgb, var(--brass) 10%, var(--felt));
}

[data-state="listening"] .btn-primary {
  background: transparent;
  border-color: var(--felt-edge);
  color: var(--nickel-dim);
}

/* ── Notices ─────────────────────────────────────────────────────────── */

.notice {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.875rem;
  padding: 2rem;
  text-align: center;
  background: color-mix(in srgb, var(--case-deep) 94%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.notice[hidden] { display: none; }

.notice-static { position: fixed; }

.notice-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--nickel);
}

.notice-body {
  margin: 0;
  max-width: 26rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--nickel-dim);
}

.notice .btn { margin-top: 0.5rem; }

/* ── Landscape and larger screens ────────────────────────────────────── */

@media (min-width: 40rem) {
  .app { max-width: 34rem; margin-inline: auto; }
}

/* Short viewports — phone in landscape. Drop the least load-bearing rows
   rather than letting the strings scroll out of reach. */
@media (max-height: 32rem) and (orientation: landscape) {
  .note-letter { font-size: clamp(3rem, 13vh, 4.5rem); }
  .data { display: none; }
  .readout { gap: 0.375rem; }
  .string { min-height: 2.25rem; }
  .string-wire { height: 1.25rem; }
}

/* ── Motion preferences ──────────────────────────────────────────────── */

/* The travelling wave is switched off in tuner.js when this is set; the string
   still shows deviation as a static bow, so no information is lost. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
