/* Load Inter for UI text and Literata for reading content from the same origin.
   fonts.css defines the font faces and subsets. Keeping fonts local allows the
   precache to include them, avoids external requests before the first paint,
   keeps visitor IP addresses from third-party font servers, and lets browser
   tests run without public internet access ([[precache-snapshot]]). */
@import url('vendor/fonts/fonts.css');

/* Define each semantic color as a light/dark pair. The dark-mode rules below bind
the unsuffixed token to its dark variant; components always use the unsuffixed name.
See /ui/docs/styles-color.html for the Open Props adaptive-color pattern. */
:root {
  color-scheme: light;
  accent-color: var(--brand);
  caret-color: var(--brand);

  --surface-1-light: var(--stone-0);   --surface-1-dark: var(--stone-12);
  --surface-2-light: var(--stone-1);   --surface-2-dark: var(--stone-11);
  --surface-3-light: var(--stone-2);   --surface-3-dark: var(--stone-10);
  /* Floating panels use shadows in light mode and a lighter fill in dark mode.
     A light panel cannot be brighter than an already white page; a shadow is
     barely visible against a dark page. This overlay token supports both cases,
     following Atlassian's overlay surfaces and Material 3 tonal elevation. */
  --surface-overlay-light: var(--stone-0);  --surface-overlay-dark: var(--stone-11);
  --text-1-light:    var(--stone-11);  --text-1-dark:    var(--stone-1);
  /* Use stone-8 for muted text. stone-6 gives about 2.5:1 contrast on light surfaces,
below the 4.5:1 AA requirement. */
  --text-2-light:    var(--stone-8);   --text-2-dark:    var(--stone-4);
  --brand-light:     var(--indigo-7);  --brand-dark:     var(--indigo-3);
  /* Text/icons sitting ON a --brand fill (dark brand is a light indigo,
     so its ink flips to near-black). */
  --on-brand-light:  white;            --on-brand-dark:  var(--stone-12);
  /* Shared borders for inputs, selects, comboboxes, and dividers. Default contrast
     against surface-1 is 1.39:1 in light mode and 2.35:1 in dark mode; hover gives
     2.67:1 and 4.83:1. Default control borders are below WCAG 1.4.11's 3:1 target.
     Raising this shared token would also make dividers too prominent. Separate
     the control and divider tokens before increasing control contrast. */
  --border-color-light: var(--stone-4);   --border-color-dark: var(--stone-9);
  /* Emphasised borders (hover, focus-adjacent) — one step stronger. */
  --border-strong-light: var(--stone-6);  --border-strong-dark: var(--stone-7);

  /* Status colors (studio sentence indicators, callouts). */
  --status-recorded-light: oklch(71.76% 0.1902 149.6);   --status-recorded-dark: oklch(59.56% 0.1273 151.46);
  /* Use amber for mismatches and items needing attention, reserving red for errors. */
  --status-mismatch-light: oklch(80.77% 0.1624 77.6);    --status-mismatch-dark: oklch(76.83% 0.1579 76.31);
  /* This dark-mode color also renders text in the rejection note. Its contrast
     on stone-12 is 5.2:1; darkening it toward the light-mode value would fail AA. */
  --status-error-light:    oklch(57.14% 0.2121 27.25);   --status-error-dark:    oklch(64.47% 0.1808 23.67);
  --status-syncing-light:  oklch(57.16% 0.1737 262.35);  --status-syncing-dark:  oklch(62.65% 0.1211 263.17);
  /* Previous audio counts as recorded but was produced outside this team. Plain grey, with
     no hue, keeps it apart from the recorded green and the selected sentence's blue; it
     matches the recorded state's lightness so both indicators have equal visual weight.
     [[previous-audio]] */
  --status-previous-light: oklch(71.76% 0 0);            --status-previous-dark: oklch(59.56% 0 0);
  /* The previous-audio sentence's background tint: the extreme of the text colour, since
     the grey above is too light to register as a background at a small mix. */
  --status-previous-bg-light: black;                      --status-previous-bg-dark: white;

  /* Feedback colors (info/success/warning/error) — ui-notifications, toasts,
     form-valid states. Text/icon roles, so dark mode needs LIGHTER steps. */
  --info-light:    var(--blue-9);    --info-dark:    var(--blue-4);
  --success-light: var(--green-7);   --success-dark: var(--green-4);
  --warning-light: var(--yellow-6);  --warning-dark: var(--yellow-4);
  --error-light:   var(--red-9);     --error-dark:   var(--red-4);

  /* Annotation accent (used 8+ times in studio editor) */
  --annotation-light: oklch(49.4% 0.1541 262.31);  --annotation-dark: oklch(62.65% 0.1211 263.17);

  /* Emphasis underline colors */
  --emphasis-light:        oklch(88.9% 0.154 94.89);   --emphasis-dark:        oklch(81.01% 0.1614 92.56);
  --emphasis-strong-light: oklch(73.39% 0.148 64.38);  --emphasis-strong-dark: oklch(69.15% 0.144 63.56);

  /* Live bindings — default to light. */
  --surface-1: var(--surface-1-light);
  --surface-2: var(--surface-2-light);
  --surface-3: var(--surface-3-light);
  --surface-overlay: var(--surface-overlay-light);
  --text-1: var(--text-1-light);
  --text-2: var(--text-2-light);
  --brand: var(--brand-light);
  --on-brand: var(--on-brand-light);
  --border-color: var(--border-color-light);
  --border-strong: var(--border-strong-light);
  --status-recorded: var(--status-recorded-light);
  --status-mismatch: var(--status-mismatch-light);
  --status-error: var(--status-error-light);
  --status-syncing: var(--status-syncing-light);
  --status-previous: var(--status-previous-light);
  --status-previous-bg: var(--status-previous-bg-light);
  --info: var(--info-light);
  --success: var(--success-light);
  --warning: var(--warning-light);
  --error: var(--error-light);
  --annotation: var(--annotation-light);
  --emphasis: var(--emphasis-light);
  --emphasis-strong: var(--emphasis-strong-light);

  /* Adjust shared elevation here; shadow-1 through shadow-6 use this strength.
     Dark-mode blocks set it to 25%. Components do not need separate shadow values.

     Open Props' default 1% produces five shadow-3 layers at 3–8% opacity, with
     blurs up to 15px and -5px spreads. Those diffuse shadows barely distinguish
     a floating panel. Chromium measurements show how raising the shared strength changes
     the opacity of the five --shadow-3 layers:
       1% (stock) → 3/3/5/6/8%       invisible
       4%         → 6/6/8/9/11%
       8%         → 10/10/12/13/15%  ← the value below
       15%        → 17/17/19/20/22%  heavy
       50%        → 52/52/54/55/57%  nearly opaque beneath hovered buttons

     --shadow-strength-3..10 are declared on :where(html) — the same element as :root —
     so calc(var(--shadow-strength) + n%) uses this override. The dark-mode rules
     below adjust the same value. */
  --shadow-strength: 8%;

  /* Reading column width — the studio's reading surfaces all read this token;
     do not hardcode the measure at a call site. */
  --reading-width: 42rem;

  /* Between --font-size-00 and --font-size-0, used for pills/snippets */
  --font-size-xs: 0.6875rem;

  /* One spinner rate for both spin icons and busy outlet overlays. Two seconds:
     a spinner stands beside waits measured in tens of seconds — a model check, a
     take's decode — and a fast one reads as urgency the wait does not have.
     Override the shared token here, not in vendored Open Props files, where an
     update could replace the change. See [[screen-reader-announcements]] § Trap
     for the same ownership rule applied to visually-hidden styles.
     Spinners convey loading state and remain animated with reduced motion, but
     slow further below. Other animations also soften: status-success lengthens,
     while status-error and badge.pulse stop. See /ui/docs/loading.html. */
  --animation-spin: spin 2s linear infinite;
}

/* Slow the same shared spinner token for reduced motion so the inline icon and
   busy outlet overlay remain consistent. */
@media (prefers-reduced-motion: reduce) {
  :root { --animation-spin: spin 3s linear infinite; }
}

/* Dark mode — 3-state (System / Light / Dark).
   No class  = follow OS via @media.
   .dark     = force dark.
   .light    = force light (wins over OS dark via :not(.light); the :root
               default color-scheme: light already covers it).
   The two blocks below are MECHANICAL rebinds of the pairs above and must
   stay identical — a media query can't share a selector list with a class,
   and the pair pattern keeps the actual values single-sourced.
   Shadow vars come from Open Props' dark set (props.shadows.dark.css).
   Demo + write-up: /ui/docs/styles-color.html. */
@media (prefers-color-scheme: dark) { /* --OSdark */
  :root:not(.light) {
    color-scheme: dark;
    --surface-1: var(--surface-1-dark);
    --surface-2: var(--surface-2-dark);
    --surface-3: var(--surface-3-dark);
    --surface-overlay: var(--surface-overlay-dark);
    --text-1: var(--text-1-dark);
    --text-2: var(--text-2-dark);
    --brand: var(--brand-dark);
    --on-brand: var(--on-brand-dark);
    --border-color: var(--border-color-dark);
    --border-strong: var(--border-strong-dark);
    --status-recorded: var(--status-recorded-dark);
    --status-mismatch: var(--status-mismatch-dark);
    --status-error: var(--status-error-dark);
    --status-syncing: var(--status-syncing-dark);
    --status-previous: var(--status-previous-dark);
    --status-previous-bg: var(--status-previous-bg-dark);
    --info: var(--info-dark);
    --success: var(--success-dark);
    --warning: var(--warning-dark);
    --error: var(--error-dark);
    --annotation: var(--annotation-dark);
    --emphasis: var(--emphasis-dark);
    --emphasis-strong: var(--emphasis-strong-dark);
    --shadow-color: 220 40% 2%;
    --shadow-strength: 25%;
  }
}
:root.dark {
  color-scheme: dark;
  --surface-1: var(--surface-1-dark);
  --surface-2: var(--surface-2-dark);
  --surface-3: var(--surface-3-dark);
  --surface-overlay: var(--surface-overlay-dark);
  --text-1: var(--text-1-dark);
  --text-2: var(--text-2-dark);
  --brand: var(--brand-dark);
  --on-brand: var(--on-brand-dark);
  --border-color: var(--border-color-dark);
  --border-strong: var(--border-strong-dark);
  --status-recorded: var(--status-recorded-dark);
  --status-mismatch: var(--status-mismatch-dark);
  --status-error: var(--status-error-dark);
  --status-syncing: var(--status-syncing-dark);
  --status-previous: var(--status-previous-dark);
  --status-previous-bg: var(--status-previous-bg-dark);
  --info: var(--info-dark);
  --success: var(--success-dark);
  --warning: var(--warning-dark);
  --error: var(--error-dark);
  --annotation: var(--annotation-dark);
  --emphasis: var(--emphasis-dark);
  --emphasis-strong: var(--emphasis-strong-dark);
  --shadow-color: 220 40% 2%;
  --shadow-strength: 25%;
}

body {
  /* Use Inter followed by the Open Props neo-grotesque stack (props.fonts.css).
Keep the named fallback fonts: a bare sans-serif depends on each machine’s font
configuration and can change line widths substantially. Against Inter, a measured
100px line was 5.9% narrower in Liberation Sans and 6.8% wider in DejaVu Sans.

The named fallbacks have Helvetica-like metrics: Roboto on Android, Helvetica Neue
on Apple, Arial Nova or Arial on Windows, and Nimbus Sans on Linux. Retain the
generic at the end for glyphs outside the shipped latin and latin-ext subsets.

font-size-adjust: from-font does not stabilize loading this way. It uses the first
available font’s aspect ratio, which is the fallback while Inter loads. Chromium
measurements showed identical widths with and without it. It instead normalizes
per-glyph substitution within an element. See
[[20260806-213700-task-body-font-fallback-stack]]. */
  font-family: var(--font-neo-grotesque);
  background: var(--surface-1);
  color: var(--text-1);
  line-height: var(--font-lineheight-3);
  margin: 0;
}

/* Soft cross-fade on theme switch (no transition on first paint). */
@media (prefers-reduced-motion: no-preference) {
  body { transition: background-color .3s var(--ease-2), color .3s var(--ease-2); }
}

html, body { overscroll-behavior-y: contain; }

/* Stable viewport width — always reserve the scrollbar lane so a page with a
   scrollbar and one without are the same width (no horizontal shift when
   navigating between short and long pages). studio.html opts out at the
   viewport and reserves the gutter on its document scroll region — see studio.html. */
html { scrollbar-gutter: stable; }

/* Safe-area padding utilities (iOS notch / home-bar clearance). */
.pb-safe { padding-bottom: calc(env(safe-area-inset-bottom) + var(--size-2)); }
.mb-safe { margin-bottom: env(safe-area-inset-bottom); }

/* ---- Header bar ---- */

body > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: nowrap;
/*  min-height: 3rem;*/
  padding: var(--size-3) var(--size-5);
  border-bottom: var(--border-size-1) solid var(--border-color);

  & > div {
    display: flex;
    align-items: center;
    gap: var(--size-2);

    /* Right-side actions div never shrinks — keeps buttons visible on narrow screens. */
    &:last-child { flex-shrink: 0; }
  }

  & ui-icon[name="logo"] {
    color: var(--brand);
    font-size: var(--font-size-4);
  }

  .header-logo-chip ui-icon[name="logo"] {
    font-size: var(--font-size-3);
  }

  .header-team-select > button {
    --_pad-y: var(--size-1);
    font-size: var(--font-size-0);
    min-width: 10rem;
  }

  & h1 {
    font-size: var(--font-size-2);
    font-weight: var(--font-weight-6);
    margin: 0;
  }
}

/* Mobile header: tighter padding, truncated title, no team badge, icon-only buttons.
   640px is this app's one mobile breakpoint — the blocks below all key off it. */
@media (max-width: 640px) {
  body > header {
    padding: var(--size-2) var(--size-3);

    & > div:first-child {
      min-width: 0;
      flex: 1;
    }

    & h1 {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    /* Team badge (non-superadmin) hidden on mobile. */
    & #team-badge { display: none; }

    /* Narrower superadmin team selector on mobile. */
    & .header-team-select > button { min-width: 7.5rem; }

    /* Icon-only buttons — hide the text label span.
       `productions.html` wraps each label in <span class="btn-label">. */
    & .btn-label { display: none; }
  }
}

.header-logo-link {
  display: contents;
  color: inherit;
  text-decoration: none;
}

.header-logo-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-7);
  height: var(--size-7);
  /* Scaled-down --radius-3 (1rem) to keep the 1/3 squircle ratio of .entry-logo at --size-7. */
  border-radius: 0.625rem;
  background: color-mix(in oklch, var(--brand) 10%, transparent);
  color: var(--brand);
}

.header-team-select {
  font-size: var(--font-size-0);
}

/* ---- Tab navigation (separate bar) ---- */

body > nav {
  border-bottom: var(--border-size-1) solid var(--border-color);
  padding-inline: var(--size-5);

  & > div {
    max-width: var(--size-lg);
    margin-inline: auto;
    display: flex;
    gap: var(--size-1);
    /* Horizontal scroll when tabs overflow (e.g. on narrow screens). */
    overflow-x: auto;
    & > * { white-space: nowrap; }
  }
}

/* Tighter nav gutter on mobile. */
@media (max-width: 640px) {
  body > nav { padding-inline: var(--size-3); }
}

/* ---- Main content ---- */

body > main {
  max-width: var(--size-lg);
  margin-inline: auto;
  padding: var(--size-fluid-4) var(--size-5);
}

/* ---- Status bar ---- */

/* Pin the statusbar to the viewport's bottom edge, out of the flow, so that showing or
   hiding a notification never moves the page under the reader. <ui-statusbar> positions
   itself nowhere: the page that mounts it owns its placement, and a page takes this one by
   mounting the bar as a child of <body> ([[notification-surfaces]]). The selector asks for
   `data-auth` because the library's own statusbar fixture loads this stylesheet for its
   semantic tokens and mounts the element at body level too, and the app's page chrome
   should not reach it. */
body[data-auth] > ui-statusbar {
  position: fixed;
  inset: auto 0 0 0;
  background: var(--surface-1);
  border-top: var(--border-size-1) solid var(--border-color);
  padding: var(--size-2) var(--size-4);
}

/* ---- Hide the page until its own dependency, the message catalog, is applied ----
   Not until the session is known: app.mjs adds `ready` once apply_static_i18n() has run, so
   a page whose server cannot be reached shows its chrome and says so rather than staying
   blank. The session gates requests and session-dependent content instead, through
   Auth.ready — see the authentication concept. */

body[data-auth] {
  opacity: 0;

  &.ready { opacity: 1; transition: opacity 0.15s }
}

/* ---- Page header ---- */

ui-outlet > * > header,
body > main > header {
  display: flex;
  justify-content: space-between;
  align-items: start;
  margin-bottom: var(--size-5);
  border: none;
  padding: 0;

  & h2 {
    font-size: var(--font-size-3);
    font-weight: var(--font-weight-6);
    margin: 0;
  }

  & p {
    font-size: var(--font-size-0);
    color: var(--text-2);
    margin: var(--size-1) 0 0;
  }

  & .header-actions {
    display: flex;
    gap: var(--size-2);
  }
}

/* On mobile: title stacks above the action button; button stretches full width. */
@media (max-width: 640px) {
  ui-outlet > * > header,
  body > main > header {
    flex-direction: column;
    align-items: stretch;
    gap: var(--size-3);
    margin-bottom: var(--size-4);

    & .header-actions { flex-direction: column; }
  }
}

/* ---- Sections ---- */

section {
  margin-block: var(--size-5);

  & > h3 {
    font-size: var(--font-size-0);
    font-weight: var(--font-weight-5);
    color: var(--text-2);
    text-transform: uppercase;
    letter-spacing: var(--font-letterspacing-3);
    margin-block: 0 var(--size-2);
  }
}

/* ---- Team groups ---- */

.team-group {
  margin-block: var(--size-6);
  padding-block-end: var(--size-4);
  border-bottom: var(--border-size-1) solid var(--border-color);

  &:last-child {
    border-bottom: none;
  }

  & > h2 {
    font-size: var(--font-size-2);
    font-weight: var(--font-weight-6);
    margin-block: 0 var(--size-3);
  }
}

/* ---- Declined assignments (assignee dashboard) ----
   Keep declined invitations at the bottom of the reader’s production list. Dim them
   while preserving access to Accept, which reverses the decline ([[invitation-answer]]).
   Hover and focus restore full opacity so the cards do not look disabled. */

.assigned-declined {
  opacity: 0.6;
  transition: opacity .2s var(--ease-2);

  &:hover,
  &:focus-within {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .assigned-declined { transition: none; }
}

/* ---- Empty states, where an app surface wants more than the shared default ----

   `.empty-state` and its defaults live in ui/ui.css, which is app-agnostic and so cannot
   name an `app-*` element; these two rules are the app's half. Why they are rules rather
   than `style=` attributes, and why neither needs `!important`:
   /ui/docs/ui-status.html § Empty state. */

/* Use a large, muted brand mark behind the productions empty-state message. */
:where(app-productions) .empty-state > ui-icon {
  font-size: var(--font-size-7);
  margin-block-end: var(--size-3);
  opacity: 0.3;
}

/* Emphasize the roster's empty title with font weight; the productions title
   uses the Literata reading font. */
:where(app-team-members) .empty-state > p:first-of-type {
  font-weight: var(--font-weight-5);
}

/* ---- Entry logo (auth.html, index.html) ---- */

.entry-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-8);
  height: var(--size-8);
  font-size: var(--font-size-4);
  background: color-mix(in oklch, var(--brand) 15%, transparent);
  border-radius: var(--radius-3);
  color: var(--brand);
  margin-bottom: var(--size-2);
}

/* ---- Member card (team members list) ---- */

.member-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--size-3);
  background: var(--surface-2);
  border: var(--border-size-1) solid var(--border-color);
  border-radius: var(--radius-3);
  padding: var(--size-3) var(--size-4);
  margin-bottom: var(--size-2);

  .card-title {
    flex-wrap: wrap;

    & > span:first-child { font-size: var(--font-size-1); font-weight: var(--font-weight-5); }
  }

  .card-meta { margin-top: 2px; }

  /* Cards with production information need the Remove button aligned with the
     member's name. Match data-crew because both a single production line and a
     multi-production disclosure add height. Keep ordinary cards centered. */
  &:has([data-crew]) { align-items: start; }

  /* Remove the disclosure's default frame and padding so its summary appears
     as a row within this card, rather than another card nested inside it. */
  .ui-disclosure {
    margin-top: var(--size-1);
    border: none;
    background: none;
    border-radius: 0;

    & > summary {
      gap: var(--size-1);
      padding: 0;
      font-size: var(--font-size-0);
      font-weight: var(--font-weight-4);
      color: var(--text-2);

      &:hover { background: none; color: var(--text-1); }
    }
  }

  .ui-disclosure-panel {
    padding: var(--size-1) 0 0;
    font-size: var(--font-size-0);

    & ul {
      margin: 0;
      padding-inline-start: var(--size-5);
      display: flex;
      flex-direction: column;
      gap: 2px;
      color: var(--text-2);
    }
  }

  /* Open the production crew dialog. Use a button because there is no destination
     URL, and style it like a link within the metadata row. */
  [data-crew] {
    padding: 0;
    border: none;
    background: none;
    font: inherit;
    color: var(--brand);
    cursor: pointer;

    &:is(:hover, :focus-visible) { text-decoration: underline; }

    /* Match the focus ring used by the shared ui.css controls. */
    &:focus-visible {
      outline: var(--border-size-2) solid var(--brand);
      outline-offset: var(--border-size-2);
    }
  }

  .btn-icon {
    flex-shrink: 0;
    width: var(--size-6);
    height: var(--size-6);
    color: var(--text-2);

    &:hover { color: var(--error); }
  }
}

/* Content-type chip is hidden on mobile — on a narrow card the type is the least
   essential meta and frees room for the rest of the row. */
@media (max-width: 640px) {
  .card-meta .ct-chip { display: none; }
}

/* ---- Language tag (production cards) ---- */

.lang-tag {
  display: inline-block;
  text-transform: uppercase;
  font-size: var(--font-size-0);
  letter-spacing: var(--font-letterspacing-3);
  font-weight: var(--font-weight-5);
  color: var(--text-2);
  background: var(--surface-3);
  padding: 1px var(--size-1);
  border-radius: var(--radius-2);
}

/* ---- Workflow step progress (assigned production cards) ----
   Earlier phases have full bars, the current phase is partially filled, and later
   phases are empty. The strip is aria-hidden; render_workflow_steps describes the
   current phase once in a visually-hidden sibling. Preserve that single accessible
   description rather than adding ARIA to individual segments ([[production-stage-model]]). */

.wf-steps {
  display: flex;
  align-items: flex-start;
  gap: var(--size-1);
  margin-block-start: var(--size-1);
  /* Grid item of .card-link (display:grid): default min-width:auto would let the
     widest label govern min-content and overflow a narrow card — shrink cleanly. */
  min-inline-size: 0;
}

.wf-step {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--size-1);
  min-inline-size: 0;

  & .wf-track {
    block-size: var(--size-1);
    border-radius: var(--radius-round);
    background: var(--surface-3);
    overflow: hidden;
  }

  & .wf-fill {
    display: block;
    block-size: 100%;
    background: var(--brand);
    transition: inline-size .2s var(--ease-2);
  }

  & small {
    display: inline-flex;
    align-items: center;
    gap: var(--size-1);
    font-size: var(--font-size-xs);
    line-height: 1;
    color: var(--text-2);
    /* One line per phase so the three cells stay vertically aligned. */
    white-space: nowrap;
  }

  /* The icon must not shrink — without `flex-shrink: 0` the glyph squashes before
     the label when a narrow cell runs out of room. */
  & small ui-icon { color: color-mix(in oklch, var(--text-2) 60%, transparent); flex-shrink: 0; }

  &[data-status="done"] small { color: color-mix(in oklch, var(--text-1) 70%, transparent); }
  &[data-status="current"] small { color: var(--brand); font-weight: var(--font-weight-5); }
  &:is([data-status="done"], [data-status="current"]) small ui-icon { color: var(--brand); }
}

/* ---- Code viewer theme ---- */

ui-code {
  --bgcolor: var(--surface-2);
  --msc-code-viewer-border-radius: var(--radius-2);
  border: var(--border-size-1) solid var(--border-color);
}

/* ---- Dialog theme ---- */

ui-dialog dialog {
  --_enter-duration: 0.4s;
  --_exit-duration: 0.2s;
  --_backdrop-duration: 0.15s;
}

/* ---- Sync-error notification (multi-line context block) ---- */

.sync-error-message {
  display: flex;
  flex-direction: column;
  gap: var(--size-1);

  & > strong { color: var(--text-1); }

  & .sync-error-context,
  & .sync-error-snippet,
  & .sync-error-reason {
    font-size: var(--font-size-0);
    color: var(--text-2);
  }

  & .sync-error-snippet { font-style: italic; }
}
