/* ==========================================================================
   Sequencer — Compositoio
   Il mondo visivo è la tipografia a caratteri mobili: caratteri solidi di corpo
   uguale, serrati in fila, mandati in macchina e ripetuti identici.
   Il colore di ogni carattere viene dal suo centroide spettrale misurato.
   ========================================================================== */

@font-face {
  font-family: 'Archivo';
  src: url('../fonts/archivo-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   Token
   -------------------------------------------------------------------------- */

:root {
  /* Ground: nero d'inchiostro, non nero puro. Le tinte hanno bisogno
     di un fondo che le lasci accendere. */
  --ink-900: oklch(0.13 0.014 285);
  --ink-800: oklch(0.16 0.016 285);
  --ink-700: oklch(0.21 0.018 285);
  --ink-600: oklch(0.27 0.020 285);
  --ink-500: oklch(0.36 0.020 285);

  --paper: oklch(0.97 0.006 285);
  --paper-dim: oklch(0.78 0.014 285);
  --paper-faint: oklch(0.60 0.018 285);

  --alarm: oklch(0.70 0.19 25);

  /* Il carattere in riproduzione: la sua tinta viene assegnata a runtime. */
  --live: var(--paper);

  /* Un solo grottesco, neutro e leggibile, in due ruoli distinti per peso e
     corpo. Un display compresso rendeva i caratteri difficili da leggere. */
  --font-display: 'Archivo', system-ui, -apple-system, sans-serif;
  --font-text: 'Archivo', system-ui, -apple-system, sans-serif;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-sharp: cubic-bezier(0.3, 0, 0, 1);

  /* Scala tipografica: tre gradini distinti, rapporto ~1.3 tra l'uno e
     l'altro. Cinque corpi ravvicinati non fanno gerarchia, fanno rumore. */
  --size-meta: 0.8125rem;   /* 13px — didascalie, piede, indicazioni */
  --size-body: 1.0625rem;   /* 17px — corpo, riscontri, comandi */
  --size-lead: 1.375rem;    /* 22px — titoli di sezione, tasti della cassetta */

  --radius: 3px;
  --gutter: clamp(1rem, 4vw, 2.5rem);

  color-scheme: dark;
  accent-color: var(--live);
}

/* --------------------------------------------------------------------------
   Reset e superfici del browser
   -------------------------------------------------------------------------- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--ink-600) transparent;
  scrollbar-width: thin;
}

body {
  min-height: 100svh;
  background: var(--ink-900);
  color: var(--paper);
  font-family: var(--font-text);
  font-size: var(--size-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

::selection {
  background: var(--live);
  color: var(--ink-900);
}

:focus-visible {
  outline: 2px solid var(--live);
  outline-offset: 3px;
  border-radius: 2px;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--ink-600);
  border-radius: 99px;
  border: 3px solid var(--ink-900);
}

button { font: inherit; color: inherit; }

/* Sistema di icone: Lucide 1.34.0 (ISC). Tratto uniforme, mai riempimento,
   mai glifi Unicode al posto di un'icona disegnata. */
.icon {
  width: 1.25em;
  height: 1.25em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

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

/* --------------------------------------------------------------------------
   Fondale
   -------------------------------------------------------------------------- */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: image-set(
    url('../img/compositoio.webp') type('image/webp'),
    url('../img/compositoio.jpg') type('image/jpeg')
  );
  background-size: cover;
  background-position: 50% 12%;
  opacity: 0.5;
}

/* Il fondale si dissolve prima della zona dei comandi: sotto questa linea
   la pagina deve restare leggibile senza compromessi. */
.backdrop::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    color-mix(in oklab, var(--ink-900) 45%, transparent) 0%,
    color-mix(in oklab, var(--ink-900) 78%, transparent) 30%,
    var(--ink-900) 62%,
    var(--ink-900) 100%
  );
}

/* Da tablet in su il taglio verticale verrebbe sacrificato: subentra una
   inquadratura larga della stessa scena. */
@media (min-width: 48rem) {
  .backdrop {
    background-image: image-set(
      url('../img/compositoio-wide.webp') type('image/webp'),
      url('../img/compositoio-wide.jpg') type('image/jpeg')
    );
    background-position: 50% 42%;
    opacity: 0.38;
  }
}

/* --------------------------------------------------------------------------
   Onda: campioni reali dall'AnalyserNode.
   Vive dietro la riga composta, non dietro la pagina: appartiene ai caratteri
   che stanno suonando, e lì non copre testo che vada letto.
   -------------------------------------------------------------------------- */

.wave {
  position: absolute;
  /* Deborda oltre la fascia e oltre le gronde della pagina: il segnale non
     è contenuto in un riquadro, attraversa lo schermo. */
  inset: -1.5rem calc(-1 * var(--gutter) - 1.5rem);
  width: calc(100% + 2 * var(--gutter) + 3rem);
  height: calc(100% + 3rem);
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.9s var(--ease);
  /* Sfuma su entrambi gli assi: l'onda non deve finire di netto contro il
     resto della pagina né risalire sul testo che sta sopra. */
  mask-image:
    linear-gradient(to right, transparent, #000 14%, #000 86%, transparent),
    linear-gradient(to bottom, transparent, #000 26%, #000 74%, transparent);
  mask-composite: intersect;
  -webkit-mask-composite: source-in;
}

body.is-playing .wave { opacity: 1; }

/* --------------------------------------------------------------------------
   Impalcatura
   -------------------------------------------------------------------------- */

.app {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  min-height: 100svh;
  max-width: 68rem;
  margin-inline: auto;
  padding:
    calc(env(safe-area-inset-top) + clamp(1.5rem, 5vw, 3rem))
    var(--gutter)
    calc(env(safe-area-inset-bottom) + 1.5rem);
  gap: clamp(1.75rem, 5vw, 3rem);
}

.main {
  display: flex;
  flex-direction: column;
  gap: clamp(1.75rem, 5vw, 3rem);
  flex: 1;
}

/* --------------------------------------------------------------------------
   Testata
   -------------------------------------------------------------------------- */

.wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  text-shadow: 0 2px 24px color-mix(in oklab, var(--ink-900) 80%, transparent);
  font-size: clamp(2.25rem, 11vw, 4.5rem);
  line-height: 0.86;
  letter-spacing: -0.035em;
  text-transform: lowercase;
}

.tagline {
  margin-top: 0.85rem;
  max-width: 34ch;
  color: var(--paper-dim);
  font-size: var(--size-body);
  text-wrap: balance;
}

/* --------------------------------------------------------------------------
   Composizione
   -------------------------------------------------------------------------- */

.composer {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.field {
  position: relative;
  display: flex;
  align-items: center;
  background: var(--ink-800);
  border: 1px solid var(--ink-600);
  border-radius: var(--radius);
  transition: border-color 0.3s var(--ease), background-color 0.3s var(--ease);
}

.field:focus-within {
  border-color: var(--live);
  background: var(--ink-700);
}

.field:has(.field__input[aria-invalid='true']) { border-color: var(--alarm); }

.field__input {
  flex: 1;
  min-width: 0;
  padding: 0.9rem 3rem 0.9rem 1rem;
  background: none;
  border: 0;
  color: var(--paper);
  caret-color: var(--live);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.5rem, 7vw, 2.25rem);
  line-height: 1.2;
  letter-spacing: 0.06em;
  text-transform: lowercase;
}

.field__input::placeholder {
  color: var(--paper-dim);
  opacity: 1;
}

.field__input:focus { outline: none; }

.field__clear {
  position: absolute;
  right: 0.4rem;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  background: none;
  border: 0;
  border-radius: var(--radius);
  color: var(--paper-faint);
  cursor: pointer;
  transition: color 0.2s var(--ease), background-color 0.2s var(--ease);
}

.field__clear:hover { color: var(--paper); background: var(--ink-600); }

.field__clear .icon { width: 20px; height: 20px; }

/* Comando di trasporto */

.transport {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  min-height: 56px;
  padding: 0 1.5rem;
  background: var(--paper);
  border: 0;
  border-radius: var(--radius);
  color: var(--ink-900);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--size-body);
  letter-spacing: 0.01em;
  cursor: pointer;
  transition:
    background-color 0.35s var(--ease),
    color 0.35s var(--ease),
    transform 0.15s var(--ease-sharp);
}

.transport:hover { background: var(--live); }
.transport:active { transform: scale(0.985); }

.transport:disabled {
  background: var(--ink-700);
  color: var(--paper-faint);
  cursor: not-allowed;
}

body.is-playing .transport {
  background: var(--live);
  color: var(--ink-900);
}

.transport__icon {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
}

.transport__icon .icon {
  grid-area: 1 / 1;
  width: 22px;
  height: 22px;
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}

.transport__stop { opacity: 0; transform: scale(0.7); }

body.is-playing .transport__play { opacity: 0; transform: scale(0.7); }
body.is-playing .transport__stop { opacity: 1; transform: scale(1); }

/* Riscontro */

.feedback {
  min-height: 1.5rem;
  margin-top: 0.75rem;
  color: var(--paper-dim);
  font-size: var(--size-body);
  text-wrap: pretty;
}

.feedback[data-tone='error'] { color: var(--alarm); }
.feedback[data-tone='live'] { color: var(--live); }

/* --------------------------------------------------------------------------
   Compositoio: la riga di caratteri serrati
   -------------------------------------------------------------------------- */

.stick {
  position: relative;
  min-height: clamp(3.5rem, 15vw, 4.75rem);
  /* La fascia è alta anche a riposo: l'onda ha bisogno di aria sopra e sotto
     i caratteri, e un'altezza che cambia al play sposterebbe i tasti sotto
     il dito proprio mentre lo si sta usando. */
  padding-block: clamp(1.5rem, 6vw, 2.75rem);
}

/* Una riga sola, che scorre. Andando a capo l'altezza cresceva con la
   lunghezza della sequenza e spingeva la cassetta giù di oltre 200 px: i tasti
   scappavano da sotto il dito proprio mentre li si stava usando. Un
   compositoio, del resto, è una riga. */
.stick__row {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: nowrap;
  gap: 2px;
  padding: 0 0 0.5rem;
  margin: 0;
  list-style: none;
  min-height: calc(clamp(3.5rem, 15vw, 4.75rem) + 0.5rem);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-behavior: smooth;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-600) transparent;
  /* I caratteri sfumano sui bordi: si vede che la riga prosegue. */
  mask-image: linear-gradient(to right, transparent, #000 2%, #000 98%, transparent);
}

.stick__row::-webkit-scrollbar { height: 4px; }
.stick__row::-webkit-scrollbar-track { background: transparent; }
.stick__row::-webkit-scrollbar-thumb { background: var(--ink-600); border-radius: 99px; }

.slug { flex: none; }

.stick__empty {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  margin: 0;
  color: var(--paper-faint);
  font-size: var(--size-body);
  pointer-events: none;
}

.stick__row:not(:empty) + .stick__empty { display: none; }

/* Il carattere: un blocco solido, come un pezzo di piombo */
.slug {
  --tint: var(--paper-faint);
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(2.75rem, 12vw, 3.75rem);
  height: clamp(3.5rem, 15vw, 4.75rem);
  overflow: hidden;
  background: color-mix(in oklab, var(--ink-800) 52%, transparent);
  backdrop-filter: blur(10px) saturate(1.3);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--paper) 8%, transparent);
  border-radius: 2px;
  color: var(--paper-dim);
  grid-template-areas: 'char';
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.875rem, 8vw, 2.75rem);
  line-height: 1;
  text-transform: lowercase;
  transition: color 0.35s var(--ease), background-color 0.35s var(--ease);
}

/* L'inchiostrazione: la tinta sale dal basso nell'arco esatto del suono. */
.slug::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--tint);
  transform: scaleY(var(--fill, 0));
  transform-origin: bottom;
  transition: none;
}

/* Il carattere è disegnato due volte: la copia scura è ritagliata esattamente
   all'altezza raggiunta dall'inchiostro, così resta leggibile sia sopra il
   fondo scuro sia sopra la tinta piena, senza affidarsi a blend mode. */
.slug__char {
  grid-area: 1 / 1;
  position: relative;
  z-index: 1;
  transition: color 0.35s var(--ease);
}

.slug__char--ink {
  z-index: 2;
  color: var(--ink-900);
  clip-path: inset(calc(100% - var(--fill, 0) * 100%) 0 0 0);
}

.slug[data-live='true'] {
  background: color-mix(in oklab, var(--ink-700) 82%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--tint) 30%, transparent);
}
.slug[data-live='true'] .slug__char--base { color: var(--paper); }

/* Il carattere non disponibile resta leggibile ma visibilmente escluso:
   superficie spenta, tinta assente, contorno tratteggiato. Nessuna texture
   decorativa a coprire il vuoto. */
.slug[data-missing='true'] {
  background: none;
  backdrop-filter: none;
  box-shadow: none;
  outline: 1px dashed var(--ink-500);
  outline-offset: -1px;
  color: var(--paper-faint);
}

.slug[data-missing='true'] .slug__char--ink { display: none; }

/* --------------------------------------------------------------------------
   Cassetta dei caratteri
   -------------------------------------------------------------------------- */

.case__title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 1rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--size-lead);
  letter-spacing: -0.02em;
}

.case__icon {
  width: 1.1em;
  height: 1.1em;
  color: var(--paper-faint);
  align-self: center;
}

.case__hint {
  color: var(--paper-faint);
  font-family: var(--font-text);
  font-weight: 400;
  font-size: var(--size-meta);
  letter-spacing: 0;
}

.case__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(3.25rem, 1fr));
  gap: 0.5rem;
}

.sort {
  --tint: var(--paper-faint);
  /* Altezza nominale del tasto: serve al filetto di piede per ricavare la
     propria scala a riposo senza animare una proprietà di layout. */
  --sort-height: 56;
  position: relative;
  display: grid;
  place-items: center;
  min-height: 56px;
  padding: 0.5rem 0.25rem 0.75rem;
  background: var(--ink-800);
  border: 1px solid var(--ink-700);
  border-radius: var(--radius);
  color: var(--paper);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--size-lead);
  line-height: 1;
  text-transform: lowercase;
  cursor: pointer;
  overflow: hidden;
  transition:
    border-color 0.25s var(--ease),
    background-color 0.25s var(--ease),
    transform 0.12s var(--ease-sharp);
}

/* Il filetto di piede porta la tinta timbrica: identifica il carattere
   anche quando è a riposo. */
.sort::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 100%;
  background: var(--tint);
  opacity: 0.85;
  transform: scaleY(calc(3 / var(--sort-height, 56)));
  transform-origin: bottom;
  transition: transform 0.3s var(--ease), opacity 0.3s var(--ease);
}

.sort:hover {
  background: var(--ink-700);
  border-color: var(--ink-500);
}

.sort:active { transform: scale(0.96); }

.sort[data-live='true'] {
  border-color: var(--tint);
  background: var(--ink-700);
}

.sort[data-live='true']::after { transform: scaleY(1); opacity: 0.22; }

.sort[data-missing='true'] {
  opacity: 0.4;
  cursor: not-allowed;
}

.sort[data-missing='true']::after { background: var(--ink-500); }

/* --------------------------------------------------------------------------
   Piede
   -------------------------------------------------------------------------- */

.footer {
  padding-top: 0.5rem;
  border-top: 1px solid var(--ink-700);
}

.footer__note {
  color: var(--paper-faint);
  font-size: var(--size-meta);
  text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   Caricamento
   -------------------------------------------------------------------------- */

.boot {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: var(--ink-900);
  transition: opacity 0.6s var(--ease), visibility 0.6s;
}

.boot::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: image-set(
    url('../img/compositoio.webp') type('image/webp'),
    url('../img/compositoio.jpg') type('image/jpeg')
  );
  background-size: cover;
  background-position: 50% 30%;
  opacity: 0.55;
  mask-image: radial-gradient(120% 80% at 50% 42%, transparent 18%, #000 72%);
}

.boot__inner { position: relative; }

.boot[hidden] {
  display: grid;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.boot__inner {
  width: min(24rem, 100%);
  text-align: center;
}

.boot__word {
  margin-bottom: 1.75rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 9vw, 3rem);
  line-height: 1;
  letter-spacing: -0.035em;
  text-transform: lowercase;
}

.boot__meter {
  height: 3px;
  background: var(--ink-700);
  overflow: hidden;
}

.boot__fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--paper);
  transform: scaleX(var(--progress, 0));
  transform-origin: left;
  transition: transform 0.4s var(--ease);
}

.boot__status {
  margin-top: 1rem;
  color: var(--paper-dim);
  font-size: var(--size-body);
  font-variant-numeric: tabular-nums;
}

.boot__retry {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.25rem;
  min-height: 44px;
  padding: 0 1.25rem;
  background: none;
  border: 1px solid var(--ink-500);
  border-radius: var(--radius);
  color: var(--paper);
  cursor: pointer;
  transition: border-color 0.25s var(--ease), background-color 0.25s var(--ease);
}

.boot__retry:hover { background: var(--ink-700); border-color: var(--paper-dim); }

/* --------------------------------------------------------------------------
   Da tablet in su
   -------------------------------------------------------------------------- */

@media (min-width: 48rem) {
  .composer {
    flex-direction: row;
    align-items: stretch;
  }

  .field { flex: 1; }

  .transport { min-width: 11rem; }

  .case__grid {
    grid-template-columns: repeat(13, 1fr);
  }
}

/* --------------------------------------------------------------------------
   Movimento ridotto
   L'app è basata sul movimento: la variante ridotta resta informativa.
   L'inchiostrazione diventa uno stato netto, l'onda si ferma.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .stick__row { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .slug::before { transform: scaleY(var(--fill-step, 0)); }
  .wave { display: none; }
}
