/* Jobbo component layer. Built only from web/tokens.css. Prefix: ui-.
   The dashboard, CV and Brev views are migrated onto these classes; new UI uses them directly. */

/* ---- Base ------------------------------------------------------------------------ */
.ui-root, .ui-root * { box-sizing: border-box; }
.ui-root {
  font-family: var(--font);
  font-size: var(--text-md);
  line-height: var(--lh-md);
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}
/* :where() keeps this default at the weight of a bare `a`, so any class that sets a link's
   colour wins. As `.ui-root a` it beat every single-class rule (the header wordmark, card
   links, the skip link and the dark band's link were all painted link-teal by accident). */
:where(.ui-root) a { color: var(--accent-ink); text-decoration: none; }
:where(.ui-root) a:hover { text-decoration: underline; text-underline-offset: 2px; }
.ui-root :focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-sm); }
/* !important, because the thing being hidden usually carries a display of its own
   (.ui-field is a grid, .ui-btn a flex) and would otherwise win on specificity. */
.hidden { display: none !important; }

/* ---- Type ------------------------------------------------------------------------ */
.ui-title { font-family: var(--font-display); font-size: var(--text-2xl); line-height: var(--lh-2xl); font-weight: var(--weight-semibold); letter-spacing: -0.02em; margin: 0; }
.ui-h2 { font-family: var(--font-display); font-size: var(--text-xl); line-height: var(--lh-xl); font-weight: var(--weight-semibold); letter-spacing: -0.02em; margin: 0; }
.ui-h3 { font-family: var(--font-display); font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-semibold); letter-spacing: -0.01em; margin: 0; }
.ui-body { font-size: var(--text-md); line-height: var(--lh-md); color: var(--ink-2); }
.ui-meta { font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--ink-3); }
.ui-caps { font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--ink-3); }
.ui-num { font-variant-numeric: tabular-nums; font-feature-settings: var(--tabular); }
.ui-muted { color: var(--ink-4); }

/* ---- Buttons --------------------------------------------------------------------- */
.ui-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  height: 38px; padding: 0 var(--s-4);
  border-radius: var(--r-sm); border: 1px solid var(--line-strong);
  background: var(--card); background-image: var(--btn-face); color: var(--ink);
  box-shadow: var(--btn-shadow);
  font: inherit; font-size: var(--text-md); font-weight: var(--weight-medium); line-height: 1;
  cursor: pointer; white-space: nowrap;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), box-shadow var(--t-base) var(--ease), transform var(--t-fast) var(--ease);
}
.ui-btn:hover { background-color: var(--hover); border-color: var(--line-strong); box-shadow: var(--btn-shadow-hover); }
.ui-btn:active { background: var(--sunken); box-shadow: inset 0 1px 2px rgba(11, 31, 42, 0.10); transform: translateY(0.5px); }
.ui-btn:disabled { color: var(--ink-4); background: var(--canvas-tint); border-color: var(--line-soft); box-shadow: none; transform: none; cursor: default; }
/* The primary: gradient face, a darker edge, a lit top line and a soft sea glow under it. */
.ui-btn-primary { background: var(--accent); background-image: var(--btn-primary-face); border-color: var(--btn-primary-edge); color: var(--on-accent); font-weight: var(--weight-semibold); box-shadow: var(--btn-primary-shadow); }
.ui-btn-primary:hover { background: var(--accent-hover); background-image: var(--btn-primary-face-hover); border-color: var(--btn-primary-edge); box-shadow: var(--btn-primary-shadow-hover); transform: translateY(-1px); }
.ui-btn-primary:active { background: var(--accent-active); border-color: var(--accent-active); box-shadow: inset 0 1px 3px rgba(3, 32, 43, 0.35); transform: translateY(0); }
.ui-btn-primary:disabled { background: var(--canvas-tint); }
.ui-btn-ghost { background: transparent; border-color: transparent; color: var(--ink-2); box-shadow: none; }
.ui-btn-ghost:hover { background: var(--hover); border-color: transparent; color: var(--ink); box-shadow: none; }
.ui-btn-ghost:active { box-shadow: none; }
@media (prefers-reduced-motion: reduce) { .ui-btn, .ui-btn:hover, .ui-btn:active { transform: none; } }
.ui-btn-danger { color: var(--danger); }
.ui-btn-danger:hover { background: var(--danger-soft); border-color: var(--danger-soft); }
/* A button that reads as a link, for an action that changes what the page is showing
   rather than going anywhere: "Glömt lösenordet?", "Skicka bekräftelselänk". A real
   <button>, so it is announced as one and does not offer "open in a new tab". */
.ui-linkbtn {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: var(--accent-ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.ui-linkbtn:hover { color: var(--accent-hover); }
.ui-linkbtn[disabled] { color: var(--ink-4); text-decoration: none; cursor: default; }
.ui-btn-sm { height: 30px; padding: 0 var(--s-3); font-size: var(--text-sm); }
.ui-btn-lg { height: 48px; padding: 0 var(--s-6); font-size: var(--text-lg); border-radius: 10px; }
.ui-btn-icon { width: 36px; padding: 0; }
.ui-btn-icon.ui-btn-sm { width: 30px; }
.ui-link-btn { background: none; border: none; padding: 0; color: var(--accent-ink); font: inherit; font-weight: var(--weight-medium); cursor: pointer; }
.ui-link-btn:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ---- Inputs (inset: darker than around them, no heavy border) ------------------- */
.ui-input, .ui-select, .ui-textarea {
  width: 100%; height: 36px; padding: 0 var(--s-3);
  border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--sunken); color: var(--ink);
  font: inherit; font-size: var(--text-md);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
/* A select draws its own chevron, so it matches the dropdown buttons beside it. */
.ui-select {
  appearance: none; -webkit-appearance: none; padding-right: 32px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%235b6f79' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center;
}
.ui-textarea { height: auto; min-height: 84px; padding: var(--s-2) var(--s-3); line-height: var(--lh-md); resize: vertical; }
.ui-input::placeholder, .ui-textarea::placeholder { color: var(--ink-4); }
.ui-input:hover, .ui-select:hover, .ui-textarea:hover { border-color: var(--line-strong); }
.ui-input:focus, .ui-select:focus, .ui-textarea:focus { outline: none; background: var(--card); border-color: var(--focus); box-shadow: var(--focus-ring); }
.ui-input:disabled { color: var(--ink-4); background: var(--canvas-tint); }
/* Inline edit inside a table row, or a search box inside a card: the same field, one
   step quieter. Declared before .ui-input-search so the search icon keeps its gutter. */
.ui-input-sm { height: 30px; padding: 0 var(--s-2); font-size: var(--text-sm); }
.ui-input-search { padding-left: 34px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: 11px center; }
.ui-field { display: grid; gap: var(--s-1); }
.ui-label { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--ink-2); }
.ui-help { font-size: var(--text-sm); color: var(--ink-3); }
.ui-switch { position: relative; width: 36px; height: 20px; border-radius: var(--r-pill); background: var(--ink-4); border: none; cursor: pointer; transition: background var(--t-fast) var(--ease); padding: 0; }
.ui-switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--on-accent); transition: transform var(--t-fast) var(--ease); }
.ui-switch[aria-checked="true"] { background: var(--accent); }
.ui-switch[aria-checked="true"]::after { transform: translateX(16px); }
.ui-check { display: inline-flex; align-items: center; gap: var(--s-2); font-size: var(--text-md); color: var(--ink-2); cursor: pointer; }
.ui-check input { width: 16px; height: 16px; accent-color: var(--accent); margin: 0; }

/* Chips input: free text that commits to removable chips (titles, excluded employers). */
.ui-chips-input {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1);
  min-height: 36px; padding: var(--s-1);
  border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--sunken); cursor: text;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.ui-chips-input:hover { border-color: var(--line-strong); }
.ui-chips-input:focus-within { background: var(--card); border-color: var(--focus); box-shadow: var(--focus-ring); }
.ui-chips-input > input {
  flex: 1 1 96px; min-width: 88px; height: 26px; padding: 0 var(--s-1);
  border: none; background: none; color: var(--ink); font: inherit; font-size: var(--text-md);
}
.ui-chips-input > input:focus { outline: none; box-shadow: none; }
.ui-chips-input > input::placeholder { color: var(--ink-4); }

/* ---- Segmented control ------------------------------------------------------------ */
.ui-seg { display: inline-flex; padding: 2px; border-radius: var(--r-sm); background: var(--sunken); border: 1px solid var(--line-soft); }
.ui-seg > button { height: 28px; padding: 0 var(--s-3); border: none; border-radius: 4px; background: transparent; color: var(--ink-2); font: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium); cursor: pointer; transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease); }
.ui-seg > button[aria-pressed="true"] { background: var(--card); color: var(--ink); box-shadow: 0 1px 2px var(--line); }

/* ---- Chips and badges ------------------------------------------------------------- */
.ui-chip { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border-radius: var(--r-pill); border: 1px solid var(--line); background: var(--card); color: var(--ink-2); font-size: var(--text-sm); font-weight: var(--weight-medium); white-space: nowrap; }
.ui-chip-btn { cursor: pointer; }
.ui-chip-btn:hover { border-color: var(--line-strong); background: var(--hover); }
.ui-chip[aria-pressed="true"], .ui-chip.is-active { background: var(--accent-soft); border-color: transparent; color: var(--accent-ink); }
.ui-chip .ui-chip-x { margin-left: 2px; padding: 0; border: none; background: none; font: inherit; line-height: 1; color: var(--ink-4); cursor: pointer; }
.ui-chip .ui-chip-x:hover { color: var(--danger); }
.ui-badge { display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 8px; border-radius: var(--r-sm); font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: 0.01em; white-space: nowrap; }
.ui-badge-neutral { background: var(--info-soft); color: var(--info); }
.ui-badge-accent { background: var(--accent-soft); color: var(--accent-ink); }
.ui-badge-ok { background: var(--ok-soft); color: var(--ok); }
.ui-badge-warn { background: var(--warn-soft); color: var(--warn); }
.ui-badge-danger { background: var(--danger-soft); color: var(--danger); }
.ui-badge-salary { background: var(--salary-bg); color: var(--salary-ink); font-weight: var(--weight-medium); font-size: var(--text-sm); height: 24px; border-radius: 6px; }
.ui-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

/* ---- Monogram tile (signature 1): deterministic initials, hue from the employer name */
.ui-tile { flex: 0 0 auto; width: var(--tile); height: var(--tile); border-radius: 10px; display: grid; place-items: center; font-family: var(--font-display); font-weight: var(--weight-bold); font-size: 15px; letter-spacing: 0.01em; color: var(--tile-ink, var(--accent-ink)); background: var(--tile-bg, var(--card)); border: 1px solid var(--tile-line, var(--line)); user-select: none; }
.ui-tile-lg { width: var(--tile-lg); height: var(--tile-lg); font-size: 20px; border-radius: 12px; }
.ui-tile img { width: 100%; height: 100%; object-fit: contain; border-radius: inherit; background: var(--card); }
.ui-tile-logo { padding: 4px; }
.ui-tile-logo img { border-radius: var(--r-2xs); background: none; }

/* ---- An ad's facts: one calm row of pills ----------------------------------------
   /jobb/ writes an ad's terms as a row under the title rather than a grid of labelled
   cells: "Tillsvidare", "Heltid", "Distans", the pay. Quiet on purpose — a tint, a
   hairline, secondary ink — because the row is scanned, not read, and nothing in it may
   compete with the title. The same rules live in web/jobb.css as .jb-pill / .jb-meta-*;
   point that page here when it is next opened. */
.ui-metarow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3); }
/* Employer and place read as facts about the ad; the framed pills after them as terms. */
.ui-metaitem { display: inline-flex; align-items: center; gap: 5px; min-width: 0; color: var(--ink-2); font-size: var(--text-sm); }
.ui-metaitem .ui-icon { width: 15px; height: 15px; color: var(--ink-3); }
.ui-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  max-width: 100%;
  /* 28 px: no token fits a control height, and the pills sit between .ui-btn-sm (30)
     and .ui-chip (26). A literal on purpose. */
  height: 28px;
  padding: 0 var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--canvas-tint);
  color: var(--ink-2);
  font-size: var(--text-sm);
  white-space: nowrap;
}
.ui-pill .ui-icon { width: 14px; height: 14px; color: var(--ink-4); }
.ui-pill-t { overflow: hidden; text-overflow: ellipsis; }
/* The pay pill is the one loud pill, so it comes after .ui-pill and wins the wash. */
.ui-pill-pay { background: var(--salary-bg); border-color: transparent; color: var(--salary-ink); font-weight: var(--weight-medium); }

/* ---- The ad's action bar ----------------------------------------------------------
   Sticky at the top of the pane, so Spara and Ansök are one reach away however far down
   the ad the reader is. Jump links at the left, the ad's actions at the right: the two
   that need no word as icons, the two that do as words, and one primary. Every action on
   the ad lives here, once. /jobb/ writes the same bar as .jb-d-bar. */
.ui-panebar {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-3) var(--s-6);
  background: var(--card);
  border-bottom: 1px solid var(--line-soft);
}
.ui-panenav { display: flex; align-items: center; gap: var(--s-5); min-width: 0; }
.ui-panenav-btn { position: relative; padding: var(--s-1) 0; border: none; background: none; font: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--ink-3); cursor: pointer; }
.ui-panenav-btn:hover { color: var(--ink); }
/* Where the reader is: ink, and an accent rule sitting on the bar's own bottom rule. */
.ui-panenav-btn.is-current { color: var(--ink); }
.ui-panenav-btn.is-current::after { content: ""; position: absolute; left: 0; right: 0; bottom: calc(-1 * var(--s-3) - 1px); height: 2px; background: var(--accent); }
.ui-panetools { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--s-2); margin-left: auto; }
/* On a phone the bar is for the actions: the jump links go, so the tools get the width. */
@media (max-width: 599px) { .ui-panenav { display: none; } }
.ui-panetools a.ui-btn, .ui-panetools a.ui-btn:hover { text-decoration: none; }
.ui-panetools .ui-btn .ui-icon { width: 16px; height: 16px; }
.ui-panetools .ui-save.is-on { background: var(--sun-soft); border-color: transparent; color: var(--saved); }
.ui-panetools .ui-save.is-on .ui-icon { fill: currentColor; }
.ui-panetools .ui-applied.is-on { background: var(--accent-soft); border-color: transparent; color: var(--accent-ink); }

/* ---- The column beside the ad ------------------------------------------------------
   How you fit the ad on the pane's tint, then the two plain cards under it: the way on,
   and the employer at a glance. */
.ui-fitbox { display: grid; gap: var(--s-2); padding: var(--s-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--canvas-tint); }
.ui-fit-count { color: var(--ink-2); font-weight: var(--weight-medium); }
.ui-sidebox { display: grid; gap: var(--s-2); padding: var(--s-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card); }
.ui-sidebox-text { color: var(--ink-3); font-size: var(--text-sm); line-height: var(--lh-md); }
.ui-sidebox-name { color: var(--ink); font-weight: var(--weight-semibold); }
.ui-sidebox-btn { justify-self: start; }
.ui-sidebox-link { font-size: var(--text-sm); font-weight: var(--weight-medium); }
.ui-sidefacts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s-1) var(--s-3); margin: 0; font-size: var(--text-sm); }
.ui-sidefacts dt { color: var(--ink-3); }
/* Long Swedish compounds hyphenate at a syllable instead of snapping wherever the
   column ends; break-word is only the last resort. */
.ui-sidefacts dd { margin: 0; color: var(--ink-2); overflow-wrap: break-word; hyphens: auto; }

/* ---- Sista ansökningsdag: the window, not just the date ---------------------------
   A date alone is work for the reader. The track shows how much of the application
   window is already gone, which is what decides whether to open the ad now or later. */
.ui-dl { display: grid; gap: var(--s-1); justify-items: start; align-content: start; max-width: 260px; text-align: left; }
.ui-dl-top { display: flex; align-items: baseline; gap: var(--s-1); }
.ui-dl-top .ui-icon { width: 13px; height: 13px; align-self: center; color: var(--ink-4); }
.ui-dl-label { font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--ink-3); }
.ui-dl-date { font-family: var(--font-display); font-size: var(--text-lg); font-weight: var(--weight-semibold); letter-spacing: -0.01em; color: var(--ink); }
.ui-dl-left { font-size: var(--text-xs); color: var(--ink-3); }
.ui-dl.is-soon .ui-dl-left { color: var(--deadline-soon); font-weight: var(--weight-medium); }
.ui-dl.is-urgent .ui-dl-left { color: var(--deadline-urgent); font-weight: var(--weight-semibold); }
/* Elapsed, not remaining: the filled part is the time already spent. */
.ui-dl-track { position: relative; width: 100%; height: var(--s-1); margin-top: 2px; border-radius: var(--r-pill); background: var(--sunken); overflow: hidden; }
.ui-dl-fill { position: absolute; top: 0; bottom: 0; left: 0; border-radius: inherit; background: var(--ok); }
.ui-dl.is-soon .ui-dl-fill { background: var(--deadline-soon); }
.ui-dl.is-urgent .ui-dl-fill { background: var(--deadline-urgent); }
.ui-dl-note { font-size: var(--text-xs); color: var(--ink-3); }

/* ---- Fit triplet (signature 2) --------------------------------------------------- */
.ui-fit { display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-3); font-size: var(--text-sm); line-height: var(--lh-sm); }
.ui-fit > span { display: inline-flex; align-items: baseline; gap: 5px; }
.ui-fit-pass { color: var(--fit-pass); }
.ui-fit-missing { color: var(--fit-missing); }
.ui-fit-unclear { color: var(--fit-unclear); }
.ui-fit b { font-weight: var(--weight-semibold); }
.ui-fit-mark { display: inline-grid; place-items: center; width: 14px; height: 14px; border-radius: 4px; font-size: 10px; font-weight: var(--weight-bold); line-height: 1; }
.ui-fit-pass .ui-fit-mark { background: var(--ok-soft); }
.ui-fit-missing .ui-fit-mark { background: var(--warn-soft); }
.ui-fit-unclear .ui-fit-mark { background: var(--info-soft); }
/* Panel form: three columns of lists */
.ui-fit-panel { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
.ui-fit-panel h4 { margin: 0 0 var(--s-2); font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-caps); text-transform: uppercase; }
.ui-fit-panel ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-1); }
.ui-fit-panel li { display: flex; gap: var(--s-2); font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--ink-2); }
@media (max-width: 720px) { .ui-fit-panel { grid-template-columns: 1fr; } }

/* ---- Cards ------------------------------------------------------------------------ */
.ui-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md); }
.ui-card-pad { padding: var(--s-4) var(--s-5); }
.ui-card-selected { border-color: var(--accent); box-shadow: 0 0 0 0.5px var(--accent); }
.ui-card-hover:hover { border-color: var(--line-strong); background: var(--hover); }

/* ---- The job row ------------------------------------------------------------------
   One shape for a list of ads, on /jobb/ and in the app. Hairline-separated rows instead
   of cards, the employer's mark at the left, the title in the display face, the ad's own
   facts on one quiet line under it and its dates on the next, in tabular figures. Rows,
   because a list of jobs is a list and not a stack of posters: a border and a shadow
   repeated 25 times separate nothing and cost height a reader would rather spend on the
   26th ad. Someone who searches on the site and then signs in should not have to learn a
   second way of reading the same ad.

   Two elements, because /jobb/ needs them: the outer owns the separator, the wash and the
   actions column, and the inner is a real <a href="/jobb/<id>"> so the row works without
   JavaScript and for a crawler. The app's inner is a plain <div>. */
.ui-list { display: grid; list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.ui-job {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  border-bottom: 1px solid var(--line-soft);
  transition: background var(--t-fast) var(--ease);
}
.ui-job:hover { background: var(--hover); }
/* The wash plus the accent edge: the pair painted on the row being read. */
.ui-job.is-selected { background: var(--selected); box-shadow: inset 3px 0 0 var(--accent); }
.ui-job-a {
  display: grid;
  grid-template-columns: var(--tile) minmax(0, 1fr);
  column-gap: var(--s-3);
  row-gap: 2px;
  align-items: start;
  padding: var(--s-3);
  color: inherit;
  cursor: pointer;
}
.ui-job-a:hover { text-decoration: none; }
.ui-job-body { min-width: 0; display: grid; gap: var(--s-1); align-content: start; }
.ui-job-title { font-family: var(--font-display); font-size: var(--text-md); line-height: var(--lh-md); font-weight: var(--weight-semibold); color: var(--ink); letter-spacing: -0.01em; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
/* Employer · ort · anställningsform, as text. The icons that used to label each of the
   three said what the words already say. */
.ui-job-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-3); min-width: 0; font-size: var(--text-sm); color: var(--ink-2); }
.ui-job-meta > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The last line: what the ad pays when it says so, how old it is, when it closes, and
   the marks this reader has put on it. */
.ui-job-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-1) var(--s-3);
  font-size: var(--text-sm);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--tabular);
}
/* Always in the DOM, so an action can drop Sparad or Sökt in without re-rendering. */
.ui-job-marks { display: inline-flex; align-items: center; gap: var(--s-1); }
.ui-job-marks:empty { display: none; }
/* An action on a row is quiet until it means something, and never underlined. */
.ui-job-action { color: var(--ink-4); }
.ui-job-action:hover { color: var(--ink-2); text-decoration: none; }
.ui-job-action.is-on { background: var(--sun-soft); border-color: transparent; color: var(--saved); }
.ui-job-action.is-on svg { fill: currentColor; }
.ui-job-actions { display: flex; gap: var(--s-1); opacity: 0; transition: opacity var(--t-fast) var(--ease); }
.ui-job:hover .ui-job-actions, .ui-job:focus-within .ui-job-actions { opacity: 1; }
/* The shortlist position: quiet, and never wider than three figures. */
.ui-job-rank { justify-self: end; padding-top: 2px; font-size: var(--text-sm); color: var(--ink-4); font-variant-numeric: tabular-nums; }
/* A phone reads one column, so the title can have the size it deserves there. */
@media (max-width: 899px) {
  .ui-job-title { font-size: var(--text-lg); line-height: var(--lh-lg); }
}

/* ---- List + reading pane -------------------------------------------------------- */
.ui-split { display: grid; grid-template-columns: var(--list-w) minmax(0, 1fr); gap: var(--s-8); align-items: start; }
.ui-pane { position: sticky; top: calc(var(--topbar-h) + var(--s-4)); max-height: calc(100vh - var(--topbar-h) - var(--s-8)); overflow: auto; }
/* The split survives down to the sidebar's own desktop breakpoint (UX-PLAN §9): at
   1024 px and up Idag keeps list + reading pane, below it the ad opens as its own view. */
@media (max-width: 1023.98px) { .ui-split { grid-template-columns: 1fr; } .ui-pane { position: static; max-height: none; } }

/* Reading pane header */
/* The ad's head: the mark, then a column that stacks title, marks, the fact row, the
   deadline, the actions and the provenance line — /jobb/'s .jb-d-head. */
.ui-pane-head { display: grid; grid-template-columns: var(--tile-lg) minmax(0, 1fr); gap: var(--s-2) var(--s-4); align-items: start; padding: var(--s-5) var(--s-6) var(--s-4); border-bottom: 1px solid var(--line-soft); }
/* Title and the fact row read as one block, so they sit close: 8 px, not 12. */
.ui-pane-headtext { min-width: 0; display: grid; gap: var(--s-2); align-content: start; justify-items: start; }
/* Narrow: the deadline goes under the fact row rather than squeezing the title. */
.ui-pane-head > .ui-dl { grid-column: 2; }
/* margin: 0 — the element is an <h2> and the browser default put 16 px under it, which
   is what kept the title and the fact row so far apart. */
.ui-pane-title { margin: 0; font-family: var(--font-display); font-size: var(--text-xl); line-height: var(--lh-xl); font-weight: var(--weight-semibold); letter-spacing: -0.01em; }
.ui-pane-cta { display: flex; gap: var(--s-2); margin-top: var(--s-3); flex-wrap: wrap; }
.ui-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--s-4); padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--line-soft); }
.ui-fact-k { font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--ink-3); }
.ui-fact-v { margin-top: 2px; font-size: var(--text-md); color: var(--ink); }
.ui-prose { padding: var(--s-6); color: var(--ink-2); font-size: var(--text-md); line-height: 1.6; max-width: 72ch; }
.ui-prose h3 { color: var(--ink); font-size: var(--text-lg); margin: var(--s-6) 0 var(--s-2); }

/* ---- Top bar and navigation ------------------------------------------------------ */
.ui-topbar { position: sticky; top: 0; z-index: var(--z-sticky); height: var(--topbar-h); display: flex; align-items: center; gap: var(--s-6); padding: 0 var(--gutter); background: var(--canvas); border-bottom: 1px solid var(--line); }
.ui-logo { font-family: var(--font-display); font-weight: var(--weight-bold); font-size: 20px; letter-spacing: -0.02em; color: var(--ink); }
.ui-nav { display: flex; gap: var(--s-1); }
.ui-nav a, .ui-nav button { height: 32px; padding: 0 var(--s-3); border-radius: var(--r-sm); border: none; background: transparent; color: var(--ink-2); font: inherit; font-size: var(--text-md); font-weight: var(--weight-medium); cursor: pointer; display: inline-flex; align-items: center; text-decoration: none; }
.ui-nav a:hover, .ui-nav button:hover { background: var(--hover); color: var(--ink); text-decoration: none; }
.ui-nav a.is-active, .ui-nav button.is-active { background: var(--accent-soft); color: var(--accent-ink); }
.ui-topbar-spacer { flex: 1; }
.ui-crumbs { display: flex; gap: var(--s-2); font-size: var(--text-sm); color: var(--ink-3); }
.ui-crumbs a { color: var(--ink-2); }

/* Page header */
.ui-page { max-width: var(--content-max); margin: 0 auto; padding: var(--s-6) var(--gutter) var(--s-16); }
.ui-page-head { display: flex; align-items: end; justify-content: space-between; gap: var(--s-4); margin-bottom: var(--s-5); }
.ui-page-sub { margin-top: var(--s-1); color: var(--ink-3); }

/* ---- Tables ------------------------------------------------------------------------ */
.ui-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--text-md); }
.ui-table th { text-align: left; font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--ink-3); padding: var(--s-2) var(--s-3); background: var(--canvas-tint); border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 1; }
.ui-table td { padding: var(--s-3); border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
.ui-table tbody tr:last-child td { border-bottom: none; }
.ui-table tbody tr:hover td { background: var(--hover); }
.ui-table .ui-num { text-align: right; }
/* Sortable head cell: the caps label stays quiet, the active column gets one arrow. */
.ui-table th[aria-sort] { cursor: pointer; user-select: none; }
.ui-table th[aria-sort]:hover { color: var(--ink); }
.ui-table th[aria-sort]::after { content: ""; margin-left: var(--s-1); color: var(--ink-4); }
.ui-table th[aria-sort="ascending"]::after { content: "↑"; color: var(--accent-ink); }
.ui-table th[aria-sort="descending"]::after { content: "↓"; color: var(--accent-ink); }
/* The frame a table sits in: hairline card outside, both scroll axes inside, so the
   caps header stays put while the rows move and a wide table never widens the page. */
.ui-table-wrap { overflow: auto; overscroll-behavior: contain; border-radius: inherit; }

/* ---- Floating layers -------------------------------------------------------------- */
.ui-popover { position: absolute; z-index: var(--z-float); min-width: 200px; padding: var(--s-2); background: var(--raised); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--shadow-float); }
.ui-menu-item { display: flex; align-items: center; gap: var(--s-2); width: 100%; height: 32px; padding: 0 var(--s-3); border: none; border-radius: var(--r-sm); background: transparent; color: var(--ink); font: inherit; font-size: var(--text-md); text-align: left; cursor: pointer; }
.ui-menu-item:hover { background: var(--hover); }
.ui-menu-item.is-danger { color: var(--danger); }
/* The value a menu is currently set to (pickers: level, language, template). */
.ui-menu-item.is-current { background: var(--selected); color: var(--accent-ink); font-weight: var(--weight-medium); }
/* ---- Picker popover (web/picker.js, UX-PLAN §11) -----------------------------------
   A search box over a checklist with counts and a "Rensa". Same surface and rows as the
   menu popover above, so a picker never reads as a second kind of floating thing.
   Under 720 px it is a bottom sheet instead. */
.ui-picker { display: grid; gap: var(--s-2); max-width: calc(100vw - 16px); }
.ui-picker-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); padding: 0 var(--s-2); }
.ui-picker-clear { font-size: var(--text-sm); }
.ui-picker-search { width: 100%; }
.ui-picker-list { display: grid; gap: 2px; max-height: 280px; overflow-y: auto; overscroll-behavior: contain; }
.ui-picker-opt { justify-content: flex-start; height: 34px; }
.ui-picker-opt:focus-visible { background: var(--hover); }
.ui-picker-opt.is-on { color: var(--accent-ink); font-weight: var(--weight-medium); }
.ui-picker-box { flex: none; width: 15px; height: 15px; border: 1px solid var(--line-strong); border-radius: 4px; background: var(--card); }
.ui-picker-opt.is-on .ui-picker-box { border-color: transparent; background: var(--accent); }
.ui-picker-opt.is-on .ui-picker-box::after { content: "✓"; display: block; margin-top: -3px; color: var(--on-accent); font-size: 12px; line-height: 16px; text-align: center; }
.ui-picker-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-picker-count { flex: none; color: var(--ink-4); font-size: var(--text-xs); }
.ui-picker-empty { padding: var(--s-2) var(--s-3); color: var(--ink-4); font-size: var(--text-sm); }

@media (max-width: 720px) {
  .ui-picker-sheet {
    position: fixed; z-index: var(--z-sheet); left: 0; right: 0; bottom: 0; top: auto;
    width: auto; max-width: none; max-height: 70vh; overflow: auto;
    padding: var(--s-4); border-radius: var(--r-lg) var(--r-lg) 0 0;
  }
  .ui-picker-sheet .ui-picker-list { max-height: 44vh; }
  .ui-picker-scrim { z-index: var(--z-float); }
}

.ui-dialog { position: fixed; z-index: var(--z-dialog); inset: 0; display: grid; place-items: center; background: var(--scrim); }
.ui-dialog-card { width: min(520px, calc(100vw - 32px)); background: var(--raised); border-radius: var(--r-lg); box-shadow: var(--shadow-dialog); padding: var(--s-6); }
.ui-toast { position: fixed; z-index: var(--z-toast); left: 50%; bottom: var(--s-6); transform: translateX(-50%); display: inline-flex; align-items: center; gap: var(--s-4); padding: var(--s-3) var(--s-4); background: var(--ink); color: var(--ink-inverse); border-radius: var(--r-md); box-shadow: var(--shadow-float); font-size: var(--text-md); }
.ui-toast .ui-link-btn { color: var(--cyan); }

/* ---- Notice, empty, skeleton ------------------------------------------------------- */
.ui-notice { display: flex; gap: var(--s-3); align-items: start; padding: var(--s-3) var(--s-4); border-radius: var(--r-md); border: 1px solid var(--line); background: var(--canvas-tint); color: var(--ink-2); font-size: var(--text-sm); }
.ui-notice-warn { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
.ui-notice-ok { background: var(--ok-soft); border-color: transparent; color: var(--ok); }
.ui-empty { text-align: center; padding: var(--s-12) var(--s-6); color: var(--ink-3); }
.ui-empty .ui-h3 { color: var(--ink); margin-bottom: var(--s-2); }
.ui-empty .ui-btn { margin-top: var(--s-4); }
.ui-skeleton { background: linear-gradient(90deg, var(--sunken) 25%, var(--hover) 50%, var(--sunken) 75%); background-size: 200% 100%; animation: ui-shimmer 1.2s linear infinite; border-radius: var(--r-sm); }
@keyframes ui-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ---- Deadline and freshness chips (Jobbo vocabulary) ------------------------------- */
.ui-deadline { color: var(--ink-3); }
.ui-deadline-soon { color: var(--deadline-soon); font-weight: var(--weight-medium); }
.ui-deadline-urgent { color: var(--deadline-urgent); font-weight: var(--weight-semibold); }
.ui-new { display: inline-flex; align-items: center; gap: 5px; color: var(--accent-ink); font-weight: var(--weight-semibold); font-size: var(--text-xs); }
.ui-new::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--fresh); }
.ui-source { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); color: var(--ink-3); }
.ui-source .ui-dot { color: var(--source-platsbanken); }
.ui-source[data-source="teamtailor"] .ui-dot { color: var(--source-teamtailor); }
.ui-source[data-source="smartrecruiters"] .ui-dot { color: var(--source-smartrecruiters); }
.ui-source[data-source="workday"] .ui-dot { color: var(--source-workday); }
.ui-source[data-source="ashby"] .ui-dot { color: var(--source-ashby); }
.ui-source[data-source="greenhouse"] .ui-dot { color: var(--source-greenhouse); }
.ui-source[data-source="lever"] .ui-dot { color: var(--source-lever); }
.ui-source[data-source="joblinks"] .ui-dot { color: var(--source-joblinks); }
.ui-source[data-source="manual"] .ui-dot { color: var(--ink-4); }

/* ---- Icons: the 16 px stroke sprite from the style guide --------------------------- */
.ui-icon { flex: 0 0 auto; width: 16px; height: 16px; stroke-width: 1.75; }
.ui-icon-sm { width: 14px; height: 14px; }

/* ---- Status badges: the two Jobbo states a card can carry -------------------------- */
.ui-badge-saved { background: var(--sun-soft); color: var(--saved); }
.ui-badge-applied { background: var(--accent-soft); color: var(--accent-ink); }

/* ---- Skeleton: the reading pane while an ad loads ---------------------------------- */
.ui-job-skeleton { display: grid; grid-template-columns: var(--tile) minmax(0, 1fr); gap: var(--s-3) var(--s-4); padding: var(--s-6); }
.ui-job-skeleton > .ui-skeleton:first-child { width: var(--tile); height: var(--tile); border-radius: 10px; }
.ui-job-skeleton .ui-skeleton-lines { display: grid; gap: var(--s-2); align-content: start; }
.ui-job-skeleton .ui-skeleton-lines > * { height: 12px; }

/* ---- Sheets and scrim: slide-over pane, filter sheet -------------------------------- */
.ui-scrim { position: fixed; inset: 0; z-index: var(--z-float); background: var(--scrim); }
.ui-sheet {
  position: fixed; z-index: var(--z-sheet); top: 0; right: 0; bottom: 0;
  width: min(var(--sheet-w), 100vw);
  background: var(--raised); border-left: 1px solid var(--line);
  box-shadow: var(--shadow-float);
  overflow: auto;
  transform: translateX(100%);
  transition: transform var(--t-base) var(--ease);
}
.ui-sheet.is-open { transform: none; }
.ui-sheet-left { right: auto; left: 0; border-left: none; border-right: 1px solid var(--line); transform: translateX(-100%); }

/* ---- Motion preferences ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) { .ui-root * { transition: none !important; animation: none !important; } }

/* ---- Bars: the one chart primitive, shared with the public site (st-chart-row) --------
   Single hue for magnitude, thin marks, direct labels. Rows ≥ 45 % of the max are full
   sea, the rest one step lighter, so the eye finds the head of the list first. */
.ui-bars { display: grid; gap: var(--s-2); margin: 0; padding: 0; list-style: none; }
.ui-bars-row { display: grid; grid-template-columns: minmax(0, 1fr) 64px; align-items: center; gap: var(--s-3); font-size: var(--text-sm); }
.ui-bars-lbl { display: grid; gap: var(--s-1); min-width: 0; }
.ui-bars-lbl > span { color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-bars-bar { height: 8px; border-radius: var(--r-xs); background: var(--sea); }
.ui-bars-bar.is-lo { background: color-mix(in srgb, var(--sea) 55%, var(--canvas)); }
.ui-bars-val { text-align: right; color: var(--ink-3); font-variant-numeric: tabular-nums; }
