/* ONEKEYTALKIE — Teenage Engineering flavoured skin on top of Ambient CSS.
   Ambient owns the light, the shadows and the material; this file owns the
   layout, the palette (via --amb-albedo) and the typography. */

:root {
  /* The scene: a soft key from the top-left, as on a desk lamp. */
  --amb-light-hue: 40;
  --amb-light-saturation: 12%;
  --amb-key-light-intensity: 0.82;
  --amb-fill-light-intensity: 0.66;

  --panel: #dcd8d0;              /* warm aluminium */
  --key-black: #1d1c1a;          /* rubber utility keys */
  --orange: #ff5c1a;
  --ink: #1b1a18;
  --room: #0d0d0f;
  --screen-ink: #ffb34d;

  --sans: "Helvetica Neue", Inter, system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  padding: 28px 16px 48px;
  background:
    radial-gradient(120% 80% at 30% 0%, #26262b 0%, var(--room) 62%);
  color: var(--ink);
  font-family: var(--sans);
  display: grid;
  /* Centred across, pinned to the top — NOT centred vertically. Centring means
     every change in height moves everything: opening the radio drawer would
     shove the walkie and the pad upward to keep the middle in the middle. */
  place-items: start center;
  align-content: start;
  -webkit-user-select: none; user-select: none;
  overscroll-behavior: none;
}

/* Tiny stencilled labels — the TE signature. */
.tag, .micro, .model, .brand, .baseline, .stamp {
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--ink) 55%, transparent);
}

/* ---------- the unit ---------- */

.unit {
  position: relative;          /* the CHAT tab hangs off its right flank */
  --amb-albedo: var(--panel);
  width: min(340px, 100%);
  padding: 16px 16px 14px;
  display: grid;
  gap: 12px;
}

.topline { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.nameplate { display: grid; gap: 3px; padding-top: 2px; }
.brand { color: var(--ink); font-weight: 700; letter-spacing: .2em; }
.pwr-wrap { display: grid; justify-items: center; gap: 4px; }

/* Perforated speaker area. */
.grille {
  --amb-shade: 0.93;
  height: 40px;
  background-image: radial-gradient(circle at center, rgba(0,0,0,.42) 1.1px, transparent 1.4px);
  background-size: 9px 9px;
  background-position: 4px 5px;
}

/* ---------- display ---------- */

.display {
  --amb-albedo: #0a0a0b;
  --amb-shade: 1;
  padding: 10px 12px 8px;
  color: var(--screen-ink);
  font-family: var(--mono);
  filter: saturate(.25) brightness(.5);
  transition: filter .25s ease;
}
body.is-on .display { filter: none; }

.display-top { display: flex; align-items: center; gap: 10px; }
.display .tag { color: color-mix(in oklab, var(--screen-ink) 60%, transparent); }
.numeral {
  font-size: 44px; line-height: 1; font-weight: 500;
  margin-right: auto;
  min-width: 34px;
  text-shadow: 0 0 12px color-mix(in oklab, var(--screen-ink) 55%, transparent);
  font-variant-numeric: tabular-nums;
}
.led-stack { display: grid; justify-items: end; gap: 6px; }
.micro { color: color-mix(in oklab, var(--screen-ink) 70%, transparent); }
.display-bottom {
  margin-top: 6px;
  font-size: 11px;
  line-height: 1.35;
  letter-spacing: .06em;
  text-transform: uppercase;
  /* Two lines are always reserved. The longest messages (mic blocked, the
     hop limit) wrap; without this the display grows, and since the unit is
     centred in the viewport the whole radio jumps. */
  height: calc(2 * 1.35 * 11px);
  overflow: hidden;
  color: color-mix(in oklab, var(--screen-ink) 85%, transparent);
}
#status[data-kind="rx"] { color: #7dffb0; }

/* Input level. Lives in the display so it reads as part of the instrument,
   and it is the only proof the mic is actually hearing you. */
.vu { display: flex; gap: 3px; margin-top: 8px; height: 5px; }
.vu i {
  flex: 1;
  border-radius: 1px;
  background: color-mix(in oklab, var(--screen-ink) 14%, transparent);
  transition: background .06s linear, box-shadow .06s linear;
}
.vu i.lit {
  background: var(--screen-ink);
  box-shadow: 0 0 5px color-mix(in oklab, var(--screen-ink) 70%, transparent);
}
.vu i.lit.hot { background: #ff5a3c; box-shadow: 0 0 5px #ff5a3c; }
#status[data-kind="tx"] { color: var(--orange); }

/* ---------- LEDs ---------- */

.led {
  width: 8px; height: 8px; border-radius: 50%;
  background: color-mix(in oklab, var(--amb-emit-color) 22%, #2a2622);
  box-shadow: inset 0 1px 1px rgba(0,0,0,.6);
  display: inline-block;
}
.led.on {
  background: var(--amb-emit-color);
  box-shadow: 0 0 7px var(--amb-emit-color), 0 0 2px #fff inset;
}
/* Somebody is on that channel. Softer and cooler than the amber you are on,
   so a glance still finds your own position first. */
.led.busy {
  background: #efe7d8;
  box-shadow: 0 0 5px rgba(239, 231, 216, .5);
}
#rec[hidden] { display: none; }
#rec { background: #d63c1e; box-shadow: 0 0 8px #ff4d24; animation: pulse 1s steps(2) infinite; }
@keyframes pulse { 50% { opacity: .3; } }

/* The scale. Ticks are positioned on the same geometry the slider thumb
   travels — centre at half a thumb in, then eight equal steps across what is
   left — so light, number and fader position are the same place, not three
   approximations of it. */
.tuner { --thumb: 20px; display: grid; gap: 4px; }
.ticks { position: relative; height: 30px; }
.tick {
  position: absolute;
  left: calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--i) / 8);
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 5px;
  padding: 2px 4px;
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
}
.tick b {
  font: 600 9px/1 var(--sans);
  letter-spacing: .1em;
  color: color-mix(in oklab, var(--ink) 45%, transparent);
}
.tick:disabled { cursor: not-allowed; }
.tick.here b { color: var(--ink); }

/* While the dial is settling the lit channel blinks: tuning, not tuned. */
.ticks.tuning .led.on { animation: tune .35s steps(2) infinite; }
@keyframes tune { 50% { opacity: .2; } }

/* ---------- controls ---------- */

.row { display: flex; gap: 16px; align-items: flex-end; }
.row:has(.power) { margin-bottom: 4px; }
.control { display: grid; gap: 6px; justify-items: start; }
.control.grow { flex: 1; }

.power {
  width: 40px; height: 40px;
  --amb-albedo: #cbc7bf;
  display: grid; place-items: center;
  cursor: pointer; border: 0; padding: 0;
  transition: --amb-elevation .12s;
}
.power-mark {
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--ink) 60%, transparent);
  border-top-color: transparent;
  transition: border-color .2s, box-shadow .2s;
}
.power[aria-pressed="true"] { --amb-albedo: var(--orange); }
.power[aria-pressed="true"] .power-mark {
  border-color: #fff; border-top-color: transparent;
  box-shadow: 0 0 8px rgba(255,255,255,.7);
}

/* Fader: a groove cut into the panel, with a brushed cap riding in it. */
.fader { width: 100%; display: block; margin-top: 2px; }
#dial {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 26px; background: transparent; cursor: pointer;
  /* Browsers put a default 2px margin on a range input, which would shift the
     whole fader off the tick scale above it. */
  margin: 0;
}
#dial:disabled { opacity: .45; cursor: not-allowed; }
#dial::-webkit-slider-runnable-track {
  height: 8px; border-radius: 4px;
  background: color-mix(in oklab, var(--ink) 26%, transparent);
  box-shadow: inset 0 1.5px 2px rgba(0,0,0,.55), inset 0 -1px 0 rgba(255,255,255,.35);
}
#dial::-moz-range-track {
  height: 8px; border-radius: 4px;
  background: color-mix(in oklab, var(--ink) 26%, transparent);
  box-shadow: inset 0 1.5px 2px rgba(0,0,0,.55);
}
#dial::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20px; height: 26px; margin-top: -9px;
  border-radius: 3px;
  background: linear-gradient(180deg, #fdfdfb, #cfcdc7);
  box-shadow: 0 2px 3px rgba(0,0,0,.4), inset 0 -1px 0 rgba(0,0,0,.2), inset 0 1px 0 #fff;
}
#dial::-moz-range-thumb {
  width: 20px; height: 26px; border: 0; border-radius: 3px;
  background: linear-gradient(180deg, #fdfdfb, #cfcdc7);
  box-shadow: 0 2px 3px rgba(0,0,0,.4);
}

/* ---------- the one key ---------- */

.ptt {
  --amb-albedo: var(--orange);
  height: 92px;
  border: 0; padding: 0; cursor: pointer;
  display: grid; place-content: center; gap: 4px;
  color: #fff;
  transition: filter .1s;
}
.ptt-label { font-size: 15px; font-weight: 700; letter-spacing: .18em; }
.ptt-sub { font-size: 9px; letter-spacing: .16em; opacity: .75; }
.ptt:disabled { --amb-albedo: #b6b2aa; color: rgba(255,255,255,.6); cursor: not-allowed; }
.ptt.held:not(:disabled) { --amb-elevation: 0; --amb-thickness: 1; filter: brightness(.94); }
.ptt.live { --amb-albedo: #e02b0b; }

/* ---------- utility keys ---------- */

.tools { gap: 10px; }
.key {
  --amb-albedo: var(--key-black);
  flex: 1;
  border: 0; padding: 10px; cursor: pointer;
  color: #efeae4;
  font: 600 10px/1 var(--sans);
  letter-spacing: .18em; text-transform: uppercase;
}
.key:disabled { opacity: .45; cursor: not-allowed; }
.key:active:not(:disabled) { --amb-elevation: 0; }

.baseline { display: flex; justify-content: space-between; align-items: center; }
/* Was the decorative screw; now the self-test, in the same corner. */
.testkey {
  --amb-albedo: var(--key-black);
  border: 0; padding: 5px 12px; cursor: pointer;
  color: #efeae4;
  font: 600 9px/1 var(--sans);
  letter-spacing: .18em;
}
.testkey:disabled { opacity: .45; cursor: not-allowed; }
.testkey:active:not(:disabled) { --amb-elevation: 0; }

/* ---------- age gate ---------- */

.gate {
  position: fixed; inset: 0; z-index: 10;
  background: rgba(8,8,10,.92);
  display: grid; place-items: center; padding: 24px;
  backdrop-filter: blur(3px);
}
.gate[hidden] { display: none; }
.gate-card {
  --amb-albedo: var(--panel);
  max-width: 330px; padding: 22px; text-align: center;
  display: grid; gap: 10px; justify-items: center;
}
.gate-card h1 { margin: 0; font-size: 17px; letter-spacing: .2em; font-weight: 700; }
.fine { margin: 0; font-size: 12px; line-height: 1.5; color: color-mix(in oklab, var(--ink) 70%, transparent); }
.gate-card .key { padding: 12px 18px; flex: none; }

/* Which build you are looking at. Deliberately almost invisible — it is for
   answering "is my change live yet", not for reading. */
.version {
  margin: 14px 0 0;
  text-align: center;
  font: 400 8px/1 var(--mono);
  letter-spacing: .18em;
  color: rgba(255, 255, 255, .16);
}

/* ---------- message pad ---------- */

/* A red tab on the walkie's right side, hinged at its left edge, reading
   downward. It stays put when the pad slides out and becomes the seam between
   the two units, which is why it sits above them both. */
.chat-tab {
  position: absolute;
  top: 50%;
  right: -26px;
  transform: translateY(-50%);
  z-index: 3;
  padding: 14px 7px;
  border: 0;
  border-radius: 0 5px 5px 0;
  background: linear-gradient(90deg, #b41d0a, #e0350f);
  color: #fff;
  cursor: pointer;
  box-shadow: 2px 2px 5px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .25);
  font: 700 9px/1 var(--sans);
  letter-spacing: .22em;
  writing-mode: vertical-rl;      /* CHAT, downward */
  text-orientation: mixed;
}
.chat-tab:active { box-shadow: 1px 1px 3px rgba(0,0,0,.5); }
.chat-tab[aria-expanded="true"] { background: linear-gradient(90deg, #8d1607, #c02a0c); }

/* The slot the pad emerges from. Width, so the row re-centres as it opens and
   the walkie slides left of its own accord — no measuring, no FLIP. */
.chat-slot {
  width: 0;
  overflow: hidden;
  flex: 0 0 auto;
  transition: width .5s cubic-bezier(.34, 1.4, .64, 1);
}
body.chat-open .chat-slot { width: min(300px, 100%); }

.chat-slot .chat {
  transform: translateX(-12px);
  opacity: 0;
  transition: transform .5s cubic-bezier(.34, 1.4, .64, 1), opacity .3s ease;
}
body.chat-open .chat-slot .chat { transform: none; opacity: 1; }

/* Room for the tab to sit in the seam once the pad is out. */
.rig { transition: gap .5s cubic-bezier(.34, 1.4, .64, 1); }
body.chat-open .rig { gap: 34px; }

/* Side by side when there is room, stacked when there is not. The explicit
   width matters: as a centred grid item the row would otherwise be sized
   shrink-to-fit, and the children's percentage widths resolve against that,
   collapsing the row into a stack even on a wide screen. */
.rig {
  width: 100%;
  max-width: 676px;          /* 340 unit + 34 seam + 300 pad + slack */
  display: flex;
  /* Zero when the pad is away: an 18px gap next to a zero-width slot still
     counts, and left the walkie nine pixels off centre on arrival. */
  gap: 0;
  align-items: flex-start;
  flex-wrap: wrap;
  justify-content: center;
}

.chat-head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 10px;
  align-items: baseline;
}
.chat-clock {
  grid-column: 1 / -1;
  font: 400 8px/1 var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--ink) 42%, transparent);
}
/* The last minute is worth noticing, not worth shouting about. */
.chat-clock.soon { color: var(--orange); }

.chat {
  --amb-albedo: var(--panel);
  width: min(300px, 100%);
  padding: 16px;
  display: grid;
  gap: 12px;
}

.chat-log {
  --amb-albedo: #0a0a0b;
  list-style: none;
  margin: 0;
  padding: 10px;
  height: 300px;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 7px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.4;
  color: var(--screen-ink);
  filter: saturate(.25) brightness(.5);
  transition: filter .25s ease;
}
body.is-on .chat-log { filter: none; }

/* The chassis is unselectable so dragging the dial does not paint the panel
   blue — but a message log is text people need to copy out of. */
.chat-log { -webkit-user-select: text; user-select: text; cursor: text; }
.chat-log a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: color-mix(in oklab, var(--screen-ink) 55%, transparent);
  word-break: break-all;
}
.chat-log a:hover { color: #fff3e2; text-decoration-color: currentColor; }
/* A big enough target to tap a link on a phone without hitting the wrong one. */
@media (pointer: coarse) {
  .chat-log a { padding: 2px 0; }
}

.chat-log li { display: grid; gap: 1px; }
.chat-log .who {
  font-size: 8px;
  letter-spacing: .16em;
  color: color-mix(in oklab, var(--screen-ink) 55%, transparent);
}
.chat-log .said { word-break: break-word; }
.chat-log li.mine { text-align: right; }
.chat-log li.mine .said { color: #fff2e0; }
.chat-log li.mine .who { color: color-mix(in oklab, var(--orange) 80%, transparent); }
.chat-log li.note .said { color: color-mix(in oklab, var(--screen-ink) 45%, transparent); font-style: italic; }

.chat-form { display: flex; gap: 8px; }
.chat-input {
  flex: 1;
  min-width: 0;
  padding: 9px 10px;
  border: 0;
  border-radius: 4px;
  background: color-mix(in oklab, var(--ink) 12%, transparent);
  box-shadow: inset 0 1.5px 2px rgba(0,0,0,.45), inset 0 -1px 0 rgba(255,255,255,.3);
  color: var(--ink);
  font: 500 12px/1 var(--mono);
}
.chat-input::placeholder { color: color-mix(in oklab, var(--ink) 40%, transparent); }
.chat-input:disabled { opacity: .45; }
.chat-form .key { flex: 0 0 auto; padding: 9px 12px; }

.chat-note {
  margin: 0;
  font: 400 8px/1.4 var(--sans);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--ink) 45%, transparent);
}

/* ---------- MUSIC RADIO module ----------
   Optional: delete this block with the section and script to remove it. */

.music {
  --amb-albedo: var(--panel);
  width: min(676px, 100%);
  margin-top: 18px;
  padding: 16px;
  display: grid;
  gap: 12px;
}

/* The dial face: dark glass with a printed band, a red needle riding across. */
.dial {
  --amb-albedo: #0a0a0b;
  position: relative;
  height: 96px;
  padding: 10px 14px 8px;
  cursor: pointer;
  touch-action: none;
  filter: saturate(.25) brightness(.55);
  transition: filter .25s ease;
  overflow: hidden;
}
.music.is-on .dial { filter: none; }

.dial-band {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 18px;
  color: #efe7d8;
  font: 600 9px/1 var(--sans);
  letter-spacing: .2em;
}
.dial-band i {
  flex: 1;
  height: 7px;
  border-left: 1px solid rgba(239, 231, 216, .5);
}
.dial-band i:nth-child(odd) { height: 12px; }

.dial-stations { position: absolute; inset: 34px 14px 8px; }
.station {
  position: absolute;
  left: var(--at);
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 3px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: color-mix(in oklab, #efe7d8 55%, transparent);
  font-family: var(--mono);
}
.station b { font-size: 13px; font-weight: 500; }
.station span { font-size: 7px; letter-spacing: .1em; white-space: nowrap; }

/* On a narrow dial the names collide, so the scale keeps only its numbers —
   which is what a real dial is printed with anyway. The station's name is on
   the display below it, where it names whatever is actually playing. */
@media (max-width: 620px) {
  .chat-tab { right: 4px; border-radius: 5px; }
  .station span { display: none; }
  /* 10px, not 11: at 11 the numbers cleared each other by 1.6px, which is not
     clearance, it is luck with one font. */
  .station b { font-size: 10px; letter-spacing: -.02em; }
  .dial { height: 76px; }
  .dial-stations { inset: 30px 14px 8px; }
}
.station.tuned { color: #fff3e2; text-shadow: 0 0 8px rgba(255, 200, 140, .5); }

/* The needle. Red, thin, and the only thing that moves. It lives inside the
   station track so both are measured against exactly the same box. */
.needle {
  position: absolute;
  top: -28px; bottom: -2px;
  left: 0;
  width: 2px;
  transform: translateX(-50%);
  background: linear-gradient(180deg, #ff4a2a, #d8210b);
  box-shadow: 0 0 7px rgba(255, 74, 42, .8);
  transition: left .25s cubic-bezier(.3, .7, .3, 1);
  pointer-events: none;
}

.fm-row { display: flex; align-items: center; gap: 12px; }
.fm-status { margin-right: 0; }
.fm-status {
  font: 600 9px/1 var(--sans);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--ink) 55%, transparent);
}
.fm-listeners {
  margin-right: auto;
  transition: opacity .2s;
  font: 400 8px/1 var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--ink) 42%, transparent);
}
.fm-listeners.muted { opacity: .55; }
.fm-vol { display: flex; align-items: center; gap: 8px; flex: 0 1 200px; }
.fm-vol input { flex: 1; accent-color: var(--orange); min-width: 0; }

/* ---------- radio drawer ---------- */

/* A handle on the bottom edge of the walkie, half tucked under it. */
.expander {
  --amb-albedo: var(--key-black);
  justify-self: center;
  margin: 4px 0 0;          /* inside the panel: nothing can clip it */
  padding: 7px 14px;
  border: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  color: #efeae4;
}
.expander:active { --amb-elevation: 0; }
.expander .glyph {
  width: 22px; height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: color .25s, transform .35s cubic-bezier(.34, 1.56, .64, 1);
}
/* Tuned in: the set lights up, the way the channel LED does. */
.expander[aria-expanded="true"] { color: var(--orange); }
.expander[aria-expanded="true"] .glyph { transform: translateY(-1px); }

/* For screen readers, which cannot see a drawn radio. */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}



.music {
  transform-origin: 50% 0;
  transition: transform .45s cubic-bezier(.34, 1.56, .64, 1), opacity .25s ease;
}
body:not(.radio-open) .music { display: none; }
.music.landing { transform: translateY(-18px) scale(.97); opacity: 0; }

/* A drawer that springs is a nice touch; a drawer that springs at somebody who
   asked for less motion is not. */
@media (prefers-reduced-motion: reduce) {
  .music, .expander .glyph, .chat-slot, .chat-slot .chat, .rig { transition-duration: .01ms; }
}
