/* jobpilot UI, transcribed from the reference design system's shadcn components.
 *
 * Every rule below is a reference component's Tailwind classes written out by
 * hand -- the source component is named above each block. No Tailwind, no
 * npm, no build step: the api image stays a slim python:3.12-slim.
 *
 * When the reference system changes, diff against the named component.
 */

/* ---------------------------------------------------------------- base -- */

*, *::before, *::after { box-sizing: border-box; border-color: var(--border); }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  font-size: 0.875rem;   /* text-sm -- the reference system's default body size */
  line-height: 1.25rem;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

/* Links inside prose should read as links; links that are UI (nav, buttons)
   opt out via their own classes. .menu-item is on that list because a dropdown
   inside .content -- a result card's action menu -- is UI that happens to be
   made of anchors, and underlining every entry made it read as a paragraph of
   links rather than as a menu. */
.content a:not(.btn):not(.nav-item):not(.brand):not(.tab):not(.menu-item) {
  color: var(--foreground);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: var(--muted-foreground);
}
.content a:not(.btn):not(.nav-item):not(.brand):not(.tab):not(.menu-item):hover {
  text-decoration-color: var(--foreground);
}

.sprite { display: none; }

.icon {
  width: 1rem; height: 1rem;      /* lucide's h-4 w-4 default */
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-lg { width: 1.5rem; height: 1.5rem; }

:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); border-radius: var(--radius-md); }

/* ---------------------------------------------------- typography scale -- */

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }

.page-title { font-size: 1.5rem; line-height: 2rem; font-weight: 700; }
.page-desc  { margin: 0.25rem 0 0; font-size: 0.875rem; color: var(--muted-foreground); }

.section-title { font-size: 1rem; line-height: 1.5rem; font-weight: 600; }

.muted { color: var(--muted-foreground); }
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.mono, code { font-family: var(--font-mono); font-size: 0.8125rem; }
code {
  background: var(--muted);
  padding: 0.1rem 0.3rem;
  border-radius: var(--radius-md);
}
.strong { color: #4ade80; }   /* green-400 -- "set" / "current" affirmatives */
.warn   { color: #fbbf24; }   /* amber-400 */
.bad    { color: var(--destructive); }

/* --------------------------------------------- shell: (dashboard)/layout -- */
/* flex h-dvh overflow-hidden > Sidebar + (Header + main overflow-y-auto)     */

.shell { display: flex; height: 100dvh; overflow: hidden; }

.main-col { display: flex; flex: 1; flex-direction: column; overflow: hidden; min-width: 0; }

.content { flex: 1; overflow-y: auto; padding: 1rem; }
@media (min-width: 768px) { .content { padding: 1.5rem; } }

/* space-y-6, the spacing the reference system uses between page sections */
.page > * + * { margin-top: 1.5rem; }
.page--narrow { max-width: 42rem; }   /* max-w-2xl, as settings/security uses */

/* -------------------------------------------------- sidebar ---------- */

.sidebar {
  display: none;   /* hidden md:flex -- below md the drawer takes over */
  height: 100%;
  flex-shrink: 0;
  flex-direction: column;
  width: 15rem;    /* w-60 */
  border-right: 1px solid var(--sidebar-border);
  background: var(--sidebar-background);
  color: var(--sidebar-foreground);
  transition: width 150ms ease;
}
@media (min-width: 768px) { .sidebar { display: flex; } }

.brand {
  display: flex; height: 3.5rem;   /* h-14, flush with the topbar */
  flex-shrink: 0; align-items: center; gap: 0.5rem;
  padding: 0 1rem;
  border-bottom: 1px solid var(--sidebar-border);
}
.brand-mark {
  display: flex; height: 2rem; width: 2rem; flex-shrink: 0;
  align-items: center; justify-content: center;
  border-radius: var(--radius-lg);
  background: var(--sidebar-primary);
  color: var(--sidebar-primary-foreground);
  font-size: 0.875rem; font-weight: 700;
}
.brand-name { font-weight: 600; color: var(--sidebar-accent-foreground); }

.nav { display: flex; flex: 1; flex-direction: column; gap: 0.25rem; overflow-y: auto; padding: 0.75rem 0.5rem; }

.nav-item {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  color: inherit;
  transition: background-color 150ms, color 150ms;
}
.nav-item:hover {
  background: var(--sidebar-accent);
  color: var(--sidebar-accent-foreground);
  text-decoration: none;
}
.nav-item.is-active {
  background: var(--sidebar-accent);
  font-weight: 500;
  color: var(--sidebar-accent-foreground);
}

.sidebar-foot { border-top: 1px solid var(--sidebar-border); padding: 0.5rem; }
.collapse-btn {
  width: 100%; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 0.875rem; text-align: left;
}

/* Collapsed rail: w-16, labels gone, rows centred. The class is set on <html>
   from localStorage before first paint (js/app.js), because every navigation
   here is a full page load and the state has to survive it. */
.rail-collapsed .sidebar { width: 4rem; }
.rail-collapsed .brand-name,
.rail-collapsed .nav-item span { display: none; }
.rail-collapsed .brand,
.rail-collapsed .nav-item { justify-content: center; padding-left: 0; padding-right: 0; }
.rail-collapsed .panel-open { display: block; }
.rail-collapsed .panel-close { display: none; }
.panel-open { display: none; }

/* ---------------------------------------------------- topbar ---------- */

.topbar {
  display: flex; height: 3.5rem; flex-shrink: 0;
  align-items: center; gap: 0.75rem;
  border-bottom: 1px solid var(--border);
  background: var(--background);
  padding: 0 1rem;
}
.topbar-end { margin-left: auto; display: flex; align-items: center; gap: 0.75rem; }

/* Native <details> instead of a Radix dropdown: no JS, works everywhere, and
   the README's "every page works without JS" survives the redesign. */
.menu { position: relative; }
.menu > summary { list-style: none; cursor: pointer; }
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary::marker { content: ""; }

.menu-trigger {
  display: flex; align-items: center; gap: 0.5rem;
  border-radius: var(--radius-md);
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
}
.menu-trigger:hover { background: var(--accent); }

.avatar {
  display: flex; height: 1.75rem; width: 1.75rem;
  align-items: center; justify-content: center;
  border-radius: 9999px;
  background: var(--muted);
  color: var(--muted-foreground);
}

.menu-content {
  position: absolute; right: 0; top: calc(100% + 0.375rem); z-index: 50;
  width: 14rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--popover);
  color: var(--popover-foreground);
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.4);
  padding: 0.25rem;
}
.menu-label { padding: 0.375rem 0.5rem; font-size: 0.875rem; font-weight: 600; }
.menu-sub { display: block; font-family: var(--font-mono); font-size: 0.75rem; font-weight: 400; color: var(--muted-foreground); }
.menu-sep { height: 1px; margin: 0.25rem -0.25rem; background: var(--border); }
.menu-item {
  display: flex; width: 100%; align-items: center; gap: 0.5rem;
  border: 0; background: none; cursor: pointer;
  border-radius: var(--radius-md);
  padding: 0.375rem 0.5rem;
  font: inherit; font-size: 0.875rem; text-align: left;
  color: inherit;
}
.menu-item:hover { background: var(--accent); }
.menu-item.is-destructive { color: var(--destructive); }

/* ------------------------------------------ drawer: mobile ------------ */

.drawer { display: block; }
@media (min-width: 768px) { .drawer { display: none; } }
.drawer > summary { list-style: none; cursor: pointer; }
.drawer > summary::-webkit-details-marker { display: none; }
.drawer > summary::marker { content: ""; }

.drawer-scrim {
  position: fixed; inset: 0; z-index: 40;
  background: rgb(0 0 0 / 0.6);
}
.drawer-panel {
  position: fixed; inset-block: 0; left: 0; z-index: 50;
  display: flex; flex-direction: column;
  width: 16rem;
  border-right: 1px solid var(--sidebar-border);
  background: var(--sidebar-background);
  color: var(--sidebar-foreground);
}
/* The drawer duplicates the rail's markup, so make sure the collapsed-rail
   rules never reach it -- a collapsed rail must not hide the drawer's labels. */
.rail-collapsed .drawer-panel .brand-name,
.rail-collapsed .drawer-panel .nav-item span { display: inline; }
.rail-collapsed .drawer-panel .brand,
.rail-collapsed .drawer-panel .nav-item { justify-content: flex-start; padding-left: 0.75rem; padding-right: 0.75rem; }
.rail-collapsed .drawer-panel .brand { padding-left: 1rem; padding-right: 1rem; }

/* --------------------------------------- tabs: settings nav --------- */
/* Links styled as a Radix TabsList. The wrapper is what scrolls, because the
   list hugs its contents -- so adding sections cannot widen the page. */

.tabs-wrap { max-width: 100%; overflow-x: auto; }
.tabs {
  display: inline-flex; height: 2.25rem;
  align-items: center; justify-content: center;
  border-radius: var(--radius-lg);
  background: var(--muted);
  padding: 0.25rem;
  color: var(--muted-foreground);
}
.tab {
  display: inline-flex; align-items: center; justify-content: center;
  white-space: nowrap;
  border-radius: var(--radius-md);
  padding: 0.25rem 0.75rem;
  font-size: 0.875rem; font-weight: 500;
  transition: background-color 150ms, color 150ms;
}
.tab:hover { color: var(--foreground); text-decoration: none; }
.tab.is-active {
  background: var(--background);
  color: var(--foreground);
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
}

/* --------------------------------------------------- auth: (auth)/login -- */

.auth {
  display: flex; min-height: 100dvh;
  align-items: center; justify-content: center;
  padding: 1rem;
}
.auth-card { width: 100%; max-width: 24rem; }

/* --------------------------------------------------------- card ----- */

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--card);
  color: var(--card-foreground);
  box-shadow: var(--shadow-sm);
}
.card-header { display: flex; flex-direction: column; gap: 0.375rem; padding: 1.5rem; }
.card-title { font-size: 1rem; font-weight: 600; line-height: 1; letter-spacing: -0.01em; }
.card-desc { margin: 0; font-size: 0.875rem; color: var(--muted-foreground); }
.card-content { padding: 0 1.5rem 1.5rem; }
.card-footer { display: flex; align-items: center; gap: 0.5rem; padding: 0 1.5rem 1.5rem; }

/* ----------------------------------------------------- tab card ----- */
/* Header row (title + actions), a separator, then the body.                 */

.tab-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  overflow: hidden;
}
.tab-card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1rem;
  border-bottom: 1px solid var(--border);
}
@media (min-width: 768px) { .tab-card-head { padding: 1.25rem 1.5rem; } }
/* A card that is only a header -- a heading and one action -- would otherwise
   trail a divider under nothing. */
.tab-card-head:last-child { border-bottom: 0; }
.tab-card-head h2 { font-size: 1rem; font-weight: 600; }
.tab-card-head .card-desc { margin-top: 0.125rem; }
.tab-card-actions { display: flex; flex-shrink: 0; align-items: center; gap: 0.5rem; }
@media (max-width: 639px) {
  .tab-card-head { flex-direction: column; align-items: stretch; gap: 0.75rem; }
  .tab-card-actions { flex-wrap: wrap; }
}
.tab-card-body { padding: 1rem; }
@media (min-width: 768px) { .tab-card-body { padding: 1.5rem; } }
.tab-card-body--flush { padding: 0; }
.tab-card-body > * + * { margin-top: 1rem; }

/* ---------------------------------------------------- stat card ----- */

.stat-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.stat {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  color: var(--card-foreground);
  padding: 1.5rem;
}
/* margin: 0 first. These are <p>, and only their top margins were ever set, so
   each tile carried about 64px of the browser's default paragraph spacing:
   three lines of text in a 186px box. */
.stat-label { margin: 0; font-size: 0.875rem; font-weight: 500; color: var(--muted-foreground); }
.stat-value { margin: 0.25rem 0 0; font-size: 1.5rem; line-height: 2rem; font-weight: 700; }
.stat-desc  { margin: 0.25rem 0 0; font-size: 0.75rem; color: var(--muted-foreground); }
.stat-icon {
  display: flex; height: 3rem; width: 3rem; flex-shrink: 0;
  align-items: center; justify-content: center;
  border-radius: 9999px;
  background: var(--muted);
  color: var(--muted-foreground);
}

/* -------------------------------------------------------- button ---- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  white-space: nowrap;
  height: 2.25rem;               /* h-9 */
  padding: 0.5rem 1rem;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 150ms, color 150ms, border-color 150ms;
  background: var(--primary);
  color: var(--primary-foreground);
}
.btn:hover { background: color-mix(in srgb, var(--primary) 90%, transparent); text-decoration: none; }
.btn:disabled { pointer-events: none; opacity: 0.5; }

.btn-outline {
  border-color: var(--input);
  background: var(--background);
  color: var(--foreground);
}
.btn-outline:hover { background: var(--accent); color: var(--accent-foreground); }

.btn-secondary { background: var(--secondary); color: var(--secondary-foreground); }
.btn-secondary:hover { background: color-mix(in srgb, var(--secondary) 80%, transparent); }

.btn-ghost { background: transparent; color: var(--foreground); }
.btn-ghost:hover { background: var(--accent); color: var(--accent-foreground); }

.btn-destructive { background: var(--destructive); color: var(--destructive-foreground); }
.btn-destructive:hover { background: color-mix(in srgb, var(--destructive) 90%, transparent); }

.btn-sm { height: 2rem; padding: 0 0.75rem; font-size: 0.75rem; }
.btn-icon { height: 2.25rem; width: 2.25rem; padding: 0; }
.btn-block { width: 100%; }

/* --------------------------------------------------------- badge ---- */

.badge {
  display: inline-flex; align-items: center;
  border: 1px solid transparent;
  border-radius: 9999px;
  padding: 0.125rem 0.625rem;
  font-size: 0.75rem; line-height: 1rem; font-weight: 600;
  background: var(--primary); color: var(--primary-foreground);
  white-space: nowrap;
}
.badge-secondary { background: var(--secondary); color: var(--secondary-foreground); }
.badge-outline { background: transparent; border-color: var(--border); color: var(--foreground); }
.badge-destructive { background: var(--destructive); color: var(--destructive-foreground); }
.badge-success { background: #14532d; color: #86efac; }   /* green-900 / green-300 */
.badge-warning { background: #451a03; color: #fcd34d; }   /* amber-950 / amber-300 */

/* --------------------------------------------------------- table ---- */
/* The wrapper scrolls and the table has a min-width: a w-full table just     */
/* shrinks its columns instead of engaging the scroll (reference data table).     */

.table-wrap { position: relative; width: 100%; overflow-x: auto; }
table { width: 100%; min-width: 40rem; border-collapse: collapse; font-size: 0.875rem; }
.table-wrap--narrow table { min-width: 0; }
.table-wrap--wide table { min-width: 52rem; }

thead tr { border-bottom: 1px solid var(--border); }
th {
  height: 2.5rem;                /* h-10 */
  padding: 0 0.5rem;
  text-align: left;
  vertical-align: middle;
  font-weight: 500;
  color: var(--muted-foreground);
}
tbody tr { border-bottom: 1px solid var(--border); transition: background-color 150ms; }
tbody tr:last-child { border-bottom: 0; }
tbody tr:hover { background: color-mix(in srgb, var(--muted) 50%, transparent); }
td { padding: 0.5rem; vertical-align: middle; }

/* ------------------------------------------------- result cards ---- */
/* One scored posting. This replaced a table because the table had run out of
   room: a posting's pay, location, workplace, level and age were all being
   scraped and stored, and a row already full of title, company and four
   stacked links had nowhere to put them. A card has a second axis -- the meta
   lines stack down while the actions sit along the top -- so the extra fields
   cost width that was already going spare instead of height that was not. */

.tier-group + .tier-group { margin-top: 1.75rem; }
.tier-head {
  display: flex; align-items: baseline; gap: 0.5rem;
  margin-bottom: 0.25rem;
}
.tier-label { font-size: 0.9375rem; font-weight: 600; }
.tier-blurb { margin: 0 0 0.5rem; }
.tier-head + .job-card, .tier-blurb + .job-card { margin-top: 0.5rem; }

.job-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.875rem 1rem;
}
.job-card + .job-card { margin-top: 0.5rem; }
.job-card > * + * { margin-top: 0.375rem; }
.job-card:hover { border-color: color-mix(in srgb, var(--foreground) 25%, var(--border)); }

.job-card-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
}
.job-card-ident { display: flex; align-items: flex-start; gap: 0.75rem; min-width: 0; }
.job-card-heading { min-width: 0; }
.job-card-title { font-size: 0.9375rem; font-weight: 600; line-height: 1.3; }

/* Fixed width so every score in a group lines up down the left edge, which is
   what makes the list scannable in score order without reading any of it. */
.job-score {
  flex-shrink: 0;
  width: 2rem; height: 2rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-md);
  background: var(--muted);
  font-size: 0.9375rem; font-weight: 700;
}

.job-card-actions {
  display: flex; flex-shrink: 0; align-items: center; gap: 0.5rem;
}
/* The trigger is a <summary>, which is display:list-item by default and so
   ignores the flex centring every other .btn gets. */
.job-card-actions > .menu > summary { display: flex; }
.job-card-actions .btn .icon { width: 0.875rem; height: 0.875rem; }

/* The facts line: pay and place, the two things that decide whether the rest
   of the card is worth reading. */
.job-meta { margin: 0; font-size: 0.8125rem; font-weight: 500; }
.job-meta--dim { font-weight: 400; color: var(--muted-foreground); }
.job-reason { margin: 0; font-size: 0.8125rem; color: var(--muted-foreground); }

.chip-row { display: flex; flex-wrap: wrap; gap: 0.25rem; }
.chip {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.0625rem 0.375rem;
  font-size: 0.6875rem;
  color: var(--muted-foreground);
}
.chip--more { border-style: dashed; }

/* Below sm the actions would be squeezed against a wrapping title, so they
   drop to their own row and align right rather than shrinking. */
@media (max-width: 639px) {
  .job-card-head { flex-direction: column; align-items: stretch; }
  .job-card-actions { justify-content: flex-end; }
}

/* .tab-card clips its overflow so a flush table cannot square off its rounded
   corners. A card holding action menus has no flush table and does need to
   paint outside itself: without this the last card's menu opens into the
   clipped region and is cut in half. */
.tab-card--popover { overflow: visible; }

/* ------------------------------------------------- clickable rows ---- */
/* The whole row navigates, but the thing being clicked is still a real <a> in
   the first cell -- it just carries an overlay stretched across the row. A
   click handler on the <tr> would have been fewer lines and would have thrown
   away everything a link gives you for free: keyboard focus and Enter,
   middle-click into a new tab, right-click to copy the address, and the
   browser's own status-bar preview of where the row goes. It would also have
   made the row dead with scripting off, which no other page here is.
   If the overlay is ever not honoured, the anchor is still an ordinary link on
   the run's date, so the row degrades to what it was before. */

tbody tr.row-link { position: relative; cursor: pointer; }
tbody tr.row-link:hover { background: color-mix(in srgb, var(--muted) 65%, transparent); }
tbody tr.row-link:focus-within { background: color-mix(in srgb, var(--muted) 65%, transparent); }

.row-link-target { color: inherit; font-weight: 600; text-decoration: none; }
.row-link-target::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-md);
}
/* The global :focus-visible ring would draw around the date alone; the row is
   what the key press activates, so the row is what should be ringed. */
.row-link-target:focus-visible { box-shadow: none; }
.row-link-target:focus-visible::after { box-shadow: inset 0 0 0 2px var(--ring); }

/* The overlay covers the row, so it swallows both text selection and any other
   control in it. Anything that needs either has to be lifted above it. */
.row-link-above { position: relative; z-index: 1; }

/* Second lines in a run row: which searches ran, or why the run failed. Free
   text of unknown length, so it needs a ceiling to truncate against -- a table
   cell will otherwise just grow to fit it. The full string is on the title.
   The ceilings are deliberately mean: these two cells are the only elastic
   ones in the row, and at 20rem apiece they ate enough width to push the last
   column -- how long the run took -- off the side of the card. */
.run-subtitle {
  max-width: 17rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The error is the more compressible of the two: it is a hint that something
   went wrong, and the run's own page quotes it in full. */
.run-subtitle--tight { max-width: 12rem; }

/* The pager under the runs table. Justified apart so the position reads on
   the left and the controls sit where a thumb expects them. */
.pager {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-top: 1px solid var(--border);
}
@media (min-width: 768px) { .pager { padding: 0.75rem 1.5rem; } }
/* A dead end is still shown, greyed: hiding it moves the live control under
   the cursor that was about to press the other one. */
.pager [aria-disabled="true"] { pointer-events: none; opacity: 0.5; }

/* A table sitting flush inside a tab-card body needs its own edge padding. */
.tab-card-body--flush .table-wrap { padding: 0; }
.tab-card-body--flush th:first-child,
.tab-card-body--flush td:first-child { padding-left: 1rem; }
.tab-card-body--flush th:last-child,
.tab-card-body--flush td:last-child { padding-right: 1rem; }
@media (min-width: 768px) {
  .tab-card-body--flush th:first-child,
  .tab-card-body--flush td:first-child { padding-left: 1.5rem; }
  .tab-card-body--flush th:last-child,
  .tab-card-body--flush td:last-child { padding-right: 1.5rem; }
}

/* ------------------------------------------- input / form field -- */

form { margin: 0; }

/* No margin between sibling fields: containers own the rhythm (.tab-card-body
   and .stack space their children, .row uses gap). A margin here also lands on
   fields sitting side by side in a .row, which pushes every column but the
   first down by 1rem. */
.field { display: flex; flex-direction: column; gap: 0.375rem; }

.field > label, label.label {
  font-size: 0.875rem; font-weight: 500; line-height: 1;
  color: var(--foreground);
}
.field-hint { margin: 0; font-size: 0.75rem; color: var(--muted-foreground); }
.label-note { font-weight: 400; color: var(--muted-foreground); }

input[type=text], input[type=password], input[type=number],
input[type=email], input[type=url], input[type=date], textarea, select {
  display: block; width: 100%;
  height: 2.25rem;               /* h-9 */
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--input);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--foreground);
  font: inherit; font-size: 0.875rem;
  box-shadow: var(--shadow-sm);
  transition: border-color 150ms, box-shadow 150ms;
}
/* The block rule above beats the `hidden` attribute's UA `display: none`, so a
   control hidden by script stays on screen. It must still be in the form -- it
   is what submits -- so it is hidden, not removed. */
input[hidden], textarea[hidden], select[hidden] { display: none; }

input::placeholder, textarea::placeholder { color: var(--muted-foreground); }
input:focus-visible, textarea:focus-visible, select:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--ring);
}
input:disabled, textarea:disabled, select:disabled { cursor: not-allowed; opacity: 0.5; }

textarea {
  height: auto;
  min-height: 8rem;
  padding: 0.5rem 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  resize: vertical;
}
.textarea-sm { min-height: 5rem; }
/* Between the one-liner and the full document: enough room to see a whole
   paragraph without scrolling, for fields that are edited as prose. */
.textarea-md { min-height: 12rem; }
.textarea-lg { min-height: 22rem; }

select {
  appearance: none;
  padding-right: 2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a3a3a3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.625rem center;
  background-size: 1rem;
}

input[type=checkbox] {
  height: 1rem; width: 1rem;
  margin: 0;
  accent-color: var(--primary);
  cursor: pointer;
}

/* setting row: a labelled control on a muted plate. */
.setting-row {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  border-radius: var(--radius-md);
  background: var(--muted);
  padding: 0.75rem 1rem;
}
.setting-row-label { font-size: 0.875rem; font-weight: 500; }
.setting-row-desc { margin: 0.125rem 0 0; font-size: 0.75rem; color: var(--muted-foreground); }

/* A checkbox that reads as a row rather than a bare box. */
.check-row {
  display: flex; align-items: flex-start; gap: 0.625rem;
  border-radius: var(--radius-md);
  background: var(--muted);
  padding: 0.75rem 1rem;
  cursor: pointer;
  font-size: 0.875rem; font-weight: 500;
}
.check-row input[type=checkbox] { margin-top: 0.125rem; }
.check-row .field-hint { display: block; margin-top: 0.125rem; font-weight: 400; }

/* The bullets editor: a contenteditable <ul> standing in for the textarea it
   replaces, so it has to look like one. The glyph and the hanging indent are
   the whole point -- they are what makes the boundary between two wrapped
   bullets visible at a glance. */
.bullet-editor {
  display: block;
  margin: 0;
  min-height: 8rem;
  padding: 0.5rem 0.75rem 0.5rem 1.875rem;
  border: 1px solid var(--input);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--foreground);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  box-shadow: var(--shadow-sm);
  list-style: disc;
  transition: border-color 150ms, box-shadow 150ms;
}
.bullet-editor:focus-visible,
.bullet-editor:focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--ring);
}
.bullet-editor li { margin: 0; }
.bullet-editor li + li { margin-top: 0.5rem; }
.bullet-editor li::marker { color: var(--muted-foreground); }
/* An empty list is one empty <li>, which would collapse to nothing and leave
   no visible place to click. */
.bullet-editor li:only-child:empty::after {
  content: attr(data-placeholder);
  color: var(--muted-foreground);
}

/* Numeric cells in an editable table: a full-width input for a two-digit
   score pushes every other column off the page. */
td > input.num { width: 5.5rem; }
td > input[type=text] { min-width: 8rem; }
td > .field-hint { margin-top: 0.25rem; }

/* A month select beside a year box. Two controls, one date, so they sit on one
   line and share the field's label rather than each claiming their own. */
.date-pair { display: flex; gap: 0.5rem; }
.date-pair select { flex: 1 1 7rem; min-width: 0; }
.date-pair input { flex: 0 1 6.5rem; min-width: 0; }

/* "Present" rides alongside From and To rather than under them: it is part of
   the same date, and it is what the To column means when it is ticked.
   Scoped to `.row-dates >` rather than left bare: `.row > *` sets a 14rem
   basis on every child and is declared later, so a bare `.date-current` loses
   the cascade, takes a full column and wraps onto its own line. These three
   share one line, so the two date fields give up basis to make room. */
.row-dates > .field { flex: 1 1 11rem; }
.row-dates > .date-current { flex: 0 0 auto; }
/* Occupies exactly the height of the sibling labels, so the box lines up with
   the inputs rather than with the labels above them. */
.date-current-spacer { height: 0.875rem; }
.date-current-box {
  display: flex; align-items: center; gap: 0.5rem;
  height: 2.25rem;               /* h-9, matching the inputs beside it */
  padding: 0 0.875rem;
  border-radius: var(--radius-md);
  background: var(--muted);
  cursor: pointer;
  font-size: 0.875rem; font-weight: 500;
  white-space: nowrap;
}
.date-current-box:hover { background: var(--accent); }
@media (max-width: 639px) { .date-current-box { justify-content: center; } }
/* The end date is not merely ignored while Present is set, it is not
   applicable -- so it reads as unavailable rather than as blank-but-editable.
   app.js sets `disabled`, which already dims it; this dims the label too. */
[data-date-range].is-current label[for^="end_month"] { opacity: 0.5; }

/* A labelled strip between two bodies of one card, where a single form covers
   more than one subject. The rule is what stops the second subject reading as
   a continuation of the first: the ladder's hint sat directly under the caps
   inputs and looked like the hint for the field above it. */
.tab-card-section {
  padding: 1rem 1rem 0.75rem;
  border-top: 1px solid var(--border);
}
@media (min-width: 768px) { .tab-card-section { padding: 1.25rem 1.5rem 0.75rem; } }
.tab-card-section > .section-title + .field-hint { margin-top: 0.25rem; }

/* One editable record in a list that saves as a whole: a skill group, a
   degree. Replaces .entity-card, whose delete button sat in a second <form>
   and had to be dragged back onto the action row with a negative margin. */
.record {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.875rem 1rem;
}
.record + .record { margin-top: 0.75rem; }
.record > * + * { margin-top: 0.625rem; }
.record-head { display: flex; align-items: center; gap: 0.75rem; }
/* The name carries the row, so it reads as a heading rather than as one more
   input in a stack of identically weighted boxes. */
.record-title { flex: 1; min-width: 0; font-weight: 600; }
.record-actions { display: flex; flex-shrink: 0; align-items: center; gap: 0.5rem; }
/* Destructive, so it reads as destructive on hover rather than as one more
   grey button beside the reorder arrows. */
.record-delete { color: var(--muted-foreground); }
.record-delete:hover { background: var(--destructive); color: var(--destructive-foreground); }
/* A row deleted but not yet saved is hidden outright rather than dimmed: it is
   gone from the working copy, and a ghost of it invites clicking the fields. */
[data-record][hidden] { display: none; }
/* Nothing to save, nothing to discard. */
[data-save]:disabled, [data-revert]:disabled { pointer-events: none; opacity: 0.4; }
/* The add row is the same shape, dimmed until it has something in it. */
.record--new { border-style: dashed; }
.record--new:focus-within { border-style: solid; }
@media (max-width: 639px) {
  .record-head { flex-direction: column; align-items: stretch; }
  .record-actions { justify-content: flex-end; }
}

/* One repeated record inside a card: a skill group, an education entry. Bordered
   rather than plated, so a card holding five of them still reads as a list of
   editable things and not as five nested cards. */
.entity-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.875rem 1rem;
}
.entity-card + .entity-card { margin-top: 0.75rem; }
.entity-card > * + * { margin-top: 0.75rem; }
.entity-card-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
}
@media (max-width: 639px) {
  .entity-card-head { flex-direction: column; align-items: stretch; }
}
.entity-card-title { font-size: 0.875rem; font-weight: 600; }
.entity-card-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
/* Delete is a separate <form>, so it cannot sit inside the edit form's action
   row; this pulls it back onto the same line without nesting forms. */
.entity-card-trailing { margin-top: -2.25rem; text-align: right; }
@media (max-width: 639px) { .entity-card-trailing { margin-top: 0.5rem; } }
.entity-card-narrow { flex: 1 1 12rem; }
.entity-card-wide { flex: 3 1 22rem; }

/* The adhoc URL box is the whole point of its card, so it takes the width and
   the button sits beside it instead of under a full-bleed input. */
.adhoc { display: flex; align-items: flex-start; gap: 0.5rem; }
.adhoc .field { flex: 1; min-width: 0; }
@media (max-width: 639px) { .adhoc { flex-direction: column; align-items: stretch; } }

/* --------------------------------------------------------- alert ---- */

.alert {
  display: flex; align-items: flex-start; gap: 0.5rem;
  border: 1px solid;
  border-radius: var(--radius-md);
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
}
.alert .icon { margin-top: 0.125rem; }
.alert-info    { border-color: #1d4ed8; background: rgb(23 37 84 / 0.4);  color: #93c5fd; }
.alert-warning { border-color: #b45309; background: rgb(69 26 3 / 0.4);   color: #fcd34d; }
.alert-success { border-color: #15803d; background: rgb(5 46 22 / 0.4);   color: #86efac; }
.alert-error   { border-color: #b91c1c; background: rgb(69 10 10 / 0.4);  color: #fca5a5; }
.alert-body > * { margin: 0; }
.alert-body > * + * { margin-top: 0.375rem; }

/* ---------------------------------------------------- empty state --- */

.empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.75rem;
  padding: 3rem 1rem;
  text-align: center;
}
.empty-icon {
  display: flex; height: 3rem; width: 3rem;
  align-items: center; justify-content: center;
  border-radius: 9999px;
  background: var(--muted);
  color: var(--muted-foreground);
}
.empty-title { font-size: 0.875rem; font-weight: 500; }
.empty-desc { margin: 0.25rem 0 0; font-size: 0.875rem; color: var(--muted-foreground); }

/* ------------------------------------------------------ info row ---- */

.info-list > .info-row + .info-row { border-top: 1px solid var(--border); }
.info-row { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.625rem 0; }
.info-row-label { width: 10rem; flex-shrink: 0; font-size: 0.875rem; color: var(--muted-foreground); }
.info-row-value { font-size: 0.875rem; font-weight: 500; }

/* Label left, value hard right -- for pairs whose label is a sentence rather
   than a short noun, where a fixed 10rem label column would wrap. */
.info-row--split { justify-content: space-between; }
.info-row--split .info-row-label { width: auto; flex: 1; }

/* ---------------------------------------------------------- utilities --- */

.row { display: flex; flex-wrap: wrap; gap: 1rem; }
.row > * { flex: 1 1 14rem; min-width: 0; }
.grid-2 { display: grid; gap: 1.5rem; }
@media (min-width: 1024px) { .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.stack > * + * { margin-top: 1rem; }
.stack-sm > * + * { margin-top: 0.5rem; }
.divide > * + * { border-top: 1px solid var(--border); }

.inline-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tabular { font-variant-numeric: tabular-nums; }
.font-semibold { font-weight: 600; }
.hide { display: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ------------------------------------------ application status cell --- */

/* The select and its button share one line inside a table cell. Without a width
   the select inherits the `width: 100%` above and pushes the button out of the
   row; `auto` would size it to the longest option, so every row would be a
   different width and the column would not line up. */
.status-form { display: flex; align-items: center; gap: 0.5rem; }
.status-form select { width: 9rem; height: 2rem; }

/* ------------------------------------------ run results columns --- */

/* Floors for the two columns that fight each other. The draft controls want one
   line, and without a floor the column narrowed until whether a row wrapped
   depended on how long its company name was, so the column came out ragged.
   Giving it the room unopposed then squeezed the reasoning prose into a
   six-line ribbon, which is worse. With a floor on each, a window too narrow
   for both scrolls the table -- which is what table-wrap is for -- instead of
   crushing whichever column loses. */
.col-reason { min-width: 16rem; }
/* 24rem, not 21: the widest case is a row whose render failed, where the
   badge stands in for the PDF button and is wider than it. */
.col-actions { min-width: 24rem; }
