/* ============================================================
   JobInventory — shared editorial system (MOBILE-FIRST, 2026)
   Used across: home, search, job, account, about, contact, terms

   Design rules:
   - Base styles target ~360px width. Layers up at 720px (tablet)
     and 1000px (desktop). Never the other way around.
   - Touch targets >= 44×44 (WCAG 2.5.5 AAA).
   - Form inputs >= 16px font-size to suppress iOS zoom-on-focus.
   - System-serif fallback so first paint is instant. size-adjust
     metrics neutralise CLS when the web font swaps in.
   - safe-area-inset-* honored on every fixed bottom element.
   - prefers-reduced-motion turns off scroll-smoothing + transitions.
   ============================================================ */

:root {
  --paper: #f4ecd8;
  --paper-2: #ede4c9;
  --paper-3: #e6dcbe;
  --ink: #18120c;
  --ink-2: #3b3225;
  --ink-3: #6e6552;
  --ink-4: #9a8f72;
  --rule: #2b2419;
  --rule-soft: #c9bf9d;
  --accent: #8b1a1a;       /* masthead red */
  --accent-2: #114e4d;     /* deep teal */
  --accent-3: #6b5210;     /* mustard */
  --bg-card: #fbf6e7;
  --gutter: 16px;          /* mobile gutter — raised at >=720px */
  --tap: 44px;             /* min hit target */
}
@media (min-width: 720px) {
  :root { --gutter: 24px; }
}
@media (min-width: 1000px) {
  :root { --gutter: 32px; }
}

/* Self-hosted fonts — no Google Fonts dependency.
   - Source Serif 4 ships as a single variable font (~50KB woff2)
     covering all weights from 400→900. Browser picks weight from
     the variable axis at zero extra request cost.
   - IBM Plex Mono 400 only — single static weight (~10KB woff2).
   - font-display: swap + the ji-serif-fallback shim above means
     first paint is instant and the swap is metrics-stable. */
@font-face {
  font-family: "Source Serif 4";
  src: url("fonts/source-serif-4.woff2") format("woff2-variations"),
       url("fonts/source-serif-4.woff2") format("woff2");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
  /* Latin range only — drops parsing of glyphs we'll never render. */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/ibm-plex-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* CLS shim: a system serif aliased so the swap to Source Serif 4
   doesn't shift line metrics. */
@font-face {
  font-family: "ji-serif-fallback";
  src: local("Georgia"), local("Times New Roman"), local("Times");
  size-adjust: 100%;
  ascent-override: 88%;
  descent-override: 22%;
  font-display: swap;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;     /* don't auto-inflate text on iOS landscape */
  text-size-adjust: 100%;
}
body {
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  background: var(--paper);
  color: var(--ink);
  font-size: 16px;                     /* up from 15px — easier read on mobile */
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;                    /* defends against any rogue full-bleed without breaking position:sticky */
}
img, svg, video { max-width: 100%; display: block; }
/* [hidden] override — beats author display:grid/flex declarations.
   Without this, hiding result cards / apply bars via `el.hidden = true`
   silently does nothing because the UA sheet's `[hidden]{display:none}`
   loses to any author display rule. */
[hidden] { display: none !important; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
button { font: inherit; cursor: pointer; }
button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* form-input baseline — keeps iOS from zoom-on-focus and lifts
   targets to 44px without each page redeclaring it. */
input, select, textarea {
  font: inherit;
  font-size: 16px;
  min-height: var(--tap);
}
textarea { min-height: 96px; }

.mono { font-family: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace; }
.eyebrow {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-3);
}

/* skip link a11y */
.skip {
  position: absolute; left: -9999px; top: 0;
  background: var(--ink); color: var(--paper);
  padding: 10px 14px; z-index: 100;
  font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 12px;
  min-height: var(--tap);
}
.skip:focus { left: 8px; top: 8px; }

/* ====== MASTHEAD ====== */
.masthead-rule { height: 3px; background: var(--ink); }
.masthead-rule + .masthead-rule { margin-top: 2px; height: 1px; }
@media (min-width: 720px) {
  .masthead-rule { height: 4px; }
}

.masthead {
  padding: 14px var(--gutter) 10px;
  text-align: center;
  border-bottom: 1px solid var(--rule);
  position: relative;
}
@media (min-width: 720px) { .masthead { padding: 22px var(--gutter) 14px; } }

.masthead .vol,
.masthead .day {
  /* On mobile these live below the brand; on tablet+ flank it. */
  display: none;
}
@media (min-width: 720px) {
  .masthead .vol,
  .masthead .day {
    display: block;
    position: absolute; top: 22px;
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-size: 11px; color: var(--ink-3);
  }
  .masthead .vol { left: var(--gutter); text-align: left; }
  .masthead .day { right: var(--gutter); text-align: right; }
}

.masthead h1.brand {
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-weight: 900;
  font-size: clamp(40px, 11vw, 84px);  /* fluid: 40px floor → 84px ceiling */
  letter-spacing: -0.03em;
  margin: 0; line-height: 0.95;
}
.masthead h1.brand a { color: var(--ink); }
.masthead h1.brand a:hover { text-decoration: none; color: var(--ink); }
.masthead h1.brand .dot { color: var(--accent); }
.masthead .tagline {
  font-style: italic; color: var(--ink-2);
  margin-top: 6px; font-size: 15px;
}
@media (max-width: 719px) {
  .masthead .tagline { display: none; }     /* save vertical real estate above the fold */
}

/* compact masthead for inner pages */
.masthead.compact { padding: 10px var(--gutter) 8px; }
.masthead.compact h1.brand { font-size: clamp(28px, 7vw, 44px); }
.masthead.compact .tagline { display: none; }
@media (min-width: 720px) {
  .masthead.compact { padding: 16px var(--gutter) 12px; }
  .masthead.compact .vol,
  .masthead.compact .day { top: 22px; }
}

/* ====== SECTION NAV — horizontal scroll on mobile, no wrap ====== */
nav.sections {
  display: flex;
  gap: 18px;
  padding: 8px var(--gutter);
  border-bottom: 2px solid var(--rule);
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  /* mobile: scroll horizontally with right-edge fade as scroll affordance */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  white-space: nowrap;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
nav.sections::-webkit-scrollbar { display: none; }
@media (min-width: 720px) {
  nav.sections {
    justify-content: center;
    flex-wrap: wrap;
    overflow-x: visible;
    white-space: normal;
    gap: 22px;
    font-size: 14px;
    padding: 10px var(--gutter);
    -webkit-mask-image: none;
            mask-image: none;
  }
}
nav.sections a {
  color: var(--ink);
  padding: 4px 0;
  min-height: 32px;
  display: inline-block;
}
nav.sections a:hover { color: var(--accent); }
nav.sections .now {
  border-bottom: 3px solid var(--accent);
  padding-bottom: 4px;
}

/* ====== UTILITY NAV (account row) ======
   Hidden on mobile to save above-the-fold space; primary actions
   collapse into a sticky bottom bar or burger on each page. */
.utilnav {
  display: none;
}
@media (min-width: 720px) {
  .utilnav {
    display: flex; gap: 14px;
    justify-content: flex-end;
    padding: 6px var(--gutter);
    border-bottom: 1px solid var(--rule-soft);
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    background: var(--paper-2);
  }
  .utilnav a { color: var(--ink-3); }
  .utilnav a:hover { color: var(--accent); }
  .utilnav .live { color: var(--accent-2); margin-right: auto; padding-left: 0; }
  .utilnav .live::before { content: "● "; }
  /* The signed-in / signed-out groups are toggled by [data-auth-state]
     on <body>. Both groups live in the markup; CSS hides the wrong one. */
  .utilnav .auth-out, .utilnav .auth-in { display: contents; }
  body[data-auth-state="in"]  .utilnav .auth-out { display: none; }
  body[data-auth-state="out"] .utilnav .auth-in  { display: none; }
  /* Account chip (signed-in). Button reset, mono, aligns with the rest. */
  .utilnav .acct-chip {
    display: inline-flex; align-items: center; gap: 8px;
    background: transparent; border: 0; padding: 0; cursor: pointer;
    font: inherit; color: var(--ink-2);
    letter-spacing: 0.12em; text-transform: uppercase;
  }
  .utilnav .acct-chip:hover { color: var(--accent); }
  .utilnav .acct-chip .avatar {
    width: 18px; height: 18px;
    background: var(--accent-2); color: var(--paper);
    display: inline-flex; align-items: center; justify-content: center;
    font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
    font-weight: 700; font-size: 10px;
    text-transform: none; letter-spacing: 0;
  }
  .utilnav .acct-chip .caret { font-size: 9px; opacity: .7; }
  /* Account menu (signed-in dropdown) */
  .utilnav .acct-menu-wrap { position: relative; }
  .utilnav .acct-menu {
    position: absolute; right: 0; top: calc(100% + 4px);
    background: var(--bg-card);
    border: 1px solid var(--ink);
    box-shadow: 0 8px 24px rgba(0,0,0,.18);
    min-width: 200px; padding: 6px 0; z-index: 50;
  }
  .utilnav .acct-menu[hidden] { display: none; }
  .utilnav .acct-menu a, .utilnav .acct-menu button {
    display: block; width: 100%; text-align: left;
    padding: 9px 14px; background: transparent; border: 0;
    font: inherit; color: var(--ink-2); cursor: pointer;
    letter-spacing: 0.06em; text-transform: uppercase;
  }
  .utilnav .acct-menu a:hover, .utilnav .acct-menu button:hover {
    background: var(--paper-3); color: var(--ink); text-decoration: none;
  }
  .utilnav .acct-menu hr {
    border: 0; border-top: 1px solid var(--rule-soft); margin: 4px 0;
  }
  .utilnav .acct-menu .signout { color: var(--accent); }
}

/* ====== EDITORIAL EMPTY / ERROR / UNAVAILABLE STATES ======
   One pattern, three uses: zero-results on search, expired/removed
   on job detail, and partner-feed failure across both. The shape is
   always the same — a stamp row over an article-style block — so
   the page stays in voice when a system goes wrong.
   Toggled via [data-state] on the closest section. */
.editorial-state {
  border: 1px solid var(--ink);
  background: var(--bg-card);
  margin: 18px 0 22px;
}
.editorial-state .stamp-row {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--ink); color: var(--paper);
  padding: 9px 16px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
}
.editorial-state.is-error .stamp-row { background: var(--accent); }
.editorial-state.is-warn  .stamp-row { background: var(--accent-3); }
.editorial-state .stamp-row .right { color: rgba(244, 236, 216, 0.7); }
.editorial-state .body {
  padding: 22px 24px 22px;
}
.editorial-state h3 {
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-weight: 900;
  font-size: clamp(22px, 4.4vw, 28px);
  line-height: 1.1; letter-spacing: -0.018em;
  margin: 0 0 8px; text-wrap: balance;
}
.editorial-state .lead {
  font-family: "Source Serif 4", serif;
  font-size: 16px; line-height: 1.5; color: var(--ink-2);
  margin: 0 0 12px; max-width: 56ch; text-wrap: pretty;
}
.editorial-state .lead .drop {
  float: left;
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-weight: 900;
  font-size: 52px; line-height: 0.85;
  margin: 4px 8px 0 0;
  color: var(--accent-2);
}
.editorial-state.is-error .lead .drop { color: var(--accent); }
.editorial-state .meta {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; color: var(--ink-3); line-height: 1.7;
  margin: 12px 0 0;
}
.editorial-state .meta .pill {
  display: inline-block;
  border: 1px solid var(--rule); padding: 2px 8px;
  margin: 0 4px 4px 0; color: var(--ink-2);
}
.editorial-state .actions {
  display: flex; flex-wrap: wrap; gap: 10px;
  border-top: 1px solid var(--rule-soft);
  margin-top: 16px; padding-top: 14px;
}
.editorial-state .actions .btn,
.editorial-state .actions .btn-ghost {
  flex: 0 1 auto; min-width: 160px; min-height: var(--tap, 44px);
}
.editorial-state .suggested {
  list-style: none; padding: 0; margin: 14px 0 0;
  border-top: 1px solid var(--rule-soft); padding-top: 14px;
}
.editorial-state .suggested li {
  display: flex; gap: 12px; align-items: baseline;
  padding: 8px 0;
  border-bottom: 1px dotted var(--rule-soft);
  font-size: 15px;
}
.editorial-state .suggested li:last-child { border-bottom: 0; }
.editorial-state .suggested .num {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; color: var(--ink-3); width: 22px; flex: 0 0 22px;
}
.editorial-state .suggested a {
  color: var(--ink); font-weight: 600;
  border-bottom: 1px solid var(--rule-soft);
}
.editorial-state .suggested a:hover { color: var(--accent); border-color: var(--accent); }
.editorial-state .suggested .hint {
  margin-left: auto;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; color: var(--ink-3);
}
.editorial-state .ref {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; color: var(--ink-3);
  border-top: 1px solid var(--rule-soft);
  margin-top: 14px; padding-top: 10px;
}
/* Mobile: a tiny pill in the masthead so signed-in users still see
   their state. We append this to <header.masthead> via JS (lighter
   than touching every page's markup). On mobile we open a small
   utility row at the top of the masthead so this pill sits ABOVE
   the brand wordmark — never floating over it. */
.mob-acct {
  display: none;
}
@media (max-width: 719px) {
  /* Make room above the brand for the utility row + pin context */
  header.masthead { position: relative; padding-top: 30px; }
  header.masthead.compact { padding-top: 26px; }

  /* Shared pill chrome — signed-in and signed-out share footprint */
  body[data-auth-state="in"] .mob-acct,
  body[data-auth-state="out"] .mob-acct {
    display: inline-flex; align-items: center;
    position: absolute; top: 6px; right: var(--gutter, 16px);
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
    line-height: 1;
    padding: 4px 6px;
    min-height: 0;
    text-decoration: none;
  }

  /* Signed-out: tiny editorial link, no underline, mono caps */
  body[data-auth-state="out"] .mob-acct {
    color: var(--accent-2);
    border-bottom: 1px solid var(--rule-soft);
    padding-bottom: 3px;
  }
  body[data-auth-state="out"] .mob-acct:hover {
    color: var(--accent);
    border-bottom-color: var(--accent);
  }

  /* Signed-in: avatar + first name as a small chip */
  body[data-auth-state="in"] .mob-acct {
    gap: 6px;
    color: var(--ink-2);
    background: var(--paper-2);
    border: 1px solid var(--rule-soft);
  }
  body[data-auth-state="in"] .mob-acct .avatar {
    width: 14px; height: 14px;
    background: var(--accent-2); color: var(--paper);
    display: inline-flex; align-items: center; justify-content: center;
    font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
    font-weight: 700; font-size: 9px;
    text-transform: none; letter-spacing: 0;
  }
}

/* ====== COL HEAD (section dividers) ====== */
.col-head {
  border-top: 3px double var(--ink);
  border-bottom: 1px solid var(--rule);
  padding: 8px 0;
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 13px;
  margin-bottom: 14px;
}
@media (min-width: 720px) { .col-head { font-size: 14px; } }

/* ====== BUTTONS ====== */
.btn {
  display: inline-block;
  border: 2px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  padding: 12px 18px;
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.02em;
  cursor: pointer;
  text-align: center;
  min-height: var(--tap);
  line-height: 1.2;
}
.btn:hover { background: var(--accent); border-color: var(--accent); color: #fff; text-decoration: none; }
.btn-ghost {
  background: transparent; color: var(--ink);
}
.btn-ghost:hover { background: var(--ink); color: var(--paper); }
.btn-block { display: block; width: 100%; }

/* tag chip */
.tag {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  color: var(--accent-2);
  border: 1px solid var(--rule-soft);
  padding: 3px 7px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  display: inline-block;
  line-height: 1.4;
}

/* ====== FOOTER ======
   Single column on mobile; 2x2 at 720; 4-up at 1000. */
footer.site {
  border-top: 4px double var(--ink);
  padding: 24px var(--gutter) calc(24px + env(safe-area-inset-bottom));
  background: var(--paper-2);
  font-size: 14px;
  /* below-the-fold on most flows — let browser skip layout work */
  content-visibility: auto;
  contain-intrinsic-size: auto 600px;
}
footer.site .foot {
  max-width: 1180px; margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}
@media (min-width: 720px) {
  footer.site .foot { grid-template-columns: 1fr 1fr; gap: 28px; }
}
@media (min-width: 1000px) {
  footer.site .foot { grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 32px; }
}

footer.site h5 {
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 10px;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 4px;
}
footer.site ul { list-style: none; margin: 0; padding: 0; }
footer.site li { padding: 4px 0; }            /* slightly larger tap targets */
footer.site li a {
  color: var(--ink);
  display: inline-block;
  min-height: 32px;
  line-height: 32px;
}
footer.site li a:hover { color: var(--accent); }
footer.site .brandlock {
  font-family: 'Source Serif 4', "ji-serif-fallback", Georgia, serif;
  font-size: 28px; font-weight: 900;
  margin: 0 0 8px; letter-spacing: -0.02em;
  color: var(--ink);
}
footer.site .brandlock .dot { color: var(--accent); }
footer.site .small {
  margin-top: 24px;
  padding-top: 14px;
  border-top: 1px solid var(--rule-soft);
  color: var(--ink-3);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 1180px;
  margin-left: auto;
  margin-right: auto;
}
@media (min-width: 720px) {
  footer.site .small {
    flex-direction: row;
    justify-content: space-between;
    gap: 12px;
  }
}

/* ====== WIRE-SERVICES LINE ======
   Cross-property strip ("Follow the wire") that sits between the
   four-column footer body and the .small fineprint row. Designed
   to read like a wire-services dateline in a real paper, not
   social-media pills: mono-caps stamps in square brackets +
   handle, joined by interpuncts.

   - Two-letter stamps only — generic typographic abbreviations,
     not brand glyphs (X, LI, FB, IG, RSS, YT, MA for Mastodon).
   - Square brackets stamped in masthead red, like the wire-loading
     bug above. The handle is paper-ink.
   - On hover, the stamp inverts (red fill, paper text) the way
     a section-head chip does on home.html.
   - Wraps gracefully on mobile; never grows past one row at desktop. */
footer.site .wire {
  margin-top: 24px;
  padding: 14px 0 0;
  border-top: 1px solid var(--rule-soft);
  max-width: 1180px;
  margin-left: auto;
  margin-right: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 18px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-2);
}
footer.site .wire .lede {
  flex: 0 0 auto;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--ink-3);
  border-right: 1px solid var(--rule-soft);
  padding-right: 18px;
  margin-right: 0;
}
@media (max-width: 719px) {
  footer.site .wire .lede {
    border-right: none;
    padding-right: 0;
    flex-basis: 100%;
  }
}
footer.site .wire a {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  color: var(--ink);
  text-decoration: none;
  padding: 4px 0;
  min-height: var(--tap);
  line-height: 1.3;
}
footer.site .wire a .stamp {
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--accent);
  font-size: 11px;
  white-space: nowrap;
}
footer.site .wire a .stamp::before { content: "[ "; color: var(--ink-3); font-weight: 400; }
footer.site .wire a .stamp::after  { content: " ]"; color: var(--ink-3); font-weight: 400; }
footer.site .wire a .handle {
  color: var(--ink-2);
  letter-spacing: 0.02em;
}
footer.site .wire a:hover .stamp { color: var(--ink); }
footer.site .wire a:hover .handle { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
footer.site .wire .sep {
  color: var(--ink-4);
  flex: 0 0 auto;
  user-select: none;
}
@media (max-width: 719px) {
  /* On mobile the interpuncts read as visual noise — hide them
     and let the items wrap naturally. */
  footer.site .wire .sep { display: none; }
  footer.site .wire { gap: 4px 14px; }
}

/* ============================================================
   LOADING STATES
   Editorial loaders for partner-feed queries. Three pieces:

   1. .wire-loading — single-line tickertape. Use as a status
      stripe above any data region that's refreshing. Mono caps,
      square brackets, three dots that animate in sequence so it
      reads like an old AP wire dribbling in characters.

   2. .skel-list / .skel-row — typeset skeleton. Rows of grey
      horizontal rules of varying lengths, like a paper being
      laid out before ink hits. No motion by default; subtle
      shimmer reveal on entry.

   3. .feed-late — failure state. Newsprint dateline + red
      column rule. Use when a partner feed times out.

   All three honour prefers-reduced-motion (no shimmer, no dots).
   Show after a 200ms grace period so fast responses don't flash.
   ============================================================ */

/* ====== 1. TICKERTAPE WIRE ====== */
/* Grace fade: hidden by default, .is-shown class makes it visible.
   The 200ms delay is enforced by JS (loader.start sets a timer that
   adds .is-shown after the grace period). No CSS transition — once
   the timer fires, the wire just appears. The grace alone prevents
   flash on fast loads; we don't need a fade-in on top of it. */
.wire-loading {
  display: none;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  margin: 0 0 16px;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--paper-2);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.wire-loading.is-shown { display: flex; }
.wire-loading[data-state="error"] {
  background: #f6e8d8;
  color: var(--accent);
  border-color: var(--accent);
}
.wire-loading .stamp {
  color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.14em;
  flex: 0 0 auto;
}
.wire-loading[data-state="error"] .stamp { color: var(--ink); }
.wire-loading .stamp::before { content: "[ "; color: var(--ink-3); }
.wire-loading .stamp::after { content: " ]"; color: var(--ink-3); }
.wire-loading .msg { flex: 1 1 auto; min-width: 0; color: var(--ink-2); }
.wire-loading .dots {
  display: inline-flex; gap: 4px; flex: 0 0 auto;
  margin-left: auto; padding-left: 10px;
  border-left: 1px solid var(--rule-soft);
}
.wire-loading .dots i {
  display: inline-block;
  width: 4px; height: 4px;
  background: var(--ink-3);
  animation: wire-dot 1.2s steps(1) infinite;
  opacity: 0.2;
}
.wire-loading .dots i:nth-child(2) { animation-delay: 0.4s; }
.wire-loading .dots i:nth-child(3) { animation-delay: 0.8s; }
.wire-loading[data-state="error"] .dots { display: none; }

@keyframes wire-dot {
  0%   { opacity: 0.2; }
  20%  { opacity: 1; background: var(--accent); }
  60%  { opacity: 0.2; background: var(--ink-3); }
  100% { opacity: 0.2; }
}

/* tabular elapsed counter — populated by JS, optional */
.wire-loading .elapsed {
  flex: 0 0 auto;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* ====== 2. SKELETON LISTINGS ====== */
.skel-list {
  display: none;
  flex-direction: column;
}
.skel-list.is-shown { display: flex; }
.skel-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  padding: 22px 0;
  border-bottom: 1px solid var(--rule-soft);
}
@media (min-width: 720px) {
  .skel-row { grid-template-columns: 1fr 200px; gap: 24px; }
}
.skel-row .left { display: flex; flex-direction: column; gap: 10px; }
.skel-row .right { display: none; }
@media (min-width: 720px) {
  .skel-row .right {
    display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
  }
}
.skel-row .bar {
  height: 10px;
  background: linear-gradient(
    90deg,
    var(--rule-soft) 0%,
    #d8cfac 50%,
    var(--rule-soft) 100%
  );
  background-size: 200% 100%;
  animation: skel-shimmer 1.6s linear infinite;
}
.skel-row .bar.tall { height: 16px; }
.skel-row .bar.short { width: 28%; }
.skel-row .bar.med { width: 56%; }
.skel-row .bar.long { width: 82%; }
.skel-row .bar.full { width: 100%; }
.skel-row .bar.headline { height: 18px; width: 70%; background-color: #c4b98e; }

@keyframes skel-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* compact skeleton for tables (salary index) */
.skel-table-row td {
  padding: 10px 0;
}
.skel-table-row .bar {
  height: 10px;
  background: linear-gradient(
    90deg,
    var(--rule-soft) 0%,
    #d8cfac 50%,
    var(--rule-soft) 100%
  );
  background-size: 200% 100%;
  animation: skel-shimmer 1.6s linear infinite;
  display: block;
}

/* ====== 3. FAILURE STATE — "late edition" ====== */
.feed-late {
  margin: 16px 0;
  padding: 18px var(--gutter);
  border-top: 3px double var(--accent);
  border-bottom: 1px solid var(--rule);
  background: var(--paper-2);
}
.feed-late .dateline {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
  margin-bottom: 6px;
}
.feed-late h3 {
  font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif;
  font-weight: 900; font-size: 22px; letter-spacing: -0.01em;
  margin: 0 0 6px;
}
.feed-late p {
  margin: 0; font-size: 15px; color: var(--ink-2); max-width: 60ch;
}
.feed-late .actions {
  display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap;
}

/* Hide loaders entirely when we're not actually loading */
[data-loading="false"] .wire-loading,
[data-loading="false"] .skel-list,
[data-loading="false"] .skel-table-overlay {
  display: none;
}
[data-loading="true"] .results-list,
[data-loading="true"] .index-cards,
[data-loading="true"] table.index tbody {
  display: none;
}

/* Reduced motion: stop shimmer + dots, just dim quietly */
@media (prefers-reduced-motion: reduce) {
  .wire-loading .dots i { animation: none; opacity: 0.6; }
  .skel-row .bar,
  .skel-table-row .bar { animation: none; background: var(--rule-soft); }
}

/* skel table variant on home.html — visible only while loading.
   Note: data-loading sits on #salary-index itself (not an ancestor),
   so the selectors must be chained directly to the ID, not descendant. */
table.index.skel-only { display: none; }
#salary-index[data-loading="true"] table.index.skel-only { display: table; }
#salary-index[data-loading="true"] .index-cards,
#salary-index[data-loading="true"] table.index:not(.skel-only) { display: none; }

/* ============================================================
   FORM VALIDATION — editorial proofing marks
   ----------------------------------------------------------------
   The metaphor: a copy editor's marks on a galley proof. Every
   field has a state stamp ([OK], [ERR], [!], …) in mono caps and
   a thin coloured rule beneath the input — the same vocabulary
   as .wire-loading and .col-head, so feedback feels like part of
   the paper, not a SaaS toast.

   Apply ONE of these classes to a wrapping element (.field-row
   or any container holding label + input + .field-msg):
     .is-valid     — accepted (teal)
     .is-invalid   — rejected (red)
     .is-warning   — soft warn / advisory (mustard)
     .is-pending   — async check in flight (ink-3, animated dots)

   The .field-msg sibling renders the message; pseudo-element
   stamps it with the matching code. Inputs pick up coloured
   borders + focus rings automatically.

   Form-level banners use .form-notice (variants: error/success/
   warning) — same vocabulary, scaled up.
   ============================================================ */

/* ----- 1. Per-field state ------------------------------------- */
/* The wrapper sets the colour token; everything else inherits.   */
.field-row {
  --state-color: transparent;
  --state-bg: transparent;
  display: block;
  position: relative;
}
.field-row.is-valid    { --state-color: var(--accent-2); --state-bg: #e9efe2; }
.field-row.is-invalid  { --state-color: var(--accent);   --state-bg: #f6e8d8; }
.field-row.is-warning  { --state-color: var(--accent-3); --state-bg: #f4ecc7; }
.field-row.is-pending  { --state-color: var(--ink-3);    --state-bg: var(--paper-2); }

/* Input borders take the state colour. Override via :where() so the
   specificity stays low — page-scoped input rules still win for
   layout (radius, padding) but lose for colour. */
.field-row.is-valid   :where(input, select, textarea),
.field-row.is-invalid :where(input, select, textarea),
.field-row.is-warning :where(input, select, textarea),
.field-row.is-pending :where(input, select, textarea) {
  border-color: var(--state-color);
  box-shadow: inset 0 -2px 0 0 var(--state-color);
}
.field-row.is-valid   :where(input, select, textarea):focus,
.field-row.is-invalid :where(input, select, textarea):focus,
.field-row.is-warning :where(input, select, textarea):focus,
.field-row.is-pending :where(input, select, textarea):focus {
  outline: 2px solid var(--state-color);
  outline-offset: 1px;
  border-color: var(--state-color);
}

/* The message line. Hidden until a state class is set.            */
.field-msg {
  display: none;
  align-items: flex-start;
  gap: 8px;
  margin-top: 6px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--ink-2);
  min-height: 18px;
}
.field-row.is-valid   > .field-msg,
.field-row.is-invalid > .field-msg,
.field-row.is-warning > .field-msg,
.field-row.is-pending > .field-msg { display: flex; }

/* The mono "stamp" prefix — square brackets, like [OK] or [ERR].
   Set per state via ::before content. */
.field-msg::before {
  content: "[ ]";
  flex: 0 0 auto;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--state-color);
  text-transform: uppercase;
}
.field-row.is-valid   > .field-msg::before { content: "[ OK ]"; }
.field-row.is-invalid > .field-msg::before { content: "[ ERR ]"; }
.field-row.is-warning > .field-msg::before { content: "[ ! ]"; }
.field-row.is-pending > .field-msg::before { content: "[ … ]"; }

.field-row.is-invalid > .field-msg,
.field-row.is-valid   > .field-msg,
.field-row.is-warning > .field-msg { color: var(--ink-2); }
.field-row.is-pending > .field-msg { color: var(--ink-3); font-style: italic; }

/* Pending dots — three dots after the message, animated like the
   wire-loading ticker. Inserted via ::after so the message text
   stays clean. */
.field-row.is-pending > .field-msg::after {
  content: "...";
  display: inline-block;
  margin-left: 4px;
  letter-spacing: 0.2em;
  animation: field-dot-fade 1.2s steps(4, end) infinite;
  color: var(--ink-3);
}
@keyframes field-dot-fade {
  0%   { clip-path: inset(0 100% 0 0); }
  33%  { clip-path: inset(0 66% 0 0); }
  66%  { clip-path: inset(0 33% 0 0); }
  100% { clip-path: inset(0 0 0 0); }
}

/* When a state is set, optionally display a light tinted strip on
   the input itself — picks up the editorial highlight feel. */
.field-row.is-invalid :where(input, select, textarea),
.field-row.is-warning :where(input, select, textarea) {
  background: var(--state-bg);
}

/* Hint line: neutral helper copy, always visible. Pairs with
   field-msg — appears above it, dims out when a real state shows. */
.field-hint {
  display: block;
  margin-top: 6px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.04em;
  color: var(--ink-3);
}
.field-row.is-invalid > .field-hint,
.field-row.is-valid   > .field-hint { display: none; }

/* Required-field marker. Use <label>Email <span class="req">*</span></label>. */
.req {
  color: var(--accent);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  margin-left: 2px;
}

/* Optional-field marker. */
.opt {
  color: var(--ink-4);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  text-transform: none;
  letter-spacing: 0.04em;
  margin-left: 4px;
}

/* Char counter — for textareas and bounded text inputs. */
.field-count {
  position: absolute;
  right: 0; top: 0;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px; letter-spacing: 0.08em;
  color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}
.field-row.over .field-count { color: var(--accent); font-weight: 700; }

/* ----- 2. Form-level notice banner ---------------------------- */
/* For "fix the 3 errors below" or "saved" confirmations.          */
.form-notice {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 12px 14px;
  margin: 0 0 18px;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--paper-2);
  font-size: 14px;
  line-height: 1.45;
}
.form-notice .stamp {
  flex: 0 0 auto;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 2px 6px;
  border: 1px solid currentColor;
  color: var(--ink);
  align-self: center;
}
.form-notice .body { flex: 1 1 auto; min-width: 0; }
.form-notice strong { font-family: "Source Serif 4", "ji-serif-fallback", Georgia, serif; }
.form-notice ul { margin: 6px 0 0; padding-left: 18px; }
.form-notice li { margin: 2px 0; }
.form-notice a { color: inherit; text-decoration: underline; }

.form-notice.is-error {
  background: #f6e8d8;
  border-top: 3px double var(--accent);
  border-bottom-color: var(--accent);
}
.form-notice.is-error .stamp { color: var(--accent); }

.form-notice.is-success {
  background: #e9efe2;
  border-top: 3px double var(--accent-2);
  border-bottom-color: var(--accent-2);
}
.form-notice.is-success .stamp { color: var(--accent-2); }

.form-notice.is-warning {
  background: #f4ecc7;
  border-top: 3px double var(--accent-3);
  border-bottom-color: var(--accent-3);
}
.form-notice.is-warning .stamp { color: var(--accent-3); }

/* Dismiss button (optional). */
.form-notice .dismiss {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--rule);
  color: var(--ink-2);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  padding: 4px 8px;
  cursor: pointer;
  min-height: 28px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.form-notice .dismiss:hover { background: var(--ink); color: var(--paper); }

/* ----- 3. Button states --------------------------------------- */
/* Disabled, loading, and destructive variants for .btn / .btn-primary. */
.btn[disabled],
.btn-primary[disabled],
.btn[aria-disabled="true"],
.btn-primary[aria-disabled="true"] {
  background: var(--rule-soft) !important;
  border-color: var(--rule-soft) !important;
  color: var(--ink-3) !important;
  cursor: not-allowed;
  box-shadow: none !important;
}

.btn.is-loading,
.btn-primary.is-loading {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
.btn.is-loading::after,
.btn-primary.is-loading::after {
  content: "";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--paper);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  content: "Working …";
  animation: btn-load-fade 1.2s linear infinite;
}
.btn-ghost.is-loading::after { color: var(--ink); }
@keyframes btn-load-fade {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}

.btn.is-success,
.btn-primary.is-success {
  background: var(--accent-2) !important;
  border-color: var(--accent-2) !important;
  color: var(--paper) !important;
}
.btn.is-success::before,
.btn-primary.is-success::before {
  content: "✓ ";
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  margin-right: 4px;
}

/* Destructive — for "delete account", "remove alert" etc. */
.btn-danger {
  background: var(--accent);
  border: 2px solid var(--accent);
  color: var(--paper);
}
.btn-danger:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* ----- 4. Inline search-bar validation ------------------------ */
/* For the home-page hero search and the search-page bar — these
   inputs sit inside a heavy black box, so we tint the field
   background instead of bordering. */
.search.is-invalid { border-color: var(--accent); box-shadow: 4px 4px 0 var(--accent); }
.search.is-invalid .field { background: #fbf2e3; }
.search-feedback {
  display: none;
  margin: 8px 0 0;
  padding: 8px 12px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.06em;
  color: var(--accent);
  border-left: 3px solid var(--accent);
  background: #f6e8d8;
}
.search.is-invalid + .search-feedback { display: block; }

/* ----- 5. Reduced motion -------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .field-row.is-pending > .field-msg::after,
  .btn.is-loading::after,
  .btn-primary.is-loading::after { animation: none; }
}

/* ====== UTILITY: visibility helpers ====== */
.mobile-only { display: block; }
.desktop-only { display: none; }
@media (min-width: 1000px) {
  .mobile-only { display: none; }
  .desktop-only { display: block; }
}

/* Reduce motion */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
