/* «Сияние»: black and white like the channel's avatar. Paused, the hands sit on a disc — the button.
   On air the disc melts away and rays move with the music. Covers show only on the lock screen.
   The light theme is the exact negative of the dark one and follows the phone's setting. */
:root{color-scheme:dark;--bg:#0b0b0b;--text:#f4f2ec;--muted:#cfcdc6;--quiet:#a8a6a0;--line:#4a4944;--edge:#2f2f2b;--sheet:#181816;--warn:#ffb38a;
  --dial:min(86vw,46svh,360px);--mono:'JetBrains Mono',ui-monospace,Menlo,monospace;--display:Geologica,system-ui,sans-serif;font-family:var(--mono);
  --ease-out:cubic-bezier(0.23,1,0.32,1)}
@media (prefers-color-scheme:light){
  :root{color-scheme:light;--bg:#efede7;--text:#0b0b0b;--muted:#45443f;--quiet:#5e5d57;--line:#b9b6ad;--edge:#cfccc3;--sheet:#f7f6f2;--warn:#a2441b}
}
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;background:var(--bg);color:var(--text);display:flex;flex-direction:column;min-height:var(--screen-h,100svh);overflow:hidden;-webkit-tap-highlight-color:transparent;user-select:none;-webkit-user-select:none;cursor:pointer;
  padding:max(20px,env(safe-area-inset-top)) max(24px,env(safe-area-inset-right)) max(14px,env(safe-area-inset-bottom)) max(24px,env(safe-area-inset-left))}
a{color:var(--text)}
/* iOS home-screen app: the page runs under the status bar, and iOS 26 blurs whatever sits just below it.
   Keep the header clear of that blur; app.mjs sizes the page to the whole screen (--screen-h). */
@media (orientation:portrait){.standalone body{padding-top:calc(env(safe-area-inset-top) + 36px)}}
button{font:inherit;color:inherit;cursor:pointer}

header{display:flex;justify-content:space-between;align-items:center;min-height:44px;gap:12px}
.wordmark{font-family:var(--display);font-weight:800;font-size:15px;letter-spacing:.14em;white-space:nowrap}
/* The status is a chip that turns solid on air, so «playing» and «not playing» read at a glance. */
.status{font-size:12px;letter-spacing:.08em;color:var(--muted);white-space:nowrap;padding:6px 12px;border:1px solid var(--line);border-radius:14px;transition:background-color .45s var(--ease-out),color .45s var(--ease-out),border-color .45s var(--ease-out)}
[data-mode=live] .status{color:var(--bg);background:var(--text);border-color:var(--text);font-weight:700}
[data-mode=error] .status{color:var(--warn)}

main{flex:1 1 auto;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:clamp(16px,4svh,40px);min-height:0}
.dial{position:relative;width:var(--dial);aspect-ratio:1;display:grid;place-items:center;flex:none}
.dial>*{grid-area:1/1}


.eq{width:100%;height:100%;color:var(--text);opacity:0;transition:opacity .6s var(--ease-out);pointer-events:none}
[data-mode=live].has-levels .eq,[data-mode=tuning] .eq{opacity:1}

/* Nothing here ever restarts an animation or swaps one drawing for another on play/pause, so nothing jumps:
   the rings run all the time and only fade, the hands are one element that changes colour, and their breathing
   settles to rest on air and starts again from rest when paused. */
.ripples{width:100%;height:100%;display:grid;place-items:center;pointer-events:none;transition:opacity .45s var(--ease-out)}
.ripple{grid-area:1/1;width:72%;aspect-ratio:1;border-radius:50%;border:1.5px solid var(--text);opacity:0;animation:ripple 2.6s ease-out infinite}
.ripple.late{animation-delay:1.3s}
[data-mode=live] .ripples,[data-mode=tuning] .ripples{opacity:0}
@keyframes ripple{0%{transform:scale(1);opacity:.55}100%{transform:scale(1.42);opacity:0}}

.hands-button{position:relative;width:75%;aspect-ratio:1;border:0;background:none;padding:0;display:grid;place-items:center;transition:transform .14s var(--ease-out)}
.hands-button:disabled{cursor:wait}
.hands-button:active{transform:scale(.96)}
.face{width:100%;height:100%;display:grid;place-items:center}  /* breathing is driven from app.mjs: it has to settle, not stop */
.face>*{grid-area:1/1}
.disc{width:96%;aspect-ratio:1;border-radius:50%;background:var(--text);transition:opacity .45s var(--ease-out)}
.mark{width:100%;display:flex;flex-direction:column;align-items:center;margin-top:-4%}
.hands{width:66%;aspect-ratio:1;color:var(--bg);background:currentColor;-webkit-mask:url(/hands.svg) center/contain no-repeat;mask:url(/hands.svg) center/contain no-repeat;
  transition:color .45s var(--ease-out)}
/* Buffering (tuning) looks like being on air: the listener pressed play, the network is just catching up. */
[data-mode=live] .disc,[data-mode=tuning] .disc{opacity:0}
[data-mode=live] .hands,[data-mode=tuning] .hands{color:var(--text)}
/* The word under the hands: «▶ СЛУШАТЬ» on the disc, «❚❚ ПАУЗА» on air. */
.cue{display:grid;font-weight:700;font-size:12px;letter-spacing:.2em;color:var(--bg);transition:color .45s var(--ease-out)}
.cue>span{grid-area:1/1;display:flex;align-items:center;justify-content:center;gap:6px;transition:opacity .3s var(--ease-out)}
.cue svg{width:10px;height:10px;fill:currentColor}
.cue-pause,[data-mode=live] .cue-play,[data-mode=tuning] .cue-play{opacity:0}
[data-mode=live] .cue-pause,[data-mode=tuning] .cue-pause{opacity:1}
[data-mode=live] .cue,[data-mode=tuning] .cue{color:var(--muted)}

.now{display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center;min-height:150px;max-width:560px;width:100%}
.artist{margin:0;font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);overflow-wrap:anywhere}
h1{margin:0;font-family:var(--display);font-weight:600;font-size:30px;line-height:1.12;letter-spacing:-.015em;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.hint{margin:0;font-size:12px;color:var(--muted);min-height:16px}
[data-mode=error] .hint{color:var(--warn)}

/* «Что играет» */
.air{display:contents}
.np-open{min-height:44px;padding:0 16px 0 10px;border-radius:22px;border:1px solid var(--line);background:none;display:inline-flex;align-items:center;gap:10px;font-size:13px}
.np-open[hidden]{display:none}
/* Telegram, Spotify, Apple Music: a button only where the track was found. */
.now-row{margin-top:6px;display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:center}
.svcs{display:flex;gap:8px}
.svc{width:44px;height:44px;flex:none;box-sizing:border-box;border-radius:50%;border:1px solid var(--line);display:grid;place-items:center;color:var(--text)}
.svc[hidden]{display:none}
.svc svg{width:20px;height:20px;fill:currentColor}
@media (hover:hover) and (pointer:fine){.svc:hover{border-color:var(--muted)}}
.np-actions{display:flex;gap:10px;align-items:center}
.np-actions .pill{flex:1}
.np .svc{width:48px;height:48px}
.np .svc svg{width:22px;height:22px}
.np .svc.solid{background:var(--text);border-color:var(--text);color:var(--bg)}
.np-open img{width:24px;height:24px;border-radius:4px;object-fit:cover}
.np-open svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:1.6}
.np{display:none;flex-direction:column;gap:18px;cursor:auto;user-select:text;-webkit-user-select:text;text-align:left}
dialog .np{display:flex}
.kicker{margin:0!important;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--quiet)!important}
.np-head{display:flex;justify-content:space-between;align-items:center;min-height:44px;margin-top:-10px}
.np-main{display:flex;gap:16px;align-items:flex-end}
.np-cover{width:152px;height:152px;flex:none;border-radius:14px;object-fit:cover;background:var(--edge)}
.np-text{display:flex;flex-direction:column;gap:6px;min-width:0}
.np-text .artist{margin:0}
.np h2{margin:0;font-family:var(--display);font-weight:600;font-size:20px;line-height:1.15;letter-spacing:0;overflow-wrap:anywhere}
.np-progress{display:flex;flex-direction:column;gap:8px}
.np-bar{height:4px;border-radius:2px;background:var(--edge);overflow:hidden}
.np-bar i{display:block;height:100%;width:0;background:var(--text);border-radius:2px}
.np-times{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--muted)}
.np-next{display:flex;flex-direction:column;gap:12px;padding-top:16px;border-top:1px solid var(--edge)}
.np-next ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}
.np-next li{display:flex;gap:12px;align-items:center}
.np-next img{width:44px;height:44px;border-radius:8px;object-fit:cover;flex:none;background:var(--edge)}
.np-next .what{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.np-next b{font:600 14px var(--display);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.np-next .what span,.np-next .when{font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.np-next .when{flex:none;color:var(--quiet)}

footer{display:flex;justify-content:space-between;align-items:center;font-size:13px}
.link{background:none;border:0;padding:14px 0;font-size:13px;color:var(--text);text-decoration:none}
@media (hover:hover) and (pointer:fine){.link:hover{color:var(--muted)}}
.link.quiet{color:var(--muted)}
[data-mode=error] .link.quiet{color:var(--warn)}
:focus-visible{outline:2px solid var(--text);outline-offset:6px;border-radius:8px}

/* The VPN hint: a quiet card above the footer, only while sound isn't arriving. Not an alarm: same ink, no red. */
.netnote{position:fixed;left:max(16px,env(safe-area-inset-left));right:max(16px,env(safe-area-inset-right));bottom:calc(max(14px,env(safe-area-inset-bottom)) + 52px);
  max-width:460px;margin:0 auto;padding:14px 52px 14px 16px;background:var(--sheet);border:1px solid var(--edge);border-radius:18px;
  font-size:13px;line-height:1.5;color:var(--muted);cursor:auto;box-shadow:0 12px 32px rgba(0,0,0,.35);animation:note-in .3s var(--ease-out)}
.netnote[hidden]{display:none}
.netnote p{margin:0}
.netnote .netnote-title{margin-bottom:4px;font-family:var(--display);font-weight:600;font-size:15px;color:var(--text)}
.netnote .close{position:absolute;top:4px;right:4px;background:none}
@keyframes note-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes note-fade{from{opacity:0}to{opacity:1}}
.tip{padding:12px 14px;border:1px solid var(--edge);border-radius:12px;color:var(--text)!important}
.tip[hidden]{display:none}

/* «Не играет?»: a report the listener copies or sends in Telegram. */
.report{margin:4px 0 16px;padding:14px 14px;max-height:38svh;overflow:auto;background:var(--bg);border:1px solid var(--edge);border-radius:12px;
  font:12px/1.55 var(--mono);color:var(--muted);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text;-webkit-user-select:text}
.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:14px}
.pill{min-height:48px;border-radius:24px;border:1px solid var(--edge);background:none;color:var(--text);display:flex;align-items:center;justify-content:center;
  padding:0 14px;font:600 14px var(--mono);text-decoration:none;text-align:center;cursor:pointer}
.pill.solid{background:var(--text);border-color:var(--text);color:var(--bg)}
.pill:disabled,.pill[aria-disabled=true]{opacity:.45;pointer-events:none}
.hands-button:focus-visible{border-radius:50%;outline-offset:10px}

dialog{background:var(--sheet);color:var(--text);border:1px solid var(--edge);border-radius:24px;padding:28px 24px 20px;max-width:min(460px,calc(100vw - 32px));max-height:calc(100svh - 48px);overflow:auto;font-size:14px;line-height:1.6;user-select:text;-webkit-user-select:text;cursor:auto}
dialog::backdrop{background:rgba(8,8,8,.6);backdrop-filter:blur(6px)}
dialog h2{margin:0 0 14px;font-family:var(--display);font-size:22px;font-weight:800;letter-spacing:.06em}
dialog p{margin:0 0 12px;color:var(--muted)}
dialog .small{font-size:12px;color:var(--quiet)}
dialog h2+.memory{display:flex;flex-direction:column;gap:4px;margin:18px 0;padding-bottom:18px;border-bottom:1px solid var(--edge)}
.memory p{margin:0}
.memory .memory-name{font-family:var(--display);font-weight:600;font-size:24px;line-height:1.15;color:var(--text)}
.memory .memory-dates{font-size:13px;letter-spacing:.08em}
.memory .memory-love{margin-top:8px;color:var(--text)}
.heart::before{content:'🤍'}
@media (prefers-color-scheme:light){.heart::before{content:'🖤'}}
@media (max-width:600px){
  dialog.sheet{margin:auto 0 0;width:100%;max-width:100%;border-radius:28px 28px 0 0;border-width:1px 0 0;padding-bottom:max(24px,env(safe-area-inset-bottom))}
}
dialog form{position:sticky;top:-16px;float:right;margin:-16px -12px 0 8px;z-index:1}
.close{width:44px;height:44px;border:0;border-radius:50%;background:var(--sheet);display:grid;place-items:center}
.close svg{width:20px;height:20px;stroke:var(--muted);stroke-width:2;stroke-linecap:round}

/* A wide screen: the dial on the left, «что играет» beside it; hidden, the page is the phone layout, only bigger. */
@media (min-width:1024px) and (min-height:600px){
  :root{--dial:min(40vw,60svh,520px)}
  body.wide main{flex-direction:row;flex-wrap:wrap;justify-content:center;gap:clamp(40px,6vw,96px);width:100%;max-width:1240px;margin:0 auto}
  body.wide .air{display:flex;flex-direction:column;align-items:center;gap:clamp(16px,3svh,32px);flex:1 1 380px;max-width:540px}
  body.wide .np{display:flex;flex:1 1 420px;max-width:520px;min-width:0}
  body.wide .np-cover{width:200px;height:200px;border-radius:16px}
  body.wide .np h2{font-size:clamp(26px,2.6vw,38px);line-height:1.08;letter-spacing:-.015em}
  body.wide .now .artist,body.wide .now h1,body.wide .now-row{display:none}
  body.wide .now{min-height:0}
  body:not(.wide) .dial{--dial:min(86vw,52svh,440px)}
}

/* Landscape: a phone in a car mount. Hands left, track right, the whole screen is the target. */
@media (orientation:landscape) and (max-height:540px){
  :root{--dial:min(calc(100svh - 48px),320px)}
  body{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto 1fr auto;column-gap:clamp(24px,6vw,64px)}
  main,.air{display:contents}
  .dial{grid-column:1;grid-row:1/-1;align-self:center}
  header{grid-column:2;grid-row:1}
  .now{grid-column:2;grid-row:2;align-self:center;align-items:flex-start;text-align:left;min-height:0}
  .now-row{justify-content:flex-start}
  h1{font-size:clamp(26px,8svh,38px)}
  footer{grid-column:2;grid-row:3}
}
@media (max-height:640px) and (orientation:portrait){h1{font-size:24px}.now{gap:6px;min-height:118px}}
/* Reduced motion: no movement or scaling, but fades and colour changes stay, so state changes are still legible. */
@media (prefers-reduced-motion:reduce){.ripple{animation:none}.netnote{animation:note-fade .2s ease}.hands-button{transition:none}.hands-button:active{transform:none}}
