/* Job search — an email client for job postings.
   Dark-first with a light override, no external fonts or CDNs. Variable
   names deliberately match Clauditorium's public/styles.css so the two apps
   look like they came from the same place. */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --bg:            #161616;
  --surface:       #1e1e1e;
  --surface-2:     #262626;
  --surface-3:     #2f2f2f;
  --border:        #363636;
  --border-light:  #444;
  --text:          #e6e4e1;
  --text-muted:    #8b8b8b;
  --text-dim:      #6a6a6a;
  --accent:        #d97757;
  --accent-dim:    #a85a40;
  --blue:          #6b9fd4;
  --green:         #6cc07a;
  --amber:         #e0a94a;
  --red:           #e07070;
  --radius:        12px;
  --radius-sm:     8px;
  --radius-xs:     5px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: light) {
  :root {
    --bg:           #f6f5f3;
    --surface:      #ffffff;
    --surface-2:    #efeeec;
    --surface-3:    #e4e2df;
    --border:       #d8d5d1;
    --border-light: #c4c0bb;
    --text:         #1f1e1d;
    --text-muted:   #6b6864;
    --text-dim:     #8e8a85;
    --accent:       #c25c38;
    --accent-dim:   #a04a2c;
    --blue:         #2f6fb0;
    --green:        #3d8b4d;
    --amber:        #a97a10;
    --red:          #b83c3c;
  }
}

html, body { height: 100%; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* Kept in sync with .topbar's real rendered height by a ResizeObserver in
     app.js — .topbar grows (run progress, the resume banner, flex-wrap on
     narrow screens) and the table header needs to sit just below it, not
     under a hardcoded guess. */
  --topbar-h: 0px;
}

.hidden { display: none !important; }
.spacer { flex: 1; }
.hint { color: var(--text-muted); font-size: 13px; }

/* ── Buttons & inputs ─────────────────────────────────────────────────── */

.btn {
  font: inherit;
  border: 1px solid var(--border-light);
  background: var(--surface-2);
  color: var(--text);
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.btn:hover:not(:disabled) { background: var(--surface-3); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-sm { padding: 5px 10px; font-size: 13px; }
.btn-block { width: 100%; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #17110e; font-weight: 600; }
.btn-primary:hover:not(:disabled) { background: #e28a6b; }
.btn-ghost { background: transparent; }

.input, .select-sm, textarea {
  font: inherit;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  width: 100%;
}
.input:focus, textarea:focus, .select-sm:focus { outline: none; border-color: var(--accent-dim); }
.select-sm { width: auto; padding: 5px 8px; font-size: 13px; border-radius: var(--radius-xs); }
textarea { resize: vertical; line-height: 1.55; }

.linkish-inline {
  background: none; border: none; color: var(--text-muted);
  font: inherit; font-size: 13px; cursor: pointer; text-decoration: underline; padding: 0 4px;
}
.linkish-inline:hover { color: var(--text); }

/* ── Login ────────────────────────────────────────────────────────────── */

.login-screen {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); padding: 20px; z-index: 100;
}
.login-card {
  width: 100%; max-width: 380px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 28px;
  display: flex; flex-direction: column; gap: 12px;
}
.login-card h1 { font-size: 20px; }
.login-error {
  color: var(--red); font-size: 13px;
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--red) 35%, transparent);
  border-radius: var(--radius-xs); padding: 8px 10px;
}
.login-divider {
  display: flex; align-items: center; gap: 10px;
  color: var(--text-dim); font-size: 12px; margin: 4px 0;
}
.login-divider::before, .login-divider::after {
  content: ''; flex: 1; height: 1px; background: var(--border);
}
a.btn { text-align: center; text-decoration: none; display: block; }

/* ── Top bar ──────────────────────────────────────────────────────────── */

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

.topbar {
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  position: sticky; top: 0; z-index: 20;
}
.topbar-main {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px; flex-wrap: wrap;
}
.app-title { font-size: 17px; font-weight: 600; }

.pill {
  background: var(--accent); color: #17110e;
  font-size: 12px; font-weight: 700;
  border-radius: 999px; padding: 2px 9px;
}

.run-status { font-size: 13px; color: var(--text-muted); }
.run-status.stale { color: var(--amber); }
.run-status.failed { color: var(--red); }

.user-menu { display: flex; align-items: center; gap: 8px; }

.run-progress {
  padding: 8px 16px 12px;
  border-top: 1px solid var(--border);
  font-size: 13px; color: var(--text-muted);
  display: flex; align-items: flex-start; gap: 12px;
}
.run-progress-list {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: 4px;
  max-height: 220px; overflow-y: auto;
}
.run-progress .task-line { display: flex; gap: 8px; align-items: baseline; }
.run-progress .task-line .st { width: 14px; flex-shrink: 0; }
.run-progress .task-line.ok .st { color: var(--green); }
.run-progress .task-line.failed .st { color: var(--red); }
.run-progress .task-line.running .st { color: var(--amber); }
.run-progress .task-line.cancelled .st,
.run-progress .task-line.skipped .st { color: var(--text-muted); }
.run-stop { flex-shrink: 0; }

/* ── Collapsible panels (brief, sources) ──────────────────────────────── */

.panel { border-bottom: 1px solid var(--border); background: var(--surface); }
.panel-toggle {
  width: 100%; text-align: left; font: inherit; font-size: 14px;
  background: transparent; border: none; color: var(--text);
  padding: 10px 16px; cursor: pointer;
  display: flex; align-items: center; gap: 8px;
}
.panel-toggle:hover { background: var(--surface-2); }
.panel-toggle .chev { display: inline-block; transition: transform .12s; color: var(--text-muted); }
.panel-toggle.open .chev { transform: rotate(90deg); }
.panel-sub { font-weight: normal; }
.panel-body { padding: 4px 16px 16px; display: flex; flex-direction: column; gap: 10px; }
.panel-actions { display: flex; align-items: center; gap: 10px; }

.sources-list { display: flex; flex-direction: column; gap: 6px; }
.source-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: start;
  padding: 8px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.source-row .src-name { font-size: 14px; }
.source-row .src-hint { font-size: 12px; color: var(--text-muted); }
.source-row .src-kind {
  font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em;
}
.source-row .src-tier { margin-top: 4px; }
.source-add { display: grid; grid-template-columns: 1fr 2fr auto; gap: 8px; }

/* ── Settings panel ───────────────────────────────────────────────────── */

.settings-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px 16px;
}
.settings-grid label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 13px; color: var(--text-muted);
}
.settings-grid select, .settings-grid input { color: var(--text); }

/* ── Resume banner ────────────────────────────────────────────────────── */

.resume-banner {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 16px; border-top: 1px solid var(--border);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  color: var(--amber); font-size: 13px;
}

/* ── Tabs & filters ───────────────────────────────────────────────────── */

.tabs { display: flex; gap: 2px; padding: 0 16px; border-bottom: 1px solid var(--border); background: var(--surface); }
.tab {
  font: inherit; font-size: 14px; background: transparent; border: none;
  color: var(--text-muted); padding: 10px 14px; cursor: pointer;
  border-bottom: 2px solid transparent;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--text); border-bottom-color: var(--accent); }

.filters {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 16px; border-bottom: 1px solid var(--border);
  background: var(--surface); font-size: 13px;
}
.filter-search { width: 260px; padding: 6px 9px; font-size: 13px; }
.filter-num { width: 100px; padding: 5px 8px; font-size: 13px; }
.filter-inline { display: flex; align-items: center; gap: 6px; color: var(--text-muted); }
.check { display: flex; align-items: center; gap: 6px; color: var(--text-muted); cursor: pointer; }
.radius-filter {
  color: var(--blue);
  background: color-mix(in srgb, var(--blue) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--blue) 35%, transparent);
  border-radius: var(--radius-xs); padding: 3px 8px;
}

/* ── Main / list ──────────────────────────────────────────────────────── */

.main { display: flex; flex: 1; min-height: 0; align-items: stretch; }
/* overflow-x: auto here was the sticky-header bug: per spec, setting one
   overflow axis to a value other than 'visible' forces the OTHER axis to
   compute to 'auto' too, so this became a scroll container along both axes —
   even though nothing here actually overflows it (the pane grows to its
   content's full height; the page, not this box, is what scrolls). A
   position:sticky descendant sticks to its nearest scroll-container
   ancestor's scrollport, not the viewport, so the header "stuck" to a box
   that itself just scrolled away with the page. `clip` does not create a
   scroll container (no scrollbar, nothing to anchor sticky to), so it's safe
   here precisely because nothing needs to actually scroll horizontally: the
   table is table-layout:fixed at 100% width, and columns already drop out
   under 900px (see the media query below). If real horizontal overflow is
   ever needed again here, that needs its own explicitly-scoped scroll
   container, not a blanket auto on the pane. */
.list-pane { flex: 1; min-width: 0; overflow-x: clip; }

/* table-layout: fixed so one pathological cell — a salary line like
   "£37,520 per year for 32 hours (£46,900 FTE), or a freelance fee of up to
   £45,024" — cannot push Location and Posted off the right-hand edge. Long
   values are clamped to two lines; the full text is in the cell's title. */
.job-table { width: 100%; border-collapse: collapse; font-size: 14px; table-layout: fixed; }
.job-table thead th {
  text-align: left; font-weight: 600; font-size: 12px;
  color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em;
  padding: 8px 10px; border-bottom: 1px solid var(--border);
  /* top: 0 would stick it directly under the ALSO-sticky .topbar (both at
     z-index above content), hiding it behind that opaque bar. --topbar-h is
     the topbar's live measured height (see body{} and app.js), so this
     parks just below it instead. */
  position: sticky; top: var(--topbar-h); background: var(--bg); z-index: 5;
}
.job-table th.sortable { cursor: pointer; user-select: none; }
.job-table th.sortable:hover { color: var(--text); }
.job-table th .sort-arrow { color: var(--accent); }

.job-table tbody tr {
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.job-table tbody tr:hover { background: var(--surface-2); }
.job-table tbody tr.selected { background: var(--surface-3); }
.job-table td { padding: 9px 10px; vertical-align: top; }

.col-check { width: 26px; padding-right: 0; }
.col-check input { display: block; }
/* Was a 34px inert dot column — now a click target, and reclaimed to just
   wide enough for the button + its padding. */
.col-flag { width: 26px; padding: 9px 2px 9px 6px; }
.col-title { width: 26%; }
.col-org { width: 17%; }
.col-salary { width: 18%; }
.col-loc { width: 18%; }
.col-posted { width: 10%; white-space: nowrap; }
.col-star { width: 28px; padding: 9px 2px; }
.col-actions { width: 32px; padding: 9px 6px 9px 2px; }

/* Clamp any overlong scraped value to two lines rather than letting it set
   the row height. The clamp goes on an inner wrapper, NOT the <td> itself:
   `display: -webkit-box` on a table cell takes it out of the row layout and
   collapses the whole table. The full string stays in the cell's title
   tooltip and is always shown in full in the detail pane. */
.clamp2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

/* Unread: an accent bar down the left edge (inset box-shadow, not a
   border-left — the table is border-collapse: collapse, where a bordered <tr>
   renders inconsistently and would shift content) plus bold. Read rows grey
   the title/org back instead, so unread genuinely stands out at a glance
   rather than needing a second look to spot the bold.
   Deliberately does NOT touch `background` — that's already carrying
   :hover (--surface-2) and .selected (--surface-3); tinting unread rows too
   would muddy both of those cues.
   On .col-check specifically (not the <tr>): it's the first cell, and unlike
   .col-org/.col-posted it is NOT display:none below 900px, so the bar
   survives on a phone where those columns disappear. */
tr.unread .col-check { box-shadow: inset 3px 0 0 var(--accent); }
tr.unread .col-title a, tr.unread .col-org { font-weight: 700; }
tr:not(.unread) .col-title a, tr:not(.unread) .col-org { color: var(--text-muted); }
tr.unread .dot { background: var(--accent); border-color: var(--accent); }

/* A row the link-checker actually confirmed dead (not merely unverified —
   see CLAUDE.md "Link status is a read of the page, not an HTTP status").
   Dimmed, not hidden-with-strikethrough: still fully readable, just visually
   deprioritised. A dead row that's also been read stacks this with the grey
   text-muted above — checked in the browser and still legible; if a future
   palette change makes that combination too faint, scope the grey rule to
   `tr:not(.link-dead):not(.unread)` rather than weakening either rule. */
tr.link-dead { opacity: 0.6; }
tr.link-dead:hover { opacity: 1; }
.dead-link-marker { margin-right: 4px; cursor: default; }

.flag-btn {
  background: none; border: none; padding: 6px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-xs);
}
.flag-btn:hover { background: var(--surface-3); }
.dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  background: transparent; border: 1px solid var(--border-light);
}

.star-btn {
  background: none; border: none; padding: 4px; cursor: pointer;
  font-size: 15px; line-height: 1; color: var(--text-dim);
  border-radius: var(--radius-xs);
}
.star-btn:hover { background: var(--surface-3); color: var(--amber); }
.star-btn.active { color: var(--amber); }

/* Archive is deliberately harder to fire by accident than star: hidden until
   the row is hovered or focused, always visible below 900px because touch
   has no hover to reveal it with. */
.archive-btn {
  background: none; border: none; padding: 4px; cursor: pointer;
  font-size: 13px; line-height: 1; color: var(--text-dim);
  border-radius: var(--radius-xs);
  visibility: hidden;
}
.archive-btn:hover { background: var(--surface-3); color: var(--text); }
.job-table tbody tr:hover .archive-btn,
.job-table tbody tr:focus-within .archive-btn { visibility: visible; }

.job-table a { color: var(--text); text-decoration: none; }
.job-table a:hover { text-decoration: underline; color: var(--accent); }

.loc-uncertain { color: var(--text-dim); font-style: italic; }
.loc-remote { color: var(--blue); }

.empty-state { padding: 40px 16px; color: var(--text-muted); text-align: center; }

/* ── Detail modal ─────────────────────────────────────────────────────── */
/* Replaces the old side pane: opened by a row click, a title click (which no
   longer navigates straight to a possibly-dead link) or the map popup — one
   view for all three. */

.detail-dialog {
  width: min(480px, 92vw); max-height: 85vh;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
  padding: 20px; margin: auto; /* centers a non-fullscreen <dialog> */
  overflow-y: auto;
}
/* display MUST be scoped to [open] — an author stylesheet rule always beats
   the UA stylesheet's `dialog:not([open]) { display: none }`, regardless of
   selector specificity (origin beats specificity in the cascade). An
   unconditional `display: flex` here left the closed, empty dialog still
   rendered: border + padding + background, centred by margin:auto, sitting
   in the page's normal flow as a small solid box. */
.detail-dialog[open] {
  display: flex; flex-direction: column; gap: 12px;
}
.detail-dialog::backdrop { background: rgba(0, 0, 0, 0.5); }
.detail-dialog h2 { font-size: 17px; line-height: 1.3; padding-right: 24px; }
.detail-dialog h2 a { color: var(--text); }
.detail-org { color: var(--text-muted); }
.detail-fields { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 13px; }
.detail-fields dt { color: var(--text-muted); }
.detail-summary { font-size: 14px; color: var(--text); }
.detail-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.detail-note {
  font-size: 12px; color: var(--text-muted);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-xs); padding: 8px 10px;
}
.detail-close { position: absolute; top: 12px; right: 12px; }

.detail-link-banner {
  font-size: 13px; padding: 8px 10px; border-radius: var(--radius-xs);
  border: 1px solid var(--border);
}
.detail-link-banner.link-dead { color: var(--red); background: color-mix(in srgb, var(--red) 10%, var(--surface)); border-color: color-mix(in srgb, var(--red) 40%, var(--border)); }
.detail-link-banner.link-unverified { color: var(--amber); background: color-mix(in srgb, var(--amber) 10%, var(--surface)); border-color: color-mix(in srgb, var(--amber) 40%, var(--border)); }

.detail-link-block {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-xs); padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px; font-size: 13px;
}
.detail-link-label { color: var(--text-muted); font-size: 12px; }
/* Selectable/copyable, not just a click target — the point is this still
   works when the link itself turns out to be dead. */
.detail-link-text { font-family: monospace; font-size: 12px; word-break: break-all; user-select: all; }
.detail-link-text a { color: var(--text); text-decoration: underline; text-decoration-color: var(--border); }
.detail-link-text a:hover { color: var(--accent); text-decoration-color: currentColor; }
.detail-alt-urls ul { list-style: none; display: flex; flex-direction: column; gap: 2px; margin-top: 2px; }
.detail-alt-urls a, .detail-fallback a { font-size: 12px; word-break: break-all; }
.detail-fallback { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }

/* ── Map ──────────────────────────────────────────────────────────────── */

.map-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; }
#map { flex: 1; min-height: 420px; }
.leaflet-container { background: var(--surface-2); }
.map-unmappable {
  border-top: 1px solid var(--border); background: var(--surface);
  padding: 10px 16px; max-height: 220px; overflow-y: auto; font-size: 13px;
}
.map-unmappable h3 { font-size: 13px; color: var(--text-muted); margin-bottom: 6px; }
.map-unmappable ul { list-style: none; display: flex; flex-direction: column; gap: 4px; }
.map-unmappable a { color: var(--text); }
.map-radius-control {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 8px 10px; font-size: 13px;
  display: flex; flex-direction: column; gap: 6px; min-width: 190px;
}
/* While armed (public/js/map.js), the next map click places the radius
   centre — the crosshair is the only cue for that, since an ordinary click
   otherwise does nothing on the map at all now. */
.leaflet-container.radius-arm-mode { cursor: crosshair; }
.leaflet-popup-content { font-size: 13px; }
.leaflet-popup-content a { color: #1f1e1d; font-weight: 600; }

@media (max-width: 900px) {
  .filter-search { width: 100%; }
  .col-org, .col-posted { display: none; }
  /* Touch has no hover, so the archive button can't rely on it to reveal —
     make it permanently visible instead of permanently unreachable. */
  .archive-btn { visibility: visible; }
}

/* ── Bulk selection bar ───────────────────────────────────────────────── */

.bulk-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  font-size: 13px;
}
.bulk-bar #bulk-count { color: var(--text-muted); font-weight: 600; }

/* ── Keyboard shortcuts popover ───────────────────────────────────────── */

.shortcuts-popover {
  position: absolute; right: 16px; margin-top: 4px; z-index: 30;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 14px 18px;
  box-shadow: 0 8px 24px rgba(0,0,0,.35);
}
.shortcuts-popover h3 { font-size: 13px; margin-bottom: 8px; color: var(--text-muted); }
.shortcuts-popover dl { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; font-size: 13px; }
.shortcuts-popover dt {
  font-family: var(--mono); background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-xs); padding: 1px 6px; text-align: center; width: fit-content;
}
.shortcuts-close { position: absolute; top: 6px; right: 6px; }
