/* ============================================================================
   Fired Club Jobs — design system (rebuilt from scratch)
   ----------------------------------------------------------------------------
   Modelled on the ProjectDiscovery Cloud Platform dashboard. The structure is
   the point, not the palette — the previous attempt recoloured a top-nav list
   and it still read as a generic template. What actually makes that screenshot
   look the way it does:

     1. a PERSISTENT LEFT SIDEBAR that owns navigation: product mark, context
        switcher, one prominent primary action, grouped nav with right-aligned
        COUNT BADGES, rules between groups, and account items pinned to the
        bottom. The active item is an INVERTED pill (light fill, dark text) —
        a confident block, not a tint;
     2. a CARD GRID, not a flat page: bordered panels on a slightly darker
        ground, each with a titled head, a body, and often a full-width footer
        action;
     3. BIG LIGHT-WEIGHT NUMERALS with small caption labels — the stat row is
        the signature element and it carries the whole dashboard;
     4. ROUNDED corners (8px cards, 6px controls) and generous padding. The
        platform is not the sharp-cornered marketing site;
     5. near-MONOCHROME: colour appears only when it means something.

   Both themes are first-class. `data-theme` on <html> selects; dark is the
   default and `prefers-color-scheme` is honoured when the member has not
   chosen. Every colour below is a token — no component hardcodes one, which is
   what makes the light theme a palette swap rather than a second stylesheet.
   ========================================================================== */

/* ─────────────────────────────────────────────────────────────── tokens ── */

:root {
  /* geometry + type are theme-independent */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 20px; --s6: 24px; --s7: 32px; --s8: 48px;

  --r-xs: 4px;
  --r-sm: 6px;
  --r:    8px;
  --r-lg: 12px;
  --r-pill: 999px;

  --sans: Inter, "Noto Sans Georgian", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --geo:  "Noto Sans Georgian", Inter, system-ui, sans-serif;

  --sidebar-w: 264px;
  --sidebar-w-collapsed: 68px;
  --topbar-h: 64px;
  --content-max: 1560px;

  --ease: cubic-bezier(.4, 0, .2, 1);
}

/* ── dark (default) ───────────────────────────────────────────────────── */
:root, [data-theme="dark"] {
  color-scheme: dark;

  --page:        #0a0a0b;
  --sidebar-bg:  #0d0d0f;
  --surface:     #121214;
  --surface-2:   #1a1a1d;
  --surface-3:   #232327;

  --line:        rgba(255, 255, 255, .08);
  --line-strong: rgba(255, 255, 255, .14);

  --fg:     #fafafa;
  --fg-mid: #a1a1aa;
  --fg-dim: #71717a;
  --fg-faint: #52525b;

  /* The inverted active pill: light block, dark glyph. */
  --invert-bg: #fafafa;
  --invert-fg: #0a0a0b;

  --red:    #ef4444;
  --amber:  #f59e0b;
  --green:  #22c55e;
  --blue:   #3b82f6;
  --violet: #a855f7;

  --red-soft:   rgba(239, 68, 68, .14);
  --amber-soft: rgba(245, 158, 11, .14);
  --green-soft: rgba(34, 197, 94, .14);
  --blue-soft:  rgba(59, 130, 246, .14);

  /* Leading edge of a job row. Orange is used for nothing else, so the
     stripe reads as "a posting starts here" and not as a status. */
  --stripe:     #f97316;
  --stripe-hot: #fb923c;

  --shadow: 0 1px 2px rgba(0, 0, 0, .4);
  --hover:  rgba(255, 255, 255, .04);
}

/* ── light ────────────────────────────────────────────────────────────── */
[data-theme="light"] {
  color-scheme: light;

  --page:        #f6f6f7;
  --sidebar-bg:  #ffffff;
  --surface:     #ffffff;
  --surface-2:   #f4f4f5;
  --surface-3:   #e8e8ea;

  --line:        rgba(0, 0, 0, .09);
  --line-strong: rgba(0, 0, 0, .16);

  --fg:     #09090b;
  --fg-mid: #52525b;
  --fg-dim: #71717a;
  --fg-faint: #a1a1aa;

  --invert-bg: #18181b;
  --invert-fg: #fafafa;

  --stripe:     #ea580c;
  --stripe-hot: #c2410c;

  --red:    #dc2626;
  --amber:  #d97706;
  --green:  #16a34a;
  --blue:   #2563eb;
  --violet: #9333ea;

  --red-soft:   rgba(220, 38, 38, .10);
  --amber-soft: rgba(217, 119, 6, .10);
  --green-soft: rgba(22, 163, 74, .10);
  --blue-soft:  rgba(37, 99, 235, .10);

  --shadow: 0 1px 2px rgba(0, 0, 0, .06);
  --hover:  rgba(0, 0, 0, .04);
}

/* Respect the OS when the member has expressed no preference. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;
    --page: #f6f6f7; --sidebar-bg: #fff; --surface: #fff;
    --surface-2: #f4f4f5; --surface-3: #e8e8ea;
    --line: rgba(0,0,0,.09); --line-strong: rgba(0,0,0,.16);
    --fg: #09090b; --fg-mid: #52525b; --fg-dim: #71717a; --fg-faint: #a1a1aa;
    --invert-bg: #18181b; --invert-fg: #fafafa;
    --red: #dc2626; --amber: #d97706; --green: #16a34a; --blue: #2563eb;
    --red-soft: rgba(220,38,38,.10); --amber-soft: rgba(217,119,6,.10);
    --green-soft: rgba(22,163,74,.10); --blue-soft: rgba(37,99,235,.10);
    --stripe: #ea580c; --stripe-hot: #c2410c;
    --shadow: 0 1px 2px rgba(0,0,0,.06); --hover: rgba(0,0,0,.04);
  }
}

/* ──────────────────────────────────────────────────────────────── reset ── */

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

html { -webkit-text-size-adjust: 100%; height: 100%; }

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--page);
  color: var(--fg);
  font: 14px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

:lang(ka), [lang="ka"] { font-family: var(--geo); }

h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.25; letter-spacing: -.011em; }
h1 { font-size: 24px; letter-spacing: -.02em; }
h2 { font-size: 15px; }
h3 { font-size: 13px; }
p  { margin: 0 0 var(--s3); }

a { color: inherit; text-decoration: none; }
a:hover { color: var(--fg); }

img, svg { max-width: 100%; vertical-align: middle; }
svg { flex: none; }

hr { border: 0; border-top: 1px solid var(--line); margin: var(--s4) 0; }

/* Machine-generated values are monospace and tabular. */
.mono, time, .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: 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;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ───────────────────────────────────────────────────────── app skeleton ── */

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: 100dvh;
}
.app.is-collapsed { grid-template-columns: var(--sidebar-w-collapsed) minmax(0, 1fr); }

/* ───────────────────────────────────────────────────────────── sidebar ── */

.side {
  display: flex; flex-direction: column;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--line);
  position: sticky; top: 0; height: 100dvh;
  padding: var(--s3) 0 var(--s2);
  overflow: hidden;
}

.side-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: 0 var(--s3) var(--s3);
}
.side-mark { width: 26px; height: 26px; border-radius: var(--r-sm); flex: none; }
.side-collapse {
  margin-left: auto; width: 30px; height: 30px;
  display: grid; place-items: center;
  color: var(--fg-dim); background: none; border: 0; cursor: pointer;
  border-radius: var(--r-sm);
}
.side-collapse:hover { background: var(--hover); color: var(--fg); }

/* Context switcher — the "team" row in the reference. */
.side-switch {
  display: flex; align-items: center; gap: var(--s2);
  margin: 0 var(--s2) var(--s3);
  padding: var(--s2) var(--s2);
  border-radius: var(--r-sm);
  color: var(--fg); font-size: 13px; font-weight: 500;
  background: none; border: 0; width: calc(100% - var(--s4)); cursor: pointer;
  text-align: left;
}
.side-switch:hover { background: var(--hover); }
.side-switch .side-switch-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-switch .side-switch-ico { color: var(--fg-dim); }

/* The one primary action, bordered and full width. */
.side-cta {
  display: flex; align-items: center; gap: var(--s2);
  margin: 0 var(--s3) var(--s4);
  min-height: 38px; padding: 0 var(--s3);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--fg);
  font: 500 13px/1 var(--sans); cursor: pointer;
}
.side-cta:hover { background: var(--surface-2); border-color: var(--line-strong); }
.side-cta .side-cta-end { margin-left: auto; color: var(--fg-dim); }

.side-scroll { flex: 1 1 auto; overflow-y: auto; overflow-x: hidden; padding: 0 var(--s3); }
.side-scroll::-webkit-scrollbar { width: 6px; }
.side-scroll::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 3px; }

.nav-group { display: flex; flex-direction: column; gap: 1px; }
.nav-group + .nav-group { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }

.nav-item {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 36px; padding: 0 var(--s3);
  border-radius: var(--r-sm);
  color: var(--fg-mid); font-size: 13.5px; font-weight: 450;
  white-space: nowrap;
}
.nav-item:hover { background: var(--hover); color: var(--fg); }
.nav-item .nav-ico { width: 17px; height: 17px; flex: none; opacity: .85; }
.nav-item .nav-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.nav-item .nav-count {
  flex: none; font-family: var(--mono); font-size: 11px;
  color: var(--fg-dim); font-variant-numeric: tabular-nums;
}
/* Active: an inverted block, which is what reads as "a tool" in the reference. */
.nav-item.is-active {
  background: var(--invert-bg); color: var(--invert-fg); font-weight: 550;
}
.nav-item.is-active .nav-ico { opacity: 1; }
.nav-item.is-active .nav-count { color: var(--invert-fg); opacity: .65; }

.side-foot {
  padding: var(--s3) var(--s3) 0;
  margin-top: var(--s2);
  border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 1px;
}

/* Collapsed: icons only. */
.is-collapsed .side-head { justify-content: center; padding-inline: 0; }
.is-collapsed .side-collapse,
.is-collapsed .side-switch,
.is-collapsed .nav-label,
.is-collapsed .nav-count,
.is-collapsed .side-cta-label,
.is-collapsed .side-cta-end { display: none; }
.is-collapsed .side-cta { justify-content: center; padding: 0; margin-inline: var(--s2); }
.is-collapsed .nav-item { justify-content: center; padding: 0; }
.is-collapsed .side-scroll { padding-inline: var(--s2); }

/* ──────────────────────────────────────────────────────── main column ── */

.main { display: flex; flex-direction: column; min-width: 0; }

.topbar {
  display: flex; align-items: center; gap: var(--s3);
  min-height: var(--topbar-h);
  padding: var(--s4) var(--s7);
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--page) 90%, transparent);
  backdrop-filter: blur(8px);
}
.topbar h1 { min-width: 0; overflow-wrap: anywhere; }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--s2); flex: none; }

.content {
  padding: 0 var(--s7) var(--s8);
  max-width: var(--content-max); width: 100%;
  /* The chat dock is fixed over the bottom of every signed-in page, so whatever
     ends a page -- the Snake d-pad and its legend, the last row of the feed --
     sat underneath it with nothing left to scroll. Reserve the dock button's
     height (44px) plus its own bottom offset. */
  padding-bottom: calc(var(--s8) + 56px + var(--s4) + env(safe-area-inset-bottom));
}

/* Burger shows only when the sidebar becomes a drawer. */
.side-open { display: none; }

/* The scrim sits between .side and .main in the DOM, so on desktop it was an
   in-flow grid item occupying column 2 -- which pushed .main onto row 2, below
   a 100dvh sidebar, and left the whole content area blank off-screen. It only
   has a job when the sidebar is a drawer, so it is display:none until then. */
.side-scrim { display: none; }

/* ─────────────────────────────────────────────────────────────── cards ── */

.grid { display: grid; gap: var(--s4); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1200px) {
  .grid-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  /* Main + narrower rail, as in the reference. */
  .grid-rail { grid-template-columns: minmax(0, 1fr) 420px; align-items: start; }
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  min-width: 0;
}
.card-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s4) var(--s5);
  min-width: 0;
}
.card-head h2 { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.card-head .card-end { margin-left: auto; display: flex; align-items: center; gap: var(--s2); flex: none; }
.card-body { padding: 0 var(--s5) var(--s5); }
.card-body.is-flush { padding: 0; }
.card-foot { padding: var(--s3) var(--s5) var(--s5); }
.card-divide { border-top: 1px solid var(--line); }

/* A full-width, quiet footer action ("See all"). */
.card-action {
  display: flex; align-items: center; justify-content: center;
  width: 100%; min-height: 38px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: none; color: var(--fg-mid); font: 500 13px/1 var(--sans); cursor: pointer;
}
.card-action:hover { background: var(--hover); color: var(--fg); }

/* ───────────────────────────────────────────────────────── stat row ───── */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: var(--s4); }
.stat { min-width: 0; }
.stat-num {
  font-family: var(--mono); font-size: 30px; line-height: 1.1; font-weight: 300;
  letter-spacing: -.02em; color: var(--fg);
}
.stat-label { margin-top: var(--s1); font-size: 12px; color: var(--fg-dim); overflow-wrap: anywhere; }
.stat.is-red .stat-num { color: var(--red); }
.stat.is-amber .stat-num { color: var(--amber); }
.stat.is-green .stat-num { color: var(--green); }
.stat.is-blue .stat-num { color: var(--blue); }
@media (min-width: 700px) { .stat-num { font-size: 36px; } }

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

.banner {
  display: flex; align-items: center; gap: var(--s5); flex-wrap: wrap;
  padding: var(--s5) var(--s6);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
}
.banner-text { flex: 1 1 320px; min-width: 0; }
.banner-text p { margin: var(--s1) 0 0; color: var(--fg-mid); font-size: 13px; }

/* ──────────────────────────────────────────────────────────── controls ── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 36px; padding: 0 var(--s4);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--fg);
  font: 500 13px/1 var(--sans); cursor: pointer; white-space: nowrap; flex: 0 0 auto;
}
.btn:hover { background: var(--surface-2); color: var(--fg); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }

/* The high-contrast CTA — "Start discovery" in the reference. */
.btn-solid { background: var(--invert-bg); border-color: var(--invert-bg); color: var(--invert-fg); }
.btn-solid:hover { background: var(--invert-bg); color: var(--invert-fg); opacity: .88; }

.btn-quiet { background: none; border-color: transparent; color: var(--fg-mid); }
.btn-quiet:hover { background: var(--hover); color: var(--fg); }
.btn-danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 40%, transparent); background: none; }
.btn-danger:hover { background: var(--red-soft); }
.btn-sm { min-height: 30px; padding: 0 var(--s3); font-size: 12.5px; }
.btn-icon { width: 36px; min-height: 36px; padding: 0; }

@media (pointer: coarse) {
  .btn { min-height: 44px; font-size: 14px; }
  .btn-sm { min-height: 38px; }
  .btn-icon { width: 44px; }
}

.row-actions { display: flex; flex-wrap: wrap; gap: var(--s2); min-width: 0; }

/* ───────────────────────────────────────────────────────────── inputs ── */

label { display: block; margin-bottom: var(--s2); font-size: 12.5px; color: var(--fg-mid); font-weight: 500; }

input[type=text], input[type=email], input[type=number], input[type=search],
input[type=password], select, textarea {
  width: 100%; min-height: 38px; padding: var(--s2) var(--s3);
  color: var(--fg); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  /* 16px or iOS Safari zooms the viewport on focus. */
  font: 16px/1.4 var(--sans);
}
@media (min-width: 760px) {
  input[type=text], input[type=email], input[type=number], input[type=search],
  input[type=password], select, textarea { font-size: 13.5px; }
}
input::placeholder, textarea::placeholder { color: var(--fg-faint); }
input:hover, select:hover, textarea:hover { border-color: var(--line-strong); }
textarea { min-height: 92px; resize: vertical; }

select {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--fg-dim) 50%),
    linear-gradient(135deg, var(--fg-dim) 50%, transparent 50%);
  background-position: right 14px center, right 9px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: var(--s7);
}

.field { margin-bottom: var(--s4); min-width: 0; }
.field-row { display: flex; gap: var(--s2); align-items: flex-end; min-width: 0; flex-wrap: wrap; }
.field-row > * { min-width: 0; }
.field-grow { flex: 1 1 140px; }

.check { display: flex; align-items: center; gap: var(--s2); min-height: 32px; }
.check input[type=checkbox] { width: 15px; height: 15px; min-height: 0; accent-color: var(--blue); flex: none; }
.check label { margin: 0; font-size: 13px; color: var(--fg); font-weight: 400; }
@media (pointer: coarse) {
  .check { min-height: 44px; }
  .check input[type=checkbox] { width: 20px; height: 20px; }
}

/* A search field with a leading icon, as pinned at the bottom of the reference. */
.search { position: relative; min-width: 0; }
.search .search-ico { position: absolute; left: var(--s3); top: 50%; transform: translateY(-50%); color: var(--fg-dim); pointer-events: none; }
.search input { padding-left: var(--s8); }

/* ───────────────────────────────────────────────────── pills & badges ── */

.pill {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 2px var(--s2); border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  font-size: 11.5px; font-weight: 500; color: var(--fg-mid);
  white-space: nowrap; flex: none;
}
.pill-red    { color: var(--red);   border-color: color-mix(in srgb, var(--red) 35%, transparent);   background: var(--red-soft); }
.pill-amber  { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 35%, transparent); background: var(--amber-soft); }
.pill-green  { color: var(--green); border-color: color-mix(in srgb, var(--green) 35%, transparent); background: var(--green-soft); }
.pill-blue   { color: var(--blue);  border-color: color-mix(in srgb, var(--blue) 35%, transparent);  background: var(--blue-soft); }
.pill-plain  { background: var(--surface-2); border-color: transparent; }

.dot { width: 7px; height: 7px; border-radius: 50%; flex: none; display: inline-block; }
.dot-green { background: var(--green); }
.dot-amber { background: var(--amber); }
.dot-red   { background: var(--red); }
.dot-idle  { background: var(--fg-faint); }

.avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  font: 600 12px/1 var(--sans); color: #fff;
}

/* ────────────────────────────────────────────────────────────── lists ── */

.list { display: flex; flex-direction: column; }
.list-row {
  display: flex; align-items: center; gap: var(--s4);
  padding: var(--s3) var(--s5);
  border-top: 1px solid var(--line);
  min-width: 0;
}
.list-row:first-child { border-top: 0; }
.list-row:hover { background: var(--hover); }
.list-main { flex: 1 1 auto; min-width: 0; }
.list-title { font-weight: 500; overflow-wrap: anywhere; }
.list-sub {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s3);
  margin-top: 2px; font-size: 12.5px; color: var(--fg-dim); min-width: 0;
}
.list-sub > * { min-width: 0; }
.list-end { flex: none; display: flex; align-items: center; gap: var(--s3); }

/* A job row is a title, a meta line, four buttons and a disclosure. Stacked on
   a phone that is ~140px tall, and a 1px hairline between two of them is not a
   boundary anyone can find at a glance -- the feed read as one long smear. The
   stripe gives every posting one unambiguous leading edge; the stronger rule
   and the extra top padding do the rest. Only job-bearing lists opt in, so the
   leaderboard and the source table keep their quiet hairlines. */
.list.is-jobs > .list-row:not(.load-more) {
  border-left: 3px solid var(--stripe);
  border-top: 1px solid var(--line-strong);
  padding-left: calc(var(--s5) - 3px);
}
.list.is-jobs > .list-row:not(.load-more):first-child { border-top: 0; }
.list.is-jobs > .list-row:not(.load-more):hover { border-left-color: var(--stripe-hot); }

/* On a phone the score, the remote pill and the meter claimed ~160px of a
   310px row, folding "Technical Account Manager, EMEA" over four lines. Let
   the row wrap instead: the title gets the full width and the score sits under
   it. Rows get taller, which the stripe above makes affordable. */
@media (max-width: 599px) {
  .list.is-jobs > .list-row:not(.load-more) { flex-wrap: wrap; row-gap: var(--s2); }
  .list.is-jobs > .list-row:not(.load-more) > .list-main { flex: 1 1 100%; }
  .list.is-jobs > .list-row:not(.load-more) > .list-end { margin-left: auto; }
}

/* ────────────────────────────────────────────────────────────── table ── */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.data { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data th, table.data td {
  padding: var(--s3) var(--s4); text-align: left; white-space: nowrap;
  border-top: 1px solid var(--line);
}
table.data th {
  font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--fg-dim); border-top: 0;
}
table.data tbody tr:hover { background: var(--hover); }
table.data td.wrap { white-space: normal; min-width: 200px; }

/* ──────────────────────────────────────────────────────────── meter ──── */

.meter { height: 6px; border-radius: var(--r-pill); background: var(--surface-3); overflow: hidden; }
.meter-fill { height: 100%; border-radius: var(--r-pill); background: var(--blue); }
.meter-fill.is-high { background: var(--green); }
.meter-fill.is-mid  { background: var(--amber); }
.meter-fill.is-low  { background: var(--fg-faint); }

.score { display: inline-flex; align-items: center; gap: var(--s2); flex: none; }
.score .meter { width: 48px; }
.score-num { font-family: var(--mono); font-size: 12px; color: var(--fg-mid); min-width: 3ch; text-align: right; }

/* ───────────────────────────────────────────────────────────── empty ──── */

.empty { padding: var(--s8) var(--s5); text-align: center; color: var(--fg-dim); font-size: 13px; }

.alert {
  padding: var(--s3) var(--s4); border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); font-size: 13px; margin-bottom: var(--s4);
  background: var(--surface);
}
.alert-error { border-color: color-mix(in srgb, var(--red) 40%, transparent); background: var(--red-soft); }
.alert-ok    { border-color: color-mix(in srgb, var(--green) 40%, transparent); background: var(--green-soft); }
.alert-info  { border-color: color-mix(in srgb, var(--blue) 40%, transparent); background: var(--blue-soft); }

/* ────────────────────────────────────────────────── responsive drawer ── */

@media (max-width: 1023px) {
  .app, .app.is-collapsed { grid-template-columns: minmax(0, 1fr); }
  .side {
    position: fixed; inset: 0 auto 0 0; z-index: 60;
    width: min(290px, 86vw);
    transform: translateX(-102%);
    transition: transform .2s var(--ease);
    box-shadow: 0 0 40px rgba(0, 0, 0, .5);
  }
  .app.is-open .side { transform: none; }
  .side-open { display: inline-flex; }
  .app.is-open .side-scrim {
    display: block;
    position: fixed; inset: 0; z-index: 55;
    background: rgba(0, 0, 0, .5); border: 0;
  }
  .topbar { padding: var(--s3) var(--s4); }
  .content { padding: 0 var(--s4) var(--s8); }
  /* The drawer always shows labels, never the collapsed rail. */
  .is-collapsed .nav-label, .is-collapsed .nav-count,
  .is-collapsed .side-switch, .is-collapsed .side-cta-label { display: initial; }
  .is-collapsed .nav-item, .is-collapsed .side-cta { justify-content: flex-start; padding: 0 var(--s3); }
}

@media print {
  .side, .topbar-actions, .side-scrim { display: none !important; }
  .app { display: block; }
}

/* ─────────────────────────────────────────── shell details & auth shell ── */

.side-wordmark {
  font: 600 13px/1 var(--sans); letter-spacing: .02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.is-collapsed .side-wordmark { display: none; }

/* The logout form must not introduce a layout box between the nav items. */
.nav-form { display: contents; }
.nav-item { border: 0; background: none; width: 100%; font-family: inherit; cursor: pointer; text-align: left; }

.nav-lang { display: grid; place-items: center; font-size: 10.5px; font-weight: 600; letter-spacing: .04em; }

/* Show whichever icon represents the theme you would switch TO. */
.theme-ico-light { display: none; }
[data-theme="light"] .theme-ico-light { display: block; }
[data-theme="light"] .theme-ico-dark  { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .theme-ico-light { display: block; }
  :root:not([data-theme]) .theme-ico-dark  { display: none; }
}

/* ── pre-login ─────────────────────────────────────────────────────────── */

.auth-body {
  display: grid;
  /* minmax(0, 1fr), NOT the default `auto`. An auto column is sized to its
     max-content, and the panel's inputs have an intrinsic width of ~500px --
     so on a 375px phone the column overflowed the viewport, and centering the
     352px panel inside that 500px column pushed it to x=75 and clipped it off
     the right edge. Capping the column at the container width fixes it at the
     cause; the panel's own max-width then does what it looks like it does. */
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  align-content: center;
  min-height: 100dvh;
  padding: var(--s5);
}
/* Inputs default to ~20 characters wide and refuse to shrink below it unless
   told; this is what made the column overflow in the first place. */
.auth-panel input, .auth-panel select, .auth-panel textarea { min-width: 0; }
.auth-stage { width: 100%; max-width: 352px; display: flex; flex-direction: column; align-items: center; gap: var(--s6); }

.auth-lockup { display: flex; align-items: center; gap: var(--s3); color: var(--fg); }
.auth-word { font: 600 15px/1 var(--sans); letter-spacing: .04em; }

.auth-panel {
  width: 100%;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow);
}
.auth-panel-head {
  padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--line);
  font-size: 13px; font-weight: 600;
}
.auth-panel-body { padding: var(--s5); }
.auth-submit { width: 100%; margin-top: var(--s2); }

.auth-theme { position: fixed; top: var(--s4); right: var(--s4); }

/* ══════════════════════════════════════════════════════════════════════════
   APPENDED — workspace pages (matches / saved / applications / profile /
   admin). Only what the core system genuinely does not already express: a
   score breakdown, a kanban board, and four layout helpers. Everything else
   on those pages is .card / .list / .stats / table.data as defined above.
   No new colours: every value here is one of the tokens.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── small helpers ───────────────────────────────────────────────────────── */

.stack { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
.muted { color: var(--fg-dim); }

/* A label beside wrapping content. Georgian runs ~30% longer than English, so
   these two rules are the difference between a tidy row and one letter per
   line -- the bug the previous nav shipped with. */
.nowrap { white-space: nowrap; flex: 0 0 auto; }

.kv { display: flex; gap: var(--s3); align-items: baseline; min-width: 0; font-size: 13px; }
.kv-k { flex: 0 0 auto; white-space: nowrap; color: var(--fg-dim); }
.kv-v { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }

/* Auto-fitting columns that can never be wider than their container, so a
   375px viewport collapses them to one column instead of scrolling. */
.form-grid,
.checks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: 0 var(--s4);
  min-width: 0;
}
.checks { grid-template-columns: repeat(auto-fit, minmax(min(168px, 100%), 1fr)); }

/* A one-time secret the member has to read and retype: code, not prose. */
.code {
  display: inline-block;
  font-family: var(--mono); font-size: 17px; letter-spacing: .12em;
  padding: var(--s3) var(--s4);
  background: var(--surface-2);
  border: 1px dashed var(--line-strong); border-radius: var(--r-sm);
  overflow-wrap: anywhere; max-width: 100%;
}

/* ── score breakdown ─────────────────────────────────────────────────────── */

/* A match row carries its breakdown underneath, inside the same row, so the
   divider still spans the full row. */
.list-row.is-stacked { flex-wrap: wrap; row-gap: var(--s2); }
.list-row.is-stacked > .bd { flex: 1 0 100%; }

.bd > summary {
  display: inline-flex; align-items: center; gap: var(--s1);
  min-height: 30px; font-size: 12px; color: var(--fg-dim);
  cursor: pointer; list-style: none;
}
.bd > summary::-webkit-details-marker { display: none; }
.bd > summary:hover { color: var(--fg); }
.bd[open] > summary { color: var(--fg); }

/* label / detail / number -- the number column stays aligned down the table. */
.bd-body {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr) max-content;
  gap: var(--s1) var(--s3);
  padding: var(--s2) 0 var(--s1);
  font-size: 12.5px;
}
.bd-label { color: var(--fg-mid); overflow-wrap: anywhere; }
.bd-detail { color: var(--fg-dim); overflow-wrap: anywhere; }
.bd-num { font-family: var(--mono); font-variant-numeric: tabular-nums; text-align: right; }
.bd-num.is-pos { color: var(--green); }
.bd-num.is-neg { color: var(--red); }

/* ── kanban ──────────────────────────────────────────────────────────────── */

/* The board scrolls inside itself; the page never scrolls sideways. */
.kanban {
  display: flex; gap: var(--s3);
  padding: 0 var(--s5) var(--s5);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  align-items: start;
}
.kan-col {
  flex: 0 0 min(252px, 76vw);
  display: flex; flex-direction: column; min-width: 0;
  background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r);
  scroll-snap-align: start;
}
.kan-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3); border-bottom: 1px solid var(--line); min-width: 0;
}
.kan-name {
  flex: 1 1 auto; min-width: 0;
  font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--fg-mid);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kan-count { flex: 0 0 auto; font-family: var(--mono); font-size: 11px; color: var(--fg-dim); }

.kan-cards {
  display: flex; flex-direction: column; gap: var(--s2);
  padding: var(--s2); min-height: 76px; flex: 1 1 auto;
  border-radius: 0 0 var(--r) var(--r);
}
.kan-cards.is-over { background: var(--blue-soft); }

.kan-card {
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: var(--s3); min-width: 0;
}
.kan-card:hover { border-color: var(--line-strong); }
.kan-card.is-dragging { opacity: .4; }
.kan-title { display: block; font-size: 13px; font-weight: 500; overflow-wrap: anywhere; }
.kan-meta { margin-top: 2px; font-size: 12px; color: var(--fg-dim); overflow-wrap: anywhere; }

/* The touch fallback: a real <select> in a real <form>. HTML5 drag events never
   fire on touch, so this is the only way a phone can move a card -- and it
   keeps working with JavaScript switched off. */
.kan-move {
  display: flex; align-items: center; gap: var(--s2);
  margin-top: var(--s3); min-width: 0;
}
.kan-move form { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.kan-move form:first-child { flex: 1 1 auto; }
.kan-move select { flex: 1 1 auto; min-width: 0; min-height: 38px; padding-block: var(--s1); }
.kan-move .btn { flex: 0 0 auto; }
@media (min-width: 760px) { .kan-move select { min-height: 32px; } }
@media (pointer: coarse) { .kan-move select { min-height: 44px; } }

.kan-empty { padding: var(--s4) var(--s2); text-align: center; color: var(--fg-faint); font-size: 12px; }

/* ============================================================================
   APPENDED — PRE-LOGIN SURFACE
   ----------------------------------------------------------------------------
   The door (/welcome, /login, /join, /auth/password) is one panel on an empty
   stage. Nothing here invents a colour or a second layout: it is the .auth-*
   skeleton already in the system plus the three things it was missing -- a
   fluid cap instead of a slab, touch sizing, and the notch.

   Width is capped with max-width and never set with a fixed `width`, so a
   375px phone shows the same panel narrower rather than scrolling sideways.
   ========================================================================== */

.auth-body {
  padding: calc(var(--s5) + env(safe-area-inset-top)) var(--s4)
           calc(var(--s5) + env(safe-area-inset-bottom));
}
.auth-stage { max-width: 352px; }
.auth-panel { width: 100%; }

/* The lockup is the only "branding"; no tagline, no marketing line. */
.auth-lockup { justify-content: center; }

/* Under the panel: the language toggle, and nothing else. */
.auth-foot {
  display: flex; align-items: center; justify-content: center; gap: var(--s3);
  width: 100%; font-size: 12.5px; color: var(--fg-dim);
}
.auth-foot a { color: var(--fg-mid); }
.auth-foot a:hover { color: var(--fg); }

/* The notch, and a toggle that stays reachable under it. */
.auth-theme {
  top: calc(var(--s4) + env(safe-area-inset-top));
  right: var(--s4);
}

@media (pointer: coarse) {
  .auth-submit { min-height: 48px; }
  .auth-panel-body input { min-height: 48px; }
  .auth-theme { min-height: 44px; }
}

/* ============================================================================
   APPENDED — CHAT, ACTIVITY, COFFEE BREAK
   ----------------------------------------------------------------------------
   Three surfaces, one vocabulary: everything below is a .card with a list in
   it. The parts that genuinely are not expressible with the core classes are
   the scrolling message log, the reaction chips, the docked panel, and the
   game's canvas/d-pad. No component hardcodes a colour.

   Georgian runs ~30% longer than English, so every flex row that holds text
   carries `min-width: 0` and every label that must not break carries
   `white-space: nowrap; flex: 0 0 auto`.
   ========================================================================== */

/* ── chat page ───────────────────────────────────────────────────────────── */

.chat { display: grid; gap: var(--s4); grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 1000px) { .chat { grid-template-columns: 236px minmax(0, 1fr); } }

.room {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 42px; padding: 0 var(--s4);
  border-top: 1px solid var(--line);
  color: var(--fg-mid); font-size: 13px; min-width: 0;
}
.room:first-child { border-top: 0; }
.room:hover { background: var(--hover); color: var(--fg); }
.room.is-active { background: var(--surface-2); color: var(--fg); font-weight: 550; }
.room-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.room-count {
  flex: 0 0 auto; white-space: nowrap;
  font-family: var(--mono); font-size: 11px; color: var(--blue);
}

/* The log owns the scroll, so the page itself never grows with the history. */
.chat-pane { display: flex; flex-direction: column; height: min(72dvh, 720px); }
.chat-log {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column;
  padding: var(--s3) 0;
  margin: 0; list-style: none;
}

.msg { display: flex; align-items: flex-start; gap: var(--s3); padding: var(--s2) var(--s5); min-width: 0; }
.msg:hover { background: var(--hover); }
.msg-main { flex: 1 1 auto; min-width: 0; }
.msg-head { display: flex; align-items: baseline; gap: var(--s2); min-width: 0; }
.msg-who {
  font-size: 13px; font-weight: 550; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msg-time { flex: 0 0 auto; white-space: nowrap; font-size: 11px; color: var(--fg-faint); }
.msg-text { font-size: 13.5px; overflow-wrap: anywhere; white-space: pre-wrap; }
.msg-gone { font-size: 13px; color: var(--fg-faint); font-style: italic; }
.msg-tools { flex: 0 0 auto; display: flex; gap: var(--s1); align-self: flex-start; }

/* The avatar is an emoji on a colour; no uploads, no image pipeline. */
.avatar-emoji { font-size: 15px; line-height: 1; }

/* ── reactions (chat and activity share the chip) ────────────────────────── */

.rx { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1); margin-top: var(--s1); min-width: 0; }
.rx-btn {
  display: inline-flex; align-items: center; gap: var(--s1);
  min-height: 26px; padding: 0 var(--s2);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--fg-mid);
  font: 12px/1 var(--sans); cursor: pointer; flex: 0 0 auto;
}
.rx-btn:hover { border-color: var(--line-strong); color: var(--fg); }
.rx-btn.is-mine { border-color: color-mix(in srgb, var(--blue) 45%, transparent); background: var(--blue-soft); color: var(--blue); }
.rx-n { font-family: var(--mono); font-size: 11px; font-variant-numeric: tabular-nums; }
.rx-add { opacity: .6; }
.rx-add:hover { opacity: 1; }
@media (pointer: coarse) { .rx-btn { min-height: 44px; padding: 0 var(--s3); } }

/* ── composer ────────────────────────────────────────────────────────────── */

.chat-form {
  display: flex; align-items: flex-end; gap: var(--s2);
  padding: var(--s3) var(--s4) calc(var(--s3) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line); min-width: 0;
}
.chat-form textarea {
  flex: 1 1 auto; min-width: 0; min-height: 40px; max-height: 120px; resize: none;
}
.chat-form .btn { flex: 0 0 auto; }

.chat-quick { display: flex; flex-wrap: wrap; gap: var(--s1); padding: 0 var(--s4) var(--s2); min-width: 0; }

/* ── the docked panel ────────────────────────────────────────────────────── */

/* Why a dock at all: this is a server-rendered MPA, so "chat that follows you
   between pages" has to live outside the swapped content region. The panel is
   mounted by chat.js on every authed page, remembers whether it was open, in
   which room, and what you had half-typed, and restores all three -- so moving
   between pages keeps the conversation where you left it. */
.dock {
  position: fixed; z-index: 50;
  right: var(--s4); bottom: calc(var(--s4) + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: flex-end; gap: var(--s2);
  max-width: calc(100vw - var(--s7));
}
.dock-btn {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 44px; padding: 0 var(--s4);
  border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  background: var(--surface); color: var(--fg);
  font: 500 13px/1 var(--sans); cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .28);
}
.dock-btn:hover { background: var(--surface-2); }
.dock-badge {
  min-width: 20px; padding: 0 5px; border-radius: var(--r-pill);
  background: var(--blue); color: #fff;
  font-family: var(--mono); font-size: 11px; line-height: 19px; text-align: center;
  flex: 0 0 auto;
}
.dock-panel {
  display: flex; flex-direction: column;
  width: min(340px, calc(100vw - var(--s7)));
  height: min(60dvh, 460px);
  background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  box-shadow: 0 8px 32px rgba(0, 0, 0, .34);
  overflow: hidden;
}
.dock-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); min-width: 0;
}
.dock-title {
  flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dock-panel .chat-log { padding: var(--s2) 0; }
.dock-panel .msg { padding-inline: var(--s3); }
.dock-panel .chat-form { padding-inline: var(--s3); }
.dock[hidden] { display: none; }

@media (max-width: 599px) {
  /* On a phone the dock is a round chat bubble in the bottom-right corner,
     tail pointing into the corner the way a messaging app draws it. Full
     width cost a strip of every page and read as a toolbar rather than as
     chat; a 56px circle is the size a thumb expects and gives the content
     back its width. The panel keeps its own rectangular shape when open. */
  .dock { left: auto; right: var(--s4); align-items: flex-end; max-width: calc(100vw - var(--s6)); }
  .dock-panel { width: min(340px, calc(100vw - var(--s6))); height: min(68dvh, 520px); }
  .dock-btn {
    position: relative;
    width: 56px; height: 56px; padding: 0;
    justify-content: center;
    /* Three round corners and one drawn-in corner: the bubble's tail. */
    border-radius: 50% 50% var(--s2) 50%;
    box-shadow: 0 6px 20px rgba(0, 0, 0, .34);
  }
  /* The word "chat" is what made it a toolbar. The bubble says it already. */
  .dock-label { display: none; }
  .dock-badge {
    position: absolute; top: -3px; right: -3px;
    border: 2px solid var(--page);
  }
}

/* ── activity feed ───────────────────────────────────────────────────────── */

.post { padding: var(--s4) var(--s5); border-top: 1px solid var(--line); min-width: 0; }
.post:first-child { border-top: 0; }
.post-head { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.post-who { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 550; overflow-wrap: anywhere; }
.post-when { flex: 0 0 auto; white-space: nowrap; font-size: 11px; color: var(--fg-faint); }
.post-text { margin-top: var(--s2); font-size: 13.5px; overflow-wrap: anywhere; white-space: pre-wrap; }
.post-act {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s2);
  margin-top: var(--s2); font-size: 13px; color: var(--fg-mid); min-width: 0;
}
.post-verb { flex: 0 0 auto; white-space: nowrap; color: var(--fg-dim); }
.post-job { min-width: 0; overflow-wrap: anywhere; color: var(--fg); font-weight: 500; }
.post-tools { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s2); flex-wrap: wrap; }

.cmt-list { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s3); }
.cmt { display: flex; align-items: flex-start; gap: var(--s2); min-width: 0; font-size: 13px; }
.cmt-main { flex: 1 1 auto; min-width: 0; }
.cmt-who { font-weight: 550; margin-right: var(--s2); }
.cmt-text { overflow-wrap: anywhere; white-space: pre-wrap; }
.cmt-form { display: flex; gap: var(--s2); align-items: flex-end; margin-top: var(--s3); min-width: 0; }
.cmt-form textarea { flex: 1 1 auto; min-width: 0; min-height: 38px; max-height: 110px; resize: none; }
.cmt-form .btn { flex: 0 0 auto; }

.composer { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; }
.composer-end { display: flex; align-items: center; justify-content: flex-end; gap: var(--s2); }

/* ── coffee break ────────────────────────────────────────────────────────── */

.game { display: flex; flex-direction: column; align-items: center; gap: var(--s4); padding: var(--s4) var(--s5) var(--s5); }

.game-stage { position: relative; width: 100%; max-width: 480px; }
/* touch-action so a swipe steers the snake instead of scrolling the page. */
.game-canvas {
  display: block; width: 100%; aspect-ratio: 1 / 1;
  background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r);
  touch-action: none;
}
.game-over {
  position: absolute; inset: 0;
  display: grid; place-items: center; gap: var(--s2);
  background: color-mix(in srgb, var(--page) 72%, transparent);
  border-radius: var(--r);
  text-align: center; padding: var(--s4);
}
.game-over-title { color: var(--red); font-size: 22px; font-weight: 600; letter-spacing: -.01em; overflow-wrap: anywhere; }
.game-over[hidden] { display: none; }

.game-hud { display: flex; flex-wrap: wrap; gap: var(--s4) var(--s6); width: 100%; max-width: 480px; min-width: 0; }
.hud-item { min-width: 0; }
.hud-v { font-family: var(--mono); font-size: 26px; font-weight: 300; line-height: 1.1; letter-spacing: -.02em; }
.hud-k { margin-top: var(--s1); font-size: 12px; color: var(--fg-dim); overflow-wrap: anywhere; }

.game-bar { display: flex; flex-wrap: wrap; gap: var(--s2); width: 100%; max-width: 480px; }

.legend { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); font-size: 12px; color: var(--fg-dim); max-width: 480px; }
.legend-item { display: inline-flex; align-items: center; gap: var(--s2); min-width: 0; }
/* Swatches, not emoji: these have to agree with what the canvas paints, and a
   platform emoji font does not. */
.legend-dot { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.legend-dot.is-team { border-radius: 50%; background: var(--blue); }
.legend-dot.is-job { background: var(--stripe); }
.legend-dot.is-bonus { background: var(--amber); }

/* The on-screen pad. Shown on touch pointers; a swipe works too, but a pad is
   the only control that works one-handed without covering the board. */
.pad {
  display: none;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s2); width: 100%; max-width: 252px;
}
.pad-btn {
  display: grid; place-items: center;
  min-height: 52px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--fg);
  cursor: pointer; touch-action: manipulation;
}
.pad-btn:active { background: var(--surface-3); }
.pad-mid { grid-column: 2; }
.pad-sp { visibility: hidden; }
/* The d-pad and the chat dock both want the bottom of the screen. Reserving
   bottom padding was not enough: the dock is fixed, so at *some* scroll offset
   it covered the controls anyway -- which is what kept happening on a phone.
   So on a touch device the pad stops scrolling. It is pinned to the bottom,
   the dock is lifted to sit directly above it, and the page reserves the
   height of both. One token drives all three so they cannot drift apart. */
@media (pointer: coarse) {
  /* The pad's full outer height: two rows of .pad-btn, the gap between them,
     and the panel's own padding and border. Everything that has to clear the
     pad reads this one value -- get it wrong and the dock lands 6px low, which
     is exactly what happened when the panel was added. On :root because a
     property set on .pad itself is invisible to .dock and .content. */
  :root { --pad-h: calc(52px * 2 + var(--s2) + var(--s2) * 2 + 2px); }

  .pad {
    display: grid;
    position: fixed; z-index: 45;
    left: 50%; transform: translateX(-50%);
    bottom: calc(var(--s3) + env(safe-area-inset-bottom));
    width: min(252px, calc(100vw - var(--s6)));
    /* Once the pad floats, the leaderboard scrolls through the gaps between
       its buttons. A panel behind it makes the pad read as one control
       surface instead of three buttons lost over someone else's card. */
    padding: var(--s2);
    background: color-mix(in srgb, var(--page) 88%, transparent);
    backdrop-filter: blur(8px);
    border: 1px solid var(--line);
    border-radius: var(--r);
  }
  /* :has() scopes both of these to the one page that has a pad. */
  body:has(.pad) .dock {
    bottom: calc(var(--s3) + var(--pad-h) + var(--s3) + env(safe-area-inset-bottom));
  }
  body:has(.pad) .content {
    padding-bottom: calc(var(--s8) + var(--pad-h) + 56px + var(--s5) + env(safe-area-inset-bottom));
  }
}

.lb-you { display: flex; align-items: baseline; gap: var(--s3); padding: var(--s3) var(--s5); min-width: 0; }
.lb-num { font-family: var(--mono); font-size: 22px; font-weight: 300; }
.lb-rank { flex: 0 0 auto; white-space: nowrap; font-family: var(--mono); font-size: 12px; color: var(--fg-dim); min-width: 2ch; }

/* A card with no head still needs padding on all four sides. */
.card-pad { padding: var(--s5); }

/* A card that opens: the head is the toggle, without the browser's marker. */
summary.card-head { cursor: pointer; list-style: none; }
summary.card-head::-webkit-details-marker { display: none; }
summary.card-head:hover { background: var(--hover); border-radius: var(--r) var(--r) 0 0; }

/* ── late additions to the block above ───────────────────────────────────── */

/* A reading column. The shell caps at 1560px, which is right for a table and
   wrong for a paragraph. */
.narrow { max-width: 720px; }

/* The reaction palette hides behind one chip, so a message carries a single
   44px tap target instead of five. <details> rather than JavaScript: it works
   under `script-src 'self'` with nothing loaded at all. */
.rx-pick { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s1); min-width: 0; }
.rx-pick > summary { list-style: none; cursor: pointer; }
.rx-pick > summary::-webkit-details-marker { display: none; }
.rx-pick[open] > summary { background: var(--surface-3); color: var(--fg); opacity: 1; }

/* The DM picker and the leaderboard tabs both put a select beside a button. */
.card-foot .field-row { margin: 0; }

/* Narrow fixed-size inputs for the term editor (a weight, a language). Both are
   far under a 375px viewport, and .field-row wraps them when it has to. */
.field-xs { flex: 0 0 96px; width: 96px; }
.field-sm { flex: 0 0 128px; width: 128px; }

/* The threshold slider is the only range control in the app. */
input[type=range] { width: 100%; accent-color: var(--blue); min-height: 32px; }

/* ════════════════════════════════ dashboard & jobs (appended) ═══════════════
   Only what the design system did not already carry. No token is redefined and
   no colour is hard-coded, so both themes still come from :root alone. */

/* A card whose body has no head above it still needs its top padding; the base
   .card-body assumes a .card-head supplied it. */
.card-pad { padding-top: var(--s5); }

/* THE LOAD-MORE BUG. .card-action is a flex container, so its label becomes an
   anonymous flex item -- and a flex item is free to shrink below its content
   width. In a narrow parent that wrapped "Load more" into a column of single
   letters. Pin the item and forbid the break. */
.card-action { white-space: nowrap; }
.card-action > * { flex: 0 0 auto; white-space: nowrap; }

/* Load more is the list's own last row, not a button floating under it. */
.list-foot { padding: var(--s3) var(--s5) var(--s5); }

/* Filter bar. One auto-fitting grid; the search owns a full row of its own so
   it can never end up overlapping the control beside it. */
.filters {
  display: grid; gap: var(--s4) var(--s3);
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  align-items: end;
}
.filters .field { margin: 0; min-width: 0; }
.filters .filters-wide { grid-column: 1 / -1; }
/* `span 2` on a single-column grid would invent a second column, so the double
   width only exists once there is more than one column to span. */
@media (min-width: 760px) { .filters .filters-double { grid-column: span 2; } }
/* Georgian labels are long; they clip rather than reflow the grid. */
.filters label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Two figures and a currency, which must stay on one line at 375px. */
.salary-row { display: flex; gap: var(--s2); align-items: flex-end; min-width: 0; }
.salary-row .field-grow { flex: 1 1 60px; min-width: 0; }
.salary-row .salary-cur { flex: 0 0 5.5em; }

/* Rows: the meter and the pills keep their size, the text gives way. */
.job-row .list-end { gap: var(--s2); }
.job-salary { font-family: var(--mono); font-size: 12px; white-space: nowrap; }

/* A swap in flight dims the list instead of blanking it. */
#joblist.htmx-request { opacity: .5; transition: opacity .12s var(--ease); }

/* Label/value pairs on the detail page. The label never wraps, the value always
   may -- the opposite is what produces a sideways scroll in Georgian. */
.meta { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s2) var(--s4); margin: 0; font-size: 13px; }
.meta dt { color: var(--fg-dim); white-space: nowrap; }
.meta dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* Source text: its own line breaks are the only structure it has. */
.prose { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.7; color: var(--fg-mid); font-size: 13.5px; }

/* Remote OK's terms require the credit to be visible and followed. */
.credit { margin: 0; font-size: 12px; color: var(--fg-dim); }
.credit a { text-decoration: underline; }

/* The paging footer inside a list: a full-width quiet action with the divider
   above it, so "load more" reads as part of the list rather than after it. */
.list-row.load-more { padding: var(--s3) var(--s5); }

/* Filter controls are the one place a member taps repeatedly on a phone; the
   base input height is 38px, which is below the 44px touch target. */
@media (pointer: coarse) {
  .filters input, .filters select { min-height: 44px; }
}

/* ─────────────────────────────────────────────────── onboarding tour ────── */
/* One step per screen. The measure is capped at ~62 characters because the body
   text is the whole point of the page and a line that runs the full width of a
   desktop card is genuinely hard to read -- especially in Georgian, where
   Mkhedruli has no capitals to anchor the eye at the start of a line. */

.tour { max-width: 680px; margin: 0 auto; padding-top: var(--s4); }

.tour-progress {
  display: flex; gap: var(--s2);
  list-style: none; margin: 0 0 var(--s5); padding: 0;
}
.tour-tick {
  flex: 1 1 0; height: 3px; border-radius: var(--r-pill);
  background: var(--surface-3);
}
.tour-tick.is-done { background: var(--fg-dim); }
.tour-tick.is-current { background: var(--fg); }

.tour-card { display: flex; flex-direction: column; }

.tour-body { padding: var(--s7) var(--s6) var(--s6); }

.tour-icon {
  display: grid; place-items: center;
  width: 44px; height: 44px; margin-bottom: var(--s4);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  color: var(--fg);
}
.tour-count {
  margin: 0 0 var(--s2);
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--fg-dim);
}
.tour-head {
  font-size: 20px; line-height: 1.3; letter-spacing: -.015em;
  margin-bottom: var(--s3); max-width: 24ch;
}
.tour-text {
  margin: 0; max-width: 62ch;
  font-size: 15px; line-height: 1.65; color: var(--fg-mid);
}
.tour-note {
  margin: var(--s5) 0 0; padding: var(--s3) var(--s4);
  max-width: 62ch;
  border-left: 2px solid var(--line-strong); border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: var(--surface-2);
  font-size: 13.5px; line-height: 1.6; color: var(--fg-mid);
}

.tour-foot {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s4) var(--s6) var(--s6);
  border-top: 1px solid var(--line);
  margin-top: auto;
}
/* Skip sits apart from the two decisions, so it is never mis-tapped. */
.tour-foot .tour-skip { margin-left: auto; }
.tour-foot .btn-solid { min-width: 128px; }

@media (max-width: 599px) {
  .tour-body { padding: var(--s6) var(--s5) var(--s5); }
  .tour-head { font-size: 18px; }
  .tour-text { font-size: 14.5px; }
  .tour-foot { padding: var(--s4) var(--s5) var(--s5); flex-wrap: wrap; }
  /* Full-width primary on a phone; skip drops below so nothing is cramped. */
  .tour-foot .btn-solid { flex: 1 1 100%; order: -1; min-width: 0; }
  .tour-foot .tour-skip { margin-left: auto; }
}

/* ============================================================================
   APPENDED — THE SWIPE DECK ON /jobs
   ----------------------------------------------------------------------------
   A second way to read the same list: one job fills the screen, you throw it
   right to save or left to mark it not relevant. It is a MODE — the list is
   untouched and still the default on a desktop — so nothing below restyles an
   existing component; it only adds the stack, the card, and the mode switch.

   The three things that make it work on a phone:

     * the stack is capped in `dvh`, never in `px`, and the card scrolls INSIDE
       itself. A 375px viewport therefore shows the whole deck — card, buttons
       and undo — without the page scrolling, however long the posting is;
     * `touch-action: pan-y` on the stack and the card: a horizontal drag is
       ours, a vertical one still scrolls the description;
     * `order: -1` floats the deck above the filter card while it is showing, so
       the card starts at the top of the content rather than below a form.

   No colour is hard-coded and no token is redefined: green and red appear only
   where they carry the verdict, which is the one place this design system
   spends colour. Georgian is ~30% longer than English, so every flex row here
   carries `min-width: 0` and every label that must not break is nowrap.
   ========================================================================== */

/* ── the list ⇄ cards switch (top bar) ───────────────────────────────────── */

.seg {
  display: inline-flex; flex: 0 0 auto; gap: 2px; padding: 2px;
  background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.seg-btn {
  flex: 0 0 auto; min-height: 30px; padding: 0 var(--s3);
  border: 0; border-radius: var(--r-xs); background: none;
  color: var(--fg-mid); font: 500 12.5px/1 var(--sans);
  white-space: nowrap; cursor: pointer;
}
.seg-btn:hover { color: var(--fg); }
/* Pressed is the inverted block the sidebar's active item uses. */
.seg-btn[aria-pressed="true"] { background: var(--invert-bg); color: var(--invert-fg); }
@media (pointer: coarse) { .seg-btn { min-height: 40px; padding: 0 var(--s4); font-size: 13.5px; } }

/* ── the deck ────────────────────────────────────────────────────────────── */

.deck { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; order: -1; }
.deck[hidden] { display: none; }

.deck-stack {
  position: relative;
  height: clamp(340px, 56dvh, 540px);
  touch-action: pan-y;
}

.deck-end { position: absolute; inset: 0; display: grid; place-items: center; }
.deck-end[hidden] { display: none; }

/* The wrapper GET /jobs/deck answers with. swipe.js lifts the cards out of it
   and it is discarded, so it must never introduce a box of its own on the rare
   path where it does reach the document. */
.deck-batch { display: contents; }

/* ── one card ────────────────────────────────────────────────────────────── */

.deck-card {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: var(--shadow);
  touch-action: pan-y;
  cursor: grab;
  transition: transform .18s var(--ease), opacity .18s var(--ease);
  will-change: transform;
}
.deck-card[hidden] { display: none; }
/* Only two cards are ever drawn: the one you are deciding on, and the hint of
   the next one behind it. */
.deck-card.is-top  { z-index: 2; }
.deck-card.is-next { z-index: 1; transform: scale(.965) translateY(10px); }
/* While the finger is down the transform must track it exactly, with no easing
   lagging behind the drag. */
.deck-card.is-dragging { transition: none; cursor: grabbing; }
.deck-card.is-past { border-color: var(--line-strong); }
.deck-card.is-gone { opacity: 0; transition: transform .22s var(--ease), opacity .22s var(--ease); }

/* The verdict labels. swipe.js ramps the opacity with the drag; past the
   threshold they are at full strength and the colour is the answer. */
.deck-stamp {
  position: absolute; top: var(--s4); z-index: 3;
  max-width: 45%; padding: var(--s1) var(--s3);
  border: 1px solid currentColor; border-radius: var(--r-sm);
  font: 600 12px/1.6 var(--sans); letter-spacing: .06em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; pointer-events: none;
}
.deck-stamp-save { right: var(--s4); color: var(--green); background: var(--green-soft); }
.deck-stamp-skip { left:  var(--s4); color: var(--red);   background: var(--red-soft); }

/* The body owns the scroll, so the page never grows with the description. */
.deck-scroll {
  flex: 1 1 auto; min-height: 0; min-width: 0;
  display: flex; flex-direction: column; gap: var(--s3);
  padding: var(--s5);
  overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
}

.deck-title {
  font-size: clamp(17px, 4.4vw, 21px); font-weight: 600; line-height: 1.3;
  letter-spacing: -.015em; color: var(--fg); overflow-wrap: anywhere;
}
.deck-title:hover { text-decoration: underline; }

.deck-who { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3); min-width: 0; font-size: 13.5px; }
.deck-company { min-width: 0; color: var(--fg); font-weight: 500; overflow-wrap: anywhere; }
.deck-place { min-width: 0; color: var(--fg-dim); overflow-wrap: anywhere; }

.deck-pills { display: flex; flex-wrap: wrap; gap: var(--s2); min-width: 0; }

/* The reason a posting is out of reach, plus the phrase that decided it. */
.deck-why { margin: 0; font-size: 12.5px; color: var(--amber); overflow-wrap: anywhere; }

.deck-facts { gap: var(--s2) var(--s4); font-size: 13px; }
.deck-text { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--fg-mid); overflow-wrap: anywhere; }

/* ── the buttons under the deck ──────────────────────────────────────────── */

/* Not a fallback. A gesture-only UI is unreachable with a keyboard or a screen
   reader, so these do exactly what the swipe does — and they sit at opposite
   ends of the row, with the detail link between them, so the one that trains
   the matcher against you cannot be hit by mistake. */
.deck-bar { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.deck-act { flex: 1 1 0; min-width: 0; min-height: 48px; padding: 0 var(--s2); font-size: 12.5px; }
.deck-act > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.deck-bar .btn-icon { flex: 0 0 auto; width: 48px; padding: 0; }

.deck-save { color: var(--green); border-color: color-mix(in srgb, var(--green) 40%, transparent); }
.deck-save:hover { background: var(--green-soft); color: var(--green); }
.deck-skip { color: var(--red); border-color: color-mix(in srgb, var(--red) 40%, transparent); }
.deck-skip:hover { background: var(--red-soft); color: var(--red); }

.deck-foot { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.deck-left {
  flex: 0 0 auto; margin-left: auto;
  display: inline-flex; align-items: baseline; gap: var(--s2);
  font-size: 12px; color: var(--fg-dim);
}
.deck-left .num { font-size: 15px; color: var(--fg-mid); }
.deck-error { margin: 0; }
.deck-error[hidden] { display: none; }

/* On anything wider than a phone the deck stops stretching and centres: a card
   the width of a desktop column is not a card. */
@media (min-width: 760px) {
  .deck { max-width: 520px; margin-inline: auto; }
  .deck-stack { height: clamp(420px, 62dvh, 620px); }
  .deck-act { font-size: 13px; padding: 0 var(--s3); }
}

/* ── deck action labels on a narrow phone ────────────────────────────────── */
/* "არარელევანტური" is 14 characters; with three controls on a 375px row it
   truncated to "არარელევანტუ…", which reads as broken rather than terse.
   Below 420px the buttons go icon-only -- the aria-label still names them, so
   nothing is lost to a screen reader -- and the label returns when it fits. */
@media (max-width: 419px) {
  .deck-bar .deck-act-label { display: none; }
  .deck-bar .deck-act { flex: 1 1 0; min-width: 0; padding: 0; }
  .deck-bar .btn-icon { flex: 0 0 auto; }
}
@media (min-width: 420px) {
  .deck-bar .deck-act-label {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
}

/* ── the dedicated swipe page (/deck) ────────────────────────────────────── */
/* One deck, nothing competing with it. The filters are a collapsible panel
   rather than a permanent column: the card needs the height more than the page
   needs a visible filter form, and on a phone a side column is not available
   anyway. Opening the panel pushes the deck down rather than overlaying it, so
   nothing is ever hidden behind a sheet. */

.deckpage {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  max-width: 560px;
  margin: 0 auto;
}
.deckpage-filters[hidden] { display: none; }

/* On the standalone page the deck is the page: it takes the height it needs
   and never reorders around a list that is not there. */
.deck.is-standalone {
  order: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.deck.is-standalone .deck-stack {
  /* Taller than the in-grid variant, because here it owns the viewport. */
  height: clamp(380px, 62dvh, 620px);
}

@media (min-width: 900px) {
  .deckpage { max-width: 600px; }
  .deck.is-standalone .deck-stack { height: clamp(420px, 60dvh, 640px); }
}

/* ── reaction picker with a wider palette ────────────────────────────────── */
/* Sixteen emoji in a row would overflow a phone, so the picker is a grid that
   wraps. It is a <details> popover, positioned above the chip row so it never
   pushes the message list around when it opens. */
.rx-pick[open] > .rx-btn,
.rx-pick > summary { position: relative; z-index: 2; }

.rx-pick[open]::after { content: none; }

.rx-pick[open] > button {
  display: inline-flex;
}
.rx-pick[open] {
  position: relative;
}
/* The palette itself: a wrapping grid, not a single row. */
.rx-pick[open] > button,
.rx-pick > button {
  flex: 0 0 auto;
}
.rx-pick[open] {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--s1);
  max-width: min(272px, 78vw);
  padding: var(--s2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .28);
}

/* ═════════════════════════════════════════════════ the job filter panel ════ */
/* "ფილტრაცია არ მომწონს" -- the dropdowns are gone. What replaces them is a
   panel of checkbox GROUPS, every option in sight at once, four of which take
   several values (location, role, source, work mode), plus a row of chips that
   says what is applied and takes one value back off.
     * no Apply button: HTMX swaps the result region on every change, so the
       panel never holds a decision the page has not acted on;
     * the panel is a side column from 1200px up, where there is room for it
       beside the list, and a collapsible card below that;
     * at 375px there is one column of boxes, 44px of tap target each, and the
       long location list keeps eight boxes out in the open with the tail in a
       <details> -- a checkbox in a shut <details> still submits, so nothing is
       lost by closing it.
   Georgian labels run ~30% longer than English, so every label wraps inside
   its own cell rather than widening the grid. */

/* ── the panel ───────────────────────────────────────────────────────────── */

.fpanel { min-width: 0; }
.fpanel-body { padding-bottom: var(--s5); }

/* The groups themselves: one auto-fitting column set that collapses to a
   single column on a phone instead of scrolling sideways. */
.fgroups {
  display: grid; gap: var(--s5) var(--s5);
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  align-items: start; min-width: 0;
}
.fgroups .field { margin: 0; min-width: 0; }
.fgroup-wide { grid-column: 1 / -1; }

/* A <fieldset> because that is what a group of related checkboxes is; the UA
   border and padding are not wanted, the <legend> is the group's label. */
.fgroup { min-width: 0; margin: 0; padding: 0; border: 0; }
.fgroup > legend {
  padding: 0; margin-bottom: var(--s2);
  font-size: 12.5px; font-weight: 500; color: var(--fg-mid);
}

.fopts {
  display: grid; gap: var(--s1) var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
  min-width: 0;
}
/* A two-line Georgian label must align with the top of its box, not float. */
.fopts .check { align-items: flex-start; }
.fopts .check input { margin-top: 3px; }
.fopts .check label { overflow-wrap: anywhere; }
.fopts .check input[type=radio] {
  width: 15px; height: 15px; min-height: 0; accent-color: var(--blue); flex: none;
}

/* How many live rows an option would leave. Metadata, so it never competes
   with the label it belongs to. */
.fcount { color: var(--fg-dim); font-family: var(--mono); font-size: 11px; }

/* The tail of the location list. */
.fmore { margin-top: var(--s2); }
.fmore > summary {
  display: inline-flex; align-items: center; min-height: 32px;
  font-size: 12.5px; color: var(--fg-mid); cursor: pointer; list-style: none;
}
.fmore > summary::-webkit-details-marker { display: none; }
.fmore > summary:hover { color: var(--fg); }
.fmore[open] > summary { margin-bottom: var(--s1); }

@media (pointer: coarse) {
  .fgroups input, .fgroups select { min-height: 44px; }
  .fopts .check input { min-height: 0; }
  .fmore > summary { min-height: 44px; }
}

/* The disclosure that only exists while the panel is a card. From 1200px the
   panel is a column of its own and there is nothing to collapse. */
@media (min-width: 1200px) {
  .fpanel-toggle { display: none; }
  .fpanel-body[hidden] { display: block; }
}

/* ── the two-column page ─────────────────────────────────────────────────── */

#jobsview, #matchesview { align-items: start; }
@media (min-width: 1200px) {
  #jobsview, #matchesview { grid-template-columns: 320px minmax(0, 1fr); }
  /* The list is long; the filters stay put while it scrolls past them, and
     scroll on their own only if the window is genuinely too short. */
  #jobsview > .fpanel,
  #matchesview > .fpanel {
    position: sticky; top: var(--s4);
    max-height: calc(100dvh - var(--topbar-h) - var(--s6)); overflow: auto;
  }
}

/* The result region: chips, then the card. One element, so a filter change
   replaces all of it in a single response. */
#joblist,
#matchlist { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }

/* ── applied-filter chips ────────────────────────────────────────────────── */
/* One per value, each with an × that removes just that one. They are real
   links to the same page minus that value, so they work with JavaScript off. */

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); min-width: 0; }

.chip {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 32px; max-width: 100%;
  padding: var(--s1) var(--s3);
  border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  background: var(--surface); color: var(--fg);
  font-size: 12.5px; text-decoration: none;
}
.chip:hover { background: var(--hover); border-color: var(--fg-dim); }
.chip svg { color: var(--fg-dim); flex: none; }
.chip:hover svg { color: var(--fg); }
.chip-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-clear { border-style: dashed; color: var(--fg-mid); }

@media (pointer: coarse) {
  .chip { min-height: 44px; font-size: 14px; }
}

/* On /deck the chips are the whole result region: with the panel shut they are
   the only thing that says what the stack is filtered to. */
.chipbar { min-width: 0; }
/* Nothing applied, nothing to show -- and no gap above the stack either. */
.chipbar:not(:has(.chips)) { display: none; }

/* ============================================================================
   ONBOARDING SPOTLIGHT (static/js/tour.js)
   ----------------------------------------------------------------------------
   The tour no longer has a page of its own: it dims whichever real page the
   member is on and cuts a hole around the actual element the step is about.

   The hole is four panels, not a clip-path or a giant box-shadow. Each panel is
   a fixed rectangle laid around the target's box, which buys two things a
   single full-screen element cannot:

     * the target is NOT covered, so it stays clickable and keeps its own hover
       and focus styles -- the member can press the thing being pointed at;
     * everything else IS covered by a panel that swallows the click, so a
       stray tap cannot navigate out of the middle of the tour.

   Nothing here transitions except the layer's own fade-in. The panels and the
   ring are re-measured on every scroll and resize, and a transition on their
   geometry would make them lag a finger-flick behind the element they frame.

   Positioning is JavaScript's job (viewport-aware, and it never covers its own
   target); this block only has to make the pieces look right in both themes.
   ========================================================================== */

.tour-layer {
  position: fixed; inset: 0; z-index: 90;
  pointer-events: none;           /* only the panels and the tooltip take input */
  opacity: 0;
  transition: opacity .16s var(--ease);
}
.tour-layer.is-on { opacity: 1; }

/* One of the four rectangles that surround the hole. */
.tour-mask {
  position: fixed;
  background: rgba(0, 0, 0, .58);
  pointer-events: auto;
}

/* The hole's edge. --blue rather than --fg: over a dimmed page a near-black
   ring is invisible in the light theme and a near-white one glares in the
   dark, while the accent reads as "look here" in both. */
.tour-ring {
  position: fixed;
  border: 2px solid var(--blue);
  border-radius: var(--r);
  box-shadow: 0 0 0 4px var(--blue-soft);
  pointer-events: none;
}

.tour-tip {
  position: fixed;
  /* Georgian runs about a third longer than English, so the tooltip is sized
     for the Georgian and never wider than a 375px screen minus its gutters. */
  width: min(340px, calc(100vw - 32px));
  max-height: calc(100dvh - 24px);
  overflow: auto;
  padding: var(--s4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .38);
  pointer-events: auto;
}
.tour-tip:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

.tour-tip-count {
  margin: 0 0 var(--s2);
  color: var(--fg-dim);
  font-size: 11.5px; letter-spacing: .02em;
}
.tour-tip-text {
  margin: 0;
  color: var(--fg);
  font-size: 14.5px; line-height: 1.55;
}
.tour-tip-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
  margin-top: var(--s4);
}
/* Skip is always reachable and always on the far side from the primary action,
   so finishing the tour and leaving it are never adjacent targets. */
.tour-tip-skip { margin-right: auto; }

/* .btn sets display:inline-flex, which beats the user agent's [hidden] rule --
   so the Back button on the first step has to be hidden explicitly. */
.tour-tip .btn[hidden] { display: none; }

/* ── job detail: one action row ──────────────────────────────────────────── */
/* The source link and the save/applied/dismiss group are one row. The group is
   its own element because HTMX swaps it, so `display: contents` lets its
   buttons sit in the same flex line as the link instead of forming a nested
   box with its own wrapping. */
.detail-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
}
.detail-actions > #job-actions { display: contents; }
/* The primary action keeps its own breathing room from the rest. */
.detail-actions > .btn-solid { margin-right: var(--s2); }

@media (max-width: 479px) {
  /* On a phone the source link takes the full width and the three secondary
     actions wrap underneath it, so the main action is never a small target
     squeezed between two others. */
  .detail-actions > .btn-solid { flex: 1 1 100%; margin-right: 0; }
}

/* ═══════════════════════════════════════════════════════════ CV builder ══ */
/* The editor is a long form of repeating blocks, and the repeating block is
   the whole design problem: on a 375px phone a job is five inputs plus up to
   twelve bullet textareas, and without a visible container they read as one
   undifferentiated column of boxes. So every repeating row is a tinted,
   bordered block with its own caption and its own remove button -- the same
   move the card grid makes one level up.

   There is deliberately NO sticky save bar. The chat dock is fixed over the
   bottom of every signed-in page and already won that fight once (the Snake
   d-pad); the topbar's Save submits the form from outside it via
   `form="cv-form"`, which needs no JavaScript and no second fixed element. */

.cv-hint {
  margin: var(--s1) 0 0;
  font-size: 12px; line-height: 1.5; color: var(--fg-dim);
  overflow-wrap: anywhere;
}
.cv-hint a { color: var(--fg-mid); text-decoration: underline; }
.cv-hint a:hover { color: var(--fg); }

/* ── one repeating row: a job, a degree, a language, a certification ────── */
.cv-rep {
  position: relative;
  padding: var(--s4);
  margin-bottom: var(--s4);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  min-width: 0;
}
.cv-rep > .field:last-child { margin-bottom: 0; }
.cv-rep-head {
  display: flex; align-items: center; gap: var(--s2);
  margin-bottom: var(--s3);
  min-width: 0;
}
.cv-rep-n {
  flex: 1 1 auto; min-width: 0;
  font: 500 12px/1 var(--mono); letter-spacing: .04em; text-transform: uppercase;
  color: var(--fg-dim); overflow-wrap: anywhere;
}
/* An input inside a block is on the tinted ground, so it needs the plain
   surface back or the two tones merge into one grey slab. */
.cv-rep input[type=text], .cv-rep input[type=email], .cv-rep textarea {
  background: var(--surface);
}

/* ── a bullet, or a link: one control plus a remove button ──────────────── */
.cv-bullet {
  display: flex; align-items: flex-start; gap: var(--s2);
  margin-bottom: var(--s2); min-width: 0;
}
.cv-bullet > input, .cv-bullet > textarea { flex: 1 1 auto; min-width: 0; }
.cv-bullet > textarea { min-height: 72px; }
.cv-bullet > .btn { margin-top: 1px; }

/* ── a link: a type, a value, a remove button ───────────────────────────────
   The select sizes to its own words rather than to a share of the row, so the
   value -- which is the long part -- gets the rest. On a phone the two
   controls wrap onto their own lines and the remove button stays beside the
   value, where a thumb expects it. */
.cv-link {
  display: flex; align-items: center; gap: var(--s2);
  margin-bottom: var(--s2); min-width: 0; flex-wrap: wrap;
}
.cv-link > select { flex: 0 0 auto; width: auto; min-width: 7.5rem; }
.cv-link > input { flex: 1 1 12rem; min-width: 0; }

/* The add button is quiet and full-width on a phone: it is the control a
   member reaches for repeatedly, and a 36px pill at the end of a wrapping row
   is the one they keep missing. */
.cv-add { margin-top: var(--s2); }
.cv-add > .btn { border-style: dashed; color: var(--fg-mid); }
.cv-add > .btn:hover { color: var(--fg); border-style: solid; }
@media (max-width: 599px) {
  .cv-add > .btn { width: 100%; }
}

/* ── skills: many short values, so they tile rather than stack ──────────── */
.cv-skills {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: var(--s2) var(--s4);
  min-width: 0;
}
.cv-skill { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.cv-skill > input { flex: 1 1 auto; min-width: 0; }

.cv-foot .row-actions { align-items: center; }
@media (max-width: 479px) {
  /* Save is the primary action and takes the full width; the PDF link and the
     way out wrap underneath it. */
  .cv-foot .row-actions > .btn-solid { flex: 1 1 100%; }
}

.cv-rename { padding: var(--s2) 0 var(--s1); }

/* ── the empty state and the design gallery ─────────────────────────────── */
.cv-hero { max-width: 56ch; }
.cv-hero h2 { margin: 0 0 var(--s3); }
.cv-hero p { margin: 0 0 var(--s3); font-size: 13.5px; line-height: 1.65; color: var(--fg-mid); }

@media (min-width: 900px) {
  .cv-gal { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
}
/* An A4 page shape reserved up front: the previews are ~1000px wide PNGs and
   without this the gallery reflowed three times as they decoded. */
.cv-shot {
  display: block; margin-top: var(--s4);
  aspect-ratio: 210 / 297;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
.cv-shot img { display: block; width: 100%; height: auto; }
.cv-shot:hover { border-color: var(--line-strong); }

/* The topbar is a burger, an <h1> and the page's actions, and the <h1> is the
   thing that gives: two buttons beside "Your CVs" on a 375px phone folded the
   heading to one letter per line (Georgian, which runs ~30% longer, folded it
   further). The secondary action drops out below 600px -- it is reachable from
   the body copy and from the sidebar, where it belongs on a phone anyway. */
@media (max-width: 599px) {
  .cv-wide { display: none; }
}

/* ── the colour scheme: a row of swatches, each one a radio ─────────────────
   The swatch shows the three colours a reader actually sees on the page --
   the dark ground, the accent and the soft tint -- as three bands in one
   chip. The fourth (muted text) is not worth a stripe: it is grey in every
   scheme by construction.

   The colours themselves are inline `style` attributes, because they come out
   of the palette table in Python rather than out of the design system, and the
   same is true of the avatar colours elsewhere in this file. */
.cv-palette { margin-top: var(--s4); }
.cv-swatches {
  display: grid;
  gap: var(--s1) var(--s4);
  /* 190px, not 150: the Georgian names are long ("მუქი ლურჯი და
     ნარინჯისფერი") and a narrower column folded them to three lines. */
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  min-width: 0;
}
.cv-swatches-wide { margin-top: var(--s3); }
.cv-swatch {
  display: flex; align-items: center; gap: var(--s2);
  min-height: 34px; min-width: 0;
  font-size: 13px; color: var(--fg); cursor: pointer;
}
.cv-swatch input[type=radio] {
  width: 15px; height: 15px; min-height: 0; accent-color: var(--blue); flex: none;
}
.cv-swatch-ink {
  display: flex; flex: none;
  width: 42px; height: 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.cv-swatch-band { display: block; flex: 1 1 0; min-width: 0; }
/* The same chip at pill size, for the CV list. */
.cv-swatch-tiny { width: 18px; height: 9px; border-radius: 2px; vertical-align: -1px; }
.cv-swatch-name { min-width: 0; overflow-wrap: anywhere; }
@media (pointer: coarse) {
  .cv-swatch { min-height: 44px; }
  .cv-swatch input[type=radio] { width: 20px; height: 20px; }
}

/* What a design trades, when it trades something. Loud enough to read before
   the preview, quiet enough not to look like an error. */
.cv-caveat {
  margin: var(--s2) 0 0;
  padding-left: var(--s3);
  border-left: 2px solid var(--amber);
  font-size: 13px; line-height: 1.6; color: var(--fg-mid);
}
/* Which scheme the picture above was rendered in. */
.cv-shotnote { margin: var(--s2) 0 0; font-size: 12.5px; color: var(--fg-dim); }
