/* ============================================================
   Projekt tab — projects, BOM import + column mapping, master-detail
   matching view. Reuses existing design tokens from style.css.
   ============================================================ */

#tab-project { padding: 0; }

/* Page is wider while the Projekt tab is active (more room for the master-detail
   view's data-heavy center column) — Søg/Batch are unaffected since `main`'s max-width
   is only overridden while body carries this class (toggled in project.js's projectInit).
   1728px→1880px (on request, "det er ikke til at se parametre for godkendte komponenter") —
   the Fastlås/pin column added 2026-09-14 pushed "Godkendte komponenter"'s own Parametre column
   down to its bare 210px floor (3 chips), while the read-only "Forslag til alternativ" table
   right below it — same shared column CSS, just without a pin column to make room for — sits at
   ~330px and shows 6; the extra 152px here flows straight into that same auto-width column since
   every other column in the row has a fixed px/percent width (see th[data-col] rules below). */
body.project-tab-active main { max-width: 1880px; }

.project-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.project-card-head {
  padding: 18px 22px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface2);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.project-card-head-text { flex: 1 1 auto; min-width: 0; }
.project-card-head h2 {
  margin: 0 0 4px;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}
.project-card-sub {
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-muted);
  line-height: 1.5;
}
.project-import-stats {
  margin: 6px 0 0;
  font-size: 0.75rem;
  color: var(--text-faint);
}

/* ── Project list ─────────────────────────────────────────── */

.project-table-wrap { overflow-x: auto; }
.project-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.project-table thead th {
  background: var(--surface2);
  text-align: left;
  padding: 9px 14px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.project-table tbody td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--divider);
  color: var(--text-2);
  vertical-align: middle;
}
.project-table tbody tr:last-child td { border-bottom: 0; }
.project-table tbody tr:hover td { background: var(--surface2); }
.project-row-customer { font-weight: 600; color: var(--text); }
.project-row-qty, .project-row-count { font-family: var(--mono); }
.project-row-open { text-align: right; }

.project-empty, .project-loading {
  padding: 32px 22px;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.85rem;
}
.project-error {
  margin: 14px 22px 0;
  padding: 10px 14px;
  background: var(--red-tint);
  color: var(--red);
  border-radius: var(--radius-sm);
  font-size: 0.8rem;
  font-weight: 600;
}
/* Incomplete-price-search banner/note (2026-09-08) — yellow, not red: this is "we don't know
   yet," not a confirmed error, and self-clears the next time the failing supplier answers. */
.project-supplier-issues-banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 14px 22px 0;
  padding: 10px 14px;
  background: var(--yellow-tint);
  color: var(--text);
  border-radius: var(--radius-sm);
  font-size: 0.8rem;
  font-weight: 500;
  line-height: 1.4;
}
.project-supplier-issues-icon { flex-shrink: 0; color: var(--yellow); }
.project-line-supplyissue-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 14px;
  background: var(--yellow-tint);
  border-radius: var(--radius-sm);
  font-size: 0.78rem;
  line-height: 1.4;
}

.project-new-form {
  padding: 16px 22px;
  border-bottom: 1px solid var(--border);
  background: var(--surface2);
}
.project-new-form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: 14px;
}
.project-new-form-row:last-of-type { margin-bottom: 0; }
.project-new-form .field-group { min-width: 220px; flex: 1 1 220px; }
/* Date+hint on one row, the "kun rettidige leverandører" checkbox on its own line below — used to
   fight the checkbox for the same horizontal strip as the date input (2026-08-26, on request). */
.project-deadline-field { display: flex; flex-direction: column; gap: 8px; }
.project-deadline-daterow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Per-production-quantity leveringsdato (2026-09-02) — each row needs more width than the plain
   quantity chips did, so it takes the whole form row instead of sharing it with a second field. */
.project-new-form .field-group-full,
.project-detail-settings-row .field-group-full { flex: 1 1 100%; }
/* One compact table instead of a stack of loosely-wrapped rows (2026-09-14, redesigned on
   request; further simplified the same day when the "Kun rettidige" checkbox was removed — a
   leveringsdato now always means "must be in stock by then" the moment one is set, so there was
   no longer a separate toggle to show). Column headers carry the labels once; each row is a plain
   grid line with the delete button as its own trailing column instead of overlapping the quantity
   field. */
.project-qtydl-table-wrap { overflow-x: auto; }
.project-qtydl-table {
  display: flex;
  flex-direction: column;
  min-width: 320px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.project-qtydl-row {
  display: grid;
  grid-template-columns: 86px minmax(0, 190px) 26px;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
}
.project-qtydl-row:not(:last-child) { border-bottom: 1px solid var(--divider); }
.project-qtydl-headrow {
  background: var(--surface2);
  padding: 6px 10px;
}
.project-qtydl-headrow span {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.project-qtydl-qty-input {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface);
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  text-align: center;
  transition: border-color .12s, box-shadow .12s;
}
.project-qtydl-qty-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.project-qtydl-row .project-qtydl-deadline-input { width: 100%; }
.project-qtydl-del {
  justify-self: end;
  background: none;
  border: none;
  color: var(--text-faint);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 3px 6px;
  border-radius: 4px;
}
.project-qtydl-del:hover { color: var(--red); background: var(--red-tint); }
.project-qtydl-add { margin-top: 10px; }
.project-qtydl-default {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.project-qtydl-default-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}
/* Read-only view: one line per production quantity once any of them has its own leveringsdato. */
.project-meta-box-deadlines { gap: 2px; }
.project-meta-value-qtydl {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text);
  font-family: var(--mono);
  white-space: nowrap;
}
.project-new-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }

/* ── Detail-view settings (production quantities / deadline) ─────────────── */

/* The meta strip below the title/actions row: the original-file link and the production-qty /
   deadline settings each get their own small labelled box (redesigned 2026-08-25 — plain
   "1 stk. produktion · Levering: ikke sat" inline text read as an afterthought stapled under the
   title; boxing each fact gives it the same weight as a stat tile). */
.project-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 8px;
  margin-top: 12px;
}
.project-meta-box {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  min-width: 96px;
  text-decoration: none;
}
.project-meta-box.is-unset { border-style: dashed; }
.project-meta-label {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.project-meta-value {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
  font-family: var(--mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 260px;
}
.project-meta-sub {
  font-size: 0.66rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
a.project-meta-file {
  cursor: pointer;
  transition: border-color .12s, background .12s;
}
a.project-meta-file:hover { border-color: var(--accent); background: var(--accent-tint); }
.project-settings-edit-btn {
  align-self: center;
  background: none;
  border: none;
  color: var(--accent);
  font-size: 0.76rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0 4px;
}
.project-settings-edit-btn:hover { text-decoration: underline; }
.project-detail-settings-edit {
  display: block;
  flex: 1 1 100%;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
}
.project-detail-settings-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 14px;
}
.project-detail-settings-row:not(:last-of-type) { margin-bottom: 14px; }
/* Same sizing as .project-new-form's fields (project-new-form-row above) — the Kunde/Beskrivelse
   row added here 2026-08-28 reuses that form's exact field-group shape so this panel and "Nyt
   projekt" read as the same component, not two different-looking forms. */
.project-detail-settings-row .field-group { min-width: 220px; flex: 1 1 220px; }
.project-detail-settings-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }
/* Slet projekt lives here now, not in the top breadcrumb next to "Alle projekter" — one click
   away behind "Rediger" so it's never hit by accident. Pushed to the opposite end from
   Annullér/Gem so it also isn't sitting right next to the buttons someone actually meant to click. */
.project-detail-settings-actions .project-delete-btn { margin-right: auto; }

/* ── Detail view (master-detail) ─────────────────────────────── */

.project-detail-head {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 16px 20px;
  margin-bottom: 16px;
}
.project-detail-head-top {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}
.project-crumb {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 0 10px;
}
.project-back-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-family: inherit;
  font-size: 0.76rem;
  font-weight: 600;
  cursor: pointer;
  padding: 5px 11px 5px 8px;
  white-space: nowrap;
  transition: color .12s, border-color .12s, background .12s;
}
.project-back-btn:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-tint); }
.project-detail-head-text { flex: 1 1 auto; min-width: 0; }
.project-detail-head-text h2 {
  margin: 0 0 2px;
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
}
.project-detail-head-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.project-reupload-btn { cursor: pointer; }
.project-delete-btn { color: var(--red); border-color: var(--red); }
.project-delete-btn:hover { background: var(--red-tint); border-color: var(--red); color: var(--red); }

.project-layout {
  display: grid;
  /* minmax(0, 1fr), not bare 1fr — without the 0 floor, a long unbreakable line (e.g. the
     raw-BOM line, .project-raw-line) forces this track past the available width and the
     whole page overflows horizontally instead of the content scrolling inside its own box. */
  grid-template-columns: 340px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.project-bom-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  max-height: 78vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* Fixed head + its own scroll body, rather than position:sticky inside one scroller — the
   head holds interactive controls (search, filter chips) that must never scroll away. */
.project-bom-list-head {
  flex-shrink: 0;
  padding: 10px 12px;
  background: var(--surface2);
  border-bottom: 1px solid var(--border);
}
.project-bom-list-scroll { flex: 1 1 auto; overflow-y: auto; }
.project-list-empty {
  padding: 24px 14px;
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.project-list-search {
  width: 100%;
  font-family: inherit;
  font-size: 0.8rem;
  padding: 7px 10px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}
.project-list-search:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

/* Status counts live on the filter buttons themselves — the same numbers as the old
   read-only dashboard row, but each one now filters the list to those lines. */
.project-filter-chips { display: flex; flex-wrap: wrap; gap: 4px 5px; margin-top: 9px; }
.project-fchip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: inherit;
  font-size: 0.68rem;
  font-weight: 600;
  padding: 3px 7px;
  border-radius: 999px;
  border: 1px solid var(--border-2);
  background: var(--surface);
  color: var(--text-2);
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
.project-fchip:hover { border-color: var(--text-faint); }
.project-fchip-count { font-family: var(--mono); font-size: 0.66rem; color: inherit; opacity: 0.75; }
.project-fchip.active {
  background: var(--text);
  border-color: var(--text);
  color: var(--surface);
}
.project-fchip.active .project-fchip-count { color: var(--surface); opacity: 0.75; }

/* Idle chips carry a tint of their own status colour — the same green/yellow/red/blue used by
   the dots in the list below and the ⚠ banner on a line with a reference issue — so "3 fejl" is
   readable as a colour, not just a number, before it's ever clicked. Only the active chip gets
   the solid fill; both states use the SAME hue so clicking one never looks like a different
   status was selected. */
.project-fchip-ok   { color: var(--green);  border-color: color-mix(in srgb, var(--green) 35%, transparent);  background: var(--green-tint); }
.project-fchip-warn { color: var(--yellow); border-color: color-mix(in srgb, var(--yellow) 35%, transparent); background: var(--yellow-tint); }
.project-fchip-err  { color: var(--red);    border-color: color-mix(in srgb, var(--red) 35%, transparent);    background: var(--red-tint); }
.project-fchip-ref  { color: var(--blue);   border-color: color-mix(in srgb, var(--blue) 35%, transparent);   background: var(--blue-tint); }
.project-fchip-dnp  { color: var(--text-faint); }
.project-fchip-manual { color: var(--blue); border-color: color-mix(in srgb, var(--blue) 35%, transparent); background: var(--blue-tint); }
.project-fchip-ok.active   { background: var(--green); border-color: var(--green); }
.project-fchip-warn.active { background: var(--yellow); border-color: var(--yellow); }
.project-fchip-err.active  { background: var(--red); border-color: var(--red); }
.project-fchip-ref.active  { background: var(--blue); border-color: var(--blue); }
.project-fchip-dnp.active  { background: var(--text-faint); border-color: var(--text-faint); color: var(--surface); }
.project-fchip-manual.active { background: var(--blue); border-color: var(--blue); color: var(--surface); }

.project-bom-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 9px 13px;
  background: none;
  border: none;
  border-left: 3px solid transparent;
  border-bottom: 1px solid var(--divider);
  cursor: pointer;
  font-family: inherit;
  color: var(--text-2);
}
.project-bom-row:last-child { border-bottom: none; }
.project-bom-row:hover { background: var(--surface2); }
.project-bom-row.active {
  background: var(--accent-tint);
  border-left-color: var(--accent);
}
.project-bom-row.active .project-bom-row-desig { color: var(--accent-ink); }

.project-dot {
  flex-shrink: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
}
.project-dot-ok      { background: var(--green);  box-shadow: 0 0 0 2px var(--green-tint); }
.project-dot-warn    { background: var(--yellow); box-shadow: 0 0 0 2px var(--yellow-tint); }
.project-dot-err     { background: var(--red);    box-shadow: 0 0 0 2px var(--red-tint); }
.project-dot-pending { background: var(--border-2); }
.project-dot-dnp     { background: var(--text-faint); }
.project-dot-manual  { background: var(--blue); box-shadow: 0 0 0 2px var(--blue-tint); }

.project-bom-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.project-bom-row-desig {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-bom-row-sub {
  font-size: 0.7rem;
  color: var(--text-muted);
  font-family: var(--mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-bom-row-dnp {
  flex-shrink: 0;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  background: var(--surface3);
  border-radius: 4px;
  padding: 2px 5px;
}
.project-bom-row-warn {
  flex-shrink: 0;
  font-size: 0.78rem;
  color: var(--yellow);
}

/* The detail side is a stack of cards on the page background rather than one large box —
   each concern (identity, chosen part, approved MPNs, prices, source rows, notes) reads as
   its own unit, matching the card language used by the Søg tab. */
.project-detail-panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

.project-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 14px 16px 16px;
  min-width: 0;
}
.project-panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.project-panel-head h3 {
  margin: 0;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.project-panel-meta {
  font-size: 0.72rem;
  color: var(--text-faint);
  font-family: var(--mono);
  white-space: nowrap;
}
.project-panel-empty {
  font-size: 0.82rem;
  color: var(--text-muted);
  padding: 14px;
  text-align: center;
  background: var(--surface2);
  border-radius: var(--radius-sm);
}
/* "Vis flere" under a candidate table — asks the backend for more alternates/candidates past its
   own default cap of 10 (on request, up to 60 — see ../CLAUDE.md). */
.project-suggest-more-row { margin-top: 10px; text-align: center; }
.project-suggest-more-btn { font-size: 0.76rem; padding: 6px 14px; }

/* ── Line quantities / issue banner ───────────────────────────
   These used to live in a separate identity header card above the references; they now sit
   inside the references card itself (.project-refs-panel), which is where the numbers come
   from. The classes are unchanged so the styling below is shared verbatim. */
.project-line-issue {
  margin-top: 12px;
  background: var(--yellow-tint);
  color: var(--yellow);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-size: 0.79rem;
  font-weight: 600;
}
/* Why this line is Fejl/Advarsel (2026-08-26) — same shape as .project-line-issue above, colored
   by severity (projectLineStatusReason()) so the two banners read apart at a glance. */
.project-line-status-reason {
  margin-top: 12px;
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-size: 0.79rem;
  font-weight: 600;
}
.project-line-status-reason-warning { background: var(--yellow-tint); color: var(--yellow); }
.project-line-status-reason-error { background: var(--red-tint); color: var(--red); }

/* Flex rather than a grid: the number of fields varies per line (IPN/beskrivelse/parametre
   are only rendered when set), and a fixed grid left conspicuous empty cells whenever a line
   had few of them. Flex packs whatever exists to the left. */
.project-line-head-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--divider);
}
.project-line-field-wide { flex: 1 1 260px; }
.project-line-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.project-line-field > span {
  font-size: 0.63rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.project-line-field b {
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text);
  overflow-wrap: break-word;
}
.project-line-qty-input {
  width: 100%;
  max-width: 92px;
  font-family: var(--mono);
  font-size: 0.85rem;
  padding: 5px 8px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}
.project-line-qty-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
.project-line-dnp {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  flex-shrink: 0;
  white-space: nowrap;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
}
.project-line-dnp:hover { border-color: var(--border-2); color: var(--text-2); }
.project-line-delete-btn {
  flex-shrink: 0;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--text-muted);
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 5px 10px;
  cursor: pointer;
}
.project-line-delete-btn:hover { color: var(--red); border-color: var(--red); background: var(--red-tint); }

/* Per-line comment (project-line-head-grid's own wide field) — same look as .project-refs-input,
   just smaller/lighter since it's a secondary field, not the primary editable value on the panel. */
.project-line-notes-input {
  width: 100%;
  resize: vertical;
  font-size: 0.8rem;
  line-height: 1.5;
  padding: 7px 9px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}
.project-line-notes-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

/* ── Approved components ─────────────────────────────────────
   Rendered with .project-cand-table — the same markup as the candidate lists below it — so the
   only rules needed here are the empty state and the remove button. */
.project-approved-empty {
  background: var(--yellow-tint);
  color: var(--yellow);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 0.8rem;
  font-weight: 600;
  margin-bottom: 10px;
}
/* The row matching chosenMpn, in the approved-components table — the only place chosenMpn is
   shown (2026-08-26: the separate "Valgt komponent" summary line above the table was removed). */
.project-cand-row-chosen { background: var(--green-tint); }
.project-cand-badge-chosen { color: var(--green); background: transparent; }
/* A candidate whose own specs fully match an already-approved component's specs (on request —
   "lyser spec grøn hvis den passer med spec for de godkendte komponenter"), same field-match rule
   as the "Samme specifikationer" IPN-less grouping tier. A left accent bar rather than
   .is-approved's full-row tint — this row is NOT approved, only spec-equivalent to something that
   is, and reusing the same full green background would read as if it already were. */
.project-cand-table tbody tr.project-cand-row-specmatch td:first-child { border-left: 3px solid var(--green); }
.project-cand-badge-specmatch { color: var(--green); background: var(--green-tint); }
.project-alt-del-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--text-faint);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 2px 5px;
  border-radius: 4px;
}
.project-alt-del-btn:hover { color: var(--red); background: var(--red-tint); }

/* Neutral surface with a yellow edge + heading, NOT a yellow-tinted fill — the rows inside
   carry their own status colours (yellow "low stock" pills especially), which disappear when
   set on a yellow background. */
.project-alt-box {
  margin-top: 14px;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--yellow);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
.project-alt-head {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--yellow);
  margin-bottom: 6px;
}

.project-manual-search {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--divider);
}
.project-manual-search-row { display: flex; gap: 8px; }
.project-manual-search-row input {
  flex: 1 1 auto;
  font-family: inherit;
  font-size: 0.8rem;
  padding: 6px 10px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}
.project-manual-search-row input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

/* ── Original BOM (source rows for the selected line) ────────── */
.project-original-bom-wrap {
  max-height: 240px;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.project-raw-line {
  white-space: nowrap;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--text-muted);
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}

.project-panel textarea {
  width: 100%;
  resize: vertical;
  font-family: inherit;
  font-size: 0.82rem;
  padding: 8px 10px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}
.project-panel textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}


/* ── BOM upload / column mapping ─────────────────────────────── */

.project-dropzone {
  border: 2px dashed var(--border-strong, #c2c7cf);
  border-radius: var(--radius-sm);
  background: var(--surface2);
  padding: 40px 16px;
  text-align: center;
  cursor: pointer;
  transition: border-color .12s, background .12s;
  color: var(--text-muted);
  margin: 4px 0 2px;
}
.project-dropzone:hover,
.project-dropzone.drag {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--accent-ink);
}
.project-dz-title { font-size: 0.85rem; font-weight: 600; color: var(--text); }
.project-dz-sub { font-size: 0.74rem; color: var(--text-muted); margin-top: 4px; }

.project-map-table-wrap {
  overflow: auto;
  max-height: 60vh;
  padding: 16px 22px 0;
}
.project-map-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; white-space: nowrap; }
.project-map-table thead { position: sticky; top: 0; z-index: 1; }
.project-map-selectrow th.project-map-selectcell {
  padding: 6px 10px 8px;
  background: var(--surface2);
  border-bottom: 2px solid var(--border);
}
.project-map-origname {
  margin-top: 4px;
  font-size: 0.66rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 160px;
}
.project-map-empty { font-style: italic; color: var(--text-faint); font-weight: 400; }
.project-map-dropdown {
  position: relative;
  display: inline-block;
  width: 100%;
  max-width: 190px;
  min-width: 150px;
}
.project-map-dropdown-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  padding: 5px 8px;
  font-size: 0.72rem;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-2);
  cursor: pointer;
  text-align: left;
  transition: color .12s, border-color .12s, background .12s;
}
.project-map-dropdown-btn:hover { border-color: var(--accent); }
.project-map-dropdown.is-open .project-map-dropdown-btn {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--accent-ink);
}
.project-map-dropdown-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
  min-width: 0;
}
.project-map-dd-placeholder { color: var(--text-faint); font-weight: 400; }
.project-map-dropdown-caret { font-size: 0.6rem; color: var(--text-faint); flex-shrink: 0; }
.project-map-dropdown-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 5;
  min-width: 175px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .22);
  padding: 4px;
  display: flex;
  flex-direction: column;
  white-space: normal;
}
.project-map-dd-opt {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 7px;
  border-radius: 4px;
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  white-space: nowrap;
}
.project-map-dd-opt:hover { background: var(--surface2); }
.project-map-dd-opt input { margin: 0; accent-color: var(--accent); cursor: pointer; }
.project-map-dd-opt.is-disabled { opacity: .4; cursor: not-allowed; }
.project-map-dd-opt.is-disabled input { cursor: not-allowed; }
.project-map-table tbody td {
  padding: 5px 10px;
  border-bottom: 1px solid var(--divider);
  vertical-align: middle;
  color: var(--text-2);
  font-family: var(--mono);
  font-size: 0.76rem;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-map-table tbody tr:hover td { background: var(--surface2); }
.project-map-more {
  text-align: center;
  font-style: italic;
  color: var(--text-muted);
  font-family: inherit;
  padding: 10px !important;
}
.project-map-more-btn {
  font: inherit;
  font-style: italic;
  color: var(--accent);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
.project-map-more-btn:hover { text-decoration: underline; }
.project-card-foot {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 18px 22px;
}

@media (max-width: 900px) {
  .project-layout { grid-template-columns: 1fr; }
  .project-bom-list { max-height: 320px; }
}

/* ── Per-reference editor ─────────────────────────────────────
   A line can carry 60+ references, so they flow in auto-filled columns rather than one tall
   list. Each row is [index] [designator] [need] [remove]. */

/* ── Part parameters (size, value, tolerance, …) ─────────────── */
.project-params { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
.project-param {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-size: 0.7rem;
  font-family: var(--mono);
  color: var(--text-2);
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 7px;
}
.project-param i {
  font-style: normal;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ── Lifecycle / supply-chain risk chips (TrustedParts-derived, per component) ─
   Solid-fill pills (not outlined like .project-param) so risk reads as a status signal at a
   glance, distinct from the neutral spec chips below them. */
.project-risk-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.project-risk-chip {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 3px 8px;
  border-radius: 4px;
  white-space: nowrap;
}
.project-risk-chip.risk-low    { background: var(--green-tint);  color: var(--green); }
.project-risk-chip.risk-med    { background: var(--yellow-tint); color: var(--yellow); }
.project-risk-chip.risk-high   { background: var(--red-tint);    color: var(--red); }
.project-risk-chip.risk-na     { background: var(--surface3);    color: var(--text-muted); }


/* ── Component search + candidate cards ──────────────────────── */
.project-compsearch { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--divider); }
/* One field, one tab pair above choosing what it searches (distributør vs. intern) — same
   segmented-control look as .tweak-seg in style.css, reused here rather than duplicated verbatim
   since this is a small, self-contained pair of buttons. */
.project-compsearch-tabs {
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 2px;
  gap: 2px;
  margin-bottom: 8px;
}
.project-compsearch-tab {
  background: none;
  border: none;
  padding: 5px 12px;
  font-size: 0.72rem;
  font-weight: 600;
  font-family: inherit;
  color: var(--text-muted);
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.1s;
}
.project-compsearch-tab:hover { color: var(--text); }
.project-compsearch-tab.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }
.project-compsearch-row { display: flex; gap: 8px; }
.project-compsearch-row input {
  flex: 1 1 auto;
  font-family: inherit;
  font-size: 0.8rem;
  padding: 7px 10px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}
.project-compsearch-row input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
/* ── Candidate table (alternate suggestions + manual search hits) ─
   A table rather than a card stack: comparing candidates means reading one field down a column.
   No overflow on the wrapper — the values column wraps its chips instead of scrolling, which
   keeps the hover popups from being clipped by a scroll container. */
/* At most six rows before the table scrolls: a 22-alternate suggestion list pushed the price
   panel far below the fold, and six is enough to compare without leaving the card. Rows are a
   fixed one line high (see tbody td), so the cap can be a plain multiple of the row height.
   overflow-y here is exactly why the hover popups are position:fixed rather than absolute. */
.project-cand-table-wrap {
  --project-cand-row-h: 38px;
  margin-top: 12px;
  min-width: 0;
  max-height: calc(var(--project-cand-row-h) * 6 + 30px);
  overflow-y: auto;
}
/* The header has to stay put while the body scrolls under it. */
.project-cand-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
}
/* On a cramped window the seven columns cannot all shrink and still be readable — the values
   column collapsed to one chip per line — so below this width the table scrolls inside its own
   box instead. The trade-off is that a hover popup is clipped by that scroll container; at the
   width this tab is actually used at (main widens to 1728px here) there is no scroller and the
   popups work normally. */
@media (max-width: 1440px) {
  .project-cand-table-wrap { overflow-x: auto; }
  /* Nine/ten columns (Total lager added 2026-08-28, Fastlås pin column added 2026-09-14) need
     this much before Værdier is squeezed to nothing and the headers start wrapping one letter
     per line. */
  .project-cand-table { min-width: 1260px; }
}
.project-cand-table {
  width: 100%;
  /* table-layout:fixed so the column widths below are honoured rather than being driven up by
     the longest MPN — an auto table cannot shrink below its content and would push the whole
     page into a horizontal scroll on a narrow window. */
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 0.78rem;
}
.project-cand-table thead th {
  text-align: left;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0 8px 7px;
  border-bottom: 1px solid var(--border-2);
  /* table-layout:fixed will not widen a column for a long header, so let the label wrap
     instead of spilling over the next one. */
  overflow-wrap: anywhere;
}
.project-cand-th-sort { cursor: pointer; user-select: none; }
.project-cand-th-sort:hover { color: var(--text-2); }
.project-cand-th-sort.active { color: var(--accent-ink); }
.project-cand-sortarrow { margin-left: 4px; font-size: 0.6rem; opacity: 0.55; }
.project-cand-th-sort.active .project-cand-sortarrow { opacity: 1; }
/* Every cell is exactly one line high. A row that wrapped (a long MPN, a two-word manufacturer,
   one chip too many) broke the scan-down-a-column rhythm the table exists for, and made the
   6-row scroll height below unpredictable. Overflow is clipped with an ellipsis; the hover
   popups carry the complete text. */
.project-cand-table tbody td {
  padding: 7px 8px;
  height: var(--project-cand-row-h);
  border-bottom: 1px solid var(--divider);
  vertical-align: middle;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-cand-table tbody tr:last-child td { border-bottom: none; }
.project-cand-table tbody tr:hover td { background: var(--surface2); }
.project-cand-table tbody tr.is-approved td { background: var(--green-tint); }
/* Column widths live on the header cells (table-layout:fixed reads only those) and are keyed by
   data-col rather than position, because the source column is omitted when every candidate is
   local. Parametre gets no explicit width so it absorbs whatever is left (including the space
   Pakke used to take, since that column was removed 2026-08-25 — Size/Package is now a chip
   inside Parametre) — but it does get a min-width floor, see below.
   mpn widened 20%→24% (2026-08-27) then narrowed 24%→18% (2026-08-28, on request, once Total
   lager/Pris-sort pushed Parametre's actual free space below its own floor at the narrow-viewport
   table width — "parameter i visning skal altid vise de 4 første parameter, gør mpn colonnen lidt
   mindre") — MPN+badge(s) still wraps onto up to 2 lines rather than clipping (see the mpn cell
   rule further below), so the trade-off is more MPN cells wrapping to their 2nd line, not any text
   actually lost. */
.project-cand-table th[data-col="mpn"]       { width: 18%; }
.project-cand-table th[data-col="mfr"]       { width: 14%; }
/* px, not %, for the columns whose content has a fixed size: an IPN tag or a risk pill must not
   be squeezed narrower than its own text, and Parametre (the one auto column) absorbs the rest. */
.project-cand-table th[data-col="ipn"]       { width: 124px; }
/* Floor so the fixed-width columns (Total lager/Pris included) and MPN/Manufacturer's percentage
   share never squeeze Parametre below room for its first 4 chips — the ACTUAL guarantee the "always
   show the first 4" request needs is this floor plus the narrow-viewport table's own min-width
   (below) leaving enough room above the other columns' combined width for it to be honoured;
   190px→210px (2026-08-28) for a bit more margin now that the row has 2 more fixed-width columns
   than when this floor was first set (2026-08-27, "mindst 4 parameter kan læses... er der plads
   til flere skal de stadig vises") — chips are short (category abbreviation, package, a value, a
   tolerance, …), so this still comfortably fits 4 with room to spare before the "+N" popup kicks in. */
.project-cand-table th[data-col="values"]    { min-width: 210px; }
/* Livscyklus/Forsyning narrowed and pulled together (2026-08-25, on request — the two risk pills
   are a few characters each and don't need a full column's worth of air between them). The
   tighter width relies on projectCandRiskCellHtml only ever emitting Low/Med/High/NA now (see
   PROJECT_RISK_BUCKET) — the old "Low-Med" text would have overflowed at this width. */
.project-cand-table th[data-col="lifecycle"] { width: 62px; }
.project-cand-table th[data-col="supply"]    { width: 62px; }
/* "Total lager" (2026-08-28): a plain right-ish number, same footprint class as Pris. */
.project-cand-table th[data-col="stock"]     { width: 78px; }
/* Widened 92px→132px (2026-09-14, on request — "0.0517 DKK" was clipping to "0.0517 DK…" at the
   old width, worse once a supplier name is appended, e.g. "1.5697 DKK · Future Electronics"). */
.project-cand-table th[data-col="price"]     { width: 132px; }
/* The MPN cell packs MPN + optional "+N MPN" hint + badge(s) onto one line. Badges now wrap onto
   their own line instead of squeezing the MPN (2026-08-27, on request — "Godkendt manuelt" +
   "★ Valgt" made a long MPN on C1 unreadable): flex-wrap lets .project-cand-mpn/.project-mpn-wrap
   claim the full row width first, dropping any badge that doesn't fit to a second line rather than
   shrinking the MPN itself. Hard-capped at exactly 2 lines (min/max-height + overflow, since
   line-clamp doesn't apply cleanly to a flex row mixing text with badge chips) — on request, "det
   skal ikke være over 2 linjer": the widened mpn column (24%, above) makes a 3rd line rare in the
   first place, this is the backstop for the rare pathological case (an unusually long MPN).
   Needs the extra `.project-cand-table td` specificity to actually win over `.project-cand-table
   tbody td`'s own `height`/`white-space`/`overflow` a few rules up — that rule is what keeps
   every OTHER cell a hard single line, and would otherwise silently clip this one back down to one
   line and re-crop the MPN despite flex-wrap being set. */
.project-cand-table td.project-cand-td-mpn {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 6px; min-width: 0;
  height: auto; min-height: var(--project-cand-row-h);
  max-height: calc(var(--project-cand-row-h) * 2);
  white-space: normal;
  overflow: hidden;
}
.project-cand-td-mfr { color: var(--text-2); }
.project-cand-td-ipn { font-family: var(--mono); }
/* Our own internal number: given the accent treatment because "we already carry this part" is
   the strongest signal in the table — those rows are sorted to the top for the same reason. */
.project-ipn-tag {
  display: inline-block;
  font-size: 0.73rem;
  font-weight: 600;
  color: var(--accent-ink);
  background: var(--accent-tint);
  border-radius: 4px;
  padding: 2px 7px;
}
.project-ipn-more { margin-left: 4px; font-size: 0.68rem; color: var(--text-faint); cursor: help; }
/* Livscyklus/Forsyning sit right next to each other — tighter side padding than the table default
   (7px 8px) so the two pills read as one grouped pair, not two full-width columns. */
.project-cand-td-risk { padding-left: 4px; padding-right: 4px; }
.project-cand-td-risk + .project-cand-td-risk { padding-left: 3px; }
.project-cand-td-stock { font-family: var(--mono); }
.project-cand-td-price { font-family: var(--mono); }
.project-cand-price { cursor: help; }
.project-cand-price-unreachable { color: var(--yellow); }
.project-cand-table th.project-cand-th-act, .project-cand-td-act { width: 96px; }
.project-cand-td-act { text-align: right; }
.project-cand-td-act button { padding: 4px 10px; font-size: 0.72rem; white-space: nowrap; }
/* Fastlås/pin column (2026-09-14) — only present on the "Godkendte komponenter" table
   (projectApprovedComponentsHtml), a plain row of small quantity chips per approved candidate.
   Not part of PROJECT_CAND_COLUMNS/projectCandidateTableHtml's shared table, so this never shows
   up on "Forslag til alternativ"/manual/spec search. */
/* Widened 150px→198px (2026-09-14, on request) — 5 chips (up to 3-digit quantities) need ~168px
   of content once cell padding is counted; the old width wrapped to 2 lines on every project with
   5+ production quantities, which is the common case, not an edge case. */
.project-cand-table th[data-col="pin"] { width: 198px; }
.project-cand-td-pin { padding-top: 4px; padding-bottom: 4px; }
.project-cand-pin-chips { display: flex; flex-wrap: wrap; gap: 3px; }
.project-pin-chip {
  min-width: 22px;
  padding: 2px 5px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface);
  color: var(--text-faint);
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.66rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color .12s, background .12s, color .12s;
}
.project-pin-chip:hover { border-color: var(--accent); color: var(--accent-ink); }
.project-pin-chip.is-pinned { border-color: var(--accent); background: var(--accent); color: #fff; }
.project-pin-chip.is-pinned:hover { background: var(--accent-ink); border-color: var(--accent-ink); }
.project-pin-chip.is-unmet { border-color: var(--yellow); background: var(--yellow); color: #fff; }
.project-pin-chip.is-unmet:hover { border-color: var(--yellow); background: var(--yellow); }
/* Pin-aware "chosen" badge — same slot as .project-cand-badge-chosen, distinct color so a pinned
   pick reads apart from the normal "backend picked cheapest" one. */
.project-cand-badge-pin { color: var(--accent-ink); background: var(--accent-tint); }
.project-cand-badge-pin-unmet { color: var(--yellow); background: var(--yellow-tint); }
/* A pin that couldn't actually be satisfied (no valid price/stock) — warning tint, distinct from
   the green .project-cand-row-chosen so it never reads as a normal successful pick. */
.project-cand-row-pin-unmet { background: var(--yellow-tint); }
.project-cand-dash { color: var(--text-faint); }
.project-cand-ds { margin-left: 6px; color: var(--accent); text-decoration: none; font-weight: 700; }
.project-cand-ds:hover { text-decoration: underline; }
.project-cand-mpn {
  font-family: var(--mono);
  font-size: 0.87rem;
  font-weight: 600;
  color: var(--text);
  min-width: 0;
  /* Never truncated (2026-08-27, on request — "sørg for MPN altid kan læses"): wraps onto a
     second line inside its column instead of ellipsis-clipping when the row is tight, so the full
     MPN is always readable regardless of how many badges sit next to it. */
  overflow-wrap: break-word;
  white-space: normal;
}
.project-mpn-wrap { display: flex; align-items: center; gap: 5px; min-width: 0; }
.project-cand-badge {
  flex-shrink: 0;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--accent-tint);
  border-radius: 4px;
  padding: 3px 7px;
}
.project-cand-mfr { font-size: 0.75rem; color: var(--text-muted); }
.project-cand-desc { font-size: 0.73rem; color: var(--text-muted); margin-top: 5px; }

/* ── Key values ("Capacitor · 22µF · 0805 · 16V · ±20%") ──────
   Bare values, no field labels: the point is to read the part's identity as one line. The field
   name is on each chip's tooltip, and every remaining parameter is in the hover list below. */
.project-kv-group {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
/* The chips scroll-clip inside their own line; the "+N" marker sits outside that box so it is
   always visible at the end of the row no matter how many chips were cut off. */
.project-kv-line {
  display: flex;
  flex-wrap: nowrap;
  gap: 4px;
  min-width: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 18px), transparent 100%);
          mask-image: linear-gradient(to right, #000 calc(100% - 18px), transparent 100%);
}
.project-kv-group > .project-kv-more { flex-shrink: 0; }
.project-kv {
  font-size: 0.72rem;
  font-family: var(--mono);
  color: var(--text-2);
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 7px;
  white-space: nowrap;
}
.project-kv-more { color: var(--text-faint); cursor: help; }
.project-cand-td-values .project-kv-group { margin-top: 0; }

/* Full parameter list (or every MPN under one IPN), one hover away.
   position:fixed, not absolute: the candidate table scrolls (max 6 rows), and an absolutely
   positioned popup would be clipped by that scroll container. A fixed element's containing block
   is the viewport, so ancestor overflow can't touch it — the price is that the coordinates must
   be set in JS on hover (projectWireDetail's .project-pop-wrap handler), which is also why there
   is no :hover rule here. */
.project-kv-pop {
  display: none;
  position: fixed;
  z-index: 60;
  min-width: 260px;
  max-width: 420px;
  max-height: 340px;
  overflow-y: auto;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg, 0 8px 24px rgba(0,0,0,0.22));
  white-space: normal;
}
.project-pop-wrap { position: relative; }
.project-kv-pop-head {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 7px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--divider);
}
/* The MPN list uses the same dl, but the MPN is the value worth the mono/emphasis, not the
   manufacturer — so this one flips the dt/dd styling. */
.project-mpn-wrap .project-kv-pop dt {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text);
}
.project-mpn-wrap .project-kv-pop dd { font-family: inherit; color: var(--text-muted); font-size: 0.72rem; }
.project-kv-pop dl { margin: 0; display: grid; grid-template-columns: 1fr; gap: 3px; }
.project-kv-pop dl > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  font-size: 0.72rem;
}
.project-kv-pop dt {
  color: var(--text-muted);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.project-kv-pop dd { margin: 0; font-family: var(--mono); color: var(--text); text-align: right; }

.project-line-perboard { font-family: var(--mono); }
.project-line-perboard small { color: var(--text-muted); font-weight: 400; font-size: 0.68rem; }
/* Extra MPN/Manufacturer pairs the BOM's own extra columns carried (line.alternates — see
   projectBuildLinesFromImport's column-index pairing) — shown under the primary pair, muted since
   these are unreviewed BOM data, not an approved component (2026-09-14, on request). */
.project-line-bom-alts { margin-top: 4px; display: flex; flex-direction: column; gap: 2px; }
.project-line-bom-alt {
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--text-muted);
}

/* ── References: one editable field for the whole line ───────── */
.project-refs-input {
  width: 100%;
  resize: vertical;
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.6;
  padding: 9px 11px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
}
.project-refs-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
.project-refs-hint { font-size: 0.72rem; color: var(--text-muted); margin-top: 7px; }

/* The references card now also carries the quantities and the do-not-place switch that used to
   sit in a separate identity header above it — every one of those numbers is derived from the
   reference list in this same card, so they belong next to it. The accent edge marks it as the
   line's primary card, the role the removed header used to play. */
.project-refs-panel { border-left: 3px solid var(--accent); }
.project-refs-panel.is-dnp { border-left-color: var(--text-faint); opacity: 0.85; }
.project-refs-panel .project-panel-head { align-items: center; }
.project-refs-panel .project-panel-meta { margin-right: auto; }
.project-refs-panel .project-line-head-grid { align-items: flex-start; }
/* The BOM's own imported parameters sit here (next to Total behov) rather than on the approved
   component — they describe the uploaded line, not the part we picked for it, and showing them
   in both places made the same values look duplicated. */
.project-params-bom { margin-top: 0; }
