/* One sheet of the player's design system (tickets/T19-room-for-two.md).
   style.css keeps the fonts, the tokens and the reset; every area has its
   own file from there, linked in index.html in this order — the cascade is
   the order of the <link>s, not of a bundler. A ticket works in its sheet.
   No @import: it serialises the requests, which is the cost being avoided. */

/* ---------- Stage: the mood, and what is playing ---------- */
.stage {
  flex: 1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  /* The least air between the mood and the record: 88 (64 until 01/11), not the gutter's 24 —
     asked 25/09, the two read as one block when the mood was fitted up to the
     sleeve. programme.js reads this gap when it sizes the mood. */
  gap: 88px;
  /* 116 = 256 - 140 in the frame — the composition, not a rung. On a short
     window it is the first thing to give, down to 32, so the page still fits. */
  margin-top: clamp(32px, 100dvh - 644px, 116px);
  min-height: 128px;   /* the sleeve: 128, so the credits (48) centre on a whole pixel */
}
/* The headline's size is the room the stage leaves it (player/programme.js,
   `fit`), capped by --headline-max: 100 on the desktop frame, 40 on the small
   one and on a short window (responsive.css). Without the script, the cap. */
.stage { --headline-max: 100px; }
.headline {
  flex: none;   /* sized to fit already: the record gives way, not the mood */
  font: 400 var(--headline-size, var(--headline-max))/max(0.88em, 40px) var(--font-display);
  font-style: italic;
  letter-spacing: var(--headline-track, -0.03em);
  white-space: nowrap;
  /* Too long for one line at 40 (programme.js): two balanced lines, in the
     width the record leaves. */
  max-width: var(--headline-wrap, none);
  text-wrap: balance;
  /* Signifier's left side bearing, so the ink lands on the margin, not the box.
     It is the first letter's: 0.052em for D and F, 0.068em for N and A — app.js
     measures it when it writes the mood. In em, so the 40px steps follow. */
  margin-left: var(--lsb, 0.052em);
}
/* The record gives way when the mood has reached its floor (programme.js):
   the credits column shrinks and its title scrolls, rather than the row
   running off the page. */
.now { display: flex; align-items: center; gap: 24px; min-width: 0; }
.cover {
  flex: none;
  position: relative;
  overflow: hidden;
  width: 128px;
  height: 128px;
  border-radius: var(--radius-sleeve);
  background: var(--sleeve-ghost);
}
/* The sleeve's own clip for the picture: .cover lets its contour out (ring.css),
   and a label still turning upright as it squares off would overhang (T126). */
.cover__well { position: absolute; inset: 0; overflow: hidden; border-radius: inherit; }
/* The sleeve, over the ghost that stays as placeholder and while it loads. */
.cover__art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: opacity 0.4s ease; }
.cover__art[hidden] { display: block; opacity: 0; }
/* The wheel switched off (WHEEL in app.js): the sleeve is a picture, and the
   hand over it should not promise a door. */
.cover:disabled { cursor: default; }
/* And when it is a door, it says so (tickets/T27-the-hand-finds-the-key.md).
   The largest target on the page answered nothing at all under the pointer.
   A ring rather than a lift: the sleeve is a picture and a picture that grows
   under the mouse drags the eye off what it is showing. */
@media (hover: hover) {
  .cover:not(:disabled) { transition: box-shadow 160ms ease; }
  .cover:not(:disabled):hover { box-shadow: 0 0 0 2px var(--primary); }
}
/* A fixed width, so the sleeve does not slide with the title. 288 holds all
   but the longest of the rotation next to the two keys; those scroll, slowly.
   Two columns: the credits, and the card's keys at the end of the title's line
   (tickets/T45-le-disque-passe-devant.md). No row gap: the rows' own heights
   are the rhythm, and 4 more on top of them pushed the artist off the title. */
.track { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 8px; width: 288px; min-width: 0; flex: 0 1 auto; }
/* White-space opens only when programme.js has set a wrap width. */
.headline[style*="--headline-wrap"] { white-space: normal; }
.track > .track__row { grid-column: 1; }
.track__row {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 8px;
  height: 24px;
  font-size: 12px;
  line-height: 16px;
  text-transform: uppercase;
  white-space: nowrap;
}
.track__text { flex: 1; min-width: 0; overflow-x: clip; }
.track__scroll { display: block; width: max-content; }
.track__scroll.is-scrolling { animation: marquee var(--travel) linear 2 alternate; }  /* out and back, then the other line's turn */
@keyframes marquee {
  0%, 18% { transform: none; }
  82%, 100% { transform: translateX(var(--shift)); }
}

/* The record's own credits say what is on the air, and nothing else: no disc,
   no mic in front of them. The title in `Body/UI Large` (16/20), the artist in
   `Body/Paragraph` (14/20) on its 24 row, straight under it (T45). */
.now .track__row > .icon { display: none; }
.now .track__row { font-size: 14px; line-height: 20px; }
.now .track__row:first-child { align-self: center; height: 20px; font-size: 16px; }
@media (min-width: 641px) { .now .track__row:first-child { font-size: 14px; } }   /* desktop: the title down to 14 (asked 24/09); the phone keeps 16 */

/* ---------- Actions: a led above each key ---------- */
/* The foot of the page, under the rail: the keys close it (T45). */
.actions { order: 3; display: flex; gap: 4px; margin-top: 36px; }
.action { display: grid; row-gap: 12px; justify-items: center; }
.action[hidden] { display: none; }   /* the desk's door (T75): only for whoever works the desk */
.action > .led { margin-bottom: 0; }   /* led 8, gap 12, key: 92 */
/* A key you push. The face sits on a plinth (the dark band at the bottom):
   4px at rest, 6 when the pointer lifts it, 1 when it is pressed — and the
   label sinks with it. Mirrors Bouton radio kind × state in Figma. */
.btn {
  --plinth-ink: rgba(0, 0, 0, 0.32);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  height: var(--control-height);
  border-radius: var(--radius);
  font-size: 12px;
  line-height: 16px;
  text-transform: uppercase;
  box-shadow: 0 4px 4px rgba(0, 0, 0, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.35);
  overflow: hidden;
  transition: box-shadow 120ms ease;
}
.btn::before, .btn::after { content: ""; position: absolute; inset: 0; pointer-events: none; }
/* The plinth is a 6px band scaled from its bottom edge: 4/6 at rest, 6/6 lifted,
   1/6 pressed — transform, not height, so nothing reflows. Same for the label. */
.btn::after { top: auto; height: 6px; background: var(--plinth-ink); transform: scaleY(0.667); transform-origin: bottom; transition: transform 120ms ease; }
.btn > * { transition: transform 80ms ease; }
.btn::before { background: transparent; transition: background 120ms ease; }
@media (hover: hover) {
  .btn:hover::after { transform: scaleY(1); }
  .btn:hover { box-shadow: 0 8px 10px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.5); }
  .btn:hover::before { background: rgba(255, 255, 255, 0.06); }
}
/* Pressed — and a key that is on STAYS pressed, it is not a flash under the
   pointer: the transport key while it plays, the heart once it is given, the
   tab whose list is showing (Bouton radio · pressed, 1327:466). After the
   hover block on purpose, so the pointer never lifts a key that is down. */
.btn:active::after, .btn[aria-pressed="true"]::after, .btn[aria-selected="true"]::after { transform: scaleY(0.167); }
.btn:active > *, .btn[aria-pressed="true"] > *, .btn[aria-selected="true"] > * { transform: translateY(3px); }
.btn:active, .btn[aria-pressed="true"], .btn[aria-selected="true"] {
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.08), inset 0 2px 4px rgba(0, 0, 0, 0.18);
}
.btn:active::before, .btn[aria-pressed="true"]::before, .btn[aria-selected="true"]::before { background: rgba(0, 0, 0, 0.08); }
/* A key with nothing to do is off: no relief, no lift — it is there, it waits. */
.btn:disabled { opacity: 0.4; box-shadow: none; }
.btn:disabled::after { transform: scaleY(0.667); }
.btn--primary { width: 193px; background: var(--primary-solid); color: var(--on-primary); }
.btn--secondary { --plinth-ink: rgba(0, 0, 0, 0.16); width: 85px; background: var(--control); color: var(--ink); }
/* The small key (tickets/T38-la-fiche-du-morceau.md): 32 high (28 until T71) and as wide as
   what it carries. A HEIGHT and a WIDTH, and nothing else — the relief, the
   plinth, the way it sinks when it is pressed all stay .btn's (Bouton radio,
   1327:466). After --primary/--secondary on purpose: it is their fixed width
   it has to undo, and these three have the same specificity. */
.btn--small { width: auto; height: 32px; padding: 0 12px; }
/* …except how far the glyph goes down: the 3 of .btn is measured on a 40px key,
   and on a 32 it lands the drawing visibly low. 2 keeps the sink, and centres it. */
.btn--small:active > *, .btn--small[aria-pressed="true"] > *, .btn--small[aria-selected="true"] > * { transform: translateY(2px); }
/* ONE SMALL KEY, 32, MOUSE OR FINGER (tickets/T71-trente-deux-partout.md).
   T64 gave a finger the big key's 48 and the card filled up with slabs; now
   the drawing stays 32 and a finger gets its 48 from the ZONE: 8 of veil above
   and below, 2 on the sides — half the 4 between two keys, so neighbours never
   share a tap. The veil is .btn's ::before; it must catch the tap (it is
   `pointer-events: none` for the mouse's hover) and stop being clipped by the
   face, whose plinth then rounds its own corners. No hover on a touch screen,
   so nothing to paint on it. Every small key: .btn--small, the tabs' .btn--s,
   and the squares that take their size from them. */
@media (pointer: coarse) {
  :is(.btn--small, .btn--s) { overflow: visible; }
  :is(.btn--small, .btn--s)::before { inset: -8px -2px; background: none; pointer-events: auto; }
  /* The haptic switch answers on the same 48 as the tap (page.css, T73). */
  :is(.btn--small, .btn--s) > [data-haptic-trigger] { inset: -8px -2px !important; width: auto !important; height: auto !important; }
  :is(.btn--small, .btn--s)::after { border-radius: 0 0 var(--radius) var(--radius); }
  /* The veil now spans the whole zone, so the pressed shade can't live on it —
     it painted a grey 48 around a held heart. It goes onto the face, as an
     inset shadow, which the face's corners round. */
  :is(.btn--small, .btn--s):is(:active, [aria-pressed="true"], [aria-selected="true"])::before { background: none; }
  :is(.btn--small, .btn--s):is(:active, [aria-pressed="true"], [aria-selected="true"]) {
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.08), inset 0 2px 4px rgba(0, 0, 0, 0.18), inset 0 0 0 32px rgba(0, 0, 0, 0.08);
  }
}
.btn--icon { padding: 0 12px; }   /* no word: 12 + a 16px glyph + 12 = 40, the frame's key */

/* The card's own key row, under the credits. One key in it today — the one
   that opens the record's card — since the heart went back down to the row of
   three (tickets/T42-le-coeur-revient-dans-la-rangee.md). It keeps the name and
   the rule: NOT `.keys`, that name belongs to the column's key row
   (css/more.css), which centres them and is linked after this sheet. */
/* Always shown, mouse included: they no longer wait for the pointer (T42) —
   a key one has to go looking for is a key half the listeners never find. */
.track__keys { display: flex; gap: 4px; grid-column: 2; grid-row: 1; }
/* Square keys, the relief of every other key (Figma 1501:7920): the small
   key's 32 (T71; 28 from T64 to T70). */
.track__keys .btn { width: 32px; height: 32px; padding: 0; }
/* Its touch zone is the small key's (above): 32 drawn, 48 reached (T71). */

.btn .icon--stop, .btn[aria-pressed="true"] .icon--play { display: none; }
.btn[aria-pressed="true"] .icon--stop { display: block; }
/* The same swap for a key whose whole drawing IS its state: outline when it is
   off, solid when it is on. The row of three says it with a led over the key
   instead, so none of the three carries these classes — only the heart at the
   end of the title does (tickets/T45-le-disque-passe-devant.md). */
/* The card's key is on while its card is open (aria-expanded): the same solid
   drawing, so the three keys of the title say "on" one way (T64). */
.btn .icon--on, .btn[aria-pressed="true"] .icon--off, .btn[aria-expanded="true"] .icon--off { display: none; }
.btn[aria-pressed="true"] .icon--on, .btn[aria-expanded="true"] .icon--on { display: block; }
.action.is-on > .led { background: var(--primary); }
