@font-face {
  font-family: 'Atkinson Hyperlegible Next';
  src: url('/vendor/fonts/atkinson-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Atkinson Hyperlegible Next';
  src: url('/vendor/fonts/atkinson-700.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

:root {
  --caption-size: 30px;
  --font: 'Atkinson Hyperlegible Next', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --ease: cubic-bezier(0.2, 0, 0, 1);
  /* Caption appearance: display.js overrides these from the Display panel. */
  --cap-font: var(--font);
  --cap-weight: 400;
  --cap-leading: 1.34;
  --cap-tracking: 0em;
  --cap-color: var(--text);
  --cap-box: transparent;
  --cap-edge: none;
  --earlier-opacity: 1;
}

/* Themes are plain attribute selectors, so any element can carry one: the theme tiles in the
   Display panel preview themselves this way. Dark is also the default for the whole page. */
:root,
[data-theme='dark'] {
  color-scheme: dark;
  /* Neutrals lean slightly blue so surfaces read as one family rather than flat greys. */
  --bg: #0e1015;
  --surface: #15181e;
  --surface-2: #1e222a;
  --surface-3: #282d37;
  --line: #2a2e38;
  /* Text-field outlines: at least 3:1 against both the field and the surface around it. */
  --field-border: #6c7280;
  --text: #f2f4f8;
  --text-2: #abb1bd;
  --interim: #9ba1ad;
  --accent: #8fb0ff;
  --accent-hover: #a7c1ff;
  --accent-ink: #0a1433;
  --ok: #5fdc96;
  --warn: #ffc94d;
  --danger: #ff8a8a;
  --local: #8fb0ff;
  --demo: #c9a2ff;
  --reply-bg: #22304f;
  --reply-text: #eef3ff;
  /* How strongly a speaker or status color tints its pill. */
  --tint: 16%;
  --tint-hover: 26%;
  /* Shadow rings stand in for borders on buttons and cards, so they sit well on any surface. */
  --ring: 0 0 0 1px rgb(255 255 255 / 0.08);
  --ring-hover: 0 0 0 1px rgb(255 255 255 / 0.13);
  --shadow-lg: 0 0 0 1px rgb(255 255 255 / 0.06), 0 24px 70px rgb(0 0 0 / 0.55);
  --backdrop: rgb(4 6 10 / 0.66);
  /* Speaker colors: labels and the bar beside each caption. */
  --spk-0: #abb1bd;
  --spk-1: #ffd54a;
  --spk-2: #6fd3ff;
  --spk-3: #ff8fc7;
  --spk-4: #8be58b;
  --spk-5: #ffae66;
  --spk-6: #c3a6ff;
  /* Chart series, stepped for dark surfaces and checked for color blindness as a set. */
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #9085e9;
}

/* Softer than Dark: a mid-grey page for less glare in bright rooms. */
[data-theme='dim'] {
  --bg: #1f2127;
  --surface: #26292f;
  --surface-2: #2f333a;
  --surface-3: #393d45;
  --line: #353941;
  --field-border: #7a808c;
  --text: #e3e5ea;
  --text-2: #aeb3bd;
  --interim: #9ea3ad;
  --accent: #9db8f5;
  --accent-hover: #b4c9f8;
  --accent-ink: #111a33;
  --local: #9db8f5;
  --reply-bg: #2c3a57;
  --reply-text: #eef2fb;
}

/* Status, speaker and shadow colors shared by every light-background theme. */
[data-theme='light'],
[data-theme='paper'],
[data-theme='custom'][data-mode='light'] {
  color-scheme: light;
  --ok: #157a3f;
  --warn: #9a6200;
  --danger: #c0262d;
  --demo: #6b3fc0;
  --tint: 11%;
  --tint-hover: 18%;
  --ring: 0 0 0 1px rgb(0 0 0 / 0.07), 0 1px 2px -1px rgb(0 0 0 / 0.06), 0 2px 4px 0 rgb(0 0 0 / 0.04);
  --ring-hover: 0 0 0 1px rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.08), 0 2px 4px 0 rgb(0 0 0 / 0.06);
  --shadow-lg: 0 0 0 1px rgb(0 0 0 / 0.06), 0 24px 70px rgb(16 24 40 / 0.2);
  --backdrop: rgb(16 20 28 / 0.42);
  --spk-0: #4a505d;
  --spk-1: #8a5a00;
  --spk-2: #005f94;
  --spk-3: #a3195b;
  --spk-4: #1e7a1e;
  --spk-5: #a04800;
  --spk-6: #5b3db5;
  --series-1: #2a78d6;
  --series-2: #d4561f;
  --series-3: #138a60;
  --series-4: #6250d6;
}

[data-theme='light'] {
  --bg: #f4f5f8;
  --surface: #ffffff;
  --surface-2: #eceef3;
  --surface-3: #e1e4eb;
  --line: #dadde4;
  --field-border: #7c828e;
  --text: #12141a;
  --text-2: #4a505d;
  --interim: #5f6573;
  --accent: #3355d6;
  --accent-hover: #2945b8;
  --accent-ink: #ffffff;
  --local: #3355d6;
  --reply-bg: #e4eafc;
  --reply-text: #0d1a40;
}

/* Warm cream and brown ink, like a printed page. */
[data-theme='paper'] {
  --bg: #f6f0e3;
  --surface: #fbf8f0;
  --surface-2: #ede5d4;
  --surface-3: #e3d9c4;
  --line: #ddd2bd;
  --field-border: #857a68;
  --text: #2b2219;
  --text-2: #5f5342;
  --interim: #6e6250;
  --accent: #9c4a14;
  --accent-hover: #833d0f;
  --accent-ink: #ffffff;
  --local: #9c4a14;
  --reply-bg: #efdfc6;
  --reply-text: #2b2219;
}

/* Tints and soft shadows give way to solid white outlines. */
[data-theme='contrast'] {
  --bg: #000;
  --surface: #000;
  --surface-2: #161616;
  --surface-3: #262626;
  --line: #ffffff;
  --field-border: #ffffff;
  --text: #ffffff;
  --text-2: #ffffff;
  --interim: #ffe14d;
  --accent: #ffe14d;
  --accent-hover: #fff09a;
  --accent-ink: #000;
  --reply-bg: #0b3b8c;
  --reply-text: #ffffff;
  --tint: 0%;
  --tint-hover: 18%;
  --ring: 0 0 0 1px #ffffff;
  --ring-hover: 0 0 0 2px #ffffff;
  --shadow-lg: 0 0 0 2px #ffffff;
  --backdrop: rgb(0 0 0 / 0.8);
  --spk-0: #ffffff;
  --series-1: #ffe14d;
  --series-2: #ff8fc7;
  --series-3: #6fd3ff;
}

/* Custom: three chosen colors (set by display.js); every other surface is mixed from them. */
[data-theme='custom'] {
  --bg: var(--custom-bg);
  --text: var(--custom-text);
  --accent: var(--custom-accent);
  --accent-ink: var(--custom-accent-ink);
  --accent-hover: color-mix(in srgb, var(--custom-accent) 82%, var(--custom-text));
  --surface: color-mix(in srgb, var(--custom-bg), var(--custom-text) 4%);
  --surface-2: color-mix(in srgb, var(--custom-bg), var(--custom-text) 9%);
  --surface-3: color-mix(in srgb, var(--custom-bg), var(--custom-text) 14%);
  --line: color-mix(in srgb, var(--custom-bg), var(--custom-text) 14%);
  --field-border: color-mix(in srgb, var(--custom-bg), var(--custom-text) 55%);
  --text-2: color-mix(in srgb, var(--custom-text) 74%, var(--custom-bg));
  --interim: color-mix(in srgb, var(--custom-text) 66%, var(--custom-bg));
  --local: var(--custom-accent);
  --reply-bg: color-mix(in srgb, var(--custom-bg), var(--custom-accent) 22%);
  --reply-text: var(--custom-text);
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

button {
  cursor: pointer;
}

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

[hidden] {
  display: none !important;
}

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

/* ---------- top bar ---------- */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right)) 8px max(12px, env(safe-area-inset-left));
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

/* A pill tinted with the state color, so the state reads at a glance without relying on the dot alone. */
.status {
  --state: var(--spk-0);
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  min-height: 36px;
  padding: 6px 14px 6px 12px;
  border-radius: 18px;
  background: var(--surface-2);
  background: color-mix(in srgb, var(--state) var(--tint), transparent);
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: background-color 150ms ease-out;
}

/* Wraps rather than truncating: the state (and download progress) must stay readable on narrow phones. */
#status-text {
  line-height: 1.35;
}

.dot {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--state, var(--spk-0));
}

.status[data-state='cloud'] {
  --state: var(--ok);
}
.status[data-state='local'] {
  --state: var(--local);
}
.status[data-state='demo'] {
  --state: var(--demo);
}
.status[data-state='failed'] {
  --state: var(--danger);
}
.status[data-state='starting'],
.status[data-state='connecting'],
.status[data-state='loading'],
.status[data-state='waiting'] {
  --state: var(--warn);
}
[data-state='starting'] .dot,
[data-state='connecting'] .dot,
[data-state='loading'] .dot,
[data-state='waiting'] .dot {
  animation: pulse 1.1s ease-in-out infinite;
}

@keyframes pulse {
  50% {
    opacity: 0.25;
  }
}

.meter {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 16px;
  flex: none;
  margin-left: 2px;
}
.meter i {
  width: 3px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--text) 22%, transparent);
  transition: background-color 100ms ease-out;
}
.meter i:nth-child(1) { height: 5px; }
.meter i:nth-child(2) { height: 8px; }
.meter i:nth-child(3) { height: 11px; }
.meter i:nth-child(4) { height: 14px; }
.meter i:nth-child(5) { height: 16px; }
.meter i.on {
  background: var(--state);
}
[data-state='off'] .meter {
  display: none;
}

.bar-actions {
  display: flex;
  gap: 2px;
  flex: none;
}
/* A narrow phone with a home agent connected has five buttons up here: everything tightens a little so
   "Not captioning" still fits on one line beside them. */
@media (max-width: 400px) {
  .bar {
    gap: 6px;
  }
  .bar-actions {
    gap: 0;
  }
  .bar-actions .icon-btn {
    width: 42px;
  }
  .status {
    gap: 6px;
    padding: 6px 11px 6px 10px;
    font-size: 14px;
  }
}

.icon-btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: transparent;
  color: var(--text);
  transition-property: background-color, color, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .icon-btn:hover {
    background: var(--surface-2);
  }
}
.icon-btn:active {
  scale: 0.96;
}
.icon-btn svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-btn.small {
  width: 36px;
  height: 36px;
  border-radius: 10px;
}
.icon-btn.small svg {
  width: 20px;
  height: 20px;
}

.badge {
  position: absolute;
  top: 3px;
  right: 1px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg);
  color: var(--accent-ink);
  font-size: 12px;
  font-weight: 700;
  line-height: 18px;
}

/* An inset card whose bar matches the caption bars; its color says what kind of notice it is. */
.banner {
  --tone: var(--accent);
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 10px max(12px, env(safe-area-inset-right)) 0 max(12px, env(safe-area-inset-left));
  padding: 6px 6px 6px 12px;
  border-radius: 16px;
  background: var(--surface-2);
  background: color-mix(in srgb, var(--tone) var(--tint), var(--bg));
  font-size: 15px;
  line-height: 1.4;
}
.banner::before {
  content: '';
  flex: none;
  align-self: stretch;
  width: 4px;
  margin: 4px 0;
  border-radius: 2px;
  background: var(--tone);
}
.banner[data-id='insecure'],
.banner[data-id='access'] {
  --tone: var(--warn);
}
.banner[data-id='demo'] {
  --tone: var(--demo);
}
.banner p {
  flex: 1;
  margin: 0;
  text-wrap: pretty;
}

/* ---------- captions ---------- */

.captions {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 20px max(16px, calc((100% - 72rem) / 2)) 32px;
  font-family: var(--cap-font);
  font-size: var(--caption-size);
  line-height: var(--cap-leading);
}
/* Earlier lines fade out under the top edge instead of being sliced mid-letter.
   The fade stays inside the top padding, so a line at rest is never dimmed. */
#captions:not(:focus-visible) {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 18px);
  mask-image: linear-gradient(to bottom, transparent, #000 18px);
}
.captions:focus-visible {
  outline-offset: -3px;
}

.line {
  --spk: var(--spk-0);
  position: relative;
  margin: 0 0 0.5em;
  padding-left: 0.5em;
}
/* The speaker bar: a rounded stroke rather than a square border. */
.line::before {
  content: '';
  position: absolute;
  top: 0.14em;
  bottom: 0.14em;
  left: 0;
  width: 4px;
  border-radius: 2px;
  background: var(--spk);
}
.line[data-color='1'] { --spk: var(--spk-1); }
.line[data-color='2'] { --spk: var(--spk-2); }
.line[data-color='3'] { --spk: var(--spk-3); }
.line[data-color='4'] { --spk: var(--spk-4); }
.line[data-color='5'] { --spk: var(--spk-5); }
.line[data-color='6'] { --spk: var(--spk-6); }
.line[data-color='0']::before,
.line.reply::before {
  display: none;
}
/* A new speaker gets more room above than a continuation of the same speaker. */
.line + .line:has(> .who:not([hidden])),
.line + .pending > .line:first-child:has(> .who:not([hidden])) {
  margin-top: 1em;
}

.who {
  display: block;
  margin: 0 0 0.1em;
  padding: 0;
  border: 0;
  background: none;
  color: var(--spk);
  font: inherit;
  font-size: max(14px, 0.5em);
  font-weight: 700;
  line-height: 1.3;
  text-align: left;
}
/* Tap to rename: a pill tinted with the speaker's color, so it reads as a control, not part of the caption. */
button.who,
.who.as-button {
  display: inline-block;
  margin-bottom: 0.2em;
  padding: 0.2em 0.65em;
  border-radius: 999px;
  background: var(--surface-2);
  background: color-mix(in srgb, var(--spk) var(--tint), transparent);
  transition-property: background-color, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  button.who:hover {
    background: color-mix(in srgb, var(--spk) var(--tint-hover), transparent);
  }
}
button.who:active {
  scale: 0.96;
}
button.who {
  position: relative;
}
button.who::after {
  content: '';
  position: absolute;
  inset: -8px -4px;
}

.said {
  display: block;
  margin: 0;
  overflow-wrap: anywhere;
}

/* In-progress words may still change: dimmer, and dotted so the difference doesn't rest on color. */
.tmp {
  color: var(--cap-interim, var(--interim));
  text-decoration: underline dotted;
  text-underline-offset: 0.18em;
}

/* A line someone read back from the captions: folded behind a small button, and dimmed when opened
   or while it's still being said, so the original stays the one that stands out. */
.fold {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  min-height: 32px;
  margin: 0 0 0.1em;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-2);
  font: inherit;
  font-size: max(14px, 0.5em);
  font-weight: 700;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
}
.fold::before {
  content: '▸';
}
.fold[aria-expanded='true']::before {
  content: '▾';
}
@media (hover: hover) {
  .fold:hover {
    text-decoration: underline;
  }
}
.line.readback::before {
  opacity: 0.4;
}
.line.readback .said,
.line.readback .tr {
  opacity: 0.6;
}
.line.folded .said,
.line.folded .tr {
  display: none;
}

/* New words appear with the short animation chosen in the Display panel (data-caption-motion), at
   most a fifth of a second so reading is never held up. Only a word's first appearance animates.
   With reduced motion, rising and blurring become a plain fade. */
@keyframes word-fade {
  from {
    opacity: 0;
  }
}
@keyframes word-rise {
  from {
    opacity: 0;
    top: 0.3em;
  }
}
@keyframes word-blur {
  from {
    opacity: 0;
    filter: blur(4px);
  }
}
.w-in {
  position: relative;
}
[data-caption-motion='fade'] .w-in,
[data-caption-motion='rise'] .w-in,
[data-caption-motion='blur'] .w-in {
  animation: word-fade 160ms ease-out both;
}
@media (prefers-reduced-motion: no-preference) {
  [data-caption-motion='rise'] .w-in {
    animation: word-rise 200ms cubic-bezier(0.2, 0, 0, 1) both;
  }
  [data-caption-motion='blur'] .w-in {
    animation: word-blur 200ms cubic-bezier(0.2, 0, 0, 1) both;
  }
}

/* Words the speech service may have misheard keep their color, with a wavy underline: "check this". */
.unsure {
  text-decoration: underline wavy;
  text-decoration-color: var(--cap-interim, var(--interim));
  text-decoration-skip-ink: auto;
  text-underline-offset: 0.2em;
}
[data-plain-unsure] .unsure {
  text-decoration: none;
}

/* A translation sits under the caption it translates, a step smaller, after the languages it's between. */
.tr {
  display: block;
  margin: 0.15em 0 0;
  font-size: 0.85em;
  overflow-wrap: anywhere;
}
.tr-label {
  margin-right: 0.45em;
  color: var(--text-2);
  font-size: max(13px, 0.55em);
  font-weight: 700;
  white-space: nowrap;
}

/* ---------- caption appearance (Display panel) ---------- */

.said,
.tr {
  font-weight: var(--cap-weight);
  letter-spacing: var(--cap-tracking);
}
.line:not(.reply) .said,
.line:not(.reply) .tr {
  color: var(--cap-color);
  text-shadow: var(--cap-edge);
}

/* A box behind each caption, like broadcast subtitles. */
[data-cap-box] .line:not(.reply) .said,
[data-cap-box] .line:not(.reply) .tr {
  width: fit-content;
  max-width: 100%;
  padding: 0.04em 0.3em 0.08em;
  border-radius: 0.2em;
  background: var(--cap-box);
}

[data-align='center'] .line:not(.reply) {
  padding-left: 0;
  text-align: center;
}
[data-align='center'] .line:not(.reply)::before {
  display: none;
}
[data-align='center'][data-cap-box] .line:not(.reply) .said,
[data-align='center'][data-cap-box] .line:not(.reply) .tr {
  margin-inline: auto;
}

[data-mono-speakers] .line[data-color] {
  --spk: var(--spk-0);
}

/* Everything before the latest two lines can be dimmed (the last child is the in-progress group). */
#captions > .line {
  transition: opacity 300ms ease-out;
}
#captions > .line:not(:nth-last-child(-n + 3)) {
  opacity: var(--earlier-opacity);
}

.line.reply {
  display: block;
  width: fit-content;
  max-width: 85%;
  margin-left: auto;
  padding: 0.35em 0.65em 0.4em;
  border: 0;
  border-radius: 0.7em 0.7em 0.2em 0.7em;
  background: var(--reply-bg);
  color: var(--reply-text);
  font: inherit;
  font-size: 0.82em;
  text-align: start;
  transition: background-color 150ms ease-out;
}
.line.reply .who {
  margin-bottom: 0.15em;
  color: var(--accent);
}

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  max-width: 30em;
  min-height: 85%;
  margin: 0 auto;
  color: var(--text-2);
  font-size: 18px;
  line-height: 1.45;
  text-align: center;
  text-wrap: balance;
}
.empty p {
  margin: 0;
}
.empty .empty-title {
  margin: 0 0 8px;
  color: var(--text);
  font-size: 30px;
  font-weight: 700;
  line-height: 1.2;
}
/* First impression: the parts arrive in reading order. */
.empty > * {
  animation: rise 400ms ease-out backwards;
}
.empty > :nth-child(2) { animation-delay: 100ms; }
.empty > :nth-child(3) { animation-delay: 200ms; }
.empty > :nth-child(4) { animation-delay: 300ms; }

@keyframes rise {
  from {
    opacity: 0;
    translate: 0 12px;
    filter: blur(4px);
  }
}

.empty-mark {
  width: 88px;
  height: 88px;
  margin-bottom: 16px;
}
.empty-mark .bubble {
  fill: var(--surface-2);
  stroke: var(--text);
  stroke-width: 2.5;
  stroke-linejoin: round;
}
.empty-mark .bar-1 {
  fill: var(--spk-1);
  animation: write-bar-1 3.2s infinite;
}
.empty-mark .bar-2 {
  fill: var(--spk-2);
  animation: write-bar-2 3.2s infinite;
}
/* The caption bars are written in, one after the other (eased out), held, and faded out: the Pi's
   start-up mark's loop (IDLE_PAGE in helper/pi.js). */
@keyframes write-bar-1 {
  0% {
    clip-path: inset(0 100% 0 0);
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  18.75%,
  75% {
    clip-path: inset(0);
    opacity: 1;
  }
  87.5%,
  100% {
    clip-path: inset(0);
    opacity: 0;
  }
}
@keyframes write-bar-2 {
  0%,
  12.5% {
    clip-path: inset(0 100% 0 0);
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  34.375%,
  75% {
    clip-path: inset(0);
    opacity: 1;
  }
  87.5%,
  100% {
    clip-path: inset(0);
    opacity: 0;
  }
}
/* While listening, the caption bars breathe; the "Listening…" title carries the same message. */
.empty:has(#start-big[hidden]) .empty-mark rect {
  animation: pulse 1.6s ease-in-out infinite;
}
.empty:has(#start-big[hidden]) .empty-mark .bar-2 {
  animation-delay: 0.3s;
}

/* Floats just above the composer, whatever height the composer has. */
.jump {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 12px);
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px 10px 18px;
  border: 0;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  transform: translateX(-50%);
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.3);
  animation: pop-in 200ms var(--ease);
  transition-property: background-color, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .jump:hover {
    background: var(--accent-hover);
  }
}
.jump:active {
  scale: 0.96;
}

@keyframes pop-in {
  from {
    opacity: 0;
    translate: 0 8px;
  }
}

/* ---------- composer ---------- */

.composer {
  --pad-l: max(12px, env(safe-area-inset-left));
  --pad-r: max(12px, env(safe-area-inset-right));
  position: relative;
  padding: 10px var(--pad-r) max(12px, env(safe-area-inset-bottom)) var(--pad-l);
  background: var(--surface);
  border-top: 1px solid var(--line);
}

/* Runs edge to edge and fades out at the sides, so a cut-off chip reads as "scroll for more". */
.phrases {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  margin: -6px calc(-1 * var(--pad-r)) 4px calc(-1 * var(--pad-l));
  padding: 6px var(--pad-r) 6px var(--pad-l);
  scrollbar-width: thin;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8px, #000 calc(100% - 40px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 8px, #000 calc(100% - 40px), transparent);
}

.chip {
  flex: none;
  min-height: 40px;
  padding: 8px 15px;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  color: var(--text);
  font: inherit;
  font-size: 15px;
  white-space: nowrap;
  transition-property: background-color, box-shadow, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .chip:hover {
    background: var(--surface-3);
    box-shadow: var(--ring-hover);
  }
}
.chip:active {
  scale: 0.96;
}

.reply-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

/* The text box and its send button share one pill. */
.reply-box {
  display: flex;
  align-items: flex-end;
  flex: 1;
  min-width: 0;
  border: 1px solid var(--field-border);
  border-radius: 28px;
  background: var(--bg);
}

#reply-input {
  flex: 1;
  min-width: 0;
  min-height: 54px;
  max-height: 30vh;
  padding: 13px 4px 13px 19px;
  /* Transparent rather than none: app.js sizes the box as scrollHeight plus a 1px border each side. */
  border: 1px solid transparent;
  border-radius: 27px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 18px;
  line-height: 1.35;
  resize: none;
}
@supports selector(:has(*)) {
  #reply-input:focus-visible {
    outline: none;
  }
  .reply-box:has(#reply-input:focus-visible) {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
  }
}

/* Lights up once there's something to send. */
.send {
  flex: none;
  margin: 5px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
}
@media (hover: hover) {
  .send:hover {
    background: var(--accent-hover);
  }
}
#reply-input:placeholder-shown ~ .send {
  background: transparent;
  color: var(--text-2);
}
@media (hover: hover) {
  #reply-input:placeholder-shown ~ .send:hover {
    background: var(--surface-2);
    color: var(--text);
  }
}

.mic-btn {
  flex: none;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  transition-property: background-color, color, box-shadow, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .mic-btn:hover {
    background: var(--accent-hover);
  }
}
.mic-btn:active {
  scale: 0.96;
}
/* Both icons stay in place and cross-fade, so the swap reads as one control changing state. */
.mic-btn svg {
  grid-area: 1 / 1;
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  transition-property: opacity, scale, filter;
  transition-duration: 300ms;
  transition-timing-function: var(--ease);
}
.mic-btn .pause,
.mic-btn.listening .mic {
  opacity: 0;
  scale: 0.25;
  filter: blur(4px);
}
.mic-btn.listening .pause {
  opacity: 1;
  scale: 1;
  filter: none;
}
.mic-btn.listening {
  background: var(--surface-2);
  background: color-mix(in srgb, var(--ok) var(--tint), var(--surface));
  color: var(--text);
  box-shadow: inset 0 0 0 2.5px var(--ok);
}
@media (hover: hover) {
  .mic-btn.listening:hover {
    background: color-mix(in srgb, var(--ok) var(--tint-hover), var(--surface));
  }
}

/* ---------- buttons and forms ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 16px;
  border: 0;
  border-radius: 12px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  color: var(--text);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  text-decoration: none; /* a link can be a button too */
  transition-property: background-color, box-shadow, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .btn:hover {
    background: var(--surface-3);
    box-shadow: var(--ring-hover);
  }
}
.btn:active {
  scale: 0.96;
}
.btn svg {
  flex: none;
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.btn.primary {
  background: var(--accent);
  box-shadow: none;
  color: var(--accent-ink);
}
@media (hover: hover) {
  .btn.primary:hover {
    background: var(--accent-hover);
  }
}
/* The leading icon side gets 2px less padding so the label looks centered. */
.btn.big {
  min-height: 56px;
  margin-top: 28px;
  padding: 0 32px 0 28px;
  border-radius: 16px;
  font-size: 20px;
}
.btn.big svg {
  width: 24px;
  height: 24px;
  stroke-width: 2.2;
}
.btn.small {
  min-height: 36px;
  padding: 0 12px;
  border-radius: 10px;
  font-size: 14px;
}
.btn.subtle {
  background: transparent;
  box-shadow: none;
}
@media (hover: hover) {
  .btn.subtle:hover {
    background: var(--surface-2);
  }
}
.btn.danger {
  color: var(--danger);
}
.btn:disabled {
  opacity: 0.5;
  cursor: default;
  scale: none;
}
/* In a card, a button sits on the card's color, so it's a step lighter than usual. */
.card .btn:not(.primary, .subtle) {
  background: var(--surface-3);
}
@media (hover: hover) {
  .card .btn:not(.primary, .subtle):hover {
    background: color-mix(in srgb, var(--surface-3), var(--text) 6%);
  }
}

input:not([type]),
input[type='text'],
input[type='email'],
input[type='url'],
input[type='password'],
textarea,
select {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--field-border);
  border-radius: 12px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 17px;
  line-height: 1.4;
}
textarea {
  resize: vertical;
}

select {
  padding-right: 42px;
  appearance: none;
  /* A mid-grey chevron reads on every theme's field color. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b919d' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 18px;
}
/* In a card a choice is a pill showing the current value, like a button, rather than a text field. */
.card select {
  min-height: 42px;
  padding: 8px 38px 8px 14px;
  border: 0;
  border-radius: 11px;
  background-color: var(--surface-3);
  box-shadow: var(--ring);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
  background-position: right 12px center;
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  .card select:hover {
    background-color: color-mix(in srgb, var(--surface-3), var(--text) 6%);
  }
}

::placeholder {
  color: var(--text-2);
  opacity: 1;
}

/* ---------- settings ---------- */

/* One setting: its label, a hint under it, then the control. On its own it's a form field; in a card it's a row. */
.setting {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  min-width: 0;
  margin: 16px 0;
  padding: 0;
  border: 0;
}
.setting > :is(label, legend, .label),
.setting > .text > :is(label, .label) {
  padding: 0;
  font-size: 16px;
  line-height: 1.35;
}
/* A floated legend is laid out like any other child, so the gaps stay even. */
.setting > legend {
  float: left;
  width: 100%;
}
.setting > legend > small {
  margin-top: 2px;
}
/* A button in a setting is as wide as its label, not the row. */
.setting > .btn {
  align-self: flex-start;
}
/* A hint hugs its label, before the control. */
.setting > small,
.setting > .text > small {
  margin-top: -4px;
}
/* A slider's label carries its value at the end of the line. */
.setting > label.readout {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}
output {
  color: var(--text-2);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}
small {
  display: block;
  color: var(--text-2);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  text-wrap: pretty;
}

/* Label and hint on the left, a short control on the right. When there's no room for both, the control
   drops under the text. */
.setting.inline {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
}
.setting.inline > .text {
  display: flex;
  flex: 1 1 14em;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.setting.inline > .text > small {
  margin: 0;
}
.setting.inline > :is(select, .btn) {
  flex: 0 0 auto;
  width: auto;
  max-width: 100%;
}
/* A pair of buttons has room beside even a short label. */
.setting.inline:has(> .stepper) > .text {
  flex-basis: 0;
}
.setting.inline > .stepper {
  flex: none;
}
.setting.inline > progress {
  flex-basis: 100%;
  margin: 0;
}

/* A setting that's on or off: a switch at the end of its row, after the label and hint. */
.toggle {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: 'label switch' 'hint switch';
  align-items: center;
  column-gap: 16px;
  row-gap: 4px;
  margin: 16px 0;
}
.toggle > label {
  grid-area: label;
  font-size: 16px;
  line-height: 1.35;
  cursor: pointer;
}
.toggle > small {
  grid-area: hint;
}
.toggle input {
  grid-area: switch;
  width: 48px;
  height: 28px;
  margin: 0;
  border-radius: 14px;
  appearance: none;
  background: var(--surface-3) radial-gradient(circle closest-side, var(--text-2) 92%, transparent) no-repeat 4px 50% / 20px 20px;
  box-shadow: inset 0 0 0 1px var(--field-border);
  cursor: pointer;
  transition-property: background-color, background-position, box-shadow;
  transition-duration: 200ms;
  transition-timing-function: var(--ease);
}
.toggle input:checked {
  background-color: var(--accent);
  background-image: radial-gradient(circle closest-side, var(--accent-ink) 92%, transparent);
  background-position: calc(100% - 4px) 50%;
  box-shadow: none;
}

/* One of several choices, with a line about it; the chosen one's dot fills in. */
.radio {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 0;
  font-size: 16px;
  line-height: 1.35;
  cursor: pointer;
}
.radio > span {
  flex: 1;
}
.radio > span > small {
  margin-top: 2px;
}
.radio input {
  flex: none;
  width: 22px;
  height: 22px;
  margin: 0;
  border: 2px solid var(--field-border);
  border-radius: 50%;
  appearance: none;
  background: var(--bg);
  cursor: pointer;
  transition-property: border-color, box-shadow;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
.radio input:checked {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 3px var(--bg), inset 0 0 0 10px var(--accent);
}
.radios .radio {
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  .radios .radio:hover {
    background: var(--surface-3);
  }
}

/* One track with the choice raised inside it; inner radius = outer radius − padding. */
.segmented {
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: 14px;
  background: var(--surface-2);
  box-shadow: var(--ring);
}
.card .segmented {
  background: var(--bg);
}
.segmented label {
  position: relative;
  display: grid;
  flex: 1;
}
.segmented input {
  position: absolute;
  opacity: 0;
}
.segmented span {
  display: grid;
  place-items: center;
  min-height: 42px;
  padding: 8px 6px;
  border-radius: 10px;
  color: var(--text-2);
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition-property: background-color, color;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .segmented label:hover span {
    color: var(--text);
  }
}
.segmented input:checked + span {
  background: var(--accent);
  color: var(--accent-ink);
}
.segmented input:focus-visible + span {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* Sliders: a quiet track and a big round thumb in the accent color. */
input[type='range'] {
  width: 100%;
  height: 36px;
  margin: 0;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
input[type='range']::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: var(--surface-3);
  box-shadow: var(--ring);
}
input[type='range']::-webkit-slider-thumb {
  width: 24px;
  height: 24px;
  margin-top: -9px;
  border: 0;
  border-radius: 50%;
  appearance: none;
  background: var(--accent);
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.35);
  transition: scale 150ms ease-out;
}
input[type='range']:active::-webkit-slider-thumb {
  scale: 1.1;
}
input[type='range']::-moz-range-track {
  height: 6px;
  border-radius: 3px;
  background: var(--surface-3);
  box-shadow: var(--ring);
}
input[type='range']::-moz-range-progress {
  height: 6px;
  border-radius: 3px;
  background: var(--accent);
}
input[type='range']::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.35);
}

progress {
  width: 100%;
  height: 8px;
  overflow: hidden;
  border: 0;
  border-radius: 4px;
  appearance: none;
  background: var(--surface-3);
  accent-color: var(--accent);
}
progress::-webkit-progress-bar {
  background: var(--surface-3);
}
progress::-webkit-progress-value {
  border-radius: 4px;
  background: var(--accent);
}
progress::-moz-progress-bar {
  border-radius: 4px;
  background: var(--accent);
}
/* With no value, how far along is unknown: a band moves across instead (still, with reduced motion). */
progress:indeterminate {
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-size: 30% 100%;
  animation: progress-band 1.4s ease-in-out infinite;
}
progress:indeterminate::-webkit-progress-bar {
  background: transparent;
}
progress:indeterminate::-moz-progress-bar {
  background: transparent;
}
@keyframes progress-band {
  from {
    background-position: -50% 0;
  }
  to {
    background-position: 150% 0;
  }
}

.hint {
  margin: 6px 0;
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.5;
  text-wrap: pretty;
}

.row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.row.wrap {
  flex-wrap: wrap;
  justify-content: flex-start;
}
.row.end {
  justify-content: flex-end;
}

/* ---------- grouped settings ---------- */

/* Settings come in cards: a rounded block of rows, one setting each, parted by hairlines. The card
   clips its rows' hover color at the corners, so focus rings on whole rows are drawn inside. */
.card {
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 16px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  overflow: hidden;
}
.card > * {
  margin: 0;
  padding: 12px 16px;
}
.card > .setting:not(.inline) {
  padding: 12px 16px 16px;
}
.card > .toggle,
.card > .setting.inline {
  min-height: 56px;
}
.card > :not([hidden]) ~ :not([hidden]) {
  border-top: 1px solid var(--line);
}
.card > .device-list {
  margin: 0;
  padding: 0;
}
.card > .device-list:empty {
  display: none;
}
.card .device-item {
  padding: 12px 16px;
}
.card .font-aa {
  background: var(--bg);
}
.card-note {
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.5;
  text-wrap: pretty;
}

/* A row that does something: an action with its icon, or a step into another sheet (with a chevron). */
.row-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 54px;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 16px;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  .row-btn:hover {
    background: var(--surface-3);
  }
}
.row-btn:focus-visible {
  outline-offset: -3px;
}
.row-btn > svg {
  flex: none;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--accent);
}
.row-btn > span {
  flex: 1;
}
.row-btn.more::after {
  content: '';
  flex: none;
  width: 9px;
  height: 9px;
  margin-right: 4px;
  border-top: 2px solid var(--text-2);
  border-right: 2px solid var(--text-2);
  rotate: 45deg;
}
.row-btn.danger,
.row-btn.danger > svg {
  color: var(--danger);
}
.row-btn:disabled {
  color: var(--text-2);
  cursor: default;
}
.row-btn:disabled > svg {
  color: inherit;
}
@media (hover: hover) {
  .row-btn:disabled:hover {
    background: transparent;
  }
}

/* A card's heading sits above it, small and quiet, in line with the rows' text; a note under the
   card says more about the group. */
.group > :is(h3, h4),
.group-head > h3 {
  margin: 0 0 8px 16px;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.3;
  text-transform: uppercase;
}
/* A heading with something to do at its end: Edit. */
.group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0 16px 8px;
}
.group-head > h3 {
  margin: 0;
}
.group-head > .link-btn {
  margin: -10px 0;
  padding: 10px 0; /* a bigger target than the line it's on */
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
}
.group-note {
  margin: 8px 16px 0;
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.45;
  text-wrap: pretty;
}
.group-note:empty {
  display: none;
}

/* ---------- dialogs ---------- */

dialog.sheet {
  --sheet-pad: 24px; /* side padding */
  width: min(560px, calc(100% - 32px));
  max-height: min(88dvh, 920px);
  padding: 0 var(--sheet-pad) 28px;
  border: 0;
  border-radius: 24px;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lg);
  overflow-y: auto;
  overscroll-behavior: contain;
  transition-property: opacity, translate;
  transition-duration: 300ms;
  transition-timing-function: var(--ease);
}
/* Sheets ease in. */
@starting-style {
  dialog.sheet[open] {
    opacity: 0;
    translate: 0 16px;
  }
}
dialog::backdrop {
  background: var(--backdrop);
  transition-property: background-color;
  transition-duration: 300ms;
  transition-timing-function: var(--ease);
}
@starting-style {
  dialog[open]::backdrop {
    background: transparent;
  }
}
/* Where a closing dialog can stay on top for a moment, sheets slip away a little faster than they
   came, taking no taps meanwhile. Elsewhere closing is instant, as the focus has already moved on. */
@supports (overlay: auto) {
  dialog.sheet,
  .big-reply {
    transition-property: opacity, translate, display, overlay;
    transition-behavior: allow-discrete;
  }
  dialog.sheet:not([open]) {
    opacity: 0;
    translate: 0 8px;
    transition-duration: 200ms;
  }
  .big-reply:not([open]) {
    opacity: 0;
  }
  dialog:not([open]) {
    pointer-events: none;
  }
  dialog::backdrop {
    transition-property: background-color, display, overlay;
    transition-behavior: allow-discrete;
  }
  dialog:not([open])::backdrop {
    background: transparent;
    transition-duration: 200ms;
  }
}

/* A sheet that's a page (Devices, at /devices): the whole screen, its content in a column wide enough for a table. */
dialog.sheet.page-sheet {
  width: 100%;
  max-width: 100%;
  height: 100dvh;
  max-height: none;
  margin: 0;
  padding: 0 16px max(24px, env(safe-area-inset-bottom));
  border-radius: 0;
}
.page-sheet-inner {
  max-width: 1400px;
  margin: 0 auto;
}
.devices-page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 12px;
}
/* Each row's name, status and buttons line up across it. */
#devices-table .admin-table td {
  vertical-align: middle;
}
.device-name-cell {
  display: flex;
  align-items: center;
  gap: 10px;
}
label.device-name-cell {
  cursor: pointer;
}
.device-name-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.device-name-cell > .device-art {
  flex: none;
  display: flex;
  color: var(--text-2);
}
.device-maker-cell {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
/* On a white tile, as the remote's app logos are, so a dark logo shows on the dark theme. */
.device-maker-logo {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 5px;
  background: #fff;
  object-fit: cover;
}
.device-maker-logo.dark {
  background: #000;
}
.device-name-cell input[type='checkbox'] {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
}

/* The settings sheets (Display, Menu) keep their top in place (the title, the Display preview, the
   Menu's sections) and scroll the settings under it. */
dialog.settings-sheet {
  padding: 0;
  overflow: hidden;
}
dialog.settings-sheet[open] {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
}
.settings-sheet .sheet-top {
  min-width: 0;
  box-shadow: 0 1px 0 var(--line);
}
.settings-panes {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 16px var(--sheet-pad) var(--sheet-pad);
}

@media (max-width: 640px) {
  dialog.sheet {
    --sheet-pad: 20px;
    width: 100%;
    max-width: 100%;
    margin: auto 0 0;
    padding: 0 var(--sheet-pad) max(24px, env(safe-area-inset-bottom));
    border-radius: 24px 24px 0 0;
  }
  dialog.sheet.tall,
  dialog.settings-sheet {
    height: 92dvh;
    max-height: none;
  }
  dialog.settings-sheet {
    padding: 0;
  }
  .settings-panes {
    padding-bottom: max(24px, env(safe-area-inset-bottom));
  }
}
/* On a tablet or computer, the sheets that hold the most get more room than the narrow column. */
@media (min-width: 641px) {
  dialog.sheet.tall {
    --sheet-pad: 32px;
    width: min(720px, calc(100% - 64px));
    height: min(88dvh, 960px);
    max-height: none;
    padding-bottom: 32px;
    border-radius: 28px;
  }
  dialog.settings-sheet {
    --sheet-pad: 32px;
    width: min(920px, calc(100% - 64px));
    height: min(88dvh, 880px);
    max-height: none;
    border-radius: 28px;
  }
  dialog.display-sheet {
    width: min(1040px, calc(100% - 64px));
  }
}

/* The negative margin lines the close and back icons up with the content edge, not their button edge. */
.sheet-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 0 -10px 4px;
  padding: 16px 0 8px;
  background: var(--surface);
}
.sheet-head > h2:first-child,
.sheet-head > [hidden]:first-child + h2 {
  padding-left: 10px;
}
.sheet h2 {
  flex: 1;
  text-wrap: balance;
  margin: 0;
  font-size: 22px;
  line-height: 1.25;
}
.settings-sheet .sheet-head {
  position: static;
  margin: 0;
  padding: 14px calc(var(--sheet-pad) - 10px) 10px var(--sheet-pad);
}
.settings-sheet .sheet-head > h2 {
  padding-left: 0;
}
/* Parts of the simpler sheets (TVs, phones): cards under small headings, as in the Menu. */
.sheet > :is(.group, .card) {
  margin-top: 24px;
}
.sheet > .sheet-head + :is(.group, .card),
.sheet > .sheet-head + [hidden] + :is(.group, .card),
.sheet > .sheet-head + [hidden] + [hidden] + :is(.group, .card) {
  margin-top: 8px;
}

/* ---------- confirming an action (confirmAction in dialogs.js) ---------- */

/* A short question: a narrow sheet on a tablet or computer, the usual bottom sheet on a phone. */
dialog.confirm {
  padding-top: 24px;
}
@media (min-width: 641px) {
  dialog.confirm {
    width: min(440px, calc(100% - 32px));
  }
}
dialog.confirm h2 {
  margin-bottom: 8px;
}
dialog.confirm p {
  margin: 0;
  color: var(--text-2);
  line-height: 1.5;
  text-wrap: pretty;
}
dialog.confirm .row {
  margin-top: 24px;
  gap: 8px;
}
/* An action that can't be undone is a filled red button, as weighty as a primary one. */
dialog.confirm .btn.danger {
  background: var(--danger);
  box-shadow: none;
  color: var(--surface);
}
@media (hover: hover) {
  dialog.confirm .btn.danger:hover {
    background: color-mix(in srgb, var(--danger) 86%, var(--text));
  }
}
/* Within a thumb's reach on a phone: both buttons full width, the action above Cancel. */
@media (max-width: 640px) {
  dialog.confirm .row {
    flex-direction: column-reverse;
    align-items: stretch;
  }
}

/* ---------- the Menu's sections ---------- */

/* On a phone the menu is a list of its sections, each with a line on what it holds; one opens in the
   list's place, under a way back. On anything wider, the list runs down the left side beside the
   section that's open. */
.settings-nav {
  display: flex;
  flex-direction: column;
  margin: 0 var(--sheet-pad) var(--sheet-pad);
  border-radius: 16px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  overflow: hidden;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 64px;
  padding: 10px 16px;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition-property: background-color, color;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
/* The sections are tabs; the admin page, for admins, is a link after them, set apart by space. It
   opens in a new tab, which its arrow says in place of the chevron. */
.nav-tabs {
  display: flex;
  flex-direction: column;
  gap: inherit;
}
.nav-item:not([hidden]) ~ .nav-item:not([hidden]) {
  border-top: 1px solid var(--line);
}
a.nav-item {
  text-decoration: none;
}
.nav-out::after {
  display: none;
}
.settings-nav .nav-item svg.nav-out-arrow {
  width: 16px;
  height: 16px;
  margin-right: 2px;
  stroke: var(--text-2);
  stroke-width: 2;
}
.nav-item:focus-visible {
  outline-offset: -3px;
}
.nav-item svg {
  flex: none;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.nav-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
}
.nav-text small {
  line-height: 1.35;
}
/* A chevron, as on any row that opens something. */
.nav-item::after {
  content: '';
  flex: none;
  width: 9px;
  height: 9px;
  margin-right: 4px;
  border-top: 2px solid var(--text-2);
  border-right: 2px solid var(--text-2);
  rotate: 45deg;
}
@media (hover: hover) {
  .nav-item:hover {
    background: var(--surface-3);
  }
}

.pane {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 640px;
}
.pane-title {
  margin: 0 0 -8px;
  font-size: 22px;
  font-weight: 700;
  line-height: 1.25;
}
.pane-note {
  margin: -12px 0 0;
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.5;
  text-wrap: pretty;
}
/* On a phone the list and a section take turns, and the title names the section that's open, so the
   pane's own title is only for screen readers. The list alone is only as tall as it is. */
@media (max-width: 719px) {
  dialog.menu-sheet[data-view='list'] {
    height: fit-content;
  }
  .menu-sheet[data-view='list'] :is(.settings-panes, #menu-back, .menu-pane-title),
  .menu-sheet[data-view='pane'] :is(.settings-nav, .menu-root-title) {
    display: none;
  }
  .menu-sheet[data-view='list'] .sheet-top {
    box-shadow: none;
  }
  #menu-back {
    margin-left: -10px;
  }
  .menu-sheet .pane-title {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .menu-sheet .pane-note {
    margin-top: 0;
  }
  /* The admin page is a card of its own under the sections' card. */
  .menu-sheet .settings-nav {
    gap: 12px;
    border-radius: 0;
    background: none;
    box-shadow: none;
    overflow: visible;
  }
  .menu-sheet :where(.nav-tabs, .settings-nav > .nav-item) {
    border-radius: 16px;
    background: var(--surface-2);
    box-shadow: var(--ring);
    overflow: hidden;
  }
}
@media (min-width: 720px) {
  dialog.menu-sheet[open] {
    grid-template-columns: 232px minmax(0, 1fr);
  }
  .menu-sheet .sheet-top {
    display: contents;
  }
  .menu-sheet .sheet-head {
    grid-column: 1 / -1;
    border-bottom: 1px solid var(--line);
  }
  #menu-back,
  .menu-pane-title {
    display: none;
  }
  .menu-sheet .settings-nav {
    gap: 2px;
    margin: 0;
    padding: 16px 12px 24px var(--sheet-pad);
    border-radius: 0;
    background: none;
    box-shadow: none;
    overflow: hidden auto;
  }
  .menu-sheet .nav-item {
    gap: 8px;
    min-height: 42px;
    padding: 0 12px;
    border-radius: 10px;
    color: var(--text-2);
  }
  .menu-sheet .nav-item:not([hidden]) ~ .nav-item:not([hidden]) {
    border-top: 0;
  }
  .menu-sheet .nav-tabs + .nav-item:not([hidden]) {
    margin-top: 16px;
  }
  .menu-sheet .nav-item svg {
    width: 20px;
    height: 20px;
    stroke: currentColor;
    stroke-width: 2;
  }
  .menu-sheet .nav-text {
    font-size: 15px;
  }
  .menu-sheet .nav-text small,
  .menu-sheet .nav-item::after {
    display: none;
  }
  @media (hover: hover) {
    .menu-sheet .nav-item:hover {
      background: var(--surface-2);
      color: var(--text);
    }
  }
  .menu-sheet .nav-item[aria-selected='true'] {
    background: color-mix(in srgb, var(--accent) var(--tint), transparent);
    color: var(--text);
  }
  .menu-sheet .nav-item[aria-selected='true'] svg {
    color: var(--accent);
  }
  .menu-sheet .settings-panes {
    padding: 22px var(--sheet-pad) var(--sheet-pad) 20px;
  }
}

/* TVs and phones: each kind of device in a row, its icon in a tinted circle and, for phones, the QR
   code beside the words; role="list" keeps them a list once the markers are gone. */
@media (min-width: 641px) {
  dialog.devices-sheet {
    width: min(600px, calc(100% - 32px));
  }
}
/* Before anything is connected, adding one is the whole sheet, so it needs no heading of its own. */
.devices-sheet:has(#remote-section[hidden]):has(#phones-section[hidden]) #add-section > h3 {
  display: none;
}
.ways {
  margin: 0;
  list-style: none;
}
.card > .ways {
  padding: 0 16px;
}
.way {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 0;
}
.way + .way {
  border-top: 1px solid var(--line);
}
.way-icon {
  flex: none;
  width: 36px;
  height: 36px;
  padding: 7px;
  border-radius: 50%;
  background: var(--surface-2);
  background: color-mix(in srgb, var(--accent) var(--tint), transparent);
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.way-text {
  flex: 1;
  min-width: 0;
}
.way-text h4 {
  margin: 6px 0 4px;
  font-size: 17px;
  line-height: 1.35;
}
.way-text p {
  margin: 0;
  color: var(--text-2);
  font-size: 15px;
  line-height: 1.5;
  text-wrap: pretty;
}
.way-text .url {
  color: var(--text);
  font-size: 17px;
}
.way .qr {
  width: 136px;
}
/* On a phone the QR code goes under the words, still in line with them. */
@media (max-width: 480px) {
  .way {
    flex-wrap: wrap;
  }
  .way-text {
    flex-basis: calc(100% - 50px); /* the row beside the icon, so the QR code wraps under it */
  }
  .way .qr {
    width: 120px;
    margin-left: 50px;
  }
}

/* The one code box, for whichever TV or phone shows a code. */
.card > .pair-form {
  padding: 16px;
}
.pair-form label {
  display: block;
  margin-bottom: 10px;
  font-size: 16px;
  font-weight: 700;
}
.card > .mic-notice:not([hidden]) {
  margin: 0 16px 16px;
  border-top: 0;
}

.url {
  display: inline-block;
  margin-top: 2px;
  font-size: 22px;
  overflow-wrap: anywhere;
}

.pair-row {
  display: flex;
  gap: 8px;
}
#pair-code {
  flex: 1;
  min-width: 0;
  min-height: 56px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: 0.25em;
  /* Balances the letter-spacing after the last character, so the code sits optically centered. */
  text-indent: 0.25em;
  text-align: center;
  text-transform: uppercase;
}
#pair-code::placeholder {
  letter-spacing: normal;
  font-weight: 400;
}
.pair-row .btn {
  min-height: 56px;
  padding: 0 22px;
  border-radius: 12px;
}

/* The bell swings as it alerts the TV. */
#alert-btn.ringing svg {
  transform-origin: 50% 12%;
  animation: ring 700ms ease-in-out;
}
/* Look for TVs turns its icon while the helpers look. */
#scan-tvs:disabled svg {
  animation: turn 1s linear infinite;
}
/* The remote's Look for TVs icon turns while the helpers look. */
#remote-scan.looking svg {
  animation: turn 1s linear infinite;
}
@keyframes turn {
  to {
    rotate: 360deg;
  }
}
@keyframes ring {
  15% { rotate: 16deg; }
  35% { rotate: -14deg; }
  55% { rotate: 9deg; }
  75% { rotate: -5deg; }
}
.stepper {
  display: flex;
  gap: 6px;
}
.stepper .btn {
  min-width: 52px;
  background: var(--surface);
}

details {
  margin-top: 16px;
  font-size: 15px;
  line-height: 1.5;
}
summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 44px;
  padding: 8px 0;
  font-weight: 700;
  list-style: none;
  cursor: pointer;
}
summary::-webkit-details-marker {
  display: none;
}
/* A chevron that turns to point down when open. */
summary::after {
  content: '';
  flex: none;
  width: 8px;
  height: 8px;
  margin-right: 6px;
  border-right: 2px solid var(--text-2);
  border-bottom: 2px solid var(--text-2);
  rotate: -45deg;
  transition: rotate 200ms var(--ease);
}
details[open] > summary::after {
  rotate: 45deg;
}
/* In a card a disclosure is a row like the others, its icon and name then the chevron; what it holds
   lines up with the name. */
.card > details {
  margin: 0;
  padding: 0;
}
.card > details > summary {
  justify-content: flex-start;
  gap: 12px;
  min-height: 54px;
  padding: 0 16px;
  font-size: 16px;
  font-weight: 400;
  line-height: 1.35;
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  .card > details > summary:hover {
    background: var(--surface-3);
  }
}
.card > details > summary:focus-visible {
  outline-offset: -3px;
}
.card > details > summary > svg {
  flex: none;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.card > details > summary > span {
  flex: 1;
}
.card > details > :not(summary) {
  margin: 0 16px 12px 50px;
  color: var(--text-2);
}
.card > details > :last-child {
  margin-bottom: 16px;
}
.card > details .link-row {
  color: var(--text);
}
.link-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.link-row a {
  flex: 1;
  color: var(--accent);
  overflow-wrap: anywhere;
}

/* Saved conversations: one card of rows, newest first. */
.history-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-start;
  margin-top: 8px;
}
.history-list:has(.history-item) {
  gap: 0;
  align-items: stretch;
  border-radius: 16px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  overflow: hidden;
}
.history-list > .hint {
  margin: 0;
}
.history-item {
  display: block;
  width: 100%;
  padding: 14px 16px;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  transition: background-color 150ms ease-out;
}
.history-item + .history-item {
  border-top: 1px solid var(--line);
}
@media (hover: hover) {
  .history-item:hover {
    background: var(--surface-3);
  }
}
.history-item:focus-visible {
  outline-offset: -3px;
}
.history-item strong {
  display: block;
  font-size: 16px;
}
.history-item span {
  display: block;
  margin-top: 4px;
  overflow: hidden;
  color: var(--text-2);
  font-size: 14px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.captions.readonly {
  margin-top: 16px;
  padding: 0;
  overflow: visible;
  font-size: 20px;
}

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

/* The preview stays in view while the controls scroll, so every change shows at once. */
.display-sheet .sheet-top {
  padding: 0 var(--sheet-pad) 14px;
}
.display-sheet .sheet-head {
  padding-inline: calc(var(--sheet-pad) - 10px) 0;
  margin-left: -10px;
}
.display-sheet .sheet-head > h2 {
  padding-left: 10px;
}
.display-sheet .settings-panes {
  padding-top: 22px;
}
/* On short screens the preview scrolls away with the rest, leaving room for the controls. */
@media (max-height: 640px) {
  dialog.display-sheet[open] {
    display: block;
    overflow-y: auto;
  }
  .display-sheet .settings-panes {
    overflow: visible;
  }
}

.captions.preview {
  flex: none;
  overflow: hidden;
  padding: 14px 16px 12px;
  border-radius: 16px;
  background: var(--bg);
  box-shadow: var(--ring);
  /* Follows the size setting without letting the largest sizes swallow the panel. */
  font-size: clamp(18px, calc(var(--caption-size) * 0.75), 34px);
}
.captions.preview .line {
  margin: 0;
}

.contrast {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 2px 0;
  color: var(--text-2);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.contrast::before {
  content: '';
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ok);
}
.contrast[data-level='fair']::before {
  background: var(--warn);
}
.contrast[data-level='poor'] {
  color: var(--danger);
  font-weight: 700;
}
.contrast[data-level='poor']::before {
  background: var(--danger);
}

/* The settings in two columns when there's room, each a stack of groups. */
.sheet-columns {
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.sheet-columns > .column {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}
@media (min-width: 900px) {
  .sheet-columns {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 24px 28px;
    align-items: start;
  }
}

/* Picture choices (themes, fonts): the chosen tile is outlined in the accent color. */
.tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 6px 6px 8px;
  border-radius: 16px;
  box-shadow: var(--ring);
  cursor: pointer;
  transition-property: background-color, box-shadow;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .tile:hover {
    box-shadow: var(--ring-hover);
  }
}
.tile input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.tile:has(input:checked) {
  background: color-mix(in srgb, var(--accent) var(--tint), transparent);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.tile:has(input:focus-visible) {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.tile-name {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
}
/* A choice with a line about it (Automatic) runs across the top, its picture the width of the ones
   under it and its words beside, so the rest fill whole rows. */
.tile:has(.tile-hint) {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: calc((100% - 4px) / 3 - 12px) minmax(0, 1fr);
  grid-template-rows: 1fr 1fr;
  gap: 2px 14px;
  padding: 6px;
}
.tile:has(.tile-hint) > :not(input, .tile-name, .tile-hint) {
  grid-row: 1 / -1;
}
.tile:has(.tile-hint) > .tile-name {
  align-self: end;
  font-size: 15px;
  text-align: left;
}
.tile:has(.tile-hint) > .tile-hint {
  align-self: start;
}
/* Each swatch carries its own data-theme, so it shows that theme's real colors. Radius = tile radius − padding. */
.theme-swatch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  height: 52px;
  padding: 0 12px;
  border-radius: 10px;
  background: var(--bg);
  box-shadow: inset 0 0 0 1px rgb(128 128 128 / 0.3);
  color: var(--text);
  font-size: 22px;
  font-weight: 700;
}
.theme-swatch[data-theme='auto'] {
  background: linear-gradient(135deg, #f7f8fa 50%, #0e1015 50%);
  color: #7d8390;
}
.theme-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent);
}
.font-aa {
  display: grid;
  place-items: center;
  width: 100%;
  height: 52px;
  border-radius: 10px;
  background: var(--surface-2);
  font-size: 26px;
  font-weight: var(--cap-weight);
  line-height: 1;
}

/* Color choices. */
.swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 2px;
}
.swatch {
  position: relative;
  display: grid;
  cursor: pointer;
}
.swatch input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.sw {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--sw);
  box-shadow: inset 0 0 0 1px rgb(128 128 128 / 0.45);
  font-size: 18px;
  font-weight: 700;
  transition: box-shadow 150ms ease-out;
}
.swatch input:checked + .sw {
  box-shadow: inset 0 0 0 1px rgb(128 128 128 / 0.45), 0 0 0 3px var(--surface-2), 0 0 0 5px var(--accent);
}
.swatch input:focus-visible + .sw {
  outline: 3px solid var(--accent);
  outline-offset: 6px;
}
/* "Theme color": an A in the theme's own text color. */
.sw-theme {
  background: var(--bg);
  color: var(--text);
}
/* "No background": a struck-through circle. */
.sw-none {
  background:
    linear-gradient(to top right, transparent calc(50% - 1.5px), var(--text-2) calc(50% - 1.5px), var(--text-2) calc(50% + 1.5px), transparent calc(50% + 1.5px)),
    var(--surface);
}
.sw-custom {
  background: conic-gradient(#ff6b6b, #ffd54a, #8be58b, #6fd3ff, #8fb0ff, #c3a6ff, #ff8fc7, #ff6b6b);
}

.color-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.color-field {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: fit-content;
  padding: 6px 14px 6px 6px;
  border-radius: 14px;
  box-shadow: var(--ring);
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
}
@media (hover: hover) {
  .color-field:hover {
    box-shadow: var(--ring-hover);
  }
}
input[type='color'] {
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  appearance: none;
  background: none;
  cursor: pointer;
}
input[type='color']::-webkit-color-swatch-wrapper {
  padding: 0;
}
input[type='color']::-webkit-color-swatch {
  border: 0;
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px rgb(128 128 128 / 0.45);
}
input[type='color']::-moz-color-swatch {
  border: 0;
  border-radius: 8px;
}

.sheet-foot {
  margin-top: 24px;
}

/* ---------- overlays ---------- */

/* Sits above the composer, or at the bottom of the screen when app.js moves it into an open dialog. */
.toast {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 12px);
  z-index: 50;
  width: max-content;
  max-width: calc(100% - 32px);
  padding: 12px 18px;
  border-radius: 14px;
  background: var(--text);
  color: var(--bg);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
  text-wrap: pretty;
  transform: translateX(-50%);
  box-shadow: 0 8px 30px rgb(0 0 0 / 0.35);
  animation: pop-in 250ms var(--ease);
}
dialog .toast {
  position: fixed;
  bottom: max(16px, env(safe-area-inset-bottom));
}

.big-reply {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 24px;
  border: 0;
  background: var(--bg);
  color: var(--text);
  text-align: center;
  cursor: pointer;
  transition-property: opacity;
  transition-duration: 200ms;
  transition-timing-function: var(--ease);
}
@starting-style {
  .big-reply[open] {
    opacity: 0;
  }
}
.big-reply[open] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.big-reply p {
  max-height: 85dvh;
  margin: 0;
  overflow-y: auto;
  font-size: clamp(34px, 9vw, 120px);
  font-weight: 700;
  line-height: 1.2;
  overflow-wrap: anywhere;
  text-wrap: balance;
}
.big-reply .hint {
  position: absolute;
  bottom: max(16px, env(safe-area-inset-bottom));
}

/* ---------- high contrast ---------- */

[data-theme='contrast'] .status,
[data-theme='contrast'] .banner,
[data-theme='contrast'] button.who,
[data-theme='contrast'] .who.as-button {
  box-shadow: inset 0 0 0 1px currentColor;
}
/* With no tint to show it, the chosen Menu section is outlined (beside it; a phone's list has none chosen). */
@media (min-width: 720px) {
  [data-theme='contrast'] .menu-sheet .nav-item[aria-selected='true'] {
    box-shadow: inset 0 0 0 2px var(--accent);
  }
}

/* Short landscape screens: quick replies sit beside the reply box, so captions keep most of the height. */
@media (max-height: 30rem) and (min-width: 34rem) {
  .composer {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .phrases {
    flex: 1 1 0;
    min-width: 0;
    margin: -6px 0 -6px calc(-1 * var(--pad-l));
    padding-right: 24px;
  }
  .reply-row {
    flex: 1.4 1 0;
    min-width: 0;
  }
}

/* Set by display.js for a moment while the theme changes. */
[data-no-transitions] *,
[data-no-transitions] *::before,
[data-no-transitions] *::after {
  transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: 0s !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- phone microphones (the app's sheet, and the /mic page) ---------- */

.status.phones .meter {
  visibility: hidden;
}

/* Scanners need dark modules on light, whatever the theme. */
.qr {
  flex: none;
  width: 168px;
  padding: 6px;
  border-radius: 12px;
  background: #fff;
}
.qr svg {
  display: block;
  width: 100%;
  height: auto;
  fill: #000;
  shape-rendering: crispEdges;
}
/* Menu → Activity: what happened, a day at a time, newest first; what went wrong in the warning color. */
.activity-day + .activity-day {
  margin-top: 16px;
}
.activity-day > h5 {
  margin: 0 0 6px 16px;
  color: var(--text-2);
  font-size: 14px;
  font-weight: 700;
}
.activity-entries {
  list-style: none;
}
.card > .activity-entry {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: start;
  gap: 12px;
  border-bottom: 1px solid var(--line);
}
.card > .activity-entry:last-child {
  border-bottom: 0;
}
.activity-icon svg {
  display: block;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  fill: none;
  stroke: var(--text-2);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.activity-entry[data-warn] .activity-icon svg {
  stroke: var(--warn);
}
.activity-body {
  min-width: 0;
}
.activity-text {
  margin: 0;
  font-size: 16px;
  line-height: 1.4;
  overflow-wrap: anywhere;
  text-wrap: pretty;
}
.activity-entry[data-warn] .activity-text {
  font-weight: 700;
}
.activity-detail {
  margin: 2px 0 0;
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.45;
  text-wrap: pretty;
}
.activity-entry time {
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.activity-empty {
  margin: 0;
  padding: 16px;
  border-radius: 16px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  color: var(--text-2);
}
.activity-list[aria-busy='true'] {
  min-height: 64px;
  opacity: 0.6;
}
.activity-more {
  display: block;
  margin: 12px auto 0;
}
.activity-more[hidden] {
  display: none;
}
#notify-kinds {
  padding: 0 16px;
  border-top: 1px solid var(--line);
}

.device-list {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}
.device-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.device-item:last-child {
  border-bottom: 0;
}
.device-label {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}
/* A TV's name reads as its name, and turns into a field when it's tapped, to rename it. */
.device-label input {
  display: block;
  width: calc(100% + 10px);
  margin: -5px 0 -1px -10px;
  padding: 4px 9px;
  border-color: transparent;
  border-radius: 8px;
  background: transparent;
  font-size: 16px;
  font-weight: 700;
  text-overflow: ellipsis;
  transition-property: background-color, border-color;
  transition-duration: 150ms;
}
@media (hover: hover) {
  .device-label input:hover {
    border-color: var(--field-border);
  }
}
.device-label input:focus {
  border-color: var(--field-border);
  background: var(--bg);
}
.device-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
}
.device-actions:empty {
  display: none;
}
.device-item[data-state='live'] {
  --state: var(--ok);
}
.device-item[data-state='starting'] {
  --state: var(--warn);
}
/* The account: its name, who is signed in, and today's cloud captions. */
.account-summary {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 16px 16px;
  border-radius: 16px;
  background: var(--surface-2);
  box-shadow: var(--ring);
}
.account-summary > p {
  margin: 0;
}
.account-summary > .account-org {
  font-size: 19px;
  font-weight: 700;
  line-height: 1.3;
}
.account-summary > .account-who {
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.45;
}
/* People in the account; the owner can give one a new password or remove them. */
.member-item {
  flex-wrap: wrap;
}
.member-actions {
  display: flex;
  gap: 4px;
}
.member-password {
  flex-basis: 100%;
}
#invite-row input {
  flex: 1;
  min-width: 0;
}
/* Two-step sign-in: setting it up, a step at a time, with the QR code and the key beside it. */
#two-step > .account-form > .setting {
  margin: 8px 0;
}
.steps {
  margin: 0;
  padding-left: 22px;
  line-height: 1.5;
}
.steps > li + li {
  margin-top: 10px;
}
.steps a {
  color: var(--accent);
}
.two-step-qr {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
}
.two-step-qr .link-row {
  flex: 1 1 200px;
  min-width: 0;
  margin: 0;
}
.two-step-qr input {
  flex: 1;
  min-width: 0;
  font-family: ui-monospace, monospace;
  font-size: 15px;
}
/* Recovery codes, in two columns, to copy or print. */
.recovery-codes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5em, 1fr));
  gap: 6px 16px;
  margin: 12px 0;
  padding: 0;
  list-style: none;
  font-family: ui-monospace, monospace;
  font-size: 17px;
  font-variant-numeric: tabular-nums;
}

/* Sign-in, sign-up and password forms */
.form-msg {
  margin: 8px 0;
  color: var(--danger);
  font-size: 15px;
  line-height: 1.5;
}
.form-msg:empty {
  display: none;
}
/* Under the field it's about, the setting's gap spaces it. */
.setting > .form-msg {
  margin: 0;
}
/* A password field's Show button sits inside it, at the end, inset to match its corners. */
.password-field {
  position: relative;
}
.password-field > input {
  padding-right: 76px;
}
.password-toggle {
  position: absolute;
  top: 4px;
  right: 4px;
  bottom: 4px;
  min-width: 60px;
  padding: 0 12px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  .password-toggle:hover {
    background: var(--surface-2);
  }
}
.form-switch {
  margin: 16px 0 0;
  color: var(--text-2);
  font-size: 15px;
}
.link-btn {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
:is(input, textarea, select)[aria-invalid='true'] {
  border-color: var(--danger);
}

/* A TV the home agent can switch to captions: its name and button, then how it shows them, lined
   up with its name past the dot and its drawing. */
.remote-item {
  flex-wrap: wrap;
  row-gap: 8px;
}
/* The name has 6rem at least beside the buttons, wrapping in it, or else a line to itself (on the
   narrowest phones), so every TV's buttons are beside its name or every TV's under it; never some of each. */
.remote-item > .device-label {
  flex: 1 1 6rem;
}
.remote-item > .device-actions {
  flex: none;
  gap: 8px;
  margin-left: auto;
}
.remote-item .remote-open {
  width: 40px;
  padding: 0;
}
.remote-item .remote-open svg {
  width: 22px;
  height: 22px;
}
/* Its drawing, between its dot and its name, in the name's quieter color. */
.remote-item > .device-art {
  flex: none;
  display: flex;
  color: var(--text-2);
}
:is(.remote-item, .device-name-cell) > .device-art svg {
  width: 42px;
  height: 28px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Hidden, as Edit lists it: faded, with Unhide. */
.remote-item.hidden-tv > :is(.dot, .device-art, .device-label) {
  opacity: 0.5;
}
.remote-item > :is(.remote-status, .remote-pin) {
  flex-basis: 100%;
  margin: 0;
  padding-left: 76px;
}
.remote-status:empty {
  display: none;
}
.remote-status:is([data-state='asking'], [data-state='pin']) {
  color: var(--text);
  font-weight: 700;
}
.remote-pin {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 15px;
}
.remote-pin input {
  flex: none;
  width: 9ch;
  padding: 8px 12px;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-align: center;
}
.remote-status[data-state='failed'] {
  color: var(--danger);
}
/* Sounds and flashing, in Menu → Home: a row for each thing that can happen at home, and a column of
   boxes for each of Readout's screens that listens for sounds, and one for the lights. */
.sounds-table {
  overflow-x: auto;
}
.sounds-table table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}
.sounds-table th,
.sounds-table td {
  padding: 10px 16px;
  border-top: 1px solid var(--line);
}
.sounds-table thead > tr > * {
  border-top: 0;
}
.sounds-table thead th {
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  vertical-align: bottom;
}
.sounds-table tbody th {
  font-weight: 600;
  text-align: left;
}
.sounds-table td {
  width: 1%; /* the boxes' columns as narrow as their headings let them be */
  text-align: center;
}
.sounds-table input {
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

/* The lights a home agent switches: each one's switch beside its name, then its brightness, whether it
   flashes, and Flash it, under it, as a TV's choices are under it. */
.light-item {
  flex-wrap: wrap;
  row-gap: 8px;
}
.light-item > .device-label {
  flex: 1 1 6rem;
}
.light-item > .device-label small {
  display: block;
  margin-top: 2px;
}
.light-item[data-state='away'] > :is(.dot, .device-label) {
  opacity: 0.55;
}
.light-item > .light-toggle {
  display: flex;
  margin: 0;
}
.light-item > :is(.light-level, .light-alerts, .light-actions, .remote-status) {
  flex-basis: 100%;
  margin: 0;
  padding-left: 22px;
}
.light-item > .light-alerts > label {
  font-size: 15px;
}
.light-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.light-actions > [hidden] {
  display: none;
}
.card > .lights-none {
  padding: 16px;
}
.hub-item {
  flex-wrap: wrap;
  row-gap: 8px;
}
.hub-item > .device-label small {
  display: block;
  margin-top: 2px;
}
.hub-item > .remote-status {
  flex-basis: 100%;
  margin: 0;
  padding-left: 22px;
}
.card > .matter-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
}
.matter-form > label {
  font-size: 16px;
  font-weight: 700;
}
.matter-form > small {
  font-size: 14px;
  line-height: 1.5;
}
#matter-code {
  flex: 1;
  min-width: 0;
  min-height: 56px;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
#ha-url,
#ha-token {
  flex: 1;
  min-width: 0;
  min-height: 56px;
}
#matter-code::placeholder {
  letter-spacing: normal;
  font-weight: 400;
}
.matter-helper {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 15px;
}
.matter-helper select {
  flex: 1;
  min-width: 0;
}

/* A phone's header has no room for the lights' button: Menu → Home has it, as it has the schedule's. */
@media (max-width: 540px) {
  #lights-btn {
    display: none;
  }
}

/* The TV remote: a column of big keys laid out as a TV's remote is, so they're found by thumb. Keys
   stand a step up from the sheet, lit from above, and sink when pressed. They press on touch and
   some repeat while held, so they don't select or call up menus. */
.remote-sheet {
  --key: var(--surface-2);
  --key-raised: var(--surface-3); /* the chosen tab, and OK in the middle of the arrows */
  --key-press: color-mix(in srgb, var(--text) 10%, transparent); /* over a part of a key: an arrow, half a rocker */
  --key-edge: inset 0 1px 0 rgb(255 255 255 / 0.05), 0 0 0 1px rgb(255 255 255 / 0.07), 0 2px 6px -2px rgb(0 0 0 / 0.55);
  --key-sunk: inset 0 2px 5px rgb(0 0 0 / 0.35), 0 0 0 1px rgb(255 255 255 / 0.05);
}
:is([data-theme='light'], [data-theme='paper'], [data-theme='custom'][data-mode='light']) .remote-sheet {
  --key-raised: var(--surface);
  --key-edge: inset 0 1px 0 rgb(255 255 255 / 0.8), 0 0 0 1px rgb(0 0 0 / 0.08), 0 1px 3px rgb(16 24 40 / 0.12);
  --key-sunk: inset 0 2px 4px rgb(16 24 40 / 0.16), 0 0 0 1px rgb(0 0 0 / 0.08);
}
[data-theme='contrast'] .remote-sheet {
  --key-edge: 0 0 0 1px #fff;
  --key-sunk: 0 0 0 2px #fff;
}
@media (min-width: 641px) {
  dialog.remote-sheet {
    width: min(420px, calc(100% - 32px));
  }
}
/* On a phone the remote gets nearly the whole height, so more of its keys fit on one screen. */
@media (max-width: 640px) {
  dialog.remote-sheet {
    max-height: 94dvh;
  }
}
/* Focus starts on the title, for screen readers; it isn't something to press, so it gets no ring. */
#remote-title:focus {
  outline: none;
}

/* The TV it controls: an icon with a dot for on or off, its name, and what it shows. With several,
   the list of them covers the card, unseen, so a tap anywhere on it opens the list; a chevron says so. */
.remote-tv {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 6px 10px 10px;
  border-radius: 18px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  transition: background-color 150ms ease-out;
}
.remote-tv[data-state='live'] {
  --state: var(--ok);
}
.remote-tv-icon {
  position: relative;
  display: grid;
  flex: none;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) var(--tint), var(--surface-3));
  color: var(--accent);
}
.remote-tv-icon svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.remote-tv-icon .dot {
  position: absolute;
  right: -3px;
  bottom: -3px;
  width: 13px;
  height: 13px;
  box-shadow: 0 0 0 3px var(--surface-2);
}
.remote-tv-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  padding-right: 4px;
}
#remote-tv-name {
  display: -webkit-box;
  overflow: hidden;
  font-size: 17px;
  line-height: 1.3;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.remote-tv-state {
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.35;
}
.remote-tv select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}
.remote-tv:has(> select:not([hidden]))::after {
  content: '';
  flex: none;
  order: 1;
  width: 9px;
  height: 9px;
  margin: 0 10px 5px 0;
  border-right: 2px solid var(--text-2);
  border-bottom: 2px solid var(--text-2);
  rotate: 45deg;
}
.remote-tv:has(> select:focus-visible) {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
@media (hover: hover) {
  .remote-tv:has(> select:not([hidden])):hover {
    background: var(--surface-3);
  }
  .remote-tv:has(> select:not([hidden])):hover .dot {
    box-shadow: 0 0 0 3px var(--surface-3);
  }
}
/* Above the unseen list, so it's pressed by itself. */
#remote-scan {
  order: 2;
  color: var(--text-2);
}

/* How the last thing asked of the TV went, in its state's color: working, done, failed, or waiting
   for someone (to answer on the TV, or to type its PIN below). */
#remote-status,
#cast-setup-status[data-state] {
  --state: var(--text-2);
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0;
  padding: 10px 14px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--state) var(--tint), var(--surface));
  color: var(--text);
  font-size: 15px;
  line-height: 1.4;
  text-wrap: pretty;
}
#remote-status:empty {
  display: none;
}
#remote-status::before,
#cast-setup-status[data-state]::before {
  content: '';
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--state);
  translate: 0 -1px;
}
#remote-status[data-state='working'],
#cast-setup-status[data-state='working'] {
  --state: var(--warn);
}
#remote-status[data-state='working']::before,
#cast-setup-status[data-state='working']::before {
  animation: pulse 1.1s ease-in-out infinite;
}
#remote-status[data-state='done'],
#cast-setup-status[data-state='done'] {
  --state: var(--ok);
}
#remote-status:is([data-state='asking'], [data-state='pin']) {
  --state: var(--accent);
}
#remote-status[data-state='failed'],
#cast-setup-status[data-state='failed'] {
  --state: var(--danger);
  color: var(--text);
}
/* Setting up a device: its status as the remote's, and under it the bar (see showSetupProgress). */
#cast-setup-status[data-state] {
  margin: 8px 0;
}
#cast-setup-progress {
  display: block;
  margin: 0 0 8px;
}
.remote-sheet .remote-pin {
  padding: 10px 10px 10px 16px;
  border-radius: 16px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-weight: 700;
}
.remote-sheet .remote-pin input {
  flex: 1;
  min-width: 0;
}
/* What the TV's screen shows (a Raspberry Pi's): a picture in the TV's shape, black until the first
   one comes, with a line under it saying what it is, or why there's none. */
.remote-view {
  margin: 12px 0 0;
}
.remote-view :is(img, video) {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  border-radius: 12px;
  background: #000;
  box-shadow: var(--ring);
}
.remote-view-media {
  position: relative;
}
/* On a call, yourself, small in a corner, as in a mirror. */
.remote-view .remote-call-self {
  position: absolute;
  right: 8px;
  bottom: 8px;
  width: 28%;
  aspect-ratio: auto;
  border-radius: 8px;
  transform: scaleX(-1);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
.remote-view figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: 6px;
  color: var(--text-2);
  font-size: 14px;
}
.remote-view figcaption > span:first-child {
  flex: 1 1 12em;
  text-wrap: pretty;
}
.remote-view-actions {
  display: flex;
  gap: 8px;
}
/* Drop in, Video call and the rest: buttons of the app's own, not the remote's keys. */
.remote-view-actions .btn {
  display: inline-flex;
  flex: none;
  padding: 0 14px 0 12px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-weight: 700;
}
.remote-view-actions .btn svg {
  width: 18px;
  height: 18px;
}
.remote-view-actions .btn.danger {
  color: var(--danger);
}
/* Dropped in: the camera's light, as on the TV's notice. */
.remote-view.live #remote-drop-in-video {
  box-shadow: 0 0 0 2px var(--danger);
}
/* A touch screen for the Raspberry Pi: dragged, it scrolls the Pi's screen, not the remote, and
   tapped, a ring shows where, a moment. */
.remote-view.touch #remote-view-img {
  cursor: pointer;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.remote-view-tap {
  position: absolute;
  width: 36px;
  height: 36px;
  margin: -18px 0 0 -18px;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
  opacity: 0;
  pointer-events: none;
}
.remote-view-tap.shown {
  animation: remote-view-tap 450ms ease-out;
}
@keyframes remote-view-tap {
  from {
    opacity: 1;
    transform: scale(0.4);
  }
  to {
    opacity: 0;
    transform: scale(1);
  }
}
.remote-view-type {
  margin-top: 10px;
}
/* Bigger: a button in the picture's corner, dark over whatever the screen shows there. */
.remote-pad .remote-view-full {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: rgb(0 0 0 / 0.55);
  color: #fff;
}
@media (hover: hover) {
  .remote-pad .remote-view-full:hover {
    background: rgb(0 0 0 / 0.75);
  }
}
.remote-pad .remote-view-full svg {
  width: 20px;
  height: 20px;
}
.remote-view-full .shrink,
.remote-view.full .remote-view-full .grow {
  display: none;
}
.remote-view.full .remote-view-full .shrink {
  display: inline;
}
/* Aiming the camera, over the live picture: arrows in a cross, zoom beside them, its views under. */
.remote-aim {
  position: absolute;
  left: 8px;
  bottom: 8px;
  display: grid;
  grid-template-columns: repeat(3, 32px) 8px repeat(2, 32px);
  grid-template-areas: '. up . . in out' 'left center right . . .' '. down . . . .' 'views views views views views views';
  gap: 4px;
}
.remote-aim button {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: rgb(0 0 0 / 0.55);
  color: #fff;
  font: inherit;
  font-size: 0.8rem;
}
.remote-aim svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.remote-aim [data-move='up'] { grid-area: up; }
.remote-aim [data-move='left'] { grid-area: left; }
.remote-aim [data-move='center'] { grid-area: center; }
.remote-aim [data-move='right'] { grid-area: right; }
.remote-aim [data-move='down'] { grid-area: down; }
.remote-aim [data-move='in'] { grid-area: in; }
.remote-aim [data-move='out'] { grid-area: out; }
.remote-aim-views {
  grid-area: views;
  display: flex;
  gap: 4px;
}
.remote-aim #remote-aim-save {
  width: auto;
  padding: 0 10px;
}
.remote-aim #remote-aim-save[aria-pressed='true'] {
  background: var(--accent, #2b6cff);
}
@media (hover: hover) {
  .remote-aim button:hover {
    background: rgb(0 0 0 / 0.75);
  }
}
/* Bigger, the picture fills the window, over the rest of the remote: all the room the box to type
   leaves it, on black, past the phone's notch and rounded corners. */
.remote-view.full {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  background: #000;
}
.remote-view.full .remote-view-media {
  flex: 1;
  min-height: 0;
}
.remote-view.full :is(#remote-view-img, #remote-drop-in-video) {
  height: 100%;
  aspect-ratio: auto;
  border-radius: 0;
  box-shadow: none;
}
.remote-view.full figcaption {
  display: none;
}
.remote-view.full .remote-view-type {
  margin: 0;
  padding: 10px 12px;
  background: var(--surface);
}
/* A phone on its side has height to spare least: the box to type goes beside the picture, at the
   bottom, where the sides are left over anyway. */
@media (orientation: landscape) and (max-height: 520px) {
  .remote-view.full {
    flex-direction: row;
  }
  .remote-view.full .remote-view-type {
    flex: none;
    flex-direction: column;
    justify-content: flex-end;
    width: 200px;
  }
  .remote-view.full .remote-view-type input {
    flex: none;
  }
}
/* A phone held upright, with a wide picture: turned, it's twice the size. */
.remote-view-turn {
  display: none;
  margin: 0;
  padding: 8px 16px;
  color: #c9ccd2;
  font-size: 14px;
  text-align: center;
}
@media (orientation: portrait) and (max-width: 640px) {
  .remote-view.full:not([data-shape='tall']) .remote-view-turn {
    display: block;
  }
}
/* A Raspberry Pi's settings, under its keys behind a line: a row with its icon and the chevron that
   opens it. */
.remote-settings {
  margin: 4px 0 0;
  border-top: 1px solid var(--line);
}
.remote-settings > summary {
  justify-content: flex-start;
  gap: 12px;
  min-height: 52px;
  padding: 8px 0 0;
  font-size: 16px;
}
.remote-pad .remote-settings > summary > svg {
  flex: none;
  width: 22px;
  height: 22px;
  color: var(--text-2);
}
.remote-settings > summary > span {
  flex: 1;
}
/* Its camera, then its speaker and microphone side by side where there's room: each a pill showing
   the one it uses, as a choice in a card is. */
.remote-devices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11em, 1fr));
  gap: 8px 12px;
  margin-top: 8px;
}
.remote-devices .remote-camera,
.remote-devices .remote-turn,
.remote-devices .toggle {
  grid-column: 1 / -1;
}
.remote-devices label {
  display: grid;
  gap: 4px;
  min-width: 0;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
}
.remote-devices select {
  min-height: 42px;
  padding: 8px 38px 8px 14px;
  border: 0;
  border-radius: 11px;
  background-color: var(--surface-2);
  background-position: right 12px center;
  box-shadow: var(--ring);
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  text-overflow: ellipsis;
  cursor: pointer;
}
/* Nothing else to choose: it still says which, without the chevron. */
.remote-devices select:disabled {
  padding-right: 14px;
  background-image: none;
  opacity: 1;
  cursor: default;
}
.remote-devices .hint {
  grid-column: 1 / -1;
  margin: 0;
  font-size: 13px;
}
/* The sounds a Raspberry Pi listens for are chosen in Menu → Home: here, a line that goes there. */
.remote-hear {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 4px 12px;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
}

/* No TV to control: a large icon, why, and what can be done from here. */
.remote-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px 8px 8px;
  text-align: center;
}
.remote-empty-icon {
  display: grid;
  place-items: center;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  background: var(--surface-2);
  box-shadow: var(--ring);
  color: var(--text-2);
}
.remote-empty-icon svg {
  width: 42px;
  height: 42px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.remote-empty-icon [data-why] {
  display: none;
}
.remote-empty[data-why='offline'] [data-why='offline'],
.remote-empty[data-why='outdated'] [data-why='outdated'] {
  display: inline;
}
.remote-empty h3 {
  margin: 20px 0 6px;
  font-size: 20px;
  line-height: 1.3;
  text-wrap: balance;
}
.remote-empty p {
  max-width: 34ch;
  margin: 0;
  color: var(--text-2);
  font-size: 15px;
  line-height: 1.5;
  text-wrap: pretty;
}
.remote-empty .btn {
  margin-top: 22px;
}
#remote-empty-scan.looking svg {
  animation: turn 1s linear infinite;
}

/* The TV's card, then the keys spanning the sheet, edge to edge with it. */
.remote-pad,
.remote-keys {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.remote-keys {
  margin-top: 6px;
}
/* Only beside the keys on a wide screen with a mouse, where there's a keyboard to use. */
.remote-shortcuts {
  display: none;
  margin: 2px 0 0;
  color: var(--text-2);
  font-size: 13px;
  line-height: 1.45;
  text-align: center;
  text-wrap: balance;
}
@media (min-width: 860px) and (hover: hover) and (pointer: fine) {
  .remote-sheet.wide .remote-shortcuts {
    display: block;
  }
}
.remote-pad button {
  display: inline-grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  transition-property: background-color, box-shadow, color, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
.remote-pad svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* A key on its own: Power, Back, Home, Mute, a number, an app. A key that's two or more (the arrows,
   a rocker, the playback bar) is one raised shape, and only the part pressed darkens. */
.remote-pad :is(.remote-key, .rocker, .remote-dpad, .remote-media, .remote-numbers button, .remote-colors button, .remote-input, .remote-app) {
  background: var(--key);
  box-shadow: var(--key-edge);
}
@media (hover: hover) {
  .remote-pad :is(.remote-key, .remote-numbers button, .remote-colors button, .remote-input, .remote-app):hover {
    background: color-mix(in srgb, var(--key), var(--text) 5%);
  }
  .remote-pad :is(.rocker, .remote-media) button:hover,
  .remote-dpad > button:not(.dpad-ok):hover {
    background: color-mix(in srgb, var(--text) 5%, transparent);
  }
}
/* After hovering, so a press shows under a mouse too. */
.remote-pad :is(.remote-key, .remote-numbers button, .remote-colors button, .remote-input, .remote-app):is(:active, .pressed) {
  background: var(--surface-3);
  box-shadow: var(--key-sunk);
  scale: 0.96;
}
.remote-pad :is(.rocker, .remote-media) button:is(:active, .pressed),
.remote-dpad > button:not(.dpad-ok):is(:active, .pressed) {
  background: var(--key-press);
}
/* Its icon, and its name under it, together in the middle of the key. */
.remote-pad .remote-key {
  align-content: center;
  gap: 4px;
  height: 60px;
  border-radius: 18px;
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}
.remote-pad .remote-key svg {
  width: 22px;
  height: 22px;
}

.remote-top {
  display: flex;
  gap: 12px;
}
.remote-pad .remote-key.power {
  flex: none;
  width: 60px;
  border-radius: 50%;
  color: var(--danger);
}
.remote-pad .remote-key.power svg {
  width: 24px;
  height: 24px;
  stroke-width: 2.4;
}
/* The remote's own button, in the accent color; once the TV shows captions, Stop is a plain key. */
.remote-pad .remote-captions {
  display: inline-flex;
  flex: 1;
  justify-content: center;
  gap: 10px;
  min-height: 60px;
  padding: 0 20px 0 18px;
  border-radius: 20px;
  background: var(--accent);
  box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--accent) 80%, transparent);
  color: var(--accent-ink);
  font-size: 17px;
  font-weight: 700;
}
.remote-pad .remote-captions:active {
  scale: 0.97;
}
@media (hover: hover) {
  .remote-pad .remote-captions:hover {
    background: var(--accent-hover);
  }
}
.remote-pad .remote-captions.showing {
  background: var(--key);
  box-shadow: var(--key-edge);
  color: var(--text);
}
.remote-pad .remote-captions.showing svg {
  color: var(--ok);
}
@media (hover: hover) {
  .remote-pad .remote-captions.showing:hover {
    background: color-mix(in srgb, var(--key), var(--text) 5%);
  }
}

/* The arrows: each a quarter of the ring, cut on the diagonals, so a thumb anywhere in it presses
   the right one; OK is raised in the middle. */
.remote-dpad {
  position: relative;
  align-self: center;
  width: min(100%, 244px);
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 50%;
  isolation: isolate;
}
.remote-dpad > button:not(.dpad-ok) {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  color: var(--text-2);
}
.remote-dpad > button:not(.dpad-ok):is(:active, .pressed) {
  color: var(--text);
}
.remote-dpad > button:not(.dpad-ok):focus-visible {
  outline: none;
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  color: var(--accent);
}
.remote-dpad svg {
  width: 28px;
  height: 28px;
  stroke-width: 2.4;
}
.remote-dpad .dpad-up {
  clip-path: polygon(0 0, 100% 0, 50% 50%);
  place-items: start center;
  padding-top: 11%;
}
.remote-dpad .dpad-down {
  clip-path: polygon(0 100%, 100% 100%, 50% 50%);
  place-items: end center;
  padding-bottom: 11%;
}
.remote-dpad .dpad-left {
  clip-path: polygon(0 0, 0 100%, 50% 50%);
  place-items: center start;
  padding-left: 11%;
}
.remote-dpad .dpad-right {
  clip-path: polygon(100% 0, 100% 100%, 50% 50%);
  place-items: center end;
  padding-right: 11%;
}
.remote-pad .dpad-ok {
  position: absolute;
  z-index: 1;
  top: 30%;
  left: 30%;
  width: 40%;
  height: 40%;
  border-radius: 50%;
  background: var(--key-raised);
  box-shadow: var(--key-edge), 0 0 0 6px color-mix(in srgb, var(--key) 60%, transparent);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.remote-pad .dpad-ok:is(:active, .pressed) {
  background: var(--key-raised);
  box-shadow: var(--key-sunk);
  scale: 0.95;
}
@media (hover: hover) {
  .remote-pad .dpad-ok:hover {
    background: color-mix(in srgb, var(--key-raised), var(--text) 5%);
  }
}

/* Back, Home and Menu share the width, whichever of them the TV has. */
.remote-row {
  display: grid;
  grid-auto-columns: 1fr;
  grid-auto-flow: column;
  gap: 12px;
}

/* Volume and channel are rockers either side of Mute and Input; a missing volume leaves its place
   empty, and with no channel (a Raspberry Pi, a Chromecast) the keys between take its place. */
.remote-levels {
  display: grid;
  grid-template-columns: 72px 1fr 72px;
  gap: 12px;
}
.remote-levels:has(> .rocker:last-child[hidden]) {
  grid-template-columns: 72px 1fr;
}
.rocker {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-radius: 36px;
}
.rocker:first-child {
  grid-column: 1;
}
.rocker:last-child {
  grid-column: 3;
}
.remote-pad .rocker button {
  height: 58px;
  border-radius: 36px;
}
.rocker-label {
  color: var(--text-2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1;
  text-align: center;
  text-transform: uppercase;
}
/* Mute and Input (or Home and Mute) stack between them, as tall as the rockers together; either
   alone is as tall as a rocker. */
.remote-middle {
  display: grid;
  grid-column: 2;
  grid-auto-rows: 1fr;
  gap: 12px;
}
.remote-pad .remote-middle .remote-key {
  height: auto;
  min-height: 62px;
}

/* The TV's volume, as an LG says it: the slider fills to it, and greys while it's muted. An LG's
   brightness is the same slider, under it, greyed with a word on its Energy Saving where that sets it. */
.remote-sliders {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.remote-level {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 4px 0 8px;
}
.remote-pad .remote-level-icon {
  flex: none;
  width: 22px;
  height: 22px;
  color: var(--text-2);
}
.remote-volume .cross,
.remote-volume.muted .waves {
  display: none;
}
.remote-volume.muted .cross {
  display: inline;
}
.remote-level input[type='range'] {
  flex: 1;
  height: 44px;
  transition: opacity 150ms ease-out;
}
.remote-volume.muted input[type='range'],
.remote-level input[type='range']:disabled {
  opacity: 0.5;
}
.remote-level input[type='range']::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: 4px;
  background: linear-gradient(to right, var(--accent) calc(13px + (100% - 26px) * var(--level, 0) / 100), var(--surface-3) 0);
}
.remote-level input[type='range']::-webkit-slider-thumb {
  width: 26px;
  height: 26px;
  margin-top: -9px;
  background: var(--text);
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.1), 0 2px 6px rgb(0 0 0 / 0.35);
}
.remote-level input[type='range']::-moz-range-track {
  height: 8px;
  border-radius: 4px;
}
.remote-level input[type='range']::-moz-range-progress {
  height: 8px;
  border-radius: 4px;
}
.remote-level input[type='range']::-moz-range-thumb {
  width: 26px;
  height: 26px;
  background: var(--text);
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.1), 0 2px 6px rgb(0 0 0 / 0.35);
}
.remote-level output {
  min-width: 3ch;
  color: var(--text-2);
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: end;
}
/* Under the slider, from where its track starts (past the icon). */
.remote-level-note {
  margin: -4px 0 4px;
  padding: 0 4px 0 42px;
  color: var(--text-2);
  font-size: 13px;
  line-height: 1.45;
  text-wrap: pretty;
}

/* Rewind, play, pause, fast forward and stop: one bar, as on most remotes. */
.remote-media {
  display: flex;
  gap: 2px;
  height: 56px;
  padding: 4px;
  border-radius: 18px;
}
.remote-pad .remote-media button {
  flex: 1;
  border-radius: 14px;
}
.remote-pad .remote-media svg {
  width: 22px;
  height: 22px;
  fill: currentColor;
  stroke-width: 1.5;
}
.remote-pad .remote-media [data-key='pause'] svg {
  stroke-width: 3.2;
}

/* The less-used controls: a tab for each the TV has, an icon over its name. */
.remote-tabs {
  margin-top: 10px;
}
.remote-tabs > label > span {
  gap: 4px;
  min-height: 58px;
  padding: 8px 2px 7px;
  font-size: 13px;
}
.remote-tabs svg {
  width: 20px;
  height: 20px;
}
.remote-tabs input:checked + span {
  background: var(--key-raised);
  box-shadow: var(--key-edge);
  color: var(--text);
}
.remote-tabs input:checked + span svg {
  color: var(--accent);
}
#remote-apps-tab {
  display: inline;
  min-height: 0;
  padding: 0;
  color: inherit;
}
.remote-panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.remote-panel:not(.chosen) {
  display: none;
}
/* The tab names the panel; a TV with only one panel has no tabs, so its title shows instead. */
.remote-panel-title {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.remote-tabs[hidden] ~ .remote-panel .remote-panel-title {
  position: static;
  width: auto;
  height: auto;
  margin: 10px 0 0;
  overflow: visible;
  clip-path: none;
  color: var(--text-2);
  font-size: 13px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.remote-panel .hint {
  margin: 0;
}

/* Inputs are chips; apps are tiles, each with its initial on a color of its own. The one the TV
   shows is outlined. */
.remote-find {
  width: 100%;
  min-height: 46px;
  padding: 10px 14px 10px 42px;
  border: 1px solid var(--field-border);
  border-radius: 14px;
  appearance: none;
  background: var(--bg) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b919d' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6.5'/%3E%3Cpath d='m15.5 15.5 5 5'/%3E%3C/svg%3E") no-repeat 14px center / 18px;
  color: var(--text);
  font: inherit;
  font-size: 17px;
}
.remote-find::-webkit-search-decoration {
  appearance: none;
}
.remote-sources {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.remote-sources h4 {
  margin: 4px 0 -2px;
  color: var(--text-2);
  font-size: 13px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.remote-inputs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.remote-pad .remote-input {
  display: inline-flex;
  gap: 8px;
  min-height: 44px;
  max-width: 100%;
  padding: 0 16px 0 12px;
  border-radius: 14px;
  font-size: 15px;
  font-weight: 700;
  overflow-wrap: anywhere;
}
.remote-input::before {
  content: '';
  flex: none;
  width: 18px;
  height: 18px;
  background: var(--text-2);
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3.5h4.5a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H14M9.5 16.5 14 12 9.5 7.5M14 12H3.5'/%3E%3C/svg%3E") center / contain no-repeat;
}
.remote-apps {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 10px;
}
.remote-pad .remote-app {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  min-width: 0;
  min-height: 108px;
  padding: 14px 6px 10px;
  border-radius: 18px;
}
.remote-app-mark {
  position: relative;
  display: grid;
  flex: none;
  place-items: center;
  width: 44px;
  height: 44px;
  overflow: hidden;
  border-radius: 13px;
  background: color-mix(in srgb, var(--mark) 22%, var(--surface));
  color: var(--mark);
  font-size: 21px;
  font-weight: 700;
  line-height: 1;
}
/* Logo.dev's logo covers the initial, on white: some logos are a mark on nothing, drawn for white. */
.remote-app-logo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: #fff;
  object-fit: cover;
}
.remote-app-logo.dark {
  background: #000;
}
.remote-app-mark[data-color='1'] { --mark: var(--spk-1); }
.remote-app-mark[data-color='2'] { --mark: var(--spk-2); }
.remote-app-mark[data-color='3'] { --mark: var(--spk-3); }
.remote-app-mark[data-color='4'] { --mark: var(--spk-4); }
.remote-app-mark[data-color='5'] { --mark: var(--spk-5); }
.remote-app-mark[data-color='6'] { --mark: var(--spk-6); }
.remote-app-name {
  display: -webkit-box;
  max-width: 100%;
  overflow: hidden;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.25;
  overflow-wrap: anywhere;
  text-align: center;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.remote-pad :is(.remote-app, .remote-input)[aria-current='true'] {
  box-shadow: inset 0 0 0 2px var(--accent), var(--key-edge);
}
.remote-credit {
  margin: 14px 0 0;
  font-size: 12px;
  text-align: center;
}
.remote-credit a {
  color: var(--text-2);
}

/* A box and its button: typing on the TV, and a link to play on it. */
.remote-type {
  display: flex;
  gap: 8px;
}
.remote-type input {
  flex: 1;
  min-width: 0;
}
.remote-pad .remote-type .btn {
  min-height: 48px;
  padding: 0 20px;
  border-radius: 14px;
}

/* Shaped like the TV's screen, and all of it the TV's: no scrolling, zooming or selecting here. Dots
   like a laptop's touchpad say it's one. */
.remote-touchpad {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  max-height: 45dvh;
  border-radius: 22px;
  background: radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--text) 12%, transparent) 1px, transparent 0) 7px 7px / 16px 16px, var(--key);
  box-shadow: var(--key-sunk);
  cursor: crosshair;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  transition: background-color 150ms ease-out;
}
.remote-touchpad:active {
  background-color: var(--surface-3);
}
.remote-touchpad svg {
  width: 32px;
  height: 32px;
  fill: none;
  stroke: var(--text-2);
  stroke-width: 1.6;
  stroke-linejoin: round;
  opacity: 0.6;
}

.remote-numbers {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.remote-pad .remote-numbers button {
  height: 54px;
  border-radius: 16px;
  font-size: 21px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* 0 sits under 8, with Dash to its left where the TV has one. */
.remote-numbers [data-key='0'] {
  grid-column: 2;
}
.remote-colors {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
/* The colors of a TV remote's buttons, the same in every theme. */
.remote-pad .remote-colors button {
  height: 46px;
  border-radius: 14px;
}
.remote-colors button::before {
  content: '';
  width: 32px;
  height: 8px;
  border-radius: 4px;
}
.remote-colors .red::before { background: #e5484d; }
.remote-colors .green::before { background: #30a46c; }
.remote-colors .yellow::before { background: #f5d90a; }
.remote-colors .blue::before { background: #3e63dd; }
.remote-extras {
  display: grid;
  grid-auto-columns: 1fr;
  grid-auto-flow: column;
  gap: 10px;
}

/* On a phone the remote fills the width, a little tighter, so its main keys fit on one screen. */
@media (max-width: 640px) {
  .remote-pad,
  .remote-keys {
    gap: 12px;
  }
  .remote-keys {
    margin-top: 4px;
  }
  .remote-pad .remote-key,
  .remote-pad .remote-captions {
    height: 56px;
    min-height: 56px;
  }
  .remote-pad .remote-key.power {
    width: 56px;
  }
  .remote-dpad {
    width: min(100%, 232px);
  }
}
/* On a wide screen a full remote is two columns, so nothing needs scrolling: the keys every remote
   has on the left, and on the right the TV, its volume and playback, and the tabs, the chosen one
   filling the height the keys leave and scrolling within it. Spacing is margins, as a grid's gaps
   would stay between rows left empty (no PIN asked, nothing said). */
@media (min-width: 860px) {
  dialog.remote-sheet.wide {
    --sheet-pad: 32px;
    width: min(820px, calc(100% - 64px));
    padding-bottom: 32px;
    border-radius: 28px;
  }
  .remote-sheet.wide .remote-pad {
    display: grid;
    grid-template-columns: 336px minmax(0, 1fr);
    grid-template-rows: repeat(6, auto) minmax(0, 1fr);
    gap: 0 36px;
    margin-top: 8px;
  }
  .remote-sheet.wide .remote-pad > * {
    grid-column: 2;
  }
  .remote-sheet.wide .remote-keys {
    grid-column: 1;
    grid-row: 1 / -1;
    margin: 0;
  }
  .remote-sheet.wide .remote-tv { grid-row: 1; }
  .remote-sheet.wide #remote-status { grid-row: 2; margin-top: 12px; }
  .remote-sheet.wide .remote-pin { grid-row: 3; margin-top: 12px; }
  .remote-sheet.wide .remote-sliders { grid-row: 4; margin-top: 14px; }
  .remote-sheet.wide .remote-media { grid-row: 5; margin-top: 10px; }
  .remote-sheet.wide .remote-tabs { grid-row: 6; margin-top: 22px; }
  /* Sized by the room left, not by what's in it (contain), so a long list of apps scrolls here. */
  .remote-sheet.wide .remote-panel {
    grid-row: 7;
    min-height: clamp(200px, calc(88dvh - 400px), 440px);
    margin: 10px -6px 0;
    padding: 4px 6px 6px;
    overflow-y: auto;
    overscroll-behavior: contain;
    contain: size;
  }
  .remote-sheet.wide .remote-apps {
    grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  }
}
/* On a short wide screen, as on a 13-inch laptop, the keys are a little smaller, so the whole remote
   still fits without scrolling; the tooltips name the keyboard's keys instead of the line under them. */
@media (min-width: 860px) and (max-height: 760px) {
  .remote-sheet.wide .remote-keys {
    gap: 10px;
  }
  .remote-sheet.wide .remote-dpad {
    width: 212px;
  }
  .remote-sheet.wide :is(.remote-key, .remote-captions) {
    height: 52px;
    min-height: 52px;
  }
  .remote-sheet.wide .remote-key.power {
    width: 52px;
  }
  .remote-sheet.wide .rocker button {
    height: 50px;
  }
  .remote-sheet.wide .remote-middle .remote-key {
    min-height: 54px;
  }
  .remote-sheet.wide .remote-shortcuts {
    display: none;
  }
}
/* With colors forced, keys keep an outline, as backgrounds and shadows are gone. */
@media (forced-colors: active) {
  .remote-pad :is(.remote-key, .remote-captions, .rocker, .remote-dpad, .dpad-ok, .remote-media, .remote-numbers button, .remote-colors button, .remote-input, .remote-app),
  .remote-tv,
  .remote-touchpad {
    border: 1px solid ButtonText;
  }
  .remote-colors button::before,
  .remote-app-mark {
    forced-color-adjust: none;
  }
}
#helper-about code {
  flex: 1;
  font-size: 0.95em;
  overflow-wrap: anywhere;
  user-select: all; /* one tap selects the whole command */
}

/* Lost its connection (usually the phone's screen locked); it comes back when the phone wakes. */
.device-item[data-state='away'] .dot {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--spk-0);
}
.device-item[data-state='away'] strong {
  color: var(--text-2);
}

body.mic-page {
  height: auto;
  min-height: 100dvh;
  overflow: auto;
}
.mic-brand {
  margin: 0;
  padding: 10px 0;
  font-size: 15px;
  font-weight: 700;
}
.mic-main {
  width: min(32rem, 100%);
  margin: 0 auto;
  padding: 20px 16px max(32px, env(safe-area-inset-bottom));
  font-size: 17px;
  line-height: 1.45;
}
.mic-main h1 {
  margin: 8px 0 12px;
  font-size: 26px;
  line-height: 1.2;
  text-wrap: balance;
}
.mic-main .lead {
  margin: 0;
}
.mic-code {
  margin: 20px 0;
  font-size: clamp(44px, 16vw, 72px);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-indent: 0.18em;
  text-align: center;
}
/* The same state-tinted pill as the app's top bar, sized for the page. */
.mic-status {
  min-height: 48px;
  padding: 12px 16px 12px 14px;
  border-radius: 16px;
  font-size: 17px;
}
.mic-status #status-text {
  flex: 1;
}
.btn.wide {
  width: 100%;
}
.heard {
  min-height: 7.5em;
  margin: 18px 0 0;
  padding: 14px 16px;
  border-radius: 16px;
  background: var(--bg);
  box-shadow: var(--ring);
  font-size: 22px;
  line-height: 1.35;
}
.heard.empty {
  color: var(--text-2);
  font-size: 17px;
}
/* ---------- the admin page, for the server's admins (admin.html) ---------- */

/* One column for every tab, wide enough for the Pis' table; the bar's back link and Sign out line up
   with its edges. */
body.admin-page {
  --admin-width: 96rem;
  height: auto;
  min-height: 100dvh;
  overflow: auto;
}
.admin-page .bar {
  padding-inline: max(16px, env(safe-area-inset-left), (100% - var(--admin-width)) / 2 + 16px)
    max(16px, env(safe-area-inset-right), (100% - var(--admin-width)) / 2 + 16px);
}
.admin-main {
  width: min(var(--admin-width), 100%);
  margin: 0 auto;
  padding: 8px 16px max(32px, env(safe-area-inset-bottom));
  font-size: 16px;
  line-height: 1.45;
}
.admin-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
}
.admin-head h1 {
  margin: 8px 0 0;
  font-size: 26px;
  line-height: 1.2;
  text-wrap: balance;
}
.admin-head .hint {
  margin: 4px 0 0;
}
.admin-range {
  min-width: min(20rem, 100%);
  margin: 0;
  border: 0;
}
.admin-msg {
  margin: 20px 0;
}
.admin-msg:empty {
  display: none;
}
.admin-msg a {
  color: var(--accent);
}
/* Usage: a summary, then a section for each question, each a few cards. */
.report-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.admin-main .report-section {
  margin-top: 40px;
}
.admin-main .report-section:first-child {
  margin-top: 24px;
}
.admin-main .report-section > h2 {
  margin: 0;
}
.report-section > :is(.hint, .pis-devices-head) {
  margin: 0;
}
.report-grid {
  display: grid;
  gap: 16px;
}
@media (min-width: 48rem) {
  .report-grid {
    grid-template-columns: 1fr 1fr;
  }
}
.report-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  padding: 16px 18px 18px;
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--ring);
}
/* Its title and what it shows on the left; See all, if it has one, on the right. */
.card-head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 2px 12px;
}
.card-head h3 {
  margin: 0;
  font-size: 16px;
  line-height: 1.35;
}
.card-head p {
  grid-column: 1;
  margin: 0;
  color: var(--text-2);
  font-size: 14px;
  text-wrap: pretty;
}
.card-head .see-all {
  grid-row: 1 / span 2;
  grid-column: 2;
  margin: -6px -8px 0 0;
}
.card-figure {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin: 0;
  font-size: 32px;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.card-foot {
  margin: auto 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  color: var(--text-2);
  font-size: 14px;
  text-wrap: pretty;
}
.card-empty {
  margin: 0;
  color: var(--text-2);
}

/* The summary: four numbers, each against the period before. The number leads, its name under it. */
.kpis {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin: 0;
}
@media (min-width: 60rem) {
  .kpis {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
.kpis > div {
  display: flex;
  flex-direction: column;
  padding: 14px 16px 16px;
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--ring);
}
.kpis dt {
  order: 2;
  margin-top: 6px;
  font-size: 14px;
  font-weight: 700;
}
.kpis dd {
  margin: 0;
}
.kpi-value {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  order: 1;
  font-size: 28px;
  font-weight: 700;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.kpi-note {
  order: 3;
  color: var(--text-2);
  font-size: 13px;
}
/* Against the period before: an arrow and how much, green or red only where one way is plainly better. */
.delta {
  padding: 1px 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.5;
  white-space: nowrap;
}
.delta[data-tone='good'] {
  background: color-mix(in srgb, var(--ok) var(--tint), transparent);
  color: var(--ok);
}
.delta[data-tone='bad'] {
  background: color-mix(in srgb, var(--danger) var(--tint), transparent);
  color: var(--danger);
}

/* What a whole was made of, as one thin bar in the charts' colors. */
.split-bar {
  display: flex;
  gap: 2px;
  height: 8px;
}
.split-bar span {
  flex: var(--share) 1 0;
  min-width: 3px;
  background: var(--part);
}
.split-bar span:first-child {
  border-radius: 4px 0 0 4px;
}
.split-bar span:last-child {
  border-radius: 0 4px 4px 0;
}
.split-bar span:only-child {
  border-radius: 4px;
}

/* A legend that's also the numbers: a row each, its figures lined up on the right. */
.value-legend {
  display: grid;
  grid-template-columns: 1fr auto auto;
  margin: 0;
  padding: 0;
  font-size: 14px;
  list-style: none;
}
.value-legend li {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  gap: 16px;
  padding: 7px 0;
}
.value-legend li + li {
  border-top: 1px solid var(--line);
}
.vl-name {
  min-width: 0;
}
.vl-value {
  text-align: end;
  font-variant-numeric: tabular-nums;
}
.vl-value + .vl-value {
  color: var(--text-2);
}

/* Horizontal bars, each out of the same whole: its name and number over its bar. */
.hbars {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
  padding: 0;
  font-size: 14px;
  list-style: none;
}
.hbars li {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 12px;
}
.hb-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hb-value {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.hb-value small {
  display: inline;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 400;
}
.hb-track {
  grid-column: 1 / -1;
  height: 8px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--text) 10%, transparent);
}
.hb-track > i {
  display: block;
  width: var(--share);
  min-width: 3px;
  height: 100%;
  border-radius: inherit;
  background: var(--series-1);
}

/* Label and value on a hairline each. */
.facts {
  margin: 0;
  font-size: 15px;
}
.facts > div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 0;
}
.facts > div + div {
  border-top: 1px solid var(--line);
}
.facts dt {
  color: var(--text-2);
}
.facts dd {
  margin: 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A few accounts in a short table, as the full one further down. */
.top-list {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}
.top-list th,
.top-list td {
  padding: 8px 0;
  text-align: start;
  vertical-align: top;
}
.top-list :is(th, td) + :is(th, td) {
  padding-left: 16px;
}
.top-list thead th {
  padding-top: 0;
  border-bottom: 1px solid var(--line);
  color: var(--text-2);
  font-size: 13px;
  font-weight: 400;
}
.top-list tbody tr + tr > * {
  border-top: 1px solid var(--line);
}
.top-list tbody th {
  font-weight: 400;
  overflow-wrap: anywhere;
}
.top-list .num {
  text-align: end;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.report-card .uc {
  --uc-height: 200px;
}
/* A tab's own sections, not the panels inside them, are spaced apart; every tab titles them alike. */
.admin-main [role='tabpanel'] > section {
  margin-top: 36px;
}
.admin-main h2 {
  margin: 0 0 12px;
  color: var(--text);
  font-size: 19px;
  line-height: 1.3;
}
/* A new period loads over the old one, dimmed, rather than blanking it. */
#report {
  transition: opacity 150ms ease-out;
}
#report[aria-busy='true'] {
  opacity: 0.55;
}

/* Until an admin signs in, the page is only its sign-in form; then who it is, and Sign out, top right. */
.admin-signin {
  width: min(26rem, 100%);
  margin: max(32px, 10vh) auto 0;
}
.admin-signin h1 {
  margin: 0 0 16px;
  font-size: 26px;
  line-height: 1.2;
}
/* Back to the captions app, top left, signed in or not. The link pulls left so the chevron, not its
   empty hover area, lines up with the page's edge. */
.admin-nav {
  display: flex;
  flex: none;
  align-items: center;
  gap: 4px;
  margin: 0;
}
.btn.back-link {
  gap: 4px;
  margin-left: -8px;
  padding-left: 4px;
}
.admin-nav .mic-brand {
  padding-left: 12px;
  border-left: 1px solid var(--line);
  color: var(--text-2);
}
/* Who is signed in, then the theme button, which stays put signed in or not. It pulls right so its
   icon, not its empty hover area, lines up with the page's edge. */
.admin-end {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
#theme-btn {
  flex: none;
  margin-right: -8px;
}
#theme-btn svg {
  display: none;
}
#theme-btn[data-choice='auto'] .theme-auto,
#theme-btn[data-choice='light'] .theme-light,
#theme-btn[data-choice='dark'] .theme-dark {
  display: block;
}
.admin-who {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  margin: 0;
  color: var(--text-2);
  font-size: 15px;
}
/* On a phone the name gives way, never the link back or Sign out. */
.admin-who span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.admin-who .btn {
  flex: none;
  white-space: nowrap;
}

/* Two tabs, People and Usage, on a hairline. */
.admin-tabs {
  display: flex;
  gap: 4px;
  margin: 8px 0 16px;
  border-bottom: 1px solid var(--line);
}
.admin-tab {
  min-height: 44px;
  margin-bottom: -1px;
  padding: 0 14px;
  border: 0;
  border-bottom: 3px solid transparent;
  background: none;
  color: var(--text-2);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  transition: color 150ms ease-out;
}
@media (hover: hover) {
  .admin-tab:hover {
    color: var(--text);
  }
}
.admin-tab[aria-selected='true'] {
  border-bottom-color: var(--accent);
  color: var(--text);
}
.admin-tab:focus-visible {
  border-radius: 8px 8px 0 0;
  outline-offset: -3px;
}

/* Accounts: forms for a new account or an invite to one, unused invites, then a card for each account. */
.admin-invite {
  margin-top: 20px;
}
.admin-invite .link-row input {
  flex: 1;
  min-width: 0;
}
/* Side by side where there's room for two, so a card's actions stay near its names. */
.admin-accounts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(34rem, 100%), 1fr));
  align-items: start;
  gap: 16px;
}
.admin-accounts > .card {
  margin: 0;
}
.admin-account h3 {
  margin: 0;
  font-size: 19px;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.admin-account-head .hint {
  margin: 2px 0 0;
}
.admin-account-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
}
.admin-invite-made .link-row input {
  flex: 1;
  min-width: 0;
}
.admin-main .member-actions {
  flex-wrap: wrap;
}
.admin-limit .form-msg {
  flex-basis: 100%;
  margin: 0;
}
.limit-picker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.limit-picker select {
  width: auto;
}
.limit-picker input {
  width: 6.5em;
  min-height: 42px;
  padding: 8px 12px;
  border: 1px solid var(--field-border);
  border-radius: 11px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}
.admin-page .toast {
  position: fixed;
  bottom: max(16px, env(safe-area-inset-bottom));
}

/* Pis: like a device manager. The list: cards of how they are together over a table of every Pi,
   each row striped by how it is. A Pi's page: its name and stripe, tabs, then its measures, what it
   is and what needs a look side by side. */
#pane-pis {
  --state-ok: var(--ok);
  --state-warn: var(--warn);
  --state-bad: var(--danger);
  --state-offline: var(--text-2);
}
#pane-pis [data-state='ok'] {
  --state: var(--state-ok);
}
#pane-pis [data-state='warn'] {
  --state: var(--state-warn);
}
#pane-pis [data-state='bad'] {
  --state: var(--state-bad);
}
#pane-pis [data-state='offline'] {
  --state: var(--state-offline);
}
#pane-pis[aria-busy='true'] :is(#pi-rows, .pi-tab-panel) {
  opacity: 0.55;
}
#pi-rows,
.pi-tab-panel {
  transition: opacity 150ms ease-out;
}
#pis-title,
#pi-page h1 {
  outline: none;
}
.pis-note {
  margin-top: 24px;
}
/* What changes with each look at the Pis keeps its width as its digits change. */
#pis-summary,
.pi-head-check,
.pi-table td,
.pi-count,
.pi-info dd {
  font-variant-numeric: tabular-nums;
}

/* How often the tab checks the Pis, and Check now, beside its title. Its controls and the table's
   filters are all one height, 40px, so a row of them lines up. */
.pis-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.pis-every {
  margin: 0 4px 0 0;
  border: 0;
}
.pis-controls > .hint {
  margin: 0 4px 0 0;
}
.admin-main .segmented {
  padding: 3px;
  border-radius: 12px;
}
.admin-main .segmented span {
  min-height: 34px;
  padding: 0 12px;
  border-radius: 9px;
  font-size: 14px;
  white-space: nowrap;
}
:is(.pis-controls, .pis-filters) .btn.small {
  min-height: 40px;
}
/* Columns opens a menu under it: its chevron points down, then up. */
.pis-columns > summary {
  gap: 10px;
  padding: 0 12px 0 14px;
}
.pis-columns > summary::after {
  width: 7px;
  height: 7px;
  margin: -4px 2px 0 0;
  rotate: 45deg;
}
.pis-columns[open] > summary::after {
  margin-top: 3px;
  rotate: -135deg;
}

/* A panel: a titled box, as the device manager's cards. */
.pi-panel {
  min-width: 0;
  padding: 16px 20px 18px;
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--ring);
}
.pi-panel > h2,
.pi-panel h3 {
  margin: 0 0 12px;
  color: var(--text);
  font-size: 17px;
  letter-spacing: 0;
  line-height: 1.3;
  text-transform: none;
}
.pis-overview {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 16px;
  margin-top: 24px;
}
@media (min-width: 72rem) {
  .pis-overview {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.7fr);
  }
  .pi-kinds {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 8px;
  }
}
.pis-overview[hidden] {
  display: none;
}
.pi-accounts,
.pi-legend,
.pi-kinds {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Two by two, so no state is left alone on a line. */
.pi-legend {
  display: grid;
  grid-template-columns: repeat(2, max-content);
  gap: 4px 8px;
  margin-top: 12px;
}
/* A row on a card that chooses what the table shows. */
.pi-chooser {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 38px;
  padding: 5px 10px;
  border: 0;
  border-radius: 10px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 15px;
  text-align: start;
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  .pi-chooser:hover {
    background: var(--surface-2);
  }
}
.pi-chooser[aria-pressed='true'] {
  background: color-mix(in srgb, var(--accent) var(--tint), transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 50%, transparent);
}
.pi-legend .pi-chooser {
  width: auto;
  gap: 8px;
  padding: 4px 10px;
  font-size: 14px;
}
.pi-kinds .pi-chooser > span:nth-child(2),
.pi-account-name {
  flex: 1;
  min-width: 0;
}
.pi-count {
  color: var(--text-2);
  font-weight: 700;
}
.pi-kinds .pi-count {
  color: var(--text);
}
/* Each account with a stripe of its worst Pi, and how many need a look. */
.pi-accounts .pi-chooser {
  padding-left: 14px;
  box-shadow: inset 4px 0 var(--state);
  border-radius: 4px 10px 10px 4px;
}
.pi-accounts .pi-chooser[aria-pressed='true'] {
  box-shadow: inset 4px 0 var(--state), inset 0 0 0 1px color-mix(in srgb, var(--accent) 50%, transparent);
}
.pi-account-name strong,
.pi-account-name small {
  display: block;
  overflow-wrap: anywhere;
}
.pi-account-name small {
  color: var(--text-2);
  font-size: 13px;
}
.pi-account-look {
  display: inline-grid;
  place-items: center;
  min-width: 26px;
  height: 26px;
  padding: 0 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--state) 22%, transparent);
  color: var(--state);
  font-size: 14px;
  font-weight: 700;
}
/* How many are healthy, and a bar of each state, as the key says. */
.pi-healthy {
  margin: 0 0 10px;
  font-size: 22px;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
.pi-bar {
  display: flex;
  gap: 2px;
  height: 14px;
  overflow: hidden;
  border-radius: 4px;
}
.pi-bar > span {
  flex-basis: 0;
  background: var(--state);
}
.pi-swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--state);
}
.pi-fine {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: var(--text-2);
}
.pi-fine .pi-icon {
  color: var(--ok);
}

/* Icons: a stroke in its text's color. */
.pi-icon,
.pi-badge,
.pi-head-icon {
  display: inline-grid;
  flex: none;
  place-items: center;
}
.pi-icon svg,
.pi-badge svg,
.pi-head-icon svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* An issue's badge: a problem's is filled, something to check a ring. */
.pi-badge {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px var(--warn);
  color: var(--warn);
}
.pi-badge svg {
  width: 15px;
  height: 15px;
}
.pi-badge[data-level='bad'] {
  background: var(--danger);
  box-shadow: none;
  color: var(--bg);
}

/* The table: a row for each Pi, striped by how it is. */
.pis-devices {
  margin-top: 32px;
}
.pis-devices[hidden] {
  display: none;
}
.pis-devices-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 16px;
  margin-bottom: 12px;
}
.pis-devices-head h2 {
  margin: 0;
}
.pis-devices-head h2 .hint {
  font-size: 15px;
  font-weight: 400;
}
.pis-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.pis-filters select {
  width: auto;
  min-height: 40px;
  padding: 7px 36px 7px 12px;
  border-radius: 10px;
  background-position: right 10px center;
  font-size: 15px;
}
.pis-search {
  width: 15rem;
  max-width: 100%;
  min-height: 40px;
  padding: 7px 12px 7px 36px;
  border: 1px solid var(--field-border);
  border-radius: 10px;
  background: var(--bg)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b919d' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-4-4'/%3E%3C/svg%3E")
    no-repeat 11px center / 17px;
  color: var(--text);
  font: inherit;
  font-size: 15px;
}
@media (max-width: 640px) {
  .pis-filters,
  .pis-search,
  .pis-filters select {
    flex: 1 1 100%;
    width: 100%;
  }
}
.pi-table tbody tr {
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  .pi-table tbody tr:hover {
    background: var(--surface-2);
  }
}
.pi-table tbody tr > th {
  min-width: 13rem;
}
/* The state's stripe, down the row's first cell: its box to choose it. */
.pi-table tbody tr > :first-child {
  box-shadow: inset 4px 0 var(--state);
  padding-left: 18px;
}
.pi-table td,
.pi-table th {
  vertical-align: middle;
}
.pi-name {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-weight: 700;
  text-decoration: none;
}
.pi-name .pi-icon {
  color: var(--text-2);
}
@media (hover: hover) {
  .pi-name:hover span {
    text-decoration: underline;
  }
}
.pi-table th small {
  font-weight: 400;
}
.pi-badges {
  white-space: nowrap;
}
.pi-badges .pi-badge + .pi-badge {
  margin-left: 4px;
}
.pi-none {
  color: var(--text-2);
}
.pi-table [data-level='warn']:not(.pi-badge, .pi-meter) {
  color: var(--warn);
  font-weight: 700;
}
.pi-table [data-level='bad']:not(.pi-badge, .pi-meter) {
  color: var(--danger);
  font-weight: 700;
}
.pi-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  margin-right: 7px;
  border-radius: 50%;
  background: var(--ok);
  vertical-align: 0.05em;
}
.idle .pi-dot,
[data-offline] .pi-dot {
  background: none;
  box-shadow: inset 0 0 0 2px var(--text-2);
}
/* A share as a short bar under its number. */
.pi-meter {
  display: block;
  width: 72px;
  height: 4px;
  margin-top: 5px;
  overflow: hidden;
  border-radius: 2px;
  background: var(--surface-3);
}
.pi-meter::before {
  content: '';
  display: block;
  width: calc(var(--share) * 100%);
  height: 100%;
  background: var(--series-1);
}
.pi-meter[data-level='warn']::before {
  background: var(--warn);
}
.pi-meter[data-level='bad']::before {
  background: var(--danger);
}
.pi-meter.wide {
  width: 100%;
  height: 10px;
  margin-top: 12px;
  border-radius: 4px;
}

/* A Pi's page. */
.pi-crumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  color: var(--text-2);
  font-size: 15px;
}
.pi-crumbs li + li::before {
  content: '›';
  margin-right: 6px;
}
.pi-crumbs a {
  color: var(--accent);
}
.pi-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
  margin-top: 12px;
  padding-left: 16px;
  box-shadow: inset 4px 0 var(--state);
}
.pi-head-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  color: var(--text-2);
}
.pi-head-icon svg {
  width: 24px;
  height: 24px;
}
.pi-head-text {
  flex: 1;
  min-width: min(16rem, 100%);
}
.pi-head h1 {
  margin: 0;
  font-size: 26px;
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.pi-head-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
  margin: 4px 0 0;
}
.pi-head-status strong {
  color: var(--state);
}
.pi-head .hint {
  margin: 2px 0 0;
}
.pi-head-check {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
}
.pi-head-check .hint {
  margin: 0;
}
.pi-tabs {
  margin-top: 20px;
  overflow-x: auto;
  scrollbar-width: none;
}
.pi-tabs .admin-tab {
  display: inline-flex;
  flex: none;
  align-items: center;
  text-decoration: none;
}
/* Overview: measures, what it is, and what needs a look; one column on a phone, what needs a look first. */
.pi-overview {
  display: grid;
  grid-template-areas: 'health' 'measures' 'about';
  gap: 16px;
}
@media (min-width: 48rem) {
  .pi-overview {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: 'health health' 'measures about';
  }
}
@media (min-width: 76rem) {
  .pi-overview {
    grid-template-columns: minmax(16rem, 1fr) minmax(0, 1.4fr) minmax(16rem, 1fr);
    grid-template-areas: 'measures about health';
    align-items: start;
  }
}
.pi-measures {
  grid-area: measures;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.pi-measures.wide {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
}
@media (max-width: 30rem) {
  .pi-measures.wide {
    grid-template-columns: minmax(0, 1fr);
  }
}
.pi-about {
  grid-area: about;
}
.pi-health {
  grid-area: health;
}
.pi-measure-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.pi-measure-top .hint {
  margin: 0;
  font-size: 13px;
}
.pi-measure h3 {
  margin: 0;
}
.pi-measure-value {
  margin: 6px 0 0;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
}
.pi-measure[data-level='warn'] .pi-measure-value {
  color: var(--warn);
}
.pi-measure[data-level='bad'] .pi-measure-value {
  color: var(--danger);
}
.pi-measure .hint {
  margin: 2px 0 0;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.pi-axis {
  display: flex;
  justify-content: space-between;
  margin: 4px 0 0;
  color: var(--text-2);
  font-size: 12px;
}
/* A day of one measure, oldest at the left, on a fixed scale; a Pi's limit dashed across. */
.trend {
  display: block;
  width: 100%;
  margin-top: 12px;
  overflow: visible;
  border-bottom: 1px solid var(--line);
}
.trend path {
  fill: none;
  stroke: var(--series-1);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.trend path.area {
  fill: color-mix(in srgb, var(--series-1) 22%, transparent);
  stroke: none;
}
.trend line {
  stroke: var(--danger);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0.7;
}
.pi-more {
  display: inline-block;
  margin-top: 14px;
  color: var(--accent);
}
/* What needs a look: its badge, what it is, and what to do. */
.pi-issues {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pi-issues li {
  display: flex;
  align-items: start;
  gap: 12px;
}
.pi-issues strong {
  display: block;
}
.pi-issues p {
  margin: 2px 0 0;
  color: var(--text-2);
  font-size: 15px;
  text-wrap: pretty;
}
/* A command to run on the Pi: one click selects it all, to copy. */
.pi-command {
  display: inline-block;
  max-width: 100%;
  margin-top: 6px;
  padding: 2px 7px;
  border-radius: 6px;
  background: var(--surface-3);
  font-size: 14px;
  overflow-wrap: anywhere;
  user-select: all;
}
.pi-health > .hint {
  margin: -6px 0 12px;
}
/* Details: its day in bigger charts, then what it is, a panel for each part. */
.pi-details {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.pi-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: 16px;
}
@media (max-width: 30rem) {
  .pi-facts {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* One column for the names in a list, so the values line up down it. */
.pi-info {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr);
  gap: 8px 16px;
  margin: 0;
}
@media (max-width: 520px) {
  .pi-info {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
  .pi-info dd + dt {
    margin-top: 8px;
  }
}
.pi-info dt {
  color: var(--text-2);
}
.pi-info dd {
  margin: 0;
  color: var(--text);
  overflow-wrap: anywhere;
}
/* What it runs: a search, how many, and a table. */
.pi-inventory-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 16px;
}
.pi-inventory-bar > .pis-filters {
  margin-top: 0;
}
/* A table that sorts and filters itself (see data-table.js): its search and choices, how many show, then it. */
.data-table-wrap > .pis-filters,
.pi-inventory-bar > .pis-filters {
  margin-top: 16px;
}
.table-count {
  margin: 12px 0;
  font-variant-numeric: tabular-nums;
}
.data-table td {
  font-variant-numeric: tabular-nums;
}
.data-table [data-level='warn'] {
  color: var(--warn);
  font-weight: 700;
}
.data-table [data-level='bad'] {
  color: var(--danger);
  font-weight: 700;
}
/* A long description stops short, a sentence wraps. */
.data-table td.wide {
  min-width: 20rem;
  max-width: 40rem;
  overflow: hidden;
  color: var(--text-2);
  text-overflow: ellipsis;
}
.data-table td.wrap {
  min-width: 20rem;
  white-space: normal;
  text-wrap: pretty;
}

/* A Pi's actions, beside its name: its star, a script, a shell, its screen. */
.pi-head-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
}
.pi-actions {
  display: flex;
  gap: 2px;
}
.pi-action,
.pi-star {
  display: inline-grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: none;
  color: var(--accent);
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
.pi-action svg,
.pi-star svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pi-action[data-action='script'] svg {
  fill: currentColor;
}
@media (hover: hover) {
  .pi-action:not(:disabled):hover,
  .pi-star:hover {
    background: var(--surface-2);
  }
}
.pi-action:disabled {
  color: var(--text-2);
  opacity: 0.45;
  cursor: default;
}
/* A star's filled once it's starred, not only colored. */
.pi-action[aria-pressed='true'],
.pi-star[aria-pressed='true'] {
  color: var(--warn);
}
.pi-action[aria-pressed='true'] svg,
.pi-star[aria-pressed='true'] svg {
  fill: currentColor;
}
.pi-star {
  width: 28px;
  height: 28px;
  margin: -4px 2px -4px -6px;
  color: var(--text-2);
}
.pi-star svg {
  width: 16px;
  height: 16px;
}
.pi-name-line {
  display: inline-flex;
  align-items: center;
}
.pi-table th small {
  padding-left: 50px;
}
.pis-starred[aria-pressed='true'] {
  background: color-mix(in srgb, var(--warn) var(--tint), transparent);
  color: var(--warn);
}
/* Their sheets: wider than the app's, the screen and shell wider still. */
dialog.pi-sheet {
  width: min(760px, calc(100% - 32px));
  padding-bottom: 24px;
}
dialog.pi-screen,
dialog.pi-shell {
  width: min(1180px, calc(100% - 32px));
}
.pi-sheet-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 0 12px;
  background: var(--surface);
}
.pi-sheet-status {
  margin: 4px 0 0;
}
.pi-screen-picture {
  display: block;
  width: 100%;
  border-radius: 10px;
  background: #000;
  cursor: crosshair;
}
.pi-screen-picture:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.pi-screen .hint {
  margin-top: 10px;
}
.pi-terminal {
  height: min(64dvh, 640px);
  padding: 8px;
  border-radius: 10px;
  background: #0b0d12;
}
.pi-script-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.pi-script-top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: 10px;
}
@media (max-width: 560px) {
  .pi-script-top {
    grid-template-columns: minmax(0, 1fr);
  }
}
.pi-script-form select,
.pi-script-form input {
  font-size: 15px;
}
.pi-script-form textarea,
.pi-script-output {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  line-height: 1.5;
}
.pi-script-values {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 10px;
}
.pi-script-values:empty {
  display: none;
}
.pi-script-form .form-msg:empty {
  display: none;
}
.pi-script-buttons {
  flex-wrap: wrap;
  gap: 8px;
}
.pi-script-output {
  max-height: 40dvh;
  margin: 16px 0 0;
  padding: 12px 14px;
  overflow: auto;
  border-radius: 10px;
  background: #0b0d12;
  color: #e6e8ee;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Choosing Pis on the list, to run a script on them together; and the table's columns. */
.pi-choose {
  width: 1px;
  padding-right: 4px;
}
.pi-choose input {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.pis-bulk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 0 0 12px;
  padding: 10px 14px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) var(--tint), transparent);
  font-weight: 700;
}
.pis-bulk[hidden] {
  display: none;
}
.pis-columns {
  position: relative;
  margin: 0;
}
.pis-columns > summary {
  list-style: none;
}
.pis-columns > summary::-webkit-details-marker {
  display: none;
}
.pis-columns-menu {
  position: absolute;
  z-index: 5;
  right: 0;
  top: calc(100% + 6px);
  display: grid;
  grid-template-columns: repeat(2, minmax(11rem, 1fr));
  gap: 2px 16px;
  width: max-content;
  max-width: calc(100vw - 32px);
  max-height: 70dvh;
  margin: 0;
  padding: 12px 14px;
  overflow: auto;
  border: 0;
  border-radius: 12px;
  background: var(--surface-2);
  box-shadow: var(--shadow-lg);
}
.pis-columns-menu label {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  cursor: pointer;
}
.pis-columns-menu input {
  accent-color: var(--accent);
}
.pis-columns-menu button {
  grid-column: 1 / -1;
  justify-self: start;
  margin-top: 6px;
}
@media (max-width: 30rem) {
  .pis-columns-menu {
    grid-template-columns: minmax(0, 1fr);
    right: auto;
    left: 0;
  }
}
/* The admins' log: a script's lines, under what it says of the run. */
.log-detail summary {
  cursor: pointer;
  color: var(--accent);
}
.log-detail pre {
  max-height: 16em;
  margin: 6px 0 0;
  overflow: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  white-space: pre-wrap;
}
.pi-table code,
.pi-files code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
}
.pi-addresses span {
  display: block;
  white-space: nowrap;
}
#pis-alerts[aria-pressed='true'] {
  background: color-mix(in srgb, var(--ok) var(--tint), transparent);
  color: var(--ok);
}

/* A Pi's overview: what needs a look, and its note under it. */
.pi-side {
  grid-area: health;
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}
.pi-side > .pi-panel {
  margin: 0;
}
.pi-note textarea {
  width: 100%;
  min-height: 6rem;
  resize: vertical;
}
.pi-note .hint {
  margin: 6px 0 8px;
}

/* A row's own buttons, in a Pi's Processes, Services and Files. */
.pi-row-actions {
  display: inline-flex;
  gap: 4px;
  white-space: nowrap;
}
.pi-row-actions .btn.small {
  min-height: 30px;
  padding: 0 10px;
}

/* A Pi's Files: where it is, then what's there. */
.pi-files-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 8px;
}
.pi-files-where {
  display: flex;
  flex: 1 1 18rem;
  gap: 6px;
  min-width: 0;
}
.pi-files-where input {
  flex: 1;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
}
.pi-files-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.pi-files-tools [aria-pressed='true'] {
  background: color-mix(in srgb, var(--accent) var(--tint), transparent);
}
.pi-files-crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  margin: 4px 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  overflow-wrap: anywhere;
}
.pi-files-crumbs button {
  min-height: 28px;
  padding: 0 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--accent);
  font: inherit;
  cursor: pointer;
}
.pi-files-crumbs button:hover {
  text-decoration: underline;
}
.pi-files-sep {
  color: var(--text-2);
}
.pi-files-status:empty {
  display: none;
}
.pi-file-name {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 28rem;
  padding: 4px 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.pi-file-name span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pi-file-name svg {
  flex: none;
  width: 18px;
  height: 18px;
  fill: none;
  stroke: var(--text-2);
  stroke-width: 1.7;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.pi-file-name[data-kind='dir'] svg,
.pi-file-name[data-kind='link-dir'] svg {
  stroke: var(--accent);
}
.pi-file-name:hover span {
  text-decoration: underline;
}
dialog.pi-file-edit {
  width: min(1100px, calc(100% - 32px));
}
.pi-file-form textarea {
  width: 100%;
  min-height: 60dvh;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  line-height: 1.5;
  white-space: pre;
  tab-size: 4;
}
.pi-file-form .form-msg:empty {
  display: none;
}

/* A Pi's Activity: its alerts now, then what happened. */
.pi-activity > .pi-panel {
  margin-bottom: 20px;
}
.pi-alert-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.pi-alert-list li {
  padding: 8px 0 8px 12px;
  box-shadow: inset 3px 0 var(--danger);
}
.pi-alert-list li + li {
  margin-top: 8px;
}
.pi-alert-list strong {
  color: var(--danger);
}
.pi-alert-list p {
  margin: 4px 0 0;
}

/* Restarting a Pi: the Pi, or only its home agent. */
.pi-restart-choice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  padding: 14px 0;
}
.pi-restart-choice > div {
  flex: 1 1 20rem;
}
.pi-restart-choice + .pi-restart-choice {
  border-top: 1px solid var(--line);
}
.pi-restart-choice h3 {
  margin: 0;
  font-size: 17px;
}
.pi-restart-choice .hint {
  max-width: 34rem;
  margin: 4px 0 0;
}

/* A script run on several Pis: a line for each, which opens to what it printed. */
.pi-script-results {
  margin: 16px 0 0;
  padding: 0;
  list-style: none;
}
.pi-script-results li {
  padding: 8px 0 8px 12px;
  box-shadow: inset 3px 0 var(--line);
}
.pi-script-results li[data-level='ok'] {
  box-shadow: inset 3px 0 var(--ok);
}
.pi-script-results li[data-level='bad'] {
  box-shadow: inset 3px 0 var(--danger);
}
.pi-script-results li + li {
  margin-top: 6px;
}
.pi-script-results summary {
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 4px 10px;
  cursor: pointer;
}
.pi-script-results summary::after {
  margin-left: auto;
}
.pi-script-results .pi-script-output {
  margin-top: 8px;
}

/* Tags on Pis: chips on the list and a Pi's overview, which takes them away and adds them. */
.pi-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pi-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 24px;
  padding: 1px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) var(--tint), transparent);
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
.pi-tag small {
  color: var(--text-2);
  font-weight: 400;
}
.pi-tag-remove {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin: -2px -8px -2px 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--text-2);
  font: inherit;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.pi-tag-remove:hover,
.pi-tag-remove:focus-visible {
  background: color-mix(in srgb, var(--text) var(--tint), transparent);
  color: var(--text);
}
.pi-tags-card .pi-tags {
  margin-bottom: 12px;
}
.pi-tag-add {
  display: flex;
  gap: 8px;
}
.pi-tag-add input {
  flex: 1 1 auto;
  min-width: 0;
}
.pi-tags-card .hint {
  margin: 8px 0 0;
}
.pi-tagging h3 {
  margin: 20px 0 8px;
  font-size: 15px;
}

/* The automations: each a card with how it went last, and its form. */
.pi-tasks-top {
  flex-wrap: wrap;
  align-items: start;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 8px;
}
.pi-tasks-top .hint {
  flex: 1 1 22rem;
  margin: 0;
}
.pi-tasks-list {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}
.pi-task {
  padding: 14px 0 14px 14px;
  box-shadow: inset 3px 0 var(--line);
}
.pi-task + .pi-task {
  margin-top: 8px;
}
.pi-task[data-level='ok'] {
  box-shadow: inset 3px 0 var(--ok);
}
.pi-task[data-level='bad'] {
  box-shadow: inset 3px 0 var(--danger);
}
.pi-task[data-level='off'] > :not(.pi-task-head) {
  opacity: 0.7;
}
.pi-task-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.pi-task-head h3 {
  margin: 0;
  font-size: 17px;
  overflow-wrap: anywhere;
}
.pi-task-on,
.pi-task-check,
.pi-task-radio {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  cursor: pointer;
}
.pi-task-on input,
.pi-task-check input,
.pi-task-radio input,
.pi-task-days input,
.pi-task-choices input {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--accent);
}
.pi-task > .hint,
.pi-task-how {
  margin: 4px 0 0;
}
.pi-task-how[data-level='bad'] {
  color: var(--danger);
}
.pi-task-buttons {
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 8px;
  margin-top: 10px;
}
.pi-task-runs {
  margin-top: 10px;
}
.pi-task-runs > summary {
  justify-content: flex-start;
  gap: 8px;
  width: max-content;
  cursor: pointer;
  color: var(--accent);
  font-weight: 600;
}
.pi-task-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.pi-task-form h3 {
  margin: 0;
  font-size: 17px;
}
.pi-task-field {
  display: grid;
  gap: 6px;
}
.pi-task-field > span,
.pi-task-form legend {
  font-weight: 700;
}
.pi-task-field .hint {
  margin: 0;
}
.pi-task-when {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 16px;
}
.pi-task-when > .pi-task-field {
  flex: 1 1 12rem;
}
.pi-task-form input[type='time'],
.pi-task-form input[type='number'] {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--field-border);
  border-radius: 12px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 17px;
  line-height: 1.4;
}
.pi-task-form fieldset {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}
.pi-task-form legend {
  margin-bottom: 6px;
  padding: 0;
}
.pi-task-days {
  display: flex;
  flex-wrap: wrap;
}
.pi-task-days label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  margin-right: 14px;
  cursor: pointer;
}
.pi-task-choices {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0 16px;
  margin: 2px 0 8px 26px;
}
.pi-task-choices label {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  overflow-wrap: anywhere;
  cursor: pointer;
}
.pi-task-pis {
  max-height: 14rem;
  overflow: auto;
}
.pi-task-form [hidden],
.pi-task-days[hidden],
.pi-task-choices[hidden] {
  display: none;
}
.pi-task-form .form-msg:empty {
  display: none;
}
/* A baseline's checks, each a check and its fix; and what each Pi said. */
.pi-baseline-checks .pi-baseline-check {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}
@media (max-width: 560px) {
  .pi-baseline-checks .pi-baseline-check {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .pi-baseline-check select:nth-child(2) {
    grid-row: 2;
  }
}
.pi-baseline-checks > .btn {
  justify-self: start;
}
.pi-baseline-checks > .hint {
  display: block;
  margin-top: 8px;
}
.pi-baseline-fix-hint {
  margin: -10px 0 0 26px;
}
.pi-baseline-said {
  margin: 4px 0 0;
  padding-left: 18px;
}
.pi-script-results .pi-baseline-said li {
  padding: 0;
  box-shadow: none;
}
.pi-script-results .pi-baseline-said li + li {
  margin-top: 2px;
}
.staff-card {
  margin-top: 24px;
}
.staff-card h2 {
  margin-top: 0;
}
.staff-list {
  margin: 12px 0 16px;
  padding: 0 16px;
  list-style: none;
}
.staff-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  padding: 10px 0;
}
.staff-list li + li {
  border-top: 1px solid var(--line);
}
.staff-list .row {
  gap: 8px;
}
.staff-form {
  max-width: 34rem;
}
.staff-accounts {
  margin: 0;
}
.staff-accounts legend {
  font-weight: 700;
}
.pi-net-form select {
  width: auto;
}
.pi-net-form input[hidden] {
  display: none;
}
.pi-access > .pi-panel {
  margin-bottom: 20px;
}
.pi-keys code {
  overflow-wrap: anywhere;
}
.pi-user-keys {
  display: block !important;
  padding-left: 16px !important;
}
.pi-key-add {
  margin-top: 12px;
}
.pi-user-add {
  margin-top: 16px;
  max-width: 34rem;
}
.pi-quiet {
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 8px 12px;
  margin-top: 12px;
}
.pi-quiet select {
  width: auto;
}
.pi-head-status .hint {
  margin-left: 10px;
}
.pi-alerting .pi-tasks-top {
  align-items: end;
}
.pi-alerting .pi-tasks-top > div {
  flex: 1 1 22rem;
}
.pi-alerting .pi-tasks-top .hint {
  margin: 0;
}
.pi-alerting .pi-tasks-top:first-child .pi-rollouts-title {
  margin-top: 0;
}
.pi-rollouts-title {
  margin: 24px 0 4px;
  font-size: 17px;
}
.pi-activity .pi-script-results {
  margin: 0 0 12px;
}

/* Asking for a name (askText in dialogs.js). */
.ask-text-field {
  display: grid;
  gap: 6px;
  margin: 0 0 16px;
}
.ask-text-field input {
  width: 100%;
}

/* ---------- the 404 page (404.html) ---------- */

body.not-found-page {
  display: flex;
  flex-direction: column;
  height: auto;
  min-height: 100dvh;
  overflow: auto;
}
.not-found {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: min(30rem, 100%);
  margin: 0 auto;
  padding: 32px 16px max(64px, env(safe-area-inset-bottom));
  color: var(--text-2);
  font-size: 17px;
  line-height: 1.5;
  text-align: center;
}
.not-found p {
  margin: 8px 0 0;
  text-wrap: pretty;
}
.not-found .not-found-code {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.not-found h1 {
  margin: 0;
  color: var(--text);
  font-size: 28px;
  line-height: 1.2;
  text-wrap: balance;
}
.not-found .row {
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 28px;
}

/* ---------- usage charts (usage-chart.js) ---------- */
/* A column a day against hairline gridlines, labelled in a gutter on the left. Hovering, touching or
   arrowing through the days washes the day's column and shows its numbers beside it. */
.uc {
  --uc-height: 160px;
  --uc-gutter: 44px;
  position: relative;
  font-size: 13px;
  line-height: 1.4;
}
.uc-plot {
  position: relative;
  height: var(--uc-height);
  margin-left: var(--uc-gutter);
}
.uc-tick,
.uc-limit {
  position: absolute;
  inset-inline: 0;
  bottom: var(--at);
  border-top: 1px solid var(--line);
  pointer-events: none;
}
.uc-tick span {
  position: absolute;
  top: 0;
  right: calc(100% + 8px);
  transform: translateY(-50%);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The limit is a threshold, not a gridline: dashed, and named where it ends. */
.uc-limit {
  z-index: 1;
  border-top: 1px dashed var(--text-2);
}
.uc-limit span {
  position: absolute;
  right: 0;
  bottom: 3px;
  padding: 0 4px;
  border-radius: 4px;
  background: var(--uc-bg, var(--surface));
  color: var(--text-2);
  font-size: 12px;
}
.uc-bars {
  position: absolute;
  inset: 0;
  display: flex;
  border-radius: 6px;
  touch-action: pan-y;
}
.uc-bars:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 4px;
}
.uc-day {
  display: flex;
  flex: 1;
  justify-content: center;
  min-width: 0;
  border-radius: 6px 6px 0 0;
}
.uc-day[data-active] {
  background: color-mix(in srgb, var(--text) 8%, transparent);
}
/* Thin columns with air between them; the parts of a day are parted by a 2px gap, not a border. */
.uc-stack {
  display: flex;
  flex-direction: column-reverse;
  gap: 2px;
  width: max(1px, min(24px, 100% - 3px));
}
.uc-stack span {
  flex: none;
  height: var(--h);
  min-height: 2px;
  background: var(--part);
}
.uc-stack span:last-child {
  border-radius: 4px 4px 0 0;
}
/* Lines: the first shaded down to the axis, the others references, dashed. The day looked at shows its points. */
.uc-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.uc-area {
  fill: color-mix(in srgb, var(--part) 22%, transparent);
}
.uc-line {
  fill: none;
  stroke: var(--part);
  stroke-width: 2;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.uc-ref {
  stroke-width: 1.5;
  stroke-dasharray: 4 4;
}
.uc-day:has(.uc-dot) {
  position: relative;
}
.uc-dot {
  position: absolute;
  bottom: var(--h);
  left: 50%;
  display: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--part);
  box-shadow: 0 0 0 2px var(--uc-bg, var(--surface));
  transform: translate(-50%, 50%);
}
.uc-day[data-active] .uc-dot {
  display: block;
}
.uc-axis {
  position: relative;
  height: 18px;
  margin: 6px 0 0 var(--uc-gutter);
  color: var(--text-2);
}
.uc-axis span {
  position: absolute;
  left: var(--at);
  transform: translateX(-50%);
  white-space: nowrap;
}
.uc-axis span.last {
  right: 0;
  left: auto;
  transform: none;
}
/* The total leads in the tooltip; the parts and the day's other numbers follow, quieter. */
.uc-tip {
  position: absolute;
  top: 0;
  z-index: 2;
  min-width: 10rem;
  max-width: 16rem;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--surface-3);
  box-shadow: var(--shadow-lg);
  pointer-events: none;
}
.uc-tip p {
  margin: 0;
}
.uc-tip-day {
  color: var(--text-2);
}
.uc-tip-total {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.3;
}
.uc-tip dl {
  display: grid;
  gap: 3px;
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}
.uc-tip dl > div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
}
.uc-tip dt {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-2);
}
.uc-tip dd {
  margin: 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Tooltips key their rows with a short stroke, not a box. */
.uc-key {
  flex: none;
  width: 10px;
  height: 3px;
  border-radius: 2px;
  background: var(--part);
}
[data-part='soniox'],
[data-part='device'] {
  --part: var(--series-1);
}
[data-part='deepgram'] {
  --part: var(--series-2);
}
[data-part='phone'] {
  --part: var(--series-3);
}
[data-part='demo'] {
  --part: var(--series-4);
}
[data-part='active'] {
  --part: var(--series-1);
}
[data-part='none'],
[data-part='all'] {
  --part: var(--text-2);
}
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin: 12px 0 0;
  padding: 0;
  color: var(--text-2);
  font-size: 14px;
  list-style: none;
}
.chart-legend li {
  display: flex;
  align-items: center;
  gap: 8px;
}
.legend-mark {
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--part);
}
.legend-mark.inline {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 6px;
  vertical-align: -1px;
}

/* How much of a daily limit is used: the fill turns to warning near it and danger at it, over a
   track of the same color. The words beside it always say so too. */
.usage-meter {
  --fill: var(--accent);
  height: 8px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--fill) 22%, transparent);
  overflow: hidden;
}
.usage-meter > i {
  display: block;
  width: var(--used);
  height: 100%;
  border-radius: inherit;
  background: var(--fill);
}
.usage-meter[data-level='near'] {
  --fill: var(--warn);
}
.usage-meter[data-level='over'] {
  --fill: var(--danger);
}

/* A wide table scrolls sideways inside its own box on narrow screens. */
.table-scroll {
  overflow-x: auto;
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--ring);
}
.table-scroll:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}
.admin-table th,
.admin-table td {
  padding: 10px 14px;
  text-align: start;
  vertical-align: top;
  white-space: nowrap;
}
.admin-table thead th {
  border-bottom: 1px solid var(--line);
  color: var(--text-2);
  font-size: 13px;
  font-weight: 400;
}
.admin-table tbody tr + tr > * {
  border-top: 1px solid var(--line);
}
.admin-table tbody th {
  min-width: 10rem;
  font-weight: 700;
  white-space: normal;
}
.admin-table .num {
  text-align: end;
  font-variant-numeric: tabular-nums;
}
.admin-table small {
  display: block;
  color: var(--text-2);
  font-size: 13px;
  white-space: nowrap; /* text-wrap on small would undo the cell's nowrap */
}
.admin-table tbody tr.idle > * {
  color: var(--text-2);
}
/* Column headings sort the table: the sorted one says which way. */
.admin-table thead button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 32px;
  margin: -6px -4px;
  padding: 0 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
@media (hover: hover) {
  .admin-table thead button:hover {
    color: var(--text);
  }
}
.admin-table th[aria-sort] button {
  color: var(--text);
  font-weight: 700;
}
.admin-table th[aria-sort='descending'] button::after {
  content: '↓';
}
.admin-table th[aria-sort='ascending'] button::after {
  content: '↑';
}
/* Each account's days in miniature, each on its own scale: the shape of its use, not the amount. */
.spark {
  display: block;
  width: 96px;
  height: 28px;
}
.spark rect {
  fill: var(--text-2);
}
/* Its share of the estimated cost. */
.share {
  display: block;
  width: 64px;
  height: 4px;
  margin: 6px 0 2px auto;
  border-radius: 2px;
  background: color-mix(in srgb, var(--text) 12%, transparent);
}
.share > i {
  display: block;
  width: var(--share);
  min-width: 2px;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
}
.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.pill {
  padding: 1px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--warn) var(--tint), transparent);
  font-size: 13px;
  white-space: nowrap;
}
.pill[data-kind='error'] {
  background: color-mix(in srgb, var(--danger) var(--tint), transparent);
}
.admin-account .usage-meter {
  max-width: 20rem;
  margin-top: 8px;
}

/* The account's cloud captions in the Account pane: today against its limit, then the last 30 days. */
.usage-card {
  overflow: visible; /* the chart's tooltip may reach past it */
}
.card > .usage-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px 16px;
}
.usage-block-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 12px;
  margin: 0;
}
.usage-block-head > span:first-child {
  font-weight: 700;
}
.usage-figure {
  color: var(--text-2);
  font-size: 15px;
}
.usage-figure strong {
  color: var(--text);
  font-size: 22px;
}
.usage-note {
  margin: 0;
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.45;
  text-wrap: pretty;
}
.usage-note[data-level='over'] {
  color: var(--text);
}
.usage-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.25rem, 1fr));
  gap: 12px;
  margin: 0 0 8px;
}
/* A label that wraps on a phone pushes its own figure down, not out of line with the others. */
.usage-stats > div {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.usage-stats dt {
  color: var(--text-2);
  font-size: 13px;
  line-height: 1.35;
}
.usage-stats dd {
  margin: 2px 0 0;
  font-size: 22px;
  font-weight: 700;
  line-height: 1.2;
}
.usage-card .uc {
  --uc-height: 112px;
  --uc-gutter: 40px;
  --uc-bg: var(--surface-2);
}
.usage-card .chart-legend {
  margin-top: 4px;
  font-size: 13px;
}
@media (forced-colors: active) {
  .uc-stack span,
  .uc-key,
  .legend-mark,
  .usage-meter,
  .split-bar span,
  .hb-track,
  .uc-lines,
  .uc-dot {
    forced-color-adjust: none;
  }
}

/* Styled like the app's banner: a tinted card with a rounded bar. */
.mic-notice {
  display: flex;
  gap: 12px;
  margin: 18px 0 0;
  padding: 12px 14px 12px 12px;
  border-radius: 16px;
  background: var(--surface-2);
  background: color-mix(in srgb, var(--warn) var(--tint), var(--bg));
  line-height: 1.45;
}
.mic-notice::before {
  content: '';
  flex: none;
  align-self: stretch;
  width: 4px;
  border-radius: 2px;
  background: var(--warn);
}

/* ---------- forced colors (Windows high contrast) ---------- */
/* Backgrounds and shadows are taken away, so controls drawn with them get real borders, and the
   chosen option is marked with the system's highlight color. */

@media (forced-colors: active) {
  .btn,
  .chip,
  .icon-btn,
  .card,
  .account-summary,
  .history-list:has(.history-item),
  .nav-item,
  .tile,
  .color-field,
  .status,
  .banner,
  .toast,
  button.who,
  .segmented,
  input[type='range']::-webkit-slider-runnable-track {
    border: 1px solid ButtonText;
  }
  .card select {
    border: 1px solid ButtonText;
  }
  .toggle input,
  .radio input {
    border: 2px solid ButtonText;
  }
  .toggle input:checked,
  .segmented input:checked + span,
  .tile:has(input:checked) {
    background: Highlight;
    color: HighlightText;
    forced-color-adjust: none;
  }
  @media (min-width: 720px) {
    .nav-item[aria-selected='true'] {
      background: Highlight;
      color: HighlightText;
      forced-color-adjust: none;
    }
  }
  input[type='range']::-webkit-slider-thumb {
    background: Highlight;
    forced-color-adjust: none;
  }
  .radio input:checked {
    border-color: Highlight;
    box-shadow: inset 0 0 0 3px Canvas, inset 0 0 0 10px Highlight;
  }
  .swatch input:checked + .sw {
    outline: 3px solid Highlight;
    outline-offset: 3px;
  }
  .line::before,
  .dot,
  .meter i,
  .sw,
  .theme-swatch,
  .theme-dot {
    forced-color-adjust: none;
  }
  .empty-mark .bubble {
    fill: Canvas;
    stroke: CanvasText;
  }
}
