/* Bookfinder — implements DESIGN.md. Tokens only; no raw hex in components. */

:root {
  /* Surfaces */
  --bg: #111827;
  --surface: #1f2937;
  --surface-raised: #374151;
  --border: #374151;
  --border-strong: #4b5563;

  /* Text */
  --text: #f3f4f6;
  --text-body: #d1d5db;
  --text-muted: #9ca3af;
  --text-faint: #6b7280;

  /* Accent */
  --accent: #6366f1;
  --accent-hover: #818cf8;
  --accent-end: #9333ea;
  --focus: #2563eb;

  /* Status */
  --ok: #10b981;
  --warn: #cc7b19;
  --danger: #ef4444;

  /* Spacing */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px;

  /* Radius */
  --r-sm: 4px; --r-md: 8px; --r-lg: 12px; --r-full: 9999px;

  --shadow: 0 4px 12px rgb(0 0 0 / 0.35);

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Code", Consolas, monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text-body);
  font: 400 0.875rem/1.5 var(--font);
  /* Stop iOS inflating text in the narrow columns after a rotate. */
  -webkit-text-size-adjust: 100%;
}

.wrap { max-width: 1100px; margin: 0 auto; padding: 0 var(--s4); }
.spacer { flex: 1; }

:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

code { font-family: var(--mono); font-size: 0.8125rem; color: var(--text-muted); }

/* ---------- top bar + nav ---------- */

.topbar { border-bottom: 1px solid var(--border); background: var(--surface); margin-bottom: var(--s5); }
.topbar-inner { display: flex; align-items: center; gap: var(--s6); padding: 0 var(--s4); min-height: 60px; }

.wordmark {
  font-size: 1.25rem; font-weight: 700; text-decoration: none;
  background-image: linear-gradient(to bottom right, var(--accent), var(--accent-end));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* Spacing lives in the links' padding, not the gap: gap-only spacing leaves the
   hit target exactly as wide as the label ("Jobs" was 28px) and still reads
   cramped. The negative margin keeps the first label's text aligned with the
   content edge rather than its padding box. */
.nav {
  display: flex; gap: var(--s1);
  align-self: stretch; align-items: stretch;
  margin-left: calc(var(--s3) * -1);
}
.nav a {
  display: flex; align-items: center; gap: var(--s2);
  padding: 0 var(--s3);
  color: var(--text-muted); text-decoration: none;
  font-size: 0.875rem; font-weight: 600;
  border-bottom: 2px solid transparent;
  border-radius: var(--r-md) var(--r-md) 0 0;
  margin-bottom: -1px;
  white-space: nowrap;
}
.nav a:hover { color: var(--text-body); background: rgb(255 255 255 / 0.04); }
.nav a.is-active { color: var(--text); border-bottom-color: var(--accent); }

.pill {
  font-size: 0.6875rem; font-weight: 600;
  border-radius: var(--r-full); padding: 1px var(--s2);
  background: rgb(99 102 241 / 0.15); color: var(--accent-hover);
}

.health { font-size: 0.6875rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; }
.health.up { color: var(--ok); }
.health.warn { color: var(--warn); }
.health.danger { color: var(--danger); }

/* ---------- view scaffolding ---------- */

.view { padding-bottom: var(--s7); }
/* The heading row needs its own bottom margin: the h1's margin only applies to
   the h1, so a row containing filter chips had nothing separating it from the
   content below. */
.view-head {
  display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  margin-bottom: var(--s4);
}
.view-head .h1 { margin-bottom: 0; }
/* Jobs has no lede paragraph to provide breathing room, so the list adds its
   own. Padding, not margin: adjacent vertical margins collapse to the larger
   of the two, so a margin here would be silently swallowed by the heading
   row's own 16px and change nothing. */
.view-head + .jobs { padding-top: var(--s2); }
.h1 { font-size: 1.5rem; font-weight: 700; color: var(--text); margin: 0 0 var(--s2); }
.h2 { font-size: 1.125rem; font-weight: 600; color: var(--text); margin: 0 0 var(--s3); }
.lede { color: var(--text-muted); margin: 0 0 var(--s4); max-width: 70ch; }
.empty { color: var(--text-faint); padding: var(--s5) 0; text-align: center; }

/* ---------- search ---------- */

.search-row { display: flex; gap: var(--s3); margin-bottom: var(--s4); }
.search {
  flex: 1; padding: var(--s3) var(--s4);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); color: var(--text); font: inherit; font-size: 1rem;
}
.search::placeholder { color: var(--text-faint); }
.search:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgb(37 99 235 / 0.4); }

/* ---------- buttons + chips ---------- */

.btn {
  border: 0; border-radius: var(--r-md); padding: var(--s2) var(--s4);
  font: inherit; font-weight: 600; cursor: pointer; color: var(--text);
}
.btn-primary { background-image: linear-gradient(to bottom right, var(--accent), var(--accent-end)); }
.btn-primary:hover:not(:disabled) { filter: brightness(1.08); }
.btn-secondary { background: var(--surface-raised); color: var(--text-body); }
.btn-secondary:hover:not(:disabled) { filter: brightness(1.15); }
.btn-ghost { background: transparent; color: var(--text-muted); padding: var(--s1) var(--s2); }
.btn-ghost:hover:not(:disabled) { color: var(--text-body); }
.btn-sm { padding: var(--s2) var(--s3); font-size: 0.8125rem; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

.filters { display: flex; gap: var(--s2); }
.chip {
  background: var(--surface); color: var(--text-muted);
  border: 1px solid var(--border); border-radius: var(--r-full);
  padding: var(--s1) var(--s3); font: inherit; font-size: 0.8125rem; font-weight: 600;
  cursor: pointer;
}
.chip:hover { color: var(--text-body); }
.chip.is-active { color: var(--text); border-color: var(--accent); background: rgb(99 102 241 / 0.06); }

/* ---------- notes ---------- */

.notes {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--accent); border-radius: var(--r-md);
  padding: var(--s3) var(--s4); margin-bottom: var(--s4);
  color: var(--text-muted); font-size: 0.8125rem;
}
.notes p { margin: var(--s1) 0; }

/* ---------- work cards (wide variant) ---------- */

.results { display: flex; flex-direction: column; gap: var(--s4); }

.work {
  display: grid; grid-template-columns: 120px 1fr; gap: var(--s4);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s4); box-shadow: var(--shadow);
}
@media (max-width: 640px) { .work { grid-template-columns: 1fr; } }

.cover, .cover-ph {
  width: 120px; aspect-ratio: 2 / 3; border-radius: var(--r-md);
  background: var(--surface-raised); object-fit: cover;
}

.work-title { margin: 0; font-size: 1.125rem; font-weight: 600; color: var(--text); }
.work-author { color: var(--text-muted); font-size: 0.8125rem; margin: var(--s1) 0 var(--s3); }

.col-label {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-faint); margin: var(--s3) 0 var(--s2);
}

/* ---------- candidate rows ---------- */

.cand {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s2) var(--s3); border: 1px solid transparent;
  border-radius: var(--r-md); cursor: pointer;
}
.cand:hover { background: var(--surface-raised); }
.cand.selected { border-color: var(--accent); background: rgb(99 102 241 / 0.06); }
.cand input { margin-top: 3px; accent-color: var(--accent); }
.cand-name { font-family: var(--mono); font-size: 0.8125rem; color: var(--text-body); word-break: break-all; }
.cand-meta { color: var(--text-faint); font-size: 0.8125rem; margin-top: 2px; }

/* ---------- badges ---------- */

.badges { display: inline-flex; flex-wrap: wrap; gap: var(--s1); margin-left: var(--s2); vertical-align: middle; }
.badge {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; border-radius: var(--r-sm); padding: 2px var(--s2);
}
.badge.ok     { color: var(--ok);     background: rgb(16 185 129 / 0.12); }
.badge.warn   { color: var(--warn);   background: rgb(204 123 25 / 0.12); }
.badge.danger { color: var(--danger); background: rgb(239 68 68 / 0.12); }
.badge.info   { color: var(--accent-hover); background: rgb(99 102 241 / 0.12); }

.actions { margin-top: var(--s4); display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }

/* ---------- poster grid (library) ---------- */

.poster-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--s4);
}
.poster {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s3); box-shadow: var(--shadow);
}
.poster-art {
  aspect-ratio: 2 / 3; border-radius: var(--r-md); background: var(--surface-raised);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-faint); font-size: 2rem; margin-bottom: var(--s3);
  overflow: hidden;
}
/* Covers vary wildly in aspect ratio; cover-fit keeps the grid even. */
.poster-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.poster-title { color: var(--text); font-weight: 600; font-size: 0.9375rem; line-height: 1.3; }
.poster-meta { color: var(--text-muted); font-size: 0.8125rem; margin-top: var(--s1); }

/* ---------- jobs ---------- */

.jobs { display: flex; flex-direction: column; gap: var(--s2); }

.job {
  position: relative; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--s3) var(--s4); overflow: hidden;
}
.job-main { display: flex; align-items: center; gap: var(--s3); }
/* min-width:0 lets long paths wrap instead of forcing the row wider. */
.job-body { flex: 1; min-width: 0; }
.dot { width: 8px; height: 8px; border-radius: var(--r-full); flex: none; background: var(--text-faint); }
.dot.active { background: var(--accent); animation: pulse 1.4s ease-in-out infinite; }
.dot.ok { background: var(--ok); }
.dot.danger { background: var(--danger); }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

.job-title { color: var(--text); font-weight: 600; font-size: 0.9375rem; }
.job-detail { color: var(--text-muted); font-size: 0.8125rem; word-break: break-word; }
.job-bar { position: absolute; left: 0; bottom: 0; height: 2px; background: var(--accent); transition: width .3s ease; }

.job-expand { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--border); }

/* ---------- stat tiles ---------- */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: var(--s2); }
.stat { background: var(--surface-raised); border-radius: var(--r-md); padding: var(--s3); }
.stat-label {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-faint);
}
.stat-value { font-size: 1.125rem; font-weight: 600; color: var(--text); margin-top: 2px; }
.stat-value.ok { color: var(--ok); }
.stat-value.danger { color: var(--danger); }

/* ---------- settings ---------- */

.settings { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s3); }
.setting { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); padding: var(--s4); }
.setting-label { font-size: 0.6875rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.setting-value { color: var(--text); font-weight: 600; margin-top: var(--s1); word-break: break-all; }
.setting-note { color: var(--text-muted); font-size: 0.8125rem; margin-top: var(--s2); }

/* ---------- skeletons ---------- */

.skeleton { background: var(--surface-raised); border-radius: var(--r-md); position: relative; overflow: hidden; }
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--border-strong), transparent);
  transform: translateX(-100%); animation: sweep 1.6s infinite;
}
@keyframes sweep { to { transform: translateX(100%); } }
.skeleton-card { height: 168px; margin-bottom: var(--s4); }

/* ---------- toasts ---------- */

.toasts {
  position: fixed; right: var(--s4); bottom: var(--s4);
  display: flex; flex-direction: column; gap: var(--s2); z-index: 50; max-width: 380px;
}
.toast {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--accent); border-radius: var(--r-md);
  box-shadow: var(--shadow); padding: var(--s3) var(--s4);
  color: var(--text-body); font-size: 0.8125rem; cursor: pointer;
}
.toast.ok { border-left-color: var(--ok); }
.toast.danger { border-left-color: var(--danger); }

/* ---------- login ---------- */

.login-body { display: grid; place-items: center; min-height: 100vh; padding: var(--s4); }
.login-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s6);
  box-shadow: var(--shadow); width: 100%; max-width: 380px;
}
.login-mark { font-size: 1.75rem; margin: 0 0 var(--s1); }
.login-lede { margin-bottom: var(--s5); }
.login-input { width: 100%; margin: var(--s1) 0 var(--s4); font-size: 0.9375rem; }
.login-submit { width: 100%; }
.login-error { color: var(--danger); font-size: 0.8125rem; margin-top: var(--s3); }

/* ---------- responsive ----------
   The page must never scroll horizontally. The topbar was the offender: a
   rigid flex row of wordmark + 4 nav links + health status needs ~520px, so
   below that it wraps the nav onto its own line. */

.topbar-inner { flex-wrap: wrap; row-gap: var(--s2); }
.search-row { flex-wrap: wrap; }
.search { min-width: 12ch; }
.toasts { max-width: min(380px, calc(100vw - 2 * var(--s4))); }

/* Nav scrolls sideways within itself rather than widening the page. */
.nav { overflow-x: auto; scrollbar-width: none; }
.nav::-webkit-scrollbar { display: none; }

@media (max-width: 720px) {
  .topbar-inner { min-height: 0; padding-top: var(--s2); gap: var(--s3); }
  .spacer { display: none; }
  .health { order: 2; margin-left: auto; }
  .nav { order: 3; width: 100%; gap: var(--s1); padding-bottom: var(--s1); }
  .h1 { font-size: 1.25rem; }
}

@media (max-width: 640px) {
  /* Job rows must stack. As a single flex row the three action buttons hold
     their intrinsic width and squeeze the body into ~130px, which wraps the
     title one word per line and turns the detail text into a vertical ribbon.
     Body claims row one; the actions share row two as equal-width targets. */
  .job-main { flex-wrap: wrap; row-gap: var(--s3); }
  .job-body { flex: 1 1 calc(100% - 20px); }   /* 8px dot + 12px gap */
  .job-main > .btn { flex: 1 1 0; }

  /* Touch wants more than a mouse does. Desktop keeps the lighter sizing. */
  .btn-sm { padding: var(--s2) var(--s3); font-size: 0.875rem; }
  .chip { padding: var(--s2) var(--s3); }
  .filters { width: 100%; }
}

@media (max-width: 420px) {
  /* Health text is the least critical thing here, and Settings shows the
     same state in full. */
  .health { display: none; }
  .poster-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .settings { grid-template-columns: 1fr; }
  .stats { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
}
