/* authhotfix1-20260725: UI cleanup + CSRF hotfix - removed the Staging
   badge and the Basic Auth indicator/text from the header, login page, and
   Settings note; added .callout.warning / .csrf-expired-banner for the new
   friendly CSRF-expired message. See app/asset_version.py for the
   deploy-staleness check this version marker feeds. */
/* auth12-20260725: auth Phase 1+2 - app-level login/logout/session on top
   of (not instead of) nginx Basic Auth, plus CSRF protection on every write
   route. New: .current-user / .logout-button / .login-* rules below. See
   app/asset_version.py for the deploy-staleness check this version marker
   feeds. */
/* fix6-20260724: fixed the top app header (header.topbar) not actually
   staying fixed while scrolling - `html` and `body` were both set to a
   non-visible overflow, which turns them into two separate nested
   scroll containers instead of one whole-page scroll, breaking
   `position: sticky`. See the comment on `body { ... overflow-x: hidden
   }` below for the full root-cause explanation. See app/asset_version.py
   for the deploy-staleness check this version marker feeds. */
/* fix5-20260724: fixed the sticky-thead/row collision bug (removed vertical
   sticky on thead, kept sticky-col-left/right), collapsed the competitor
   and trademark tables + dashboard Run Info column to fit page width
   without horizontal scroll in most cases, bare-number tab counts. See
   app/asset_version.py for the deploy-staleness check this version marker
   feeds. */
/* fix4-20260724: wide-table UX rebuild (synced top+bottom scrollbar, sticky
   thead, sticky first/last columns, row-details modal) + Delete Run button
   fix. See app/asset_version.py for the deploy-staleness check this version
   marker feeds. */
/* fix3-20260724: polished SaaS-admin styling pass. See app/asset_version.py
   for why this file carries a version marker in its very first line below -
   a prior deploy served a stale pre-fix2 copy of this exact file, and this
   comment (plus the ?v= cache-busting query string in base.html) is the
   fastest way to confirm from a browser or `curl` that the RIGHT file is
   live: `curl -s <site>/static/style.css | head -1` should show this
   version string. */

:root {
  --bg: #f4f6fb;
  --panel-bg: #ffffff;
  --border: #e2e6ee;
  --border-strong: #d3d9e4;
  --text: #1a2033;
  --text-muted: #6b7286;
  --text-faint: #9aa1b3;
  --accent: #4c5fd7;
  --accent-dark: #3a49b0;
  --accent-bg: #eceffd;
  --green: #178a4c;
  --green-bg: #e5f7ed;
  --red: #c62d3f;
  --red-bg: #fdeaec;
  --orange: #b5620a;
  --orange-bg: #fdf1e2;
  --blue: #1a5fb4;
  --blue-bg: #e3edfb;
  --gray: #5f6478;
  --gray-bg: #eceef4;
  --shadow-sm: 0 1px 2px rgba(20, 24, 50, 0.05);
  --shadow-md: 0 4px 16px rgba(20, 24, 50, 0.08);
  --radius: 10px;
  --radius-sm: 6px;
}

* { box-sizing: border-box; }

/* fix2 (kept in fix3): the page itself must NEVER scroll horizontally -
   only individual table wrappers (.table-scroll) may.

   fix6: this was the ACTUAL root cause of "the top header doesn't stay
   fixed when I scroll" reported after fix5. fix5 had put `overflow-x:
   hidden; overflow-y: auto` on BOTH `html` AND `body` "defensively".
   That was wrong: per the CSS overflow-propagation spec, when `body`
   has a non-visible overflow AND the root `<html>` element's own
   overflow is left at its default ("visible"), the browser propagates
   body's overflow behavior up to the viewport itself - there is only
   ONE scrolling box for the whole page, and a `position: sticky` child
   of body (header.topbar) sticks correctly against it.
   But once `html` ALSO gets an explicit non-visible overflow value (as
   fix5 did), that propagation rule no longer applies - propagation only
   fires when the root element's OWN computed overflow is "visible".
   Instead, `html` and `body` become two SEPARATE, nested scroll
   containers. `body` has no set height so it never actually needs its
   own scrollbar (its content just makes it tall, and `html`/the
   viewport ends up doing the actual scrolling) - but `position: sticky`
   picks its "nearest scrolling ancestor" purely from which ancestor
   declares a non-visible overflow, NOT from which ancestor actually
   scrolls. So header.topbar's sticky offset was being computed against
   body's (never-scrolling) box instead of the real page scroll,
   which is why it visually never moved/stuck as expected.
   Fix: only `body` gets `overflow-x: hidden` (its overflow-y is left
   unset - per the same overflow-axis auto-conversion behavior noted in
   past fixes, it still resolves to "auto", just on body alone this
   time). `html` is left completely untouched (default/visible), so the
   propagation rule fires and the whole document scrolls as a single
   unit again - exactly what `position: sticky` on a direct child of
   body needs. */
body { max-width: 100%; overflow-x: hidden; }

body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* ---------------------------------------------------------------------- */
/* Header / app shell                                                      */
/* ---------------------------------------------------------------------- */

header.topbar {
  background: var(--panel-bg);
  border-bottom: 1px solid var(--border);
  padding: 0 24px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-shadow: var(--shadow-sm);
  position: sticky;
  top: 0;
  z-index: 50;
}

.topbar-brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
  width: 32px; height: 32px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700;
  font-size: 15px;
  flex-shrink: 0;
}
.brand-text { display: flex; align-items: center; gap: 8px; }
.brand-name { font-size: 15px; font-weight: 700; color: var(--text); letter-spacing: -0.01em; }

.topbar-nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.topbar-nav a {
  color: var(--text-muted);
  text-decoration: none;
  font-size: 13px;
  font-weight: 600;
  padding: 7px 14px;
  border-radius: var(--radius-sm);
}
.topbar-nav a:hover { color: var(--accent); background: var(--accent-bg); }
.topbar-nav a.active { color: var(--accent); background: var(--accent-bg); }

/* auth Phase 1: the logged-in username + logout button. */
.current-user {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  margin-left: 10px;
  white-space: nowrap;
}
.inline-logout-form { display: inline-block; margin: 0; }
.logout-button {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 13px;
  font-weight: 600;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-decoration: none;
}
.logout-button:hover { color: var(--accent); background: var(--accent-bg); }

main { padding: 24px; max-width: 1440px; margin: 0 auto; }

@media (max-width: 720px) {
  header.topbar { flex-wrap: wrap; height: auto; padding: 10px 16px; gap: 8px; }
  .topbar-nav { width: 100%; justify-content: flex-start; }
  main { padding: 14px; }
}

/* ---------------------------------------------------------------------- */
/* Login page (auth Phase 1) - standalone, does not extend base.html's      */
/* header/nav shell, so it reuses the same CSS variables/panel look here.   */
/* ---------------------------------------------------------------------- */

.login-body { margin: 0; background: var(--bg); }
.login-page-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.login-card {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: 32px;
  width: 100%;
  max-width: 360px;
}
.login-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.login-card h2 { margin-top: 0; }

/* ---------------------------------------------------------------------- */
/* Cards / panels                                                          */
/* ---------------------------------------------------------------------- */

.panel {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 18px;
  box-shadow: var(--shadow-sm);
}

h2 { font-size: 15px; margin: 0 0 14px 0; font-weight: 700; letter-spacing: -0.01em; }
h3 { font-size: 12px; margin: 18px 0 10px 0; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; }
h4 { font-size: 12px; margin: 14px 0 8px 0; color: var(--text-muted); font-weight: 700; }

/* fix3: KPI summary strip at the top of the dashboard. */
.kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 18px; }
.kpi-card {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow-sm);
  border-left: 3px solid var(--border-strong);
}
.kpi-card .kpi-value { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.2; }
.kpi-card .kpi-label { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; margin-top: 2px; }
.kpi-card.kpi-running { border-left-color: var(--blue); }
.kpi-card.kpi-success { border-left-color: var(--green); }
.kpi-card.kpi-failed { border-left-color: var(--red); }
.kpi-card.kpi-queued { border-left-color: var(--orange); }

/* ---------------------------------------------------------------------- */
/* Tables                                                                   */
/* ---------------------------------------------------------------------- */

table { border-collapse: collapse; width: 100%; font-size: 13px; }
table th, table td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  /* fix2 (kept): default is normal wrapping, not nowrap - the old blanket
     nowrap with zero overflow-x containers was the root cause of the
     page-wide horizontal scrollbar. Wide free-text columns additionally
     use `.truncate` (single-line + ellipsis + tooltip); tables that need
     literal single-line cells opt in with `.nowrap`. */
  white-space: normal;
  vertical-align: top;
}
table th {
  color: var(--text-muted);
  font-weight: 700;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  background: #fafbfd;
}
table tbody tr:hover { background: #fafbfd; }
table.wrap td, table.wrap th { white-space: normal; }
table .nowrap { white-space: nowrap; }

/* fix2 (kept): the ONLY sanctioned horizontal-scroll container. */
.table-scroll {
  overflow-x: auto;
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.table-scroll table { min-width: max-content; border: none; }
.table-scroll table th:first-child, .table-scroll table td:first-child { padding-left: 14px; }

/* fix5: REMOVED the fix4 vertical "sticky thead" rule
   (`position: sticky; top: 60px`) - it caused the header row to visually
   collide with/overlap table data rows in production. Root cause: setting
   `overflow-x: auto` on .table-scroll without an explicit `overflow-y`
   triggers a CSS rule that auto-converts the missing axis's default
   ("visible") into "auto" too - so .table-scroll silently became its OWN
   vertical scroll container, even though nothing ever scrolls it
   vertically (only the page itself scrolls; .table-scroll only ever
   scrolls horizontally). Once that happens, `position: sticky` on a
   thead th nested inside .table-scroll resolves its "stick to" boundary
   against .table-scroll's own (short) box instead of the page viewport -
   so `top: 60px` docked the header 60px below the TOP OF THE TABLE, not
   60px below the top of the page, landing it directly on top of the
   table's own rows. This combination (sticky thead + a horizontally
   scrolling wrapper) is a well-known CSS conflict without a clean
   pure-CSS fix, so rather than patching it further the thead simply no
   longer sticks vertically - it stays in normal flow at the top of each
   table, which is correct and glitch-free. Sticky LEFT/RIGHT columns
   below are unaffected - those correctly stick relative to .table-scroll,
   which is exactly the container they're meant to be sticky within. */
.table-scroll table thead th {
  background: #fafbfd;
}

/* fix4: sticky first/last column - keeps the row's identifying value (or
   its action buttons) in view while scrolling horizontally through a wide
   table, so you're never scrolled so far right you can no longer tell
   which row you're looking at. Applied via an explicit opt-in class rather
   than a blanket nth-child rule, since not every table wants this (see
   report.html/dashboard.html for which columns use it). A solid background
   is required - without it, content from columns scrolling underneath
   would show through. */
.sticky-col-left {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--panel-bg);
  box-shadow: 2px 0 4px rgba(20, 24, 50, 0.06);
}
.sticky-col-right {
  position: sticky;
  right: 0;
  z-index: 2;
  background: var(--panel-bg);
  box-shadow: -2px 0 4px rgba(20, 24, 50, 0.06);
}
table thead th.sticky-col-left, table thead th.sticky-col-right { z-index: 4; background: #fafbfd; }

/* fix5: dashboard Run Info cell - Run ID + Created/Started/Finished stacked
   as small lines instead of 4 separate wide columns, to cut down how much
   the Runs table needs to scroll horizontally. */
.run-info-cell { font-size: 11px; line-height: 1.5; white-space: nowrap; }
.run-info-cell div:first-child { font-weight: 600; color: var(--text); }

/* fix4: synced top+bottom horizontal scrollbar. app.js automatically
   inserts a `.table-scroll-top` sibling above every `.table-scroll` on the
   page and keeps their scrollLeft in sync both ways - no per-template
   markup needed. This solves the "I have to scroll all the way down, then
   right, then back up" complaint for long tables: the top scrollbar is
   reachable without leaving the top of the table. */
.table-scroll-top {
  overflow-x: auto;
  overflow-y: hidden;
  max-width: 100%;
  height: 14px;
  margin-bottom: 6px;
}
.table-scroll-top > div { height: 1px; }

/* fix2 (kept): long free-text cells truncate to a single line with an
   ellipsis; the full value is still available via title="". */
.truncate {
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.truncate.wide { max-width: 420px; }

/* fix3: clamp + expand for long paragraph-style cells (Trademark tab's
   Reason / Evidence Summary / Top Goods-Services columns) - a single-line
   ellipsis with only a hover tooltip was not good enough UX for genuinely
   long text. Pure <details>/<summary> - no JS required, degrades safely:
   the summary shows a short preview, clicking it reveals the full text. */
.clamp-cell { max-width: 320px; }
.clamp-cell summary {
  cursor: pointer;
  color: var(--text);
  font-size: 13px;
  line-height: 1.45;
  list-style: none;
}
.clamp-cell summary::-webkit-details-marker { display: none; }
.clamp-cell summary .clamp-toggle-label { color: var(--accent); font-weight: 600; font-size: 11px; white-space: nowrap; }
.clamp-cell .clamp-full { margin-top: 6px; font-size: 13px; line-height: 1.45; }

/* fix2 (kept): fixed-size competitor thumbnails - never break table row
   height or column width. fix3 adds a hover affordance + wraps the image in
   a link (see report.html) so it's clear the thumbnail opens full-size. */
.thumb-link { display: inline-block; border-radius: 6px; overflow: hidden; line-height: 0; }
.thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 6px;
  display: block;
  background: var(--gray-bg);
  border: 1px solid var(--border);
  transition: transform 0.15s ease;
}
.thumb-link:hover .thumb { transform: scale(1.9); position: relative; z-index: 5; box-shadow: var(--shadow-md); }
.thumb-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-faint);
  font-size: 9px;
  text-align: center;
  line-height: 1.1;
  width: 56px;
  height: 56px;
  border-radius: 6px;
  border: 1px dashed var(--border-strong);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.badge-green  { background: var(--green-bg);  color: var(--green); }
.badge-red    { background: var(--red-bg);    color: var(--red); }
.badge-orange { background: var(--orange-bg); color: var(--orange); }
.badge-blue   { background: var(--blue-bg);   color: var(--blue); }
.badge-gray   { background: var(--gray-bg);   color: var(--gray); }

a.button, button.button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.12s ease, box-shadow 0.12s ease;
}
a.button:hover, button.button:hover { background: var(--accent-dark); }
button.button.secondary, a.button.secondary { background: var(--panel-bg); color: var(--text); border: 1px solid var(--border-strong); }
button.button.secondary:hover, a.button.secondary:hover { background: var(--gray-bg); }
button.button.danger, a.button.danger { background: var(--red); }
button.button.danger:hover, a.button.danger:hover { background: #a32233; }
button.button.small, a.button.small { padding: 4px 10px; font-size: 12px; }

.icon-link { color: var(--accent); text-decoration: none; margin-right: 10px; font-size: 12px; font-weight: 600; }
.icon-link:hover { text-decoration: underline; }

.actions-cell { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }

.muted { color: var(--text-muted); }
.error-text { color: var(--red); }
.warning-text { color: var(--orange); }

form.inline { display: inline; }

.field-row { margin-bottom: 14px; }
.field-row label { display: block; font-size: 12px; font-weight: 600; color: var(--text-muted); margin-bottom: 5px; }
.field-row input[type=text], .field-row input[type=number], .field-row select {
  width: 100%;
  max-width: 360px;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 13px;
}
.field-row input:focus, .field-row select:focus { outline: 2px solid var(--accent-bg); border-color: var(--accent); }
.field-row .help { font-size: 11px; color: var(--text-muted); margin-top: 3px; }

details.collapsible { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 16px; margin-top: 12px; background: var(--panel-bg); }
details.collapsible summary { cursor: pointer; font-weight: 700; font-size: 13px; }

.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 16px; flex-wrap: wrap; }
.tabs a {
  padding: 10px 16px;
  text-decoration: none;
  color: var(--text-muted);
  font-size: 13px;
  font-weight: 600;
  border-bottom: 2px solid transparent;
}
.tabs a:hover { color: var(--accent); }
.tabs a.active { color: var(--accent); border-bottom-color: var(--accent); }

/* fix4: source-competitor count badge inside each target-group tab label -
   kept visually secondary (smaller, muted) so the group name itself stays
   the most readable part of the tab and long labels don't overflow badly. */
.tab-competitor-count { font-size: 11px; font-weight: 500; color: var(--text-muted); }
.tabs a.active .tab-competitor-count { color: var(--accent); }

.step-list { list-style: none; margin: 0; padding: 0; }
.step-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.step-list li:last-child { border-bottom: none; }
.step-meta { color: var(--text-muted); font-size: 12px; }

.queue-position { font-size: 11px; color: var(--text-muted); font-weight: 600; }

.settings-section { margin-bottom: 22px; }
.setting-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.setting-row label { flex: 0 0 320px; font-size: 13px; font-weight: 600; }
.setting-row .setting-value { flex: 1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.setting-status { font-size: 12px; margin-left: 8px; }

.callout {
  background: var(--orange-bg);
  color: var(--orange);
  border: 1px solid #f0d9b5;
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: 12px;
  margin-bottom: 12px;
}
.callout.info { background: var(--blue-bg); color: var(--blue); border-color: #bcd6f5; }
.callout.warning { background: var(--orange-bg); color: var(--orange); border-color: #f0d9b5; }

/* auth hotfix: banner shown after a form's CSRF token expired and the
   browser was redirected back to the same page with a fresh one - see
   base.html and app/main.py's CsrfFormError handler. */
.csrf-expired-banner { max-width: 1440px; margin-left: auto; margin-right: auto; }

/* fix2 (kept): Trademark Check tab PASS/BLOCK/All filter pills. */
.filter-bar { display: flex; gap: 6px; margin-bottom: 14px; flex-wrap: wrap; }
.filter-button {
  padding: 6px 14px;
  border: 1px solid var(--border-strong);
  background: var(--panel-bg);
  border-radius: 20px;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.12s ease;
}
.filter-button:hover { border-color: var(--accent); color: var(--accent); }
.filter-button.active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* fix4: Dashboard keyword/status filter bar (plain GET form above the Runs
   table) + pagination bar (below the Runs table). Kept visually consistent
   with the existing .filter-bar pill styling above. */
.dashboard-filter-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.dashboard-filter-bar input[type="text"],
.dashboard-filter-bar select {
  padding: 7px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 13px;
  background: var(--panel-bg);
  color: var(--text);
}
.dashboard-filter-bar input[type="text"] { min-width: 200px; }

.pagination-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 14px;
  flex-wrap: wrap;
  font-size: 13px;
}
.pagination-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pagination-controls select {
  padding: 6px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 12px;
  background: var(--panel-bg);
  color: var(--text);
}
.pagination-controls a.button.disabled {
  pointer-events: none;
  opacity: 0.45;
  cursor: default;
}

/* fix2 (kept): collapsed-by-default raw traceback/error blocks. */
details.error-details { margin-top: 6px; }
details.error-details summary {
  cursor: pointer;
  color: var(--red);
  font-size: 12px;
  font-weight: 700;
}
details.error-details pre {
  background: #fafbfd;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px;
  font-size: 11px;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 320px;
  overflow-y: auto;
  margin-top: 6px;
}

/* fix2 (kept): Delete Run confirmation modal - hidden by default via
   display:none here. This exact rule is what was MISSING on the live site
   after the fix2 deploy (the deployed style.css was a stale pre-fix2 copy),
   which made the modal render as a permanently-visible inline block at the
   bottom of the dashboard instead of a hidden overlay. */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20, 24, 33, 0.5);
  align-items: center;
  justify-content: center;
  z-index: 100;
}
.modal-overlay.open { display: flex; }
.modal-box {
  background: var(--panel-bg);
  border-radius: var(--radius);
  padding: 22px;
  width: 100%;
  max-width: 480px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-md), 0 12px 40px rgba(0, 0, 0, 0.25);
}
.modal-box h3 { margin: 0 0 10px 0; font-size: 15px; text-transform: none; letter-spacing: 0; color: var(--text); font-weight: 700; }
.modal-box .modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 18px; }
.modal-box label.checkbox-row { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; margin-top: 12px; }
.modal-box label.checkbox-row input { margin-top: 2px; }

/* fix4: row-details modal - long per-concept fields (Quote Strategy, Market
   Gap, Design Direction, Typography, Visual Elements, Color Palette,
   Commercial Reasoning, Differentiation) no longer get their own columns in
   the Concepts table (that was most of what made it painfully wide) - a
   single "Details" button per row opens this modal with all of them laid
   out readably instead. */
.modal-box.wide { max-width: 640px; }
.row-details-grid { display: grid; grid-template-columns: 160px 1fr; gap: 10px 16px; margin-top: 4px; }
.row-details-grid dt { font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; margin: 0; }
.row-details-grid dd { font-size: 13px; margin: 0; line-height: 1.5; }

/* fix3: full-size image preview modal (competitor thumbnail click-to-enlarge). */
.image-modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(10, 12, 20, 0.82);
  align-items: center;
  justify-content: center;
  z-index: 110;
  cursor: zoom-out;
}
.image-modal-overlay.open { display: flex; }
.image-modal-overlay img { max-width: 92vw; max-height: 88vh; border-radius: 8px; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
