@layer components {
  /* Icons take their size from the text beside them. */
  .icon {
    flex: none;
    block-size: 1.25cap;
    inline-size: auto;
    aspect-ratio: 1;
  }

  .with-icon {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
  }

  /* Buttons */
  .button {
    --button-bg: var(--surface);
    --button-fg: var(--text);
    --button-border: var(--border);
    --button-bg-hover: color-mix(in oklch, var(--surface), var(--text) 6%);

    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5em;
    min-block-size: 2.5rem;
    padding-block: 0.4em;
    padding-inline: 1em;
    border: 1px solid var(--button-border);
    border-radius: 999px;
    background: var(--button-bg);
    color: var(--button-fg);
    font: inherit;
    font-size: var(--step--1);
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 160ms var(--ease-out), border-color 160ms var(--ease-out);
  }

  .button:focus-visible { outline-color: var(--primary); }

  .button:active:not(:disabled) { transform: scale(0.97); }

  @media (prefers-reduced-motion: no-preference) {
    .button {
      transition:
        background-color 160ms var(--ease-out),
        border-color 160ms var(--ease-out),
        transform 160ms var(--ease-out);
    }
  }

  @media (hover: hover) and (pointer: fine) {
    .button:hover:not(:disabled) { background: var(--button-bg-hover); }
  }

  .button:disabled {
    cursor: not-allowed;
    opacity: 0.5;
  }

  .button.primary {
    --button-bg: var(--primary);
    --button-fg: var(--on-primary);
    --button-border: var(--primary);
    --button-bg-hover: var(--primary-hover);
  }

  .button.record {
    --button-bg: var(--record);
    --button-fg: var(--on-record);
    --button-border: var(--record);
    --button-bg-hover: var(--record-hover);
  }

  /* Start: the main action of the app. */
  .button.go {
    --button-bg: var(--go);
    --button-fg: var(--on-go);
    --button-border: var(--go);
    --button-bg-hover: var(--go-hover);
  }

  /* An action that removes data. */
  .button.danger {
    --button-fg: var(--danger);
    --button-border: color-mix(in oklch, var(--danger) 45%, transparent);
    --button-bg-hover: var(--record-subtle);
  }

  .button.danger.solid {
    --button-bg: var(--danger);
    --button-fg: var(--on-record);
    --button-border: var(--danger);
    --button-bg-hover: var(--record-hover);
  }

  .button.quiet {
    --button-bg: transparent;
    --button-border: transparent;
    --button-bg-hover: color-mix(in oklch, var(--text) 8%, transparent);
  }

  .button.large {
    min-block-size: 3rem;
    padding-inline: 1.4em;
    font-size: var(--step-0);
  }

  /* An icon button: the visible box is small, the hit area is 44px. */
  .button.icon-only {
    position: relative;
    inline-size: 2.25rem;
    padding-inline: 0;
  }

  .button.icon-only::after {
    content: "";
    position: absolute;
    inset: min(0px, (100% - 44px) / 2);
  }

  .button.small {
    min-block-size: 2.25rem;
    padding-inline: 0.85em;
  }

  .button.primary:focus-visible,
  .button.record:focus-visible,
  .button.go:focus-visible,
  .button.danger.solid:focus-visible { outline-color: var(--text); }

  /* Cards */
  .card {
    display: flex;
    flex-direction: column;
    gap: var(--space-m);
    padding: var(--space-s-l);
    border: 1px solid var(--border);
    border-radius: var(--radius-l);
    background: var(--surface);
    box-shadow: var(--shadow);
  }

  .card-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-xs) var(--space-s);
  }

  .card-header > :last-child:not(:first-child) { margin-inline-start: auto; }

  /* Form fields */
  .fields {
    display: grid;
    gap: var(--space-m);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  }

  .field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
  }

  .field > label { font-weight: 600; }

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

  .input,
  .select {
    inline-size: 100%;
    min-block-size: 2.75rem;
    padding-block: 0.5em;
    padding-inline: 0.75em;
    border: 1px solid var(--border);
    border-radius: var(--radius-s);
    background: var(--surface-sunken);
    color: var(--text);
    font-family: inherit;
  }

  .input:focus-visible,
  .select:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
  }

  .input:user-invalid { border-color: var(--danger); }

  .error-text {
    color: var(--danger);
    font-size: var(--step--1);
  }

  .check {
    display: inline-flex;
    align-items: center;
    gap: 0.6em;
    cursor: pointer;
  }

  .check:has(> input:disabled) {
    color: var(--text-muted);
    cursor: not-allowed;
  }

  .check > input {
    flex: none;
    inline-size: 1.15em;
    block-size: 1.15em;
    margin: 0;
    accent-color: var(--primary);
  }

  /* Status chips */
  .chip {
    --chip-color: var(--text-muted);

    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    padding-block: 0.2em;
    padding-inline: 0.65em;
    border-radius: 999px;
    background: color-mix(in oklch, var(--chip-color) 14%, transparent);
    color: var(--chip-color);
    font-size: var(--step--1);
    font-weight: 600;
    white-space: nowrap;
  }

  .chip::before {
    content: "";
    flex: none;
    inline-size: 0.55em;
    block-size: 0.55em;
    border-radius: 50%;
    background: currentColor;
  }

  /* A chip with an icon shows the icon in place of the dot. */
  .chip:has(> .icon)::before { display: none; }

  .chip[data-state="live"] { --chip-color: var(--success); }
  /* A warning chip is filled amber, so that it asks for attention. */
  .chip[data-state="warn"] {
    background: var(--amber);
    color: var(--on-amber);
  }
  .chip[data-state="error"] { --chip-color: var(--danger); }
  .chip[data-state="rec"] { --chip-color: var(--record); }

  @media (prefers-reduced-motion: no-preference) {
    .chip[data-state="rec"]::before { animation: blink 1.2s steps(2, jump-none) infinite; }
  }

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

  /* Notices */
  .notice {
    --notice-color: var(--primary);

    display: flex;
    align-items: start;
    gap: 0.6em;
    padding: var(--space-s) var(--space-m);
    border: 1px solid color-mix(in oklch, var(--notice-color) 40%, transparent);
    border-radius: var(--radius-m);
    background: color-mix(in oklch, var(--notice-color) 10%, transparent);
  }

  .notice > .icon {
    inline-size: 1.1em;
    block-size: 1lh;
    color: var(--notice-color);
  }

  .notice > div {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
  }

  .notice > div > .button {
    align-self: start;
    margin-block-start: var(--space-2xs);
  }

  .notice[data-kind="warn"] { --notice-color: var(--warning); }
  .notice[data-kind="error"] { --notice-color: var(--danger); }
  .notice[data-kind="success"] { --notice-color: var(--success); }

  /* Media frames for live previews and playback */
  /* The frame keeps 16:9 for any video. Without min-block-size: 0 a square
     video makes the frame square, and the rows must not grow with the video. */
  .frame {
    display: grid;
    grid-template: minmax(0, 1fr) / minmax(0, 1fr);
    aspect-ratio: 16 / 9;
    min-block-size: 0;
    border-radius: var(--radius-m);
    background: var(--video-bg);
    color: var(--video-text);
    overflow: clip;
  }

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

  .frame > video {
    inline-size: 100%;
    block-size: 100%;
    object-fit: contain;
  }

  .frame > .placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: safe center;
    gap: var(--space-xs);
    padding: var(--space-m);
    color: color-mix(in oklch, var(--video-text) 70%, transparent);
    text-align: center;
  }

  .frame > .placeholder > .icon { block-size: 2rem; }

  .frame > .badge {
    position: relative;
    place-self: start;
    margin: var(--space-xs);
  }

  .badge {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    padding-block: 0.2em;
    padding-inline: 0.6em;
    border-radius: var(--radius-s);
    background: oklch(0.1 0 none / 0.65);
    color: var(--video-text);
    font-size: var(--step--1);
    font-weight: 600;
  }

  .badge.rec { background: var(--record); color: oklch(1 0 none); }

  /* Camera previews are a mirror image, as in a video call. Recordings are not. */
  .mirror { scale: -1 1; }


  /* Microphone level meter. A script sets --level from 0 to 1. */
  .meter {
    --level: 0;
    --peak: 0;

    position: relative;
    block-size: 0.75rem;
    border-radius: 999px;
    background: var(--surface-sunken);
    outline: 1px solid var(--border);
    outline-offset: -1px;
    overflow: clip;
  }

  .meter::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to right in oklch, var(--success) 0% 70%, var(--warning) 85%, var(--danger) 100%);
    clip-path: inset(0 calc((1 - var(--level)) * 100%) 0 0);
  }

  .meter::after {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: calc(var(--peak) * 100% - 2px);
    inline-size: 2px;
    background: var(--text);
    opacity: calc(var(--peak) * 4);
  }

  /* Definition lists for source details */
  .details {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-2xs) var(--space-s);
    margin: 0;
    font-size: var(--step--1);
  }

  .details dt { color: var(--text-muted); }
  .details dd { margin: 0; }

  /* Dialogs */
  dialog {
    inline-size: min(100% - 2rem, 30rem);
    padding: var(--space-l);
    border: 1px solid var(--border);
    border-radius: var(--radius-l);
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 24px 64px oklch(0 0 none / 0.3);
  }

  dialog::backdrop { background: oklch(0.1 0.01 264 / 0.6); }

  dialog > form,
  dialog > .dialog-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-m);
  }

  .actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-s);
  }

  .actions.end { justify-content: flex-end; }

  dialog {
    opacity: 0;
    transition:
      opacity 0.2s var(--ease-out),
      translate 0.2s var(--ease-out),
      display 0.2s allow-discrete,
      overlay 0.2s allow-discrete;
  }

  dialog::backdrop {
    opacity: 0;
    transition:
      opacity 0.2s var(--ease-out),
      display 0.2s allow-discrete,
      overlay 0.2s allow-discrete;
  }

  dialog[open],
  dialog[open]::backdrop { opacity: 1; }

  @starting-style {
    dialog[open],
    dialog[open]::backdrop { opacity: 0; }
  }

  @media (prefers-reduced-motion: no-preference) {
    dialog { translate: 0 0.5rem; }
    dialog[open] { translate: 0 0; }

    @starting-style {
      dialog[open] { translate: 0 0.5rem; }
    }
  }

  html:has(dialog:modal) { overflow: hidden; }

  /* Progress bars */
  .progress {
    inline-size: 100%;
    block-size: 0.5rem;
    border: none;
    border-radius: 999px;
    background: var(--surface-sunken);
    accent-color: var(--primary);
    overflow: clip;
  }

  .progress::-webkit-progress-bar { background: var(--surface-sunken); }
  .progress::-webkit-progress-value { background: var(--primary); border-radius: 999px; }
  .progress::-moz-progress-bar { background: var(--primary); border-radius: 999px; }

  @media (prefers-reduced-motion: no-preference) {
    .spin { animation: spin 1s linear infinite; }
  }

  @keyframes spin {
    to { rotate: 1turn; }
  }
}
