:root {
  /* ================================================================
     BRAND PALETTE — the only four values you should need to replace.
     Everything else below derives from these automatically.
     ================================================================ */
  --palette-dominant: #6fffdc;   /* dominant color  — buttons, links, active nav/tabs */
  --palette-support-1: #0f1b2d;  /* support color 1 — body text, strong headings      */
  --palette-support-2: #46566b;  /* support color 2 — muted text, borders             */
  --palette-accent: #dce9f5;     /* accent color    — page background tint, highlights */

  /* ---- Derived neutrals (computed — no need to edit) ---- */
  --color-bg: color-mix(in srgb, var(--palette-accent) 35%, #fbfcfe);
  --color-surface: #ffffff;
  --color-border: color-mix(in srgb, var(--palette-support-2) 22%, white);
  --color-border-soft: color-mix(in srgb, var(--palette-support-2) 12%, white);
  --color-text: var(--palette-support-1);
  --color-text-muted: color-mix(in srgb, var(--palette-support-1) 58%, white);
  --color-text-faint: color-mix(in srgb, var(--palette-support-1) 38%, white);

  /* ---- Derived interactive tones (computed — no need to edit) ---- */
  --color-accent: var(--palette-dominant);
  --color-accent-strong: color-mix(in srgb, var(--palette-dominant) 80%, black);
  --color-accent-bg: color-mix(in srgb, var(--palette-dominant) 10%, white);
  --color-accent-border: color-mix(in srgb, var(--palette-dominant) 32%, white);

  /* Very light, sober tint for a cell actively being edited — deliberately
     close to EngTool's own #fffdf0, not tied to the brand palette. */
  --color-cell-editing-bg: #fffdf0;
  /* Focus border for a cell being edited — a plain, neutral blue instead of
     the brand's mint-green accent, which read as garish on a text input. */
  --color-cell-editing-border: #1a56db;

  /* ---- Grid header tones (EngTool-style: neutral gray, not accent-tinted) ---- */
  --color-th-bg: color-mix(in srgb, var(--palette-support-2) 7%, white);
  --color-th-filter-bg: color-mix(in srgb, var(--palette-support-2) 3%, white);

  --color-danger: #c0392b;
  --color-danger-bg: rgba(192, 57, 43, 0.08);
  --color-danger-border: rgba(192, 57, 43, 0.3);

  --color-success: #1f8a5f;
  --color-success-bg: rgba(31, 138, 95, 0.08);
  --color-success-border: rgba(31, 138, 95, 0.3);

  /* ---- Dark top bar (derived from support-1) + fixed on-dark text ---- */
  --color-topbar-bg: color-mix(in srgb, var(--palette-support-1) 92%, black);
  --color-on-dark: #ffffff;
  --color-on-dark-muted: rgba(255, 255, 255, 0.68);
  --color-on-dark-border: rgba(255, 255, 255, 0.22);
  --color-on-dark-hover: rgba(255, 255, 255, 0.1);

  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  
  --topbar-height: 52px;
  --radius-pill: 999px;
  --radius-lg: 16px;
}

* { box-sizing: border-box; }
html, body { height: 100%; }

body {
  margin: 0;
  background-color: var(--color-bg);
  background-image:
    radial-gradient(circle at 8% 0%, color-mix(in srgb, var(--palette-accent) 55%, transparent), transparent 32rem),
    radial-gradient(circle at 100% 12%, color-mix(in srgb, var(--palette-accent) 30%, transparent), transparent 30rem);
  background-attachment: fixed;
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: 15.5px;
}

a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; }

.app-shell { min-height: 100vh; display: flex; flex-direction: column; }

/* Decorative dot pattern — generic accent texture, not tied to any brand asset */
.dot-pattern {
  position: absolute;
  width: 160px;
  height: 130px;
  pointer-events: none;
  opacity: 0.5;
  background-image: radial-gradient(circle, var(--palette-dominant) 1.6px, transparent 1.8px);
  background-size: 16px 16px;
  -webkit-mask-image: linear-gradient(135deg, #000, transparent 78%);
  mask-image: linear-gradient(135deg, #000, transparent 78%);
}

/* Top bar */

.topbar {
  height: var(--topbar-height);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 24px;
  background-color: var(--color-topbar-bg);
  position: sticky;
  top: 0;
  z-index: 20;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06) inset, 0 6px 20px rgba(0, 0, 0, 0.08);
}

.topbar__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  font-size: 17.5px;
  text-decoration: none;
  color: var(--color-on-dark);
  flex-shrink: 0;
}

.topbar__brand:hover .topbar__brand-name { text-decoration: underline; }

.topbar__logo {
  width: 30px; height: 30px;
  border-radius: 9px;
  background: var(--color-accent);
  color: white;
  display: flex; align-items: center; justify-content: center;
  font-size: 14.5px; font-weight: 700;
  overflow: hidden;
}
.topbar__logo img { width: 100%; height: 100%; object-fit: contain; display: block; }

.topbar__version { font-family: var(--font-mono); font-size: 12px; color: var(--color-on-dark-muted); }

.topbar__nav { display: flex; align-items: center; gap: 2px; height: 100%; }

.topbar__link {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 14px;
  color: var(--color-on-dark-muted);
  text-decoration: none;
  font-weight: 500;
  font-size: 15px;
  border-bottom: 2px solid transparent;
}

.topbar__link:hover { color: var(--color-on-dark); background-color: var(--color-on-dark-hover); }
.topbar__link.is-active { color: var(--color-on-dark); border-color: var(--palette-dominant); }

.topbar__right { display: flex; align-items: center; gap: 10px; margin-left: auto; }

.topbar__link--help { gap: 6px; }
.topbar__help-icon { width: 15px; height: 15px; flex-shrink: 0; color: currentColor; }

.dropdown { position: relative; }
.dropdown__toggle {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 14px 6px 6px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-on-dark-border);
  background-color: transparent;
  color: var(--color-on-dark);
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 14.5px;
  cursor: pointer;
}
.dropdown__toggle:hover { background-color: var(--color-on-dark-hover); }
.dropdown__caret { flex-shrink: 0; font-size: 11px; line-height: 1; }

.dropdown--project .dropdown__toggle { width: 140px; }
.dropdown--project .dropdown__toggle-label {
  flex: 1;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dropdown__role { padding: 4px 10px 8px; font-size: 14.5px; }
.dropdown__menu {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 200px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(20, 30, 50, 0.12);
  padding: 6px;
  z-index: 30;
}
.dropdown.is-open .dropdown__menu { display: block; }
.dropdown__item, .dropdown__link {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border-radius: 6px;
  border: none;
  background: none;
  text-align: left;
  color: var(--color-text);
  text-decoration: none;
  font-size: 14.5px;
  font-family: var(--font-ui);
  cursor: pointer;
}
.dropdown__item:hover, .dropdown__link:hover { background-color: var(--color-bg); }
.dropdown__label { padding: 6px 10px; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--color-text-faint); }
.dropdown__divider { height: 1px; background: var(--color-border-soft); margin: 6px 2px; }

.avatar-circle {
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--color-accent);
  color: white;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700;
  flex-shrink: 0;
}

.btn--onDark {
  border-color: var(--color-on-dark-border);
  background-color: transparent;
  color: var(--color-on-dark);
}
.btn--onDark:hover { background-color: var(--color-on-dark-hover); }

#content { flex: 1; padding: 28px 20px; max-width: 1800px; width: 100%; margin: 0 auto; position: relative; z-index: 1; }
#content.content--wide {
  max-width: none;
  display: flex;
  flex: 0 0 auto; /* override the base rule's `flex:1` (flex-basis:0), which
    would otherwise ignore the explicit height below and let content grow
    app-shell past one viewport instead of scrolling internally */
  flex-direction: column;
  min-height: 0;
  height: calc(100vh - var(--topbar-height));
}

/* Flash message on the standalone login page (not part of the app shell,
   so it isn't routed through the global notification stack below). */

.flash {
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 15px;
  border: 1px solid var(--color-border);
}
.flash--success { background-color: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success); }
.flash--error { background-color: var(--color-danger-bg); border-color: var(--color-danger-border); color: var(--color-danger); }

/* Global notification stack — every flash/status message in the app (create,
   delete, update, errors...) renders here as an overlay, centered under the
   topbar, so it never shifts surrounding layout. Auto-dismisses after 30s or
   on click; see notifications.js. */

.notification-stack {
  position: fixed;
  top: calc(var(--topbar-height) + 12px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 2000;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: max-content;
  max-width: min(560px, calc(100vw - 32px));
  pointer-events: none;
}
.notification {
  pointer-events: auto;
  cursor: pointer;
  padding: 10px 16px;
  border-radius: 8px;
  font-size: 14.5px;
  border: 1px solid var(--color-border);
  background-color: var(--color-surface);
  color: var(--color-text);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.15);
  max-width: 100%;
  opacity: 1;
  transform: translateY(0);
  transition: opacity .2s ease, transform .2s ease;
}
.notification--leaving { opacity: 0; transform: translateY(-6px); }
.notification--success { background-color: color-mix(in srgb, var(--color-success) 12%, var(--color-surface)); border-color: var(--color-success-border); color: var(--color-success); }
.notification--error { background-color: color-mix(in srgb, var(--color-danger) 12%, var(--color-surface)); border-color: var(--color-danger-border); color: var(--color-danger); }

/* Page header */

.page-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 24px;
}
.page-header__title { margin: 0; font-size: 22px; font-weight: 700; }
.page-header__subtitle { margin: 4px 0 0; font-family: var(--font-ui); font-size: 14.5px; color: var(--color-text-muted); }
.page-header__actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.page-header__actions form { margin: 0; }

/* Buttons */

.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background-color: var(--color-surface);
  color: var(--color-text);
  text-decoration: none;
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 14.5px;
  cursor: pointer;
  transition: transform 120ms ease, background-color 120ms ease, box-shadow 120ms ease;
}
.btn:hover { background-color: var(--color-bg); transform: translateY(-1px); }
.btn--accent { background-color: var(--color-accent); border-color: var(--color-accent); color: var(--color-text); box-shadow: 0 6px 16px color-mix(in srgb, var(--color-accent) 35%, transparent); }
.btn--accent:hover { background-color: var(--color-accent-strong); color: white; }
.btn--danger { background-color: var(--color-danger-bg); border-color: var(--color-danger-border); color: var(--color-danger); }
.btn--danger:hover { background-color: var(--color-danger-border); }
.btn--sm { padding: 5px 10px; font-size: 13px; }
.btn--icon { padding: 6px; width: 30px; height: 30px; gap: 0; justify-content: center; }
.btn--icon:hover { transform: none; }
.btn--icon.btn--sm { width: 26px; height: 26px; padding: 4px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* Cards / sections */

.card {
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 20px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.detail-section { margin-bottom: 24px; }
.detail-section__title { margin: 0 0 12px; font-size: 16.5px; font-weight: 700; }
.detail-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
.detail-grid__item { display: flex; flex-direction: column; gap: 4px; }
.detail-grid__label { font-size: 12px; text-transform: uppercase; letter-spacing: .03em; color: var(--color-text-faint); }
.detail-grid__value { font-size: 15px; word-break: break-word; }

/* Forms */

.form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.form-field { display: flex; flex-direction: column; gap: 6px; font-size: 14.5px; color: var(--color-text-muted); margin-bottom: 14px; }
.form-field input, .form-field select, .form-field textarea {
  padding: 8px 10px;
  border-radius: 7px;
  border: 1px solid var(--color-border);
  background-color: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: 14.5px;
}
.form-field textarea { resize: vertical; }
.form-actions { display: flex; gap: 10px; margin-top: 18px; }
.checkbox-row { display: flex; align-items: center; gap: 8px; font-size: 14.5px; }
.checkbox-list { display: flex; flex-direction: column; gap: 6px; max-height: 220px; overflow-y: auto; border: 1px solid var(--color-border); border-radius: 8px; padding: 10px; }
.checkbox-list label { display: flex; align-items: center; gap: 8px; font-size: 14.5px; }

/* Tables / grid */

.table-wrap { border: 1px solid var(--color-border); border-radius: 8px; background: var(--color-surface); width: fit-content; }
.table { border-collapse: collapse; font-size: 14.5px; }
/* Fixed layout so a column's width (drag-resized or saved) is authoritative
   instead of a mere hint the browser overrides to fit the widest cell — this
   is what lets a column shrink past its content's natural width, with the
   overflowing text ellipsized below rather than forcing the column back
   open. The tiny explicit `width` alongside it matters just as much: inside
   a `width: fit-content` .table-wrap (kept, so an unresized grid still hugs
   its own content instead of stretching edge-to-edge), a fixed-layout table
   left at `width: auto` has Chrome fall back to measuring every column by
   its content again, silently undoing per-column widths. Any definite
   length breaks that — the table still renders at the sum of its column
   widths (a fixed table is never *narrower* than that), so the actual value
   here is arbitrary. grid.js locks every not-yet-resized column to its
   rendered width on load, so this doesn't otherwise change how a grid looks. */
.table.js-grid { table-layout: fixed; width: 1px; }
.table th, .table td { padding: 10px 14px; text-align: left; white-space: nowrap; border-bottom: 1px solid var(--color-border-soft); }
/* Truncate with "…" instead of overflowing into the next cell once a column
   is narrower than its content. Deliberately not on the <th> itself — its
   .grid-resizer child sits partly outside the header's own box so it stays
   grabbable even on a column resized down to nothing, and clipping the
   header would clip that handle along with it; .grid-th (below) clips the
   header's own label/filter instead, at the same effective boundary. */
.table td { overflow: hidden; text-overflow: ellipsis; }
.table thead th { color: var(--color-text-muted); font-weight: 700; font-size: 12px; text-transform: uppercase; letter-spacing: .02em; background: var(--color-bg); position: sticky; top: 0; }
.table tbody tr:hover { background-color: var(--color-bg); }
.table__actions { display: flex; gap: 6px; }

.grid-th { display: flex; flex-direction: column; gap: 0; cursor: grab; overflow: hidden; }
/* Two visually separated zones per column, EngTool-style: a label+sort row
   with its own bottom border, and a filter row with a slightly different
   background — instead of the two being one loosely-gapped flex stack. */
.grid-th__sort {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  cursor: pointer; user-select: none; text-decoration: none; color: inherit;
  padding: 8px 10px;
  border-bottom: 1px solid var(--color-border-soft);
}
.grid-th__label { overflow: hidden; text-overflow: ellipsis; }
.grid-th__required { color: var(--color-danger); font-weight: 600; }
.grid-th__sort-icon { flex: 0 0 auto; color: var(--color-text-faint); font-size: .85em; }
.grid-th__actions { display: flex; align-items: center; gap: 4px; padding: 4px 6px; background: var(--color-th-filter-bg); }
/* Icon/delete column headers carry no sort link or filter — an empty
   .grid-th shell just to pick up the same two-tone sort/filter row
   background split as the real columns, so the header row reads as one
   continuous band edge-to-edge instead of a flat block at each end. */
.grid-th--blank { cursor: default; }
.grid-th--blank .grid-th__sort { cursor: default; }
.grid-th__filter { flex: 1 1 auto; min-width: 0; padding: 4px 6px; font-size: 12.5px; border-radius: 3px; border: 1px solid var(--color-border-soft); background: var(--color-surface); font-weight: 400; text-transform: none; letter-spacing: 0; }
.grid-th__filter:disabled { background-color: var(--color-bg); color: var(--color-text-faint); font-style: italic; }
.grid-th__icon-btn {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  padding: 0;
  border-radius: 5px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: 11.5px;
  cursor: pointer;
}
.grid-th__icon-btn:hover { background: var(--color-bg); color: var(--color-text); }
.grid-th__icon-btn.is-active { background: var(--color-accent-strong); border-color: var(--color-accent-strong); color: white; }
th[data-col].is-dragging-col { opacity: .4; }
/* Editable-cell look — EngTool-style: perfectly flat at rest (no border), a
   very sober hover cue, and a pale-yellow tint with a plain blue border while
   a cell is actually being edited. The cell (td) itself carries no padding —
   the display span and the edit input each carry the *same* padding instead,
   so at-rest and editing produce a pixel-identical box with no dependency on
   percentage-height table quirks. The editing border is an inset box-shadow
   rather than a real border so it doesn't consume any extra box space. */
/* One explicit font declaration for every grid cell's content, regardless of
   column kind (text, number, date, fk, list, boolean) — instead of relying
   on inheritance through .table/.db-editor, which is easy to accidentally
   diverge from at some other level. This is the single source of truth for
   grid cell typography, in both tables and views. */
.grid-cell, .grid-cell__display, .grid-cell.is-editing > input, .grid-cell.is-editing > select {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 400;
}
.grid-cell { position: relative; cursor: pointer; padding: 0; }
.grid-cell__display { display: block; padding: 4px 10px; }
.grid-cell[data-editable="true"]:hover { box-shadow: inset 0 0 0 1px var(--color-border-soft, var(--color-border)); }
.grid-cell.is-editing { background: var(--color-cell-editing-bg); }
/* Scoped to a *direct* child of an editing cell — not just any ".grid-cell
   input" — so this never leaks onto the boolean checkbox, which lives one
   level deeper inside .grid-cell__display and is never part of this
   edit-mode swap (see the boolean rules below: no border, no editing tint,
   ever — it isn't "being edited", every click just toggles it). */
.grid-cell.is-editing > input, .grid-cell.is-editing > select {
  display: block;
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: 4px 10px;
  border: 0;
  box-shadow: inset 0 0 0 1.5px var(--color-cell-editing-border);
  border-radius: 2px;
  background: var(--color-cell-editing-bg);
  font: inherit;
  color: inherit;
  outline: none;
}
/* Boolean cells: the checkbox IS the cell, always — no double-click, no
   edit mode, no border/outline/tint of any kind; every click toggles and
   saves immediately (grid.js). A genuine third, indeterminate state covers
   "no value yet" on an EAV attribute column (grayed dash, not blue, so it
   never reads as a third kind of "checked"). */
.grid-cell[data-kind="boolean"] { text-align: center; }
.grid-cell[data-kind="count"] { text-align: center; cursor: default; }
.grid-cell__count-link { color: var(--color-text); text-decoration: none; cursor: pointer; }
.grid-cell__count-link:hover { text-decoration: underline; }
.grid-cell__display input[type="checkbox"] {
  display: inline-block;
  width: 16px;
  height: 16px;
  vertical-align: middle;
  margin: 0;
  cursor: pointer;
  outline: none;
}
.grid-cell__display input[type="checkbox"]:indeterminate { accent-color: var(--color-text-faint); }
.grid-cell--error { outline: 2px solid var(--color-danger); }
.grid-cell--nowrite { cursor: not-allowed; opacity: .55; }

/* Inline autocomplete for "text" cells — the other values already used in
   that column, filtered as you type; picking one is a shortcut, not a
   requirement (see grid.js attachSuggestions). Positioned with `fixed` +
   getBoundingClientRect (left/top/width set inline by JS) rather than
   `absolute` off the cell, so it isn't clipped by the grid's own scroll
   containers (.table-wrap/.table-scroll both use overflow: auto). */
.cell-suggestions {
  position: fixed;
  z-index: 40;
  margin: 1px 0 0;
  padding: 0;
  max-height: 220px;
  overflow-y: auto;
  list-style: none;
  background: var(--color-surface);
  border: 1px solid var(--color-border-soft);
  border-radius: 4px;
  box-shadow: 0 8px 20px rgba(15, 23, 42, .15);
  font-size: 13px;
  font-family: var(--font-ui);
}
.cell-suggestions li { padding: 7px 12px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cell-suggestions li:hover, .cell-suggestions li.is-active { background: var(--color-bg); }
.grid-resizer { position: absolute; right: -4px; top: 0; bottom: 0; width: 11px; cursor: col-resize; z-index: 1; }
/* Hover relies on the cursor change alone (no bar) — only an active drag
   gets a thin centered line, so the feedback stays sober instead of a wide
   dark-gray band across the whole hit-area. */
.grid-resizer.is-dragging {
  background: linear-gradient(to right, transparent calc(50% - 1px), var(--palette-support-2) calc(50% - 1px), var(--palette-support-2) calc(50% + 1px), transparent calc(50% + 1px));
}
/* The last data column has no next column to share the handle's usual
   straddle of the border, so its normal 7px right-side bleed would poke past
   the table's own edge — just enough overflow to force a horizontal
   scrollbar even when every column otherwise fits the available width. Keep
   that one handle flush with (not past) the table's right edge instead. */
th[data-col]:not(:has(~ th[data-col])) .grid-resizer { right: 0; width: 7px; }
/* Row action icons (view details, delete) — sized by their own icon glyph
   plus a small hit-area padding, not a fixed box, so they never force the
   row taller than the text/edit cells next to them. Plain at rest, no
   pill/circle background (that's what .btn--icon is for elsewhere, not a
   dense grid row) — just a sober rounded-square tint on hover. */
.grid-row__delete, .grid-row__view, .grid-row__merge, .grid-row__edit {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 3px;
  border: 0; border-radius: 4px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 13px;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
.grid-row__delete:hover { background: var(--color-danger-bg); color: var(--color-danger); }
.grid-row__view:hover { background: var(--color-bg); color: var(--color-text); }
.grid-row__merge:hover { background: var(--color-accent-bg); color: var(--color-accent-strong); }
.grid-row__edit:hover { background: var(--color-bg); color: var(--color-accent-strong); }
/* Groups the trailing per-row icon buttons (Merge, Delete) into one cell
   instead of a dedicated column each — same idea as .toolbar__group, just
   tighter, since it sits inside a dense grid row. */
.row-actions { display: inline-flex; align-items: center; gap: 2px; }
.grid-th__sort-idle { opacity: .35; font-size: .85em; }

.toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
.toolbar__group { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.pagination { display: flex; align-items: center; gap: 10px; margin-top: 14px; font-size: 14.5px; color: var(--color-text-muted); }
.pagination select { padding: 4px 6px; border-radius: 6px; border: 1px solid var(--color-border); }

/* Badges */

.badge { display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 700; letter-spacing: .02em; text-transform: uppercase; border: 1px solid transparent; }
.badge--accent { background-color: var(--color-accent-bg); border-color: var(--color-accent-border); color: var(--color-accent-strong); }
.badge--muted { background-color: var(--color-bg); border-color: var(--color-border); color: var(--color-text-muted); }
.badge--admin { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.badge--editor { background: #dbeafe; color: #1e3a8a; border-color: #bfdbfe; }
.badge--viewer { background: var(--color-bg); color: var(--color-text-muted); border-color: var(--color-border); }

/* Stat tiles */

.stat-tiles { display: flex; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; }
.stat-tile {
  flex: 1; min-width: 150px; padding: 18px 18px 16px;
  border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  border-bottom: 3px solid var(--palette-dominant);
  transition: transform 140ms ease, box-shadow 140ms ease;
}
.stat-tile:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); }
a.stat-tile { display: block; color: inherit; text-decoration: none; }
.stat-tile:nth-child(2) { border-bottom-color: var(--palette-support-2); }
.stat-tile:nth-child(3) { border-bottom-color: color-mix(in srgb, var(--palette-accent) 70%, var(--palette-dominant)); }
.stat-tile:nth-child(4) { border-bottom-color: var(--palette-support-1); }
.stat-tile__label { margin: 0 0 8px; font-size: 13px; color: var(--color-text-muted); }
.stat-tile__value { margin: 0; font-size: 28.5px; font-weight: 700; }

/* Settings layout — left sidebar + panel, at the app's normal spacious
   density (Settings pages are simple forms/small reference grids, not the
   dense Database editor — see the "Database density" comment below). The
   Users/Groups/Projects admin-table pages reuse .db-editor/.db-sidebar
   instead, for the same dense grid look as the Database screen. */

.settings-layout { display: flex; width: 100%; gap: 20px; align-items: flex-start; }

.settings-sidebar {
  flex: 0 0 220px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 10px;
  align-self: stretch;
}
.settings-sidebar__group-label { padding: 14px 10px 4px; font-size: 12px; text-transform: uppercase; color: var(--color-text-faint); letter-spacing: .03em; }
.settings-sidebar__link { display: block; padding: 8px 10px; border-radius: 7px; text-decoration: none; color: var(--color-text-muted); font-size: 14.5px; font-weight: 400; }
.settings-sidebar__link:hover { background-color: var(--color-bg); }
.settings-sidebar__link.is-active { background-color: var(--color-accent-bg); color: var(--color-accent-strong); font-weight: 600; }
.settings-sidebar__link--sub { padding-left: 20px; }

.settings-panel { flex: 1 1 auto; min-width: 0; }

/* Sidebar layout (Database screen) — fixed-width sidebar + panel, each
   scrolling independently, instead of the whole page scrolling together. */

.db-editor { display: flex; width: 100%; gap: 20px; align-items: stretch; flex: 1 1 auto; min-height: 0; }

.db-sidebar {
  flex: 0 0 220px;
  /* Flush to the topbar, the window's left edge, and the window's bottom —
     EngTool-style: cancel #content's own padding on all three sides with a
     negative margin, then restore the left inset as padding so the links
     still line up with the page title above. */
  margin: -28px 0 -28px -20px;
  background: var(--color-surface);
  border: none;
  border-right: 1px solid var(--color-border);
  padding: 10px 10px 10px 20px;
  overflow-y: auto;
  align-self: stretch;
}
.db-sidebar__group-label { margin-top: 14px; padding: 8px 10px 4px; font-size: 11px; text-transform: uppercase; color: var(--color-text-faint); letter-spacing: .03em; display: flex; align-items: center; justify-content: space-between; }
.db-sidebar__group-label-link { color: inherit; text-decoration: none; }
.db-sidebar__group-label-link:hover { text-decoration: underline; }
.db-sidebar__group-label--module { border-top: 1px solid var(--color-border-soft); margin-top: 18px; padding-top: 14px; }
.db-sidebar__link { display: block; padding: 7px 10px; border-radius: 7px; text-decoration: none; color: var(--color-text-muted); font-size: 14.5px; font-weight: 400; }
.db-sidebar__link:hover { background-color: var(--color-bg); }
.db-sidebar__link.is-active { background-color: var(--color-accent-bg); color: var(--color-accent-strong); font-weight: 600; }
.db-sidebar__link--view { display: flex; align-items: center; justify-content: space-between; padding-left: 14px; gap: 4px; }
.db-sidebar__link--view .db-sidebar__link-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.db-sidebar__add-icon, .db-sidebar__edit-icon {
  width: 20px; height: 20px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; border-radius: 4px; color: var(--color-text-faint);
  text-decoration: none;
}
.db-sidebar__add-icon:hover, .db-sidebar__edit-icon:hover { background: var(--color-bg); color: var(--color-accent-strong); }

/* Panel: fixed-height header, table body scrolls on its own, pagination
   stays anchored at the bottom — mirrors EngTool's layout. */

.db-panel { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.db-panel__header { position: relative; flex: 0 0 auto; }

/* The filter <form> wraps both the scrolling table and the footer bar, so it
   has to be a flex column itself — otherwise .table-scroll's flex:1 sizing
   (and the footer's anchored position below it) has no effect, and a long
   table just pushes the footer down past the viewport instead of leaving it
   pinned at the bottom of the panel. Shared by the Database grid and the
   Views grid (grid-form class, not an id) so both keep the same anchored
   pagination footer instead of drifting apart. */
.grid-form { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

.table-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; }

.db-panel__footer {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--color-border-soft);
  font-size: 14.5px;
  color: var(--color-text-muted);
}
.db-panel__footer-count { white-space: nowrap; }
.db-panel__footer-pagination { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.db-panel__page-size { display: flex; align-items: center; gap: 6px; }
.db-panel__page-size select { padding: 4px 6px; border-radius: 6px; border: 1px solid var(--color-border); font-family: var(--font-ui); }

/* Database density — resembles EngTool's compact data-grid spacing.
   Scoped to the Database/Views screens only; other pages (Settings, Docs,
   forms) keep the app's normal, more spacious sizing. */
.db-editor { font-size: 14px; }
.db-editor .table { font-size: 13px; }
.db-editor .table th, .db-editor .table td { padding: 3px 10px; }
.db-editor .table td.grid-cell { padding: 0; }
.db-editor .table thead th {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text);
  text-transform: none;
  letter-spacing: normal;
  background: var(--color-th-bg);
  padding: 0;
  vertical-align: top;
}
.db-editor .grid-th__filter { font-size: 12px; }
.db-editor .db-sidebar__link { padding: 5px 8px; font-size: 13px; }
.db-panel .page-header__title { font-size: 18px; }
.db-editor .btn--sm { padding: 3px 8px; font-size: 12.5px; }
.db-editor .btn--icon.btn--sm { width: 24px; height: 24px; padding: 3px; }

/* Empty state */

.empty-state { padding: 44px 20px; text-align: center; border: 1px dashed var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); color: var(--color-text-muted); }

/* Modal / dialog */

dialog.modal { padding: 0; border: 1px solid var(--color-border); border-radius: var(--radius-lg); max-width: 480px; width: 92%; background: var(--color-surface); color: var(--color-text); box-shadow: 0 24px 48px rgba(15, 23, 42, 0.16); }
dialog.modal::backdrop { background-color: rgba(15, 23, 42, 0.45); }
.modal__body { padding: 22px; display: flex; flex-direction: column; gap: 14px; }
.modal__title { margin: 0; font-size: 17.5px; font-weight: 700; }
.modal__text { margin: 0; color: var(--color-text-muted); font-size: 15px; line-height: 1.5; }
.modal__actions { display: flex; justify-content: flex-end; gap: 10px; }

/* Side-panel modal variant — used for the PDF attachment preview */
dialog.modal--panel {
  position: fixed;
  inset: 0 0 0 auto;
  margin: 0;
  max-width: none;
  width: min(560px, 92vw);
  height: 100vh;
  max-height: 100vh;
  border-radius: 0;
  border-top: none;
  border-bottom: none;
  border-right: none;
  animation: panel-slide-in 180ms ease;
}
dialog.modal--panel::backdrop { background-color: rgba(15, 23, 42, 0.35); }
dialog.modal--panel .modal__body { height: 100%; padding: 16px; }
@keyframes panel-slide-in {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}
.preview-frame {
  flex: 1;
  width: 100%;
  height: 100%;
  border: 1px solid var(--color-border-soft);
  border-radius: 10px;
  background: var(--color-bg);
}

/* Views: side-panel attachment preview (§4.6)
   .view-body is the direct .db-panel child (mirrors .grid-form's role in
   the plain Database grid) so the pagination footer anchors identically;
   .view-layout switches it to a row so the preview aside sits beside the
   grid-form column instead of below it — the aside keeps its own fixed
   height, the form column stretches to the full row height by default so
   its own .table-scroll/footer flex-column sizing still works. */
.view-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.view-body.view-layout { flex-direction: row; gap: 20px; }
.view-body.view-layout > .grid-form { flex: 3 1 0; min-width: 0; }
.view-body.view-layout > .view-preview-panel { flex: 2 1 0; }
.view-preview-panel {
  position: sticky;
  top: calc(var(--topbar-height) + 20px);
  height: min(80vh, 720px);
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.view-preview-panel__header { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.view-preview-panel__title { margin: 0; font-size: 14px; font-weight: 700; }
.view-preview-panel__body { flex: 1; min-height: 0; display: flex; }
.view-preview-panel__placeholder { margin: auto; max-width: 240px; text-align: center; color: var(--color-text-muted); font-size: 13px; line-height: 1.5; }

/* Drop zone (Attachments) */

.dropzone {
  border: 1.5px dashed var(--color-border);
  border-radius: 10px;
  padding: 20px;
  text-align: center;
  color: var(--color-text-muted);
  font-size: 14.5px;
  margin-bottom: 14px;
}
.dropzone.is-dragover { border-color: var(--color-accent); background-color: var(--color-accent-bg); color: var(--color-accent-strong); }

/* Files column (Database grid: Units/Equipments) — count + drag-and-drop +
   manual-upload icon, all inside one grid cell. Padding matches
   .grid-cell__display's own 4px vertical exactly, with no min-height, so a
   files-cell row is the same height as a plain text-cell row instead of
   taller — the extra height that used to come from a 28px min-height and a
   circle-shaped upload button (see .files-cell__upload-btn below) is gone. */
.grid-cell--files { cursor: default; padding: 0; }
.grid-cell--files:hover { box-shadow: none; }
.files-cell {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  height: 100%; padding: 4px 8px;
  border-radius: 4px;
  transition: background-color .15s, box-shadow .15s;
}
.files-cell.is-dragover { background-color: var(--color-accent-bg); box-shadow: inset 0 0 0 1.5px var(--color-accent); }
.files-cell__count { color: var(--color-text); font-weight: 400; font-size: 13px; font-family: var(--font-ui); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.files-cell__count:hover { text-decoration: underline; }
/* Manual-upload icon — same plain, circle-free treatment as .grid-row__view/
   .grid-row__delete: sized by the glyph plus a small hit-area padding, no
   border/background at rest, just a sober square tint on hover. Replaces the
   former .btn--icon.btn--sm, whose pill border-radius on an equal-width/
   height box rendered as a circle. Padding is 2px (not .grid-row__view's
   3px) so this icon's own content-box height (~17px) stays at or under
   .grid-cell__display's text line height — otherwise, as the tallest thing
   in a files-cell, it alone would still set the row taller than a plain
   text-cell row. */
.files-cell__upload-btn {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 2px;
  border: 0; border-radius: 4px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.files-cell__upload-btn:hover { background: var(--color-bg); color: var(--color-text); }

/* Login page */

.login-page {
  position: relative;
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  background:
    radial-gradient(circle at 12% 8%, color-mix(in srgb, var(--palette-accent) 55%, transparent), transparent 30rem),
    radial-gradient(circle at 90% 90%, color-mix(in srgb, var(--palette-dominant) 10%, transparent), transparent 28rem),
    var(--color-bg);
}
.login-page .dot-pattern { left: 2.5rem; bottom: 2rem; }
.login-card {
  position: relative;
  width: 360px; padding: 32px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 48px rgba(15, 23, 42, 0.08);
}
.login-card__logo {
  width: 44px; height: 44px; border-radius: 12px;
  background: var(--color-accent); color: white;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; margin: 0 auto 16px;
  overflow: hidden;
}
.login-card__logo img { width: 100%; height: 100%; object-fit: contain; }
.login-card h1 { text-align: center; font-size: 20px; margin: 0 0 22px; }

/* Errors page */

.error-page { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; min-height: 60vh; padding: 40px 20px; }
.error-page__code { font-size: 70.5px; font-weight: 800; margin: 0; color: var(--color-accent); }
.error-page__title { margin: 12px 0 6px; font-size: 22px; font-weight: 700; }
.error-page__subtitle { color: var(--color-text-muted); max-width: 440px; margin: 0 0 20px; }

/* Docs */

.docs { max-width: 760px; display: flex; flex-direction: column; gap: 32px; }
.docs h2 { font-size: 18.5px; margin: 0 0 12px; }
.docs h3 { font-size: 15.5px; margin: 18px 0 8px; }
.docs p, .docs li { color: var(--color-text-muted); font-size: 15px; line-height: 1.65; }

/* Utility */

.text-muted { color: var(--color-text-muted); }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.mb-16 { margin-bottom: 16px; }
