/*
 * Design system — ported from the axon (fluyenta) design system.
 *
 * Token names and values match axon's `--dm-*` vocabulary so patterns can be
 * copied between the two apps without translation. `--dm-brand` is the only
 * deliberate divergence: it keeps this app's existing indigo instead of axon's
 * teal. Changing the brand is a one-line edit here.
 *
 * This file is loaded AFTER tailwind.css (see layouts/_head), so `.dm-*` rules
 * win over Tailwind utilities for the same property. Consequence: never pair a
 * `dm-*` class with a Tailwind utility that sets the same property — e.g.
 * `class="dm-card bg-white"` renders the token background, not bg-white.
 *
 * Rule of thumb: colors, surfaces, borders, radii and shadows come from here;
 * everything else (spacing, layout, typography size/weight) stays Tailwind.
 *
 * v1 — introduced with phase F1 of docs/auditoria-ui-2026-07-25.md.
 */

:root {
  --dm-brand: #4f46e5;
  --dm-brand-hover: #4338ca;
  --dm-brand-bg: rgba(79, 70, 229, 0.08);
  /* Separate token for brand-coloured TEXT. The brand itself is tuned to carry
     white text as a button background; as small text on a surface the dark-theme
     value only reaches 4.0:1, so links and the active sort header read from this
     lighter/darker variant instead. */
  --dm-brand-text: #4338ca;

  --dm-bg: #f8fafc;
  --dm-surface: #ffffff;
  --dm-surface-alt: #f8fafc;
  --dm-surface-hover: #f1f5f9;
  --dm-surface-solid: #ffffff;

  --dm-nav: #1e293b;
  --dm-nav-active: #0f172a;
  --dm-nav-text: #cbd5e1;
  --dm-nav-hover: #334155;

  --dm-text: #0f172a;
  --dm-text-secondary: #475569;
  --dm-text-muted: #64748b;
  --dm-text-placeholder: #94a3b8;

  --dm-border: #e2e8f0;
  --dm-border-light: #f1f5f9;
  --dm-input-bg: #ffffff;
  --dm-input-border: #cbd5e1;
  --dm-hover-bg: #f8fafc;

  /* ── Density, spacing, elevation, layers ──────────────────────────
     Declared because they were already in use as ad-hoc numbers scattered across
     the views, which is how a table row ends up 30px in one screen and 34px in
     another. Values are the ones the audit measured, not new guesses.

     Row height and cell padding come in two: a comfortable default and a compact
     one for operators who want more rows on screen. The type scale does not
     change between them — shrinking text to fit more rows trades legibility for
     density, and this tool is read for eight hours at a time. */
  --dm-row-h: 30px;
  --dm-row-h-compact: 24px;
  --dm-cell-x: 9px;
  --dm-cell-x-compact: 7px;
  /* One bar for the whole app. It was 64px, which is 39% of a 900px laptop spent
     on chrome before the first row of data. */
  --dm-bar-h: 44px;

  --dm-sp-1: 4px;
  --dm-sp-2: 8px;
  --dm-sp-3: 12px;
  --dm-sp-4: 16px;
  --dm-sp-5: 24px;
  --dm-sp-6: 32px;
  /* The same gutter on all five screens: they were 1280px-capped in two places
     and full-width in the others. */
  --dm-gutter: 14px;

  /* Three radii, which is what the app already used — now they have names. */
  --dm-r-1: 4px;
  --dm-r-2: 7px;
  --dm-r-3: 11px;

  /* Stacking order in one place. The Inspector, the sticky header and a tooltip
     have to agree on who covers whom, and three separate z-index guesses in three
     files is how they stop agreeing.

     The sticky header needs its own layer above the sticky column: naming both
     `--dm-z-sticky` left the DOM to break the tie, tbody comes after thead, and the
     customer name painted over the CUSTOMER header on every vertical scroll. */
  --dm-z-sticky: 10;
  --dm-z-sticky-head: 11;
  --dm-z-drawer: 40;
  --dm-z-overlay: 60;

  /* A monospaced face for the values that are identifiers rather than prose:
     customer ids, region codes, timestamps and keyboard hints. They line up in a
     column and stop being mistaken for names. */
  --dm-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --dm-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --dm-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
  /* Elevation as three named steps: cards, the drawer and popovers, the command
     palette. Aliased onto the existing shadows so nothing shifts visually. */
  --dm-elev-1: var(--dm-shadow-sm);
  --dm-elev-2: var(--dm-shadow-md);
  --dm-elev-3: 0 20px 25px -5px rgba(0, 0, 0, 0.12), 0 8px 10px -6px rgba(0, 0, 0, 0.08);

  /* Semantic scales. The `-text` variants are the only ones safe for small text
     on a surface: they are the AA-compliant ends of each ramp (>= 4.5:1). */
  --dm-success: #10b981;
  --dm-success-bg: #ecfdf5;
  --dm-success-text: #065f46;
  --dm-success-border: #a7f3d0;

  --dm-warning: #f59e0b;
  --dm-warning-bg: #fffbeb;
  --dm-warning-text: #92400e;
  --dm-warning-border: #fde68a;

  --dm-error: #ef4444;
  --dm-error-bg: #fef2f2;
  --dm-error-text: #991b1b;
  --dm-error-border: #fecaca;

  --dm-info: #3b82f6;
  --dm-info-bg: #eff6ff;
  --dm-info-text: #1e40af;
  --dm-info-border: #bfdbfe;

  --dm-neutral-bg: #f1f5f9;
  --dm-neutral-text: #475569;
  --dm-neutral-border: #e2e8f0;
}

html.dark {
  --dm-brand: #6366f1;
  --dm-brand-hover: #818cf8;
  --dm-brand-bg: rgba(99, 102, 241, 0.14);
  --dm-brand-text: #a5b4fc;

  --dm-bg: #0b1120;
  --dm-surface: #0f172a;
  --dm-surface-alt: #131c2e;
  --dm-surface-hover: rgba(30, 41, 59, 0.6);
  --dm-surface-solid: #0f172a;

  --dm-nav: #0f172a;
  --dm-nav-active: #1e293b;
  --dm-nav-text: #94a3b8;
  --dm-nav-hover: #1e293b;

  --dm-text: #f1f5f9;
  /* One step lighter than axon's dark ramp, deliberately. Its secondary/muted
     pair (#94a3b8 / #64748b) measures 3.58:1 for muted text on these surfaces,
     which fails AA for anything under 18px — and almost every label, hint and
     secondary figure in this app is 11-12px. This pair keeps the same three-level
     hierarchy while clearing 4.5:1: measured 9.7:1 and 5.3:1 on --dm-surface. */
  --dm-text-secondary: #cbd5e1;
  --dm-text-muted: #94a3b8;
  --dm-text-placeholder: #64748b;

  --dm-border: rgba(255, 255, 255, 0.1);
  --dm-border-light: rgba(255, 255, 255, 0.06);
  --dm-input-bg: rgba(15, 23, 42, 0.8);
  --dm-input-border: rgba(255, 255, 255, 0.14);
  --dm-hover-bg: rgba(30, 41, 59, 0.5);

  --dm-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --dm-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
  --dm-elev-1: var(--dm-shadow-sm);
  --dm-elev-2: var(--dm-shadow-md);
  --dm-elev-3: 0 20px 25px -5px rgba(0, 0, 0, 0.6), 0 8px 10px -6px rgba(0, 0, 0, 0.5);

  --dm-success: #34d399;
  --dm-success-bg: rgba(52, 211, 153, 0.12);
  --dm-success-text: #6ee7b7;
  --dm-success-border: rgba(52, 211, 153, 0.22);

  --dm-warning: #fbbf24;
  --dm-warning-bg: rgba(251, 191, 36, 0.12);
  --dm-warning-text: #fcd34d;
  --dm-warning-border: rgba(251, 191, 36, 0.22);

  --dm-error: #f87171;
  --dm-error-bg: rgba(248, 113, 113, 0.12);
  --dm-error-text: #fca5a5;
  --dm-error-border: rgba(248, 113, 113, 0.22);

  --dm-info: #60a5fa;
  --dm-info-bg: rgba(96, 165, 250, 0.12);
  --dm-info-text: #93c5fd;
  --dm-info-border: rgba(96, 165, 250, 0.22);

  --dm-neutral-bg: rgba(148, 163, 184, 0.12);
  --dm-neutral-text: #cbd5e1;
  --dm-neutral-border: rgba(255, 255, 255, 0.1);
}

/* ── Surfaces and text ─────────────────────────────────────────── */

.dm-bg { background-color: var(--dm-bg); }
.dm-surface { background-color: var(--dm-surface); }
.dm-surface-alt { background-color: var(--dm-surface-alt); }

.dm-text { color: var(--dm-text); }
.dm-text-secondary { color: var(--dm-text-secondary); }
.dm-text-muted { color: var(--dm-text-muted); }
.dm-text-brand { color: var(--dm-brand-text); }

.dm-border { border-color: var(--dm-border); }
.dm-border-light { border-color: var(--dm-border-light); }
/* Tailwind v4 implements `divide-y` as a bottom border on every child but the last,
   inside :where() so it carries no specificity — and it sets the width only, leaving the
   colour at currentColor. Colouring the v3 shape (siblings after the first) therefore
   coloured every divider except the first one, which stayed near-black: a dark rule under
   row one and light rules under the rest, in every divided list in the app. Both shapes
   are matched now. */
.dm-divide > :not(:last-child),
.dm-divide > :not([hidden]) ~ :not([hidden]) { border-color: var(--dm-border-light); }

/* Semantic text — use these instead of text-red-600 / text-amber-500 so the
   value stays AA-compliant in both themes. */
.dm-text-success { color: var(--dm-success-text); }
.dm-text-warning { color: var(--dm-warning-text); }
.dm-text-error { color: var(--dm-error-text); }
.dm-text-info { color: var(--dm-info-text); }

/* ── Cards ─────────────────────────────────────────────────────── */

.dm-card {
  background-color: var(--dm-surface);
  border: 1px solid var(--dm-border);
  border-radius: 0.75rem;
  box-shadow: var(--dm-shadow-sm);
}

.dm-card-flat {
  background-color: var(--dm-surface);
  border: 1px solid var(--dm-border);
  border-radius: 0.75rem;
}

.dm-card-inset {
  background-color: var(--dm-surface-alt);
  border: 1px solid var(--dm-border-light);
  border-radius: 0.5rem;
}

/* ── Tables ────────────────────────────────────────────────────── */

.dm-table-header {
  background-color: var(--dm-surface-alt);
  color: var(--dm-text-muted);
  border-bottom: 1px solid var(--dm-border);
}

/* Element+class on purpose: this has to outrank `.dm-col-sticky` and Tailwind's `z-10`
   without depending on which stylesheet loads last. A header cell that a data cell can
   paint over is not a header. */
th.dm-table-header {
  z-index: var(--dm-z-sticky-head);
}

.dm-table-row {
  border-bottom: 1px solid var(--dm-border-light);
  transition: background-color 0.15s ease;
}

.dm-table-row:hover { background-color: var(--dm-hover-bg); }

/* Row-level severity accent. An inset left border instead of a background tint
   so it survives :hover — the tint used to disappear exactly when pointed at. */
.dm-row-flag-error { box-shadow: inset 2px 0 0 0 var(--dm-error); }
.dm-row-flag-warning { box-shadow: inset 2px 0 0 0 var(--dm-warning); }

/* ── Accents ───────────────────────────────────────────────────── */
/* Left accent bar for cards, keyed by semantic level. */
.dm-accent-ok { box-shadow: inset 3px 0 0 0 var(--dm-success), var(--dm-shadow-sm); }
.dm-accent-warning { box-shadow: inset 3px 0 0 0 var(--dm-warning), var(--dm-shadow-sm); }
.dm-accent-error { box-shadow: inset 3px 0 0 0 var(--dm-error), var(--dm-shadow-sm); }
.dm-accent-info { box-shadow: inset 3px 0 0 0 var(--dm-info), var(--dm-shadow-sm); }
.dm-accent-neutral { box-shadow: inset 3px 0 0 0 var(--dm-border), var(--dm-shadow-sm); }

/* ── Badges ────────────────────────────────────────────────────── */

.dm-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.5rem;
  border-radius: 0.375rem;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.25rem;
  white-space: nowrap;
}

.dm-badge-neutral { background-color: var(--dm-neutral-bg); color: var(--dm-neutral-text); }
.dm-badge-success { background-color: var(--dm-success-bg); color: var(--dm-success-text); }
.dm-badge-warning { background-color: var(--dm-warning-bg); color: var(--dm-warning-text); }
.dm-badge-error { background-color: var(--dm-error-bg); color: var(--dm-error-text); }
.dm-badge-info { background-color: var(--dm-info-bg); color: var(--dm-info-text); }
.dm-badge-brand { background-color: var(--dm-brand-bg); color: var(--dm-brand); }

.dm-badge-outlined {
  border: 1px solid var(--dm-border);
  background: transparent;
  color: var(--dm-text-secondary);
}

/* A hairline ring in the badge's own hue, derived from `currentColor` so one rule
   covers all five levels in both themes. Pairs with the soft fill: the fill alone
   floats on a roomy row, and the solid variant is too loud for a status that is
   simply "normal". */
.dm-badge-ring {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent);
}

/* No fill. A coloured dot and the label, for a state that sits inside a column of
   other values — fifteen filled pills per row read as decoration, and the dot is the
   part that carries the state. */
.dm-badge-plain {
  background: transparent;
  padding-left: 0;
  padding-right: 0;
  color: var(--dm-text-secondary);
  font-weight: 500;
}

/* Solid variant for the one place a badge must dominate: the detail header. */
.dm-badge-solid-error { background-color: var(--dm-error); color: #ffffff; }
.dm-badge-solid-warning { background-color: var(--dm-warning); color: #431407; }
.dm-badge-solid-success { background-color: var(--dm-success); color: #022c22; }

.dm-dot { width: 0.375rem; height: 0.375rem; border-radius: 9999px; flex-shrink: 0; }
.dm-dot-success { background-color: var(--dm-success); }
.dm-dot-warning { background-color: var(--dm-warning); }
.dm-dot-error { background-color: var(--dm-error); }
.dm-dot-info { background-color: var(--dm-info); }
.dm-dot-neutral { background-color: var(--dm-text-muted); }

/* ── Buttons ───────────────────────────────────────────────────── */

.dm-btn-brand {
  background-color: var(--dm-brand);
  color: #ffffff;
  border: none;
  border-radius: 0.5rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.15s ease;
}
.dm-btn-brand:hover { background-color: var(--dm-brand-hover); transform: translateY(-1px); }
.dm-btn-brand:active { transform: translateY(0); }
.dm-btn-brand:disabled, .dm-btn-brand[disabled] { opacity: 0.5; cursor: not-allowed; transform: none; }

.dm-btn-secondary {
  background-color: var(--dm-surface);
  border: 1px solid var(--dm-border);
  color: var(--dm-text-secondary);
  border-radius: 0.5rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s ease;
}
.dm-btn-secondary:hover {
  background-color: var(--dm-surface-hover);
  border-color: var(--dm-brand);
  color: var(--dm-text);
  transform: translateY(-1px);
}

.dm-btn-danger {
  background-color: var(--dm-error);
  color: #ffffff;
  border: none;
  border-radius: 0.5rem;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.dm-btn-danger:hover { opacity: 0.88; }

/* ── Inputs ────────────────────────────────────────────────────── */

.dm-input {
  background-color: var(--dm-input-bg);
  border: 1px solid var(--dm-input-border);
  color: var(--dm-text);
  border-radius: 0.5rem;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.dm-input::placeholder { color: var(--dm-text-placeholder); }
.dm-input:focus {
  border-color: var(--dm-brand);
  outline: none;
  box-shadow: 0 0 0 3px var(--dm-brand-bg);
}

/* A checkbox, because a native one is grey-on-grey in the dark theme and its focus ring is the
   browser's rather than the brand's. `accent-color` does the checked state in one line and
   keeps the native control — the keyboard behaviour and the indeterminate state come free, and
   a div dressed as a checkbox loses both. Sized in rem so it tracks the row height rather than
   fighting it. */
.dm-checkbox {
  accent-color: var(--dm-brand);
  width: 0.875rem;
  height: 0.875rem;
  border-radius: var(--dm-r-1);
  cursor: pointer;
  vertical-align: middle;
}
.dm-checkbox:focus-visible {
  outline: 2px solid var(--dm-brand);
  outline-offset: 2px;
}

/* ── Navigation ────────────────────────────────────────────────── */

.dm-nav { background-color: var(--dm-nav); }
.dm-nav-link {
  color: var(--dm-nav-text);
  border-radius: 0.375rem;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.dm-nav-link:hover { background-color: var(--dm-nav-hover); color: #ffffff; }
.dm-nav-link[aria-current="page"] { background-color: var(--dm-nav-active); color: #ffffff; }

/* ── Links and focus ───────────────────────────────────────────── */

.dm-link { color: var(--dm-brand-text); }
.dm-link:hover { color: var(--dm-brand); text-decoration: underline; }

/* One visible focus treatment for the whole app. Previously only the login
   form styled focus and everything else fell back to the UA outline. */
:focus-visible {
  outline: 2px solid var(--dm-brand);
  outline-offset: 2px;
  border-radius: 0.25rem;
}

/* ── Auth shell ────────────────────────────────────────────────── */
/* Split-screen sign-in, following axon's auth pattern: a branded panel carrying
   the product story next to the form card. The panel is desktop-only — on a
   phone it would push the form below the fold for no benefit.
   The gradient derives from --dm-brand through color-mix, so re-branding the app
   re-brands this panel too instead of leaving a hardcoded indigo behind. */

.auth-shell {
  display: grid;
  min-height: 100vh;
  grid-template-columns: 1fr;
}

.auth-panel { display: none; }

@media (min-width: 1024px) {
  .auth-shell { grid-template-columns: 1.05fr 1fr; }

  .auth-panel {
    position: relative;
    display: flex;
    align-items: flex-end;
    overflow: hidden;
    isolation: isolate;
    padding: 3.5rem;
    color: #ffffff;
  }
}

.auth-panel__bg {
  position: absolute;
  inset: 0;
  z-index: -3;
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--dm-brand) 45%, #0b1120) 0%,
    var(--dm-brand) 58%,
    color-mix(in srgb, var(--dm-brand) 72%, #ffffff) 100%);
}

/* Two blurred radials keep the flat gradient from reading as a solid block. */
.auth-panel__glow {
  position: absolute;
  z-index: -2;
  border-radius: 50%;
  filter: blur(64px);
  opacity: 0.62;
  pointer-events: none;
}
.auth-panel__glow--1 {
  width: 26rem; height: 26rem; top: -7rem; right: -7rem;
  background: radial-gradient(circle, rgba(129, 140, 248, 0.8), transparent 70%);
}
.auth-panel__glow--2 {
  width: 22rem; height: 22rem; bottom: -6rem; left: -5rem;
  background: radial-gradient(circle, rgba(56, 189, 248, 0.55), transparent 70%);
}

/* Darkens the lower-left corner so the tagline keeps its contrast over the
   brightest part of the gradient. */
.auth-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(58deg, rgba(7, 13, 28, 0.62) 0%, rgba(7, 13, 28, 0.2) 34%, rgba(7, 13, 28, 0) 60%);
}

.auth-panel__content { position: relative; max-width: 30rem; }

.auth-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 0.875rem;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.22);
  box-shadow: 0 8px 22px -6px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(6px);
}

.auth-wordmark {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: #ffffff;
}

.auth-tagline {
  margin: 0 0 1.25rem;
  font-size: 2rem;
  line-height: 1.14;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: #ffffff;
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.28);
}

.auth-features {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  padding: 0;
  margin: 0;
  list-style: none;
}
.auth-features li {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.875rem;
  border-radius: 9999px;
  font-size: 0.8125rem;
  font-weight: 500;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(4px);
}
.auth-features .dm-dot { background: rgba(255, 255, 255, 0.9); }

/* Form side */
.auth-form-side {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 3rem 1.5rem;
}
@media (min-width: 1024px) {
  .auth-form-side { padding: 3.5rem; }
}

/* ── Meters ────────────────────────────────────────────────────── */

.dm-meter {
  background-color: var(--dm-neutral-bg);
  border-radius: 9999px;
  overflow: hidden;
}
.dm-meter-fill { height: 100%; border-radius: 9999px; transition: width 0.3s ease; }
.dm-meter-fill-success { background-color: var(--dm-success); }
.dm-meter-fill-warning { background-color: var(--dm-warning); }
.dm-meter-fill-error { background-color: var(--dm-error); }
.dm-meter-fill-brand { background-color: var(--dm-brand); }

/* Fill colours for server-rendered SVG. `currentColor` plus a text colour would work
   for strokes, but a rect needs the fill to follow the theme too. */
.dm-fill-success { color: var(--dm-success); }
.dm-fill-error { color: var(--dm-error); }
.dm-fill-warning { color: var(--dm-warning); }

/* The row the Inspector is showing. A fourth row state next to hover, focus and the
   severity rail, so it has to be quiet enough not to compete with them. */
.dm-row-selected { background: var(--dm-surface-2); box-shadow: inset 2px 0 0 0 var(--dm-brand); }

/* ── Density ──────────────────────────────────────────────────────────
   The table reads its row height and cell padding from tokens, so the compact
   mode is one class on a wrapper instead of a second set of utilities on every
   cell. Type size is deliberately untouched between modes. */
.dm-dense td,
.dm-dense th { padding-top: 0; padding-bottom: 0; height: var(--dm-row-h); padding-left: var(--dm-cell-x); padding-right: var(--dm-cell-x); }
.dm-dense-compact td,
.dm-dense-compact th { height: var(--dm-row-h-compact); padding-left: var(--dm-cell-x-compact); padding-right: var(--dm-cell-x-compact); }

/* Identifiers, not prose. */
.dm-mono { font-family: var(--dm-mono); font-variant-numeric: tabular-nums; }

/* Layers, so the drawer, the sticky header and a popover cannot disagree. */
/* The tooltip. It lived as inline hex in tooltip_controller.js — the one component outside the
   token system, and therefore the one that stayed dark in the light theme. `--dm-surface-solid`
   because a tooltip has to be opaque over whatever it covers. */
/* The plate behind a customer logo. A logo is somebody else's artwork and most of them are
   transparent PNGs drawn for a light background, so it needs a light plate — but written as
   `bg-white dark:bg-transparent` it was the last hardcoded colour in app/views, which made the
   grep that guards this rule return two hits instead of nothing. Transparent in dark mode
   because a white card behind a logo is brighter than anything else on that screen. */
.dm-logo-plate { background-color: #ffffff; }
@media (prefers-color-scheme: dark) {
  .dm-logo-plate { background-color: transparent; }
}
:root[data-theme="dark"] .dm-logo-plate { background-color: transparent; }
:root[data-theme="light"] .dm-logo-plate { background-color: #ffffff; }

.dm-tooltip {
  z-index: var(--dm-z-overlay);
  max-width: 300px;
  padding: 6px 10px;
  background-color: var(--dm-surface-solid);
  color: var(--dm-text);
  border: 1px solid var(--dm-border);
  border-radius: var(--dm-r-2);
  box-shadow: var(--dm-elev-2);
  font-size: 12px;
  line-height: 1.4;
  white-space: pre-line;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.dm-layer-sticky { z-index: var(--dm-z-sticky); }
.dm-layer-drawer { z-index: var(--dm-z-drawer); }
.dm-layer-overlay { z-index: var(--dm-z-overlay); }

/* Elevation by name. */
.dm-elev-1 { box-shadow: var(--dm-elev-1); }
.dm-elev-2 { box-shadow: var(--dm-elev-2); }
.dm-elev-3 { box-shadow: var(--dm-elev-3); }

/* ── Command palette ──────────────────────────────────────────────────
   The active row is marked by class rather than by :focus, because the focus stays in
   the input while the arrow keys move the selection. */
.dm-palette-item:hover,
.dm-palette-active { background-color: var(--dm-hover-bg); }
.dm-palette-active { box-shadow: inset 2px 0 0 0 var(--dm-brand); }

/* The customer name stays put when the table scrolls sideways: it is the only reference
   that matters, and losing it made the right-hand columns unreadable. */
.dm-col-sticky { position: sticky; left: 0; z-index: var(--dm-z-sticky); background-color: var(--dm-surface); }
.dm-table-row:hover .dm-col-sticky { background-color: var(--dm-hover-bg); }

/* ── Overlays ─────────────────────────────────────────────────────────
   `hidden` + `sm:flex` does not work: both set `display`, the responsive variant wins
   from 640px up, and the dialog is permanently visible while the JS toggles a class
   that changes nothing. Display lives here and JS only toggles `is-open`. */
.dm-overlay { display: none; }
.dm-overlay.is-open { display: flex; }

/* ── Avatars ──────────────────────────────────────────────────────────
   A logo when the account has one, its initials when it does not. Never an empty
   square: a blank avatar inside a column of avatars reads as a broken image, and most
   rows of a fresh master have no logo yet.

   The monogram hue comes from the row as `--dm-mono-hue`, derived from
   (region, customer_id), so an account keeps its colour on every screen and between
   sessions without anybody picking one. */
.dm-avatar {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--dm-r-2);
  background-color: var(--dm-surface-alt);
  border: 1px solid var(--dm-border);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--dm-text-secondary);
}

.dm-avatar-mono {
  background-color: hsl(var(--dm-mono-hue, 240) 62% 94%);
  border-color: hsl(var(--dm-mono-hue, 240) 45% 82%);
  color: hsl(var(--dm-mono-hue, 240) 55% 32%);
}

html.dark .dm-avatar-mono {
  background-color: hsl(var(--dm-mono-hue, 240) 40% 22%);
  border-color: hsl(var(--dm-mono-hue, 240) 35% 34%);
  color: hsl(var(--dm-mono-hue, 240) 70% 84%);
}

/* The initials sit behind the image and only become visible if the image fails, which
   the img's onerror flags on the wrapper. A dead logo URL in the master is normal —
   companies redesign their sites — and it must not leave the browser's broken-image
   glyph in the middle of a table. */
.dm-avatar-fallback {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.dm-avatar-failed img { display: none; }
.dm-avatar-failed .dm-avatar-fallback { z-index: 0; }

/* ── Drawer ───────────────────────────────────────────────────────────
   The Inspector's shell. Still `role="complementary"` and still not a modal — the
   table has to stay operable behind it while j/k walk the list — but it now reads as a
   panel rather than as a card that happens to be pinned to the edge: a banner in the
   brand hue, the account's logo breaking that banner's baseline, and a description
   list of label/value rows underneath. */
.dm-drawer {
  background-color: var(--dm-surface);
  border-left: 1px solid var(--dm-border);
}

.dm-drawer-banner {
  height: 4.5rem;
  background: linear-gradient(120deg,
    color-mix(in srgb, var(--dm-brand) 78%, #0b1120) 0%,
    var(--dm-brand) 100%);
}

/* Label on the left, value on the right, one hairline between rows — the description
   list shared by the drawer and the account detail page. The Inspector used to answer
   "what is this number" with a three-column grid that reflowed at every width; a
   two-column list does not reflow and reads top to bottom. */
.dm-dl-row {
  display: grid;
  grid-template-columns: minmax(6rem, 38%) 1fr;
  gap: var(--dm-sp-3);
  padding: var(--dm-sp-2) 0;
  border-top: 1px solid var(--dm-border-light);
  font-size: 0.8125rem;
}
.dm-dl-row > dt { color: var(--dm-text-muted); }
.dm-dl-row > dd { color: var(--dm-text); overflow-wrap: anywhere; }

/* ── Account cards ────────────────────────────────────────────────────
   The optional card view. Fifteen columns are the right shape for comparing 133 rows
   and the wrong one for reading one account, so the same data gets a second form: logo
   and name in a header, then the four or five facts that matter as label/value rows.
   Which form is on screen is a URL param like every other bit of monitor state. */
.dm-account-card {
  display: flex;
  flex-direction: column;
  background-color: var(--dm-surface);
  border: 1px solid var(--dm-border);
  border-radius: var(--dm-r-3);
  box-shadow: var(--dm-elev-1);
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.dm-account-card:hover { box-shadow: var(--dm-elev-2); border-color: var(--dm-brand); }
.dm-account-card[aria-current="true"] { box-shadow: inset 3px 0 0 0 var(--dm-brand), var(--dm-elev-2); }

.dm-account-card__head {
  display: flex;
  align-items: center;
  gap: var(--dm-sp-3);
  padding: var(--dm-sp-3);
  background-color: var(--dm-surface-alt);
  border-bottom: 1px solid var(--dm-border-light);
  border-radius: var(--dm-r-3) var(--dm-r-3) 0 0;
}

.dm-account-card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--dm-sp-3);
  padding: 0.375rem var(--dm-sp-3);
  font-size: 0.8125rem;
}
.dm-account-card__row + .dm-account-card__row { border-top: 1px solid var(--dm-border-light); }
.dm-account-card__row > dt { color: var(--dm-text-muted); }

/* ── Stat band ────────────────────────────────────────────────────────
   One cell of `shared/_stat_band`. The padding lives here rather than in utilities so
   every band in the app measures the same, and the hover state is a token — a cell that
   is a link has to look like one before it is clicked.

   py at 0.625rem, not the reference pattern's py-10: on this app's screens the figures
   frame the work below them and must not push it under the fold, which is the same
   measurement that took the nav bar from 64px to 44. */
.dm-band-cell {
  padding: 0.625rem 1rem;
  transition: background-color 0.15s ease;
}
a.dm-band-cell:hover { background-color: var(--dm-hover-bg); }

/* A missing field, named. Quiet on purpose: on a fresh master most rows are missing
   most fields, and a red pill per gap would turn the screen into a wall of alarms. */
.dm-chip-missing {
  display: inline-block;
  padding: 0 0.375rem;
  border-radius: var(--dm-r-1);
  border: 1px dashed var(--dm-border);
  background: transparent;
  color: var(--dm-text-muted);
  font-size: 0.625rem;
  line-height: 1.125rem;
  white-space: nowrap;
}

/* ── Dropdown ─────────────────────────────────────────────────────────
   The navbar's account menu and the mobile navigation. Same `is-open` convention as
   the overlay above, for the same reason: display lives in CSS, JS only toggles the
   class. */
.dm-dropdown { position: relative; }
.dm-dropdown-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.375rem);
  display: none;
  min-width: 12rem;
  padding: 0.25rem;
  background-color: var(--dm-surface-solid);
  border: 1px solid var(--dm-border);
  border-radius: var(--dm-r-2);
  box-shadow: var(--dm-elev-3);
  z-index: var(--dm-z-overlay);
}
.dm-dropdown-panel.is-open { display: block; }

.dm-dropdown-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.375rem 0.5rem;
  border-radius: var(--dm-r-1);
  font-size: 0.8125rem;
  color: var(--dm-text-secondary);
  text-align: left;
}
.dm-dropdown-item:hover { background-color: var(--dm-hover-bg); color: var(--dm-text); }

/* The brand mark. A wordmark alone left the bar's left edge indistinguishable from a
   paragraph of text at 44px tall. */
.dm-brand-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.625rem;
  height: 1.625rem;
  border-radius: var(--dm-r-2);
  background: linear-gradient(140deg, var(--dm-brand) 0%, color-mix(in srgb, var(--dm-brand) 55%, #38bdf8) 100%);
  color: #ffffff;
  font-size: 0.625rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

/* Mobile navigation. Stacked links under the bar, shown only while the hamburger is
   open — the bar previously rendered every link in one row at every width, which on a
   phone pushed the account email and the log-out button off screen. */
.dm-nav-mobile { display: none; border-top: 1px solid rgba(255, 255, 255, 0.08); }
.dm-nav-mobile.is-open { display: block; }
@media (min-width: 768px) {
  .dm-nav-mobile.is-open { display: none; }
}

/* ══ Feedback layer ════════════════════════════════════════════════════════════
   Dropping a point on a screen, and everything that hangs off it.

   Violet, and it is the one hue this file adds that is not already in the palette. The
   reason is that this layer draws **on top of** every other screen: its dots sit over
   tables that already use green, amber and red for health, and over `--dm-brand` indigo
   for anything actionable. A colour borrowed from any of those would read as one more
   number on the row. Violet says "different layer" without having to be read.

   `--dm-fb-done` is its own green rather than `--dm-success`, because a resolved point and
   an in-progress one are exactly the two that have to be told apart at a glance in the
   inbox, and success-green is already spoken for by the health columns. */
:root {
  --dm-fb: #7c3aed;
  --dm-fb-on: #ffffff;
  --dm-fb-soft: color-mix(in srgb, #7c3aed 10%, transparent);
  --dm-fb-ring: color-mix(in srgb, #7c3aed 26%, transparent);
  --dm-fb-done: #15803d;

  /* The layer's own stack, above `--dm-z-overlay` (60) on purpose. Annotating is a MODE:
     while it is on, every click belongs to it, including clicks on the command palette and
     on the Inspector — those are screens like any other and the whole point of FB-style
     feedback is being able to annotate them. The four are named rather than spelled as
     numbers for the same reason the rest of the layers are: the veil, the dots, the button
     and the drawer have to agree about who covers whom, and the drawer has to be above the
     veil or the mode could not be typed into. */
  --dm-z-fb-catcher: 84;
  --dm-z-fb-pins: 85;
  --dm-z-fb-btn: 86;
  --dm-z-fb-drawer: 87;
}

html.dark {
  /* In dark, pure violet dulls against the navy background and white text on it loses
     contrast. The hue goes up and the number inside the dot turns dark. */
  --dm-fb: #a78bfa;
  --dm-fb-on: #1e1035;
  --dm-fb-soft: color-mix(in srgb, #a78bfa 12%, transparent);
  --dm-fb-ring: color-mix(in srgb, #a78bfa 30%, transparent);
  --dm-fb-done: #4ade80;
}

/* ── The veil that catches the click ─────────────────────────────────
   It is what makes dropping a point not press the button underneath. It covers the whole
   window and only exists with the mode on: without it, every button in the app would
   create a point when used. */
.dm-fb__catcher {
  position: fixed;
  inset: 0;
  z-index: var(--dm-z-fb-catcher);
  cursor: crosshair;
  /* The dot grid says "you are in annotate mode" without a bar covering anything. Very
     faint: above this there is a screen that has to stay readable to be annotated. */
  background-image: radial-gradient(circle at 1px 1px, var(--dm-fb-ring) 1px, transparent 0);
  background-size: 18px 18px;
}

/* ── The points ──────────────────────────────────────────────────────
   The dot layer does NOT catch clicks: `pointer-events: none` on the container and `auto`
   on each dot. Without that it would cover the whole screen with the mode off, when there
   is nothing to catch. */
.dm-fb__pins {
  position: fixed;
  inset: 0;
  z-index: var(--dm-z-fb-pins);
  pointer-events: none;
}

.dm-fb__pin {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  font-family: var(--dm-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--dm-fb-on);
  background-color: var(--dm-fb);
  border: 2px solid var(--dm-surface-solid);
  /* Round except for one corner: it is the shape of a map marker, and it makes the dot
     distinguishable from the round badges and counters the app already carries. */
  border-radius: 50% 50% 50% 0.1875rem;
  box-shadow: 0 2px 8px -2px var(--dm-fb-ring);
  cursor: pointer;
}

.dm-fb__pin:hover { transform: translate(-50%, -50%) scale(1.12); }

.dm-fb__pin:focus-visible {
  outline: 2px solid var(--dm-text);
  outline-offset: 2px;
}

/* The selected one, with a ring. With the drawer open, without this you cannot tell which
   of six you are reading. */
.dm-fb__pin.is-on {
  box-shadow: 0 0 0 4px var(--dm-fb-ring), 0 2px 8px -2px var(--dm-fb-ring);
}

/* Resolved or dismissed: outlined. It is still there — the thread is the record of why
   something changed — but it stops asking for attention. */
.dm-fb__pin.is-closed {
  color: var(--dm-fb);
  background-color: var(--dm-surface-solid);
  border-color: var(--dm-fb);
}

@media (prefers-reduced-motion: no-preference) {
  .dm-fb__pin { transition: transform 0.12s ease-out; }
}

/* ── The button ──────────────────────────────────────────────────────
   Floating, bottom right, and it is the only floating control this app has — so unlike the
   tool this was ported from there is no stack of FABs to align with.

   The text is `--dm-text` and the ICON is what carries the accent. The first version put
   the text in `--dm-text-muted` on a light surface, aiming for "discreet with the mode
   off": #94a3b8 on white is 2.6:1, and AA asks 4.5:1 for text. Discreet is not the same as
   invisible, and a button nobody finds turns the whole layer into code nobody uses. */
.dm-fb__btn {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: var(--dm-z-fb-btn);
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  padding: 0.5rem 0.8125rem;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--dm-text);
  background-color: var(--dm-surface-solid);
  border: 1px solid var(--dm-fb-ring);
  border-radius: 999px;
  box-shadow: 0 4px 14px -4px rgb(15 23 42 / 22%), 0 0 0 1px rgb(15 23 42 / 4%);
  cursor: pointer;
}

.dm-fb__btn svg { width: 0.9375rem; height: 0.9375rem; color: var(--dm-fb); }

.dm-fb__btn:hover {
  border-color: var(--dm-fb);
  background-color: var(--dm-fb-soft);
}

.dm-fb__btn:focus-visible {
  outline: 2px solid var(--dm-fb);
  outline-offset: 2px;
}

/* On: solid violet. Being in a mode that catches EVERY click on the screen has to be
   visible without looking for it, because until it is switched off the app does not
   respond the way it always does. */
.dm-fb.is-on .dm-fb__btn {
  color: var(--dm-fb-on);
  background-color: var(--dm-fb);
  border-color: var(--dm-fb);
  box-shadow: 0 6px 20px -6px var(--dm-fb), 0 0 0 3px var(--dm-fb-ring);
}

.dm-fb.is-on .dm-fb__btn svg { color: currentColor; }

/* The count, in solid violet. It is what says "there is already something written here"
   from across the room, so it cannot wear the grey of any other counter. */
.dm-fb__count {
  font-family: var(--dm-mono);
  font-size: 0.625rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 0.0625rem 0.3125rem;
  border-radius: 999px;
  color: var(--dm-fb-on);
  background-color: var(--dm-fb);
}

.dm-fb.is-on .dm-fb__count { background-color: rgb(255 255 255 / 24%); }

/* The network failed and the layer says so, quietly. Anything louder would be an error
   screen inside an annotation tool. */
.dm-fb[data-offline] .dm-fb__btn {
  border-color: var(--dm-error);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--dm-error) 30%, transparent);
}

/* On narrow screens the text goes and the icon plus the count stay: the button cannot eat
   the width of a phone screen. */
@media (max-width: 640px) {
  .dm-fb__btn-text { display: none; }
}

/* ── The point's drawer ──────────────────────────────────────────────
   A drawer and not a centred modal: a modal covers the screen being annotated, and not
   losing sight of it is half the reason the layer exists. `.dm-drawer` in this file is
   surface and border only, so the geometry lives here.

   Above the veil, or with the mode on it could not be typed into. */
.dm-fbd {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: var(--dm-z-fb-drawer);
  display: flex;
  width: min(34rem, 100vw);
  flex-direction: column;
  background-color: var(--dm-surface-solid);
  box-shadow: -12px 0 32px -12px rgb(0 0 0 / 25%);
}

@media (prefers-reduced-motion: no-preference) {
  .dm-fbd { animation: dm-fbd-in 0.18s ease-out; }

  @keyframes dm-fbd-in {
    from { transform: translateX(1.5rem); opacity: 0; }
    to { transform: none; opacity: 1; }
  }
}

.dm-fbd__head {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 1rem;
  padding: 1rem 1.25rem 0.875rem;
  border-bottom: 1px solid var(--dm-border);
}

.dm-fbd__toprow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
}

.dm-fbd__code {
  font-family: var(--dm-mono);
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--dm-fb);
}

.dm-fbd__title {
  margin: 0.375rem 0 0;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.012em;
  color: var(--dm-text);
}

.dm-fbd__sub {
  margin: 0.125rem 0 0;
  font-size: 0.75rem;
  color: var(--dm-text-muted);
  overflow-wrap: anywhere;
}

.dm-fbd__close {
  flex-shrink: 0;
  margin-left: auto;
  padding: 0.25rem;
  color: var(--dm-text-muted);
  border-radius: 0.375rem;
}

.dm-fbd__close:hover { color: var(--dm-text); background-color: var(--dm-hover-bg); }
.dm-fbd__close svg { width: 1.125rem; height: 1.125rem; }

/* Own scroll: it is half the sense of a drawer. The screen behind keeps its position
   because it has not moved, and a long thread is read without dragging the whole page. */
.dm-fbd__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1rem 1.25rem 2rem;
}

.dm-fbd__legend {
  margin: 0 0 0.625rem;
  font-family: var(--dm-mono);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--dm-text-muted);
}

/* The two-step warnings. A strip across the drawer rather than a browser `confirm()`,
   which interrupts, gets accepted with Enter without being read, and cannot be tested. */
.dm-fbd__dirty {
  margin: 0;
  padding: 0.5rem 1.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--dm-warning);
  background-color: color-mix(in srgb, var(--dm-warning) 12%, transparent);
  border-bottom: 1px solid var(--dm-border);
}

/* Deleting reuses that strip and only changes its colour: what is lost here does not come
   back, so it cannot read the same as "you have something unsaved". */
.dm-fbd__danger {
  color: var(--dm-error);
  background-color: color-mix(in srgb, var(--dm-error) 12%, transparent);
}

/* The anchor, monospaced and unadorned. Shown on purpose: whoever drops the point sees
   which element it landed on, so a misplaced click is caught immediately instead of three
   days later by whoever reads it. */
.dm-fbd__anchor {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  margin: 0.75rem 1.25rem 0;
  padding: 0.5rem 0.625rem;
  font-family: var(--dm-mono);
  font-size: 0.625rem;
  line-height: 1.5;
  color: var(--dm-text-secondary);
  background-color: var(--dm-fb-soft);
  border: 1px solid var(--dm-fb-ring);
  border-radius: var(--dm-r-1);
  overflow-wrap: anywhere;
}

.dm-fbd__anchor b { color: var(--dm-fb); font-weight: 700; }

/* The lost anchor. It gets said, and in the colour of something that needs attention. */
.dm-fbd__lost, .dm-fbd__lost b { color: var(--dm-warning); }

.dm-fbd__error {
  margin: 0 0 0.75rem;
  padding: 0.5rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--dm-error-text);
  background-color: var(--dm-error-bg);
  border: 1px solid var(--dm-error-border);
  border-radius: var(--dm-r-1);
}

/* ── The thread ──────────────────────────────────────────────────── */

.dm-fbd__thread { display: flex; flex-direction: column; gap: 0.5rem; }

.dm-fbd__notes {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.dm-fbd__note { display: flex; gap: 0.5rem; }

.dm-fbd__who {
  flex-shrink: 0;
  width: 1.5rem;
  height: 1.5rem;
  font-size: 0.5625rem;
}

.dm-fbd__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.375rem;
  margin: 0;
  font-size: 0.625rem;
  color: var(--dm-text-muted);
}

.dm-fbd__meta b { font-size: 0.6875rem; font-weight: 600; color: var(--dm-text); }

/* The note's own text. Named `__text` and not `__body`, which is the drawer's scrolling
   container: one name for both meant a note inherited `flex`, `overflow-y: auto` and the
   drawer's 2rem of padding. */
.dm-fbd__text p {
  margin: 0.125rem 0 0;
  font-size: 0.78125rem;
  line-height: 1.5;
  color: var(--dm-text-secondary);
  overflow-wrap: anywhere;
}

/* System lines: indented and monospaced, not one more message. They are what happened, not
   what somebody said, and without the distinction a state change reads as if a person had
   typed the word "resolved". */
.dm-fbd__event {
  padding-left: 2rem;
  font-family: var(--dm-mono);
  font-size: 0.625rem;
  color: var(--dm-text-muted);
  overflow-wrap: anywhere;
}

.dm-fbd__empty { font-size: 0.78125rem; color: var(--dm-text-muted); }

/* ── Writing, deciding, deleting ─────────────────────────────────── */

.dm-fbd__compose,
.dm-fbd__decide,
.dm-fbd__remove {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--dm-border);
}

.dm-fbd__input {
  width: 100%;
  padding: 0.5rem 0.625rem;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--dm-text);
  background-color: var(--dm-input-bg);
  border: 1px solid var(--dm-input-border);
  border-radius: var(--dm-r-2);
  resize: vertical;
}

.dm-fbd__input::placeholder { color: var(--dm-text-placeholder); }
.dm-fbd__input:focus-visible { outline: 2px solid var(--dm-fb); outline-offset: -1px; }

.dm-fbd__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.dm-fbd__hint { margin-top: 0.375rem; font-size: 0.6875rem; color: var(--dm-text-muted); }

/* Deleting exists for MISTAKES — the point dropped by accident, the duplicate — not for
   "we are not going to do this", which is the `dismissed` state. Hence the button goes at
   the bottom, outlined and grey: it is the layer's emergency exit, not one of its actions.
   A red button here would invite using it to close real feedback. */
.dm-fbd__remove { display: flex; flex-direction: column; gap: 0.3125rem; }

.dm-fbd__delete {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--dm-text-muted);
}

.dm-fbd__delete:hover {
  color: var(--dm-error);
  border-color: color-mix(in srgb, var(--dm-error) 40%, transparent);
}

.dm-fbd__delete svg { width: 0.875rem; height: 0.875rem; }

/* ── Attachments ─────────────────────────────────────────────────── */

.dm-fbd__files {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.5rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--dm-text-muted);
  background-color: var(--dm-surface-alt);
  border: 1px dashed var(--dm-border);
  border-radius: var(--dm-r-1);
  cursor: pointer;
}

.dm-fbd__files:hover { color: var(--dm-fb); border-color: var(--dm-fb-ring); }
.dm-fbd__files:focus-within { outline: 2px solid var(--dm-fb); outline-offset: 1px; }

/* The native file input is hidden and the label is what gets clicked: `<input type=file>`
   cannot be styled and its default rendering carries a browser-language button label in
   the middle of a trilingual interface. */
.dm-fbd__file-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.dm-fbd__files-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin: 0.375rem 0 0;
  padding: 0;
  list-style: none;
}

.dm-fbd__thumb {
  display: block;
  max-width: 7rem;
  border: 1px solid var(--dm-border);
  border-radius: var(--dm-r-1);
  overflow: hidden;
}

.dm-fbd__thumb img { display: block; max-height: 5rem; width: auto; }

.dm-fbd__doc {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  max-width: 14rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.6875rem;
  color: var(--dm-text-secondary);
  background-color: var(--dm-surface-alt);
  border: 1px solid var(--dm-border);
  border-radius: var(--dm-r-1);
}

.dm-fbd__doc:hover { border-color: var(--dm-fb-ring); color: var(--dm-fb); }
.dm-fbd__doc svg { width: 0.875rem; height: 0.875rem; flex-shrink: 0; }
.dm-fbd__size { flex-shrink: 0; font-family: var(--dm-mono); color: var(--dm-text-muted); }

/* ── The type, changeable ────────────────────────────────────────────
   Four pressable pills instead of a `select`: there are four, all four are visible at once
   and the one that is set is readable without opening anything. The ones that are NOT set
   are muted — grey edge and grey letter — so the current type stands out; without that,
   four pills in four colours look like four labels rather than one choice. */
.dm-fbd__kinds { display: inline-flex; gap: 0.1875rem; }
.dm-fbd__kinds form { display: inline; margin: 0; }

.dm-fbd__kind {
  display: inline-block;
  padding: 0.0625rem 0.375rem;
  font-family: var(--dm-mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: 0.25rem;
}

.dm-fbd__kinds .dm-fbd__kind {
  color: var(--dm-text-muted);
  background-color: transparent;
  border-color: var(--dm-border);
  cursor: pointer;
}

.dm-fbd__kinds .dm-fbd__kind:hover {
  color: var(--dm-text);
  border-color: var(--dm-text-muted);
}

.dm-fbd__kind.is-on { background-color: var(--dm-surface); }

/* Defect in amber and not red: red is what the health columns use for a customer in
   trouble, and a note about a misaligned header is not that. */
.dm-fbd__kind--defect,
.dm-fbd__kinds .dm-fbd__kind--defect.is-on {
  color: var(--dm-warning);
  border-color: color-mix(in srgb, var(--dm-warning) 40%, transparent);
  background-color: color-mix(in srgb, var(--dm-warning) 10%, transparent);
}

.dm-fbd__kind--improvement,
.dm-fbd__kinds .dm-fbd__kind--improvement.is-on {
  color: var(--dm-info);
  border-color: color-mix(in srgb, var(--dm-info) 40%, transparent);
  background-color: color-mix(in srgb, var(--dm-info) 10%, transparent);
}

.dm-fbd__kind--question,
.dm-fbd__kinds .dm-fbd__kind--question.is-on {
  color: var(--dm-text-muted);
  border-color: var(--dm-border);
  background-color: var(--dm-neutral-bg);
}

/* Untriaged is dashed: it is not a classification, it is the absence of one, and it has to
   look like a gap rather than like a fourth category somebody chose. */
.dm-fbd__kind--untriaged,
.dm-fbd__kinds .dm-fbd__kind--untriaged.is-on {
  color: var(--dm-fb);
  border-style: dashed;
  border-color: var(--dm-fb-ring);
  background-color: var(--dm-fb-soft);
}

/* ── The state, as a pill ────────────────────────────────────────── */

.dm-fbs {
  display: inline-block;
  padding: 0.0625rem 0.375rem;
  font-size: 0.625rem;
  font-weight: 700;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: 0.25rem;
}

.dm-fbs--open {
  color: var(--dm-fb);
  border-color: var(--dm-fb-ring);
  background-color: var(--dm-fb-soft);
}

.dm-fbs--accepted {
  color: var(--dm-warning);
  border-color: color-mix(in srgb, var(--dm-warning) 35%, transparent);
  background-color: color-mix(in srgb, var(--dm-warning) 10%, transparent);
}

.dm-fbs--in_progress {
  color: var(--dm-brand-text);
  border-color: color-mix(in srgb, var(--dm-brand) 35%, transparent);
  background-color: var(--dm-brand-bg);
}

.dm-fbs--resolved {
  color: var(--dm-fb-done);
  border-color: color-mix(in srgb, var(--dm-fb-done) 35%, transparent);
  background-color: color-mix(in srgb, var(--dm-fb-done) 10%, transparent);
}

/* Dismissed in grey and not in red: closing without doing anything is a legitimate answer,
   not an error. */
.dm-fbs--dismissed {
  color: var(--dm-text-muted);
  border-color: var(--dm-border);
  background-color: var(--dm-surface);
}

/* ── The inbox ───────────────────────────────────────────────────── */

/* Tabs with their count. The count sits inside the tab rather than beside it, so a tab and
   its number cannot end up on two lines when the row wraps. */
.dm-fb-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  border-bottom: 1px solid var(--dm-border);
  padding-bottom: 0.5rem;
}

.dm-fb-tab {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--dm-text-muted);
  border: 1px solid transparent;
  border-radius: 999px;
}

.dm-fb-tab:hover { color: var(--dm-text); background-color: var(--dm-hover-bg); }

.dm-fb-tab.is-on {
  color: var(--dm-fb);
  background-color: var(--dm-fb-soft);
  border-color: var(--dm-fb-ring);
}

.dm-fb-tab__count {
  font-family: var(--dm-mono);
  font-size: 0.625rem;
  font-variant-numeric: tabular-nums;
  color: var(--dm-text-muted);
}

.dm-fb-tab.is-on .dm-fb-tab__count { color: var(--dm-fb); }

.dm-fbi__code {
  font-family: var(--dm-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  white-space: nowrap;
  color: var(--dm-fb);
}

.dm-fbi__where { display: flex; flex-direction: column; gap: 0.0625rem; }
.dm-fbi__where:hover .dm-text { color: var(--dm-brand); }

.dm-fbi__selector {
  font-family: var(--dm-mono);
  font-size: 0.625rem;
  color: var(--dm-text-muted);
  overflow-wrap: anywhere;
}

/* "Anchor lost" and "nobody wrote a word". Both are facts about the ROW that change how it
   should be read, so they go under the text rather than in a column of their own. */
.dm-fbi__lost {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-top: 0.1875rem;
  font-family: var(--dm-mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dm-warning);
}

.dm-fbi__lost svg { width: 0.6875rem; height: 0.6875rem; }

/* The decision in the row itself. It wraps on narrow screens instead of overflowing: the
   table already has its own horizontal scroll and two nested scrolls are unusable. */
.dm-fbi__decide {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
}

.dm-fbi__select {
  width: auto;
  min-width: 6.5rem;
  padding: 0.1875rem 0.375rem;
  font-size: 0.6875rem;
}

/* In the inbox, deleting is quiet until it is asked for: an icon with its word, in the grey
   of the secondary. The row is for triaging — state and owner — and deleting is what gets
   done now and then. */
.dm-fbi__delete {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-top: 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--dm-text-muted);
}

.dm-fbi__delete:hover { color: var(--dm-error); }
.dm-fbi__delete svg { width: 0.75rem; height: 0.75rem; }

.dm-fbi__confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.375rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--dm-error);
}

/* `button_to` renders a form, and without this it breaks the line and knocks the three
   controls out of alignment. */
.dm-fbi__confirm form { display: inline; margin: 0; }

/* The "assigned to me" mark in "My points". The list mixes the ones I left with the ones I
   was given, and without the mark "did I write this?" is answered by opening it. */
.dm-fbm__tag {
  display: inline-block;
  margin-top: 0.1875rem;
  padding: 0.0625rem 0.375rem;
  font-family: var(--dm-mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dm-fb);
  background-color: var(--dm-fb-soft);
  border: 1px solid var(--dm-fb-ring);
  border-radius: 0.25rem;
}

/* Reduced motion, last so it overrides every transition declared above. Nothing in this app
   animates to convey meaning — the transitions are all hover and opacity easing — so honouring
   the preference costs no information. `!important` because the declarations it overrides are
   spread across components rather than centralised. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
