/* ═══════════════════════════════════════════════════════════════
   Kithby: Main Stylesheet

   DESIGN SYSTEM RULES (keep these or the site drifts back to chaos):
   1. Type: only the 7 --text-* steps. Never invent a font-size.
   2. Spacing: only the --space-* steps. Never invent a margin/padding.
   3. Depth: a surface gets a hairline border AND a resting shadow. This
      reverses the old "never both" rule. Hairline-only read as flat: every
      card was white-on-white held apart by a line, and the page had no
      z-axis. The shadows below are three-part (contact, diffusion, ambient)
      and carry a lit top edge, which is what makes a surface read as an
      object under a light rather than a rectangle. Floating things use
      --shadow-lg and still sit above everything else.
   4. Radius: --radius everywhere; --radius-lg only for floating panels.

   Default theme = INK & KITH. See themes.css for the two alternates.
   Legacy var names (--teal/--amber/--cream) are retained on purpose so
   existing inline styles inherit new values instead of breaking.
═══════════════════════════════════════════════════════════════ */

:root {
  /* ── INK & KITH (default) ── cool near-black, paper, one live green ── */
  --ink:        #14241C;   /* primary text: near-black, biased green */
  --ink-dark:   #0B1611;
  --ink-mid:    #2B3A33;   /* body copy */
  /* Meta text has to clear 4.5 on BOTH grounds it appears on, and the
     stone one is the harder of the two. #6A7A70 passed on white at 4.54
     and failed on stone at 3.84, which is the trap in having a real
     ground at last: a colour tuned against white is not tuned. */
  /* Tuned against three grounds, not two. The old #5E6C62 cleared 4.5 on the
     stone ground and on white, but it is also used on --cream-dark, the
     recessed fill, where it measured 3.94 to 4.25 depending on theme — under
     AA in five of seven. This value clears 4.5 on all three in every theme;
     the worst pairing is now 4.62. */
  --ink-light:  #556158;   /* 4.62:1 worst case across every theme and ground */

  --cream:      #FAFAF9;   /* near-white: light text on dark, subtle hovers */

  /* The page canvas, and its own token rather than --cream, because --cream is
     also the light TEXT colour on dark buttons and the row-hover fill. The two
     jobs pulled in opposite directions and the canvas lost: at #FAFAF9 it sat
     1.5% from the #FFFFFF card surface, which is no step at all. Every card was
     white on white, held apart by a hairline, and the whole page read as one
     flat field with lines drawn on it. A real ground means a white surface is
     an object without needing a heavier border or a bigger shadow to say so.
     Every theme sets its own: see themes.css. */
  --ground:     #EAEDE9;   /* page canvas: stone, tinted toward the ink */

  /* The colour of "the main thing you can press". It is ink by default, which
     is the restrained choice and reads well. It is a TOKEN because that is the
     actual question the accent studies exist to answer: an accent that appears
     on one badge and a link hover has no surface area, so varying it produces
     no visible difference however good the colour is. A study that moves the
     primary action to the accent is testing something real. Ink Only keeps ink
     here, which is precisely that study's argument. */
  /* The LIGHT brass, not the dark one. --amber is tuned to be readable AS TEXT
     on the stone ground, so it is dark; ink text on top of it measured 2.9:1,
     well under the 4.5 floor. --amber-light is the fill, and ink on it clears
     6.4:1. The two brasses are for opposite jobs and using the wrong one is an
     easy mistake to make twice. */
  --action:     var(--amber-light);
  --action-ink: var(--ink);
  --action-hover: #B8903E;
  --cream-dark: #DEE3DD;   /* recessed fill: sponsor, selected states */
  --white:      #FFFFFF;   /* card surface */
  --border:     #D3D9D2;   /* hairline */
  --line:       #D3D9D2;   /* alias, preferred name going forward */

  /* Primary (dark), used for header, primary buttons, brand panels */
  --teal:       #14241C;
  --teal-dark:  #0B1611;
  --teal-light: #2E4437;
  /* --teal as *text on a light surface*: identical to --teal in every
     theme where the page is light, so this changes nothing by default.
     It only earns its keep in a theme where the canvas itself goes dark
     and --teal stays reserved for structural surfaces (header, buttons),
     the two roles need different values there, or teal-on-teal text goes
     invisible. See the product theme override in themes.css. */
  --teal-ink:   var(--teal);

  /* Accent, kith green: the one "live" colour, doing double duty as the
     mark's presence dot and any active/highlighted UI state. */
  /* Two values, one hue, and an explicit rule about which ground each is for.
     --amber is the accent on PAPER and must clear 4.5:1 there; the old
     #2FBF71 managed 2.09:1, which is what made every section eyebrow the
     least readable text on the site. --amber-light is the accent on INK,
     where #4FD98C already measured 9.84:1 and is unchanged.
     This is a legibility fix inside the existing green, not a brand decision
     — the four candidate directions live in themes.css. */
  --amber:      #8A6420;   /* brass on the stone ground — 4.9:1 */
  --amber-light:#C8A14E;   /* brass on ink — 6.7:1 */

  --red:        #8C2F2F;
  --green:      #3B6B4A;
  /* --amber is the brand accent (green, see above), so a genuine amber/gold
     gets its own name here, only for the middle step of a red/amber/green
     scale (mobility score, etc.) where three distinct hues actually matter. */
  --caution:      #B8863D;
  --caution-wash: #F7F1E7;

  /* Status washes, all derived from the palette, no stray Material hues */
  --teal-wash:   #ECECED;
  /* Lightened from #EFE9DA: --amber on it measured 4.42, just under AA, and
     this wash exists specifically to carry --amber text. */
  --amber-wash:  #F4EFE5;  /* brass wash */

  /* The colour of --amber-coloured TEXT when it sits on --amber-wash. It is
     --amber on every light theme, which is what it always was. A dark theme
     needs its own value: there, the wash is near-black and --amber is a mid
     tone, so no wash adjustment can open the gap — the text has to come up.
     See the product override in themes.css. */
  --amber-on-wash: var(--amber);
  --green-wash:  #EDF2EE;
  --red-wash:    #F7EDEC;
  --plum:        #5C4A5E;
  --plum-wash:   #F2EEF2;

  /* 4px read as an un-rounded box at card size — present enough to notice,
     too small to look intentional. 10 is a decision. Inputs and buttons
     inherit it and are the reason it is not larger still. */
  --radius:     10px;
  --radius-sm:  6px;
  --radius-lg:  16px;
  /* Genuine pills only: badges and the floating widgets. Not a 4th step on
     the structural scale, which stays 3 / 6 / 12. Circles use 50%. */
  --radius-pill: 999px;

  /* Shadows, three-part: a hairline contact shadow, a mid diffusion and a wide
     ambient one. A single blurred drop shadow is what makes a UI look
     generated; real depth has a near term and a far term. Tinted toward the
     ink rather than pure black, so shadows sit in the palette. */
  --shadow:     0 1px 1px rgba(18,26,21,.04), 0 3px 6px -2px rgba(18,26,21,.05), 0 12px 28px -12px rgba(18,26,21,.10);
  --shadow-md:  0 1px 1px rgba(18,26,21,.04), 0 8px 16px -6px rgba(18,26,21,.09), 0 28px 56px -20px rgba(18,26,21,.18);
  --shadow-lg:  0 2px 4px rgba(18,26,21,.06), 0 18px 36px -12px rgba(18,26,21,.16), 0 48px 88px -32px rgba(18,26,21,.28);

  /* The top edge that makes a light surface read as lit instead of flat. Goes
     alongside a shadow, never instead of one. */
  --hairline:   inset 0 1px 0 rgba(255,255,255,.9);

  /* ── Spacing scale (4px base) ── */
  --space-1: .25rem;   /* 4  */
  --space-2: .5rem;    /* 8  */
  --space-3: .75rem;   /* 12 */
  --space-4: 1rem;     /* 16 */
  --space-5: 1.5rem;   /* 24 */
  --space-6: 2rem;     /* 32 */
  --space-7: 3rem;     /* 48 */
  --space-8: 4rem;     /* 64 */

  /* ── Type scale: 7 steps, nothing between ── */
  --text-xs:   .75rem;    /* 12, badges, micro labels */
  --text-sm:   .875rem;   /* 14, meta, captions */
  --text-base: 1rem;      /* 16, body / UI default */
  --text-md:   1.0625rem; /* 17, emphasized body, card titles */
  --text-lg:   1.3125rem; /* 21, h3 */
  --text-xl:   1.75rem;   /* 28, h2 */
  --text-2xl:  2.5rem;    /* 40, h1 */

  /* ── Fonts ──
     Sans for display, deliberately. A light display serif at hero size reads
     thin and awkward, and every comparable site (Wealthfront, Chase, Amegy,
     CaringBridge) uses a sans headline. The serif option lives in the
     'editorial' theme for anyone who wants it. Instrument Sans carries the
     brand's own character at display size; Inter stays underneath it for
     UI, where a quieter face is correct. */
  --font-display: 'Archivo', 'Instrument Sans', system-ui, -apple-system, sans-serif;
  --font-ui:      'Inter', system-ui, -apple-system, sans-serif;
  /* A member's own words: post bodies and profile bios. Kithby speaks in the
     sans; the serif is kept for people speaking for themselves, so
     "human-written" is something you can see on the page, not just a rule.
     Messages stay in --font-ui: quick back-and-forth reads better there. */
  --font-voice:   'Newsreader', Georgia, 'Times New Roman', serif;
  --display-weight: 700;
  /* Barely-negative tracking. Anything tighter makes large headings feel
     cramped and hard to read. */
  --display-spacing: -.025em;
}

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

html { font-size: 16px; scroll-behavior: smooth; }

/* Film grain at ~3% across the whole page, so flat fills and gradients have
   tooth rather than banding. Fixed, pointer-events off, below every control.
   The single cheapest thing that stops a surface looking machine-made. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .03;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) { body::after { display: none; } }

body {
  font-family: var(--font-ui);
  font-size: var(--text-base);
  background: var(--ground);
  color: var(--ink);
  line-height: 1.65;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* Guards against sub-pixel overflow from full-bleed sections.
     'clip' rather than 'hidden', hidden turns body into a scroll container
     and breaks window scrolling; clip does not. */
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: var(--ink); text-decoration: none; }
a:hover { color: var(--amber); }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-spacing);
  color: var(--ink);
  line-height: 1.25;
  text-wrap: balance;
}
/* In-app heading scale. Marketing pages opt into larger sizes via their own
   classes (.lp-h1/.lp-h2, .page-hero h1) rather than inflating these. */
h1 { font-size: var(--text-xl); }
h2 { font-size: var(--text-lg); }
h3 { font-size: var(--text-md); }
h4 { font-size: var(--text-base); font-weight: 600; }

/* ── Container ── */
.container { max-width: 1440px; margin: 0 auto; padding: 0 1.25rem; }
/* Reclaim space on large displays rather than stranding it in the margins */
@media (min-width: 1700px) {
  .container { max-width: 1560px; }
}

/* ── Page header ── one pattern for every in-app page title, so pages stop
   inventing their own h1 size. Use: <div class="page-head"><h1>…</h1>
   <p class="page-sub">…</p></div> */
.page-head { margin-bottom: var(--space-6); }
.page-head h1 {
  font-size: var(--text-xl);
  margin-bottom: var(--space-2);
}
.page-sub {
  color: var(--ink-light);
  font-size: var(--text-base);
  line-height: 1.65;
  max-width: 62ch;
}

/* ── Demo Bar ── */
.demo-bar {
  background: #1a1a2e;
  color: #e0e0e0;
  padding: .5rem 1rem;
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: var(--text-sm);
  flex-wrap: wrap;
  position: sticky;
  top: 0;
  z-index: 1000;
}
.demo-label { font-weight: 700; color: #f0c040; margin-right: .25rem; }
.demo-role-btn {
  padding: .2rem .65rem;
  border-radius: var(--radius-pill);
  border: 1px solid #444;
  color: #ccc;
  font-size: var(--text-xs);
  transition: all .2s;
}
.demo-role-btn:hover, .demo-role-btn.active {
  background: var(--amber);
  border-color: var(--amber);
  color: #fff;
}

/* ── Site Header ── */
.site-header {
  background: var(--teal);
  border-bottom: 1px solid rgba(255,255,255,.08);
  position: sticky;
  top: 0;
  z-index: 900;
}
.demo-bar ~ .site-header { top: 38px; }

.header-inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  height: 68px;
}

.site-logo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: #fff;
  flex-shrink: 0;
  padding-right: var(--space-4);   /* clear space: half the 32px badge */
}
/* Size scale: 24 compact, 32 header, 40 auth, 64 share cards. Nothing
   between. Clear space is half the badge height on the open side — the
   search field used to abut the lockup with nothing reserved. */
.logo-icon { width: 40px; height: 40px; flex-shrink: 0; }

/* Kith Path mark: a route between four known people, wandered rather than
   drawn straight, with a short trail (not a bare dot) tracing it forever.
   Self-contained badge, so it reads the same regardless of what it sits on. */
/* Literal values, never tokens. A brand mark must not change with a theme:
   wired to --ink-dark and --white, the badge inverted to a glaring white tile
   with near-black dots under the Modern Dark theme, and the live dot turned
   indigo under one theme and navy under another — a colour a visitor could
   change from the public theme switcher.
   The face is #0E0F13 rather than #000000 so the badge sits IN the #16181D
   header instead of reading as a darker tile floating on it. */
.kith-face  { fill: #0E0F13; }
.kith-frame { fill: none; stroke: rgba(255,255,255,.10); stroke-width: 1; }
/* Without a badge framing it, the route carries the mark on its own, so it
   takes a little more presence than it needed sitting inside a tile. */
.kith-route { fill: none; stroke: #FFFFFF; stroke-opacity: .72; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.kith-node  { fill: #FFFFFF; }
.kith-live      { fill: #2FBF71; }   /* 9.8:1 on the badge face */
.kith-live-rest { fill: #2FBF71; display: none; }
/* SMIL (animateMotion) cannot be paused from CSS, so a reduced-motion
   preference swaps the travelling trail for the resting dot rather than
   leaving the mark with nothing on it. */
@media (prefers-reduced-motion: reduce) {
  .kith-live, .cs-logomark-live { display: none; }
  .kith-live-rest { display: inline; }
}
.site-logo-text {
  display: flex;
  flex-direction: column;
  gap: 0;
  line-height: 1;
}
.site-name {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-lg);
  letter-spacing: .01em;
  color: #fff;
  white-space: nowrap;
}
.site-tagline {
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(255,255,255,.55);
  margin-top: .2rem;
  white-space: nowrap;
}
@media (max-width: 640px) { .site-tagline { display: none; } }

.header-search {
  flex: 1;
  display: flex;
  max-width: 360px;
  position: relative;
}
.header-search input {
  flex: 1;
  /* Without this, the input keeps its intrinsic placeholder width no matter
     how narrow the form gets — a flex item defaults to min-width:auto. That
     is what pushed 192px of input out of a 90px form on phones and printed
     the placeholder straight through the nav links. */
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: var(--radius);
  font-size: var(--text-base);
  background: rgba(255,255,255,.06);
  color: #fff;
  font-family: inherit;
  transition: background .15s, border-color .15s;
}
.header-search input::placeholder { color: rgba(255,255,255,.38); }
.header-search input:focus {
  outline: none;
  border-color: rgba(255,255,255,.30);
  background: rgba(255,255,255,.11);
}
/* Search submit sits inside the field as a quiet glyph, not a loud block */
.header-search button {
  position: absolute;
  right: 1px; top: 1px; bottom: 1px;
  width: 34px;
  background: none;
  color: rgba(255,255,255,.45);
  border: none;
  border-radius: 0 var(--radius) var(--radius) 0;
  cursor: pointer;
  font-size: var(--text-md);
  transition: color .15s;
}
.header-search button:hover { background: none; color: #fff; }
.header-search button { display: flex; align-items: center; justify-content: center; }
.header-search button .icon { width: 16px; height: 16px; vertical-align: 0; }

.header-nav {
  display: flex;
  align-items: center;
  gap: .25rem;
  margin-left: auto;
  flex-shrink: 0;
}
.nav-link {
  padding: .4rem .75rem;
  border-radius: var(--radius);
  font-size: var(--text-base);
  font-weight: 600;
  color: rgba(255,255,255,.7);
  transition: all .18s;
  position: relative;
}
.nav-link:hover, .nav-link.active { color: #fff; background: rgba(255,255,255,.12); }
.admin-link { color: var(--amber) !important; }
.logout-link { color: rgba(255,255,255,.4); font-weight: 400; }
.logout-link:hover { color: #fff; background: none; }
.nav-avatar { display: inline-flex; align-items: center; border-radius: 50%; flex-shrink: 0; }
.nav-avatar:hover { opacity: .85; }

/* ── Account dropdown (Profile / Career survey / Security / Sign out) ──
   Same floating-panel treatment as #theme-panel and #mention-dropdown:
   hairline border + shadow, never both with a heavier structural border. */
.nav-account { position: relative; flex-shrink: 0; }
.nav-account-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 210px;
  background: var(--white);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: .4rem;
  z-index: 10000;
  opacity: 0;
  transform: translateY(-6px) scale(.98);
  pointer-events: none;
  transition: opacity .15s ease, transform .15s ease;
}
.nav-account-menu.is-open { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
  .nav-account-menu { transition: none; }
}
.nav-account-menu a {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .55rem .65rem;
  border-radius: var(--radius-sm);
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--ink);
  transition: background .12s;
}
.nav-account-menu a:hover { background: var(--cream-dark); color: var(--ink); }
.nav-account-menu a.logout-link { color: var(--red); font-weight: 500; }
.nav-account-menu a.logout-link:hover { background: var(--red-wash); color: var(--red); }
.nav-account-menu-divider { height: 1px; background: var(--line); margin: .35rem .25rem; }

.badge-count {
  background: var(--amber);
  color: #fff;
  font-size: var(--text-xs);
  padding: 1px 5px;
  border-radius: var(--radius-pill);
  margin-left: 3px;
  vertical-align: top;
}

.avatar-circle {
  width: 34px; height: 34px;
  background: linear-gradient(135deg, var(--teal), var(--teal-light));
  box-shadow: inset 0 -2px 4px rgba(0,0,0,.12);
  color: #fff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1rem;
}

/* ── Header, small screens ──────────────────────────────────────────────
   Logo + search + up to 8 nav items were laid out with flex-wrap:nowrap
   and .header-nav pinned to flex-shrink:0, so below ~984px of combined
   natural width neither the search box nor the nav could give up any
   space, and the flex row forced the entire page wider than the device
   instead of adapting, the classic "phone shows a zoomed-out desktop
   site" bug. Letting the nav scroll internally (rather than push its
   parent wide) and shrinking the search box fixes that without a new
   menu component to get wrong under time pressure. Placed after the
   base header rules on purpose: same specificity, so source order is
   what decides, and a media query earlier in the file loses to a plain
   rule later in it regardless of whether the query matches. */
@media (max-width: 780px) {
  /* A 90px text field can't be typed into usefully, so collapse the whole
     search to its icon. The button still submits to search.php, which has a
     full-width field of its own. */
  .header-search {
    max-width: none;
    flex: 0 0 auto;
    overflow: hidden;
  }
  .header-search input { display: none; }
  .header-search button {
    position: static;
    width: 38px; height: 38px;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--radius);
    background: rgba(255,255,255,.06);
    border: 1px solid rgba(255,255,255,.14);
  }
  .header-search button:hover { background: rgba(255,255,255,.12); color: #fff; }
  .header-nav {
    flex-shrink: 1;
    min-width: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .header-nav::-webkit-scrollbar { display: none; }
  .nav-link { flex-shrink: 0; white-space: nowrap; padding: .4rem .55rem; font-size: var(--text-sm); }
  /* Buttons were wrapping to two lines from ~700px down — "Sign In" over two
     rows next to a 34px "Preview" reads as broken layout, not tight layout.
     They never need to wrap: they are two and three words. */
  .header-nav .btn { white-space: nowrap; flex-shrink: 0; }
  /* Below 640 the secondary marketing links go, so the two actions that
     matter keep their room instead of competing for it. */
  @media (max-width: 640px) {
    .header-nav .nav-link { display: none; }
  }
}
@media (max-width: 480px) {
  .site-name { display: none; } /* icon-only logo, the tightest screens need the room more than the wordmark does */
}

/* ── Main Content ── */
.main-content { flex: 1; padding: 1.5rem 0 2.5rem; }

/* ── Layout Grid ── */
.layout-grid {
  display: grid;
  grid-template-columns: 300px 1fr 330px;
  gap: 1.5rem;
  align-items: start;
}
.layout-grid-2col {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 1.5rem;
  align-items: start;
}
/* Give sidebars more room on large displays so names stop truncating */
@media (min-width: 1700px) {
  .layout-grid      { grid-template-columns: 330px 1fr 360px; }
  .layout-grid-2col { grid-template-columns: 330px 1fr; }
}
/* One scroll for the whole page (audit, 30 Sep 2026). The three columns used
   to scroll independently inside a viewport-high box, which put three
   scrollbars on screen and made the wheel move whichever column the pointer
   happened to be over. The cost is that the footer now follows the last post,
   as on any other site. */
@media (max-width: 900px) {
  .layout-grid, .layout-grid-2col {
    grid-template-columns: 1fr;
  }
  .feed-center, .sidebar-left, .sidebar-right {
    max-height: none;
    overflow-y: visible;
  }
  .sidebar-left, .sidebar-right { display: none; }

  /* profile.php's sidebar carries real per-page content (stats, mobility
     score, hiring/looking-for panels), not the promo/nav filler that
     justifies hiding it everywhere else. Show it, stacked below the
     identity card rather than above it. */
  .profile-sidebar { display: block; order: 2; }
  .profile-main { order: 1; }
}

/* ── Cards ── hairline AND shadow. See design rule 3. */
.card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--hairline), var(--shadow);
  transition: border-color .3s cubic-bezier(.16,1,.3,1),
              transform .3s cubic-bezier(.16,1,.3,1),
              box-shadow .3s cubic-bezier(.16,1,.3,1);
}
.card:hover {
  border-color: #C9CFC6;
  transform: translateY(-3px);
  box-shadow: var(--hairline), var(--shadow-md);
}
.card + .card { margin-top: var(--space-4); }
.card-title {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-md);
  letter-spacing: var(--display-spacing);
  color: var(--ink);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}

/* ── Post Composer ── */
.composer { margin-bottom: 1.25rem; }
.composer textarea {
  width: 100%;
  min-height: 100px;
  padding: .85rem 1rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: var(--text-base);
  line-height: 1.5;
  color: var(--ink);
  background: var(--white);
  resize: vertical;
  transition: border-color .2s;
}
.composer textarea:focus { outline: none; border-color: var(--teal-ink); }
/* What gets typed here becomes a post, so it is set as one (--font-voice) */
.composer textarea#post-composer { font-family: var(--font-voice); font-size: 1.125rem; line-height: 1.5; }
.composer textarea#post-composer::placeholder { font-style: italic; }
.composer-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: .6rem;
}
.char-count { font-size: var(--text-sm); color: var(--ink-light); }

/* ── AI Warning Banner ── */
.ai-warning {
  background: var(--amber-wash);
  border: 1.5px solid var(--amber);
  border-radius: var(--radius);
  padding: .85rem 1rem;
  margin-top: .75rem;
  font-size: var(--text-base);
  display: none;
}
.ai-warning.show { display: block; }
.ai-warning label {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  cursor: pointer;
  margin-top: .5rem;
}

/* ── Posts / Feed Items ── */
.post {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-5);
  margin-bottom: var(--space-4);
  transition: border-color .2s, transform .18s ease-out, box-shadow .18s ease-out;
}
.post:hover { border-color: #D5CCBB; transform: translateY(-1px); box-shadow: var(--shadow); }
.post-header {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  margin-bottom: .65rem;
}
.post-avatar {
  width: 42px; height: 42px;
  background: linear-gradient(135deg, var(--teal), var(--teal-light));
  box-shadow: inset 0 -2px 4px rgba(0,0,0,.12);
  color: #fff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1.1rem;
  flex-shrink: 0;
}
.post-meta { flex: 1; }
.post-author {
  font-weight: 700;
  color: var(--ink);
  font-size: var(--text-base);
}
.post-author:hover { color: var(--teal-ink); }
.post-tagline { font-size: var(--text-sm); color: var(--ink-light); }
.post-time { font-size: var(--text-sm); color: var(--ink-light); margin-top: .1rem; }
.post-body {
  font-family: var(--font-voice);
  /* Newsreader sets small against Inter at the same size; 18px matches the
     x-height of the 16px UI text around it. */
  font-size: 1.1875rem; line-height: 1.55; color: var(--ink);
}
/* Kithby's own note under a post is not the member's words */
.post-attest { font-family: var(--font-ui); font-size: var(--text-xs); color: var(--ink-light); margin-top: .4rem; }
.profile-bio { font-family: var(--font-voice); font-size: 1.125rem; line-height: 1.55; color: var(--ink); margin: 0; }
.post-body .tag { color: var(--teal-ink); font-weight: 600; }
.post-actions {
  display: flex;
  gap: 1rem;
  margin-top: .75rem;
  padding-top: .75rem;
  border-top: 1px solid var(--cream-dark);
}
.post-action-btn {
  background: none;
  border: none;
  font-size: var(--text-sm);
  color: var(--ink-light);
  cursor: pointer;
  font-family: inherit;
  padding: .2rem .4rem;
  border-radius: var(--radius-sm);
  transition: all .18s;
}
.post-action-btn:hover { color: var(--teal-ink); background: var(--cream-dark); }
.post-action-btn.flag-btn:hover { color: var(--red); }
/* Option A: the conversation is the action that matters, so the comment count
   leads in ink; Report is still one click but sits apart at the far right as
   an icon, so it no longer reads as one of four equal choices. */
.post-actions { align-items: center; gap: .5rem; }
.post-action-btn.comment-toggle-btn { color: var(--ink); font-weight: 600; }
.post-action-btn.flag-btn { margin-left: auto; }

/* ── Profile Card (sidebar) ── */
.profile-mini { text-align: center; }
.profile-mini .avatar-lg {
  width: 72px; height: 72px;
  background: linear-gradient(135deg, var(--teal), var(--teal-light));
  box-shadow: inset 0 -3px 6px rgba(0,0,0,.12);
  color: #fff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.8rem;
  font-weight: 700;
  margin: 0 auto .75rem;
}
.profile-mini h3 { font-size: var(--text-base); margin-bottom: .2rem; }
.profile-mini p { font-size: var(--text-sm); color: var(--ink-mid); }
.profile-mini .tagline { font-size: var(--text-sm); color: var(--ink-light); margin-top: .25rem; }
.profile-stats {
  display: flex;
  justify-content: center;
  gap: 1.5rem;
  margin-top: .85rem;
  padding-top: .85rem;
  border-top: 1px solid var(--cream-dark);
}
.stat-item { text-align: center; }
/* The clickable variant of a stat tile. Kept as its own class so the plain
   .stat-item (another member's connection count) gains no affordance it
   cannot honour. */
.stat-item-link {
  display: block;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-sm);
  padding: var(--space-1) 0;
  transition: background .15s;
}
.stat-item-link:hover { background: var(--cream-dark); }
.stat-item-link:hover .stat-label { color: var(--teal); }
.stat-item-link:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* ── Match: your open roles ───────────────────────────────────────────────
   The landing list on match.php. Each row is a whole link, because the thing
   you want to click is the role, not its title. The count on the right is the
   reason the list exists: it says whether opening a role is worth doing before
   you open it. */
.match-roles { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-3); }
.match-role {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--white);
  text-decoration: none;
  color: inherit;
  transition: border-color .15s, background .15s, transform .15s;
}
.match-role:hover {
  border-color: var(--teal-ink);
  background: var(--cream-dark);
  transform: translateY(-1px);
}
.match-role:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.match-role-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.match-role-title {
  font-weight: 600; font-size: var(--text-md); color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.match-role-meta { font-size: var(--text-xs); color: var(--ink-light); }
.match-role-count { display: flex; flex-direction: column; align-items: flex-end; flex-shrink: 0; }
.match-role-n {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-lg);
  line-height: 1;
  color: var(--ink-light);
  font-variant-numeric: tabular-nums;
}
/* Only a count that means "there are people to contact" takes the accent. */
.match-role-n-strong { color: var(--amber); }
.match-role-lab { font-size: var(--text-xs); color: var(--ink-light); margin-top: 3px; }
.stat-num { font-weight: 700; color: var(--teal-ink); font-size: var(--text-md); }
.stat-label { font-size: var(--text-xs); color: var(--ink-light); }

/* ── Buttons ── 1px borders, not 2px. Tighter radius, calmer transitions. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: 500;
  line-height: 1.4;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background .16s, border-color .16s, color .16s, transform .1s ease-out;
  text-decoration: none;
}
.btn:active { transform: scale(.97); }
@media (prefers-reduced-motion: reduce) {
  .card, .post, .btn { transition: none; }
  .card:hover, .btn:active { transform: none; }
}
.btn-primary {
  background: var(--action);
  color: var(--action-ink);
  border-color: var(--action);
}
.btn-primary:hover { background: var(--action-hover); border-color: var(--action-hover); color: var(--action-ink); }
.btn-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}
.btn-ghost:hover { background: var(--cream-dark); color: var(--ink); border-color: #D5CCBB; }
/* Ghost buttons sitting on a dark surface need light treatment, or they
   render as near-invisible dark-on-dark. */
.site-header .btn-ghost,
.lp-hero .btn-ghost,
.lp-section-ink .btn-ghost,
.gate-panel-brand .btn-ghost {
  color: #fff;
  border-color: rgba(255,255,255,.30);
}
.site-header .btn-ghost:hover,
.lp-hero .btn-ghost:hover,
.lp-section-ink .btn-ghost:hover,
.gate-panel-brand .btn-ghost:hover {
  background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.55);
  color: #fff;
}

/* Primary buttons have the same dark-on-dark problem the ghost rules above
   solve: .btn-primary fills with --ink, and every surface listed here IS
   --ink, so the button rendered as plain text with no visible edge. On a
   dark surface the primary inverts to paper-on-ink instead. */
.site-header .btn-primary,
.lp-hero .btn-primary,
.lp-section-ink .btn-primary,
.gate-panel-brand .btn-primary {
  background: var(--cream);
  color: var(--ink);
  border-color: var(--cream);
}
.site-header .btn-primary:hover,
.lp-hero .btn-primary:hover,
.lp-section-ink .btn-primary:hover,
.gate-panel-brand .btn-primary:hover {
  background: #FFFFFF;
  border-color: #FFFFFF;
  color: var(--ink);
}
.btn-amber {
  background: var(--amber);
  color: #fff;
  border-color: var(--amber);
}
.btn-amber:hover { background: var(--amber-light); border-color: var(--amber-light); color: #fff; }
.btn-sm { padding: var(--space-1) var(--space-3); font-size: var(--text-sm); }
.btn-danger { background: var(--red); color: #fff; border-color: var(--red); }

/* ── Forms ── */
.form-group { margin-bottom: var(--space-4); }
/* The label ABOVE a field, not every <label> inside a form group.
   
   It used to be `.form-group label`, which also matched .checkbox-tile and
   .pill-checkbox, because those are labels too. At 0,1,1 it outranked
   .checkbox-tile at 0,1,0 and forced display:block onto them, which silently
   disabled every flex property those components depend on: gap, flex-wrap and
   the flex-basis that puts a tile's description on its own line.

   That is why three separate attempts to widen the checkbox gap changed
   nothing. The gap was correct and never applied, because the element was not
   a flex container. It also meant the tiles have never looked as designed
   anywhere on the site, on any page, since the rule was written.

   :not() states the real intent: a field label, not a component that happens
   to be built from a label. */
.form-group label:not(.checkbox-tile):not(.pill-checkbox):not(.shapepick-tile) {
  display: block;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ink-mid);
  margin-bottom: var(--space-2);
}
.form-control {
  width: 100%;
  padding: var(--space-3) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: var(--text-base);
  color: var(--ink);
  background: var(--white);
  transition: border-color .18s, box-shadow .18s;
}
.form-control:focus {
  outline: none;
  border-color: var(--ink-mid);
  box-shadow: 0 0 0 3px rgba(154,123,79,.12);
}
select.form-control { cursor: pointer; }
.form-hint { font-size: var(--text-sm); color: var(--ink-light); margin-top: var(--space-2); }

/* ── Multi-select checkbox groups (seeking.php, match.php's job spec) ──
   Two shapes for the same pattern: a grid of tiles for longer option lists,
   a wrapped row of pills for shorter ones. Both just toggle a "checked"
   class on the wrapping label, done inline by each input's onchange. */
.checkbox-tile {
  display: flex;
  align-items: center;
  /* .75rem, not .5rem. A native checkbox draws with almost no optical margin
     of its own, so half a rem measures correctly and reads as cramped: the
     label looks stuck to the box. Set here rather than on one variant, because
     every checkbox on the site has the same problem and fixing one of them
     only makes the others look wrong by comparison. */
  gap: .75rem;
  padding: .55rem .7rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  font-size: .85rem;
  color: var(--ink);
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.checkbox-tile:hover { border-color: var(--teal-light); }
.checkbox-tile.checked { border-color: var(--teal-ink); background: var(--teal-wash); font-weight: 600; }

/* CR-60. The four role shapes.
   Not the search control the function picker uses: four options with a line of
   explanation each are better shown than searched, and the explanation is the
   point. "Senior Leader" means nothing until it says "manages managers". */
.shapepick {
  display: grid;
  /* 300px, not 210. On a 900px form 210 fits four across and each description
     wraps to three lines; 300 fits two, at about 430px, which is where
     "Individual contributor, no reports" sits on one line. The number is the
     width the longest description needs, not a column count. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.shapepick-tile {
  align-items: flex-start;
  flex-wrap: wrap;
  padding: var(--space-3);
  line-height: 1.4;
}
/* The checkbox keeps its own line so a two-line description does not wrap
   underneath it and lose its left edge. */
.shapepick-tile input { margin-top: 2px; flex: 0 0 auto; }
.shapepick-name { font-weight: 600; flex: 1 1 auto; }
.shapepick-desc {
  flex: 1 0 100%;
  /* Lines up under the NAME, not under the checkbox, so it is the box width
     plus the gap. The box is 15px (.checkbox-tile input) and the gap comes
     from .checkbox-tile, so both numbers live elsewhere and this has to track
     them. */
  padding-left: calc(15px + .75rem);
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--ink-light);
}
.shapepick-tile.checked .shapepick-desc { color: var(--ink-mid); }
.checkbox-tile input { width: 15px; height: 15px; accent-color: var(--teal-ink); flex-shrink: 0; }

.pill-checkbox {
  display: inline-flex;
  align-items: center;
  padding: .4rem .85rem;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  font-size: .82rem;
  color: var(--ink-mid);
  cursor: pointer;
  transition: border-color .15s, background .15s, color .15s;
}
.pill-checkbox:hover { border-color: var(--teal-light); }
.pill-checkbox.checked { border-color: var(--teal-ink); background: var(--teal-wash); color: var(--teal-ink); font-weight: 600; }

/* ── Dropdown multi-select (match.php's Job Spec) ──
   Looks and closes like a plain <select> at rest; opens into a checklist.
   Same floating-panel treatment as .nav-account-menu / #theme-panel. */
.multiselect { position: relative; }
.multiselect-trigger {
  width: 100%;
  text-align: left;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  cursor: pointer;
  color: var(--ink);
}
.multiselect-trigger.is-placeholder { color: var(--ink-light); }
.multiselect-trigger .icon { transition: transform .15s ease; flex-shrink: 0; color: var(--ink-light); }
.multiselect.is-open .multiselect-trigger .icon { transform: rotate(180deg); }
.multiselect-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: 260px;
  overflow-y: auto;
  background: var(--white);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: .4rem;
  z-index: 500;
  opacity: 0;
  transform: translateY(-6px) scale(.98);
  pointer-events: none;
  transition: opacity .15s ease, transform .15s ease;
}
.multiselect.is-open .multiselect-panel { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
  .multiselect-panel { transition: none; }
}
.multiselect-option {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .45rem .55rem;
  border-radius: var(--radius-sm);
  font-size: .85rem;
  color: var(--ink);
  cursor: pointer;
  transition: background .12s;
}
.multiselect-option:hover { background: var(--cream-dark); }
.multiselect-option input { width: 15px; height: 15px; accent-color: var(--teal-ink); flex-shrink: 0; }

/* ── Badges ── */
.badge {
  display: inline-block;
  padding: .15rem .55rem;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
}
/* Role is a label, state is news.
   Every role badge is identical and neutral: role is already carried by the
   word inside the chip, and colour is a poor channel for five categories.
   The previous set spent five unrelated token families on it, which meant
   "Recruiter" wore the brand accent, "Admin" wore the error red, and the
   five chips ranged from 15:1 to 7:1 in weight for no chosen reason.
   Colour is reserved below for the two things that are actually news. */
.badge-member,
.badge-recruiter,
.badge-sales,
.badge-hiring,
.badge-admin     { background: var(--cream-dark); color: var(--ink-mid); }

/* ── Charter ──────────────────────────────────────────────────────────────
   Two placements, both replacing a dark manifesto panel that restated the
   homepage pitch to members who had already joined.

   .composer-charter puts the charter at the moment of writing, which is the
   only moment it can change behaviour. It was previously an italic grey
   fragment wedged between the character count and the Post button.

   .charter-card shows enforcement instead of asserting values. */
.composer-charter {
  font-size: var(--text-sm);
  color: var(--ink-light);
  margin: var(--space-2) 0 0;
  min-height: 1.2em;
}
.charter-card { margin-top: var(--space-4); }
.charter-stat {
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--ink-mid);
  margin: 0 0 var(--space-3);
}
.charter-stat strong {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-right: 2px;
}
.charter-link {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-mid);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
  transition: border-color .15s ease-out, color .15s ease-out;
}
.charter-link:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* ── Messages layout ──────────────────────────────────────────────────────
   Was an inline two-column grid: 280px thread list and a 1fr conversation
   pane that reached 1100px at desktop width while holding short bubbles.
   The third column turns that dead space into a rail. The conversation pane
   still gets the largest share, and the rail drops out before it could
   squeeze it. */
.messages-grid {
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr) 300px;
  gap: var(--space-5);
  height: calc(100vh - 180px);
}
.messages-rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  overflow-y: auto;
  min-width: 0;
}
.messages-rail .sponsor { margin-bottom: 0; }

/* Grid and flex items default to min-height:auto and refuse to shrink below
   their content, so the pane needs this to stay inside its row. */
.messages-grid > * { min-height: 0; }
#msg-scroll { min-height: 0; }

/* The conversation pane. Previously an inline style carrying
   overflow:hidden, which is what made /messages.php?to=N&job=N unusable:
   the first-contact outreach composer is 543px tall (notice, textarea, role
   select, reason radios, submit) and cannot shrink, so in a 495px pane it
   left 32px for the message area and clipped 147px of the form — including
   the submit button — with no way to scroll to it.

   overflow-y:auto lets the pane scroll when its content genuinely cannot
   fit. Only one scroller is ever active: with a tall composer the pane
   scrolls and #msg-scroll collapses to its content; with an established
   thread the composer is short, the pane fits its row, and #msg-scroll does
   the scrolling instead. */
.msg-pane {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow-y: auto;
  min-height: 0;
}
.rail-note {
  font-size: var(--text-sm);
  color: var(--ink-mid);
  line-height: 1.6;
  margin: 0 0 var(--space-3);
}
@media (max-width: 1100px) {
  .messages-grid { grid-template-columns: 280px minmax(0, 1fr); }
  .messages-rail { display: none; }
}
@media (max-width: 760px) {
  .messages-grid { grid-template-columns: 1fr; height: auto; }
}

/* ── Sponsor ──────────────────────────────────────────────────────────────
   Sponsors pay for the site, so they have to be noticed. The target is
   "differentiated, not louder": the unit should read as a distinct placed
   object rather than as member content, while never outranking a post.

   Before, it outranked everything — accent fill, 1.5px accent border, 4px
   accent top rule, and the only accent-filled pill button in the product.
   The correction is the ground and the label, not volume: the unit sits on
   a recessed panel instead of a white card, so the eye registers it as a
   different kind of thing, and the logo runs in full colour because that is
   the asset the advertiser is actually paying to show. What it still does
   not get is the brand accent, which belongs to member actions. */
.sponsor {
  background: var(--cream-dark);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  transition: opacity .3s;
}
.sponsor-label {
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-light);
  margin-bottom: var(--space-3);
}

/* ── Advertiser card, banded logo ──────────────────────────────────────────
   The logo gets a white band the full width of the card instead of a square
   beside the name. A square box sizes a logo by its HEIGHT, which is the wrong
   axis for a wordmark: "Meridian" in a 64px square renders about 12px tall and
   unreadable. The band is wide and short, so object-fit:contain sizes a
   wordmark by width and a square mark by height, and both land at a usable
   size. The white ground also means a logo with a transparent background is
   never sitting on cream, which is what most brand guidelines require.

   Usable band width is 296px at the 330px sidebar (328 inner - 32 padding),
   326px on displays over 1700px. A logo up to about 4:1 fills the full 72px
   height; wider than that and it is width-limited and sits shorter. */
.sponsor.sponsor-banded { padding: 0; overflow: hidden; }
.sponsor.sponsor-banded .sponsor-label {
  padding: var(--space-4) var(--space-4) 0;
  margin-bottom: var(--space-3);
}
.sponsor-band {
  background: var(--white);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: var(--space-4);
  /* A floor, not a fixed height. The band wraps whatever the logo needs, so a
     wordmark does not sit in a tall box of white space; the floor stops a very
     wide mark from collapsing the band to a strip. */
  min-height: 96px;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: var(--space-4);
}
/* Constrain BOTH axes and let the shape decide which one binds. This is the
   whole trick, and it removes the need for a square layout and a rectangular
   one as separate treatments:

     square (1:1)     height binds  -> 104 x 104
     wordmark (4:1)   width binds   -> 296 x 74
     very wide (7:1)  width binds   -> 296 x 42

   object-fit:contain never crops and never distorts, so one rule serves every
   shape and there is nothing for an advertiser to pick wrongly. */
.sponsor-band img {
  max-height: 104px;
  max-width: 100%;
  object-fit: contain;
  display: block;
}
/* No logo supplied: the initials tile, sized to the same 72px so a card
   without a logo still reads as designed rather than as a gap. It is a pill
   rather than a square so it occupies the band the way a wordmark would. */
.sponsor-band .sponsor-initials {
  height: 88px; min-width: 88px;
  padding: 0 var(--space-5);
  border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 2rem; letter-spacing: .02em;
}
.sponsor-copy { padding: 0 var(--space-4) var(--space-4); }
.sponsor-copy .sponsor-body { margin-top: var(--space-2); }

/* ── The two sold card formats ────────────────────────────────────────────
   Banner is the default and is the card above: wide logo band, description,
   CTA. Showcase trades the description for a much larger logo — a brand
   impression rather than a classified — and is sold as the dearer unit.

   Showcase is TALLER, which is the one real cost. Two slots stack in a 330px
   rail, so an unbounded Showcase in slot one would push slot two off-screen
   and quietly devalue whoever bought it. The band is therefore capped in vh as
   well as px: on a short viewport it gives height back rather than taking the
   second advertiser's visibility. */
.sponsor-size-showcase .sponsor-band {
  min-height: 200px;
  max-height: 34vh;
  padding: var(--space-5) var(--space-4);
}
.sponsor-size-showcase .sponsor-band img {
  max-height: 168px;
  max-height: min(168px, 26vh);
}
.sponsor-size-showcase .sponsor-band .sponsor-initials {
  height: 140px; min-width: 140px;
  font-size: 3rem;
}
.sponsor-size-showcase .sponsor-name { font-size: 1.18rem; }
.sponsor-size-showcase .sponsor-cta  { margin-top: var(--space-3); }

/* Below the two-column breakpoint the rail is gone and the card runs full
   width, where a 168px logo is no longer a height problem. */
@media (max-width: 900px) {
  .sponsor-size-showcase .sponsor-band { max-height: none; }
  .sponsor-size-showcase .sponsor-band img { max-height: 168px; }
}

/* ── Admin: advertiser card preview ───────────────────────────────────────
   Admin-only, but it lives here rather than inline in admin.php because the
   stage renders the REAL .sponsor markup and has to inherit the real rules.
   A preview built from a copy of those rules is a preview of the copy. */
.ad-preview-wrap {
  display: grid;
  grid-template-columns: 330px 1fr;
  gap: var(--space-5);
  align-items: start;
  margin: var(--space-4) 0 var(--space-5);
  padding: var(--space-4);
  background: var(--ground);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
@media (max-width: 860px) { .ad-preview-wrap { grid-template-columns: 1fr; } }
.ad-preview-cap {
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-light); margin-bottom: var(--space-2);
}
/* The sidebar is 330px. Pinning the stage to it means the preview is not an
   impression of the card, it is the card at its delivered width. */
.ad-preview-stage { width: 330px; max-width: 100%; }
.ad-preview-stage .sponsor { margin-bottom: 0; }
.ad-preview-stage .sponsor-hide-btn { pointer-events: none; }
.ad-preview-stage .sponsor-cta { pointer-events: none; }

.ad-verdict { font-size: var(--text-sm); line-height: 1.6; color: var(--ink-mid); }
.ad-verdict-empty { color: var(--ink-light); font-style: italic; margin: 0; }
.ad-verdict dl { display: grid; grid-template-columns: auto 1fr; gap: .3rem var(--space-3); margin: 0 0 var(--space-3); }
.ad-verdict dt { color: var(--ink-light); white-space: nowrap; }
.ad-verdict dd { margin: 0; font-weight: 600; color: var(--ink); }
.ad-note {
  display: flex; gap: var(--space-2); align-items: flex-start;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
}
.ad-note-ok   { background: rgba(59,107,74,.10);  color: var(--green); }
.ad-note-warn { background: rgba(138,100,32,0.12); color: var(--amber); }
.ad-note-warn { background: color-mix(in srgb, var(--amber) 12%, transparent); color: var(--amber); }
.ad-note-bad  { background: rgba(140,47,47,.10);  color: var(--red); }
.ad-note b { font-weight: 700; }
.sponsor-name    { font-weight: 600; font-size: var(--text-md); line-height: 1.25; color: var(--ink); }
.sponsor-tagline { font-size: var(--text-xs); color: var(--ink-light); margin-top: 2px; }
.sponsor-body    { font-size: var(--text-sm); color: var(--ink-mid); line-height: 1.6; margin-bottom: var(--space-3); }
/* A real button, so it reads as clickable — but outlined and in ink, not a
   filled accent pill. Clear affordance without taking the brand colour. */
.sponsor-cta {
  display: inline-block;
  font-size: var(--text-sm); font-weight: 600;
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-4);
  text-decoration: none;
  transition: background .15s ease-out, color .15s ease-out;
}
.sponsor-cta:hover { background: var(--ink); color: var(--cream); }
.sponsor-hide-btn {
  background: none; border: none; cursor: pointer; line-height: 1;
  color: var(--ink-light); padding: 0;
  transition: color .15s ease-out;
}
.sponsor-hide-btn:hover { color: var(--ink); }
.sponsor-foot {
  margin-top: var(--space-3); padding-top: var(--space-2);
  border-top: 1px solid var(--line);
  font-size: var(--text-xs); color: var(--ink-light);
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-2);
}
.sponsor-foot a { color: var(--ink-mid); font-weight: 600; white-space: nowrap; }

/* ── Alerts ── */
.alert {
  padding: .8rem 1rem;
  border-radius: var(--radius);
  font-size: var(--text-base);
  margin-bottom: 1rem;
}
.alert-success { background: var(--green-wash); border: 1px solid var(--green); color: var(--green); }
.alert-error   { background: var(--red-wash);   border: 1px solid var(--red);   color: var(--red); }
.alert-warning { background: var(--amber-wash); border: 1px solid var(--amber); color: var(--amber-on-wash); }
.alert-info    { background: var(--teal-wash);  border: 1px solid var(--teal-light); color: var(--teal-ink); }

/* ── Divider ── */
.divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 1rem 0;
}

/* ── Gate / Login Pages ── asymmetric split: brand panel is the hero */
.gate-page {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  position: relative;
  overflow: hidden;
  /* The two grounds live HERE, not on the panels. The route is a sibling of
     the panels so it can cross the column boundary, but a panel with a solid
     background paints over it — which is exactly what happened: the curve was
     drawn and then buried under --teal, so it appeared only on the form side.
     With the colour on the page and the panels transparent, the route paints
     above the ground and the panel content still paints above the route.
     The stop matches the 1.15fr / 1fr split. */
  background: linear-gradient(to right,
    var(--teal)  0 53.49%,
    var(--cream) 53.49% 100%);
}
@media (max-width: 860px) {
  .gate-page { grid-template-columns: 1fr; }
}

.gate-panel-brand {
  background: transparent;   /* the page carries it, see .gate-page */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-8) var(--space-7);
  position: relative;
  overflow: hidden;
}
/* Soft brass wash so the panel has depth instead of reading as a flat block */
/* Hiding the brand panel on narrow screens has to come AFTER the base rule,
   not before it. It used to sit in a media query above, where it had the same
   specificity and lost the cascade to the display:flex below — so the panel
   never actually hid and the sign-in form was squeezed into a half-width
   column on every phone. */
@media (max-width: 860px) {
  .gate-panel-brand { display: none; }
  /* The page carries the two grounds as a left-right gradient, which only
     makes sense while both columns exist. With the brand panel gone the phone
     would otherwise get a teal band down 53% of the screen behind the form. */
  .gate-page { background: var(--cream); }
  .gate-route-wrap { display: none; }
}

.gate-panel-brand::after {
  content: '';
  position: absolute;
  width: 520px; height: 520px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(154,123,79,.16) 0%, transparent 68%);
  top: -140px; right: -180px;
  pointer-events: none;
}
/* Narrowed so the copy sits within the route rather than overrunning it. */
.gate-brand-inner { max-width: 25rem; position: relative; z-index: 1; }

/* Still used by gate.php (dark brand panel) and locked.php (badge variant on
   the light canvas). I removed these when the sign-in lockup moved to the form
   side and did not check who else emitted the class — both pages would have
   lost their flex alignment and the wordmark its display face. The mark itself
   is fine on both: gate.php sits on ink, locked.php uses the badged variant. */
.gate-logo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-7);
  transition: opacity .15s;
}
.gate-logo:hover { opacity: .8; }
.gate-logo svg { width: 56px; height: 56px; flex-shrink: 0; }
.gate-logo-name {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: clamp(2rem, 3.2vw, 2.75rem);
  letter-spacing: var(--display-spacing);
  line-height: 1;
  color: #fff;
}

/* ── The mark, on the form side ───────────────────────────────────────────
   It used to sit in the brand panel, which is display:none below 860px, so a
   phone got no brand at all. Here it survives the breakpoint and sits with the
   thing being signed into. On the light panel it takes the paper-tuned accent,
   not the ink-tuned one. */
.gate-form-logo {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
  text-decoration: none;
  transition: opacity .15s;
}
.gate-form-logo:hover { opacity: .78; }
.gate-form-logo svg { width: 44px; height: 44px; flex-shrink: 0; color: var(--amber); }
.gate-form-logo-name {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: clamp(1.75rem, 2.4vw, 2.1rem);
  letter-spacing: var(--display-spacing);
  line-height: 1;
  color: var(--ink);
}
.gate-form-logo:focus-visible { outline: 2px solid var(--amber); outline-offset: 4px; border-radius: var(--radius-sm); }

/* kith_mark() is drawn in hardcoded white, because until now it only ever sat
   on the dark header or the dark gate panel. On this light panel that measured
   1.04:1 — the mark was there at its full 44px and completely invisible; the
   only thing visible was the green live dot at 2.28:1, which read as a stray
   speck beside the wordmark.
   Scoped to this lockup so the header and the dark gate pages keep their white
   mark. --amber measures 5.12:1 here, --green 5.93:1. */
.gate-form-logo .kith-route     { stroke: var(--amber); stroke-opacity: .9; }
.gate-form-logo .kith-node      { fill: var(--amber); }
.gate-form-logo .kith-live      { fill: var(--green); }
.gate-form-logo .kith-live-rest { fill: var(--green); }

/* With the lockup gone from the brand panel, the headline is the first thing
   there and carries the weight the wordmark used to. */
.gate-headline { margin-top: 0; }

/* ── The route, as the panel's ground ─────────────────────────────────────
   Oversized and bled off the panel so it reads as texture rather than a logo
   sitting in a corner. It is behind the copy (z-index 0 against the inner's
   1) and the stroke is faint enough that the headline never fights it.
   The traveller is the only moving part: one dot, slow, so it registers as
   alive without pulling the eye off the form. */
/* The two clipped copies. inset:0 on .gate-page means these percentages are
   page-relative, so the split tracks the 1.15fr / 1fr grid: 1.15/2.15 =
   53.49%. Each wrapper shows only its own column's half of the curve. */
.gate-route-wrap {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.gate-route-onink   { clip-path: inset(0 46.51% 0 0); }
.gate-route-onpaper { clip-path: inset(0 0 0 53.49%); }

.gate-route {
  position: absolute;
  /* Anchored to the SEAM, not to the copy (design 4, Sep 2026). The loop is
     drawn large with its left side off the page, and its right-hand node
     (49,30 in the 64-unit viewBox) sits just over the seam, beside the form:
     that node is the person signing in (.gate-route-you). The copy stays
     centred in the dark panel, inside the loop.

     --gate-s is the drawn size, --gate-dx how far past the seam the node
     sits. The right-hand bulge reaches about 0.07 x size + dx past the seam,
     so the size steps down on narrower windows to keep the bulge clear of the
     form, which is pushed toward the right edge below (.gate-panel-form). */
  /* No upper cap: capped at 1700px, a 1757px-wide window showed the loop's
     left side again (measured on Jon's screen). Growing with the window keeps
     it off the page at any width; the bulge stays well clear of the form,
     which also moves right as the window widens. */
  /* Height matters too: a tall window (1358x1105, Jon's) fit the whole loop
     top to bottom and showed its left side again. 175vh keeps it cropped
     like the 1440x860 design, where the width term wins anyway. */
  --gate-s: max(111vw, 175vh);
  --gate-dx: 20px;
  width: var(--gate-s);
  height: var(--gate-s);
  left: calc(53.49% + var(--gate-dx) - var(--gate-s) * 0.765625);  /* 49/64 */
  top: calc(48% - var(--gate-s) * 0.46875);                         /* 30/64 */
}
/* Narrower windows: smaller loops cut through the copy, so the loop stays big
   and the node moves back toward the seam instead. Measured closest approach
   (line to copy / line to form): 1440 201/156, 1280 153/98, 1150 100/72,
   1024 49/45, 900 25/39. */
@media (max-width: 1279px) { .gate-route { --gate-s: max(111vw, 175vh); --gate-dx: 0px; } }
@media (max-width: 1099px) { .gate-route { --gate-s: 1200px; --gate-dx: -20px; } }
/* The whole loop is drawn now. At this scale most of it is off-page, so the
   cropping does the work the dasharray used to — and the curve reads as one
   continuous line passing through rather than an arc that starts and stops. */
.gate-route-line {
  fill: none;
  stroke-width: .8;
  stroke-linecap: round;
}
/* On the dark panel: the ink-tuned accent. On the form panel: the paper-tuned
   one. Using either on both grounds is what made it invisible on one side. */
/* The dark half, quieter again. Opacity alone runs out: below about .20 the
   stroke measures under 1.4:1 and stops registering at all — the hero watermark
   sat at 1.34 and read as absent. So the weight comes off the stroke WIDTH as
   well, .8 -> .5 units, roughly 9px to 6px at this scale. Thinner reads slighter
   while every drawn pixel stays above the floor. */
.gate-route-onink   .gate-route-line { stroke: var(--gauge-fill-dark); stroke-opacity: .22; stroke-width: .5; }
.gate-route-onink   .gate-route-node { fill: var(--gauge-fill-dark); fill-opacity: .26; }
.gate-route-onink   .gate-route-travel { fill: var(--gauge-fill-dark); opacity: .5; }

/* The slow walk along the left path. A 14-unit segment of the 100-unit loop,
   a little brighter than the static stroke, travelling it once every two
   minutes. Slow enough that it reads as ambient rather than as something
   asking to be watched — at this size a faster pass turns into a distraction
   beside a sign-in form.

   Hidden on the paper side: one moving element on the screen is plenty, and
   the form panel is where the eye needs to settle. */
.gate-route-draw {
  fill: none;
  stroke: var(--gauge-fill-dark);
  stroke-opacity: .34;
  stroke-width: .5;
  stroke-linecap: round;
  stroke-dasharray: 14 86;
  animation: gateDraw 120s linear infinite;
}
.gate-route-onpaper .gate-route-draw { display: none; }
@keyframes gateDraw {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: -100; }
}
@media (prefers-reduced-motion: reduce) {
  .gate-route-draw { animation: none; }
}
.gate-route-onpaper .gate-route-line { stroke: var(--gauge-fill, var(--amber)); stroke-opacity: .32; }
.gate-route-onpaper .gate-route-node { fill: var(--gauge-fill, var(--amber)); fill-opacity: .3; }
.gate-route-onpaper .gate-route-travel { fill: var(--gauge-fill, var(--amber)); }

/* Design 4: one confident line, not a watermark. Brass on the ink, ink on the
   paper, the same stroke width at every size (non-scaling), and the four
   people on it as dots. Static: the traveller and the drawing segment are off,
   so the only thing on the page asking for attention is the form. */
.gate-route-line,
.gate-route-node { vector-effect: non-scaling-stroke; }
.gate-route-onink   .gate-route-line { stroke: var(--amber-light); stroke-opacity: .85; stroke-width: 3px; }
.gate-route-onpaper .gate-route-line { stroke: var(--teal); stroke-opacity: .7; stroke-width: 3px; }
.gate-route-onink   .gate-route-node { fill: #FAFAF9; fill-opacity: 1; r: .28; }
.gate-route-onpaper .gate-route-node { fill: var(--teal); fill-opacity: .7; r: .28; }
.gate-route .gate-route-you {
  r: .44;
  fill: var(--amber-light);
  fill-opacity: 1;
  stroke: var(--cream);
  stroke-width: 4px;
}
/* Gentle motion: one small dot walks the loop, a lap a minute, taking each
   side's colour as it crosses the seam (the two clipped copies do that for
   free). The drawing segment stays off. The "you" node breathes slowly. Both
   stop under reduced motion (the traveller's rule is above). */
.gate-route-draw { display: none; }
/* Scoped under .gate-route so it outranks the base .gate-route-travel rule
   further down, whose animation shorthand would otherwise reset this to 48s. */
.gate-route .gate-route-travel { r: .32; opacity: 1; animation-duration: 60s; }
.gate-route-onink   .gate-route-travel { fill: var(--amber-light); }
.gate-route-onpaper .gate-route-travel { fill: var(--teal); }
.gate-route .gate-route-you {
  transform-box: fill-box;
  transform-origin: center;
  animation: gateYou 4.5s ease-in-out infinite;
}
@keyframes gateYou {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.18); }
}
@media (prefers-reduced-motion: reduce) {
  .gate-route .gate-route-you { animation: none; }
}

/* The two halves were not evenly weighted: the dark side measured 2.41:1 and
   the paper side 1.51:1, so one read as a line and the other as a smudge. Both
   sit near 1.6:1 now — quiet, but above the ~1.5 floor where a stroke stops
   registering at all (the hero watermark sat at 1.34 and read as absent). The
   paper side could not go quieter; it was already at that limit. */
.gate-route-travel {
  opacity: .72;
  offset-path: path("M30,14 C42,12 52,20 49,30 C54,42 46,50 38,49 C26,54 14,48 16,36 C10,22 18,10 30,14 Z");
  offset-rotate: 0deg;
  animation: gateTravel 48s linear infinite;
}
/* Full lap again, matching the full drawn loop. Slower, because at this scale
   the visible portion is long and a fast dot reads as a distraction. */
@keyframes gateTravel {
  from { offset-distance: 0%; }
  to   { offset-distance: 100%; }
}

/* Reduced motion: the route stays, the traveller stops. The panel still reads
   as designed rather than losing a element. */
@media (prefers-reduced-motion: reduce) {
  .gate-route-travel { animation: none; offset-distance: 0%; }
}
.gate-headline {
  font-family: var(--font-display);
  /* Stepped down from clamp(1.75rem, 2.6vw, 2.25rem). It was larger than the
     wordmark above it; it is the supporting line and now reads as one. */
  font-size: clamp(1.35rem, 1.9vw, 1.6rem);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-spacing);
  color: #fff;
  /* Slightly looser leading + a max measure keeps this from stacking into a
     narrow, tall column that's uncomfortable to read. */
  line-height: 1.25;
  max-width: 22ch;
  text-wrap: balance;
  margin-bottom: var(--space-6);
}
.gate-values {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.gate-values li {
  color: rgba(255,255,255,.62);
  font-size: var(--text-base);
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
}
.gate-values li::before {
  content: '';
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--amber-light);
  flex-shrink: 0;
  transform: translateY(-3px);
}

/* The member count is the only proof on this page that anyone else is here,
   and it sat at --text-sm, the same size as the charter note and the subnav
   below it — five consecutive elements within one step of each other, which
   reads as one undifferentiated block. It gets its own size and more air. */
.gate-stat {
  margin-top: var(--space-6);
  margin-bottom: var(--space-5);
  color: rgba(255,255,255,.85);
  font-size: var(--text-base);
}
.gate-stat strong {
  color: #fff;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: var(--display-weight);
  margin-right: .2rem;
}

.gate-charter-note {
  margin-top: var(--space-4);
  color: rgba(255,255,255,.52);
  font-size: var(--text-sm);
  line-height: 1.7;
}
.gate-charter-note a { color: rgba(255,255,255,.72); text-decoration: underline; text-underline-offset: 2px; }

.gate-subnav {
  margin-top: 1.5rem;
  display: flex;
  gap: .5rem;
  font-size: var(--text-sm);
}
.gate-subnav a { color: rgba(255,255,255,.55); }
.gate-subnav a:hover { color: #fff; }
.gate-subnav span { color: rgba(255,255,255,.3); }

.gate-panel-form {
  background: transparent;  /* the page carries it, see .gate-page */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-8) var(--space-7);
}
/* Two columns: sit the form toward the right so the route's bulge over the
   seam has room (see .gate-route). */
@media (min-width: 861px) {
  .gate-panel-form { justify-content: flex-end; padding-right: clamp(24px, 8vw, 140px); }
}
.gate-form-inner { width: 100%; max-width: 340px; }
.gate-form-title {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-spacing);
  font-size: var(--text-xl);
  color: var(--ink);
  margin-bottom: var(--space-1);
}
.gate-form-sub {
  color: var(--ink-light);
  font-size: var(--text-base);
  margin-bottom: var(--space-6);
}
/* request-invite.php: for someone who already holds an emailed invite link */
.gate-invite-note {
  display: flex; gap: var(--space-3); align-items: flex-start;
  font-size: var(--text-sm); line-height: 1.5; color: var(--ink-mid);
  background: var(--amber-wash); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--space-3) var(--space-4);
  margin: calc(-1 * var(--space-3)) 0 var(--space-6);
}
.gate-invite-note .icon { color: var(--amber); flex-shrink: 0; margin-top: 1px; }
.gate-invite-note strong { color: var(--ink); }
/* The applicant's own words, in the voice face like a member's post */
.form-control-voice { font-family: var(--font-voice); font-size: 1.0625rem; line-height: 1.55; }
.form-control-voice::placeholder { font-style: italic; }
.reason-count { color: var(--ink-light); font-variant-numeric: tabular-nums; }
.reason-count.is-met { color: var(--amber); font-weight: 600; }
.gate-tagline {
  color: var(--ink-light);
  font-size: var(--text-base);
  margin-bottom: var(--space-6);
}

/* ── Messages ── */
.thread-list { list-style: none; }
.thread-item {
  display: flex;
  align-items: center;
  gap: .85rem;
  padding: .85rem 0;
  border-bottom: 1px solid var(--cream-dark);
  cursor: pointer;
  transition: background .15s;
}
.thread-item:hover { background: var(--cream); margin: 0 -.5rem; padding-left: .5rem; padding-right: .5rem; }
.thread-item.unread .thread-name { font-weight: 700; }
.thread-name { font-size: var(--text-base); }
.thread-preview { font-size: var(--text-sm); color: var(--ink-light); }
.thread-time { font-size: var(--text-xs); color: var(--ink-light); margin-left: auto; white-space: nowrap; }

.message-bubble {
  /* 72% of an 1100px pane ran to ~790px, which is a paragraph, not a bubble.
     The ch cap keeps the line length readable however wide the pane gets. */
  max-width: min(72%, 46ch);
  padding: .65rem .95rem;
  /* Not --radius-pill: these were 18px before the radius consolidation, and a
     999px radius turns any multi-line bubble into a lozenge. */
  border-radius: var(--radius-lg);
  font-size: var(--text-base);
  line-height: 1.5;
  margin-bottom: .5rem;
}
.bubble-sent {
  background: var(--teal);
  color: #fff;
  margin-left: auto;
  border-bottom-right-radius: 4px;
}
.bubble-recv {
  background: var(--cream-dark);
  color: var(--ink);
  border-bottom-left-radius: 4px;
}

/* ── Profile Page ── */
.profile-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
}
.profile-info { padding: 1.25rem 1.5rem; }
/* The -40px pull existed so the avatar straddled the cover banner. With the
   banner gone there is nothing to straddle, and the offset clipped the avatar
   against the top of the card. The white ring and the inset gloss were also
   banner-era: a white ring on a white card separates nothing. */
.profile-avatar-wrap {
  margin-bottom: .75rem;
}
.profile-avatar-lg {
  width: 80px; height: 80px;
  background: linear-gradient(160deg, var(--teal-light), var(--teal));
  color: #fff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  font-weight: 700;
}
.profile-headline { color: var(--ink-mid); font-size: var(--text-base); margin: .25rem 0; }
.profile-location { color: var(--ink-light); font-size: var(--text-sm); }
.profile-actions { margin-top: 1rem; display: flex; gap: .5rem; flex-wrap: wrap; }

/* ── Search Results ── */
.search-result {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .85rem 0;
  border-bottom: 1px solid var(--cream-dark);
}
.search-result:last-child { border-bottom: none; }
.search-result-info { flex: 1; }
.search-result-name { font-weight: 700; font-size: var(--text-base); }
.search-result-meta { font-size: var(--text-sm); color: var(--ink-light); }

/* ── Admin Panel ── */
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
/* ── Admin stat grid ── */
.admin-stats-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: .75rem;
  margin-bottom: 1.5rem;
}
.admin-stat-card { padding: .85rem .5rem; }
.admin-stat-num  { font-size: var(--text-xl); font-weight: 700; color: var(--teal-ink); line-height: 1; }
.admin-stat-label{ font-size: var(--text-xs); color: var(--ink-light); margin-top: .25rem; }
.stat-warn       { border-color: var(--amber) !important; }
.stat-warn-num   { color: var(--amber) !important; }

@media (max-width: 700px) {
  .admin-stats-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 420px) {
  .admin-stats-grid { grid-template-columns: repeat(2, 1fr); }
}

.admin-table th, .admin-table td {
  text-align: left;
  padding: .65rem .85rem;
  border-bottom: 1px solid var(--border);
}
.admin-table th {
  background: var(--cream-dark);
  font-weight: 700;
  color: var(--ink-mid);
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.admin-table tr:hover td { background: var(--cream); }

/* ── Warning banner ── */
.warning-banner {
  background: var(--red);
  color: #fff;
  padding: .85rem 1.1rem;
  border-radius: var(--radius);
  margin-bottom: 1rem;
  animation: warnPulse 2s ease-in-out 3;
}
@keyframes warnPulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: .75; }
}

/* ── Status chips ── */
.status-chip {
  display: inline-block;
  padding: .1rem .5rem;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
}
.status-active  { background: var(--green-wash); color: var(--green); }
.status-warned  { background: var(--amber-wash); color: var(--amber-on-wash); }
.status-locked  { background: var(--red-wash);   color: var(--red); }

/* ── Recruiter search panel ── */
.filter-panel { margin-bottom: 1.25rem; }
.filter-row { display: flex; gap: .75rem; flex-wrap: wrap; }
.filter-row .form-control { flex: 1; min-width: 160px; }

/* ── No-paste warning toast ── */
.toast {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  background: var(--ink);
  color: #fff;
  padding: .75rem 1.1rem;
  border-radius: var(--radius);
  font-size: var(--text-sm);
  box-shadow: var(--shadow-md);
  opacity: 0;
  transform: translateY(10px);
  transition: all .3s;
  z-index: 9999;
  max-width: 320px;
}
.toast.show { opacity: 1; transform: translateY(0); }
/* It closes on a click, so it has to look like it can be clicked. */
.toast { cursor: pointer; }
.toast:hover { filter: brightness(1.15); }

/* ── @mention links ── */
/* An ordinary link in the surrounding face. The boxed, bold chip read heavier
   than the sentence it sat in, especially inside Newsreader post text. */
.mention-link {
  color: var(--teal-ink);
  font-weight: 500;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--amber) 60%, transparent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.mention-link:hover { text-decoration-color: var(--amber); }

/* ── @mention autocomplete dropdown ── */
#mention-dropdown {
  display: none;
  position: absolute;
  background: #fff;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 6px 20px rgba(0,0,0,.12);
  z-index: 10000;
  min-width: 230px;
  max-width: 300px;
  max-height: 230px;
  overflow-y: auto;
}
.mention-item {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .5rem .75rem;
  cursor: pointer;
  border-bottom: 1px solid var(--cream-dark);
  transition: background .1s;
}
.mention-item:last-child { border-bottom: none; }
.mention-item:hover, .mention-item.selected { background: var(--cream-dark); }
.mention-item-av {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--teal), var(--teal-light));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: var(--text-xs);
  flex-shrink: 0;
}
.mention-item-info { flex: 1; min-width: 0; }
.mention-item-name { display: block; font-weight: 600; font-size: var(--text-sm); }
.mention-item-title {
  display: block;
  font-size: var(--text-xs);
  color: var(--ink-light);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mention-item-conn {
  font-size: var(--text-xs);
  color: var(--teal-ink);
  font-weight: 700;
  letter-spacing: .03em;
  flex-shrink: 0;
}

/* ── Site Footer ── */
.site-footer {
  background: var(--teal-dark);
  color: rgba(255,255,255,.7);
  padding: 1.5rem 0;
  margin-top: auto;
}
/* The fixed Page Feedback button sat on top of the right end of the footer,
   cutting off "A space for real people". Only signed-in pages have it. */
body:has(#feedback-widget) .site-footer .footer-inner { padding-right: 150px; }
.footer-inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.footer-logo {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-spacing);
  color: rgba(255,255,255,.9);
  font-size: var(--text-md);
  transition: color .15s;
}
.footer-logo:hover { color: #fff; }
.footer-logo-icon { width: 24px; height: 24px; flex-shrink: 0; display: inline-flex; }
.footer-logo-icon svg { width: 100%; height: 100%; }
.footer-logo strong { color: var(--amber-light); }
.footer-nav { display: flex; gap: 1rem; }
.footer-nav a { color: rgba(255,255,255,.6); font-size: var(--text-sm); }
.footer-nav a:hover { color: #fff; }
.footer-copy { font-size: var(--text-sm); margin-left: auto; }

/* ── Theme Switcher Widget ── */
#theme-widget {
  position: fixed;
  bottom: 1.5rem;
  left: 1.5rem;
  z-index: 8000;
  font-family: 'Inter', sans-serif;
}
#theme-widget-btn {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .45rem .85rem;
  background: var(--white);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-mid);
  cursor: pointer;
  box-shadow: var(--shadow-md);
  transition: all .18s;
}
#theme-widget-btn:hover {
  border-color: var(--teal-ink);
  color: var(--teal-ink);
  box-shadow: 0 4px 16px rgba(0,0,0,.15);
}
#theme-panel {
  position: absolute;
  bottom: calc(100% + .6rem);
  left: 0;
  background: var(--white);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem 1.1rem;
  box-shadow: var(--shadow-md);
  min-width: 286px;
  opacity: 0;
  transform: translateY(6px) scale(.98);
  pointer-events: none;
  transition: opacity .15s ease, transform .15s ease;
}
#theme-panel.is-open { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
  #theme-panel { transition: none; }
}
#theme-options {
  display: flex;
  flex-direction: column;
  gap: .4rem;
  margin-bottom: .85rem;
}
.theme-opt {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .4rem .55rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  background: none;
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--ink);
  font-family: inherit;
  transition: all .15s;
  text-align: left;
}
.theme-opt:hover { border-color: var(--teal-ink); background: var(--cream-dark); }
.theme-opt.active { border-color: var(--teal-ink); background: var(--cream-dark); font-weight: 600; }
.theme-swatch {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.5px solid rgba(0,0,0,.1);
  flex-shrink: 0;
}
.theme-opt-label { flex: 1; }
.theme-opt-check { color: var(--teal-ink); font-size: var(--text-sm); opacity: 0; }
.theme-opt.active .theme-opt-check { opacity: 1; }
#theme-vote-row { border-top: 1px solid var(--cream-dark); padding-top: .75rem; }
#theme-vote-btn {
  width: 100%;
  padding: .4rem;
  background: none;
  border: 1.5px solid var(--teal-ink);
  border-radius: var(--radius);
  color: var(--teal-ink);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  transition: all .15s;
}
#theme-vote-btn:hover { background: var(--teal); color: #fff; }

/* ── Seeking badge ── */
/* State badges. These carry colour because they are news about the person,
   not a label for them. Same metrics as .badge so they line up in a row. */
.seeking-badge,
.retired-badge {
  font-size: var(--text-xs);
  padding: .15rem .55rem;
  border-radius: var(--radius-pill);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: #fff;
}
.seeking-badge { background: var(--green); }
.retired-badge { background: var(--ink-light); }

/* ── Pagination ── */
.pagination { display: flex; gap: .4rem; margin-top: 1.25rem; }
.page-btn {
  padding: .35rem .75rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  color: var(--ink-mid);
  background: var(--white);
  cursor: pointer;
}
.page-btn.active, .page-btn:hover { background: var(--teal); color: #fff; border-color: var(--teal); }

/* ── Comments ── */
.comments-section {
  border-top: 1px solid var(--cream-dark);
  margin-top: .75rem;
  padding-top: .85rem;
}
.comment {
  display: flex;
  gap: .6rem;
  margin-bottom: .75rem;
}
.comment-body {
  flex: 1;
  background: var(--cream-dark);
  border-radius: 0 var(--radius) var(--radius) var(--radius);
  padding: .55rem .75rem;
  font-size: var(--text-sm);
}
.comment-author {
  font-weight: 700;
  color: var(--ink);
  margin-right: .4rem;
  font-size: var(--text-sm);
}
.comment-time {
  font-size: var(--text-xs);
  color: var(--ink-light);
}
.comment-text {
  margin-top: .25rem;
  font-family: var(--font-voice);
  font-size: 1.0625rem; /* one step under a post body, same voice */
  color: var(--ink);
  line-height: 1.5;
}
.comment-actions {
  margin-top: .3rem;
  display: flex;
  gap: .75rem;
}
.reply-btn {
  background: none;
  border: none;
  font-size: var(--text-xs);
  color: var(--teal-ink);
  cursor: pointer;
  padding: 0;
  font-weight: 600;
}
.reply-btn:hover { text-decoration: underline; }

/* ── DM link on individual comments ── */
.comment-dm-btn {
  font-size: var(--text-xs);
  color: var(--ink-light);
  text-decoration: none;
  font-weight: 500;
  opacity: 0;
  transition: opacity .15s, color .15s;
  white-space: nowrap;
}
.comment:hover .comment-dm-btn { opacity: 1; }
.comment-dm-btn:hover { color: var(--teal-ink); }

/* ── Heated thread banner ── */
.heated-thread-banner {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
  margin-top: .85rem;
  padding: .55rem .85rem;
  background: var(--cream-dark);
  border-left: 3px solid var(--amber);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: var(--text-xs);
  color: var(--ink-mid);
}
.heated-thread-banner span:first-child { font-weight: 700; color: var(--ink); }
.heated-dm-link {
  color: var(--teal-ink);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  margin-left: auto;
}
.heated-dm-link:hover { text-decoration: underline; }

.comment-replies {
  margin-left: 2.6rem;
  margin-bottom: .4rem;
}
.comment-compose {
  display: flex;
  gap: .6rem;
  align-items: flex-start;
  margin-top: .5rem;
}
.comment-compose textarea,
.reply-compose textarea {
  width: 100%;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: .45rem .65rem;
  font-size: var(--text-sm);
  font-family: inherit;
  resize: none;
  background: var(--white);
  transition: border-color .15s;
}
.comment-compose textarea:focus,
.reply-compose textarea:focus {
  outline: none;
  border-color: var(--teal-ink);
}
.comment-compose-inner { flex: 1; }
.reply-compose {
  margin-top: .5rem;
  padding-left: 2.6rem;
  display: none;
}
.reply-compose.open { display: block; }
.comment-submit-row {
  display: flex;
  gap: .4rem;
  margin-top: .4rem;
  justify-content: flex-end;
}

/* ── Sidebar person row ── used by People You May Know, Recruiters,
   Active This Week. Names WRAP rather than truncate: a half-shown name is
   useless, and these lists are the main discovery surface. */
.sb-person {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--cream-dark);
}
.sb-person:last-of-type { border-bottom: none; }
.sb-person-body { flex: 1; min-width: 0; }
.sb-person-name {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--ink);
  display: block;
  line-height: 1.35;
  /* wrap onto a second line instead of ellipsing */
  overflow-wrap: anywhere;
}
.sb-person-name:hover { color: var(--amber); }
.sb-person-meta {
  font-size: var(--text-sm);
  color: var(--ink-light);
  line-height: 1.45;
  margin-top: .1rem;
  /* the title may still clamp: it's secondary to the name */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.sb-person .connect-btn { flex-shrink: 0; margin-top: .1rem; }

/* ── Eyebrow label (small caps section tag) ── */
.eyebrow {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--amber);
}

/* ── Chip (plain tag, no icon, for lists that don't need card treatment) ── */
.chip {
  display: inline-block;
  padding: .4rem .9rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  color: var(--ink-mid);
  background: var(--white);
}

/* ── Icon (inline SVG, replaces emoji glyphs) ── */
.icon {
  width: 1.35em;
  height: 1.35em;
  display: inline-block;
  vertical-align: -.28em;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-tile {
  width: 40px;
  height: 40px;
  border-radius: var(--radius);
  background: var(--teal-wash);
  color: var(--teal-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.icon-tile .icon { width: 20px; height: 20px; }

/* ── Job Detail Modal ── */
.jd-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20,30,38,.55);
  backdrop-filter: blur(4px);
  z-index: 9999;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
}
.jd-backdrop.open { display: flex; }
.jd-modal {
  position: relative;
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: 2rem 1.75rem 1.75rem;
  max-width: 560px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 24px 60px rgba(0,0,0,.3);
  animation: jdSlideUp .22s cubic-bezier(.16,1,.3,1);
}
@keyframes jdSlideUp { from { transform: translateY(16px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.jd-close {
  position: absolute;
  top: 1rem; right: 1rem;
  background: var(--cream-dark);
  border: none;
  border-radius: 50%;
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  color: var(--ink-mid);
  transition: background .15s, color .15s;
}
.jd-close:hover { background: var(--border); color: var(--ink); }
.jd-close .icon { width: 16px; height: 16px; }
.jd-match {
  display: none;
  border: 1.5px solid;
  border-radius: var(--radius);
  padding: .55rem .85rem;
  font-size: var(--text-sm);
  font-weight: 700;
  margin-bottom: 1rem;
}
.jd-match-reasons {
  margin-top: .35rem;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--ink-mid);
  display: flex;
  flex-direction: column;
  gap: .2rem;
}
.jd-job-title {
  font-size: var(--text-lg);
  margin-bottom: 1rem;
  padding-right: 2rem;
}
.jd-poster {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-bottom: 1.1rem;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--cream-dark);
}
.jd-poster-name { font-weight: 700; color: var(--ink-dark); font-size: var(--text-base); }
.jd-poster-title { font-size: var(--text-sm); color: var(--ink-light); }
.jd-details {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1rem;
  font-size: var(--text-base);
  color: var(--ink-mid);
  margin-bottom: 1.1rem;
}
.jd-description {
  font-size: var(--text-base);
  line-height: 1.7;
  color: var(--ink);
  margin-bottom: 1.1rem;
}
.jd-apply {
  background: var(--teal-wash);
  border-radius: var(--radius);
  padding: .65rem .9rem;
  font-size: var(--text-base);
  color: var(--teal-dark);
  margin-bottom: 1.25rem;
}
.jd-contact-btn { justify-content: center; }

/* ═══════════════════════════════════════════════════════════════
   LANDING PAGE (index.php)
   Marketing scale: type and spacing here run larger than the app.
═══════════════════════════════════════════════════════════════ */

/* Break out of .container to go edge-to-edge.
   Uses 100% + negative padding rather than 100vw, because 100vw includes the
   scrollbar width and would cause a horizontal scroll on desktop. */
.full-bleed {
  width: auto;
  /* exactly cancels .container's 1.25rem padding, no overflow */
  margin-left: -1.25rem;
  margin-right: -1.25rem;
}
@media (min-width: 1281px) {
  .full-bleed {
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
  }
}
/* The landing hero sits flush under the site header, and the final band sits
   flush on the footer, cancel .main-content's vertical padding at both ends. */
.lp-hero { margin-top: -1.5rem; }
.lp-final { margin-bottom: -2.5rem; }

/* Scroll-reveal removed. Thirteen blocks sat at opacity:0 waiting on an
   observer, then faded up over 600ms with up to 240ms of stagger — at normal
   scroll speed you hit bands of empty colour, and the "Four rules" heading
   arrived before its four rules did. It read as a page failing to load, which
   is the opposite of what an invite-only network needs to project.

   One orchestrated entrance replaces it, above the fold only, where it cannot
   be mistaken for a loading failure. */
.lp-hero-copy > * { animation: lpRise .5s cubic-bezier(.16,1,.3,1) backwards; }
.lp-hero-copy > :nth-child(1) { animation-delay: 0s; }
.lp-hero-copy > :nth-child(2) { animation-delay: .07s; }
.lp-hero-copy > :nth-child(3) { animation-delay: .14s; }
.lp-hero-copy > :nth-child(4) { animation-delay: .21s; }
@keyframes lpRise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .lp-hero-copy > * { animation: none; } }

.btn-lg {
  padding: var(--space-3) var(--space-5);
  font-size: var(--text-md);
  border-radius: var(--radius);
}

.lp-eyebrow {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--amber-light);
  margin-bottom: var(--space-4);
}
.lp-eyebrow-dark { color: var(--amber); }
/* The hero's only mention of the name below the header */
.lp-eyebrow-name { color: #fff; font-weight: 700; }

.lp-h1 {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-spacing);
  /* Marketing scale, but the measure matters as much as the size. Too narrow
     a max-width stacks this into a tall, cramped column that's hard to read. */
  font-size: clamp(2.25rem, 4vw, 3.5rem);
  font-weight: 700;
  /* Looser leading: 1.08 stacked the lines into a dense, tall block */
  line-height: 1.18;
  color: #fff;
  margin-bottom: var(--space-5);
  /* ~26ch lets this land on two lines instead of a narrow four-line stack */
  max-width: 26ch;
}
.lp-h2 {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-spacing);
  font-size: clamp(2rem, 3.4vw, 3rem);
  line-height: 1.12;
  color: var(--ink);
  margin-bottom: var(--space-4);
  max-width: 20ch;
}
.lp-h2-light { color: #fff; }

.lp-lede {
  font-size: var(--text-md);
  line-height: 1.7;
  color: rgba(255,255,255,.68);
  max-width: 46ch;
  margin-bottom: var(--space-6);
}
.lp-body {
  font-size: var(--text-md);
  line-height: 1.75;
  color: var(--ink-mid);
  max-width: 62ch;
  margin-bottom: var(--space-4);
}
.lp-body-light { color: rgba(255,255,255,.66); }

/* The hero line is the member's complaint in their own words, so it takes
   the voice face (see --font-voice), in italic as a quotation. */
.lp-lede {
  font-family: var(--font-voice);
  font-style: italic;
  font-size: 1.3rem;
  line-height: 1.55;
}
.lp-body-voice {
  font-family: var(--font-voice);
  font-style: italic;
  font-size: 1.35rem;
  line-height: 1.5;
  color: var(--ink);
}

/* ── Left out on purpose ── what other networks have and this one does not.
   On the card surface: it follows "How it works" on the plain ground, and the
   quiet fill was too close to that ground to read as a new section. */
.lp-section-surface { background: var(--white); border-block: 1px solid var(--line); }
.lp-absent { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--space-7); align-items: start; }
.lp-absent-head .lp-h2 { margin-bottom: 0; }
.lp-absent-table-wrap { overflow-x: auto; }
.lp-absent-table { width: 100%; border-collapse: collapse; font-size: var(--text-base); }
.lp-absent-table th {
  text-align: left; font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-light);
  padding: 0 var(--space-5) var(--space-3) 0;
}
.lp-absent-table th:last-child { color: var(--ink); }
.lp-absent-table td { border-top: 1px solid var(--line); padding: var(--space-4) var(--space-5) var(--space-4) 0; vertical-align: top; line-height: 1.5; }
.lp-absent-table td:first-child { width: 40%; color: var(--ink-light); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--ink-light) 55%, transparent); }
.lp-absent-table td:last-child { color: var(--ink); font-weight: 500; padding-left: 1.9rem; text-indent: -1.9rem; }
.lp-absent-table td:last-child .icon { color: var(--amber); width: 1.1em; height: 1.1em; margin-right: .8rem; text-indent: 0; }
@media (max-width: 900px) { .lp-absent { grid-template-columns: 1fr; gap: var(--space-5); } }

.lp-cta-row { display: flex; gap: var(--space-3); flex-wrap: wrap; }
.lp-cta-center { justify-content: center; }

.lp-proof {
  margin-top: var(--space-5);
  font-size: var(--text-sm);
  color: rgba(255,255,255,.55);
}
.lp-proof strong { color: #fff; font-weight: 600; }

/* ── Hero ── */
.lp-hero {
  background: var(--teal);
  padding: clamp(3.5rem, 7vw, 7rem) var(--space-5) clamp(4rem, 8vw, 8rem);
  position: relative;
  overflow: hidden;
}
.lp-hero::after {
  content: '';
  position: absolute;
  width: 900px; height: 900px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(154,123,79,.20) 0%, transparent 65%);
  top: -420px; right: -280px;
  pointer-events: none;
}
.lp-hero-inner {
  /* Same as .lp-wide. At 1280 the hero headline started 50px left of every
     section headline below it — too small a difference to read as a
     deliberate wider opener, big enough to read as a mistake. */
  max-width: 1180px;
  margin: 0 auto;
  display: grid;
  /* Copy column gets the larger share so the headline can breathe */
  grid-template-columns: 1.15fr 1fr;
  gap: clamp(2rem, 4vw, 4rem);
  align-items: center;
  position: relative;
  z-index: 1;
}
@media (max-width: 960px) {
  .lp-hero-inner { grid-template-columns: 1fr; }
  /* The old hero art was a CSS drawing of a browser window, which could not
     survive a single column, so it was hidden below 960 and phone visitors
     got a wall of text. The Kith Path is a circle — it scales down and stacks
     under the copy instead of disappearing. */
  .lp-hero-art { display: block; margin-top: var(--space-6); }
  .lp-h1 { max-width: 100%; }
}

/* ── Product mockup ── layered + angled so it reads as a real object ── */
.lp-hero-art { position: relative; perspective: 1600px; }
.lp-mock {
  background: var(--white);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transform: rotateY(-13deg) rotateX(5deg) rotate(1.2deg);
  transform-origin: center left;
  box-shadow:
    0 2px 6px rgba(0,0,0,.10),
    0 18px 40px rgba(0,0,0,.28),
    0 60px 120px rgba(0,0,0,.34);
  border: 1px solid rgba(255,255,255,.10);
}
.lp-mock-chrome {
  background: #EFEAE1;
  padding: 9px 12px;
  display: flex;
  gap: 6px;
  border-bottom: 1px solid #E0D8C9;
}
.lp-mock-chrome span { width: 9px; height: 9px; border-radius: 50%; background: #CFC5B4; }
.lp-mock-body { padding: 14px; background: var(--cream); }

.lp-mock-topbar {
  background: var(--teal);
  border-radius: var(--radius);
  padding: 9px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.lp-mock-brand {
  font-family: var(--font-display);
  color: #fff;
  font-size: 13px;
}
.lp-mock-navdots { display: flex; gap: 6px; }
.lp-mock-navdots i { width: 22px; height: 5px; border-radius: var(--radius-sm); background: rgba(255,255,255,.28); }

.lp-mock-cols { display: grid; grid-template-columns: 1fr 74px; gap: 10px; }
.lp-mock-feed { display: flex; flex-direction: column; gap: 10px; }

.lp-mock-composer {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.lp-mock-placeholder { font-size: 9.5px; color: var(--ink-light); }
.lp-mock-av { width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0; display: block; }
.lp-av-a { background: linear-gradient(135deg,#1D5F6A,#2A7A87); }
.lp-av-b { background: linear-gradient(135deg,#C8873A,#E0A050); }

.lp-mock-post {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px;
}
.lp-mock-post-head { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.lp-mock-name { width: 68px; height: 6px; border-radius: var(--radius-sm); background: #2B2620; margin-bottom: 4px; }
.lp-mock-sub  { width: 92px; height: 5px; border-radius: var(--radius-sm); background: #CFC5B4; }
.lp-mock-lines { display: flex; flex-direction: column; gap: 5px; }
.lp-mock-lines i { height: 5px; border-radius: var(--radius-sm); background: #DED5C6; display: block; }

.lp-mock-job { border-color: var(--amber); background: var(--amber-wash); }
.lp-mock-jobtag {
  display: inline-block;
  font-size: 7.5px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 6px;
}
.lp-mock-jobtitle { width: 108px; height: 7px; border-radius: var(--radius-sm); background: #2B2620; margin-bottom: 7px; }

.lp-mock-side { display: flex; flex-direction: column; gap: 10px; }
.lp-mock-sidecard {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  height: 82px;
}
.lp-mock-sidecard.short { height: 54px; }

/* Floating match card, sits in front of the mockup for real depth */
.lp-float {
  position: absolute;
  left: -6%;
  bottom: 12%;
  background: #fff;
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  box-shadow: 0 12px 34px rgba(0,0,0,.30), 0 2px 6px rgba(0,0,0,.14);
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  transform: rotate(-2deg);
}
/* The bob is gone. A match score is a data point, not a balloon, and together
   with the logo's six SMIL loops it meant seven animations running forever on
   a page where nothing responded to the reader. */
.lp-float-pct {
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--green);
  line-height: 1;
}
.lp-float-label { font-size: var(--text-sm); color: var(--ink-mid); }

/* ── Sections ── */
.lp-section { padding: clamp(3.5rem, 7vw, 7rem) var(--space-5); }
.lp-section-quiet { background: var(--cream-dark); }
.lp-section-ink { background: var(--teal); }
/* One content column for every section. .lp-narrow was 720px centred inside
   the 1180px one, so the left edge of the text jumped inward 128px and back
   out as you scrolled — 24 / 152 / 24 / 24 / 24 / 152. Prose is constrained
   by measure now, not by a narrower container, so every section starts on the
   same line. .lp-final stays centred: that is a deliberate composition rather
   than an accidental offset. */
.lp-narrow { max-width: 1180px; margin: 0 auto; }
.lp-narrow > *  { max-width: 68ch; }
.lp-wide   { max-width: 1180px; margin: 0 auto; }
.lp-final-inner { max-width: 640px; margin: 0 auto; }

/* ── Section rhythm ──
   Every section carried identical padding, so nothing read as more important
   than anything else — uniform spacing plus an alternating light/dark stripe
   is the most recognisable template pattern there is. Three tiers now: the
   closing owns the most air, supporting sections the least. */
.lp-section-quiet { padding-block: clamp(2.5rem, 4.5vw, 4rem); }
.lp-final         { padding-block: clamp(4.5rem, 8vw, 8rem); }
.lp-section-head { margin-bottom: var(--space-7); }

.lp-grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-5); }
.lp-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-5); }
@media (max-width: 900px) { .lp-grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px) {
  .lp-grid-4, .lp-grid-3 { grid-template-columns: 1fr; }
}

.lp-feature-icon {
  width: 40px; height: 40px;
  color: var(--amber);
  margin-bottom: var(--space-4);
}
.lp-feature-icon .icon { width: 28px; height: 28px; stroke-width: 1.5; }
.lp-feature-title {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-lg);
  color: var(--ink);
  margin-bottom: var(--space-2);
}
.lp-feature-body {
  font-size: var(--text-base);
  line-height: 1.65;
  color: var(--ink-mid);
}

.lp-audience {
  border-top: 2px solid var(--ink);
  padding-top: var(--space-4);
}

/* Charter band */
.lp-charter {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
@media (max-width: 860px) { .lp-charter { grid-template-columns: 1fr; } }
.lp-charter-list { list-style: none; display: flex; flex-direction: column; gap: var(--space-4); }
.lp-charter-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  color: rgba(255,255,255,.82);
  font-size: var(--text-md);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid rgba(255,255,255,.10);
}
.lp-charter-list li:last-child { border-bottom: none; padding-bottom: 0; }
.lp-charter-list .icon { color: var(--amber-light); flex-shrink: 0; width: 18px; height: 18px; margin-top: .25em; }
.lp-link-light {
  color: #fff;
  font-weight: 500;
  font-size: var(--text-base);
  border-bottom: 1px solid rgba(255,255,255,.35);
  padding-bottom: 2px;
}
.lp-link-light:hover { color: var(--amber-light); border-color: var(--amber-light); }

.lp-final { background: var(--cream); text-align: center; }
.lp-final-inner .lp-h2, .lp-final-inner .lp-body { margin-left: auto; margin-right: auto; }
.lp-final-inner .lp-h2 { max-width: 100%; }
.lp-final-inner .lp-body { margin-bottom: var(--space-6); }

/* ── Utility ── */
.text-muted  { color: var(--ink-light); }
.text-teal   { color: var(--teal-ink); }
.text-amber  { color: var(--amber); }
.text-center { text-align: center; }
.text-right  { text-align: right; }
.mt-1 { margin-top: .5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: .5rem; }
.mb-2 { margin-bottom: 1rem; }
.flex { display: flex; }
.gap-1 { gap: .5rem; }
.gap-2 { gap: 1rem; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.w-full { width: 100%; }
.hidden { display: none; }

/* ── Mobility score: stoplight ring, used on the profile page and in
   recruiter-facing candidate lists (recruiter.php, match.php). Colour is
   set per tier here, not inline, so it tracks theme changes like everything
   else on the page. ── */
.mobility-ring-track { stroke: var(--line); }
.mobility-ring-high  .mobility-ring-fill { stroke: var(--green); }
.mobility-ring-high  .mobility-ring-text { fill: var(--green); }
.mobility-ring-medium .mobility-ring-fill { stroke: var(--caution); }
.mobility-ring-medium .mobility-ring-text { fill: var(--caution); }
.mobility-ring-low   .mobility-ring-fill { stroke: var(--red); }
.mobility-ring-low   .mobility-ring-text { fill: var(--red); }
.mobility-ring-text  { font-family: var(--font-ui); }

.mobility-score-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .8rem 1.1rem;
  margin-top: .85rem;
}
.mobility-score-card .mobility-ring { flex-shrink: 0; }
.mobility-score-eyebrow {
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-light);
}
.mobility-score-tier { font-size: var(--text-md); font-weight: 700; margin-top: .1rem; }
.mobility-tier-high   .mobility-score-tier { color: var(--green); }
.mobility-tier-medium .mobility-score-tier { color: var(--caution); }
.mobility-tier-low    .mobility-score-tier { color: var(--red); }
.mobility-score-note { font-size: var(--text-sm); color: var(--ink-light); margin-top: .1rem; }

/* Compact inline version for candidate-list rows (recruiter.php, match.php) */
.mobility-inline {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mid);
}
.mobility-inline .mobility-ring { flex-shrink: 0; }

/* ── Career-mobility survey ── */
.mobility-intro {
  display: flex;
  align-items: flex-start;
  gap: .85rem;
  background: var(--teal-wash);
  border: 1px solid var(--teal-light);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  margin-bottom: 1.5rem;
  font-size: var(--text-base);
  line-height: 1.55;
  color: var(--ink);
}
.mobility-intro-icon {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--white);
  color: var(--teal-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-base);
}
.mobility-form { padding: var(--space-5) var(--space-6); }
.mobility-q { padding: 1.4rem 0; border-bottom: 1px solid var(--line); }
.mobility-q:first-child { padding-top: 0; }
.mobility-q-head { display: flex; align-items: flex-start; gap: .85rem; }
.mobility-q-num {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--teal-wash);
  color: var(--teal-ink);
  font-weight: 700;
  font-size: var(--text-sm);
  font-family: var(--font-display);
  display: flex;
  align-items: center;
  justify-content: center;
}
.mobility-q-label {
  font-weight: 600;
  font-size: var(--text-base);
  color: var(--ink);
  line-height: 1.4;
  padding-top: .1rem;
}
.mobility-q-hint {
  font-size: var(--text-sm);
  color: var(--ink-light);
  margin-top: .25rem;
  padding-left: calc(26px + .85rem);
}
.mobility-choices {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  margin-top: .85rem;
  padding-left: calc(26px + .85rem);
}
.mobility-choices-row { flex-direction: row; flex-wrap: wrap; }
.mobility-choices-row .mobility-choice { flex: 1 1 160px; }
.mobility-choice {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .65rem .9rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: var(--text-base);
  color: var(--ink);
  transition: border-color .15s, background .15s;
}
.mobility-choice:hover { border-color: var(--teal-light); background: var(--cream-dark); }
.mobility-choice:has(input:checked) { border-color: var(--teal-ink); background: var(--teal-wash); font-weight: 600; }
.mobility-choice input { width: 17px; height: 17px; accent-color: var(--teal-ink); flex-shrink: 0; }
@media (max-width: 560px) {
  .mobility-form { padding: var(--space-4); }
  .mobility-choices, .mobility-q-hint { padding-left: 0; }
}


/* ── Focus ────────────────────────────────────────────────────────────────
   There was previously no :focus-visible rule anywhere in this stylesheet,
   so keyboard users got the browser default — which on the ink surfaces is
   effectively nothing. Placed last so it wins over the handful of
   `outline: none` declarations on :focus further up; those still apply to
   mouse focus, which is what they were for.

   :focus-visible rather than :focus, so a clicked button does not sprout a
   ring but a tabbed one does. */
:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible,
.composer textarea:focus-visible,
.form-control:focus-visible,
.header-search input:focus-visible,
.btn:focus-visible,
.nav-link:focus-visible,
.post-action-btn:focus-visible,
.thread-item:focus-visible,
.sponsor-cta:focus-visible,
.sponsor-hide-btn:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

/* The paper-side accent vanishes on the dark surfaces, so those take the
   ink-side half of the pair instead. */
.site-header :focus-visible,
.lp-hero :focus-visible,
.lp-section-ink :focus-visible,
.gate-panel-brand :focus-visible,
.site-footer :focus-visible,
footer :focus-visible {
  outline-color: var(--amber-light);
}

/* Skipped links and icon-only controls need the ring to hug the shape. */
.nav-avatar:focus-visible,
.logo-icon:focus-visible,
.site-logo:focus-visible { outline-offset: 3px; border-radius: var(--radius); }

/* The vouch count on a suggestion. Quiet — it is a reason, not a badge. */
.sb-person-mutual { color: var(--ink-mid); font-weight: 600; }

/* ── Composer modes ──
   The two things a member can create, sitting together as peers. A job is a
   structured record rather than a paragraph, so that option leads to its own
   form instead of changing this textarea. */
.composer-modes {
  display: flex;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}
.composer-mode {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-light);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .15s ease-out, border-color .15s ease-out;
}
.composer-mode:hover { color: var(--ink); }
.composer-mode.is-active {
  color: var(--ink);
  /* One of the few places the accent marks state rather than decorating. It is
     also how a theme swap becomes visible on the page a member spends their
     time on, rather than only on the landing page. */
  border-bottom-color: var(--amber);
}
.composer-mode .icon { width: 15px; height: 15px; flex-shrink: 0; }

/* Why a job fits, shown to the member instead of a score. Quiet: it is
   context for the post below it, not a verdict on the reader. */
.job-match-why {
  margin-left: auto;
  font-size: var(--text-sm);
  color: var(--ink-mid);
}

/* ── The Kith Path at figure scale ────────────────────────────────────────
   The hero uses this mark large, animated and labelled. These are the same
   drawing at a smaller volume, in the three places the vouching chain is
   actually the subject: an empty state, a loading moment, a quiet divider.
   It is deliberately not a background texture — a motif everywhere is
   wallpaper, which is the opposite of considered. */
.kith-figure { display: block; }
/* Weights tuned against a white card: at .22/.3 the figure read as a smudge
   rather than a mark — the same mistake the logo badge made against the
   header. Quiet is the goal, invisible is not. */
.kf-route { fill: none; stroke: currentColor; stroke-opacity: .5; stroke-width: .9;
            stroke-linecap: round; stroke-linejoin: round; }
.kf-node  { fill: currentColor; fill-opacity: .8; }

/* Empty: the nodes are there but nothing is travelling between them yet. */
.kf-empty { color: var(--ink-light); }

/* Loading: an unlit route with the traveller going round it. */
.kf-loading { color: var(--ink-light); }
.kf-loading .kf-travel {
  fill: var(--amber);
  offset-path: path("M30,14 C42,12 52,20 49,30 C54,42 46,50 38,49 C26,54 14,48 16,36 C10,22 18,10 30,14 Z");
  offset-rotate: 0deg;
  animation: kfTravel 2.4s linear infinite;
}
.kf-loading .kf-t2 { opacity: .4;  animation-delay: -.09s; }
.kf-loading .kf-t3 { opacity: .16; animation-delay: -.18s; }
@keyframes kfTravel { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@media (prefers-reduced-motion: reduce) {
  .kf-loading .kf-travel { animation: none; offset-distance: 0%; }
  .kf-loading .kf-t2, .kf-loading .kf-t3 { display: none; }
}

.kith-empty {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-5);
  text-align: center;
}
.kith-empty .kith-figure { width: 56px; height: 56px; opacity: .75; }
.kith-empty-line {
  margin: 0; max-width: 38ch;
  font-size: var(--text-base); line-height: 1.6; color: var(--ink-mid);
}

/* ── How two members are connected ────────────────────────────────────────
   The real mark, not an abstraction of it: the full loop drawn faintly (the
   network) with the segments you actually travel lit on top (your chain).
   An earlier pass used a generic open curve — defensible in theory, but it
   did not read as Kithby, which is the entire point of using it here. */
.conn-path { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); text-align: center; }
.cp-stage  { position: relative; width: 340px; height: 198px; max-width: 100%; }
/* Scoped to .cp-stage on purpose. Unscoped, an absolutely-positioned figure
   with no positioned ancestor escapes to the viewport — which is exactly what
   happened when this CSS shipped ahead of the PHP that renders .cp-stage, and
   three loose circles landed all over the page. Scoped, a version mismatch
   degrades to a plain inline figure instead. */
.cp-stage > .cp-figure { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.cp-figure { max-width: 100%; height: auto; overflow: visible; }

.cp-loop { fill: none; stroke: var(--ink-light); stroke-opacity: .28; stroke-width: .55;
           stroke-linecap: round; stroke-linejoin: round; }
.cp-walk { fill: none; stroke: var(--ink); stroke-opacity: .85; stroke-width: .85;
           stroke-linecap: round; stroke-linejoin: round; }

.cp-node { fill: var(--white); stroke: var(--ink); stroke-width: .7; }
.cp-me   { fill: var(--amber); stroke: var(--amber); }
.cp-them { fill: var(--ink);   stroke: var(--ink); }
.cp-off  { fill: var(--cream-dark); stroke: var(--ink-light); stroke-width: .5; stroke-opacity: .5; }

.cp-label {
  position: absolute;
  font-family: var(--font-display);
  font-size: var(--text-sm); font-weight: 600;
  color: var(--ink-mid); white-space: nowrap;
}
.cp-label.is-me { color: var(--amber); }
.cp-label.is-link {
  color: var(--ink); text-decoration: none;
  border-bottom: 1px solid var(--line);
  transition: border-color .15s ease-out;
}
.cp-label.is-link:hover { border-bottom-color: var(--ink); }
.cp-label.is-link:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
/* The first broker does something different from the other labels: it opens
   the introduction request rather than a profile. There is only room for a
   first name out here, so the accent underline carries the difference and the
   title attribute says it in words. */
.cp-label.is-intro {
  border-bottom: 1.5px solid var(--amber);
  border-bottom-style: dashed;
}
.cp-label.is-intro:hover { border-bottom-color: var(--amber); border-bottom-style: solid; }

.conn-path-line { margin: 0; font-size: var(--text-base); color: var(--ink-mid); }
.conn-path-line strong { color: var(--ink); font-weight: 600; }
.conn-path-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; justify-content: center; }

@media (max-width: 420px) {
  .cp-stage { width: 300px; height: 186px; }
  .cp-label { font-size: var(--text-xs); }
}

/* The banner on the composer when the message is an introduction request. */
.intro-ref {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--text-sm); color: var(--ink-mid);
  background: var(--cream-dark); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
}
.intro-ref strong { color: var(--ink); font-weight: 600; }
.intro-ref span { color: var(--ink-light); }

/* ── Hero: the Kith Path at scale ─────────────────────────────────────────
   Replaces a CSS drawing of a browser window filled with empty grey bars —
   the universal shorthand for "content does not exist yet". This is the
   mark carrying the argument instead: four members, a closed loop, and a
   traveller lighting each one as it reaches them, so the vouching chain
   reads in order rather than as four labels at once.

   The loop is closed on purpose. Invite-only means the chain has no start
   and no end, and the shape says that before any copy does. */
.kp-stage {
  position: relative;
  width: 100%;
  max-width: 520px;
  aspect-ratio: 1;
  margin-inline: auto;
}
.kp-figure { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.kp-route  { fill: none; stroke: #FBFAF7; stroke-opacity: .26; stroke-width: .5;
             stroke-linecap: round; stroke-linejoin: round; }
.kp-node   { fill: #FBFAF7; fill-opacity: .55; }

.kp-travel {
  fill: var(--amber-light);
  offset-path: path("M30,14 C42,12 52,20 49,30 C54,42 46,50 38,49 C26,54 14,48 16,36 C10,22 18,10 30,14 Z");
  offset-rotate: 0deg;
  animation: kpTravel 16s linear infinite;
}
/* Tight enough to read as one trail. At -.45s/-.9s on a 16s loop the
   spacing was ~30px on screen and they read as three separate dots. */
.kp-t2 { opacity: .42; animation-delay: -.22s; }
.kp-t3 { opacity: .18; animation-delay: -.44s; }
@keyframes kpTravel { from { offset-distance: 0%; } to { offset-distance: 100%; } }

/* each node and its label brighten as the traveller arrives */
.kp-node, .kp-label { animation: kpLit 16s linear infinite; }
@keyframes kpLit { 0% { opacity: 1 } 14% { opacity: .38 } 86% { opacity: .38 } 100% { opacity: 1 } }
.kp-n1, .kp-l1 { animation-delay: 0s; }
.kp-n2, .kp-l2 { animation-delay: -12s; }
.kp-n3, .kp-l3 { animation-delay: -8s; }
.kp-n4, .kp-l4 { animation-delay: -4s; }

.kp-label {
  position: absolute; opacity: .38;
  display: flex; flex-direction: column; gap: 2px;
  white-space: nowrap;
}
.kp-label b {
  font-family: var(--font-display);
  font-size: var(--text-base); font-weight: 600; color: #FBFAF7;
}
.kp-label i { font-style: normal; font-size: var(--text-sm); color: rgba(251,250,247,.52); }

.kp-l1 { left: 46.9%; top: 21.9%; transform: translate(-50%, calc(-100% - 14px)); align-items: center; }
.kp-l2 { left: 76.6%; top: 46.9%; transform: translate(16px, -50%); }
.kp-l3 { left: 59.4%; top: 76.6%; transform: translate(-50%, 16px); align-items: center; }
.kp-l4 { left: 25%;   top: 56.3%; transform: translate(calc(-100% - 16px), -50%); align-items: flex-end; }

/* The caption has to sit INSIDE the loop with air around it. The clear space
   inside the route is roughly a circle of radius 24% of the stage, centred on
   the four nodes' average (52%, 50.4%), not on the stage's own centre. At 62%
   wide the text box's corners reached the line. 34% keeps every corner well
   inside that circle at every stage size the two-column layout uses. */
.kp-caption {
  position: absolute; left: 52%; top: 50.4%; transform: translate(-50%, -50%);
  width: 34%; text-align: center;
  display: flex; flex-direction: column; gap: 4px;
  font-size: var(--text-sm); line-height: 1.45; color: rgba(251,250,247,.58);
}
.kp-caption em {
  font-family: var(--font-display); font-style: normal;
  font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: rgba(251,250,247,.34);
}

/* Two columns: the right-hand label ("Director of Engineering") sits 16px
   past a node at 76.6% of the stage and is ~190px long, so with the stage
   filling its column it ran past the hero's edge and was clipped at most
   desktop widths. Narrow the stage a little, sit it left in its column, and
   let that one label take two lines. */
@media (min-width: 961px) {
  .lp-hero-art .kp-stage { width: 88%; margin-inline: 0 auto; }
  .kp-l2 { white-space: normal; }
  .kp-l2 b { max-width: 7.5em; line-height: 1.25; }
}
/* Below the two-column breakpoint the labels no longer have room beside the
   loop, so the mark carries it alone rather than colliding with itself. */
@media (max-width: 960px) { .kp-stage { max-width: 400px; } }
@media (max-width: 860px) { .kp-stage { max-width: 360px; } }
@media (max-width: 620px) {
  /* Small stage: the eyebrow goes so the one line fits inside the loop */
  .kp-stage { max-width: 280px; }
  .kp-label { display: none; }
  .kp-caption { width: 38%; font-size: 13px; }
  .kp-caption em { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .kp-travel { animation: none; offset-distance: 0%; }
  .kp-t2, .kp-t3 { display: none; }
  .kp-node, .kp-label { animation: none; opacity: 1; fill-opacity: .8; }
}

/* ── Job responses ──
   Who asked about one role. Built on messages.job_id, which the product
   already records; views and click-throughs are absent because jobs render
   inline in the feed and have no page of their own to count. */
.job-sum-head { display: flex; align-items: baseline; gap: var(--space-3); margin-bottom: var(--space-4); }
.job-sum-h1 { font-family: var(--font-display); font-size: var(--text-lg); font-weight: 600; margin: 0; }
.job-sum-unread {
  font-size: var(--text-xs); font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--amber-on-wash); background: var(--amber-wash);
  padding: 3px 9px; border-radius: var(--radius-pill);
}
.job-sum-outbound { font-size: var(--text-sm); color: var(--ink-light); margin: calc(-1 * var(--space-2)) 0 var(--space-4); }
.job-sum-title { font-family: var(--font-display); font-size: var(--text-md); font-weight: 600; margin: 0 0 var(--space-3); }
.job-sum-meta { display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--text-sm); color: var(--ink-mid); }
.job-sum-meta span { display: flex; align-items: center; gap: var(--space-2); }
.job-sum-posted { font-size: var(--text-sm); color: var(--ink-light); margin: var(--space-3) 0 0; }

.job-sum-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.job-sum-row {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4) 0; border-top: 1px solid var(--line);
}
.job-sum-row:first-child { border-top: none; padding-top: 0; }
.job-sum-row.is-unread .job-sum-name { color: var(--ink); }
.job-sum-body { flex: 1; min-width: 0; }
.job-sum-who { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.job-sum-name { font-family: var(--font-display); font-weight: 600; font-size: var(--text-base); color: var(--ink); text-decoration: none; }
.job-sum-name:hover { text-decoration: underline; }
.job-sum-when { font-size: var(--text-sm); color: var(--ink-light); margin-left: auto; white-space: nowrap; }
.job-sum-role { font-size: var(--text-sm); color: var(--ink-light); margin-top: 2px; }
.job-sum-msg  { font-size: var(--text-sm); color: var(--ink-mid); line-height: 1.6; margin: var(--space-2) 0 0; }
.job-sum-fit  { font-size: var(--text-sm); color: var(--ink-light); margin: var(--space-2) 0 0; }
.job-sum-reply { flex-shrink: 0; }
@media (max-width: 620px) {
  .job-sum-row { flex-wrap: wrap; }
  .job-sum-reply { margin-left: 52px; }
}

/* Three numbers a poster can act on: who opened it, who wrote, and the gap
   between them. The gap is the interesting one — interest that did not
   convert — so it is the one that carries colour. */
/* A three-column grid, not a wrapping flex row: wrapping left the third stat
   alone on its own line still carrying the divider that tied it to the two
   above it, which read as a rendering fault. */
.job-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.job-stat { display: flex; flex-direction: column; gap: 2px; }
.job-stat strong {
  font-family: var(--font-display); font-size: var(--text-xl); font-weight: 700;
  color: var(--ink); line-height: 1; font-variant-numeric: tabular-nums;
}
.job-stat span { font-size: var(--text-sm); color: var(--ink-light); }
/* The drop-off, not an achievement: --amber resolves to the accent green, which
   made "read it and moved on" read as the good number in the row. */
.job-stat.is-quiet strong { color: var(--ink-light); }
/* The two derived figures sit under the headline count, so a shared rule marks
   them as a breakdown of it rather than three unrelated numbers. */
.job-stat-of { padding-left: var(--space-4); border-left: 1px solid var(--line); }
@media (max-width: 560px) {
  .job-stats   { grid-template-columns: 1fr; gap: var(--space-3); }
  .job-stat-of { padding-left: 0; border-left: none; }
}

/* ── A role's own page ────────────────────────────────────────────────────
   Jobs used to live only in a feed modal. Now they have an address, so the
   detail needs the weight of a page rather than a card inside a card. */
.job-hero-title {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 var(--space-3);
}
.job-hero-meta {
  display: flex; flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-size: var(--text-sm); color: var(--ink-mid);
}
.job-hero-meta span { display: inline-flex; align-items: center; gap: var(--space-2); }
.job-hero-body {
  font-size: var(--text-base); line-height: 1.7; color: var(--ink);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
  white-space: pre-wrap;
}
.job-hero-apply {
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--amber-wash);
  border-radius: var(--radius);
  font-size: var(--text-sm); color: var(--ink);
}
.job-poster { display: flex; align-items: center; gap: var(--space-3); }
.job-poster-name {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--text-base); color: var(--ink); text-decoration: none;
}
.job-poster-name:hover { text-decoration: underline; }
.job-poster-title { font-size: var(--text-sm); color: var(--ink-light); margin-top: 2px; }

/* ── Currently hiring, on a profile ───────────────────────────────────────
   Built from the member's live job posts, so every role here is a link. */
.hiring-card { background: var(--amber-wash); border-color: var(--amber); }
.hiring-card-title {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-sm); font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--amber);
  margin-bottom: var(--space-3);
}
.hiring-list { list-style: none; margin: 0; padding: 0; }
.hiring-item { padding: var(--space-3) 0; border-top: 1px solid rgba(27,122,69,.22); }
.hiring-item:first-child { border-top: none; padding-top: 0; }
.hiring-item:last-child { padding-bottom: 0; }
.hiring-role, .hiring-role-plain {
  display: block;
  font-size: var(--text-base); font-weight: 700; color: var(--ink);
  text-decoration: none; line-height: 1.3;
}
.hiring-role:hover { text-decoration: underline; }
.hiring-meta { font-size: var(--text-sm); color: var(--ink-mid); margin-top: 2px; }
.hiring-summary { font-size: var(--text-sm); color: var(--ink); line-height: 1.55; margin-top: var(--space-2); }
.hiring-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-2); }
.hiring-actions a {
  font-size: var(--text-sm); font-weight: 600;
  color: var(--amber); text-decoration: none;
}
.hiring-actions a:hover { text-decoration: underline; }

/* ── How well a role fits you ─────────────────────────────────────────────
   Three tiers, no number. Only roles that cleared the feed bar carry one, so
   the weakest badge a member ever sees is Partial, never a verdict on them. */
.match-badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .01em;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  white-space: nowrap;
  cursor: default;
}
.match-badge .icon { width: 11px; height: 11px; }
.match-badge-strong { background: var(--green-wash); color: var(--green);      border-color: var(--green); }
.match-badge-good   { background: var(--green-wash); color: var(--green);      border-color: transparent; }
.match-badge-fair   { background: var(--cream-dark); color: var(--ink-light);  border-color: transparent; }

.job-hero-match {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
  font-size: var(--text-sm); color: var(--ink-light);
}

/* The modal says the same thing the card does. */
.jd-match-strong { color: var(--green); border-color: var(--green); }
.jd-match-good   { color: var(--green); border-color: var(--green); }
.jd-match-fair   { color: var(--ink-light); border-color: var(--border); }

/* Why the feed is shorter than it would otherwise be. */
.feed-filter-note {
  font-size: var(--text-sm);
  color: var(--ink-light);
  line-height: 1.6;
  margin: var(--space-5) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.feed-filter-note a { color: var(--ink-mid); font-weight: 600; }

/* ── Who said what ────────────────────────────────────────────────────────
   Side and colour alone made the reader remember which side was theirs, and a
   long exchange stacked into one undifferentiated block. Consecutive messages
   from one person are grouped into a turn: name and avatar once at the top,
   timestamp once at the bottom, and real space between turns. The spacing is
   what makes the shape of a conversation readable at a glance. */
.msg-scroll {
  flex: 1; overflow-y: auto;
  padding: var(--space-4) var(--space-5);
  display: flex; flex-direction: column;
  gap: 2px;
}
.msg-row { display: flex; align-items: flex-start; gap: var(--space-2); }
.msg-row.is-turn-start { margin-top: var(--space-4); }
.msg-row.is-turn-start:first-child { margin-top: 0; }
.msg-row-out { flex-direction: row-reverse; }

/* Holds the avatar on a turn's first message and the same space on the rest,
   so every bubble in a turn keeps one edge. */
.msg-gutter { width: 30px; flex-shrink: 0; }
.msg-gutter a { display: block; line-height: 0; }

.msg-col {
  display: flex; flex-direction: column;
  min-width: 0; max-width: calc(100% - 38px);
}
.msg-row-in  .msg-col { align-items: flex-start; }
.msg-row-out .msg-col { align-items: flex-end; }

.msg-author {
  font-family: var(--font-display);
  font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .02em; color: var(--ink-mid);
  margin-bottom: 3px;
}
/* The bubble no longer positions itself: the column does. */
.msg-col .message-bubble { margin: 0; max-width: min(100%, 46ch); }
.msg-col .bubble-sent { margin-left: 0; }
.msg-time { font-size: var(--text-xs); color: var(--ink-light); margin-top: 3px; }
.msg-note { font-size: var(--text-xs); color: var(--amber); margin-bottom: 3px; }

/* The role a message is about, as a link rather than a dead label. */
.msg-job-ref {
  display: inline-flex; align-items: center; gap: var(--space-2);
  flex-wrap: wrap;
  font-size: var(--text-xs); color: var(--ink-mid);
  background: var(--cream-dark);
  border-radius: var(--radius-pill);
  padding: 3px 10px; margin-bottom: 4px;
  text-decoration: none;
}
.msg-job-ref:hover { color: var(--ink); }
.msg-job-ref strong { color: var(--ink); font-weight: 600; }

/* CR-69a. What sits where the fit badge would have been when the member has
   never stated what they are looking for.

   Deliberately not a card. This is one quiet line under the role pill inside
   somebody else's message thread, and a bordered panel there would read as
   the recruiter having said it. The brass rule is the same device the gap card
   uses on the feed, cut to a hairline for the smaller context. */
.msg-fit-blank {
  max-width: 46ch;
  margin: 0 0 6px;
  padding-left: var(--space-3);
  border-left: 2px solid var(--amber-light);
  font-size: var(--text-xs);
  line-height: 1.55;
  color: var(--ink-light);
}
.msg-fit-blank a {
  color: var(--teal);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.msg-fit-blank a:hover { text-decoration: underline; }

/* ── CR-68. Strong fits who are not looking ──────────────────────────────
   Visually subordinate on purpose. These people did not ask to hear from a
   recruiter, and the styling has to say "also worth knowing" rather than
   compete with the ranked list above it. Hence the rule across the top, the
   quieter surface, and rows instead of cards. */
.match-quiet {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 2px solid var(--line);
}
.match-quiet-head {
  display: flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-md);
  color: var(--ink);
  margin: 0 0 var(--space-2);
}
.match-quiet-count {
  font-family: var(--font-body);
  font-size: var(--text-xs); font-weight: 600;
  color: var(--ink-light);
  background: var(--cream-dark);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
}
.match-quiet-note {
  max-width: 62ch;
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
  line-height: 1.65;
  color: var(--ink-light);
}
.match-quiet-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line);
}
.match-quiet-who { flex: 1 1 auto; min-width: 0; }
.match-quiet-who a {
  display: block;
  font-weight: 600; font-size: var(--text-sm);
  color: var(--ink); text-decoration: none;
}
.match-quiet-who a:hover { text-decoration: underline; }
.match-quiet-role {
  display: block;
  font-size: var(--text-xs); color: var(--ink-light);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.match-quiet-reach {
  display: inline-flex; align-items: center; gap: 4px;
  margin-top: 2px;
  font-size: var(--text-xs); color: var(--teal);
}
.match-quiet-fit {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-md);
  color: var(--ink-mid);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
/* [9]. The number carries the weight; the word next to it says which of the
   two questions it answers. Small and quiet on purpose: it is a label, not a
   second figure competing with the first. */
.match-quiet-fit-label {
  display: block;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: var(--text-xs);
  line-height: 1.15;
  color: var(--ink-light);
}

/* Stated once above the list rather than repeated on every card. */
.match-legend {
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--ink-mid);
  background: var(--cream, #FAFAF9);
  border-left: 3px solid var(--amber);
  border-radius: 0 6px 6px 0;
  padding: var(--space-3) var(--space-4);
  margin: 0 0 var(--space-4);
}
.match-legend strong { color: var(--ink); font-weight: 700; }

.match-quiet-act { display: flex; gap: var(--space-2); flex-shrink: 0; }

@media (max-width: 640px) {
  .match-quiet-row { flex-wrap: wrap; }
  .match-quiet-act { width: 100%; }
}

/* ── The page hero ────────────────────────────────────────────────────────
   One band, four pages. About had this and Terms, Privacy, Support and
   Advertise each opened with a pale centred card instead, so the secondary
   pages read as loose documents rather than parts of a site. Lifted out of
   about.php's inline <style> so there is one definition to change.

   The route runs behind it, slowly. See kith_page_hero(). */
.page-hero {
  background: var(--teal);
  padding: 4rem 1.25rem;
  /* The watermark is deliberately larger than the band, so it is anchored and
     clipped here. Without overflow hidden it pushes the page sideways. */
  position: relative;
  overflow: hidden;
}
/* Content above the mark. It is aria-hidden and decorative, and must never be
   able to take a click or a caret. */
.page-hero { margin-bottom: var(--space-6); }
.page-hero-inner { position: relative; max-width: 740px; margin: 0 auto; }
.page-hero .eyebrow { color: var(--amber-light); margin-bottom: 1rem; }
.page-hero h1 {
  font-size: clamp(1.9rem, 4vw, 2.65rem);
  color: #fff;
  line-height: 1.2;
  margin-bottom: 1.25rem;
  text-wrap: balance;
}
.page-hero p {
  font-size: 1rem;
  color: rgba(255,255,255,.62);
  line-height: 1.8;
  max-width: 54ch;
  margin: 0;
}
/* On a phone the band is full width and the headline fills it, so a large loop
   travels straight behind the words. Shrunk into the corner instead. The motion
   may sit beside what you are reading, never underneath it. */
@media (max-width: 640px) {
  .page-hero .kith-wm {
    width: 280px; height: 280px;
    right: -100px !important;
    top: -60px !important;
  }
}

/* ── Open to Opportunities ────────────────────────────────────────────────
   The form was 680px and eleven sections long, a narrow ribbon with empty
   space either side. Two sections were already paired grids and at 680px each
   half came out around 320px, too narrow to read as a pair at all.

   Nothing on this form is prose. A form wants to be scannable, not narrow, and
   a shorter page matters on one people abandon. */
.seeking-form { max-width: 900px; margin: 0 auto; }

/* auto-fit rather than 1fr 1fr: the pairs collapse when they stop fitting
   rather than at a width somebody guessed. */
.seeking-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1rem;
}

/* ── The advertiser page ──────────────────────────────────────────────────
   Two measures, not one. The page was a flat 740px, a prose width applied to a
   page that is mostly two-column grids: each column came out around 350px and
   short paragraphs fragmented into five-word lines. 920px gives those columns
   roughly 440px, which is where they stop breaking up. */
.sponsor-page { max-width: 920px; margin: 0 auto; }

/* The hero is the exception and keeps a narrow measure. A centred headline and
   standfirst set to 920px is genuinely hard to read: the eye loses the start of
   the next line. Narrow for prose, wide for structure. */
.sponsor-hero { position: relative; overflow: hidden; }
.sponsor-hero h1 { max-width: 20ch; margin-left: auto; margin-right: auto; }

@media (max-width: 640px) {
  /* Same rule as the About hero: the motion may sit beside what you are
     reading, never behind it. */
  .sponsor-hero .kith-wm {
    width: 260px; height: 260px;
    right: -100px !important;
    top: -60px !important;
  }
}

/* The word beside the number, inside the panel where there is room for both.
   Quieter than the figure: the number is the claim, the word is the reading. */
.fit-verdict-word {
  margin-left: var(--space-2);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--text-sm);
  color: var(--ink-light);
}

/* CR-78. Why someone wants to connect, on the screen where it is decided. */
.conn-reason {
  margin-top: var(--space-2);
  padding-left: var(--space-3);
  border-left: 2px solid var(--amber-light);
  font-size: var(--text-xs);
  line-height: 1.55;
}
.conn-reason-kind {
  display: block;
  font-weight: 600;
  color: var(--ink-mid);
}
.conn-reason-note { display: block; color: var(--ink-light); }
.conn-reason-none { display: block; color: var(--ink-light); font-style: italic; }

/* CR-69. The reason behind the score, as text rather than a hover tooltip. */
.msg-fit-why {
  max-width: 46ch;
  margin: 0 0 6px;
  font-size: var(--text-xs);
  line-height: 1.5;
  color: var(--ink-light);
}

/* ── The role page, tightened ─────────────────────────────────────────────
   A posting with nothing but a title and one line was leaving the page mostly
   white. The eyebrow gives the hero a constant top line whatever the poster
   filled in, and the rhythm below it is set rather than left to the card. */
.job-hero { padding-bottom: var(--space-5); }
.job-hero-eyebrow {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2) var(--space-3);
  font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-light);
  margin-bottom: var(--space-3);
}
.job-hero-eyebrow > span + span::before {
  content: ""; display: inline-block;
  width: 3px; height: 3px; border-radius: 50%;
  background: var(--ink-light);
  vertical-align: middle;
  margin-right: var(--space-3);
  opacity: .6;
}
.job-hero-status { display: inline-flex; align-items: center; gap: 4px; color: var(--amber); }
.job-hero-status .icon { width: 12px; height: 12px; }

.job-close-link {
  display: inline-block; margin-top: var(--space-3);
  font-size: var(--text-sm); color: var(--ink-light);
  text-decoration: underline; text-underline-offset: 2px;
}
.job-close-link:hover { color: var(--red); }

/* ── Open roles, in the left rail ─────────────────────────────────────────
   Somewhere to go and look, rather than waiting for a role to scroll past. */
.or-list { list-style: none; margin: 0; padding: 0; }
.or-item { padding: var(--space-3) 0; border-top: 1px solid var(--line); }
.or-item:first-child { border-top: none; padding-top: 0; }
.or-item:last-child { padding-bottom: 0; }
.or-title {
  font-family: var(--font-display);
  font-size: var(--text-sm); font-weight: 600;
  color: var(--ink); text-decoration: none;
  line-height: 1.35; display: block;
}
.or-title:hover { text-decoration: underline; }
.or-meta {
  font-size: var(--text-xs); color: var(--ink-light);
  margin-top: 2px; margin-bottom: 4px;
}

/* The recessed panel a role sits in inside a feed card. Was a literal #FAFAF7
   inline, which stayed white on the dark theme while its text went light, so
   the whole posting became unreadable. A token tracks whatever ground the
   theme is using. */
.job-panel {
  background: var(--cream-dark);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-3);
}

/* ═══════════════════════════════════════════════════════════════
   SURFACES, SPENT BY ROLE

   The old system had exactly one surface treatment — white, hairline,
   6px radius, 24px padding — and stamped it on everything: the feed
   posts, the composer, six rail modules, the sponsor. Nine identical
   rounded rectangles in three columns, on a ground 1.5% away from the
   same white. Nothing claimed to matter more than anything else.

   Now there are three materials:
     RAISED    posts and the composer. White on the ground, one shadow.
     FLAT      rail modules. No fill, no border. The ground carries them.
     RECESSED  the sponsor. Inset, so bought space reads as a different
               material from what members wrote.
   ═══════════════════════════════════════════════════════════════ */

/* ── The feed: one column, not a stack of cards ──────────────────────────
   Posts are a continuous surface divided by hairlines. A list of things
   reads as a list; N objects each drawing their own edges reads as debris. */
.feed-list {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgba(30,26,18,.05);
  overflow: hidden;
}
.feed-list > .post {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  margin: 0;
  box-shadow: none;
  padding: var(--space-5);
}
.feed-list > .post:first-child { border-top: 0; }
/* The lift belonged to a free-floating card. Inside a single surface it would
   tear the column apart, so the hover cue becomes a wash instead. */
.feed-list > .post:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--line);
  background: var(--ground);
  background: color-mix(in srgb, var(--ground) 35%, transparent);
}
/* A role still announces itself, but through structure rather than a second
   bordered box drawn inside the first. */
.feed-list > .post.job-post { border-top-width: 2px; border-top-color: var(--action); }
.feed-list > .post.job-post:first-child { border-top: 0; }

/* The role inside a post: no panel. The title can finally take the size it
   deserves, since nothing is boxing it in. */
.job-panel {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin-bottom: var(--space-3);
}
.job-panel > .job-detail-trigger:first-child {
  font-size: var(--text-lg) !important;
  font-weight: 600 !important;
  letter-spacing: -.02em;
  line-height: 1.2;
}

/* ── The composer keeps its own surface, above the list ──────────────── */
.composer {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--hairline), var(--shadow);
  margin-bottom: var(--space-5);
}
.composer:hover { transform: none; box-shadow: var(--hairline), var(--shadow); border-color: var(--line); }

/* ── Rails: flat on the ground ───────────────────────────────────────────
   Six bordered boxes competing with the column you actually read. They are
   now content, told apart by space and a single rule. */
.sidebar-left  > .card:not(.hiring-card):not(.sponsor):not(.charter-card),
.sidebar-right > .card:not(.hiring-card):not(.sponsor) {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}
.sidebar-left  > .card:hover,
.sidebar-right > .card:hover { transform: none; box-shadow: none; border-color: transparent; }
.sidebar-left  > .card + .card,
.sidebar-right > .card + .card {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}
/* A card that kept its own surface brings its own edges, so it does not also
   need the divider that separates the flat ones. */
.sidebar-left  > .card.hiring-card + .card,
.sidebar-right > .card.sponsor + .card,
.sidebar-left  > .card + .card.hiring-card,
.sidebar-right > .card + .card.sponsor { border-top: 0; padding-top: 0; }
/* The card after the charter needs no divider; the charter itself keeps its
   own padding (only the divider goes), or its title sits on its top edge. */
.sidebar-left  > .card.charter-card + .card { border-top: 0; padding-top: 0; }
.sidebar-left  > .card + .card.charter-card { border-top: 1px solid var(--teal); padding-top: var(--space-4); }

/* A module heading was 17px over 15px body with a rule under it: a ratio of
   1.13 and six horizontal lines near the top of the screen. An eyebrow names
   the module without pretending to be a headline. */
.sidebar-left .card-title,
.sidebar-right .card-title {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-light);
  margin-bottom: var(--space-3);
  padding-bottom: 0;
  border-bottom: 0;
}

/* ── The charter: the left rail's one dark card (Option A) ───────────────
   Kept off the flat-rail rule above so it reads as different in kind from the
   lists around it. After the rail rules on purpose: same specificity, source
   order decides. Tokens, so every theme repaints it. */
.sidebar-left > .card.charter-card {
  background: var(--teal);
  border: 1px solid var(--teal);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  box-shadow: none;
}
.sidebar-left .charter-card .card-title { color: var(--amber-light); letter-spacing: .16em; }
.charter-card .charter-stat { color: rgba(255,255,255,.78); }
.charter-card .charter-stat strong { color: #fff; }
.charter-card .charter-link { color: var(--amber-light); border-bottom-color: rgba(255,255,255,.25); }
.charter-card .charter-link:hover { color: #fff; border-bottom-color: #fff; }

/* ── The sponsor: the one rail module that keeps a surface ───────────────
   Recessed rather than raised, so it reads as bought space without shouting.
   It is paying to be noticed, not to be ignored. */
.sidebar-right > .card.sponsor,
.sponsor {
  background: var(--cream-dark);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-5);
  box-shadow: inset 0 1px 2px rgba(30,26,18,.05);
}

/* ── The profile summary reads as a person, not a widget ─────────────────
   Centred avatar over centred name over centred stats over a full-width
   outlined button is the most template-looking arrangement in the product. */
.profile-mini { text-align: left; }
.profile-mini > div:first-child { margin: 0 0 var(--space-3) !important; }
.profile-mini h3 { margin: 0; }
.profile-mini .profile-stats {
  justify-content: flex-start;
  gap: var(--space-5);
  border-top: 0;
  padding-top: var(--space-3);
  margin-top: var(--space-3);
}
.profile-mini .stat-item { flex: 0 0 auto; display: flex; align-items: baseline; gap: var(--space-2); }
.profile-mini .stat-num { font-size: var(--text-base); font-weight: 600; }
.profile-mini .stat-label { font-size: var(--text-sm); }
.profile-mini .btn.w-full { width: auto; justify-content: flex-start; }

/* ── Hover lift only where something is actually clickable ───────────────
   .card had transform + shadow on hover, so the charter panel promised to be
   interactive and then wasn't.
   A card now carries a resting shadow (design rule 3), so hover must HOLD that
   rather than clear it — "box-shadow: none" here flattened every card the
   moment a mouse crossed it. */
.card:hover { transform: none; box-shadow: var(--hairline), var(--shadow); }
a.card:hover, .card.is-interactive:hover {
  transform: translateY(-3px);
  box-shadow: var(--hairline), var(--shadow-md);
  border-color: #C9CFC6;
}

/* The typeface is now half of what an accent study is proposing, so the lab
   names it beside the swatch rather than leaving it to be discovered. */
.theme-opt-face {
  font-size: var(--text-xs);
  color: var(--ink-light);
  white-space: nowrap;
}
.theme-opt-face + .theme-opt-check { margin-left: var(--space-2); }

/* What a match score is not telling you. Caution, not alarm: this is a note
   about the confidence of a number, never a judgement on the person. */
.match-caveat {
  display: flex; align-items: flex-start; gap: var(--space-2);
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--caution-wash);
  border-left: 2px solid var(--caution);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--text-xs);
  line-height: 1.55;
  color: var(--ink-mid);
}
.match-caveat .icon { width: 13px; height: 13px; flex-shrink: 0; color: var(--caution); margin-top: 2px; }
.match-caveat > div > div + div { margin-top: 3px; }

/* Why a candidate scored what they scored. Reads as a short note, not a data
   dump: the label is the quiet half and the sentence carries the meaning. */
.match-why {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--ink-mid);
}
.match-why-head {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--ink);
  margin-bottom: var(--space-2);
}
.match-why-line + .match-why-line { margin-top: var(--space-2); }
.match-why-lab {
  display: block;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-light);
}

/* How to reach a candidate. A warm route is the point of the whole network, so
   it is stated before the score is justified, not buried under it. */
.match-reach {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--ink-light);
}
.match-reach .icon { width: 13px; height: 13px; flex-shrink: 0; align-self: center; }
.match-reach.is-warm { color: var(--ink-mid); }
.match-reach.is-warm .icon { color: var(--amber); }
.match-reach strong { color: var(--ink); font-weight: 600; }
.match-reach a { font-weight: 600; color: var(--amber); text-decoration: none; }
.match-reach a:hover { text-decoration: underline; }

.match-warm-toggle {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-4);
  font-size: var(--text-sm); color: var(--ink-mid);
  cursor: pointer;
}

/* The terms attached to posting a role. Stated where the permission is granted,
   not in a policy page, because an expiring permission nobody was told about is
   a trap rather than a feature. */
.hiring-terms {
  margin-bottom: var(--space-5);
  padding: var(--space-4);
  background: var(--caution-wash);
  border-left: 2px solid var(--caution);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--ink-mid);
}
.hiring-terms strong { color: var(--ink); }

/* ═══════════════════════════════════════════════════════════════
   THE ROUTE AT DISPLAY SCALE

   The device already carried the argument and was doing one job. These are
   the other two: a banner that replaces photography, and a gauge that
   replaces three unrelated ways of drawing a number about a person.
   ═══════════════════════════════════════════════════════════════ */

/* A dark band with the route cropped behind it. What a page wears instead of
   a stock photograph. */
.k-banner {
  position: relative;
  overflow: hidden;
  background: var(--teal);
  color: var(--cream);
  padding: var(--space-6) var(--space-7);
  border-radius: var(--radius) var(--radius) 0 0;
}
.k-banner > * { position: relative; }
.kith-wm { position: absolute; color: var(--cream); pointer-events: none; }

@keyframes wmTravel { from { offset-distance: 0%; } to { offset-distance: 100%; } }
.wm-t {
  fill: var(--amber-light);
  offset-path: path('M30,14 C42,12 52,20 49,30 C54,42 46,50 38,49 C26,54 14,48 16,36 C10,22 18,10 30,14 Z');
  offset-rotate: 0deg;
  animation: wmTravel 26s linear infinite;
  opacity: .62;
}
.wm-2 { animation-delay: -1.1s; opacity: .34; }
.wm-3 { animation-delay: -2.2s; opacity: .15; }
/* Parked partway along rather than at the start, so a reader with motion off
   still gets a composed graphic instead of a dot sitting on the top node. */
@media (prefers-reduced-motion: reduce) {
  .wm-t { animation: none; offset-distance: 22%; }
  .wm-2, .wm-3 { display: none; }
}

.k-banner-eyebrow {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-xs); font-weight: 600;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--amber-light);
  margin-bottom: var(--space-3);
}
.k-banner-eyebrow .muted { color: rgba(237,241,238,.42); letter-spacing: .02em; text-transform: none; font-weight: 400; font-size: var(--text-sm); }
.k-banner h1, .k-banner h2 {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-spacing);
  color: var(--cream);
  margin: 0 0 var(--space-3);
  line-height: 1.06;
}
.k-banner-meta {
  display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5);
  font-size: var(--text-base); color: rgba(237,241,238,.62);
}
.k-banner-meta .pay { color: var(--amber-light); font-weight: 500; }

/* The gauge, and the one line of text that always sits beside it. */
.kith-gauge { color: var(--ink); flex-shrink: 0; }

/* The gauge's two strokes.
   TRACK is the unfilled route. At the old .12 it measured 1.26:1 against a
   light card, which is not a faint line, it is no line — the shape the gauge
   is drawn on could not be read at all. .22 roughly doubles that.
   FILL is the scored portion. It defaults to --amber, which is the accent
   tuned to be legible on PAPER. Any dark surface must override --gauge-fill,
   because --amber on the dark banner measures 1.10:1 in Ink Only and 1.68:1
   in Ivy. --gauge-fill-dark is the ink-tuned counterpart and each theme may
   set its own. */
:root { --gauge-fill-dark: var(--amber-light); }
.kith-gauge-track { stroke: currentColor; stroke-opacity: .22; }
.kith-gauge-fill  { stroke: var(--gauge-fill, var(--amber)); }

/* Every surface where the gauge sits on ink rather than paper. Setting the
   custom property on the container means the gauge itself needs no variant
   class and nothing has to know which context it was rendered into. */
.k-banner,
.fit-corner { --gauge-fill: var(--gauge-fill-dark); }
.k-figure { display: flex; align-items: center; gap: var(--space-3); }
.k-figure-num { font-family: var(--font-display); font-weight: var(--display-weight); font-size: var(--text-lg); line-height: 1; }
.k-figure-lab { font-size: var(--text-sm); color: var(--ink-light); margin-top: 3px; }

/* The career-move figure is the route now, not a donut. The number sits beside
   the gauge rather than inside it: a numeral centred in a 44px loop was
   unreadable, and the route has no middle to put one in. */
.mobility-ring { display: inline-flex; align-items: center; gap: var(--space-2); }
.mobility-ring .kith-gauge { color: var(--ink); }
.mobility-ring-text {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-md);
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.mobility-ring-low  .kith-gauge { color: var(--ink-light); }
.mobility-ring-low  .mobility-ring-text { color: var(--ink-light); }

/* On a profile the banner is a band the identity card sits into, rather than a
   header carrying its own copy. The avatar overlaps it, which is what gives the
   page a z-axis instead of a stack of rectangles. */
.k-banner-profile { height: 132px; padding: 0; border-radius: var(--radius) var(--radius) 0 0; }
.profile-card > .profile-info { padding: 0 var(--space-6) var(--space-6); }
.profile-card .profile-avatar-wrap { margin-top: -46px; position: relative; }
.profile-card .profile-avatar-lg,
.profile-card .profile-avatar-wrap img {
  border: 3px solid var(--white);
  box-shadow: 0 2px 8px rgba(20,36,28,.14);
}
.profile-card h1 { margin-top: var(--space-3); }

/* The job card inside the feed list. The band runs to the card's edges, so the
   negative margin cancels the row padding rather than the row losing it. */
.feed-list > .post.job-post { padding: 0; border-top-width: 1px; border-top-color: var(--line); }
.k-banner-card { border-radius: 0; padding: var(--space-5) var(--space-5) var(--space-4); }
.feed-list > .post.job-post:first-child .k-banner-card { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.k-banner-card h2 { margin-bottom: var(--space-2); }
.job-post-body { padding: var(--space-4) var(--space-5) 0; }
.feed-list > .post.job-post .post-actions { padding: var(--space-3) var(--space-5) var(--space-4); margin-top: 0; }

/* ── Match screen: sticky search, instant re-sort ────────────────────────
   The spec form is ten fields long, so the button that runs it was below the
   fold from the moment you started filling it in. */
.match-submit-bar {
  position: sticky;
  bottom: 0;
  margin: var(--space-4) calc(var(--space-5) * -1) calc(var(--space-5) * -1);
  padding: var(--space-4) var(--space-5);
  background: linear-gradient(to bottom, rgba(255,255,255,0), var(--white) 28%);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}
.match-submit-hint {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  color: var(--ink-light);
  text-align: center;
  min-height: 1.2em;
}

/* Controls sit above the list and say what they did. A sort that hides its own
   state is the thing the original warm-only note was warning about. */
.match-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3) var(--space-4);
  padding: var(--space-3) 0 var(--space-4);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-2);
}
.match-control { display: flex; flex-direction: column; gap: 4px; }
.match-control > span {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-light);
}
.match-control .form-control {
  padding: .35rem .55rem;
  font-size: var(--text-sm);
  min-width: 150px;
}
.match-control-check {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--ink-mid);
  padding-bottom: 5px;
}
.match-control-check input { cursor: pointer; }
.match-shown {
  margin: 0 0 5px auto;
  font-size: var(--text-xs);
  color: var(--ink-light);
  font-variant-numeric: tabular-nums;
}
.match-empty {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  font-size: var(--text-sm);
  color: var(--ink-light);
  line-height: 1.6;
}
@media (max-width: 700px) {
  .match-control .form-control { min-width: 0; width: 100%; }
  .match-control { flex: 1 1 100%; }
}

/* ── Why a role fits ─────────────────────────────────────────────────────
   Collapsed it is one sentence; opened it is the evidence, both sides of every
   scored dimension. Native <details>, so it works with no JS at all.
   Every value below is a --text-* or --space-* step: see design rules 1 and 2. */
/* ── The readout ─────────────────────────────────────────────────────────
   The site's own read, inside a card that otherwise belongs to somebody else.

   On a role card every other word is the employer's; on a candidate row in
   match.php every other word is the candidate's. In both, this block is
   Kithby speaking — and when it shared the surrounding surface it read as one
   more paragraph of the thing it was commenting on, and got skipped.

   So: its own surface, a brass rail down the side, and a kicker naming whose
   words these are. A card inside a card is normally a smell; it earns its keep
   in exactly this case, where the inner content has a different author. One
   component, both sides of the marketplace. */
.k-read {
  position: relative;
  margin: var(--space-4) 0;
  border: 1px solid var(--line);
  border-left: 3px solid var(--amber-light);
  border-radius: var(--radius);
  background: var(--white);
  box-shadow: var(--hairline), var(--shadow);
  overflow: hidden;
}
.fit { margin: 0; border: 0; border-radius: 0; background: transparent; }
.fit-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  list-style: none;
  font-size: var(--text-sm);
  color: var(--ink);
  transition: background .18s;
}
.fit-headline { flex: 1; min-width: 0; display: block; }
.fit-kicker {
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 2px;
}
.fit-verdict {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-md);
  letter-spacing: var(--display-spacing);
  color: var(--ink);
  line-height: 1.2;
}
.fit-thin {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--line);
  background: var(--ground);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--ink-mid);
}
.fit-head::-webkit-details-marker { display: none; }
.fit-head:hover { background: var(--cream-dark); }
.fit[open] .fit-head { border-bottom: 1px solid var(--line); }
.fit-lead {
  display: block;
  font-size: var(--text-sm);
  font-weight: 400;
  color: var(--ink-mid);
  line-height: 1.45;
  margin-top: 2px;
}
.fit-more {
  flex-shrink: 0;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--teal-ink);
}
.fit[open] .fit-more::after { content: ' \2191'; }
.fit:not([open]) .fit-more::after { content: ' \2193'; }

/* ── The comparison table ────────────────────────────────────────────────
   Genuinely tabular: one scored dimension per row, the member's answer against
   the employer's. As stacked "You said X → Y" lines the two sides never lined
   up and neither column could be scanned. */
.fit-tablewrap { padding: var(--space-3) var(--space-4) var(--space-4); }
.fit-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}
.fit-table thead th {
  text-align: left;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-light);
  padding: 0 var(--space-3) var(--space-2) 0;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.fit-table tbody th {
  text-align: left;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  width: 1%;
}
.fit-table th,
.fit-table td {
  padding: var(--space-2) var(--space-3) var(--space-2) 0;
  vertical-align: baseline;
  line-height: 1.45;
}
/* NOT display:flex — a flex <th> leaves the table layout, and the row rule
   then stopped short of the label column. The dot aligns inline instead. */
.fit-table tbody th { padding-left: 0; }
.fit-table tbody th .fit-dot { margin-right: var(--space-2); vertical-align: -.2em; }
/* The -.2em nudge seats a filled circle against the label text. A bare dash has
   no mass to seat, so it needs the opposite correction — and it has to be
   written at this specificity or the rule above wins. */
.fit-table tbody th .fit-dot-no { vertical-align: middle; }
.fit-table tbody tr + tr th,
.fit-table tbody tr + tr td { border-top: 1px solid var(--line); }
.fit-table td { color: var(--ink-light); }

/* The employer's column is the new information, so it carries the weight. */
.fit-said { color: var(--ink); font-weight: 500; }
.fit-role-missing { color: var(--ink-light); font-weight: 400; font-style: italic; }
.fit-row-gap th { font-weight: 500; color: var(--ink-mid); }

/* Three columns do not survive a phone. Each row becomes its own small block
   with the column names carried down from the header. */
@media (max-width: 620px) {
  .fit-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .fit-table tbody tr { display: block; padding: var(--space-3) 0; }
  .fit-table tbody tr + tr { border-top: 1px solid var(--line); }
  .fit-table tbody th,
  .fit-table tbody td { display: block; width: auto; border: 0; padding: 0; }
  .fit-table tbody td { padding-left: calc(1.125rem + var(--space-2)); }
  .fit-table tbody td::before {
    content: attr(data-label) ': ';
    color: var(--ink-light);
    font-size: var(--text-xs);
  }
}

.fit-dot {
  display: inline-grid;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  flex-shrink: 0;
  font-size: var(--text-xs);
  line-height: 1;
}
.fit-dot-yes { background: var(--green-wash); color: var(--green); }
/* No badge on a gap. A filled circle is an affirmative marker — it says "this
   is a thing that happened" — and an absence is not. The dash keeps the same
   20px slot so the label column still aligns row to row, but carries no
   surface of its own: present gets a badge, absent gets a mark. */
.fit-dot-no  { background: none; color: var(--ink-light); }

/* The two marks have to carry the same optical weight, or the matches shout
   and the gaps whisper. A tick is an SVG spanning most of its box; an en-dash
   was a text glyph about half as wide and a hair tall, so the same circle read
   as two different sizes. Both are drawn now, to the same extent and the same
   stroke. */
/* The mark fills ~60% of its circle. At 41% the tick read as lost inside a
   badge that was mostly empty space, which is what "the checks look small"
   meant. Stroke weight rises with it so the tick stays a tick rather than a
   hairline scratch. */
.fit-dot .icon { width: 1.02em; height: 1.02em; stroke-width: 3; }
/* Shorter than the tick on purpose: a solid bar carries more ink than a
   stroked tick across the same width, so matching them by extent made the gaps
   the heavier of the two. Equal weight, not equal size. */
.fit-dot-no::before {
  content: '';
  display: block;
  width: .78em;
  height: 1.75px;
  border-radius: 1px;
  background: currentColor;
}

.fit-tail,
.fit-note {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--ink-mid);
  border-top: 1px solid var(--line);
}
.fit-note { font-size: var(--text-xs); color: var(--ink-light); }

@media (max-width: 640px) {
  .fit-row { grid-template-columns: 1fr; }
  .fit-row dd { padding-left: calc(1.125rem + var(--space-2)); }
}

/* ── Role chips ──────────────────────────────────────────────────────────
   A tinted fill rather than an outlined box: six hard-bordered pills in a row
   competed with the card's own edge. Scoped to .chip-row so the existing plain
   .chip used elsewhere is untouched. */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.chip-row .chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border: 1px solid transparent;
  background: var(--ground);
  color: var(--ink-mid);
  font-size: var(--text-xs);
  font-weight: 500;
  white-space: nowrap;
  transition: background .18s, color .18s;
}
.chip-row .chip .icon { width: 1em; height: 1em; vertical-align: -.12em; opacity: .7; }

/* Pay is the fact people look for first, so it is the one that gets colour. */
.chip-row .chip-pay {
  background: var(--amber-wash);
  color: var(--amber);
  font-weight: 600;
  border-color: rgba(138,100,32,0.18);
  border-color: color-mix(in srgb, var(--amber) 18%, transparent);
}

/* ── The fit figure ──────────────────────────────────────────────────────
   The route, at the same size the rest of the site draws a figure about a
   person. The word sits beside it: the route has no middle to put a number in,
   and a member is shown a word rather than a percentage by design. */
.job-fit-figure {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--ink);
}
.job-fit-figure .kith-gauge { display: block; }
/* Chips sitting on the ink band: the card's own terms, not a second copy of
   them further down. Light-on-dark, so the tinted fill inverts. */
.chip-row-onband { margin-bottom: 0; }
.chip-row-onband .chip {
  background: rgba(237,241,238,.10);
  color: rgba(237,241,238,.88);
  border-color: transparent;
}
.chip-row-onband .chip-pay {
  background: rgba(200,161,78,0.16);
  background: color-mix(in srgb, var(--amber-light) 16%, transparent);
  color: var(--amber-light);
  border-color: rgba(200,161,78,0.3);
  border-color: color-mix(in srgb, var(--amber-light) 30%, transparent);
}
/* Readout, compact: a candidate row is denser than a role card, so the same
   component runs tighter here. Kicker and rail are identical — the point is
   that a member and a recruiter learn to recognise one thing. */
.k-read-sm { margin: var(--space-3) 0 0; }
.k-read-body { padding: var(--space-3) var(--space-4); }
.k-read-kicker {
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: var(--space-2);
}
/* Inside the readout the old section separators are redundant: the surface
   already says where the block starts. */
.k-read .match-why { margin-top: 0; padding-top: 0; border-top: 0; }
.k-read .match-caveat { margin-top: var(--space-3); }

/* Visually hidden, still announced. */
.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;
}
/* ── Posting completeness ────────────────────────────────────────────────
   A meter, not a gate. It reports what the blanks cost in reach, so a poster
   fills the fields in because they want the role seen. */
.post-meter {
  margin-top: var(--space-5);
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-left: 3px solid var(--amber-light);
  border-radius: var(--radius);
  background: var(--white);
  box-shadow: var(--hairline), var(--shadow);
}
.post-meter-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.post-meter-count {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.post-meter-track {
  flex: 1;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--cream-dark);
  overflow: hidden;
}
.post-meter-track > i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--radius-pill);
  background: var(--amber-light);
  transition: width .3s cubic-bezier(.16,1,.3,1), background .3s;
}
.post-meter[data-full="1"] .post-meter-track > i { background: var(--green); }
.post-meter-note {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--ink-mid);
}
/* ── Skill picker (CR-58) ────────────────────────────────────────────────
   Shared by the posting form and a member's Open to Opportunities. Both sides
   pick from one vocabulary, which is what makes the comparison between them
   work at all. */
.skillpick { position: relative; }
.skillpick-box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--white);
  transition: border-color .18s, box-shadow .18s;
}
.skillpick-box:focus-within {
  border-color: var(--amber-light);
  box-shadow: 0 0 0 3px rgba(200,161,78,0.16);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--amber-light) 16%, transparent);
}
.skillpick-chips { display: contents; }
.skillpick-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--ground);
  color: var(--ink);
  font-size: var(--text-xs);
  font-weight: 500;
  white-space: nowrap;
}
/* Typed before the vocabulary existed. Kept, but marked, because it only
   matches its own exact wording. */
.skillpick-chip.is-freetext {
  background: var(--amber-wash);
  color: var(--amber);
  border: 1px dashed rgba(138,100,32,0.35);
  border: 1px dashed color-mix(in srgb, var(--amber) 35%, transparent);
}
.skillpick-x {
  border: 0;
  background: none;
  cursor: pointer;
  color: inherit;
  opacity: .55;
  font-size: var(--text-sm);
  line-height: 1;
  padding: 0;
}
.skillpick-x:hover { opacity: 1; }
.skillpick-input {
  flex: 1;
  min-width: 140px;
  border: 0;
  outline: 0;
  background: none;
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--ink);
  padding: var(--space-1) 0;
}
.skillpick-menu {
  position: absolute;
  z-index: 40;
  left: 0;
  right: 0;
  margin-top: var(--space-1);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
.skillpick-opt {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--ink);
}
.skillpick-opt em {
  font-style: normal;
  font-size: var(--text-xs);
  color: var(--ink-light);
  white-space: nowrap;
}
.skillpick-opt:hover,
.skillpick-opt.is-active { background: var(--ground); }
/* A skill the screen turned away: an explanation, not an option, so it does
   not highlight like one, and it carries the way to ask for a review. */
.skillpick-opt.skillpick-add-error { cursor: default; color: var(--red); line-height: 1.45; }
.skillpick-opt.skillpick-add-error:hover { background: transparent; }
.skillpick-review { color: var(--ink); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; margin-left: .25rem; }
.skillpick-review:hover { color: var(--amber); }
/* Status, not a control. It was a pill that looked pressable and whose action
   was to switch the feature off. */
.seeking-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--green);
}
/* Destructive, so it is quiet and says what it does rather than naming the
   feature it removes. */
.seeking-off {
  font-size: var(--text-sm);
  color: var(--ink-light);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.seeking-off:hover { color: var(--red); }
/* ── Profile gaps ────────────────────────────────────────────────────────
   Two prompts, one idea: something the member has not told us is costing them
   roles, and neither the cost nor the cause is visible from the outside.

   Deliberately quieter than the readout beside it. This is an invitation, not
   a finding about the role, and it must not compete with the posting itself.
   Both disappear the moment the gap is closed, which is why neither needs a
   dismiss control. */
.fit-invite {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--ground);
  text-decoration: none;
  transition: border-color .2s, background .2s;
}
.fit-invite:hover { border-color: var(--amber-light); background: var(--amber-wash); }
/* Same card, no destination: the recruiter note on a role they do not own.
   Suppresses the hover affordance so it does not look like a dead link. */
.fit-invite-icon { color: var(--amber); flex-shrink: 0; line-height: 1.5; }
.fit-invite strong {
  display: block;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ink-mid);
  line-height: 1.55;
}
.fit-invite em {
  display: block;
  margin-top: var(--space-1);
  font-style: normal;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--teal-ink);
}

.gapcard { border-left: 3px solid var(--amber-light); }
.gapcard-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink);
  margin-bottom: var(--space-3);
}
.gapcard-icon { color: var(--amber); }
.gapcard-list { list-style: none; margin: 0; padding: 0; }
.gapcard-list li + li {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}
.gapcard-list li {
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--ink-mid);
}
/* The member's own words, quoted back, so "some of your skills" is specific
   rather than something they have to go and work out. */
.gapcard-detail {
  display: block;
  margin: var(--space-1) 0;
  font-size: var(--text-xs);
  color: var(--ink-light);
  font-style: italic;
}
.gapcard-list a {
  display: inline-block;
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--teal-ink);
}
/* ── Fit, in the band corner ─────────────────────────────────────────────
   The collapsed state of the evidence panel. The route filled to the member's
   score, with the verdict beside it: a mark alone would mean clicking before
   learning anything, and a bare logo reads as branding rather than a control. */
.fit-corner {
  position: absolute;
  top: var(--space-4);
  right: var(--space-5);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-1);
  border: 1px solid rgba(237,241,238,.16);
  border-radius: var(--radius-pill);
  background: rgba(237,241,238,.06);
  color: var(--cream);
  cursor: pointer;
  font-family: inherit;
  transition: background .2s, border-color .2s, transform .2s;
}
.fit-corner:hover {
  background: rgba(200,161,78,0.16);
  background: color-mix(in srgb, var(--amber-light) 16%, transparent);
  border-color: rgba(200,161,78,0.45);
  border-color: color-mix(in srgb, var(--amber-light) 45%, transparent);
  transform: translateY(-1px);
}
.fit-corner .kith-gauge { display: block; color: var(--cream); }
.fit-corner-word {
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
  padding-right: var(--space-1);
}
.fit-corner[aria-expanded="true"] {
  background: rgba(200,161,78,0.2);
  background: color-mix(in srgb, var(--amber-light) 20%, transparent);
  border-color: var(--amber-light);
}

/* With the corner driving it, the panel's own summary row is redundant. Without
   JavaScript the class is never set and the row remains the control. */
.fit-corner-on .fit .fit-head { display: none; }
.fit-corner-on .fit[open] { margin-top: 0; }

/* Collapsed, the shell was drawing its rail and border around nothing, which
   left a stray line under the band. It keeps its margin so the card does not
   jump when the panel opens. */
.k-read.is-collapsed {
  border: 0;
  border-left: 0;
  background: none;
  box-shadow: none;
  margin: 0;
}
/* Unless it is carrying the thin-posting note, which is content in its own
   right and must not vanish with the panel. */
.k-read.is-collapsed:has(.fit-thin) {
  border: 1px solid var(--line);
  border-left: 3px solid var(--amber-light);
  background: var(--white);
  box-shadow: var(--hairline), var(--shadow);
  margin: var(--space-4) 0;
}

/* The lead sentence lives inside now, because the corner carries the verdict
   and it would otherwise be said twice. */
.fit-lead-inner {
  margin: 0;
  padding: var(--space-3) var(--space-4) 0;
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--ink-mid);
}

@media (max-width: 560px) {
  /* At this width the band's title needs the full line, so the corner sits
     under the eyebrow rather than overlapping it. */
  .fit-corner { position: static; margin-top: var(--space-3); display: inline-flex; }
}


/* ── Phone tab bar ──
   Signed-in only (header.php). Hidden above 640px, where the header nav
   still shows its links; at 640 and below the header hides them, so this
   takes over. Colours are tokens, so every theme repaints it. */
.tab-bar { display: none; }
@media (max-width: 640px) {
  .tab-bar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
    background: var(--white);
    border-top: 1px solid var(--line);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
    box-shadow: 0 -8px 24px -16px rgba(18,26,21,.25);
  }
  .tab-bar-item {
    position: relative;
    min-height: 48px; /* 44px touch target plus room for the label */
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    color: var(--ink-light); text-decoration: none;
    font-size: 10.5px; font-weight: 500; line-height: 1.2;
    border-radius: var(--radius-sm);
  }
  .tab-bar-item .icon { width: 22px; height: 22px; vertical-align: 0; }
  .tab-bar-item[aria-current="page"] { color: var(--ink); font-weight: 600; }
  .tab-bar-item[aria-current="page"] .icon { stroke-width: 2.1; }
  .tab-bar-item:active { background: var(--cream-dark); }
  .tab-bar-badge {
    position: absolute; top: 2px; left: calc(50% + 6px);
    margin: 0; min-width: 16px; text-align: center; line-height: 14px;
  }
  /* Content and footer clear the fixed bar */
  .tab-bar ~ .main-content { padding-bottom: calc(2.5rem + 64px + env(safe-area-inset-bottom, 0px)); }
  .tab-bar ~ .site-footer { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); }
}


/* ── Limited profile card [22] ────────────────────────────────────────────
   What someone outside your Kith Path sees. Deliberately a single narrow
   column rather than the two-column profile shell: it should read as a calling
   card, not as a profile with most of it missing. */
.profile-limited {
  max-width: 34rem;
  margin: 0 auto;
}
.profile-limited-card { padding: var(--space-5) var(--space-5) var(--space-4); }
.profile-limited-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.profile-limited-avatar {
  width: 64px;
  height: 64px;
  font-size: var(--text-lg);
  flex-shrink: 0;
}
.profile-limited-name {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--text-lg);
  line-height: 1.2;
  color: var(--ink);
  margin: 0 0 .15rem;
}
.profile-limited-title {
  font-size: var(--text-sm);
  color: var(--ink-mid);
  margin: 0;
  line-height: 1.4;
}
.profile-limited-why {
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--ink-mid);
  background: var(--cream);
  border-left: 3px solid var(--amber);
  border-radius: 0 6px 6px 0;
  padding: var(--space-3) var(--space-4);
  margin: 0 0 var(--space-4);
}
.profile-limited-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.profile-limited-pending {
  font-size: var(--text-sm);
  color: var(--ink-light);
}
.profile-limited-foot {
  font-size: var(--text-xs);
  line-height: 1.6;
  color: var(--ink-light);
  text-align: center;
  margin: var(--space-4) 0 0;
}


/* ── Job card declutter [8] ───────────────────────────────────────────────
   The owner's way into match.php, in the band corner a seeker's fit figure
   would occupy. It inherits .fit-corner's pill so the two read as the same
   slot answering to whoever is looking, rather than as two different controls
   that happen to sit in the same place. */
.fit-corner-link {
  text-decoration: none;
  padding-left: var(--space-2);
}
.fit-corner-link:hover,
.fit-corner-link:focus-visible {
  background: rgba(237,241,238,.12);
  border-color: rgba(237,241,238,.28);
}
.fit-corner-link-icon {
  display: inline-flex;
  width: 15px;
  height: 15px;
  opacity: .85;
}
.fit-corner-link-icon svg { width: 100%; height: 100%; }

/* Replaces a three-line panel that said the same thing. Shown only to a hiring
   person on somebody else's role, where the fit area would otherwise be blank
   and look broken. */
.fit-note-role {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--ink-light);
  margin: 0 0 var(--space-3);
}
.fit-note-role svg { width: 15px; height: 15px; flex-shrink: 0; opacity: .7; }


/* ── Reach beside fit, on the roles listing [13] ──────────────────────────
   The second, quieter column. Fit is what a recruiter acts on, so reach must
   not compete with it: same shape, dimmer, and it sits to its left so the
   sentence reads "18 people saw it, 3 strong fits". */
.match-role-count-soft {
  margin-right: var(--space-4);
  opacity: .72;
}
.match-role-count-soft .match-role-n { font-size: var(--text-md); }



/* ── Deciding on a connection request [29] ────────────────────────────────
   Who already knows them, which on an invite-only network is the whole
   question. Given the same weight as the stated reason beside it: the reason
   is what they say about themselves, this is what your own people say. */
.conn-mutual {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--ink-mid);
  margin-top: var(--space-2);
}
.conn-mutual svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--amber); }
.conn-mutual strong { color: var(--ink); font-weight: 600; }

/* Said plainly rather than left blank. A silent absence reads as "we did not
   check", and the absence is itself the useful answer here. */
.conn-mutual-none { color: var(--ink-light); }
.conn-mutual-none svg { color: var(--ink-light); opacity: .6; }

/* Their own words, trimmed. Enough to recognise someone, not so much that the
   row stops being a list. */
.conn-bio {
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--ink-mid);
  margin: var(--space-2) 0 0;
}
