/* together — the film is the only colour. Everything else is quiet, in the device's own typeface,
   and says as little as it can. Colour is kept for the few things that need someone's attention. */

@import url("./kit/tokens.css");

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

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--black);
  color: var(--text);
  font: 400 var(--step-0) / 1.5 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
p {
  margin: 0;
}

button,
input {
  font: inherit;
  color: inherit;
}

[hidden] {
  display: none !important;
}

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

::selection {
  background: rgb(255 255 255 / 0.3);
}

.icon {
  width: 20px;
  height: 20px;
  flex: none;
}



/* Arriving: a short rise into place. Screens use it staggered, one beat after another. */
@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

/* ---------- Buttons and fields ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45em;
  min-height: 40px;
  padding: 0 1.15em;
  border: 0;
  border-radius: 999px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.15s, color 0.15s, transform 0.35s var(--spring);
}

.btn:active {
  transform: scale(0.96);
  transition-duration: 0.08s;
}

.btn-primary {
  background: var(--white);
  color: var(--black);
}

.btn-primary:hover {
  background: rgb(255 255 255 / 0.84);
}

.btn-secondary {
  background: var(--fill-hover);
  color: var(--text);
}

.btn-secondary:hover {
  background: rgb(255 255 255 / 0.17);
}

.btn-icon {
  width: 40px;
  padding: 0;
  background: transparent;
  color: var(--text);
}

.btn-icon:hover {
  background: var(--fill-hover);
}

.btn-small {
  width: 32px;
  min-height: 32px;
}

.btn-small .icon {
  width: 16px;
  height: 16px;
}

.btn-large {
  min-height: 52px;
  padding: 0 1.6em;
  font-size: var(--step-1);
}

.btn-large .icon {
  width: 22px;
  height: 22px;
}

.input {
  width: 100%;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-field);
  background: var(--fill);
  transition: border-color 0.15s, background-color 0.15s;
}

.input::placeholder {
  color: var(--faint);
}

.input:hover {
  border-color: var(--line-strong);
}

.input:focus-visible {
  border-color: var(--white);
  background: transparent;
  box-shadow: none;
}

.input[aria-invalid="true"] {
  border-color: var(--wrong);
  animation: no 0.4s var(--ease-out);
}

.input-mono {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-overflow: ellipsis;
}

.field-error {
  color: var(--wrong);
  font-size: var(--step--1);
}

.field-error:empty {
  display: none;
}

.field-error:not(:empty) {
  animation: rise-in 0.3s var(--ease-out);
}

a.btn {
  text-decoration: none;
}

.quiet-link {
  padding: 2px 4px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--muted);
  font-size: var(--step--1);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 3px;
  cursor: pointer;
}

.quiet-link:hover {
  color: var(--text);
}

/* ---------- The mark: you, solid, and a friend, drawn in outline ---------- */

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border-radius: 6px;
  color: var(--text);
  font: 600 1.25rem / 1 var(--font);
  letter-spacing: -0.01em;
  text-decoration: none;
}

.wordmark-mark {
  width: 30px;
  height: 19px;
  flex: none;
}

/* ---------- Landing and invite: one line, one card, one obvious thing to do ---------- */

.screen:not(.room) {
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  min-height: 100%;
  padding: max(1.25rem, env(safe-area-inset-top)) max(1.25rem, env(safe-area-inset-right))
    max(1.25rem, env(safe-area-inset-bottom)) max(1.25rem, env(safe-area-inset-left));
}

.masthead {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
}

.middle {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 1.5rem;
  width: min(100%, 560px);
  padding-bottom: 8vh;
}

.headline {
  font-size: clamp(1.9rem, 5vw, 2.75rem);
  font-weight: 650;
  line-height: 1.1;
  letter-spacing: -0.025em;
  text-align: center;
  text-wrap: balance;
}

.stage-card {
  display: grid;
  justify-items: center;
  gap: 0.9rem;
  width: 100%;
  padding: 0.5rem 0 1rem;
  text-align: center;
}

/* Each time a screen appears, its parts arrive one beat after another. */
.screen:not(.room) .masthead,
.middle > *,
.stage-card > * {
  animation: rise-in 0.55s var(--ease-out) calc(var(--beat, 0) * 70ms) backwards;
}

.middle > :nth-child(2) {
  --beat: 1;
}

.stage-card > :nth-child(2) {
  --beat: 2;
}

.stage-card > :nth-child(n + 3) {
  --beat: 3;
}

.middle > :nth-child(n + 3) {
  --beat: 4;
}

.masthead {
  animation-name: fade-in;
}

.middle > .stage-card {
  animation: none;
}

.stage-card > :first-child {
  --beat: 1;
}

.stage-card .btn-large {
  width: min(100%, 320px);
}

.stage-card label.btn {
  cursor: pointer;
}

.stage-card label.btn:not(.btn-primary) {
  background: var(--fill-hover);
}

.stage-card label.btn:focus-within {
  box-shadow: var(--focus);
}

.dropzone {
  transition: opacity 0.3s var(--ease-out);
}

.dropzone.is-busy {
  pointer-events: none;
  opacity: 0.55;
}

.hint {
  color: var(--muted);
  font-size: var(--step--1);
}

/* An invite this version can't use: only the reason, and the way home, are left. */
.invited.is-refused [data-stream],
.invited.is-refused [data-own-copy],
.invited.is-refused [data-own-copy-hint],
.invited.is-refused .name-slot {
  display: none;
}

/* A room on a site's own player: joined through the extension, so nothing here takes a file. */
.invited.is-on-site [data-dropzone] {
  display: none;
}

.on-site-where {
  font-size: var(--step-1);
  font-weight: 600;
  text-wrap: balance;
}

.name-slot {
  display: grid;
  justify-items: center;
  gap: 0.35rem;
  width: min(100%, 320px);
}

.name-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  color: var(--muted);
  font-size: var(--step--1);
}

.name-row strong {
  color: var(--text);
  font-weight: 600;
}

.name-row {
  animation: fade-in 0.25s var(--ease-out);
}

.name-input {
  width: 100%;
  min-height: 48px;
  font-size: var(--step-1);
  text-align: center;
}

.join-form {
  display: flex;
  gap: 0.5rem;
  width: 100%;
}

.join-form .btn {
  min-height: 44px;
}

.tune-offer {
  display: grid;
  justify-items: center;
  gap: 0.2rem;
}

.tune-how {
  margin: 0;
  color: var(--faint);
  text-align: center;
}

.tune-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin: 0;
  text-align: center;
}

/* Tuning, while it runs. Shared with the extension's Tune page (build-extension.sh copies this
   file), because the complaint it answers is the same on both: thirty seconds of nothing. */

.tune-run {
  display: grid;
  gap: 0.6rem;
  width: 100%;
  padding: 14px 16px;
  border-radius: var(--radius-field);
  background: var(--fill);
  box-shadow: inset 0 0 0 1px var(--line);
  text-align: left;
  animation: rise-in 0.3s var(--ease-out);
}

.tune-step {
  margin: 0;
  font-weight: 600;
}

/* A level meter, in segments, so it reads as one at a glance rather than as a progress bar. The
   lit part is a hard stop at --level; the notches are a mask, so there is one element and one
   custom property to set twelve times a second. */
.tune-meter {
  --level: 0;
  height: 12px;
  border-radius: 2px;
  background: linear-gradient(
    to right,
    var(--white) 0 calc(var(--level) * 100%),
    var(--line) calc(var(--level) * 100%) 100%
  );
  mask: repeating-linear-gradient(to right, #000 0 5px, transparent 5px 8px);
}

/* How far through, in the three parts it actually has, each named. Nothing here is a guess about
   how long the whole thing takes: each bar fills over its own part and stops at full if that part
   runs long. */
.tune-phases {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.tune-phase {
  --through: 0;
  display: grid;
  gap: 0.35rem;
  color: var(--faint);
  font-size: var(--step--1);
  transition: color 0.3s var(--ease-out);
}

.tune-phase[data-state="now"] {
  color: var(--text);
  font-weight: 600;
}

.tune-phase[data-state="done"] {
  color: var(--muted);
}

.tune-phase-bar {
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(
    to right,
    currentColor 0 calc(var(--through) * 100%),
    var(--line) calc(var(--through) * 100%) 100%
  );
}

.tune-hot {
  margin: 0;
  color: var(--wrong);
}

/* ---------- Room ---------- */

.room,
.stage {
  position: fixed;
  inset: 0;
}

.stage {
  background: var(--black);
  overflow: hidden;
}

.video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Stacking inside the stage: video, overlay states, then everything you interact with. */
.overlay {
  z-index: 1;
}

.chrome,
.notice,
.toasts {
  z-index: 2;
}

.chrome {
  transition: opacity 0.4s var(--ease-out), transform 0.4s var(--ease-out);
}

/* Into the room: the film fades up and the chrome settles in from the edges around it. */
.room:not([hidden]) .video {
  animation: fade-in 0.6s var(--ease-out);
}

.room:not([hidden]) .topbar {
  animation: settle-down 0.55s var(--ease-out) 0.1s backwards;
}

.room:not([hidden]) .controls {
  animation: settle-up 0.55s var(--ease-out) 0.15s backwards;
}

@keyframes settle-down {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
}

@keyframes settle-up {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

.stage.is-idle {
  cursor: none;
}

.stage.is-idle .chrome {
  opacity: 0;
  pointer-events: none;
}

.stage.is-idle .topbar {
  transform: translateY(-6px);
}

.stage.is-idle .controls {
  transform: translateY(6px);
}

.topbar {
  position: absolute;
  inset: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: max(14px, env(safe-area-inset-top)) max(18px, env(safe-area-inset-right)) 40px
    max(18px, env(safe-area-inset-left));
  background: linear-gradient(rgb(0 0 0 / 0.7), rgb(0 0 0 / 0));
}

.room-title {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-width: 0;
  flex: 1 1 auto;
}

.room-mark {
  width: 28px;
  height: 18px;
  flex: none;
  color: var(--text);
}

.room-name {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex: none;
}

/* Show timings: small and quiet, for whoever looks for it. */
.timings-toggle {
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.timings-toggle[aria-pressed="true"] {
  color: var(--text);
  background: var(--fill);
}

/* People: one circle each. Yours is solid and everyone else's is drawn in outline, like the mark.
   How someone is doing is drawn around their circle, never in a second dot or a colour. */

.people {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
  padding: 0;
  list-style: none;
  flex: none;
}

.person {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 3px 12px 3px 3px;
  border-radius: 999px;
  background: var(--glass);
  backdrop-filter: blur(14px);
  animation: arrive 0.5s var(--spring) calc(var(--i, 0) * 60ms) backwards;
}

.person.is-leaving {
  animation: depart 0.3s var(--ease-in) forwards;
}

@keyframes arrive {
  from {
    opacity: 0;
    transform: scale(0.6);
  }
}

@keyframes depart {
  to {
    opacity: 0;
    transform: scale(0.6);
  }
}

.avatar {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px var(--text);
  color: var(--text);
  font: 650 0.66rem / 1 var(--font);
  letter-spacing: 0.02em;
  transition: opacity 0.2s, transform 0.3s var(--spring);
}

.avatar.is-you {
  background: var(--text);
  box-shadow: none;
  color: var(--black);
}

.face {
  position: relative;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
}

.face > * {
  grid-area: 1 / 1;
}

/* The ring around a circle: closed when they're ready, turning while the room waits for them,
   broken while their sync is still being measured or is off. */
.face-ring {
  width: 34px;
  height: 34px;
  overflow: visible;
  fill: none;
  stroke: var(--text);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
  opacity: 0;
  transform: rotate(-90deg);
  transition: stroke-dashoffset 0.55s var(--ease-out), opacity 0.25s, stroke-dasharray 0.3s;
}

.person[data-sync="unknown"] .face-ring {
  stroke-dasharray: 2 6;
  stroke-dashoffset: 0;
  opacity: 0.45;
}

.person[data-state="ready"] .face-ring {
  stroke-dasharray: 100 100;
  stroke-dashoffset: 0;
  opacity: 1;
}

.person[data-state="stalled"] .face-ring {
  stroke-dasharray: 14 11;
  stroke-dashoffset: 0;
  opacity: 0.9;
  animation: turn 2.4s linear infinite;
}

.person[data-state="stalled"] .avatar {
  animation: breathe 2.4s ease-in-out infinite;
}

@keyframes turn {
  from {
    transform: rotate(-90deg);
  }
  to {
    transform: rotate(270deg);
  }
}

@keyframes breathe {
  50% {
    opacity: 0.5;
    transform: scale(0.92);
  }
}

/* The sync meter: a hairline on the rim, where the ring would be. It hangs at the bottom while
   someone is with the room and slides round, right when ahead and left when behind; its length
   is how sure the measurement is. Faint while all is well, plain when it's worth a glance, and
   heavier, with words beside the circle, when they're clearly off. The numbers are in the
   tooltip. */
.face-meter {
  width: 34px;
  height: 34px;
  overflow: visible;
  fill: none;
  stroke: var(--text);
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-dasharray: var(--meter-length, 16) 360;
  opacity: 0;
  transform: rotate(var(--meter-start, 82deg));
  transition:
    transform 0.9s var(--spring),
    stroke-dasharray 0.9s var(--ease-out),
    opacity 0.4s,
    stroke-width 0.3s;
  pointer-events: none;
}

.face-meter.is-shown {
  opacity: 0.55;
}

.person[data-sync="fair"] .face-meter.is-shown {
  opacity: 0.85;
}

.person[data-sync="poor"] .face-meter.is-shown {
  opacity: 1;
  stroke-width: 2;
}

/* A ring says more than the meter while someone is held up, or has said they're ready. */
.person[data-state="ready"] .face-meter,
.person[data-state="stalled"] .face-meter,
.person[data-sync="off"] .face-meter,
.person[data-sync="none"] .face-meter {
  opacity: 0;
}

/* Not on the room's video: the circle steps back. */
.person[data-sync="off"] .avatar {
  opacity: 0.45;
}

/* Roles keep one shape per person. A screen is a small TV, with its stand; a remote is a narrow
   capsule, like the thing in your hand. */
.person[data-role="screen"] .avatar {
  width: 30px;
  height: 21px;
  border-radius: 5px;
  margin-top: -3px;
}

.person[data-role="screen"] .face::after {
  content: "";
  grid-area: 1 / 1;
  align-self: end;
  justify-self: center;
  width: 12px;
  height: 1.5px;
  margin-bottom: 2px;
  border-radius: 1px;
  background: var(--text);
}

.person[data-role="remote"] .avatar {
  width: 19px;
  height: 30px;
  border-radius: 999px;
  font-size: 0.58rem;
}

/* The moment someone says they're ready: their initials make way for a tick, which draws itself
   and then hands back. */
.face-tick {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: var(--text);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  stroke-dasharray: 1 1;
  stroke-dashoffset: 1;
}

.face.is-you .face-tick {
  stroke: var(--black);
}

.person.is-confirming .avatar {
  animation: make-way 1.3s var(--ease-out);
}

.person.is-confirming .face-tick {
  animation: tick 1.3s var(--ease-out);
}

.person.is-confirming .face {
  animation: pop 0.5s var(--spring);
}

@keyframes make-way {
  12%,
  80% {
    color: transparent;
  }
}

@keyframes tick {
  0% {
    opacity: 1;
    stroke-dashoffset: 1;
  }
  30%,
  78% {
    opacity: 1;
    stroke-dashoffset: 0;
  }
  100% {
    opacity: 0;
    stroke-dashoffset: 0;
  }
}

@keyframes pop {
  40% {
    transform: scale(1.14);
  }
}

.person-text {
  display: grid;
  line-height: 1.15;
}

.person-name {
  max-width: 9rem;
  overflow: hidden;
  font-size: var(--step--1);
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Only there when something is off: "buffering", "±1.2s". */
.person-note {
  color: var(--muted);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.person-note:empty {
  display: none;
}

.person-note:not(:empty) {
  animation: rise-in 0.3s var(--ease-out);
}

/* The ready check: the one thing left to do once friends are here, so it's big and centred. */

.ready-prompt {
  position: absolute;
  left: 50%;
  bottom: max(96px, calc(env(safe-area-inset-bottom) + 96px));
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: 0.35rem;
  width: min(calc(100vw - 32px), 380px);
  padding: 1.25rem 1.5rem 1.4rem;
  border-radius: 24px;
  background: var(--glass-solid);
  backdrop-filter: blur(24px);
  box-shadow: 0 0 0 1px var(--line), 0 24px 64px rgb(0 0 0 / 0.5);
  text-align: center;
  transform: translateX(-50%);
  animation: rise 0.5s var(--spring);
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translate(-50%, 16px) scale(0.96);
  }
}

.ready-title {
  font-size: var(--step-2);
  font-weight: 650;
  letter-spacing: -0.01em;
}

.ready-note {
  color: var(--muted);
  font-size: var(--step--1);
}

.ready-prompt .btn {
  width: 100%;
  margin-top: 0.6rem;
}

/* Controls */

.controls {
  position: absolute;
  inset: auto 0 0;
  display: grid;
  gap: 2px;
  padding: 56px max(14px, env(safe-area-inset-right)) max(10px, env(safe-area-inset-bottom))
    max(14px, env(safe-area-inset-left));
  background: linear-gradient(rgb(0 0 0 / 0), rgb(0 0 0 / 0.75));
}

.controls-row {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.btn-play .icon {
  width: 24px;
  height: 24px;
}

.time {
  padding: 0 0.4rem;
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.time-sep,
[data-duration] {
  color: var(--muted);
}

.spacer {
  flex: 1;
}

.volume {
  display: flex;
  align-items: center;
}

input[type="range"] {
  --fill: 0%;
  appearance: none;
  height: 22px;
  margin: 0;
  background: transparent;
  cursor: pointer;
}

input[type="range"]::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--white) var(--fill), rgb(255 255 255 / 0.24) var(--fill));
}

input[type="range"]::-moz-range-track {
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--white) var(--fill), rgb(255 255 255 / 0.24) var(--fill));
}

input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 13px;
  height: 13px;
  margin-top: -5px;
  border: 0;
  border-radius: 50%;
  background: var(--white);
  transition: transform 0.15s;
}

input[type="range"]::-moz-range-thumb {
  width: 13px;
  height: 13px;
  border: 0;
  border-radius: 50%;
  background: var(--white);
}

input[type="range"]:hover::-webkit-slider-thumb {
  transform: scale(1.25);
}

input[type="range"]:focus-visible {
  box-shadow: none;
}

input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: var(--focus);
}

input[type="range"]:focus-visible::-moz-range-thumb {
  box-shadow: var(--focus);
}

.scrubber {
  width: 100%;
}

.volume-slider {
  width: 0;
  opacity: 0;
  transition: width 0.2s var(--ease-out), opacity 0.2s;
}

.volume:hover .volume-slider,
.volume:focus-within .volume-slider {
  width: 84px;
  margin-right: 0.4rem;
  opacity: 1;
}

/* Toasts */

.toasts {
  position: absolute;
  top: 78px;
  left: 50%;
  display: grid;
  justify-items: center;
  gap: 0.5rem;
  width: min(92vw, 420px);
  margin: 0;
  padding: 0;
  list-style: none;
  transform: translateX(-50%);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 6px 16px 6px 6px;
  border-radius: 999px;
  background: var(--glass-solid);
  backdrop-filter: blur(16px);
  box-shadow: 0 0 0 1px var(--line), 0 12px 32px rgb(0 0 0 / 0.4);
  animation: toast-in 0.45s var(--spring);
}

.toast.is-plain {
  padding-left: 16px;
}

.toast .avatar {
  width: 30px;
  height: 30px;
}

.toast .icon {
  margin-left: 6px;
}

.toast.is-leaving {
  animation: toast-out 0.25s var(--ease-in) forwards;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(-10px) scale(0.94);
  }
}

@keyframes toast-out {
  to {
    opacity: 0;
    transform: translateY(-6px);
  }
}

/* Media mismatch notice */

.notice {
  position: absolute;
  top: 74px;
  right: max(18px, env(safe-area-inset-right));
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  width: min(calc(100vw - 36px), 380px);
  padding: 12px 8px 12px 14px;
  border-radius: 16px;
  background: var(--glass-solid);
  backdrop-filter: blur(16px);
  box-shadow: 0 0 0 1px var(--line), 0 12px 32px rgb(0 0 0 / 0.4);
  animation: toast-in 0.35s var(--ease-out);
}

.notice-icon {
  margin-top: 1px;
}

.notice-body {
  display: grid;
  gap: 0.15rem;
  flex: 1;
}

.notice-title {
  font-weight: 600;
}

.notice-text {
  color: var(--muted);
  font-size: var(--step--1);
}

/* Overlay states */

.overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: rgb(0 0 0 / 0.6);
  backdrop-filter: blur(8px);
  animation: fade-in 0.3s ease-out;
}

.overlay.is-clear {
  background: transparent;
  backdrop-filter: none;
  pointer-events: none;
}

/* A countdown only has to make a number readable, so the film stays visible under it. */
.overlay[data-kind="countdown"] {
  background: rgb(0 0 0 / 0.45);
  backdrop-filter: blur(3px);
  transition: background-color 0.5s var(--ease-out), backdrop-filter 0.5s var(--ease-out);
  pointer-events: none;
}

.overlay[data-kind="countdown"].is-clear {
  background: rgb(0 0 0 / 0);
  backdrop-filter: blur(0);
}

.overlay.is-clear .overlay-card {
  pointer-events: auto;
}

.overlay-card {
  display: grid;
  justify-items: center;
  gap: 0.75rem;
  max-width: 400px;
  padding: 1.75rem 2rem;
  border-radius: var(--radius-card);
  background: var(--glass-solid);
  backdrop-filter: blur(24px);
  box-shadow: 0 0 0 1px var(--line), 0 24px 64px rgb(0 0 0 / 0.5);
  text-align: center;
  animation: card-in 0.5s var(--spring);
}

@keyframes card-in {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.95);
  }
}

/* For states that interrupt a film rather than open one: the same card, said more softly. */
.overlay-card.is-quiet {
  max-width: 420px;
  gap: 0.55rem;
  padding: 1.35rem 1.7rem;
}

.overlay-card.is-quiet .overlay-title {
  font-size: var(--step-2);
}

.overlay-card.is-quiet .overlay-mark {
  width: 46px;
  height: 30px;
}

.overlay-title {
  font: 600 var(--step-3) / 1.1 var(--font);
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.overlay-text {
  color: var(--muted);
  text-wrap: pretty;
}

.overlay-card .btn {
  margin-top: 0.4rem;
}

.overlay-mark {
  width: 60px;
  height: 38px;
  margin-bottom: 0.25rem;
  color: var(--text);
}

.overlay-mark .friend {
  transform-origin: 29px 14px;
  animation: spin 14s linear infinite;
}

.overlay-mark .pulse {
  transform-box: fill-box;
  transform-origin: center;
  animation: pulse 1.6s ease-in-out infinite;
}

.overlay-mark .pulse:nth-of-type(2) {
  animation-delay: 0.8s;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes pulse {
  50% {
    transform: scale(0.84);
    opacity: 0.5;
  }
}

/* Countdown: one number a second, each landing rather than ticking over, then a ring opening
   out of the last beat as the film starts. */

.countdown {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 0.5rem;
  text-align: center;
}

/* Whatever frame the film is paused on, the number has to read against it. */
.countdown::before {
  content: "";
  position: absolute;
  inset: -45% -60%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(0 0 0 / 0.7), rgb(0 0 0 / 0));
}

.countdown-dial {
  display: grid;
  place-items: center;
}

.countdown-number {
  grid-area: 1 / 1;
  place-self: center;
  font: 500 clamp(6rem, 22vw, 11rem) / 0.9 var(--font);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
  animation: count-in 0.55s var(--spring) both;
}

@keyframes count-in {
  from {
    opacity: 0;
    transform: scale(1.35);
    filter: blur(8px);
  }
}

/* Each second closes a hairline circle around its number, so the wait has a visible end. */
.countdown-sweep {
  grid-area: 1 / 1;
  width: clamp(11rem, 32vw, 16rem);
  aspect-ratio: 1;
  overflow: visible;
  fill: none;
  stroke: var(--white);
  stroke-width: 1.5;
  stroke-linecap: round;
  transform: rotate(-90deg);
}

.countdown-sweep circle:first-child {
  opacity: 0.16;
}

.countdown-sweep circle:last-child {
  stroke-dasharray: 1 1;
  stroke-dashoffset: 1;
  animation: sweep 1s linear forwards;
  animation-delay: var(--into, 0ms);
}

@keyframes sweep {
  to {
    stroke-dashoffset: 0;
  }
}

.countdown-caption {
  color: var(--muted);
  font-size: var(--step-1);
}

.countdown-ring {
  width: clamp(11rem, 32vw, 16rem);
  aspect-ratio: 1;
  border: 1.5px solid var(--white);
  border-radius: 50%;
  animation: go-ring 0.5s var(--ease-out) forwards;
}

@keyframes go-ring {
  from {
    opacity: 0.9;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(1.9);
  }
}

.join-playback {
  display: grid;
  justify-items: center;
  gap: 1rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  background: none;
  cursor: pointer;
}

.join-playback-disc {
  display: grid;
  place-items: center;
  width: 92px;
  height: 92px;
  border-radius: 50%;
  background: var(--white);
  color: var(--black);
  box-shadow: 0 0 0 10px rgb(255 255 255 / 0.12), 0 0 80px rgb(255 255 255 / 0.25);
  transition: transform 0.2s var(--ease-out);
}

.join-playback:hover .join-playback-disc {
  transform: scale(1.05);
}

.join-playback-disc .icon {
  width: 38px;
  height: 38px;
  margin-left: 5px;
}

/* Invite popover */

.popover {
  position: fixed;
  inset: 70px max(18px, env(safe-area-inset-right)) auto auto;
  width: min(calc(100vw - 36px), 400px);
  margin: 0;
  padding: 1.25rem;
  border: 0;
  border-radius: 18px;
  background: var(--glass-solid);
  backdrop-filter: blur(24px);
  color: var(--text);
  box-shadow: 0 0 0 1px var(--line), 0 24px 64px rgb(0 0 0 / 0.55);
}

.popover:popover-open {
  display: grid;
  gap: 0.6rem;
  animation: toast-in 0.4s var(--spring);
}

.popover-title {
  font: 600 var(--step-2) / 1.2 var(--font);
}

.popover-text {
  color: var(--muted);
  font-size: var(--step--1);
}

.popover-faint {
  color: var(--faint);
}

.popover-faint code {
  font-family: var(--font-mono);
  font-size: 0.95em;
}

/* The QR code: black modules on white with its quiet zone, as scanners expect, drawn square-edged
   at whole-module scale so a phone reads it off the screen first time. */
.invite-qr {
  justify-self: center;
  margin: 0.25rem 0 0.35rem;
  border-radius: 10px;
  background: var(--white);
  overflow: hidden;
  line-height: 0;
}

.invite-qr svg {
  display: block;
  shape-rendering: crispEdges;
  image-rendering: pixelated;
}

.copy-row {
  display: flex;
  gap: 0.5rem;
}

.copy-row .input {
  min-height: 40px;
}

.copy-row .btn {
  flex: none;
}

/* ---------- Dropping a file anywhere ---------- */

/* The whole window, dimmed, with a soft edge drawn just inside it: this is where it goes. */
.drop {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: rgb(0 0 0 / 0.8);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.22s var(--ease-out), visibility 0s linear 0.22s;
}

/* The page steps back too, so the words read even where there's no blur behind them. */
.screen {
  transition: opacity 0.25s var(--ease-out);
}

body:has(> .drop[data-state="accept"], > .drop[data-state="refuse"]) .screen {
  opacity: 0.35;
}

.drop::before {
  content: "";
  position: absolute;
  inset: max(14px, env(safe-area-inset-top)) max(14px, env(safe-area-inset-right)) max(14px, env(safe-area-inset-bottom))
    max(14px, env(safe-area-inset-left));
  border-radius: 28px;
  box-shadow: inset 0 0 0 1.5px rgb(255 255 255 / 0.5);
  opacity: 0;
  transform: scale(1.03);
  transition: transform 0.45s var(--spring), opacity 0.3s var(--ease-out), box-shadow 0.3s;
}

.drop[data-state] {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.18s var(--ease-out);
}

.drop[data-state="accept"]::before {
  opacity: 1;
  transform: none;
}

.drop[data-state="refuse"]::before {
  opacity: 1;
  transform: scale(0.99);
  box-shadow: inset 0 0 0 1.5px rgb(255 255 255 / 0.14);
}

/* Let go: the edge settles and everything fades back to the page. */
.drop[data-state="taken"] {
  opacity: 0;
  transition: opacity 0.4s var(--ease-out) 0.05s;
}

.drop-body {
  display: grid;
  justify-items: center;
  gap: 1.1rem;
  text-align: center;
  pointer-events: none;
  transform: translateY(6px);
  transition: transform 0.45s var(--spring);
}

.drop[data-state] .drop-body {
  transform: none;
}

.drop-mark {
  width: 160px;
  height: 70px;
  overflow: visible;
}

.drop-you,
.drop-friend {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.5s var(--spring), opacity 0.3s, stroke-dasharray 0.3s;
}

.drop-you {
  fill: var(--text);
}

.drop-friend {
  fill: none;
  stroke: var(--text);
  stroke-width: 1.5;
}

/* A video on its way: the two of you lean in, ready. */
.drop[data-state="accept"] .drop-you {
  transform: translateX(3px);
}

.drop[data-state="accept"] .drop-friend {
  transform: translateX(-3px);
  animation: lean 1.6s ease-in-out 0.5s infinite alternate;
}

@keyframes lean {
  from {
    transform: translateX(-3px);
  }
  to {
    transform: translateX(-6px);
  }
}

/* Anything else: they drift apart and the friend goes faint. */
.drop[data-state="refuse"] .drop-you {
  transform: translateX(-6px);
  opacity: 0.4;
}

.drop[data-state="refuse"] .drop-friend {
  transform: translateX(6px);
  opacity: 0.4;
  stroke-dasharray: 3 3;
}

.drop[data-state="taken"] .drop-you,
.drop[data-state="taken"] .drop-friend {
  transform: scale(0.9);
}

.drop-title {
  font: 600 var(--step-3) / 1.1 var(--font);
  letter-spacing: -0.015em;
  transition: color 0.3s;
}

.drop[data-state="refuse"] .drop-body {
  animation: no 0.45s var(--ease-out);
}

@keyframes no {
  25% {
    transform: translateX(-5px);
  }
  55% {
    transform: translateX(4px);
  }
  80% {
    transform: translateX(-2px);
  }
}

.drop[data-state="refuse"] .drop-title {
  color: var(--muted);
}

.noscript {
  padding: 2rem;
  text-align: center;
}

/* ---------- Small screens ---------- */

@media (max-width: 760px) {
  .person-text,
  .topbar .btn-primary span {
    display: none;
  }

  .people {
    gap: 0;
  }

  .person {
    padding: 0;
    background: none;
    backdrop-filter: none;
  }

  .topbar .btn-primary {
    width: 40px;
    padding: 0;
  }

  .volume-slider {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
