/* =========================================================================
   Breezy Design System
   Source of truth: .interface-design/styleguide.html · .interface-design/system.md
   Loaded standalone (not via Tailwind) so the system is decoupled from utility
   purging. All component classes are prefixed `.ds-`. Tokens use the canonical
   names from the styleguide.
   ========================================================================= */

/* ---------- Tokens · Light ---------- */
:root {
  --paper-page:   #F2EDE2;
  --paper-card:   #F8F4EA;
  --paper-pop:    #FDFBF3;
  --paper-inset:  #EAE3D2;
  --paper-well:   #DFD6C2;

  --ink-1: #1F1D1A;
  --ink-2: #463E33;
  --ink-3: #7C7065;
  --ink-4: #B5AC9F;

  --rule-soft:   rgba(31, 29, 26, 0.05);
  --rule:        rgba(31, 29, 26, 0.09);
  --rule-strong: rgba(31, 29, 26, 0.16);
  --rule-focus:  rgba(123, 90, 56, 0.45);

  --accent:        #7B5A38;
  --accent-on-ink: #C0945A;
  --accent-soft:   #E8DDC9;
  --accent-deep:   #4F3A24;

  --success:      #5F7448;
  --success-soft: #DCE3C9;
  --warning:      #8B6526;
  --warning-soft: #EBDFC0;
  --warning-ink:  #5A3F12;
  --danger:       #A23A24;
  --danger-hover: #862E1B;
  --danger-soft:  #EFC9B6;
  --info:         #3D5168;
  --info-soft:    #D6DCE5;

  --r-sm: 2px;
  --r-md: 4px;
  --r-lg: 6px;
  --r-xl: 10px;

  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  --sans:  "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --mono:  "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --ease-settle: cubic-bezier(0.2, 0.7, 0.2, 1);
  --dur-quick:   100ms;
  --dur-settle:  240ms;
  --dur-reveal:  180ms;
}

/* ---------- Tokens · Dark (OS-synced unless [data-theme="light"]) ---------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper-page:   #1A1815;
    --paper-card:   #232017;
    --paper-pop:    #2C2820;
    --paper-inset:  #14120F;
    --paper-well:   #0E0C09;

    --ink-1: #EDE6D5;
    --ink-2: #C9BFA8;
    --ink-3: #918674;
    --ink-4: #5A5347;

    --rule-soft:   rgba(237, 230, 213, 0.06);
    --rule:        rgba(237, 230, 213, 0.10);
    --rule-strong: rgba(237, 230, 213, 0.18);
    --rule-focus:  rgba(192, 148, 90, 0.60);

    --accent:        #C0945A;
    --accent-on-ink: #7B5A38;
    --accent-soft:   #3A2D18;
    --accent-deep:   #E8C896;

    --success:      #93AC76;
    --success-soft: #2A3520;
    --warning:      #D49E4F;
    --warning-soft: #3D2C0F;
    --warning-ink:  #F0CB85;
    --danger:       #DC7461;
    --danger-hover: #C25E48;
    --danger-soft:  #3D1F12;
    --info:         #8FA5C0;
    --info-soft:    #1F2734;
  }
}

:root[data-theme="dark"] {
  --paper-page:   #1A1815;
  --paper-card:   #232017;
  --paper-pop:    #2C2820;
  --paper-inset:  #14120F;
  --paper-well:   #0E0C09;

  --ink-1: #EDE6D5;
  --ink-2: #C9BFA8;
  --ink-3: #918674;
  --ink-4: #5A5347;

  --rule-soft:   rgba(237, 230, 213, 0.06);
  --rule:        rgba(237, 230, 213, 0.10);
  --rule-strong: rgba(237, 230, 213, 0.18);
  --rule-focus:  rgba(192, 148, 90, 0.60);

  --accent:        #C0945A;
  --accent-on-ink: #7B5A38;
  --accent-soft:   #3A2D18;
  --accent-deep:   #E8C896;

  --success:      #93AC76;
  --success-soft: #2A3520;
  --warning:      #D49E4F;
  --warning-soft: #3D2C0F;
  --warning-ink:  #F0CB85;
  --danger:       #D26B53;
  --danger-hover: #C45A40;
  --danger-soft:  #3D1F12;
  --info:         #8FA5C0;
  --info-soft:    #1F2734;
}

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


/* =========================================================================
   .ds-page · scope all component styles to a wrapper so we don't bleed into
   pre-redesign pages or the shared chrome (top nav, sidebar) until those
   migrate. Place `<div class="ds-page">…</div>` around any view content that
   should adopt the system.
   ========================================================================= */
.ds-page {
  background: var(--paper-page);
  color: var(--ink-1);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
.ds-page :focus-visible {
  outline: 2px solid var(--rule-focus);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* The shell owns page rhythm. Components no longer carry margin-bottom; the
   shell decides how blocks breathe. Nest .ds-shell-tight on a wrapper to
   bring the next sibling into a tight (16px) pair instead of the base 32px. */
.ds-shell {
  max-width: 1080px;
  margin: 0 auto;
  padding: 32px 24px 80px;
}
.ds-shell > * + * { margin-top: 32px; }
.ds-shell > .ds-shell-tight + * { margin-top: 16px; }
/* Narrower shell for reading-and-writing pages (forms, single-document
   editing) where 1080px is too wide for a single column of prose. */
.ds-shell--narrow { max-width: 720px; }

/* Two-column workspace, form on the left and a margin rail on the right.
   The rail is a sticky aside so AI feedback stays visible while the
   operator scrolls through their workflow. The metaphor is the marginalia
   in a real book: the system writes beside the work, not over it. Stacks
   below 960px so the form gets full width on tablets and phones. */
.ds-workspace {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 48px;
  align-items: start;
}
.ds-workspace__main { min-width: 0; }
.ds-workspace__rail {
  position: sticky;
  top: 32px;
  min-width: 0;
}
@media (max-width: 960px) {
  .ds-workspace { grid-template-columns: 1fr; gap: 32px; }
  .ds-workspace__rail { position: static; }
}

/* ---------- Type voices ---------- */
.ds-serif { font-family: var(--serif); font-variation-settings: "opsz" 24; font-weight: 500; letter-spacing: -0.005em; }
.ds-serif-italic { font-family: var(--serif); font-style: italic; font-variation-settings: "opsz" 24; }
.ds-italic-ai { font-style: italic; color: var(--ink-2); }
.ds-mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ---------- Page header · the label motif at full scale ---------- */
.ds-page-header {
  padding: 8px 0 32px;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 32px;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 24px;
  align-items: end;
}
.ds-page-header__lead { min-width: 0; }
.ds-page-header__kicker {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  margin: 0 0 12px 0;
  font-family: var(--sans);
}
.ds-page-header__title {
  font-family: var(--serif);
  font-variation-settings: "opsz" 60;
  font-weight: 500;
  font-size: 36px;
  letter-spacing: -0.018em;
  line-height: 1.1;
  color: var(--ink-1);
  margin: 0;
}
.ds-page-header__lede {
  font-family: var(--serif);
  font-variation-settings: "opsz" 16;
  font-style: italic;
  font-size: 16px;
  color: var(--ink-2);
  margin: 8px 0 0 0;
  line-height: 1.55;
  max-width: 60ch;
}
.ds-page-header__actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

/* ---------- Section header · smaller label motif inline with sections ---------- */
.ds-section-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  margin: 0 0 16px;
}
.ds-section-header__kicker {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
}
.ds-section-header__lede {
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-3);
}

/* Stacked variant: kicker on top, lede below. Use on settings/configure pages
   where the lede is descriptive prose rather than a tight count or status,
   and where the lede flying to the right edge would create an awkward gap
   on a wide shell. */
.ds-section-header--stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  margin-bottom: 20px;
}
.ds-section-header--stacked .ds-section-header__lede {
  max-width: 56ch;
}

/* ---------- Reassurance card · the calm anchor (page header role) ---------- */
.ds-reassurance {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  padding: 40px 36px 32px;
}
/* Quiet "you are here" line — outlet identity in the operator's writing,
   above the date kicker. Proper-case (never lowercased), serif italic so
   it reads as content rather than system chrome. */
.ds-reassurance__subtitle {
  font-family: var(--serif);
  font-variation-settings: "opsz" 16;
  font-style: italic;
  font-size: 14px;
  color: var(--ink-3);
  margin: 0 0 10px 0;
  line-height: 1.4;
}
.ds-reassurance__kicker {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--ink-3);
  margin: 0 0 14px 0;
}
.ds-reassurance__dot {
  display: inline-block;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}
.ds-reassurance__title {
  font-family: var(--serif);
  font-variation-settings: "opsz" 60;
  font-weight: 500;
  font-size: 36px;
  letter-spacing: -0.018em;
  color: var(--ink-1);
  margin: 0 0 14px 0;
  line-height: 1.05;
}
.ds-reassurance__lede {
  font-family: var(--serif);
  font-variation-settings: "opsz" 18;
  font-style: italic;
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 0;
  max-width: 56ch;
}
.ds-reassurance__lede a {
  color: var(--accent-deep);
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 1px;
}
.ds-reassurance__lede a:hover { color: var(--ink-1); }
.ds-reassurance__actions {
  margin-top: 20px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
/* Hairline rule beneath the lede; draws in left-to-right on first paint
   (the system's pen-on-page gesture, scoped to the page's defining horizontal). */
.ds-reassurance__rule {
  display: block;
  height: 1px;
  background: var(--rule);
  margin: 28px 0 24px;
  transform-origin: left center;
}
html.initial-paint .ds-reassurance__rule {
  transform: scaleX(0);
  animation: ds-reveal var(--dur-settle) var(--ease-settle) 80ms forwards;
}

/* ---------- Instrument strip · vanity stats inside the reassurance card ---------- */
.ds-instruments {
  display: flex;
  align-items: baseline;
  gap: 48px;
  flex-wrap: wrap;
}
.ds-instrument {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.ds-instrument__label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  color: var(--ink-3);
  font-weight: 500;
}
.ds-instrument__value-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.ds-instrument__value {
  font-family: var(--mono);
  font-size: 24px;
  font-weight: 500;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.018em;
  line-height: 1.1;
}
.ds-instrument__delta {
  font-size: 11px;
  color: var(--ink-3);
  font-style: italic;
}
.ds-instrument__delta--up    { color: var(--success); }
.ds-instrument__delta--down  { color: var(--danger); }
.ds-instrument__delta--first { color: var(--ink-3); }

/* ---------- Inline sparkline · 56×16, currentColor ---------- */
.ds-spark {
  display: inline-block;
  vertical-align: middle;
  width: 56px;
  height: 16px;
  color: var(--ink-3);
}
.ds-spark__line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* ---------- Credits gauge · the fuel cell, separate from vanity stats ---------- */
.ds-gauge {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 14px 22px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
}
.ds-gauge--low {
  background: var(--warning-soft);
  border-color: transparent;
}
.ds-gauge__label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  color: var(--ink-3);
  font-weight: 500;
  flex-shrink: 0;
}
.ds-gauge--low .ds-gauge__label { color: var(--warning-ink); }
.ds-gauge__value {
  font-family: var(--mono);
  font-size: 18px;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.ds-gauge--low .ds-gauge__value { color: var(--warning-ink); }
.ds-gauge__state {
  font-size: 13px;
  color: var(--ink-2);
  font-style: italic;
  margin-left: auto;
}
.ds-gauge__state a {
  color: inherit;
  font-style: normal;
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 1px;
}
.ds-gauge--low .ds-gauge__state {
  color: var(--warning-ink);
  font-style: normal;
}
.ds-gauge--low .ds-gauge__state a { border-bottom-color: var(--warning-ink); }

/* ---------- Ledger · the spine ---------- */
.ds-ledger {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.ds-ledger__head {
  display: grid;
  grid-template-columns: [time] 60px [body] 1fr [margin] auto;
  gap: 18px;
  padding: 12px 24px;
  border-bottom: 1px solid var(--rule);
  background: var(--paper-page);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-3);
  font-weight: 500;
}
.ds-ledger__row {
  display: grid;
  grid-template-columns: [time] 60px [body] 1fr [margin] auto;
  gap: 18px;
  padding: 20px 28px;
  border-bottom: 1px solid var(--rule-soft);
  align-items: center;
  transition: background 120ms ease-out;
  position: relative;
  text-decoration: none;
  color: inherit;
}
.ds-ledger__row:last-child { border-bottom: 0; }
.ds-ledger__row::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform var(--dur-reveal) var(--ease-settle);
}
.ds-ledger__row:hover { background: var(--paper-pop); }
.ds-ledger__row:hover::before { transform: scaleY(1); }
.ds-ledger__row--selected { background: var(--paper-pop); }
.ds-ledger__row--selected::before { transform: scaleY(1); }
.ds-ledger__row--selected .ds-ledger__primary { font-weight: 500; }
.ds-ledger__time {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.ds-ledger__time { position: relative; z-index: 1; }
.ds-ledger__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; position: relative; z-index: 1; }
.ds-ledger__primary {
  font-size: 14px;
  color: var(--ink-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-ledger__primary strong { font-weight: 500; }
.ds-ledger__secondary {
  font-size: 13px;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-ledger__secondary--ai { font-style: italic; color: var(--ink-2); }
/* Same italic styling as --ai but for system-generated tonal prose
   (e.g. derived setup-status sentences) that isn't AI-authored. The
   visual intent (calm considered prose) matches; the naming makes
   the AI-vs-system distinction explicit at the call site. */
.ds-ledger__secondary--prose { font-style: italic; color: var(--ink-2); }
.ds-ledger__margin {
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
  z-index: 1;
}

/* Setup ledger variant: drops the time gutter so rows read as
   [area name + italic status] [margin chip + chevron]. Used on the
   Settings page where each row reports the state of a configuration
   area rather than a time-anchored event. */
.ds-ledger--setup .ds-ledger__row {
  grid-template-columns: [body] 1fr [margin] auto;
}

/* Stretched-link pattern for ledger rows that carry secondary actions
   (e.g. delete) alongside the primary navigation. The row stays a
   <div>; the .ds-ledger__primary-link wraps the primary text and uses
   an inset ::before to make the entire row clickable. Children with
   z-index: 1 (margin column, time gutter, body, action button) stay
   above the overlay so they remain independently focusable. */
.ds-ledger__primary-link {
  text-decoration: none;
  color: var(--ink-1);
}
.ds-ledger__primary-link::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}
.ds-ledger__primary-link:hover { color: var(--ink-1); }

/* Secondary action (typically delete) inline in the margin column.
   Quiet at rest, fades in when the row is hovered or any of its
   focusable descendants are focused. Hover paints the danger soft
   ground so the destructive intent is visible the moment the cursor
   lands on it. */
.ds-ledger__action {
  background: transparent;
  border: 0;
  padding: 4px 10px;
  border-radius: var(--r-sm);
  color: var(--ink-3);
  font-family: var(--sans);
  font-size: 12px;
  text-decoration: none;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-quick) ease-out, color var(--dur-quick) ease-out, background var(--dur-quick) ease-out;
}
.ds-ledger__row:hover .ds-ledger__action,
.ds-ledger__row:focus-within .ds-ledger__action,
.ds-ledger__action:focus-visible { opacity: 1; }
.ds-ledger__action:hover { color: var(--danger); background: var(--danger-soft); }

/* Optional third body line for ledger rows. Reserved for italic AI flags
   like a customer-unhappiness or rocky-handling note. Smaller and quieter
   than the secondary line so it sits beneath the summary, not above it. */
.ds-ledger__note {
  font-family: var(--serif);
  font-style: italic;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 2px;
}

/* ---------- Chips · paper-toned, restrained, lowercase ---------- */
.ds-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  padding: 3px 9px;
  border-radius: var(--r-sm);
  text-transform: lowercase;
  font-family: var(--sans);
}
.ds-chip--ai      { background: var(--success-soft); color: var(--success); }
.ds-chip--escal   { background: var(--warning-soft); color: var(--warning-ink); }
.ds-chip--operator{ background: var(--info-soft);    color: var(--info); }
.ds-chip--voice   { background: var(--accent-soft);  color: var(--accent-deep); }
.ds-chip--awaiting{ background: var(--paper-inset);  color: var(--ink-3); }
.ds-chip--attention { background: var(--warning-soft); color: var(--warning-ink); }
.ds-chip__dot {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.85;
}
.ds-chip__duration {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: 0.02em;
}

/* Toggle variant of the chip, used for filter-bar pills. Default is
   transparent and ink-3; active picks up the paper-pop hover surface
   plus a 1px rule, mirroring the lift-on-select feel of ledger rows
   without the umber pen-stroke (filters are ambient, not commitments). */
.ds-chip--toggle {
  background: transparent;
  color: var(--ink-3);
  text-decoration: none;
  cursor: pointer;
  padding: 4px 10px;
  transition: background 120ms ease-out, color 120ms ease-out, box-shadow 120ms ease-out;
}
.ds-chip--toggle:hover { background: var(--paper-inset); color: var(--ink-2); }
.ds-chip--toggle.is-active {
  background: var(--paper-pop);
  color: var(--ink-1);
  box-shadow: inset 0 0 0 1px var(--rule);
}

/* ---------- Filter bar · pill-set above a ledger ---------- */
.ds-filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 28px;
  margin: 0 0 16px;
}
.ds-filter-bar__group {
  display: flex;
  gap: 4px;
  align-items: center;
}

/* ---------- Buttons · flat & considered ---------- */
.ds-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 18px;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.005em;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  transition: background 100ms ease-out, border-color 100ms ease-out, color 100ms ease-out;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  background: transparent;
  color: var(--ink-1);
}
.ds-btn--primary { background: var(--ink-1); color: var(--paper-page); }
.ds-btn--primary:hover { background: var(--ink-2); }
.ds-btn--primary:active { background: var(--ink-2); filter: brightness(0.88); }

.ds-btn--secondary {
  background: var(--paper-card);
  color: var(--ink-1);
  border-color: var(--rule-strong);
}
.ds-btn--secondary:hover { background: var(--paper-pop); border-color: var(--ink-3); }

.ds-btn--ghost {
  background: transparent;
  color: var(--ink-2);
  position: relative;
  padding: 9px 4px;
  border-radius: 0;
}
.ds-btn--ghost::after {
  content: "";
  position: absolute;
  left: 4px; right: 4px; bottom: 6px;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 160ms ease-out;
}
.ds-btn--ghost:hover { color: var(--ink-1); }
.ds-btn--ghost:hover::after { transform: scaleX(1); }

.ds-btn--danger { background: var(--danger); color: var(--paper-page); }
.ds-btn--danger:hover { background: var(--danger-hover); }

.ds-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.ds-btn--compact { padding: 6px 14px; font-size: 12px; }

/* Busy state · the in-flight pen mark. Driven by submit_controller, which
   adds .ds-btn--busy to the form's submit button on submit and removes it
   on turbo:submit-end. Three small things happen at once: the label shifts
   to the verb in motion (handled by submit_controller via data-busy-label),
   the ground mutes a notch, and a 1px umber pen-stroke draws and fades
   beneath the label, repeating until the request resolves. The pen lives
   inside so the layout below doesn't shift while the request runs. */
.ds-btn--busy {
  position: relative;
  cursor: progress;
  pointer-events: none;
  overflow: hidden;
}
.ds-btn--primary.ds-btn--busy { background: var(--ink-2); }
.ds-btn--secondary.ds-btn--busy { background: var(--paper-pop); }
.ds-btn--danger.ds-btn--busy { background: var(--danger-hover); }
.ds-btn--busy::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--accent-on-ink);
  transform: scaleX(0);
  transform-origin: left center;
  animation: ds-btn-busy-draw 1400ms var(--ease-settle) infinite;
}
.ds-btn--secondary.ds-btn--busy::after,
.ds-btn--ghost.ds-btn--busy::after { background: var(--accent); }
@keyframes ds-btn-busy-draw {
  0%   { transform: scaleX(0); opacity: 1; }
  60%  { transform: scaleX(1); opacity: 1; }
  85%  { transform: scaleX(1); opacity: 0; }
  100% { transform: scaleX(0); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .ds-btn--busy::after {
    animation: none;
    transform: scaleX(1);
    opacity: 0.6;
  }
}

/* ---------- Forms ---------- */
/* The pen-stroke focus indicator is the system's signature input gesture.
   A 1.5px umber stroke draws beneath the field on :focus-within via the
   Reveal primitive, replacing the SaaS box-shadow focus ring. The wrapper
   carries the pseudo-element; the input itself only changes background
   and border colour on focus. Pure CSS; no JS for the focus animation. */
.ds-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.ds-field__label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-family: var(--sans);
}
.ds-field--required .ds-field__label::after {
  content: " *";
  color: var(--ink-3);
  font-weight: 400;
}
.ds-field__wrapper {
  position: relative;
  border-radius: var(--r-sm);
}
.ds-field__wrapper::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1.5px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-settle) var(--ease-settle);
  pointer-events: none;
  border-bottom-left-radius: var(--r-sm);
  border-bottom-right-radius: var(--r-sm);
}
.ds-field__wrapper:focus-within::after { transform: scaleX(1); }
.ds-field__input,
.ds-field__textarea {
  width: 100%;
  display: block;
  background: var(--paper-inset);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: 9px 12px;
  color: var(--ink-1);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  transition: border-color var(--dur-quick) ease-out, background var(--dur-quick) ease-out;
}
.ds-field__input::placeholder,
.ds-field__textarea::placeholder { color: var(--ink-4); }
.ds-field__input:hover,
.ds-field__textarea:hover { border-color: var(--rule-strong); }
.ds-field__input:focus,
.ds-field__textarea:focus {
  outline: none;
  border-color: var(--rule-strong);
  background: var(--paper-pop);
}
.ds-field__input:disabled,
.ds-field__textarea:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  background: var(--paper-card);
}
.ds-field__input[readonly],
.ds-field__textarea[readonly] {
  color: var(--ink-3);
  background: var(--paper-card);
}
.ds-field__textarea {
  min-height: 96px;
  resize: vertical;
}
.ds-field__help {
  font-family: var(--sans);
  font-style: italic;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.5;
}
.ds-field__error {
  font-family: var(--sans);
  font-size: 12px;
  color: var(--danger);
  line-height: 1.5;
}
.ds-field--error .ds-field__wrapper::after {
  background: var(--danger);
  transform: scaleX(1);
}
.ds-field--error .ds-field__input,
.ds-field--error .ds-field__textarea {
  border-color: var(--danger);
}
.ds-field--error .ds-field__help { color: var(--danger); font-style: normal; }

/* ---------- Fieldset · groups related fields under a shared legend ---------- */
.ds-fieldset {
  border: 0;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}
.ds-fieldset__legend {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-family: var(--sans);
  padding: 0;
  margin: 0 0 4px 0;
}
.ds-fieldset__helper {
  font-family: var(--sans);
  font-style: italic;
  font-size: 12px;
  color: var(--ink-3);
  margin: -4px 0 4px 0;
  line-height: 1.5;
}
.ds-fieldset__items {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.ds-fieldset--row .ds-fieldset__items {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 28px;
}

/* ---------- Custom select · trigger button + popover panel ----------
   Native <select> can't carry the system's input vocabulary (background,
   border, padding, the pen-stroke focus). The trigger looks and behaves
   like an input; the panel lifts via lightness (paper-pop on paper-page),
   no shadow, the same flat-depth strategy as the outlet switcher popover. */
.ds-select { position: relative; }
.ds-select__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--paper-inset);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: 9px 12px;
  color: var(--ink-1);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--dur-quick) ease-out, background var(--dur-quick) ease-out;
}
.ds-select__trigger:hover { border-color: var(--rule-strong); }
.ds-select__trigger:focus {
  outline: none;
  border-color: var(--rule-strong);
  background: var(--paper-pop);
}
.ds-select__trigger[aria-expanded="true"] {
  border-color: var(--rule-strong);
  background: var(--paper-pop);
}
.ds-select__trigger:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  background: var(--paper-card);
}
.ds-select__label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ds-select__label--placeholder { color: var(--ink-4); }
.ds-select__chevron {
  width: 12px;
  height: 12px;
  color: var(--ink-3);
  flex-shrink: 0;
  transition: transform var(--dur-quick) ease-out, color var(--dur-quick) ease-out;
}
.ds-select__trigger[aria-expanded="true"] .ds-select__chevron {
  transform: rotate(180deg);
  color: var(--ink-2);
}
.ds-select__panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 30;
  background: var(--paper-pop);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: 4px;
  display: none;
  max-height: 280px;
  overflow-y: auto;
}
.ds-select__panel.is-open { display: block; }
.ds-select__option {
  display: block;
  padding: 8px 10px;
  font-size: 13px;
  color: var(--ink-2);
  border-radius: var(--r-sm);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background: transparent;
  border: 0;
  width: 100%;
  text-align: left;
  font-family: var(--sans);
}
.ds-select__option:hover,
.ds-select__option--active {
  background: var(--paper-card);
  color: var(--ink-1);
}
.ds-select__option[aria-selected="true"] {
  background: var(--paper-card);
  color: var(--ink-1);
  font-weight: 500;
}
.ds-field--error .ds-select__trigger { border-color: var(--danger); }

/* ---------- Checkbox & radio ----------
   Native input is visually hidden but focusable; the visible visual is a
   sibling span. Sibling-selector CSS draws the tick / dot. Native click
   and keyboard handling stay intact, so the controls are accessible and
   forms submit normally. */
.ds-checkbox,
.ds-radio {
  display: inline-flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
  position: relative;
  font-family: var(--sans);
  font-size: 14px;
  color: var(--ink-2);
  line-height: 1.4;
}
.ds-checkbox__input,
.ds-radio__input {
  position: absolute;
  opacity: 0;
  width: 16px;
  height: 16px;
  margin: 0;
  cursor: pointer;
}
.ds-checkbox__visual,
.ds-radio__visual {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  background: var(--paper-inset);
  border: 1px solid var(--rule-strong);
  position: relative;
  margin-top: 2px;
  transition: background var(--dur-quick) ease-out, border-color var(--dur-quick) ease-out;
}
.ds-checkbox__visual { border-radius: var(--r-sm); }
.ds-radio__visual { border-radius: 50%; }

.ds-checkbox:hover .ds-checkbox__visual,
.ds-radio:hover .ds-radio__visual { border-color: var(--ink-3); }

.ds-checkbox__input:checked + .ds-checkbox__visual {
  background: var(--ink-1);
  border-color: var(--ink-1);
}
.ds-checkbox__input:checked + .ds-checkbox__visual::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid var(--paper-page);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.ds-radio__input:checked + .ds-radio__visual {
  border-color: var(--ink-1);
}
.ds-radio__input:checked + .ds-radio__visual::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-1);
}
.ds-checkbox__input:focus-visible + .ds-checkbox__visual,
.ds-radio__input:focus-visible + .ds-radio__visual {
  outline: 2px solid var(--rule-focus);
  outline-offset: 2px;
}
.ds-checkbox__input:disabled ~ .ds-checkbox__visual,
.ds-radio__input:disabled ~ .ds-radio__visual { opacity: 0.55; }
.ds-checkbox__input:disabled ~ .ds-checkbox__label,
.ds-radio__input:disabled ~ .ds-radio__label,
.ds-checkbox--disabled,
.ds-radio--disabled { cursor: not-allowed; opacity: 0.7; }

.ds-checkbox__label,
.ds-radio__label { min-width: 0; }
.ds-checkbox__label-text,
.ds-radio__label-text { display: block; }
.ds-checkbox__help,
.ds-radio__help {
  display: block;
  font-style: italic;
  font-size: 12px;
  color: var(--ink-3);
  margin-top: 2px;
}

.ds-field--error .ds-checkbox__visual,
.ds-field--error .ds-radio__visual { border-color: var(--danger); }

/* ---------- Form rhythm ----------
   Fields stack with a quiet 24px gap so helpers don't crowd the next
   label. The actions row carries the same margin so a form reads as
   evenly-spaced top to bottom. */
.ds-field + .ds-field { margin-top: 24px; }
.ds-form__actions {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 24px;
}
/* Small italic notice line above an actions row. Used to set
   expectations about what happens after submit (e.g. "saving will
   re-check this against the AI"). */
.ds-form__notice {
  font-family: var(--sans);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-3);
  line-height: 1.55;
  margin: 24px 0 0 0;
  max-width: 56ch;
}
.ds-form__notice + .ds-form__actions { margin-top: 12px; }

/* ---------- Rich-text editor (Lexxy) ----------
   Maps Lexxy's CSS custom properties to design tokens so the editor reads
   as part of the page rather than a generic Flowbite drop-in. The editor
   stays quiet on focus (no own border highlight, no box-shadow ring); the
   pen-stroke focus indicator comes from the parent .ds-field__wrapper.
   Wrap the field with .ds-field--rich-text on the FieldComponent so the
   wrapper's pen-stroke sits flush against the editor's bottom edge. */
:root {
  --lexxy-color-canvas:          var(--paper-inset);
  --lexxy-color-text:            var(--ink-1);
  --lexxy-color-text-subtle:     var(--ink-4);
  --lexxy-color-ink-lighter:     var(--rule-strong);
  --lexxy-color-ink-lightest:    var(--paper-card);
  --lexxy-color-ink-inverted:    var(--paper-page);
  --lexxy-color-accent-dark:     var(--ink-1);
  --lexxy-color-accent-medium:   var(--ink-2);
  --lexxy-color-accent-lightest: var(--paper-pop);
  --lexxy-color-selected:        var(--paper-pop);
  --lexxy-color-selected-dark:   var(--ink-2);
  --lexxy-color-link:            var(--accent);
  --lexxy-color-code-bg:         var(--paper-well);
  --lexxy-border-color:          var(--rule);
  --lexxy-focus-ring-color:      transparent;
  --lexxy-focus-ring-offset:     0;
  --lexxy-focus-ring-size:       0;
  --lexxy-radius:                var(--r-sm);
}

/* The <lexxy-editor> is the single bordered container. The <lexxy-toolbar>
   and .lexxy-editor__content live inside it as flat regions separated by
   a hairline rule, no nested borders or radii. overflow: hidden clips the
   inner backgrounds to the wrapper's rounded corners. */
:where(lexxy-editor) {
  display: block;
  background: var(--paper-inset);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  color: var(--ink-1);
  font-family: var(--sans);
  font-size: 14px;
  overflow: hidden;
  transition: border-color var(--dur-quick) ease-out, background var(--dur-quick) ease-out;
}
:where(lexxy-editor:hover) { border-color: var(--rule-strong); }
:where(lexxy-editor:focus-within) {
  outline: none;
  border-color: var(--rule-strong);
  background: var(--paper-pop);
  box-shadow: none;
}

:where(lexxy-toolbar) {
  background: var(--paper-page);
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  margin: 0;
  padding: 6px 8px;
}
:where(.lexxy-editor__toolbar-button) {
  color: var(--ink-3);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
}
:where(.lexxy-editor__toolbar-button:hover) {
  background: var(--paper-card);
  color: var(--ink-1);
}
:where(.lexxy-editor__toolbar-button[aria-pressed="true"]) {
  background: var(--paper-card);
  color: var(--ink-1);
}

:where(.lexxy-editor__content) {
  background: transparent;
  color: var(--ink-1);
  padding: 14px 16px;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.6;
  min-height: 160px;
  border: 0;
  border-radius: 0;
  margin: 0;
}
:where(.lexxy-editor--empty) .lexxy-editor__content:not(:has(ul, ol))::before {
  color: var(--ink-4);
  opacity: 1;
  font-style: italic;
}

/* Link dialog */
:where(.lexxy-link-dialog) dialog {
  background: var(--paper-pop);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  color: var(--ink-1);
}
:where(.lexxy-link-dialog) input[type="url"] {
  background: var(--paper-inset);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  color: var(--ink-1);
  padding: 9px 12px;
  font-family: var(--sans);
  font-size: 14px;
}
:where(.lexxy-link-dialog) input[type="url"]:focus {
  outline: none;
  border-color: var(--rule-strong);
  background: var(--paper-pop);
  box-shadow: none;
}
:where(.lexxy-link-dialog) button {
  background: var(--paper-card);
  color: var(--ink-1);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-md);
  padding: 6px 14px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
}
:where(.lexxy-link-dialog) button[type="submit"] {
  background: var(--ink-1);
  color: var(--paper-page);
  border-color: var(--ink-1);
}

/* Code rendering */
:where(.lexxy-editor__content) pre,
.lexxy-content pre {
  background: var(--paper-well);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  color: var(--ink-1);
  padding: 14px 16px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 13px;
}
.lexxy-content pre code {
  background: transparent;
  border: 0;
  padding: 0;
}
.lexxy-content code {
  background: var(--paper-well);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: 1px 5px;
  font-size: 0.92em;
  font-family: var(--mono);
}

/* Code block language picker */
:where(.lexxy-code-language-picker) {
  background: var(--paper-pop);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  color: var(--ink-2);
}

/* Slash-command / prompt menu */
:where(.lexxy-prompt-menu) {
  background: var(--paper-pop);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  color: var(--ink-2);
}
:where(.lexxy-prompt-menu__item:hover) { background: var(--paper-card); }
:where(.lexxy-prompt-menu__item[aria-selected]) {
  background: var(--paper-card);
  color: var(--ink-1);
}

/* Display mode (rendered rich text outside the editor) */
.lexxy-content {
  color: var(--ink-1);
  font-family: var(--sans);
  line-height: 1.6;
}
.lexxy-content a {
  color: var(--accent);
  text-decoration: underline;
}

/* Pen-stroke offset for rich-text wrappers. The wrapper's ::after sits at
   bottom: 0 by default; the editor's own 1px bottom border lands one pixel
   below that, leaving a sliver gap. Drop the pen by 1px so it draws flush. */
.ds-field--rich-text .ds-field__wrapper::after { bottom: -1px; }

/* ---------- Empty state ---------- */
.ds-empty {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  padding: 80px 32px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.ds-empty__title {
  font-family: var(--serif);
  font-variation-settings: "opsz" 32;
  font-weight: 500;
  font-size: 24px;
  letter-spacing: -0.01em;
  color: var(--ink-1);
  margin: 0;
  line-height: 1.2;
}
.ds-empty__title em { font-style: italic; color: var(--ink-2); }
.ds-empty__body {
  font-family: var(--serif);
  font-variation-settings: "opsz" 16;
  font-style: italic;
  font-size: 15px;
  color: var(--ink-2);
  margin: 0;
  max-width: 44ch;
  line-height: 1.5;
}
.ds-empty__action {
  margin-top: 18px;
  display: flex;
  align-items: center;
  gap: 18px;
}
.ds-empty__alt {
  color: var(--ink-3);
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: color var(--dur-quick) ease-out, border-color var(--dur-quick) ease-out;
}
.ds-empty__alt:hover {
  color: var(--ink-1);
  border-bottom-color: var(--accent);
}

/* ---------- Thinking · the AI mid-thought ----------
   The Ink primitive in §Movement: italic AI text with a small umber
   dot at the trailing edge. The dot doesn't blink, it just exists,
   like the nib of a pen pressed against the page. Used wherever the
   operator is waiting for the AI to finish (workflow analysis, template
   suggestions, future async AI flows). When the AI is done, callers
   replace the markup; no JS lift-off animation in v1. */
.ds-thinking {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 96px 32px;
  gap: 18px;
}
.ds-thinking__kicker {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  margin: 0;
  font-family: var(--sans);
}
.ds-thinking__title {
  font-family: var(--serif);
  font-variation-settings: "opsz" 60;
  font-style: italic;
  font-weight: 500;
  font-size: 32px;
  color: var(--ink-1);
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.012em;
}
.ds-thinking__body {
  font-family: var(--serif);
  font-variation-settings: "opsz" 16;
  font-style: italic;
  font-size: 15px;
  color: var(--ink-3);
  margin: 0;
  max-width: 44ch;
  line-height: 1.55;
}
.ds-thinking__line {
  font-family: var(--serif);
  font-variation-settings: "opsz" 18;
  font-style: italic;
  font-size: 17px;
  color: var(--ink-3);
  margin: 0;
  line-height: 1.55;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
}
/* The phase text is the part that swaps as the AI moves through stages
   (getting the gist → checking clarity → looking for gaps → drafting
   suggestions). The transition lives on the span itself so inline-style
   opacity changes from the Stimulus controller actually fade. */
.ds-thinking__phase {
  display: inline-block;
  transition: opacity var(--dur-settle) var(--ease-settle);
}
.ds-thinking__dot {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  align-self: center;
  animation: ds-thinking-pulse 1800ms ease-in-out infinite;
}
@keyframes ds-thinking-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
  .ds-thinking__dot { animation: none; }
}
.ds-thinking__skip {
  margin-top: 24px;
  font-size: 13px;
  color: var(--ink-3);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: color var(--dur-quick) ease-out, border-color var(--dur-quick) ease-out;
}
.ds-thinking__skip:hover {
  color: var(--ink-1);
  border-bottom-color: var(--accent);
}

/* ---------- Source-list reveal · the loading state's signal of work ----------
   Empty <ul>, server-side rendered as part of _review_thinking and
   _templates_thinking. Items append via Turbo Stream broadcast as the
   corresponding job phase completes; each carries .ds-settle-entry so
   it animates in on insertion.

   The 12px hairline before each item references the pen — Breezy
   marking off what it has read so far. Restrained: ink-3, italic,
   small enough not to compete with the indicator below. */
.ds-thinking__sources {
  list-style: none;
  margin: 4px 0 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
  min-width: 240px;
}
.ds-thinking__source {
  font-family: var(--sans);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-3);
  line-height: 1.55;
  position: relative;
  padding-left: 22px;
}
.ds-thinking__source::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.65em;
  width: 12px;
  height: 1px;
  background: var(--accent);
  opacity: 0.7;
}
.ds-thinking__indicator {
  margin-top: 8px;
}

/* ---------- Review · the post-analysis destination page ----------
   Renders inside the narrow shell of /workflows/:id/analyzing once
   ai_feedback is populated. Replaces the old rail-panel feedback
   pattern; the page is now the destination, not a sidebar. */
.ds-review {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  padding: 48px 56px;
  position: relative;
}
.ds-review__head {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 24px;
}
.ds-review__head .ds-chip {
  align-self: flex-start;
}
.ds-review__kicker {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  margin: 0;
  font-family: var(--sans);
}
/* Upright Newsreader, not italic: this is a verdict, not the AI's
   marginalia voice. Italic stays reserved for the summary lede below. */
.ds-review__title {
  font-family: var(--serif);
  font-variation-settings: "opsz" 32;
  font-weight: 500;
  font-size: 26px;
  color: var(--ink-1);
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.012em;
}
.ds-review__summary {
  font-family: var(--serif);
  font-variation-settings: "opsz" 16;
  font-style: italic;
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 0 0 18px 0;
  max-width: 56ch;
}
.ds-review__section { margin-top: 18px; }
.ds-review__section-label {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  margin: 0 0 8px 0;
  font-family: var(--sans);
}
.ds-review__suggestions {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ds-review__suggestions li {
  font-family: var(--serif);
  font-variation-settings: "opsz" 16;
  font-style: italic;
  font-size: 15px;
  color: var(--ink-2);
  line-height: 1.55;
  padding: 6px 0 6px 18px;
  position: relative;
  max-width: 60ch;
}
.ds-review__suggestions li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 14px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}
/* "What we look for" rubric inside the analysed feedback panel.
   Quiet by default, summary-style. Reveals a short bulleted list of the
   AI's evaluation criteria so the customer can build a mental model of
   what makes a workflow "clear" without leaving the page. */
.ds-review__rubric { margin-top: 18px; }
.ds-review__rubric-summary {
  cursor: pointer;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  list-style: none;
}
.ds-review__rubric-summary::-webkit-details-marker { display: none; }
.ds-review__rubric-summary::before {
  content: "+ ";
  color: var(--ink-4);
}
details[open] > .ds-review__rubric-summary::before { content: "− "; }
.ds-review__rubric-summary:hover { color: var(--ink-1); }
.ds-review__rubric-list {
  list-style: none;
  margin: 12px 0 0 0;
  padding: 0;
}
.ds-review__rubric-list li {
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.55;
  padding: 4px 0 4px 14px;
  position: relative;
}
.ds-review__rubric-list li::before {
  content: "·";
  position: absolute;
  left: 4px;
  color: var(--ink-4);
}

.ds-review__enhanced {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--rule-soft);
}
.ds-review__enhanced-summary {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  font-family: var(--sans);
  list-style: none;
}
.ds-review__enhanced-summary::-webkit-details-marker { display: none; }
.ds-review__enhanced-summary:hover { color: var(--ink-1); }
.ds-review__enhanced-summary svg {
  width: 10px;
  height: 10px;
  color: var(--ink-3);
  transition: transform var(--dur-quick) ease-out;
}
details[open] .ds-review__enhanced-summary svg { transform: rotate(90deg); }
.ds-review__enhanced-body {
  margin-top: 14px;
  padding: 18px 20px;
  background: var(--paper-pop);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
}
/* "What changed" lede above the rewrite prose. The operator reads this
   before clicking "replace mine with this" so they can judge the trade
   without diffing two prose blocks mentally. */
.ds-review__enhanced-kicker {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  margin: 0 0 6px 0;
  font-family: var(--sans);
}
.ds-review__enhanced-summary-line {
  font-family: var(--sans);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-2);
  line-height: 1.55;
  margin: 0 0 16px 0;
  max-width: 56ch;
}
.ds-review__enhanced-rule {
  border: 0;
  border-top: 1px solid var(--rule-soft);
  margin: 0 0 14px 0;
}
.ds-review__enhanced-prose {
  font-size: 14px;
  color: var(--ink-2);
  line-height: 1.6;
  margin-bottom: 16px;
}
.ds-review__enhanced-prose :first-child { margin-top: 0; }
.ds-review__enhanced-prose :last-child { margin-bottom: 0; }

/* The two-button row at the bottom of the review page. Primary CTA
   sits to the right (the "leave as is" — the implicit done). Secondary
   to its left ("edit further" — the optional return-to-edit path). */
.ds-review__cta-row {
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--rule-soft);
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}

/* ---------- Details · paper-card collapsibles ----------
   Light-touch expandable panels for contextual help (writing tips,
   example workflows, advanced settings). Uses the kicker + italic body
   register; the chevron rotates on open. */
.ds-details {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: 14px 18px;
}
.ds-details + .ds-details { margin-top: 10px; }
.ds-details__summary {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  list-style: none;
}
.ds-details__summary::-webkit-details-marker { display: none; }
.ds-details__summary:hover { color: var(--ink-1); }
.ds-details__summary svg {
  width: 10px;
  height: 10px;
  color: var(--ink-4);
  transition: transform var(--dur-quick) ease-out;
}
details[open] > .ds-details__summary svg { transform: rotate(90deg); }
details[open] > .ds-details__summary { color: var(--ink-1); }
.ds-details__body { margin-top: 14px; }
.ds-details__body > * + * { margin-top: 12px; }
.ds-details__body p {
  font-family: var(--serif);
  font-variation-settings: "opsz" 16;
  font-style: italic;
  font-size: 14px;
  color: var(--ink-2);
  line-height: 1.55;
  margin: 0;
  max-width: 60ch;
}
.ds-details__example {
  background: var(--paper-pop);
  border: 1px solid var(--rule-soft);
  border-radius: var(--r-sm);
  padding: 12px 16px;
}
.ds-details__example-title {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 6px 0;
}
.ds-details__example-body {
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.55;
  margin: 0;
}

/* ---------- Footnote · peripheral signals at page bottom ---------- */
.ds-footnote {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin-top: 32px;
  padding-top: 20px;
  border-top: 1px solid var(--rule-soft);
  font-size: 13px;
  color: var(--ink-3);
}
.ds-footnote__cell { display: flex; align-items: baseline; gap: 8px; }
.ds-footnote__label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-3);
  font-weight: 500;
}
.ds-footnote a {
  color: var(--accent-deep);
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 1px;
}
.ds-footnote a:hover { color: var(--ink-1); }
@media (max-width: 720px) {
  .ds-footnote { grid-template-columns: 1fr; }
}

/* ---------- Motion · Settle, Reveal ----------
   Entry animations are gated to html.initial-paint, which is set once on
   DOMContentLoaded and removed before any Turbo render. Pages animate
   in on first app load (and on hard refresh) but never on Turbo navs,
   so cached-preview + fresh-swap doesn't fire animations twice. */
html.initial-paint .ds-settle {
  animation: ds-settle var(--dur-settle) var(--ease-settle) backwards;
}
@keyframes ds-settle {
  from { transform: translateY(4px); opacity: 0; }
  to   { transform: translateY(0);   opacity: 1; }
}

/* Reveal keyframe is consumed by component-scoped rules
   (e.g. .ds-reassurance__rule); no standalone class. */
@keyframes ds-reveal {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Entry animation for stream-appended items. Distinct from .ds-settle,
   which is gated to html.initial-paint and only plays on first app
   load. .ds-settle-entry plays unconditionally on element insertion —
   appropriate for content arriving via Turbo Stream during an active
   session (review-page source-list lines, template cards landing one
   at a time). The optional --stagger CSS variable delays the start so
   sibling items land sequentially rather than simultaneously. */
.ds-settle-entry {
  animation: ds-settle var(--dur-settle) var(--ease-settle) backwards;
  animation-delay: var(--stagger, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .ds-settle-entry { animation: none; }
}

/* Skeleton placeholder for the upcoming ledger rows. Holds the page's
   structure during the 10-15s AI draft so the operator sees Breezy
   preparing slots rather than a sparse screen. Two pulse layers run
   simultaneously: a slow opacity oscillation on the skeleton bars
   (the "writing rhythm") and a left-to-right shimmer that sweeps
   across the ledger as a whole (signals "still working"). The two
   are offset so neither dominates. */
/* The pen-stroke: a 1px umber line that draws across the full width
   of the section, pauses, fades, and redraws. Indeterminate progress
   in the brand's own gesture vocabulary — the pen on the page,
   marking each beat while the AI works. Anchored directly under the
   section header so it reads as part of the section's structure
   rather than floating decoration. Removed via Turbo Stream when
   results land. */
.ds-pen-stroke {
  display: block;
  width: 100%;
  height: 1px;
  background: var(--accent);
  transform-origin: left center;
  animation: ds-pen-stroke 2800ms ease-in-out infinite;
  margin: 12px 0 24px;
}
@keyframes ds-pen-stroke {
  0%   { transform: scaleX(0);   opacity: 1; }
  55%  { transform: scaleX(1);   opacity: 1; }
  70%  { transform: scaleX(1);   opacity: 1; }
  90%  { transform: scaleX(1);   opacity: 0; }
  100% { transform: scaleX(0);   opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .ds-pen-stroke { animation: none; opacity: 0.5; }
}
/* Skeleton ledger rows. Each row pulses on its own --pulse-delay
   so the skeletons read as a soft wave rather than a strobe. The
   row itself settles in on first paint (staggered via --stagger).
   Real templates broadcast in to replace each row by id, in place,
   so the skeleton's job is to hold the slot until the AI's answer
   lands. */
.ds-ledger__row--skeleton {
  pointer-events: none;
  animation: ds-settle var(--dur-settle) var(--ease-settle) backwards;
  animation-delay: var(--stagger, 0ms);
}
.ds-skeleton {
  display: inline-block;
  background: var(--paper-well);
  border-radius: var(--r-sm);
  height: 12px;
  vertical-align: middle;
  animation: ds-skeleton-pulse 1800ms ease-in-out infinite;
  animation-delay: var(--pulse-delay, 0ms);
}
.ds-skeleton--name {
  width: 40%;
  height: 14px;
  display: block;
  margin-bottom: 8px;
}
.ds-skeleton--line {
  width: 86%;
  height: 12px;
  display: block;
}
.ds-skeleton--summary {
  width: 70%;
  height: 18px;
}
.ds-skeleton--chip {
  width: 88px;
  height: 22px;
  border-radius: var(--r-sm);
  align-self: flex-start;
}
.ds-review__suggestions--skeleton {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ds-review__suggestions--skeleton li {
  padding: 0;
  margin: 0;
}
.ds-review__suggestions--skeleton li::before {
  content: none;
}
@keyframes ds-skeleton-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
  .ds-skeleton,
  .ds-ledger__row--skeleton { animation: none; }
}

/* The Ink primitive's pen-tip dot. The `ink` Stimulus controller
   appends one to the trailing edge of an element while it's being
   typed out character-by-character, then drops --lift on it when
   the writing completes. The dot lifts off (translateY -2px,
   opacity 0, 400ms) and is then removed from the DOM.

   Plain text only — for rich/structured content (HTML lists,
   paragraphs) splitting children into per-character would feel
   mechanical, so the controller is opt-in via data-controller="ink"
   on plain-text targets only. */
.ds-ink__dot {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  margin-left: 4px;
  vertical-align: baseline;
  transform: translateY(-1px);
}
.ds-ink__dot--lift {
  animation: ds-ink-lift 400ms var(--ease-settle) forwards;
}
@keyframes ds-ink-lift {
  from { opacity: 1; transform: translateY(-1px); }
  to   { opacity: 0; transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .ds-ink__dot, .ds-ink__dot--lift { display: none; }
}

/* =========================================================================
   Chrome · the page shell
   Lives outside .ds-page because the chrome wraps both DS pages and the
   pre-redesign content pages until each migrates. Sets the paper canvas,
   draws the sidebar, owns the mobile drawer + outlet switcher popover.
   ========================================================================= */

/* SR-only utility used inside the brand wordmark so the literal "Breezy"
   string is in the DOM for find-in-page, SEO, RSS, archival tools. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Brand wordmark · integrated ---------- */
/* Custom hand-drawn 'B' (SVG) + "reezy" in Newsreader italic + 1px umber
   pen-stroke beneath. The wordmark is the brand presence; no separate logo. */
.brand {
  font-family: var(--serif);
  font-style: italic;
  font-variation-settings: "opsz" 96, "wght" 500;
  font-size: 28px;
  letter-spacing: -0.02em;
  color: var(--ink-1);
  display: inline-flex;
  align-items: baseline;
  position: relative;
  padding-bottom: 0.55em;
  line-height: 1;
}
.brand__b {
  display: inline-block;
  width: 0.82em;
  height: 1em;
  position: relative;
  top: 0.32em;
  margin-right: 0.04em;
  flex-shrink: 0;
}
.brand__b svg {
  width: 100%;
  height: 100%;
  display: block;
  fill: currentColor;
  overflow: visible;
}
.brand__rest { display: inline-block; }
.brand::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--accent);
  transform-origin: left center;
}
/* Reveal class is added by brand-wordmark Stimulus controller on the
   first connect of an instance and removed on animationend, so the
   stroke draws once per app load — not on every Turbo navigation. */
.brand--reveal::after {
  animation: ds-reveal var(--dur-settle) var(--ease-settle) 80ms backwards;
}
.brand--small { font-size: 19px; }

/* ---------- App shell · sidebar + main ---------- */
/* The chrome wraps every outlet page. Body class .ds-app paints the canvas;
   the layout is a CSS grid with a fixed-width sidebar on the left and a
   flexible main column on the right. */
.ds-app {
  background: var(--paper-page);
  color: var(--ink-1);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.6;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
.ds-app__shell {
  display: grid;
  grid-template-columns: 240px 1fr;
  min-height: 100vh;
}
.ds-app__main {
  min-width: 0;
  padding: 24px 32px 80px;
}

/* Mobile toggle · only visible below the sidebar breakpoint. */
.ds-mobile-toggle {
  display: none;
  position: fixed;
  top: 14px;
  left: 14px;
  z-index: 60;
  width: 36px;
  height: 36px;
  align-items: center;
  justify-content: center;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  color: var(--ink-2);
  cursor: pointer;
}
.ds-mobile-toggle:hover { color: var(--ink-1); }
.ds-mobile-toggle svg { width: 18px; height: 18px; }

/* ---------- Sidebar ----------
   Cream-on-cream: same canvas as the page, separated from content by a
   single rule on the right edge. Internal hierarchy is carried by space
   alone — no horizontal dividers between brand, outlet, nav, footer.
   The wordmark's umber pen-stroke is part of the brand, not a divider. */
.ds-sidebar {
  background: var(--paper-page);
  border-right: 1px solid var(--rule-strong);
  padding: 20px 12px 16px;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.ds-sidebar__brand {
  padding: 0 4px;
  margin-bottom: 4px;
}
.ds-sidebar__brand-link {
  text-decoration: none;
  color: inherit;
}

/* Outlet identity · sits beneath the brand. With multiple outlets it's a
   click target that opens the switcher popover (full padding lives on the
   trigger so the entire row reacts to hover); with one, it's a quiet row. */
.ds-sidebar__outlet {
  position: relative;
  margin-bottom: 12px;
}
.ds-sidebar__outlet-trigger,
.ds-sidebar__outlet-static {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  font: 500 13px var(--sans);
  color: var(--ink-2);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
}
.ds-sidebar__outlet-trigger {
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: background var(--dur-quick) ease-out, color var(--dur-quick) ease-out;
}
.ds-sidebar__outlet-trigger:hover {
  color: var(--ink-1);
  background: var(--paper-card);
}
.ds-sidebar__outlet-trigger[aria-expanded="true"] {
  color: var(--ink-1);
  background: var(--paper-card);
}
.ds-sidebar__outlet-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-sidebar__outlet-chevron {
  width: 11px;
  height: 11px;
  color: var(--ink-3);
  flex-shrink: 0;
  transition: transform var(--dur-quick) ease-out;
}
.ds-sidebar__outlet-trigger[aria-expanded="true"] .ds-sidebar__outlet-chevron {
  transform: rotate(180deg);
  color: var(--ink-2);
}

/* Nav groups · today / configure / account */
.ds-sidebar__nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.ds-sidebar__group-items {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ds-sidebar__group-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-weight: 500;
  color: var(--ink-3);
  padding: 0 10px 6px;
  margin: 0;
}

.ds-sidebar__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px 8px 14px;
  border-radius: var(--r-sm);
  color: var(--ink-2);
  font-size: 13px;
  text-decoration: none;
  position: relative;
  transition: background var(--dur-quick) ease-out, color var(--dur-quick) ease-out;
}
.ds-sidebar__item:hover {
  color: var(--ink-1);
  background: var(--paper-card);
}
.ds-sidebar__item.active,
.ds-sidebar__item[aria-current="page"] {
  background: var(--paper-card);
  color: var(--ink-1);
  font-weight: 500;
}
/* The bar exists on every item but is collapsed (scaleY 0) by default;
   activating an item transitions it to scaleY 1. Using a transition rather
   than a keyframe means it fires only when the active state actually
   changes — Turbo's element-move on each render touches no property values
   and stays silent. The .initial-paint variant still uses a keyframe so
   the very first paint gets the satisfying draw-in. */
.ds-sidebar__item::before {
  content: "";
  position: absolute;
  left: 0; top: 8px; bottom: 8px;
  width: 2px;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--dur-reveal) var(--ease-settle);
}
.ds-sidebar__item.active::before,
.ds-sidebar__item[aria-current="page"]::before {
  transform: scaleY(1);
}
html.initial-paint .ds-sidebar__item.active::before,
html.initial-paint .ds-sidebar__item[aria-current="page"]::before {
  animation: ds-reveal-y var(--dur-reveal) var(--ease-settle) backwards;
}
.ds-sidebar__count {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.ds-sidebar__item[aria-current="page"] .ds-sidebar__count,
.ds-sidebar__item.active .ds-sidebar__count {
  color: var(--ink-2);
}

/* Footer · theme toggle + auxiliary actions. Separated from the nav by
   margin-top: auto and the breathing room of the empty space — no rule.
   The system spec reserves rules for moments of commitment; the footer
   sits below the nav as quiet background. */
.ds-sidebar__footer {
  margin-top: auto;
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ds-sidebar__footer-link {
  display: block;
  width: 100%;
  padding: 8px 10px;
  font: 400 13px var(--sans);
  color: var(--ink-3);
  text-align: left;
  text-decoration: none;
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: color var(--dur-quick) ease-out, background var(--dur-quick) ease-out;
}
.ds-sidebar__footer-link:hover {
  color: var(--ink-1);
  background: var(--paper-card);
}
.ds-sidebar__footer form { margin: 0; }

/* Theme toggle · auto / light / dark pill in the sidebar footer.
   Visually distinct from the footer-link rows: it conveys state, the
   links convey actions. Mono / pill / dot is a chip's vocabulary. */
.ds-theme-toggle {
  align-self: flex-start;
  margin: 0 4px 8px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px 4px 8px;
  background: var(--paper-card);
  color: var(--ink-2);
  border: 1px solid var(--rule);
  border-radius: 9999px;
  font: 500 11px var(--mono);
  letter-spacing: 0.06em;
  cursor: pointer;
  transition: background var(--dur-quick) ease-out, border-color var(--dur-quick) ease-out, color var(--dur-quick) ease-out;
}
.ds-theme-toggle:hover {
  background: var(--paper-pop);
  border-color: var(--rule-strong);
  color: var(--ink-1);
}
.ds-theme-toggle__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  display: block;
  flex-shrink: 0;
}

/* ---------- Popover · outlet switcher ----------
   Lifts via lightness (paper-pop on paper-page) and a quiet 1px rule —
   no shadow, in line with the system's flat depth strategy. */
.ds-popover {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 4px;
  z-index: 30;
  background: var(--paper-pop);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: 4px;
  display: none;
}
.ds-popover.is-open { display: block; }
.ds-popover__item {
  display: block;
  padding: 8px 10px;
  font-size: 13px;
  color: var(--ink-2);
  text-decoration: none;
  border-radius: var(--r-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ds-popover__item:hover {
  background: var(--paper-card);
  color: var(--ink-1);
}
.ds-popover__divider {
  display: block;
  height: 1px;
  background: var(--rule-soft);
  margin: 4px 8px;
}
.ds-popover__company {
  display: block;
  padding: 8px 10px;
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-3);
  text-decoration: none;
}
.ds-popover__company:hover { color: var(--ink-1); }

/* ---------- Flash ----------
   Paper-toned semantic ground, lowercase tone (§17), one quiet × dismiss.
   Sits inside the page's content well, not the chrome — but defined here
   alongside the chrome since FlashComponent is part of the layout shell. */
.ds-flash {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--r-md);
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: 16px;
}
.ds-flash--success { background: var(--success-soft); color: var(--success); }
.ds-flash--info    { background: var(--info-soft);    color: var(--info); }
.ds-flash--danger  { background: var(--danger-soft);  color: var(--danger); }
.ds-flash--warning { background: var(--warning-soft); color: var(--warning-ink); }
.ds-flash__body { flex: 1; min-width: 0; }
.ds-flash__dismiss {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  margin: -2px -4px -2px 0;
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  color: currentColor;
  opacity: 0.55;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  transition: opacity var(--dur-quick) ease-out;
}
.ds-flash__dismiss:hover { opacity: 1; }
/* Bulleted list inside a flash body. Used for validation summaries where
   we need to show several errors under a lede. Tightens the gutter since
   flash bodies sit at 13px. */
.ds-flash__list {
  margin: 8px 0 0;
  padding-left: 20px;
}
.ds-flash__list li + li { margin-top: 4px; }

/* ---------- Mobile drawer ---------- */
@media (max-width: 767px) {
  .ds-mobile-toggle { display: inline-flex; }
  .ds-app__shell { grid-template-columns: 1fr; }
  .ds-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: 260px;
    z-index: 50;
    transform: translateX(-100%);
    transition: transform var(--dur-settle) var(--ease-settle);
    box-shadow: 0 0 24px rgba(31, 29, 26, 0);
  }
  .ds-sidebar.is-open {
    transform: translateX(0);
    box-shadow: 0 0 24px rgba(31, 29, 26, 0.12);
  }
  /* Right-align the wordmark so it doesn't sit under the fixed toggle
     button (which lives in the top-left of the viewport on mobile). */
  .ds-sidebar__brand { text-align: right; }
  .ds-app__main { padding: 56px 18px 60px; }
}

/* Vertical reveal (used by the active nav bar). */
@keyframes ds-reveal-y {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* ---------- Snippet · the embed code as a copyable artifact (not a form input) ---------- */
.ds-snippet {
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--paper-well);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: 14px 14px 14px 20px;
}
.ds-snippet__field {
  flex: 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-1);
  background: transparent;
  border: 0;
  padding: 0;
  white-space: nowrap;
  overflow-x: auto;
}
.ds-snippet__field:focus {
  outline: none;
}
.ds-snippet__copy {
  flex: none;
}

/* ---------- Status · rolled-up signal as semantic-coloured prose. No dot, that
              shape is reserved for the AI's ink mark; here colour carries it. ---------- */
.ds-status {
  font-size: 13px;
  color: var(--ink-3);
  margin: 14px 0 0;
}
.ds-status--installed { color: var(--success); }
.ds-status--partial   { color: var(--warning-ink); }
.ds-status--pending   { color: var(--ink-3); }

/* ---------- Tooltip · plain ink panel revealed on hover or focus, used to
              attach short explanations to chips and other small affordances. ---------- */
.ds-tooltip {
  position: relative;
  display: inline-flex;
}
.ds-tooltip__trigger {
  cursor: help;
}
.ds-tooltip__panel {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 240px;
  font-family: var(--sans);
  font-size: 12px;
  line-height: 1.45;
  text-align: left;
  text-transform: none;
  letter-spacing: 0;
  color: var(--paper-page);
  background: var(--ink-1);
  padding: 8px 12px;
  border-radius: var(--r-md);
  opacity: 0;
  pointer-events: none;
  transition: opacity 100ms var(--ease-settle);
  z-index: 10;
}
.ds-tooltip:hover .ds-tooltip__panel,
.ds-tooltip:focus-within .ds-tooltip__panel { opacity: 1; }

/* ---------- Form rhythm · between sections of a multi-section form
              (e.g. widget page: sites + appearance under one save). ---------- */
.ds-shell > form > section + section { margin-top: 48px; }

/* ---------- Form stack · narrower column for short-content controls inside
              a wider shell. Form fields don't want to stretch to 1080px;
              this caps them at a comfortable reading-and-typing width. ---------- */
.ds-form-stack { max-width: 520px; }
.ds-form-stack > * + * { margin-top: 24px; }
.ds-form-stack .ds-field + .ds-field { margin-top: 0; }

/* ---------- Preview frame · a quiet cream surface for the launcher to sit on ---------- */
.ds-preview-frame {
  position: relative;
  background: var(--paper-page);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  height: 180px;
  max-width: 520px;
  overflow: hidden;
}
.ds-preview-frame__launcher {
  position: absolute;
  bottom: 20px;
}
.ds-preview-frame__launcher--right { right: 20px; }
.ds-preview-frame__launcher--left  { left: 20px; }

/* ---------- Sites editor · inline add/remove of widget-allowed sites ---------- */
.ds-sites { display: flex; flex-direction: column; gap: 10px; max-width: 720px; }
.ds-sites__row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 16px;
  align-items: center;
}
.ds-sites__row[data-removed="true"] { display: none; }
.ds-sites__remove {
  font-size: 12px;
  color: var(--ink-3);
  background: transparent;
  border: 0;
  padding: 6px 8px;
  cursor: pointer;
}
.ds-sites__remove:hover { color: var(--danger); }
.ds-sites__add {
  align-self: flex-start;
  margin-top: 4px;
}

/* ---------- Suggestions row · prefix to a free-text field, ghost buttons that
              reveal an umber hairline on hover (the system's signature gesture) ---------- */
.ds-suggestions {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  margin-top: 8px;
}
.ds-suggestions__label {
  font-size: 12px;
  color: var(--ink-3);
}

/* ---------- Color row · color picker swatch + hex readout ---------- */
.ds-color-row {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}
.ds-color-row__swatch {
  width: 40px;
  height: 32px;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  padding: 0;
  background: var(--paper-inset);
  cursor: pointer;
}
.ds-color-row__swatch::-webkit-color-swatch-wrapper { padding: 2px; }
.ds-color-row__swatch::-webkit-color-swatch { border: 0; border-radius: var(--r-sm); }
.ds-color-row__hex {
  font-family: var(--mono);
  text-transform: uppercase;
  width: 124px;
}

/* ---------- Preview hint · the in-line nudge that sits next to the framed preview ---------- */
.ds-preview-hint {
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-3);
  margin-top: 10px;
}
