/* GoodTrades — the whole stylesheet. Tokens are specified in docs/DESIGN.md; change both together.
   Order: fonts, tokens, reset, type, layout, docket, forms, buttons, lists, badges, header/footer. */

/* ---------------------------------------------------------------- fonts
   Self-hosted: no external request, so no visitor IP reaches a third party on page load. */

@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/archivo-400-latin.woff2') format('woff2');
  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: 'Archivo'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/archivo-400-latin-ext.woff2') format('woff2'); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 500; font-display: swap;
  src: url('/assets/fonts/archivo-500-latin.woff2') format('woff2');
  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: 'Archivo'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('/assets/fonts/archivo-600-latin.woff2') format('woff2');
  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: 'Archivo'; font-style: normal; font-weight: 700; font-display: swap;
  src: url('/assets/fonts/archivo-700-latin.woff2') format('woff2');
  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: 'Archivo'; font-style: normal; font-weight: 700; font-display: swap;
  src: url('/assets/fonts/archivo-700-latin-ext.woff2') format('woff2'); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }

@font-face { font-family: 'Atkinson'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/atkinson-400-latin.woff2') format('woff2');
  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: 'Atkinson'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/atkinson-400-latin-ext.woff2') format('woff2'); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Atkinson'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('/assets/fonts/atkinson-600-latin.woff2') format('woff2');
  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: 'Atkinson'; font-style: normal; font-weight: 700; font-display: swap;
  src: url('/assets/fonts/atkinson-700-latin.woff2') format('woff2');
  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; }

/* ---------------------------------------------------------------- tokens */

:root {
  color-scheme: light dark;

  --ink:         #14251d;
  --ink-soft:    #4a5c52;
  --ink-faint:   #5d6f66;   /* was #71837a — 3.66:1 on --paper, failed AA on every 13px string */
  --accent:      #1f7a5c;
  --accent-deep: #0f4a37;
  --accent-wash: #e6efe9;
  --paper:       #f3f5f2;
  --surface:     #ffffff;
  --rule:        #cfd6cf;   /* decorative dividers only — 1.48:1, nowhere near 3:1 */
  --rule-strong: #8a9690;   /* form-control borders: WCAG 1.4.11 needs 3:1 and --rule is not it */
  --rule-soft:   #e3e8e3;
  --hi:          #f2b134;
  --hi-wash:     #fdf3dc;
  --brick:       #b4472f;
  --brick-wash:  #fbeae6;

  /* Text drawn ON a filled accent/brick/amber surface. These exist because #fff was hardcoded in
     five places, so dark mode had no single point at which to correct it — and white on the dark
     accent measures 2.28:1, which is a button label almost nobody can read. */
  --on-accent:   #ffffff;
  --on-brick:    #ffffff;
  --hi-ink:      #7a5200;

  /* A darker accent purely for a hover FILL. --accent-deep also serves as link text, and in dark
     mode it goes lighter than --accent, which is right for text and wrong for a hover state. */
  --accent-hover: #0f4a37;

  /* The colour of "we have not checked this". Neither grey (which reads as unimportant) nor red
     (which reads as bad) — a trust-led product has to be able to say UNKNOWN in one colour, and
     say it on a profile without condemning the trade. */
  --unchecked:      #4c5b68;
  --unchecked-wash: #eceff1;

  --display: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --body:    'Atkinson', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* 16px base, scaled. Named by role rather than size so a change stays coherent. */
  --t-micro: 0.8125rem;   /* 13 */
  --t-base:  1rem;        /* 16 */
  --t-lead:  1.125rem;    /* 18 */
  --t-h4:    1.3125rem;   /* 21 */
  --t-h3:    1.625rem;    /* 26 */
  --t-h2:    2.0625rem;   /* 33 */
  --t-h1:    2.625rem;    /* 42 */
  --t-mega:  3.3125rem;   /* 53 */

  --gutter:  1rem;
  --measure: 62ch;   /* 68ch of Atkinson renders 78-84 chars, past our own 72 ceiling */
  --radius:  4px;
  --radius-lg: 8px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink:         #eef2ef;
    --ink-soft:    #b3c1b9;
    --ink-faint:   #8b9b92;
    --accent:      #4fbf94;
    --accent-deep: #7fd6b4;
    --accent-wash: #16281f;
    --paper:       #101814;
    --surface:     #18221d;
    --rule:        #2f3d36;
    --rule-soft:   #232e28;
    --hi:          #f5c45c;
    --hi-wash:     #2b2213;
    --brick:       #e08163;
    --brick-wash:  #2c1a15;
    /* On a light accent, the label has to be dark: #14251d on #4fbf94 is 7.02:1. */
    --on-accent:   #14251d;
    --on-brick:    #14251d;
    --hi-ink:      #f5c45c;
    --rule-strong: #607268;
    --accent-hover: #3aa77c;
    --unchecked:      #a3b4bf;
    --unchecked-wash: #1b2429;
  }
}
/* Must stay in lockstep with the media query above. The first version of this block set the dark
   GROUNDS but not the dark FOREGROUNDS, so anyone on a light OS who chose dark mode explicitly got
   #7a5200 amber text on a near-black wash: 2.26:1, on the one number a trade reads before spending
   money. */
:root[data-theme="dark"] {
  --ink: #eef2ef; --ink-soft: #b3c1b9; --ink-faint: #8b9b92;
  --accent: #4fbf94; --accent-deep: #7fd6b4; --accent-wash: #16281f;
  --paper: #101814; --surface: #18221d;
  --rule: #2f3d36; --rule-strong: #607268; --rule-soft: #232e28;
  --hi: #f5c45c; --hi-wash: #2b2213; --brick: #e08163; --brick-wash: #2c1a15;
  --on-accent: #14251d; --on-brick: #14251d; --hi-ink: #f5c45c;
  --accent-hover: #3aa77c; --unchecked: #a3b4bf; --unchecked-wash: #1b2429;
}

/* ---------------------------------------------------------------- reset */

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--t-base)/1.6 var(--body);
  overflow-wrap: break-word;
  /* Sticky footer: on a short page (404, /contact) the footer used to float mid-viewport with a band
     of background below it. Grow #main to push the footer to the bottom; the footer's margin-top is
     accounted for in the flex free-space calc, so tall pages are unaffected. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
#main { flex: 1 0 auto; }

img, svg { max-width: 100%; height: auto; display: block; }

/* Motion only ever answers something the person did, and stops entirely if they ask. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* One focus treatment everywhere, and it is always visible. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

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

/* Keyboard users get a way past the nav; it only appears when focused. */
.skip-link {
  position: absolute; left: var(--gutter); top: -3rem; z-index: 10;
  background: var(--accent); color: var(--on-accent); padding: 0.5rem 0.875rem;
  border-radius: var(--radius); text-decoration: none; font-family: var(--display);
  transition: top 120ms ease;
}
.skip-link:focus { top: 0.5rem; }

/* ---------------------------------------------------------------- type */

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 0.5em;
  text-wrap: balance;
}
/* Fluid, because 42px is a poster headline on a 1280px screen and a wall of text on a 390px one: it
   turned a five-word heading into three lines and pushed the job sheet — the thing to actually do on
   that page — below the fold. Tops out at the same --t-h1 it always was on desktop. */
h1 { font-size: clamp(2rem, 1.4rem + 2.6vw, var(--t-h1)); }
h2 { font-size: clamp(1.625rem, 1.25rem + 1.6vw, var(--t-h2)); }
h3 { font-size: var(--t-h3); }
h4 { font-size: var(--t-h4); letter-spacing: -0.01em; }

@media (min-width: 60rem) {
  h1.hero-title { font-size: var(--t-mega); }
}

p { margin: 0 0 1rem; max-width: var(--measure); }

.lead { font-size: var(--t-lead); color: var(--ink-soft); }
.micro { font-size: var(--t-micro); color: var(--ink-faint); }

a { color: var(--accent-deep); text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
a:hover { color: var(--accent); }

/* Numbers that sit in a column — prices, job references, ratings — line up. */
.figures { font-family: var(--display); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- layout */

.wrap { width: 100%; max-width: 72rem; margin: 0 auto; padding: 0 var(--gutter); }
.wrap-narrow { max-width: 44rem; }
/* Article-length prose (/advice, /blog) reads better narrower than a card grid. */
.wrap-prose { max-width: 38rem; }

/* Vertical only — never the shorthand. `.section` is often on the same element as `.wrap`
   (`class="wrap … section"`), and `padding: 2.5rem 0` would reset .wrap's `padding: 0 var(--gutter)`
   to zero (same specificity, declared later), stripping the side gutter. Harmless on desktop where
   max-width centres the block, but on a phone it puts content flush against both screen edges. */
.section { padding-top: 2.5rem; padding-bottom: 2.5rem; }
.section + .section { border-top: 1px solid var(--rule-soft); }
@media (min-width: 48rem) { .section { padding-top: 3.5rem; padding-bottom: 3.5rem; } }

.stack > * + * { margin-top: 1rem; }
.stack-tight > * + * { margin-top: 0.5rem; }
/* On a list, also normalise the browser's own indent. Five bullet lists shared this class and two of
   them carried an inline indent while three did not, so the same class rendered two different ways. */
ul.stack-tight, ol.stack-tight { margin: 0; padding-left: 1.1rem; }
/* In copy pages (legal, about, help) the body lists use .stack-tight, but a paragraph following one
   then sat flush against it and read as an un-bulleted list item. Restore bottom spacing there only —
   docket stack-tight lists keep margin:0. */
.prose ul.stack-tight, .prose ol.stack-tight { margin-bottom: 1rem; }

.row { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }

.split { display: grid; gap: 2rem; }
@media (min-width: 56rem) { .split { grid-template-columns: 1.1fr 0.9fr; gap: 3rem; align-items: start; } }

/* ---------------------------------------------------------------- the docket
   Anything job-shaped is a job sheet: a boxed, ruled form with its reference in the header.
   Used only where it is literally true — a job, a lead, a quote. */

.docket {
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  /* Almost square, on purpose. An 8px radius with a tinted header is an identical rounded card with
     a soft header — the exact pattern this design's own avoid-list rejects, and what both reference
     sites already do. A job sheet has a square corner and a ruled line under its heading. Radius is
     also the difference a reader registers fastest, faster than hue. */
  border-radius: 2px;
}

.docket-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding: 0.75rem 1rem 0.625rem;
  /* A ruled line, not a fill. Dropping the tint also removes the need for overflow: hidden, which
     was clipping focus rings on controls at the edge of the box. */
  border-bottom: 2px solid var(--ink);
  font-family: var(--display);
}
/* h1 was missing here, so a docket heading that happened to be the page's h1 fell through to the
   42px generic rule and looked like a stray headline sitting above the form. */
.docket-head h1, .docket-head h2, .docket-head h3 { margin: 0; font-size: var(--t-h4); }

/* A page heading that sits ABOVE a docket rather than inside it: same ruled line, but it must not
   look like the top of a box it is not part of, so it loses the side padding. */
.docket-head-page { padding-left: 0; padding-right: 0; margin-bottom: 1.25rem; }

/* The job reference. A real code, so it is set as one. */
.docket-ref {
  font-family: var(--display);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  font-size: var(--t-micro);
  color: var(--ink-soft);
  white-space: nowrap;
}

.docket-body { padding: 1rem; }
@media (min-width: 40rem) { .docket-body { padding: 1.25rem 1.5rem; } }

/* Ruled rows inside a docket, the way a printed form divides its sections. */
.docket-rows { margin: 0; border-top: 1px solid var(--rule-soft); }
.docket-row {
  display: grid; gap: 0.125rem 1rem;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--rule-soft);
}
@media (min-width: 40rem) {
  .docket-row { grid-template-columns: 11rem 1fr; align-items: baseline; }
}
.docket-row dt, .docket-row .label {
  font-family: var(--display); font-weight: 500; font-size: var(--t-micro); color: var(--ink-soft);
}
.docket-row dd { margin: 0; }

.docket-foot {
  padding: 0.75rem 1rem;
  border-top: 1px solid var(--rule);
  background: var(--paper);
  font-size: var(--t-micro);
  color: var(--ink-soft);
}

/* ---------------------------------------------------------------- forms */

.field { margin-bottom: 1.125rem; }

/* Labels sit outside the box, as they do on a printed form. */
.field > label, .field-label {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--t-base);
  margin-bottom: 0.3125rem;
}
.field-hint { display: block; font-size: var(--t-micro); color: var(--ink-faint); margin-bottom: 0.375rem; }

input[type="text"], input[type="email"], input[type="tel"], input[type="password"],
input[type="search"], input[type="number"], input[type="url"], select, textarea {
  width: 100%;
  font: 400 var(--t-base)/1.4 var(--body);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  padding: 0.625rem 0.75rem;
  /* 16px minimum, or iOS Safari zooms the page on focus. */
}
textarea { min-height: 7rem; resize: vertical; line-height: 1.55; }
select { appearance: none; padding-right: 2.25rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.1rem) 1.15rem, calc(100% - 0.75rem) 1.15rem;
  background-size: 0.35rem 0.35rem, 0.35rem 0.35rem;
  background-repeat: no-repeat; }

input:focus, select:focus, textarea:focus { border-color: var(--accent); }

input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--brick);
}

/* An error says what happened and what to do, so it needs room to be a sentence. */
.field-error {
  display: block; margin-top: 0.375rem; font-size: var(--t-micro);
  color: var(--brick); font-weight: 600;
}

.postcode { max-width: 9rem; text-transform: uppercase; }

fieldset { border: 0; margin: 0 0 1.125rem; padding: 0; }
legend { font-family: var(--display); font-weight: 600; padding: 0; margin-bottom: 0.4375rem; }

.choice { display: flex; gap: 0.625rem; align-items: flex-start; padding: 0.4375rem 0; }
.choice input { margin: 0.2rem 0 0; flex: none; width: 1.125rem; height: 1.125rem; accent-color: var(--accent); }
.choice label { font-family: var(--body); font-weight: 400; margin: 0; }

.notice {
  border: 1px solid var(--rule); border-left: 3px solid var(--accent);
  background: var(--accent-wash); padding: 0.75rem 1rem; border-radius: var(--radius);
  /* Own margin-bottom so a notice never butts against what follows it (e.g. the /emergency "Fire"
     box, which is followed by low-top-margin content). Collapses with a following heading's top
     margin, so this adds no double spacing in the common case. */
  margin-bottom: 1rem;
}
.notice-error { border-left-color: var(--brick); background: var(--brick-wash); }
.notice-money { border-left-color: var(--hi); background: var(--hi-wash); }
.notice > :first-child, .docket-body > :first-child { margin-top: 0; }
.notice > :last-child,  .docket-body > :last-child  { margin-bottom: 0; }

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font: 600 var(--t-base)/1 var(--display);
  padding: 0.8125rem 1.25rem;
  min-height: 2.75rem;            /* a thumb target, for someone holding a phone one-handed */
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--on-accent);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease;
}
.btn:hover { background: var(--accent-hover); color: var(--on-accent); }
.btn:active { transform: translateY(1px); }

.btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--rule); }
.btn-secondary:hover { background: var(--paper); color: var(--ink); border-color: var(--ink-faint); }

.btn-quiet { background: none; color: var(--accent-deep); padding-inline: 0.25rem; }
.btn-quiet:hover { background: none; color: var(--accent); text-decoration: underline; }

.btn-danger { background: var(--brick); color: var(--on-brick); }
.btn-danger:hover { background: #91351f; }

.btn-block { width: 100%; }

.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }

/* ---------------------------------------------------------------- ruled lists
   266 categories and 100 locations. A card grid cannot show that much and would misrepresent
   how dense the data is. A trade directory has always read as a list. */

.index-group { margin-bottom: 2rem; }
.index-group > h3 {
  font-size: var(--t-h4);
  padding-bottom: 0.375rem;
  margin-bottom: 0;
  border-bottom: 2px solid var(--ink);
}

.index-list { list-style: none; margin: 0; padding: 0; columns: 1; }
@media (min-width: 40rem) { .index-list { columns: 2; column-gap: 2rem; } }
@media (min-width: 64rem) { .index-list { columns: 3; } }

.index-list li { break-inside: avoid; border-bottom: 1px solid var(--rule-soft); }

/* A long list stays single-column. Multi-column is harmless to a screen reader — DOM order is
   preserved — but hostile to someone using an OS magnifier, which cannot reflow the way pinch-zoom
   does: every column change is a pan right, reacquire, pan back. The 47-item county list was the
   worst case, and it appears on every trade page. Short per-group lists keep their columns. */
.index-list-long { columns: 1 !important; }
.index-list a {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: 0.5rem 0; text-decoration: none; color: var(--ink);
}
.index-list a:hover { color: var(--accent-deep); text-decoration: underline; }
.index-list .count {
  font-family: var(--display); font-variant-numeric: tabular-nums;
  font-size: var(--t-micro); color: var(--ink-faint); flex: none;
}
/* The cost-guide price is pushed right by space-between; when it wraps (a long range like
   "£30,000 to £100,000") its lines must still hug the right edge, not default to left. */
.index-list .price { text-align: right; }

/* ---------------------------------------------------------------- trade listing */

.pro { padding: 1.125rem 0; border-bottom: 1px solid var(--rule); display: grid; gap: 0.5rem; }
@media (min-width: 44rem) { .pro { grid-template-columns: 1fr auto; column-gap: 2rem; align-items: start; } }
.pro h3 { margin: 0 0 0.25rem; font-size: var(--t-h4); }
.pro-meta { font-size: var(--t-micro); color: var(--ink-soft); margin: 0; }
.pro-actions { display: flex; flex-direction: column; gap: 0.5rem; }

/* Rating. The number does the work; no five-star glyph row, which reads the same at every value. */
.rating { display: inline-flex; align-items: baseline; gap: 0.375rem; font-family: var(--display); }
.rating-score { font-size: var(--t-h4); font-weight: 700; font-variant-numeric: tabular-nums; }
.rating-count { font-size: var(--t-micro); color: var(--ink-faint); }

/* ---------------------------------------------------------------- badges
   Green says checked. Amber says money or a clock. Nothing else uses either. */

.badge {
  display: inline-flex; align-items: center; gap: 0.3125rem;
  font: 600 var(--t-micro)/1 var(--display);
  padding: 0.3125rem 0.5rem;
  border-radius: var(--radius);
  border: 1px solid currentColor;
}
.badge-checked { color: var(--accent-deep); background: var(--accent-wash); }
.badge-money   { color: var(--hi-ink); background: var(--hi-wash); }
/* Urgent must not read as the same amber pill as the price (.badge-money): a time-sensitive job and
   its cost were visually identical on the feed. Red carries the urgency; amber stays the money colour. */
.badge-urgent  { color: var(--brick); background: var(--brick-wash); }
.badge-quiet   { color: var(--ink-soft); background: var(--paper); border-color: var(--rule); }

.badge-row { display: flex; flex-wrap: wrap; gap: 0.375rem; }

/* The price of a lead, and a credit balance. The only places amber carries meaning. */
.price { font-family: var(--display); font-variant-numeric: tabular-nums; font-weight: 700; }

/* ---------------------------------------------------------------- header & footer */

.site-header {
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}
.site-header .wrap {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 3.75rem; flex-wrap: wrap; position: relative;
}
.brand {
  font: 700 var(--t-h4)/1 var(--display);
  letter-spacing: -0.02em;
  color: var(--accent-deep);
  text-decoration: none;
}
.brand:hover { color: var(--accent); }

.nav { display: flex; align-items: center; gap: 1rem; }

/* No-JS disclosure nav (CSS checkbox toggle, mobile-first). Below 44rem the links collapse
   behind the "Menu" toggle so the header stays one line, and open as a dropdown panel. */
.nav-toggle-checkbox { position: absolute; width: 1px; height: 1px; opacity: 0; }
.nav-toggle {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font: 500 var(--t-body)/1 var(--display); color: var(--ink);
  padding: 0.45rem 0.3rem; cursor: pointer; user-select: none;
}
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
  content: ""; display: block; width: 1.25rem; height: 2px; background: currentColor; border-radius: 2px;
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before { position: absolute; top: -6px; }
.nav-toggle-bars::after  { position: absolute; top: 6px; }
.nav-toggle-checkbox:focus-visible + .nav-toggle { outline: 3px solid var(--accent); outline-offset: 2px; }
.site-header .nav { display: none; }
.nav-toggle-checkbox:checked ~ .nav {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1rem;
  position: absolute; right: 0; top: 100%; z-index: 60;
  min-width: 12rem; padding: 1.1rem 1.25rem;
  background: var(--surface); border: 1px solid var(--rule); border-radius: 0.6rem;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.14);
}
@media (min-width: 44rem) {
  .nav-toggle { display: none; }
  .site-header .nav,
  .nav-toggle-checkbox:checked ~ .nav {
    position: static; display: flex; flex-direction: row; align-items: center; gap: 1rem;
    min-width: 0; padding: 0; background: none; border: 0; border-radius: 0; box-shadow: none;
  }
}
.nav a { font-family: var(--display); font-weight: 500; text-decoration: none; color: var(--ink); }
.nav a:hover { color: var(--accent-deep); text-decoration: underline; }
.nav a[aria-current="page"] { color: var(--accent-deep); text-decoration: underline; }

/* Sign out is a POST form in the nav; make its button read as a nav link, not a default button. */
.nav-inline-form { display: inline; margin: 0; }
.nav .linkish {
  font-family: var(--display); font-weight: 500; color: var(--ink);
  background: none; border: 0; padding: 0; cursor: pointer; font-size: inherit;
}
.nav .linkish:hover { color: var(--accent-deep); text-decoration: underline; }
.nav .linkish:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.breadcrumb { font-size: var(--t-micro); padding: 0.75rem 0 0; }
.breadcrumb ol { list-style: none; display: flex; flex-wrap: wrap; gap: 0.375rem; margin: 0; padding: 0; }
/* Separator trails its crumb (::after on all but the last) rather than leading the next one, so a
   long final crumb that wraps to a new line at phone width does not start with an orphaned "/". */
.breadcrumb li:not(:last-child)::after { content: "/"; margin-left: 0.375rem; color: var(--ink-faint); }

.site-footer {
  margin-top: 3rem;
  border-top: 1px solid var(--rule);
  background: var(--surface);
  padding: 2rem 0;
  font-size: var(--t-micro);
}
.footer-cols { display: grid; gap: 1.5rem; }
@media (min-width: 44rem) { .footer-cols { grid-template-columns: repeat(4, 1fr); } }
.footer-cols h2 { font-size: var(--t-micro); margin-bottom: 0.5rem; }
.footer-cols ul { list-style: none; margin: 0; padding: 0; }
.footer-cols li { margin-bottom: 0.3125rem; }
.footer-cols a { color: var(--ink-soft); text-decoration: none; }
.footer-cols a:hover { color: var(--accent-deep); text-decoration: underline; }

/* ---------------------------------------------------------------- how it works
   Numbered because it genuinely is a sequence. */

.steps { list-style: none; counter-reset: step; margin: 0; padding: 0; display: grid; gap: 1.25rem; }
@media (min-width: 48rem) { .steps { grid-template-columns: repeat(3, 1fr); gap: 2rem; } }
.steps li { counter-increment: step; padding-top: 0.75rem; border-top: 2px solid var(--accent); }
.steps li::before {
  content: counter(step);
  display: block;
  font: 700 var(--t-h3)/1 var(--display);
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  margin-bottom: 0.25rem;
}
.steps h3 { font-size: var(--t-h4); margin-bottom: 0.25rem; }
.steps p { margin: 0; font-size: var(--t-base); color: var(--ink-soft); }

/* ---------------------------------------------------------------- pagination */

.pagination { display: flex; gap: 0.375rem; flex-wrap: wrap; align-items: center; margin-top: 1.5rem; }
.pagination a, .pagination span {
  font-family: var(--display); font-variant-numeric: tabular-nums; font-size: var(--t-micro);
  padding: 0.5rem 0.6875rem; min-width: 2.25rem; text-align: center;
  border: 1px solid var(--rule); border-radius: var(--radius); text-decoration: none; color: var(--ink);
}
.pagination a:hover { border-color: var(--accent); color: var(--accent-deep); }
.pagination [aria-current="page"] { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

/* ---------------------------------------------------------------- tables (trade dashboard) */

.table-scroll { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: var(--t-micro); }
table.data th, table.data td { text-align: left; padding: 0.625rem 0.75rem; border-bottom: 1px solid var(--rule-soft); }
table.data th { font-family: var(--display); font-weight: 600; border-bottom: 2px solid var(--rule); white-space: nowrap; }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- empty states
   An empty screen is an invitation to act, not an apology. */

.empty {
  border: 1px dashed var(--rule);
  border-radius: var(--radius-lg);
  padding: 2rem 1.25rem;
  text-align: left;
  background: var(--surface);
}
.empty h3 { margin-bottom: 0.375rem; }
.empty p { color: var(--ink-soft); }

/* ---------------------------------------------------------------- trust rows
   A check is a row with a fact and a date, never a badge. Three reasons: a badge cannot carry a
   date, a badge that disappears when a certificate lapses is unauditable, and a wall of badges is
   what every competitor already has. The "not checked" rows use --unchecked, so the page can say
   we do not know without either hiding it or condemning the trade. */

.checks { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule-soft); }

.check {
  display: grid; gap: 0.125rem 1rem;
  /* Mark | what | when on one row at every width — without a column template the mobile grid
     stacked each cell, leaving the "–" mark alone on its own line. Detail spans below (2 / -1). */
  grid-template-columns: 1.5rem 1fr auto; align-items: baseline;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--rule-soft);
}

.check-mark { font-family: var(--display); font-weight: 700; color: var(--accent); }
.check-what { font-weight: 600; }
.check-when {
  font-family: var(--display); font-variant-numeric: tabular-nums;
  font-size: var(--t-micro); color: var(--ink-soft); white-space: nowrap;
}
.check-detail { grid-column: 2 / -1; font-size: var(--t-micro); color: var(--ink-soft); margin: 0; }

/* A lapsed check KEEPS its row. Removing it would quietly rewrite history. */
.check-lapsed .check-mark,
.check-lapsed .check-when { color: var(--brick); }

/* Not checked: stated, not hidden, and not dressed up as a failure. */
.check-unknown .check-mark,
.check-unknown .check-when { color: var(--unchecked); }
.check-unknown .check-what { font-weight: 400; color: var(--ink-soft); }

/* The sentence that makes the whole block credible. */
.checks-caveat {
  margin: 1rem 0 0;
  padding: 0.75rem 1rem;
  background: var(--unchecked-wash);
  border-left: 3px solid var(--unchecked);
  border-radius: var(--radius);
  font-size: var(--t-micro);
  color: var(--ink);
}

/* A profile with nothing verified yet gets no green anywhere. That rule is what makes green mean
   something on the profiles that have earned it. */
.badge-unchecked { color: var(--unchecked); background: var(--unchecked-wash); }

/* ---------------------------------------------------------------- utilities
   A small spacing scale, so a one-off gap between blocks does not become an inline style. Named by
   size rather than by purpose, because the purpose varies and the size does not. */

.mt-sm { margin-top: 0.75rem; }
.mt-md { margin-top: 1.25rem; }
.mt-lg { margin-top: 1.5rem; }
.mt-xl { margin-top: 2rem; }

/* The inline search on /trades: one coherent layout rather than three unrelated spacing hacks. */
.search-inline { margin-bottom: 1.5rem; }
.search-inline .field { margin: 0; flex: 1 1 16rem; }
.search-inline .btn { align-self: flex-end; }

/* An error that belongs on the same line as the value it is about, rather than under it. */
.field-error-inline { display: inline; margin-top: 0; }

/* ---------------------------------------------------------------- messages
   A conversation, not a chat bubble UI. Both sides read the same transcript in the same order, and
   "mine" is marked by a rule down the side rather than by floating right — a right-floated column is
   unreadable at 390px once a message runs to more than a line, which is most of them here. */
.messages { list-style: none; margin: 0; padding: 0; }

.message {
  border: 1px solid var(--rule-soft);
  border-left: 3px solid var(--rule-strong);
  background: var(--surface);
  padding: 0.75rem 1rem;
  margin-bottom: 0.75rem;
}
.message:last-child { margin-bottom: 0; }
.message p { margin: 0; }
.message p + p { margin-top: 0.25rem; }
.message p.micro { color: var(--ink-soft); }

/* Your own, so you can find your place in a long thread at a glance. */
.message-mine { border-left-color: var(--accent); background: var(--accent-wash); }

/* ---------------------------------------------------------------- browse-all disclosure
   A native <details>, so a hundred county links stay in the page for anything that reads it without
   being the first thing a person has to wade through. No JavaScript involved. */
.browse-all { margin-top: 1.5rem; border-top: 1px solid var(--rule); padding-top: 1rem; }
.browse-all > summary {
  font-family: var(--display);
  font-weight: 600;
  cursor: pointer;
  padding: 0.5rem 0;
}
.browse-all > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.browse-all[open] > summary { margin-bottom: 0.5rem; }

/* ---------------------------------------------------------------- job photos */
.photo-grid {
  list-style: none; padding: 0; margin: 1rem 0 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 1rem;
}
.photo-item { margin: 0; }
.photo-item img {
  display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover;
  border: 1px solid var(--rule); border-radius: 0.4rem; background: var(--paper);
}
.photo-delete { margin-top: 0.35rem; color: var(--brick); }
.photo-strip { list-style: none; padding: 0; margin: 0.5rem 0 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
.photo-strip img { display: block; width: 5rem; height: 3.75rem; object-fit: cover; border: 1px solid var(--rule); border-radius: 0.3rem; }
.photo-more {
  display: inline-flex; align-items: center; justify-content: center; width: 3.75rem; height: 3.75rem;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 0.3rem;
  color: var(--ink-soft); font: 600 var(--t-micro)/1 var(--display);
}
.qr { display: block; width: 190px; height: 190px; margin: 0.75rem 0; border: 1px solid var(--rule); border-radius: 0.4rem; background: #fff; padding: 0.5rem; }

/* Narrow screens: let data tables (e.g. the /pricing "cost to take" table) fit instead of clipping
   a column off-screen behind an invisible touch scrollbar — wrap the headers and tighten spacing. */
@media (max-width: 32rem) {
  .table-scroll table.data { font-size: var(--t-micro); }
  .table-scroll table.data th,
  .table-scroll table.data td { padding: 0.45rem 0.4rem; }
  .table-scroll table.data th { white-space: normal; }
}
