@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 TV showing captions and a home agent 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;
}

/* The bar's buttons are the same at every width; on a tablet or computer, where there's room, each
   says what it is beside its icon. */
.btn-name {
  display: none;
}
@media (min-width: 860px) {
  .bar-actions {
    gap: 4px;
  }
  .icon-btn.named {
    grid-auto-flow: column;
    gap: 8px;
    width: auto;
    padding: 0 14px 0 10px;
    font: inherit;
    font-size: 15px;
    font-weight: 700;
  }
  .icon-btn.named svg {
    width: 22px;
    height: 22px;
  }
  .btn-name {
    display: inline;
  }
  .icon-btn.named .new-dot {
    top: 6px;
    right: 4px;
  }
}

/* The account this device is in, for someone in more than one (see showAccountButton in app.js): its
   initial, and its name where there's room. A tap lists their accounts, to switch. */
.account-btn {
  grid-auto-flow: column;
  gap: 8px;
  width: auto;
  padding: 0 8px;
}
.account-mark {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--surface-3);
  color: var(--text);
  font-size: 15px;
  font-weight: 800;
  line-height: 1;
}
.account-btn-name {
  max-width: 16ch;
  overflow: hidden;
  font-size: 15px;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.account-btn .chevron {
  width: 16px;
  height: 16px;
  margin-left: -4px;
  color: var(--text-2);
}
/* On a phone, its initial alone: the bar's buttons and the captioning state need the room. */
@media (max-width: 700px) {
  .account-btn {
    width: 44px;
    padding: 0;
  }
  .account-btn-name,
  .account-btn .chevron {
    display: none;
  }
}
.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);
}

/* A sound that isn't words, as "[applause]": in italics, as TV captions set them. */
.line.sound .said {
  font-style: italic;
}

.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 .edge {
  fill: none;
  stroke: var(--text);
  stroke-width: 2.5;
  stroke-linejoin: round;
}
.empty-mark .bar-1 {
  fill: var(--spk-1);
}
.empty-mark .bar-2 {
  fill: var(--spk-2);
}
.empty-mark .bar-3 {
  fill: var(--spk-3);
}
/* The start screen's caption lines scroll as the captions view does: every 1.4 seconds the three
   glide up a line, the top one fading out under the bubble's edge, and a new one is written in at
   the bottom, six lines taking turns: the Pi's start-up mark's loop (setup_boot in install.sh,
   IDLE_PAGE in helper/pi.js). Each line's turn is the same 6 steps (caption-line), from the lowest
   line's place: it glides in at the start of each of its first 4, is written in from the left in
   its first and fades out in its fourth. Standing still, they're where the icon has them. A line
   is hidden until it starts to be written in (caption-line-fade), not only clipped: as its turn
   starts again it jumps back under the lowest line, and a busy browser drew it there whole for a
   frame before clipping it. */
.empty-mark .line {
  animation:
    caption-line 8.4s infinite,
    caption-line-fade 8.4s infinite;
}
.empty-mark .l0 {
  transform: translateY(-16px);
  animation-delay: -4.2s;
}
.empty-mark .l1 {
  transform: translateY(-8px);
  animation-delay: -2.8s;
}
.empty-mark .l2 {
  animation-delay: -1.4s;
}
.empty-mark .l3 {
  opacity: 0;
}
.empty-mark .l4 {
  opacity: 0;
  animation-delay: -7s;
}
.empty-mark .l5 {
  opacity: 0;
  animation-delay: -5.6s;
}
@keyframes caption-line {
  0% {
    transform: translateY(8px);
    clip-path: inset(0 100% 0 0);
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  5% {
    transform: translateY(0);
    clip-path: inset(0 100% 0 0);
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  11.667% {
    clip-path: inset(0);
  }
  16.667% {
    transform: translateY(0);
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  21.667% {
    transform: translateY(-8px);
  }
  33.333% {
    transform: translateY(-8px);
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  38.333% {
    transform: translateY(-16px);
  }
  50% {
    transform: translateY(-16px);
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  55%,
  100% {
    transform: translateY(-24px);
    clip-path: inset(0);
  }
}
@keyframes caption-line-fade {
  0% {
    opacity: 0;
    animation-timing-function: step-end;
  }
  5%,
  50% {
    opacity: 1;
    animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
  }
  55%,
  100% {
    opacity: 0;
  }
}
/* While listening, the caption lines stand still and breathe; the "Listening…" title carries the
   same message. */
.empty:has(#start-big[hidden]) .empty-mark .line {
  animation: none;
}
.empty:has(#start-big[hidden]) .empty-mark :is(.l0, .l1, .l2) {
  animation: pulse 1.6s ease-in-out infinite;
}
.empty:has(#start-big[hidden]) .empty-mark .l1 {
  animation-delay: 0.3s;
}
.empty:has(#start-big[hidden]) .empty-mark .l2 {
  animation-delay: 0.6s;
}

/* 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='tel'],
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;
}
/* One that opens a page in a new tab: a link, underlined no more than the rest, with an arrow out. */
a.row-btn {
  text-decoration: none;
}
.row-btn > svg.row-out {
  width: 16px;
  height: 16px;
  margin-right: 2px;
  stroke-width: 2;
  color: var(--text-2);
}
.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 > :is(h3, h4) {
  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 > :is(h3, h4) {
  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;
}

/* Someone said a name this device's user answers to (see nameSaid in app.js): the screen flashes three
   times, about once a second (well under the three a second that can trigger seizures), as a TV does
   (see heard.css). Without motion, a steady frame stands in for the flashes. */
.name-flash {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  background: var(--accent);
  opacity: 0;
}
.name-flash.on {
  animation: name-flash 900ms ease-in-out 3;
}
@keyframes name-flash {
  50% {
    opacity: 0.55;
  }
}
@media (prefers-reduced-motion: reduce) {
  .name-flash.on {
    animation: none;
    background: transparent;
    box-shadow: inset 0 0 0 12px var(--accent);
    opacity: 1;
  }
}

/* ---------- 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 sheet (the Menu) keeps its top in place (the title, and on a phone the list of
   sections) and scrolls the settings under it. */
dialog.settings-sheet {
  padding: 0;
  overflow: hidden;
  overflow: clip; /* never scrolled itself, even by a scrollIntoView within it: only its panes are */
}
dialog.settings-sheet[open] {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
}
.settings-sheet .sheet-top {
  position: relative;
  z-index: 3; /* its line stays over what a section pins to its top (Display's preview) */
  min-width: 0;
  box-shadow: 0 1px 0 var(--line);
}
.settings-panes {
  --panes-top: 16px; /* the room over the open section, which what's pinned to its top reaches into */
  position: relative; /* what's placed out of the flow in a section (a label for screen readers) scrolls, and is cut off, with it */
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--panes-top) 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;
  }
}

/* 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;
}

/* ---------- a phone call (see phone-app.js) ---------- */

/* Who's calling, then their words, big, as they talk: the newest at the bottom. */
.phone-call-from {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.phone-call-words {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 7.5em;
  max-height: 50dvh;
  overflow-y: auto;
  margin: 8px 0 0;
  font-size: 22px;
  line-height: 1.35;
  overflow-wrap: break-word;
}
.phone-call-words .who,
.phone-call-words .tr {
  display: none; /* one caller, named at the top; nothing's translated */
}
.phone-call-note:empty {
  display: none;
}
/* A message left, in Menu → Phone line: all of what they said, under who and when. */
.phone-message-text {
  margin: 6px 0 0;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
/* A text, in Menu → Phone line: who it's from (or to, and how it's going), what it says, and Reply. */
.phone-text {
  flex-wrap: wrap;
  align-items: flex-start;
}
/* What it says goes the whole width; Reply and Delete go under it. */
.phone-text .device-label {
  flex-basis: 100%;
}
.phone-text.outgoing .device-label {
  padding-left: 12px;
  border-left: 3px solid var(--line);
}
.phone-text-buttons {
  display: flex;
  gap: 8px;
  margin-left: auto;
}
.phone-text-problem {
  color: var(--danger);
}
/* Writing a reply, under the text it answers. */
.phone-reply {
  flex-basis: 100%;
}
.phone-reply textarea {
  width: 100%;
  font: inherit;
  resize: vertical;
}
.phone-reply-count {
  display: block;
  text-align: right;
}

/* ---------- Menu → Help (feedback.js), and the /changes page ---------- */

/* Something new in Help: a dot on the menu's button, where the devices badge sits on its own, and one
   on Help's icon, which the menu's list keeps where it has no room for Help's "New: …". */
.new-dot,
.nav-item.has-new::before {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg);
}
.new-dot {
  top: 8px;
  right: 8px;
}
.nav-item.has-new {
  position: relative;
}
.nav-item.has-new::before {
  content: '';
  top: calc(50% - 15px);
  left: 32px;
}
.nav-item.has-new small {
  color: var(--accent);
}

/* A change: its day, small, over its title, then what it says. */
.change-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.change-day {
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
}
.change-title {
  margin: 0;
  font-size: 16px;
  line-height: 1.3;
  text-wrap: balance;
}
.change-item p {
  margin: 0;
  line-height: 1.5;
  text-wrap: pretty;
}
.help-changes {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* What they sent: how it stands, their words, and the answer in a tinted box under them. */
.help-sent-head {
  margin: 0;
  color: var(--text-2);
  font-size: 14px;
}
.help-state {
  color: var(--text);
  font-weight: 700;
}
.help-state-open {
  color: var(--accent);
}
.help-state-done {
  color: var(--ok);
}
.help-sent-text {
  margin: 6px 0 0;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.help-reply {
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) var(--tint), var(--surface-2));
}
.help-reply strong {
  font-size: 14px;
}
.help-reply p {
  margin: 4px 0 0;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

/* /guest: a guest on a call (see guest.html), in a column as wide as a phone's, or a little more. */
body.guest-page {
  display: flex;
  flex-direction: column;
  height: auto;
  min-height: 100dvh;
  overflow: auto;
}
.guest {
  width: min(44rem, 100%);
  margin: 0 auto;
  padding: 24px 16px max(48px, env(safe-area-inset-bottom));
  box-sizing: border-box;
}
.guest h1 {
  margin: 0;
  font-size: 28px;
  line-height: 1.2;
}
.guest-msg {
  margin: 8px 0 16px;
  color: var(--text-2);
}
.guest-msg:empty {
  display: none;
}

/* /changes: every change, as a column to read down, each a link's place. */
body.changes-page {
  display: flex;
  flex-direction: column;
  height: auto;
  min-height: 100dvh;
  overflow: auto;
}
.changes {
  width: min(40rem, 100%);
  margin: 0 auto;
  padding: 32px 16px max(64px, env(safe-area-inset-bottom));
  font-size: 17px;
  line-height: 1.5;
}
.changes h1 {
  margin: 0;
  font-size: 32px;
  line-height: 1.2;
}
.changes-intro {
  margin: 8px 0 0;
  color: var(--text-2);
}
.changes-msg {
  margin: 24px 0 0;
}
.changes-msg:empty {
  display: none;
}
#changes-list {
  margin-top: 24px;
}
.changes .change-item {
  gap: 6px;
  padding: 24px 0;
  border-top: 1px solid var(--line);
  scroll-margin-top: 16px;
}
.changes .change-title {
  font-size: 21px;
}

/* ---------- switching account (showAccountChoices in app.js) ---------- */

/* Their accounts, a row each to tap; the one this device is in is marked. */
@media (min-width: 641px) {
  dialog.switch-sheet {
    width: min(440px, calc(100% - 32px));
  }
}
.account-choices {
  margin: 0 -10px;
  padding: 0;
  list-style: none;
}
.account-choice {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 60px;
  padding: 8px 10px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  transition-property: background-color, scale;
  transition-duration: 150ms;
  transition-timing-function: ease-out;
}
@media (hover: hover) {
  .account-choice:hover {
    background: var(--surface-2);
  }
}
.account-choice:active {
  scale: 0.98;
}
.account-choice[aria-current='true'] {
  background: var(--surface-2);
}
.account-choice .account-mark {
  width: 40px;
  height: 40px;
  border-radius: 12px;
  font-size: 18px;
}
.account-choice-label {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  overflow-wrap: anywhere;
}
.account-choice-label strong {
  font-size: 16px;
}
.account-choice-label small {
  color: var(--text-2);
  font-size: 14px;
}
.account-choice > svg {
  flex: none;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.switch-sheet .group-note {
  margin-top: 12px;
}

/* ---------- 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 ---------- */

/* Everything in the app, in one list: its sections in groups, with a box over them that finds one, or
   a setting in one, by name (see menu-search.js). On a phone the list is the whole menu, each group a
   card of rows that say what they hold, and a section 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. The search stays
   over the list and the account, Help and the admin page under it, while the groups scroll between. */
.settings-nav {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  padding: 4px var(--sheet-pad) 0;
}
/* A little room at its sides, so the cards' edges aren't cut off where it scrolls. */
.nav-scroll {
  position: relative;
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  margin: 0 -2px;
  padding: 2px 2px 20px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.nav-scroll > * {
  flex: none; /* as tall as what's in it, however much that is: the list scrolls */
}
.menu-search {
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  gap: 8px;
  min-height: 46px;
  padding: 0 12px 0 14px;
  border-radius: 14px;
  background: var(--surface-2);
  box-shadow: var(--ring);
}
.menu-search:focus-within {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.menu-search > svg {
  flex: none;
  width: 18px;
  height: 18px;
  fill: none;
  stroke: var(--text-2);
  stroke-width: 2.2;
  stroke-linecap: round;
}
.menu-search input {
  flex: 1;
  min-width: 0;
  align-self: stretch;
  padding: 0;
  border: 0;
  appearance: none;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 16px; /* smaller, and an iPhone zooms in on it */
  outline: 0;
}
.menu-search input::placeholder {
  color: var(--text-2);
  opacity: 1;
}
.menu-search input::-webkit-search-decoration {
  appearance: none;
}
/* The keys that bring the search up from anywhere, until something's typed; not where there are no keys. */
.menu-search kbd {
  flex: none;
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--surface-3);
  color: var(--text-2);
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.4;
}
.menu-search:has(input:not(:placeholder-shown)) kbd {
  display: none;
}
@media (hover: none) {
  .menu-search kbd {
    display: none;
  }
}

.nav-groups {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
/* A group with nothing to list (no home agent yet, say) isn't there. */
.nav-group:not(:has(.nav-item:not([hidden]))) {
  display: none;
}
.nav-group > 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;
}
.nav-list,
.nav-results {
  display: flex;
  flex-direction: column;
}
.nav-item,
.nav-result {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 60px;
  padding: 9px 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;
}
a.nav-item {
  text-decoration: none;
}
:is(.nav-item, .nav-result):focus-visible {
  outline-offset: -3px;
}
:is(.nav-item, .nav-result) 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: 1px;
  min-width: 0;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
}
.nav-text small {
  color: var(--text-2);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.35;
}
/* A chevron, as on any row that opens something; the admin page, which opens in a tab of its own, has an arrow. */
.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;
}
.nav-out::after,
.nav-act::after {
  display: none; /* nor on a row that does something there and then, such as Full screen */
}
.nav-item svg.nav-out-arrow {
  width: 16px;
  height: 16px;
  margin-right: 2px;
  stroke: var(--text-2);
  stroke-width: 2;
}
@media (hover: hover) {
  :is(.nav-item, .nav-result):hover {
    background: var(--surface-3);
  }
}

/* What the search found, in the list's place: each with where it is. One without an icon is a setting
   inside a section, and lines up with the sections' names. */
.nav-results {
  border-radius: 16px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  overflow: hidden;
}
.nav-result {
  min-height: 56px;
}
.nav-result:not(:has(svg)) {
  padding-left: 52px;
}
.nav-result ~ .nav-result {
  border-top: 1px solid var(--line);
}
.nav-none {
  margin: 4px 16px;
  color: var(--text-2);
  font-size: 15px;
  line-height: 1.45;
}

/* What the search went to, marked for a moment so the eye lands on it. */
.found {
  animation: found 2.4s ease-out;
}
@keyframes found {
  0%,
  55% {
    background-color: color-mix(in srgb, var(--accent) 18%, transparent);
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }
  100% {
    background-color: transparent;
    outline: 2px solid transparent;
    outline-offset: -2px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .found {
    animation: none;
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }
}

.pane {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 660px;
}
/* A section with more to lay out than a column of cards (Display's two) takes the room there is. */
.pane.wide {
  max-width: none;
}
/* The sheet's title names the section that's open, so each section's own is only for screen readers. */
.pane-title {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.pane-note {
  margin: 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. */
@media (max-width: 719px) {
  .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;
  }
  dialog.menu-sheet[data-view='list'][open] {
    grid-template-rows: minmax(0, 1fr);
  }
  .menu-sheet[data-view='list'] .sheet-top {
    display: flex;
    flex-direction: column;
    min-height: 0;
    box-shadow: none;
  }
  #menu-back {
    margin-left: -10px;
  }
  /* The list fills the sheet, so the row along its bottom stays there when little is listed (a search's finds). */
  .menu-sheet .settings-nav {
    flex: 1;
  }
  /* Each group is a card of rows. */
  .menu-sheet .nav-list {
    border-radius: 16px;
    background: var(--surface-2);
    box-shadow: var(--ring);
    overflow: hidden;
  }
  .menu-sheet .nav-item:not([hidden]) ~ .nav-item:not([hidden]) {
    border-top: 1px solid var(--line);
  }
  /* The account, Help and the admin page: a row along the bottom, each its icon over its name, in
     reach of a thumb however far the list is scrolled. */
  .menu-sheet .nav-foot {
    flex: none;
    margin: 0 calc(var(--sheet-pad) * -1);
    padding: 6px var(--sheet-pad) max(8px, env(safe-area-inset-bottom));
    border-top: 1px solid var(--line);
  }
  .menu-sheet .nav-foot .nav-list {
    flex-direction: row;
    gap: 4px;
    border-radius: 0;
    background: none;
    box-shadow: none;
    overflow: visible;
  }
  .menu-sheet .nav-foot .nav-item {
    flex: 1;
    flex-direction: column;
    justify-content: center;
    gap: 3px;
    min-width: 0;
    min-height: 54px;
    padding: 6px 4px;
    border-radius: 12px;
  }
  .menu-sheet .nav-foot .nav-item:not([hidden]) ~ .nav-item:not([hidden]) {
    border-top: 0;
  }
  .menu-sheet .nav-foot .nav-text {
    flex: none;
    align-items: center;
    font-size: 13px;
  }
  .menu-sheet .nav-foot :is(.nav-text small, .nav-out-arrow),
  .menu-sheet .nav-foot .nav-item::after {
    display: none;
  }
  .menu-sheet .nav-foot .nav-item.has-new::before {
    top: 5px;
    left: calc(50% + 3px);
    box-shadow: 0 0 0 2px var(--surface);
  }
}

/* Beside the section: a quieter list, a line each, the open section's filled in. The sheet is as big
   as a settings window, and stays that size from one section to the next. */
@media (min-width: 720px) {
  dialog.menu-sheet {
    width: min(1120px, calc(100% - 48px));
    height: min(900px, calc(100dvh - 32px));
  }
  dialog.menu-sheet[open] {
    grid-template-columns: 272px minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  .menu-sheet .sheet-top {
    display: contents;
  }
  .menu-sheet .sheet-head {
    grid-column: 2;
    grid-row: 1;
    padding: 18px 18px 8px var(--sheet-pad);
  }
  #menu-back,
  .menu-root-title {
    display: none;
  }
  .menu-sheet .settings-nav {
    grid-column: 1;
    grid-row: 1 / -1;
    gap: 0;
    padding: 14px 10px 10px;
    border-right: 1px solid var(--line);
    background: color-mix(in srgb, var(--bg) 60%, var(--surface));
  }
  .menu-sheet .nav-scroll {
    gap: 0;
    margin: 0;
    padding: 0;
  }
  .menu-sheet .menu-search {
    min-height: 38px;
    margin-bottom: 8px;
    padding: 0 8px 0 12px;
    border-radius: 12px;
    background: var(--surface);
  }
  .menu-sheet .menu-search input {
    font-size: 15px;
  }
  .menu-sheet .nav-groups {
    gap: 0;
  }
  .menu-sheet .nav-group > h3 {
    margin: 10px 12px 4px;
    font-size: 12px;
  }
  .menu-sheet .nav-group:first-child > h3 {
    margin-top: 2px;
  }
  /* The account, Help and the admin page, under the list: a line sets them apart. */
  .menu-sheet .nav-foot {
    flex: none;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--line);
  }
  .menu-sheet .nav-list {
    gap: 1px;
  }
  /* A line each, close enough together that the whole list fits a laptop's window without scrolling. */
  .menu-sheet :is(.nav-item, .nav-result) {
    gap: 10px;
    min-height: 34px;
    padding: 0 10px;
    border-radius: 9px;
  }
  @media (pointer: coarse) {
    .menu-sheet :is(.nav-item, .nav-result) {
      min-height: 44px; /* a finger's width, on a tablet */
    }
  }
  .menu-sheet :is(.nav-item, .nav-result) svg {
    width: 20px;
    height: 20px;
    stroke: var(--text-2);
    stroke-width: 2;
  }
  .menu-sheet .nav-item .nav-text {
    font-size: 15px;
    font-weight: 400;
  }
  .menu-sheet .nav-item .nav-text small {
    display: none;
  }
  /* Only what opens a sheet of its own keeps its chevron: it leaves the menu. */
  .menu-sheet .nav-item::after {
    display: none;
  }
  .menu-sheet .nav-item[data-opens]::after {
    display: block;
    width: 6px;
    height: 6px;
    border-width: 1.5px;
    opacity: 0.7;
  }
  @media (hover: hover) {
    .menu-sheet :is(.nav-item, .nav-result):hover {
      background: var(--surface-2);
    }
  }
  .menu-sheet :is(.nav-item[aria-current='page'], .nav-result[aria-current='true']) {
    background: color-mix(in srgb, var(--accent) var(--tint), transparent);
  }
  .menu-sheet .nav-item[aria-current='page'] .nav-text {
    font-weight: 700;
  }
  .menu-sheet :is(.nav-item[aria-current='page'], .nav-result[aria-current='true']) svg:not(.nav-out-arrow) {
    stroke: var(--accent);
  }
  .menu-sheet .nav-item.has-new::before {
    top: calc(50% - 12px);
    left: 24px;
    width: 8px;
    height: 8px;
    box-shadow: 0 0 0 2px var(--surface);
  }
  /* What's found, in the narrow list: a line each, with where it is under it. */
  .menu-sheet .nav-results {
    gap: 2px;
    border-radius: 0;
    background: none;
    box-shadow: none;
    overflow: visible;
  }
  .menu-sheet .nav-result {
    min-height: 46px;
    padding-block: 5px;
  }
  .menu-sheet .nav-result:not(:has(svg)) {
    padding-left: 40px;
  }
  .menu-sheet .nav-result ~ .nav-result {
    border-top: 0;
  }
  .menu-sheet .nav-result .nav-text {
    font-size: 15px;
  }
  .menu-sheet .nav-result .nav-text small {
    font-size: 13px;
  }
  .menu-sheet .nav-none {
    margin: 4px 12px;
    font-size: 14px;
  }
  .menu-sheet .settings-panes {
    --panes-top: 10px;
  }
}

/* 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. */
/* Before anything is connected, adding one is the whole section, so it needs no heading of its own. */
#pane-devices:has(#remote-section[hidden]):has(#phones-section[hidden]) #add-section > h4 {
  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;
}
.pair-scan {
  width: 100%;
  margin-top: 10px;
}

/* 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;
}

/* Recording a meeting (see meetings.js): a red pill with how long so far, in the top bar; a tap stops it. */
.rec-pill {
  --state: var(--danger);
  display: flex;
  flex: none;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 6px 14px 6px 12px;
  border: 0;
  border-radius: 18px;
  background: var(--surface-2);
  background: color-mix(in srgb, var(--state) var(--tint), transparent);
  color: var(--text);
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.rec-pill[hidden] {
  display: none;
}
.rec-pill .dot {
  animation: pulse 1.1s ease-in-out infinite;
}

/* A meeting's transcript: its text as Soniox or Sofer.ai wrote it, one speaker's turn to a paragraph. */
.meeting-text {
  margin-top: 16px;
  font-size: 17px;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* A meeting's or call's notes, as whoever kept it typed them: its recap is built around them. */
.meeting-notes {
  display: grid;
  gap: 6px;
  margin-top: 16px;
}
.meeting-notes label {
  font-weight: 600;
}
.meeting-notes textarea {
  min-height: 96px;
  line-height: 1.45;
}
.meeting-notes .hint:empty {
  display: none;
}

/* A recap (see recap.js): its title and summary, then each list under a small heading, as a group's. */
.recap {
  margin-top: 16px;
  font-size: 17px;
  line-height: 1.55;
  overflow-wrap: anywhere;
}
.recap h3 {
  margin: 0 0 4px;
  font-size: 22px;
  line-height: 1.3;
  text-wrap: balance;
}
.recap p {
  margin: 0;
  text-wrap: pretty;
}
.recap h4 {
  margin: 22px 0 6px;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.3;
  text-transform: uppercase;
}
.recap ul {
  margin: 0;
  padding-inline-start: 1.2em;
}
.recap li + li {
  margin-top: 6px;
}
.recap .recap-when {
  color: var(--text-2);
}
.recap .recap-note {
  margin-top: 20px;
}
/* Writing one, or another: under the recap, set apart from it. */
.recap-ask {
  margin-top: 8px;
}
.recap:not([hidden]) + .recap-ask {
  margin-top: 24px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}
/* Questions about it (see recap.js): each with its answer and the lines it rests on, then the box. */
.recap-questions {
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.recap-questions h3 {
  margin: 0 0 8px;
  font-size: 19px;
  line-height: 1.3;
}
.recap-answers:empty {
  display: none;
}
.recap-qa {
  margin: 0 0 18px;
  font-size: 17px;
  line-height: 1.55;
  overflow-wrap: anywhere;
}
.recap-qa .q {
  margin: 0 0 4px;
  font-weight: 700;
}
.recap-qa .a {
  margin: 0;
  white-space: pre-line;
  text-wrap: pretty;
}
.recap-qa .a.waiting {
  color: var(--text-2);
}
.recap-qa blockquote {
  margin: 8px 0 0;
  padding: 2px 0 2px 12px;
  border-inline-start: 3px solid var(--line);
}
.recap-qa blockquote p {
  margin: 0;
}
.recap-qa blockquote footer {
  color: var(--text-2);
  font-size: 15px;
}
.recap-question {
  display: flex;
  gap: 8px;
}
.recap-question input {
  flex: 1;
  min-width: 0;
}

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

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

/* The preview stays in view at the top of the section while the controls scroll under it, so every
   change shows at once. */
.display-top {
  position: sticky;
  top: calc(var(--panes-top) * -1);
  z-index: 2;
  margin: calc(var(--panes-top) * -1) -4px -8px;
  padding: var(--panes-top) 4px 14px;
  background: var(--surface);
}
/* On short screens the preview scrolls away with the rest, leaving room for the controls. */
@media (max-height: 640px) {
  .display-top {
    position: static;
  }
}

.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: 1040px) {
  .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 open Menu section is outlined (beside it; a phone's list has none open). */
@media (min-width: 720px) {
  [data-theme='contrast'] .menu-sheet :is(.nav-item[aria-current='page'], .nav-result[aria-current='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 → Share with an audience: the event's QR code beside its link and how many read it; on a phone,
   the code above them. Shown big, it fills the screen for a room to scan. */
.event-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  padding: 16px;
}
.event-qr {
  width: 200px;
}
.event-share-text {
  display: flex;
  flex: 1 1 14rem;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
}
.event-share-text .link-row {
  align-self: stretch;
  margin: 0;
}
.event-count {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.event-qr-big[open] {
  gap: 2.5vmin;
  padding-bottom: max(56px, calc(env(safe-area-inset-bottom) + 40px)); /* clear of "Tap anywhere to close" */
}
.event-qr-big .qr {
  width: min(62vmin, calc(100vw - 48px), 600px);
  padding: 2vmin;
}
.event-qr-big .event-qr-big-name {
  max-height: none;
  font-size: clamp(22px, 5vmin, 56px);
}
.event-qr-big .event-qr-big-name:empty {
  display: none;
}
.event-qr-big-say {
  font-size: clamp(18px, 3.2vmin, 34px);
  font-weight: 700;
}
/* 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;
}
/* What happened, not what went wrong: a link sent, say. */
.form-msg[data-tone='note'] {
  color: var(--text-2);
}
/* 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);
}
/* Menu → Sounds and flashing: 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;
}

/* Full screen (see fullscreen.js), in the bar and in the menu: its arrows point out, and back in once
   the app fills the screen. The bar's button isn't on a phone, whose bar has no room for it. */
.fullscreen-toggle .shrink,
.fullscreen-toggle.on .grow {
  display: none;
}
.fullscreen-toggle.on .shrink {
  display: inline;
}
@media (max-width: 540px) {
  #fullscreen-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);
}
/* Your screen, shared, the way round it is. */
.remote-view .remote-call-self.screen {
  transform: none;
}
/* Adding someone to a call: a guest's link, to copy or share, once it's made. */
.call-invite-link {
  display: grid;
  gap: 8px;
  margin-top: 12px;
}
.call-invite-link input {
  width: 100%;
  box-sizing: border-box;
  font-size: 14px;
}
.call-add-sheet .device-item {
  align-items: center;
}

/* On a call, the others on it, three to a row under the room, each with their name; their name
   alone with their camera off. */
.remote-call-peers {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  margin-top: 6px;
}
.remote-call-peers[hidden] {
  display: none;
}
.remote-call-peer {
  position: relative;
  margin: 0;
}
.remote-view .remote-call-peer video {
  object-fit: cover;
  border-radius: 8px;
}
.remote-call-peer.off video {
  visibility: hidden; /* it still plays, for their voice */
}
.remote-view .remote-call-peer.screen video {
  object-fit: contain; /* a screen they share, all of it */
}
.remote-view .remote-call-peer figcaption {
  position: absolute;
  left: 4px;
  bottom: 4px;
  max-width: calc(100% - 8px);
  margin: 0;
  padding: 1px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.remote-view .remote-call-peer.off figcaption {
  inset: 0;
  max-width: none;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: #1b1f27;
  font-size: 14px;
}
.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;
  flex-wrap: wrap; /* on a call: hang up, mute, camera, add someone */
  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 . . follow follow' '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; }
/* Follow: lit while the camera follows whoever's there, which any other aim stops. */
.remote-aim [data-move='follow'] {
  grid-area: follow;
  width: auto;
  padding: 0 6px;
}
.remote-aim [data-move='follow'][aria-pressed='true'] {
  background: #fff;
  color: #000;
}
.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 .remote-leave,
.remote-devices .remote-xfinity,
.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 → Sounds and flashing: 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, .remote-channel-tune, .remote-channel-star) {
  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, .remote-channel-tune, .remote-channel-star):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, .remote-channel-tune, .remote-channel-star):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-devices .remote-xfinity {
  display: grid;
  gap: 6px;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
}
.remote-xfinity .hint {
  margin: 0;
}
.remote-xfinity-row,
.remote-xfinity-token,
.remote-listings {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.remote-listings > span,
.remote-listings > .hint {
  flex: 1 0 100%;
}
.remote-xfinity-token input,
.remote-listings input {
  flex: 1 1 11em;
  min-width: 0;
  min-height: 36px;
  padding: 6px 10px;
  border: 1px solid var(--field-border);
  border-radius: 10px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 15px;
}
.remote-listings.signed-in input,
.remote-listings.signed-in [type='submit'] {
  display: none;
}
.remote-channels {
  display: grid;
  gap: 6px;
}
.remote-channel {
  display: flex;
  gap: 6px;
  align-items: stretch;
}
.remote-pad .remote-channel-tune {
  flex: 1;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  align-items: baseline;
  justify-items: start;
  min-width: 0;
  padding: 10px 12px;
  border-radius: 14px;
  text-align: left;
}
.remote-channel-number {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.remote-channel-name {
  font-weight: 600;
  overflow-wrap: anywhere;
}
/* A station's logo, before its name in the list (see channelRow in app.js). */
.remote-channel-logo {
  height: 20px;
  max-width: 64px;
  margin-right: 8px;
  object-fit: contain;
  object-position: left center;
  vertical-align: -4px;
}
.remote-channel-on,
.remote-channel-next {
  grid-column: 2;
  max-width: 100%;
  overflow: hidden;
  color: var(--text-2);
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.remote-pad .remote-channel-star {
  flex: none;
  width: 44px;
  border-radius: 14px;
  font-size: 20px;
}
.remote-pad .remote-channel-star[aria-pressed='true'] {
  color: var(--accent);
}

/* The Channels tab as a guide: the channels down a column pinned on the left, the time across a row
   pinned on top in half-hour columns, and each show a cell as long as it lasts, tinted for news,
   movies and sports. It scrolls both ways within itself; on a phone it runs to the sheet's edges. */
/* In the remote, a button to the guide over the list: the guide's own page, as a phone's guide is. */
.remote-pad .remote-guide-open {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 50px;
  padding: 0 18px;
  border-radius: 14px;
  background: var(--surface-2);
  box-shadow: var(--key-edge);
  font-size: 16px;
  font-weight: 700;
}
.remote-pad .remote-guide-open svg {
  width: 22px;
  height: 22px;
}
@media (hover: hover) {
  .remote-pad .remote-guide-open:hover {
    background: var(--surface-3);
  }
}
.remote-pad .remote-guide-open:active {
  box-shadow: var(--key-sunk);
}
/* The find box, and in the guide Jump to beside it, where there's room. */
.remote-channels-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
}
.remote-channels-tools .remote-find {
  flex: 1 1 260px;
  width: auto;
  min-width: 0;
}
.remote-guide-jump {
  display: flex;
  flex: 1 1 300px;
  align-items: center;
  gap: 8px;
  color: var(--text-2);
  font-size: 14px;
  font-weight: 600;
}
.remote-guide-jump > span {
  flex: none;
}
.remote-guide-jump select {
  flex: 1 1 0;
  min-width: 0;
  max-width: 220px;
  min-height: 40px;
  padding: 6px 34px 6px 12px;
  border: 0;
  border-radius: 11px;
  background-color: var(--surface-2);
  background-position: right 10px center;
  box-shadow: var(--ring);
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  text-overflow: ellipsis;
  cursor: pointer;
}
.remote-guide {
  --chan: 88px; /* the channels' column */
  --slot: 124px; /* half an hour */
  --row: 60px;
  --head: 34px;
  position: relative;
  overflow: auto;
  overscroll-behavior: contain;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--ring);
  font-size: 14px;
}
@media (max-width: 640px) {
  .remote-guide {
    margin-inline: calc(-1 * var(--sheet-pad));
    border-radius: 0;
    box-shadow: 0 -1px 0 var(--line), 0 1px 0 var(--line);
  }
}
@media (min-width: 641px) {
  .remote-guide {
    --chan: 96px;
    --slot: 140px;
  }
}
.guide-sheet {
  position: relative;
  width: calc(var(--chan) + var(--slots) * var(--slot));
}
.guide-head {
  position: sticky;
  top: 0;
  z-index: 4;
  display: flex;
  height: var(--head);
  background: var(--surface);
  box-shadow: 0 1px 0 var(--line);
}
.guide-corner {
  position: sticky;
  left: 0;
  z-index: 1;
  display: flex;
  flex: none;
  width: var(--chan);
  background: var(--surface);
  box-shadow: 1px 0 0 var(--line);
}
.remote-guide .guide-move {
  flex: 1;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 22px;
  line-height: 1;
}
.remote-guide .guide-move:disabled {
  color: var(--text-2);
  opacity: 0.4;
  cursor: default;
}
@media (hover: hover) {
  .remote-guide .guide-move:not(:disabled):hover {
    background: var(--surface-2);
  }
}
.guide-times {
  position: relative;
  flex: none;
  width: calc(var(--slots) * var(--slot));
}
.guide-time-label {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--at) * var(--slot));
  display: flex;
  align-items: center;
  padding-left: 10px;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.guide-time-label:not(:first-child) {
  box-shadow: -1px 0 0 var(--line);
}
.guide-row {
  display: flex;
  height: var(--row);
  box-shadow: 0 1px 0 var(--line);
}
.guide-channel {
  position: sticky;
  left: 0;
  z-index: 3;
  flex: none;
  width: var(--chan);
  background: var(--surface-2);
  box-shadow: 1px 0 0 var(--line);
}
.remote-guide .guide-tune {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  gap: 1px;
  padding: 0 8px 0 10px;
  border-radius: 0;
  text-align: left;
}
.guide-number {
  padding-right: 22px; /* clear of the star */
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.guide-name {
  overflow: hidden;
  color: var(--text-2);
  font-size: 12px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.remote-guide .guide-star {
  position: absolute;
  top: 3px;
  right: 1px;
  width: 30px;
  height: 30px;
  color: var(--text-2);
  font-size: 17px;
}
.remote-guide .guide-star[aria-pressed='true'] {
  color: var(--accent);
}
/* With its logo, a channel is the logo over its number, the star beside the number; the call sign
   is the logo. */
.remote-guide .guide-channel.with-logo .guide-tune {
  justify-content: flex-start;
  gap: 5px;
  padding: 8px 8px 0 10px;
}
.guide-logo {
  flex: none;
  width: 100%;
  height: 26px;
  object-fit: contain;
  object-position: left center;
}
.remote-guide .guide-channel.with-logo .guide-number {
  padding-right: 0;
}
.remote-guide .guide-channel.with-logo .guide-name {
  display: none;
}
.remote-guide .guide-channel.with-logo .guide-star {
  top: auto;
  bottom: 1px;
}
.guide-track {
  position: relative;
  flex: none;
  width: calc(var(--slots) * var(--slot));
}
.remote-guide .guide-show,
.guide-wait,
.guide-none {
  position: absolute;
  top: 0;
  bottom: 0;
}
.guide-wait,
.guide-none {
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  padding-left: 12px;
  color: var(--text-2);
}
.remote-guide .guide-show {
  --show: var(--surface);
  left: calc(var(--at) * var(--slot));
  width: calc(var(--span) * var(--slot));
  display: flex;
  justify-content: flex-start;
  align-items: center;
  min-width: 0;
  padding: 0 8px 0 10px;
  border-radius: 0;
  border: 0;
  background: var(--show);
  box-shadow: inset -1px 0 0 var(--line);
  color: var(--text);
  font: inherit;
  text-align: left;
  transition: background-color 150ms ease-out;
}
.remote-guide .guide-show[data-kind='news'] {
  --show: color-mix(in srgb, var(--warn) var(--tint), var(--surface));
}
.remote-guide .guide-show[data-kind='movie'] {
  --show: color-mix(in srgb, var(--danger) var(--tint), var(--surface));
}
.remote-guide .guide-show[data-kind='sports'] {
  --show: color-mix(in srgb, var(--ok) var(--tint), var(--surface));
}
@media (hover: hover) {
  .remote-guide .guide-show:hover,
  .remote-guide .guide-tune:hover {
    background: color-mix(in srgb, var(--text) 6%, var(--show, var(--surface-2)));
  }
}
.remote-guide .guide-show:active,
.remote-guide .guide-tune:active {
  background: color-mix(in srgb, var(--text) 12%, var(--show, var(--surface-2)));
}
.remote-guide .guide-show:focus-visible,
.remote-guide .guide-tune:focus-visible,
.remote-guide .guide-star:focus-visible,
.remote-guide .guide-move:focus-visible {
  outline-offset: -3px;
}
/* What's over is quieter; a show that began earlier keeps its title in view as it scrolls; a sliver
   of one, too thin to read, is only its color. */
.remote-guide .guide-show.past .guide-show-text {
  opacity: 0.55;
}
.remote-guide .guide-show.thin {
  padding: 0;
}
.remote-guide .guide-show.thin .guide-show-text {
  display: none;
}
.remote-guide .guide-show-text {
  position: sticky;
  left: calc(var(--chan) + 10px);
  z-index: 2; /* over the line at now, under the channels */
  display: grid;
  min-width: 0;
  max-width: 100%;
}
.guide-title {
  overflow: hidden;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.guide-when {
  overflow: hidden;
  color: var(--text-2);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A show's flags, after its times: live and new in color, a premiere or finale in the accent, and
   how it can be followed (captions, sign language) quieter. The list's lines have them too. */
.guide-tag {
  color: var(--text-2);
  font-size: 0.92em;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.guide-tag[data-tag='new'] {
  color: var(--ok);
}
.guide-tag[data-tag='live'] {
  color: var(--danger);
}
.guide-tag[data-tag='special'] {
  color: var(--accent);
}

/* A show's details, over the remote: what it is and when, its flags as chips, what it's about, the
   rest in a quieter line, and Watch. */
.guide-airing-sheet p {
  margin: 0 0 10px;
}
.guide-airing-episode {
  font-size: 17px;
  font-weight: 600;
}
.guide-airing-when {
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.guide-airing-marks {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 14px;
  padding: 0;
  list-style: none;
}
.guide-airing-marks li {
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-size: 14px;
  font-weight: 600;
}
.guide-airing-marks li[data-tag='live'] {
  background: color-mix(in srgb, var(--danger) var(--tint), var(--surface));
}
.guide-airing-marks li[data-tag='new'] {
  background: color-mix(in srgb, var(--ok) var(--tint), var(--surface));
}
.guide-airing-marks li[data-tag='special'] {
  background: color-mix(in srgb, var(--accent) var(--tint), var(--surface));
}
.guide-airing-about {
  font-size: 17px;
  line-height: 1.45;
}
.guide-airing-about.waiting {
  color: var(--text-2);
}
.guide-airing-actions {
  display: flex;
  margin-top: 18px;
}
.guide-airing-actions .btn {
  flex: 1;
}
/* The line at now, down the shows (over their colors, under their titles); a tick on the times above it. */
.guide-now,
.guide-now-tick {
  position: absolute;
  left: calc(var(--now) * var(--slot));
  width: 2px;
  margin-left: -1px;
  background: var(--accent);
  pointer-events: none;
}
.guide-now {
  top: var(--head);
  bottom: 0;
  left: calc(var(--chan) + var(--now) * var(--slot));
  z-index: 1;
}
.guide-now-tick {
  bottom: 0;
  height: 8px;
  border-radius: 2px 2px 0 0;
}
.remote-guide-key {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 0;
  color: var(--text-2);
  font-size: 13px;
}
.remote-guide-key span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.remote-guide-key span::before {
  content: '';
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--show);
  box-shadow: inset 0 0 0 1px var(--line);
}
.remote-guide-key [data-kind='news'] {
  --show: color-mix(in srgb, var(--warn) var(--tint), var(--surface));
}
.remote-guide-key [data-kind='movie'] {
  --show: color-mix(in srgb, var(--danger) var(--tint), var(--surface));
}
.remote-guide-key [data-kind='sports'] {
  --show: color-mix(in srgb, var(--ok) var(--tint), var(--surface));
}
/* The guide, over the whole window, as the remote's bigger picture is: its title and the way back on
   top, the find box and Jump to under them, the grid taking all the height that leaves, and a line
   under it, past a phone's notch and rounded corners. It shows as many hours as fit (see fit in guide.js). */
.remote-sheet .remote-pad .remote-panel.full {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto auto minmax(0, 1fr) auto;
  gap: 0 16px;
  min-height: 0;
  margin: 0;
  padding: max(12px, env(safe-area-inset-top)) max(var(--sheet-pad), env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom)) max(var(--sheet-pad), env(safe-area-inset-left));
  overflow: hidden;
  contain: none;
  background: var(--surface);
}
.remote-sheet .remote-pad .remote-panel.full > * {
  grid-column: 1 / -1;
  min-width: 0;
}
.remote-sheet .remote-pad .remote-panel.full > .remote-panel-title {
  position: static;
  grid-area: 1 / 1;
  align-self: center;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip-path: none;
  color: var(--text);
  font-size: 22px;
  letter-spacing: normal;
  text-transform: none;
}
.remote-sheet .remote-pad .remote-panel.full > .remote-guide-close {
  grid-area: 1 / 2;
  background: var(--surface-2);
  box-shadow: var(--ring);
}
@media (hover: hover) {
  .remote-sheet .remote-pad .remote-panel.full > .remote-guide-close:hover {
    background: var(--surface-3);
  }
}
.remote-sheet .remote-pad .remote-panel.full > .remote-channels-tools {
  grid-row: 2;
  margin-top: 12px;
}
.remote-sheet .remote-pad .remote-panel.full > #remote-no-channels {
  grid-row: 3;
  margin-top: 12px;
}
.remote-sheet .remote-pad .remote-panel.full > .remote-guide {
  grid-row: 4;
  min-height: 0;
  margin-top: 12px;
}
.remote-sheet .remote-pad .remote-panel.full > .remote-channels-foot {
  grid-row: 5;
  margin-top: 10px;
}
/* Under the list, or the guide: the guide's key to its colors, then what a tap does, said shorter in the guide. */
.remote-channels-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
}
.remote-panel.full .remote-list-hint,
.remote-panel:not(.full) .remote-guide-hint {
  display: none;
}
.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);
}
/* The remote's radio: what's on the TV, with Stop; a search, Captions where the TV can show them, and
   the stations to choose from, each a key across the panel with its logo, name, and where it's from
   and how it sounds, and a star beside it for the account's favorites; the one playing outlined, as
   an app the TV shows is. */
.radio-now {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 4px 0 14px;
  padding: 10px;
  border-radius: 18px;
  background: color-mix(in srgb, var(--accent) 14%, var(--key));
  box-shadow: inset 0 0 0 2px var(--accent), var(--key-edge);
}
.radio-now[data-state='failed'] {
  background: color-mix(in srgb, var(--warn) 12%, var(--key));
  box-shadow: inset 0 0 0 2px var(--warn), var(--key-edge);
}
.radio-now[hidden] {
  display: none;
}
.radio-now-words {
  display: grid;
  flex: 1;
  gap: 2px;
  min-width: 0;
  margin: 0;
}
.radio-now-words strong {
  overflow: hidden;
  font-size: 16px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.radio-now-words small {
  color: var(--text-2);
  font-size: 13px;
}
.radio-now[data-state='playing'] .radio-now-words small {
  color: var(--text);
}
.remote-radio-captions,
.remote-radio-translate {
  margin: 12px 0 0;
}
#remote-radio-status:empty {
  display: none;
}
.radio-stations {
  display: grid;
  gap: 8px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}
/* Near the home, then the rest: a heading over each, as the Apps tab's are. */
.radio-stations-head {
  margin: 6px 0 -2px;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.radio-stations li:not(.radio-stations-head) {
  display: flex;
  gap: 8px;
}
.remote-pad .radio-station {
  display: flex;
  flex: 1;
  min-width: 0;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  width: 100%;
  min-height: 60px;
  padding: 8px 14px 8px 8px;
  border-radius: 16px;
  background: var(--key);
  box-shadow: var(--key-edge);
  text-align: left;
}
@media (hover: hover) {
  .remote-pad .radio-station:hover {
    background: color-mix(in srgb, var(--key), var(--text) 5%);
  }
}
.remote-pad .radio-station[aria-pressed='true'] {
  box-shadow: inset 0 0 0 2px var(--accent), var(--key-edge);
}
.radio-station-logo {
  display: grid;
  flex: none;
  place-items: center;
  width: 44px;
  height: 44px;
  overflow: hidden;
  border-radius: 11px;
  background: #fff;
}
.radio-station-logo:empty {
  background: var(--surface-2);
}
.radio-station-logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.radio-station-words {
  display: grid;
  gap: 2px;
  min-width: 0;
}
.radio-station-words strong {
  overflow: hidden;
  font-size: 16px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.radio-station-words small {
  overflow: hidden;
  color: var(--text-2);
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.remote-pad .radio-star {
  display: grid;
  flex: none;
  place-items: center;
  width: 52px;
  border-radius: 16px;
  background: var(--key);
  box-shadow: var(--key-edge);
  color: var(--text-2);
}
.radio-star svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linejoin: round;
}
.remote-pad .radio-star[aria-pressed='true'] {
  color: var(--warn);
}
.radio-star[aria-pressed='true'] svg {
  fill: currentColor;
}
.remote-radio-foot {
  display: grid;
  justify-items: center;
  margin-top: 12px;
}
.remote-pad #remote-radio-stop {
  flex: none;
  min-height: 44px;
  padding: 0 18px;
  border-radius: 14px;
  background: var(--key);
  box-shadow: var(--key-edge);
  font-weight: 700;
}

/* The Devices page's link to what Readout works with (supported.html). */
.devices-supported a {
  color: var(--accent);
  font-weight: 700;
}

/* 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-channel-tune, .remote-channel-star),
  .remote-tv,
  .remote-touchpad {
    border: 1px solid ButtonText;
  }
  .remote-colors button::before,
  .remote-app-mark {
    forced-color-adjust: none;
  }
}
#helper-about code,
#pi-setup-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);
}

/* ---------- an event's captions (event.html) ---------- */
/* The app's captions, full height, on a phone that's only reading: the event's name and whether it's
   live above, the reader's text size below, and a note that it's over in place of the size once it is. */
.event-head {
  min-width: 0;
}
.event-head .mic-brand {
  padding: 0;
  font-size: 13px;
  color: var(--text-2);
}
.event-name {
  margin: 0;
  font-size: 18px;
  line-height: 1.25;
  overflow-wrap: anywhere;
  text-wrap: balance;
}
.event-page .status {
  flex: none;
}
.event-page .status[data-state='off'] .dot {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--spk-0);
}
/* The event's stream, above its captions: the picture at most a third of the screen, so the words keep most of it. */
.event-watch {
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.event-watch video {
  display: block;
  width: 100%;
  max-height: 34dvh;
  background: #000;
  object-fit: contain;
}
.event-watch .row {
  padding: 8px max(12px, env(safe-area-inset-right)) 8px max(12px, env(safe-area-inset-left));
}
.event-watch-state {
  margin: 0;
  color: var(--text-2);
  font-size: 15px;
}
.event-stream {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
}
.event-stream-state {
  margin: 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.event-empty {
  margin: 20vh auto 0;
  max-width: 22em;
  color: var(--text-2);
  font-family: var(--font);
  font-size: 17px;
  text-align: center;
}
.event-foot {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding: 8px max(12px, env(safe-area-inset-right)) max(8px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
  border-top: 1px solid var(--line);
  background: var(--bg);
}
.event-size-label {
  color: var(--text-2);
  font-size: 15px;
}
.event-over {
  padding: 16px max(16px, env(safe-area-inset-right)) max(20px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  border-top: 1px solid var(--line);
  background: var(--surface);
  font-size: 17px;
  line-height: 1.45;
}
.event-over h2 {
  margin: 0 0 4px;
  font-size: 20px;
}
.event-over p {
  margin: 0;
  color: var(--text-2);
}
.event-over:not([hidden]) ~ .event-foot,
body:has(.event-over:not([hidden])) .event-foot {
  display: none;
}

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 :is([role='tabpanel'], #accounts-all, #account-page) > 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;
}
/* How many on a tab need a look: Feedback's nobody has read yet. */
.tab-count {
  display: inline-block;
  min-width: 20px;
  margin-left: 4px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 13px;
  line-height: 20px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Errors (admin-errors.js): where each was, under its words in the list; on its page, its words as
   the heading, and the frames it was in, in the code's own type. */
.error-place {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  overflow-wrap: anywhere;
}
#pane-errors h1.error-message {
  overflow-wrap: anywhere;
}
#pane-errors .pi-facts {
  margin-top: 16px;
}
#pane-errors .pi-facts h3 {
  margin: 16px 0 8px;
}
.error-stack,
.error-days {
  margin: 0;
  padding-left: 20px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  overflow-wrap: anywhere;
}

/* Feedback (admin-feedback.js): one's page is their words and how the app was going side by side,
   then what to do with it. */
.feedback-text {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  line-height: 1.5;
}
.feedback-facts {
  margin-top: 16px;
}
.feedback-facts h3 {
  margin: 16px 0 8px;
}
.feedback-errors {
  margin: 0;
  padding-left: 20px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  overflow-wrap: anywhere;
}
.feedback-act {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-top: 16px;
}
.feedback-act > :is(fieldset, form, div) {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  border: 0;
}
.feedback-act .link-row input {
  flex: 1;
  min-width: 0;
}
.feedback-act textarea {
  width: 100%;
}

/* On a phone the admin page's six tabs sit closer together, and scroll sideways rather than the page
   where even that is too wide. */
@media (max-width: 30rem) {
  #tabs {
    overflow-x: auto;
    scrollbar-width: none;
  }
  #tabs .admin-tab {
    flex: none;
    padding: 0 9px;
  }
}

/* Accounts: forms for a new account or an invite to one, a row for each account, unused invites, and
   who else sees the page; each account's own page, as a Pi's: its title, today and its limit, its people. */
.admin-invite {
  margin-top: 20px;
}
:is(.admin-invite, .account-invite) .link-row input {
  flex: 1;
  min-width: 0;
}
#accounts-title,
#account-page h1 {
  outline: none;
}
/* On a phone, the buttons beside the title go onto more lines rather than squeezing their words. */
#pane-accounts .admin-head > .row {
  flex-wrap: wrap;
  justify-content: flex-start;
}
#pane-accounts .admin-head > .row .btn {
  white-space: nowrap;
}
#account-page .admin-head {
  margin-top: 4px;
}
@media (hover: hover) {
  #pane-accounts a.pi-name:hover {
    text-decoration: underline;
  }
}
#account-page .admin-head h1 {
  overflow-wrap: anywhere;
}
.account-invite {
  margin-top: 20px;
}
.account-invite .hint {
  margin: 0 0 12px;
}
.account-overview {
  display: grid;
  gap: 16px;
  margin-top: 24px;
}
@media (min-width: 48rem) {
  .account-overview {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.account-overview .hint {
  margin: 4px 0 0;
}
.account-overview .usage-meter {
  max-width: 20rem;
  margin-top: 12px;
}
.admin-limit .limit-picker {
  margin-top: 12px;
}
.admin-limit .form-msg {
  margin: 8px 0 0;
}
.admin-limit .form-msg:empty {
  display: none;
}
dialog.account-sheet {
  width: min(520px, calc(100% - 32px));
}
.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-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: how it's doing, a grid of cards, then what it is, with what needs a look, its tags and
   its note beside them; in one column, what needs a look first, then how it's doing and what it is,
   then its tags and note. */
.pi-overview {
  display: grid;
  gap: 16px;
}
.pi-overview > .pi-side {
  display: contents;
}
.pi-overview .pi-health {
  order: 1;
}
.pi-now {
  order: 2;
  min-width: 0;
}
.pi-about {
  order: 3;
}
.pi-side > :not(.pi-health) {
  order: 4;
}
@media (min-width: 76rem) {
  .pi-overview {
    grid-template-columns: minmax(0, 2.4fr) minmax(18rem, 1fr);
    grid-template-areas: 'measures side' 'about side';
    align-items: start;
  }
  .pi-overview > .pi-side {
    display: flex;
  }
  .pi-now {
    grid-area: measures;
  }
  .pi-about {
    grid-area: about;
  }
}
/* How it's doing: its name and the choice of what its charts show, over its cards. */
.pi-now-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 12px;
}
.pi-now-top h2 {
  margin: 0;
  color: var(--text);
  font-size: 17px;
  line-height: 1.3;
}
.pi-range {
  margin: 0;
  border: 0;
}
.pi-range input:disabled + span {
  opacity: 0.45;
  cursor: not-allowed;
}
.pi-measures {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 16px;
}
.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;
}
/* A chart of the last minutes, as the Pi says how busy it is each second: its dot beats as each reading comes. */
.pi-live {
  color: var(--text-2);
}
.pi-live::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--ok);
  vertical-align: 0.05em;
}
@media (prefers-reduced-motion: no-preference) {
  .pi-live::before {
    animation: pi-beat 900ms ease-out;
  }
}
@keyframes pi-beat {
  from {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 60%, transparent);
  }
  to {
    box-shadow: 0 0 0 6px transparent;
  }
}
.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) 14%, transparent);
  stroke: none;
}
.trend path.line-2 {
  stroke: var(--series-2);
}
.trend line.limit {
  stroke: var(--danger);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0.7;
}
.trend line.grid {
  stroke: var(--line);
  stroke-width: 1;
}
/* A Pi's last minutes, live (see liveChart): its scale's top in its corner, a dot on each line's
   newest reading, and what each said where the pointer is. */
.pi-chart {
  position: relative;
  margin-top: 14px;
  touch-action: pan-y;
}
/* Its lines run on beyond its edges as they move (see liveChart), so it shows only its minute. */
.pi-chart .trend {
  margin-top: 0;
  overflow: hidden;
}
.pi-chart-scale {
  position: absolute;
  top: -3px;
  left: 0;
  padding-right: 6px;
  background: var(--surface);
  color: var(--text-2);
  font-size: 11px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.pi-chart-dot {
  position: absolute;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--series-1);
  box-shadow: 0 0 0 2px var(--surface);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.pi-chart-dot[data-series='2'],
[data-series='2'] > .pi-series-key {
  background: var(--series-2);
}
.pi-cross {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--text-2);
  opacity: 0.45;
  pointer-events: none;
}
/* Beside the crosshair, on whichever side has more room. */
.pi-tip {
  position: absolute;
  top: 50%;
  z-index: 2;
  padding: 7px 10px;
  border-radius: 10px;
  background: var(--surface-2);
  box-shadow: var(--shadow-lg);
  color: var(--text-2);
  font-size: 12px;
  white-space: nowrap;
  transform: translate(10px, -50%);
  pointer-events: none;
}
.pi-tip.left {
  transform: translate(calc(-100% - 10px), -50%);
}
.pi-tip p {
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.pi-tip p + p {
  margin-top: 3px;
}
.pi-tip strong {
  color: var(--text);
  font-weight: 600;
}
.pi-tip-when {
  font-size: 11px;
}
/* A chart's lines, each its figure and a short line in its color. */
.pi-series {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 6px 0 0;
  padding: 0;
  color: var(--text-2);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  list-style: none;
}
.pi-series strong {
  color: var(--text);
  font-weight: 600;
}
.pi-series-key {
  display: inline-block;
  width: 12px;
  height: 2px;
  margin-right: 7px;
  border-radius: 1px;
  background: var(--series-1);
  vertical-align: 0.3em;
}
.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: 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;
}
.admin-table td.wrap {
  min-width: 20rem;
  white-space: normal;
  text-wrap: pretty;
}
.admin-table td.wrap small {
  white-space: normal;
}

/* 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;
}
/* Copying a Pi's root password: the approval page's /copy, in a frame, unseen while all it takes is
   the passkey, and a dialog where it needs a look (see copyPassword in pi-passwords.js). */
.pi-password-copy {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgb(0 0 0 / 0.55);
}
.pi-password-copy.unseen {
  inset: auto 0 0 auto;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  background: none;
}
.pi-password-copy-panel {
  width: min(520px, 100%);
  padding: 0 20px 20px;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 20px 60px rgb(0 0 0 / 0.45);
}
.pi-password-copy.unseen .pi-password-copy-panel {
  width: 520px;
}
.pi-password-frame {
  display: block;
  width: 100%;
  height: 280px;
  border: 1px solid var(--line);
  border-radius: 10px;
}
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 tags and note under it. */
.pi-side {
  grid-area: side;
  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;
}
/* The kinds of device plugged into the Pis or found by home agents (see pi-device-types.js): each
   with the icon a Pi's tray draws it with, in the text's colour, at a size it can be judged at; the
   one being drawn; and how one is listed on the supported devices page. */
.device-type-title {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.device-type-icon {
  flex: none;
  width: 32px;
  height: 32px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.device-type-icon .dot {
  fill: currentColor;
  stroke: none;
}
.device-type-icon .off {
  opacity: 0.3;
}
.device-type-id {
  color: var(--text-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  overflow-wrap: anywhere;
}
.device-type-listed {
  margin: 0;
  color: var(--ok);
  font-weight: 600;
}
.device-type-editor {
  margin-top: 12px;
}
.device-type-editor textarea {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--field-border);
  border-radius: 12px;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  resize: vertical;
}
.device-type-previews {
  display: flex;
  gap: 24px;
}
.device-type-preview {
  display: grid;
  justify-items: center;
  gap: 6px;
  margin: 0;
}
.device-type-preview .device-type-icon {
  width: 64px;
  height: 64px;
  padding: 10px;
  border-radius: 12px;
  background: var(--surface-2);
}
.device-type-preview figcaption {
  margin: 0;
}
.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-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-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;
}
/* A setup link in the app's TVs and phones: the Pi's name, and Make a setup link beside it. */
.pi-setup-form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.pi-setup-form input {
  flex: 1 1 12rem;
  min-width: 0;
}
#pi-setup-made > p {
  margin: 0 0 8px;
}

/* The Pis tab's title, with Set up a Pi beside it. */
.pis-title-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
}

/* Set up a Pi (see pi-setup.js): the commands of the link just made, each with Copy. */
.pi-setup-made {
  display: grid;
  gap: 8px;
  margin-bottom: 20px;
  padding: 14px 16px;
  border-radius: 14px;
  background: var(--surface-2);
}
.pi-setup-made h3,
.pi-setup-made p {
  margin: 0;
}
.pi-setup-label {
  font-weight: 700;
}
.pi-setup-command {
  display: flex;
  align-items: center;
  gap: 10px;
}
.pi-setup-command code {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--bg);
  font-size: 14px;
  overflow-wrap: anywhere;
  user-select: all;
}
.pi-rollouts-title {
  margin: 24px 0 4px;
  font-size: 17px;
}
.pi-alerting .pi-tasks-top:first-child .pi-rollouts-title {
  margin-top: 0;
}
.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='sofer'] {
  --part: color-mix(in oklab, var(--series-1) 50%, var(--series-3));
}
[data-part='soniox-async'] {
  --part: color-mix(in oklab, var(--series-1) 60%, var(--series-4));
}
/* What it cost besides speech, beside the speech services. */
[data-part='video'] {
  --part: var(--series-3);
}
[data-part='claude'] {
  --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);
}

/* 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) {
    :is(.nav-item[aria-current='page'], .nav-result[aria-current='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;
  }
  .empty-mark .edge {
    stroke: CanvasText;
  }
}

/* The Features tab on the admin page (see admin-features.js): a row for each of the app's features,
   and each one's page: who has it, as rules any one of which gives it, whom that reaches, and what was
   chosen before. */
.flag-about {
  max-width: 52rem;
  margin: 4px 0 20px;
}
.flag-key {
  padding: 1px 7px;
  border-radius: 6px;
  background: var(--surface-3);
  color: var(--text-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.5;
}
/* How a feature stands: a dot and its words, in its color. */
.flag-status {
  --tone: var(--text-2);
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 7px;
  padding: 3px 10px 3px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--tone) var(--tint), transparent);
  color: var(--tone);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
}
.flag-status::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}
.flag-status[data-level='on'] {
  --tone: var(--ok);
}
.flag-status[data-level='some'] {
  --tone: var(--accent);
}
.flag-status[data-level='unset'] {
  --tone: var(--warn);
}
.flag-meter::before {
  border-radius: inherit;
  background: var(--accent);
}
.flag-meter[data-full='true']::before {
  background: var(--ok);
}

/* The list: a table on a wide screen, a card each on a narrow one. */
.flag-table {
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--ring);
  overflow: hidden;
}
.flag-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.flag-row {
  display: grid;
  grid-template-columns: minmax(0, 2.4fr) 9.5rem minmax(0, 1.4fr) 10rem 9rem;
  grid-template-areas: 'name status who reach when' 'hint status who reach when';
  align-items: center;
  gap: 3px 20px;
  padding: 16px 20px;
  color: inherit;
  text-decoration: none;
}
.flag-table-head {
  grid-template-areas: none;
}
.flag-list li + li .flag-row {
  border-top: 1px solid var(--line);
}
a.flag-row {
  transition: background-color 150ms ease-out;
}
@media (hover: hover) {
  a.flag-row:hover {
    background: var(--surface-2);
  }
  a.flag-row:hover .flag-cell-name strong {
    color: var(--accent);
  }
}
a.flag-row:focus-visible {
  outline-offset: -3px;
}
.flag-table-head {
  padding-block: 10px;
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
}
.flag-cell {
  min-width: 0;
}
.flag-cell-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  grid-area: name;
}
.flag-cell-status {
  grid-area: status;
}
.flag-cell-who {
  grid-area: who;
}
.flag-cell-reach {
  grid-area: reach;
}
.flag-cell-when {
  grid-area: when;
}
.flag-cell-name strong {
  font-size: 16px;
  transition: color 150ms ease-out;
}
.flag-row-hint {
  grid-area: hint;
  overflow: hidden;
  color: var(--text-2);
  font-size: 14px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.flag-unsaved {
  color: var(--warn);
  font-size: 13px;
  font-weight: 700;
}
.flag-cell-who {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.flag-who-chip {
  max-width: 100%;
  padding: 2px 8px;
  overflow: hidden;
  border-radius: 6px;
  background: var(--surface-2);
  box-shadow: var(--ring);
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.flag-who-chip[data-level='off'] {
  color: var(--text-2);
}
.flag-reach-mini {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-2);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.flag-reach-mini .pi-meter {
  flex: none;
  width: 72px;
  height: 6px;
  margin: 0;
  border-radius: 3px;
}
.flag-cell-when {
  display: grid;
  font-size: 14px;
}
.flag-cell-when small {
  color: var(--text-2);
  font-size: 13px;
}
.flag-cell-when .hint {
  margin: 0;
}
@media (max-width: 64rem) {
  .flag-table-head {
    display: none;
  }
  .flag-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: 'name status' 'hint hint' 'who who' 'reach when';
    gap: 8px 16px;
    padding: 16px;
  }
  .flag-cell-status {
    align-self: start;
  }
  .flag-cell-when {
    text-align: right;
  }
  .flag-row-hint {
    white-space: normal;
  }
}

/* A feature's page: its title, then who has it beside whom that reaches and what was chosen before. */
.flag-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-top: 14px;
}
.flag-head-icon {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--accent) var(--tint), var(--surface));
  box-shadow: var(--ring);
  color: var(--accent);
}
.flag-head-icon svg,
.flag-rule-icon svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.flag-head-text {
  flex: 1;
  min-width: 0;
}
.flag-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
}
.flag-title h1 {
  margin: 0;
  font-size: 26px;
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.flag-hint {
  max-width: 52rem;
  margin: 6px 0 0;
  font-size: 15px;
}
.flag-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 0;
  margin: 10px 0 0;
  color: var(--text-2);
  font-size: 14px;
}
.flag-meta > * + *::before {
  content: '·';
  margin: 0 10px;
  color: var(--text-2);
}
.flag-meta > .flag-key + *::before {
  margin-left: 10px;
}
.flag-note {
  margin-top: 16px;
  padding: 12px 16px 12px 20px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--warn) var(--tint), var(--bg));
  box-shadow: inset 4px 0 var(--warn);
}
.flag-note p {
  margin: 0;
}
.flag-layout {
  display: grid;
  gap: 20px;
  margin-top: 24px;
}
.flag-main,
.flag-side {
  display: grid;
  gap: 16px;
  min-width: 0;
}
@media (min-width: 64rem) {
  .flag-layout {
    grid-template-columns: minmax(0, 1fr) 22rem;
    align-items: start;
  }
  .flag-side {
    position: sticky;
    top: 16px;
  }
}
.flag-setup {
  padding: 18px 20px 20px;
}
.pi-panel.flag-setup > h2 {
  margin-bottom: 2px;
}
.flag-setup-hint {
  margin: 0 0 16px;
}
.flag-who {
  max-width: 26rem;
  min-width: 0;
  margin: 0 0 20px;
  border: 0;
}

/* The rules, a card each, joined by "or", then everyone else's. */
.flag-rules {
  display: grid;
}
.flag-rule {
  border-radius: 14px;
  background: var(--surface-2);
  box-shadow: var(--ring);
}
.flag-rule.quiet {
  margin-top: 14px;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--line);
}
.flag-rule-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 12px 14px 16px;
}
.flag-rule-icon {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) var(--tint), transparent);
  color: var(--accent);
}
.flag-rule-icon svg {
  width: 18px;
  height: 18px;
}
.flag-rule[data-rule='everyone'] .flag-rule-icon {
  background: color-mix(in srgb, var(--ok) var(--tint), transparent);
  color: var(--ok);
}
.flag-rule.quiet .flag-rule-icon,
.flag-rule[data-rule='nobody'] .flag-rule-icon {
  background: var(--surface-3);
  color: var(--text-2);
}
.flag-rule-words {
  flex: 1;
  min-width: 0;
  align-self: center;
}
.flag-rule-words h3 {
  margin: 0;
  font-size: 16px;
  line-height: 1.3;
}
.flag-rule-words .hint {
  margin: 2px 0 0;
}
.flag-pass {
  flex: none;
  align-self: center;
  padding: 3px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ok) var(--tint), transparent);
  color: var(--ok);
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}
.flag-pass[data-gives='false'] {
  background: var(--surface-3);
  color: var(--text-2);
}
.btn.small.flag-rule-remove {
  align-self: center;
  width: 36px;
  padding: 0;
  color: var(--text-2);
}
.flag-rule-remove .pi-icon svg {
  width: 16px;
  height: 16px;
}
/* "or" on the line joining one rule's icon to the next's. */
.flag-or {
  position: relative;
  height: 32px;
  padding-left: 48px;
  color: var(--text-2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 32px;
  text-transform: uppercase;
}
.flag-or::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 33px;
  width: 2px;
  background: var(--line);
}
.flag-empty {
  margin: 0;
  padding: 18px 16px;
  border-radius: 14px;
  background: var(--surface-2);
  color: var(--text-2);
  text-align: center;
}
.flag-kept {
  margin: 12px 0 0;
}
.flag-add {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 14px 0 0;
  padding: 10px 12px;
  border: 1px dashed var(--field-border);
  border-radius: 14px;
}
.flag-add-label {
  margin: 0 4px 0 4px;
  color: var(--text-2);
  font-size: 14px;
  font-weight: 700;
}
.flag-add-rule .pi-icon svg {
  width: 16px;
  height: 16px;
}

/* What a rule is chosen with, under its name. */
.flag-field {
  padding: 0 16px 16px 64px;
}
.flag-condition {
  display: inline-flex;
  margin: 0 0 8px;
  overflow: hidden;
  border-radius: 8px;
  box-shadow: var(--ring);
  font-size: 13px;
  font-weight: 700;
}
.flag-cond-what {
  padding: 3px 10px;
  background: var(--surface-3);
}
.flag-cond-how {
  padding: 3px 10px;
  color: var(--text-2);
}
.flag-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-height: 46px;
  padding: 6px;
  border: 1px solid var(--field-border);
  border-radius: 12px;
  background: var(--bg);
  cursor: text;
}
.flag-chips:focus-within {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.flag-chip-list {
  display: contents;
}
.flag-chips .flag-chips-input {
  flex: 1 0 12rem;
  width: auto;
  min-width: 0;
  padding: 4px 6px;
  border: 0;
  background: transparent;
  font-size: 16px;
  outline: none;
}
.flag-chosen {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
}
.flag-chosen .hint {
  margin: 0;
}
.flag-chip {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  max-width: 100%;
  padding: 2px 2px 2px 10px;
  border-radius: 8px;
  background: var(--surface-3);
  font-size: 14px;
  line-height: 1.5;
}
.flag-chip > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.flag-chip-remove {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-2);
  cursor: pointer;
}
@media (hover: hover) {
  .flag-chip-remove:hover {
    background: var(--surface-2);
    color: var(--text);
  }
}
.flag-chip-remove .pi-icon svg {
  width: 14px;
  height: 14px;
}
.flag-field > .form-msg {
  margin: 6px 0 0;
}
.flag-picker > summary {
  width: max-content;
  list-style: none;
  cursor: pointer;
}
.flag-picker > summary::-webkit-details-marker {
  display: none;
}
.flag-picker > summary .pi-icon svg {
  width: 16px;
  height: 16px;
}
.flag-picker[open] > summary {
  margin-bottom: 10px;
}
.flag-picker .pis-search {
  width: min(22rem, 100%);
  margin-bottom: 8px;
}
.flag-options {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: 0 8px;
  max-height: 15rem;
  padding: 4px;
  overflow: auto;
  border-radius: 12px;
  background: var(--bg);
  box-shadow: var(--ring);
}
.flag-option {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 38px;
  padding: 0 8px;
  border-radius: 8px;
  cursor: pointer;
}
@media (hover: hover) {
  .flag-option:hover {
    background: var(--surface-2);
  }
}
.flag-option[hidden] {
  display: none;
}
.flag-option input {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--accent);
}
.flag-option span {
  overflow-wrap: anywhere;
}
.flag-share-row {
  display: flex;
  align-items: center;
  gap: 16px;
}
.flag-share-number-wrap {
  position: relative;
  flex: none;
}
.flag-share-number {
  width: 6rem;
  padding: 7px 30px 7px 12px;
  border: 1px solid var(--field-border);
  border-radius: 10px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  appearance: textfield;
}
.flag-share-number::-webkit-inner-spin-button,
.flag-share-number::-webkit-outer-spin-button {
  margin: 0;
  appearance: none;
}
.flag-share-unit {
  position: absolute;
  top: 50%;
  right: 12px;
  translate: 0 -50%;
  color: var(--text-2);
  font-weight: 700;
  pointer-events: none;
}
.flag-share input[type='range']::-webkit-slider-runnable-track {
  background: linear-gradient(to right, var(--accent) var(--fill, 0%), var(--surface-3) var(--fill, 0%));
}
.flag-share-in {
  margin: 8px 0 0;
}

/* Saving: only while there's something to save, kept in view at the bottom as the page scrolls. */
.flag-save {
  position: sticky;
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  padding: 8px 8px 8px 18px;
  border-radius: 14px;
  background: var(--surface-3);
  box-shadow: var(--shadow-lg);
}
.flag-save[hidden] {
  display: none;
}
.flag-save-words {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-weight: 700;
}
.flag-save-words::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warn);
}
.flag-save .form-msg {
  flex: 1 1 12rem;
  margin: 0;
}
.flag-save-buttons {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

/* Beside the rules: whom it reaches, and what was chosen before. */
.flag-reach-number {
  margin: 0;
  color: var(--text-2);
  font-size: 15px;
}
.flag-reach-number strong {
  margin-right: 2px;
  color: var(--text);
  font-size: 34px;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.flag-reach .pi-meter.wide {
  height: 8px;
  margin: 12px 0;
}
.flag-reach-line {
  margin: 4px 0 0;
}
.flag-reach-draft {
  margin: 10px 0 0;
  color: var(--warn);
  font-size: 13px;
  font-weight: 700;
}
.flag-history-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.flag-history-list li {
  position: relative;
  padding: 0 0 14px 22px;
}
.flag-history-list li:last-child {
  padding-bottom: 0;
}
.flag-history-list li::before {
  content: '';
  position: absolute;
  top: 6px;
  left: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px var(--accent);
}
.flag-history-list li:first-child::before {
  background: var(--accent);
}
.flag-history-list li:not(:last-child)::after {
  content: '';
  position: absolute;
  top: 20px;
  bottom: 2px;
  left: 4px;
  width: 2px;
  border-radius: 1px;
  background: var(--line);
}
.flag-history-text {
  margin: 0;
  font-size: 15px;
  overflow-wrap: anywhere;
}
.flag-history-list .hint {
  margin: 2px 0 0;
  font-size: 13px;
}
/* The tour's own part of its page (see admin-tour.js): its extras' switches, then how far visitors got. */
.tour-admin .toggle {
  margin: 12px 0;
}
.tour-admin h3 {
  margin: 22px 0 6px;
}
.tour-admin-totals {
  margin: 0 0 10px;
  font-size: 15px;
  line-height: 1.45;
}
.tour-admin-steps {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.tour-admin-steps th,
.tour-admin-steps td {
  padding: 7px 8px 7px 0;
  border-top: 1px solid var(--line);
  text-align: start;
  vertical-align: middle;
}
.tour-admin-steps thead th {
  border-top: 0;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
}
.tour-admin-steps tbody th {
  font-weight: 400;
}
.tour-admin-steps td:last-child {
  width: 1%;
  white-space: nowrap;
}
.tour-admin-bar {
  display: block;
  height: 4px;
  margin-top: 4px;
  overflow: hidden;
  border-radius: 2px;
  background: var(--surface-3);
}
.tour-admin-bar i {
  display: block;
  height: 100%;
  background: var(--accent);
}
/* On a phone: the facts about a feature a line each, and a rule's description the card's width, with
   whether it gives the feature under it. */
@media (max-width: 40rem) {
  .flag-head-icon {
    display: none;
  }
  .flag-meta {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
  .flag-meta > * + *::before {
    content: none;
  }
  .flag-rule-head {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .flag-rule-words {
    flex-basis: calc(100% - 96px);
  }
  .flag-rule-remove {
    order: 3;
  }
  .flag-pass {
    order: 4;
    margin-left: 48px;
  }
  .flag-field {
    padding-left: 16px;
  }
}
