/* TenderScan — the free UK tender search site.

   Same family as Bidvane, and the same decisions the IL search arrived at after
   it shipped — LTR English. Tokens are the shared design tokens (tokens.css)
   restated rather than imported: this site is served to strangers arriving cold
   from Google, it must paint in one round trip, and the app's stylesheet is
   373KB of things a search page never uses.

   Logical properties throughout (inline-start/end, not left/right) because this
   is a translation of an RTL design, and every mirrored margin is a bug waiting
   for the first Hebrew string to be pasted back in.

   Where this looks unlike a generic search page, it is because IL got there
   first and the reason held: the query frame is SQUARE on the house radius, not
   a pill, and the submit stretches to the frame's content box rather than
   guessing a height.

   Where it differs from IL, there is one place and it is deliberate: the button
   is near-black rather than seal. See --btn. */

:root {
  --paper: #FAFAF7;
  --surface: #FFFFFF;
  --ink: #1C2B33;
  --ink-soft: #5A6B73;
  --line: #E3E5DE;
  --line-hover: #D3D6CD;
  --accent: #E8650D;
  --accent-ink: #B14A05;
  --accent-wash: #FCEFE4;
  --seal: #16557A;
  --seal-wash: #E8EEF3;
  /* A quiet dark button: the ink the body text already uses, not a near-black
     slab, and no orange bar across the bottom.
     The bar was too loud beside a page this calm, and it was also spending the
     accent on decoration — orange does real work here (a deadline inside seven
     days, a planning badge), and a colour that means "urgent" in one place and
     "button" in another means neither. The wordmark keeps the dot. */
  --btn: #1C2B33;
  --btn-hover: #14212A;
  --r-card: 10px;
  --r-control: 8px;
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;
  --t-xs: 13px; --t-sm: 15px; --t-md: 18px; --t-lg: 24px;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-sm);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--seal); }

.ts-shell { max-width: 1080px; margin-inline: auto; padding-inline: 22px; }
.ts-main { padding-block: var(--s5) 60px; }
.ts-hero { width: min(680px, 100%); margin-inline: auto; text-align: center;
  padding-block: 8vh var(--s6); }

/* ── Header ──────────────────────────────────────────────────────────────────
   Sticky and translucent so the query box stays reachable while results scroll
   under it. The blur is what stops white cards showing through as a smear. */
.ts-head {
  position: sticky; top: 0; z-index: 5;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface) 85%, transparent);
  backdrop-filter: saturate(1.4) blur(8px);
}
.ts-head-inner {
  max-width: 1080px; margin-inline: auto; padding: 10px 22px;
  display: flex; align-items: center; gap: 18px;
}
.ts-mark { font-size: 21px; font-weight: 700; letter-spacing: -.015em;
  color: var(--ink); text-decoration: none; flex: none; }
.ts-mark-lg { font-size: clamp(40px, 7.5vw, 62px); font-weight: 700;
  letter-spacing: -.02em; display: block; margin-bottom: 8px; }
/* The family signature — the one mark that says who built this.

   Drawn, not typed. As the "·" character its position was whatever the font
   felt like: middle-dot metrics vary between faces and it floated above the
   letters at some sizes. A circle placed in em units sits on the optical
   centre of the final "n" at every size, from the 21px header mark to the
   62px hero — x-height is ~0.52em, so its centre is ~0.26em above the
   baseline, and the dot is nudged to sit on exactly that line. */
.ts-dot { display: inline-block; inline-size: .17em; block-size: .17em;
  border-radius: 50%; background: var(--accent);
  margin-inline-start: .07em; position: relative; bottom: .175em; }
.ts-bidvane { margin-inline-start: auto; font-size: 14px; color: var(--ink-soft);
  text-decoration: none; white-space: nowrap; flex: none; }
.ts-bidvane:hover { color: var(--ink); text-decoration: underline; }

.ts-tagline { color: var(--ink-soft); margin: 0 0 var(--s5); font-size: 16px; }

/* ── The query frame ─────────────────────────────────────────────────────────
   SQUARE, on the site's own card radius. A pill is the generic search-engine
   shape; this is the shape everything else here is. The FRAME owns the focus
   state — the input inside has no border, no outline and no shadow of its
   own — so the whole control lights up as one object. */
.ts-form {
  position: relative;
  display: flex; align-items: center; gap: 6px; width: 100%;
  border: 1px solid var(--line); background: var(--surface);
  border-radius: var(--r-card);
  padding: 5px 5px 5px 6px;
  box-shadow: 0 1px 2px rgb(28 43 51 / .04), 0 6px 18px -10px rgb(28 43 51 / .18);
  transition: box-shadow .16s ease, border-color .16s ease;
}
.ts-form:hover { border-color: var(--line-hover);
  box-shadow: 0 1px 2px rgb(28 43 51 / .05), 0 10px 26px -12px rgb(28 43 51 / .24); }
/* The frame glows warm on focus, not blue — so the lit frame and the black
   button read as one control with one accent, rather than two colours meeting
   in the middle. */
.ts-form:focus-within { border-color: var(--btn);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 15%, transparent),
              0 10px 30px -12px rgb(28 43 51 / .28); }

/* Decorative — the input carries its own aria-label. */
.ts-form-icon { flex: none; display: grid; place-items: center; inline-size: 22px;
  color: var(--ink-soft); margin-inline-start: 10px; transition: color .16s ease; }
.ts-form:focus-within .ts-form-icon { color: var(--seal); }
.ts-form-icon svg { display: block; }

.ts-input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  font: inherit; font-size: 16.5px; padding: 11px 10px; color: var(--ink);
  border-radius: 6px; box-shadow: none; width: auto; }
.ts-input:hover, .ts-input:focus { border: 0; outline: 0; box-shadow: none; }
.ts-input::placeholder { color: var(--ink-soft); opacity: .75; }
/* Chrome draws its own clear affordance in a search field; ours is the frame. */
.ts-input::-webkit-search-decoration,
.ts-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

/* align-self: stretch rather than a hand-tuned height — the button fills the
   frame's content box exactly, so the two stay flush if the font or the padding
   ever changes. */
.ts-submit { flex: none; align-self: stretch; display: grid; place-items: center;
  border: 0; border-radius: var(--r-control); cursor: pointer;
  background: var(--btn); color: #fff; font: inherit; font-weight: 500;
  font-size: 14.5px; padding: 0 26px; line-height: 1.2;
  transition: background .12s ease, transform .12s ease; }
.ts-submit:hover { background: var(--btn-hover); }
.ts-submit:active { transform: translateY(1px); }
/* The accent survives where it is doing a job rather than decorating one: as
   the focus ring, which is the moment a keyboard user needs to be shouted at. */
.ts-submit:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Compact variant on a results page: same frame, less air. */
.ts-form-compact { max-width: 640px; padding: 3px 3px 3px 4px;
  box-shadow: 0 1px 2px rgb(28 43 51 / .04); }
.ts-form-compact .ts-form-icon { inline-size: 18px; margin-inline-start: 8px; }
.ts-form-compact .ts-input { font-size: 15px; padding: 9px 8px; }
.ts-form-compact .ts-submit { padding: 0 20px; font-size: 14px; }

/* ── Typeahead ───────────────────────────────────────────────────────────── */
.ts-suggest { position: absolute; inset-inline: 0; top: calc(100% + 8px);
  margin: 0; padding: 6px; list-style: none; z-index: 20; text-align: start;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: 0 18px 40px -18px rgb(28 43 51 / .45), 0 2px 6px rgb(28 43 51 / .05);
  max-height: 340px; overflow-y: auto; }
.ts-suggest[hidden] { display: none; }
.ts-suggest-item { display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 9px 14px; border-radius: 6px; font-size: 15px; color: var(--ink);
  transition: background .1s ease; }
.ts-suggest-item:hover, .ts-suggest-item.is-active { background: var(--seal-wash); }
.ts-suggest-item.is-active { color: var(--seal); }
.ts-suggest-kind { margin-inline-start: auto; font-size: 11px; color: var(--ink-soft); }

/* ── Example queries on the empty state ──────────────────────────────────── */
.ts-examples { display: flex; flex-wrap: wrap; gap: var(--s2); justify-content: center;
  margin-top: var(--s5); }
.ts-hero .ts-adv { margin-top: var(--s3); }
.ts-example { font-size: 13px; padding: 5px 12px; border-radius: 999px;
  text-decoration: none; color: var(--ink-soft); background: var(--surface);
  border: 1px solid var(--line); transition: border-color .14s ease, color .14s ease; }
.ts-example:hover { color: var(--seal);
  border-color: color-mix(in srgb, var(--seal) 40%, var(--line)); }
/* Native to the frame, not a banner under it: sits close enough to read as the
   box's own caption, and quiet enough that the numbers inform without
   competing with the thing you came here to type in. */
.ts-counts { margin: 10px 0 0; color: var(--ink-soft); font-size: 12.5px;
  line-height: 1.5; }
.ts-counts b { color: var(--ink-soft); font-weight: 600;
  font-variant-numeric: tabular-nums; }

/* ── How the query was read ──────────────────────────────────────────────── */
.ts-chips { display: flex; flex-wrap: wrap; gap: var(--s2); margin: var(--s5) 0 var(--s4); }
.ts-chip { display: inline-flex; align-items: center; gap: 7px; font-size: 13px;
  padding: 5px 12px; border-radius: 999px; font-weight: 500;
  background: color-mix(in srgb, var(--seal) 10%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--seal) 26%, transparent);
  color: var(--seal); }
/* A removable chip reads as a control, so it gets a hover and a close mark. */
.ts-chip-x { text-decoration: none; gap: 8px; transition: border-color .14s ease; }
.ts-chip-x span { font-size: 14px; line-height: 1; opacity: .55; }
.ts-chip-x:hover { border-color: color-mix(in srgb, var(--seal) 50%, transparent); }
.ts-chip-x:hover span { opacity: 1; }
.ts-chip-region { background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  color: var(--accent-ink); }

/* ── Filters ─────────────────────────────────────────────────────────────── */
.ts-filters { display: flex; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s4); }
.ts-filter { position: relative; display: inline-flex; align-items: center; }
.ts-filter select { appearance: none; -webkit-appearance: none;
  font: inherit; font-size: 13px; color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: 999px;
  padding-block: 6px; padding-inline: 15px 34px;
  background: var(--surface); width: auto; cursor: pointer;
  transition: border-color .14s ease, color .14s ease; }
.ts-filter select:hover { border-color: var(--line-hover); color: var(--ink); }
.ts-filter.is-set select { color: var(--seal);
  border-color: color-mix(in srgb, var(--seal) 30%, var(--line)); }
/* The chevron, drawn rather than shipped as an image. */
.ts-filter::after { content: ""; position: absolute; inset-inline-end: 14px;
  width: 8px; height: 8px; pointer-events: none;
  border-inline-end: 1.6px solid currentColor; border-block-end: 1.6px solid currentColor;
  color: var(--ink-soft); transform: translateY(-2px) rotate(45deg); }

/* The count line introduces the list; at 16px it read as part of the first
   card instead of a label above it. */
.ts-meta { font-size: var(--t-xs); color: var(--ink-soft); margin: 0 0 var(--s5); }
.ts-meta b { color: var(--ink); font-variant-numeric: tabular-nums; }
.ts-note { color: var(--accent-ink); }

/* ── Results ─────────────────────────────────────────────────────────────── */
/* Cards are separated by space, not by rules. At 12px they read as one banded
   table; the gap has to be wide enough that each card is its own object,
   because the hover lift assumes it is. */
.ts-results { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--s4); }

/* The decision strip: source → title → snippet → the facts that decide it. */
.ts-card { background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: 15px 17px;
  transition: border-color .14s ease, box-shadow .14s ease, transform .14s ease; }
.ts-card:hover { border-color: var(--line-hover); transform: translateY(-1px);
  box-shadow: 0 10px 26px -16px rgb(28 43 51 / .45); }
.ts-card.is-closed { opacity: .72; }

/* Line one: who, what kind, when. One size, one colour, so it reads as a
   single caption rather than three competing data points — the buyer is the
   only thing in it allowed to look clickable. */
.ts-card-meta { font-size: 12.5px; color: var(--ink-soft); display: flex;
  flex-wrap: wrap; align-items: baseline; gap: 7px; }
.ts-card-buyer { color: var(--ink); font-weight: 600; text-decoration: none;
  max-width: 46ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ts-card-buyer:hover { color: var(--seal); text-decoration: underline;
  text-underline-offset: 3px; }
.ts-sep { color: var(--line-hover); }
/* The stage badge, pushed to the end of the line: it qualifies the whole card,
   not the buyer, and at the end it lands in the same place on every card that
   has one — which is what makes a page of results scannable for it. */
.ts-tag-planning { margin-inline-start: auto; color: var(--accent-ink);
  font-weight: 600; background: var(--accent-wash); padding: 1px 9px;
  border-radius: 999px; font-size: 11.5px; letter-spacing: .01em; }

/* The only large thing on the card. Two lines maximum: a title that runs to
   four sets every card below it at a different rhythm and the list stops
   reading as a list. */
.ts-card-title { margin: 5px 0 5px; font-size: 17px; line-height: 1.32;
  font-weight: 600; letter-spacing: -.006em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }
.ts-card-title a { color: var(--seal); text-decoration: none; }
.ts-card-title a:hover { text-decoration: underline; text-underline-offset: 3px; }
.ts-card-title a:visited { color: color-mix(in srgb, var(--seal) 72%, #6b4b8a); }
.ts-card-snip { margin: 0 0 11px; font-size: 13.5px; line-height: 1.55;
  color: var(--ink-soft);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }

/* The decision row. No captions and no dividing rules: three short facts that
   say what they are, sitting on the card rather than in a table drawn on it. */
.ts-cfacts { display: flex; flex-wrap: wrap; gap: 6px; }
.ts-cfact { font-size: 12px; font-weight: 600; color: var(--ink);
  background: var(--paper); border: 1px solid var(--line);
  padding: 3px 10px; border-radius: 999px;
  font-variant-numeric: tabular-nums; }
.ts-cfact-region { font-weight: 500; color: var(--ink-soft); }
/* The one place on a result card that gets colour. Inside seven days only —
   if every deadline were orange the colour would mean "deadline", which the
   word already says. */
.ts-cfact.is-urgent { color: var(--accent-ink); background: var(--accent-wash);
  border-color: color-mix(in srgb, var(--accent) 32%, transparent); }
.ts-cfact.is-muted { color: var(--ink-soft); font-weight: 500;
  background: transparent; }

.ts-zero { padding: var(--s6) 0; }
.ts-zero h2 { margin: 0 0 var(--s2); font-size: var(--t-lg); font-weight: 500; }
.ts-zero p { margin: 0; color: var(--ink-soft); max-width: 52ch; }

/* Pagination. Twelve bordered boxes is a toolbar, not a page index — the
   borders were drawing more attention than the numbers inside them. Now the
   numbers are just numbers, the page you are on is the only one wearing the
   site's button, and prev/next are words rather than another box. */
.ts-pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 2px; margin-top: var(--s6); padding-top: var(--s4);
  border-top: 1px solid var(--line); }
/* Every page carries a transparent border, so the one that lights up changes
   colour without changing size and the row never shifts by a pixel. */
.ts-page { font-size: var(--t-xs); min-width: 34px; text-align: center;
  padding: 7px 9px; border-radius: var(--r-control);
  border: 1px solid transparent;
  text-decoration: none; color: var(--ink-soft); background: transparent;
  font-variant-numeric: tabular-nums;
  transition: background .12s ease, color .12s ease, border-color .12s ease; }
.ts-page:hover { background: color-mix(in srgb, var(--ink) 6%, transparent);
  color: var(--ink); }
/* The current page is marked, not shouted: a plain border and darker ink. The
   button treatment belongs to things you press to make something happen, and
   "you are here" is not one of them. */
.ts-page.is-current { background: var(--surface); color: var(--ink); font-weight: 600;
  border-color: var(--line-hover); box-shadow: none; }
.ts-page.is-current:hover { background: var(--surface); }
/* The two that are words, not numbers: pushed away from the digits so they
   read as directions rather than as page "0" and page "11". */
.ts-page-prev { margin-inline-end: var(--s3); }
.ts-page-next { margin-inline-start: var(--s3); }
.ts-page-prev, .ts-page-next { color: var(--seal); font-weight: 500; }
/* "of 18" — the honest tail when there are more pages than links. Without it
   the row of ten just stops, and the reader cannot tell whether page 10 is the
   end or the end of the list of links. */
/* The ellipsis is a gap in the sequence, not a page you can click — it gets
   the row's spacing but none of its affordance. */
.ts-page-gap { font-size: var(--t-xs); color: var(--line-hover);
  padding-inline: 4px; user-select: none; }

/* ── Alerts ──────────────────────────────────────────────────────────────── */
.ts-alert { margin-top: var(--s6); padding: var(--s4) var(--s5);
  border: 1px solid color-mix(in srgb, var(--seal) 22%, transparent);
  border-radius: 14px; background: color-mix(in srgb, var(--seal) 5%, var(--surface)); }
.ts-alert-copy { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--s3); }
.ts-alert-copy strong { font-weight: 600; }
.ts-alert-copy span { font-size: var(--t-xs); color: var(--ink-soft); }
.ts-alert-row { display: flex; gap: var(--s2); flex-wrap: wrap; }
.ts-alert-input { flex: 1 1 240px; font: inherit; font-size: 14.5px;
  padding: 10px var(--s3); border: 1px solid var(--line);
  border-radius: var(--r-control); background: var(--surface); }
.ts-alert-input:focus { outline: 0; border-color: var(--seal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--seal) 13%, transparent); }
.ts-alert-btn { font: inherit; font-weight: 500; font-size: 14.5px; border: 0;
  cursor: pointer; border-radius: var(--r-control); padding: 10px var(--s5);
  background: var(--btn); color: #fff; }
.ts-alert-btn:hover { background: var(--btn-hover); }
.ts-alert-fine { margin: var(--s2) 0 0; font-size: 11.5px; color: var(--ink-soft); }

/* ── A notice page ───────────────────────────────────────────────────────────
   Bidvane's own detail anatomy, translated: main column plus a sticky aside
   whose facts are a dashed label/value list, not tiles. Tiles look tidy with
   four facts and fall apart with nine, which is how many a real notice has. */
.ts-detail { max-width: 1080px; }
.ts-crumb { font-size: 12.5px; color: var(--ink-soft);
  display: flex; flex-wrap: wrap; gap: 7px; align-items: baseline; }
.ts-crumb a { color: var(--ink-soft); text-decoration: none; }
.ts-crumb a:hover { color: var(--seal); text-decoration: underline; }

/* The lead answers "can I still bid" before the title says what it is. */
.ts-detail-lead { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s3);
  margin-top: var(--s4); }
.ts-badge { font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  padding: 3px 10px; border-radius: 999px; }
.ts-badge-live { background: color-mix(in srgb, #1E7F4F 12%, var(--surface));
  color: #1E7F4F; border: 1px solid color-mix(in srgb, #1E7F4F 30%, transparent); }
.ts-badge-planning { background: var(--accent-wash); color: var(--accent-ink);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); }
/* The countdown, as IL renders it: a pill rather than a coloured word.
   Colour alone was too quiet — a bidder scanning the page read "4 days left"
   as ordinary text and only noticed the number after reading it. Inside seven
   days it takes the accent wash and border, which is the one place on the site
   the accent means anything. */
.ts-lead-deadline { font-size: 12.5px; font-weight: 600; color: var(--ink-soft);
  padding: 4px 10px; border-radius: 999px; background: var(--paper);
  border: 1px solid var(--line); font-variant-numeric: tabular-nums; }
.ts-lead-deadline.is-urgent { color: var(--accent-ink);
  background: var(--accent-wash);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.ts-lead-note { font-size: 13px; color: var(--ink-soft); }

.ts-detail-title { font-size: clamp(26px, 3.2vw, 34px); font-weight: 500;
  line-height: 1.2; margin: var(--s2) 0 var(--s2); text-wrap: balance;
  letter-spacing: -.01em; }
.ts-detail-buyer { margin: 0 0 var(--s5); color: var(--ink-soft); font-size: var(--t-sm);
  display: flex; flex-wrap: wrap; gap: 7px; align-items: baseline; }
.ts-detail-buyer a { color: var(--seal); text-decoration: none; font-weight: 500; }
.ts-detail-buyer a:hover { text-decoration: underline; text-underline-offset: 3px; }
.ts-detail-more { font-weight: 400 !important; }

.ts-detail-grid { display: grid; gap: var(--s6);
  grid-template-columns: minmax(0, 1fr) 330px; align-items: start; }
@media (max-width: 900px) {
  /* The panel goes FIRST on a phone: the facts and the source link are what a
     bidder came for, and burying them under prose costs a scroll. */
  .ts-detail-grid { grid-template-columns: 1fr; }
  .ts-detail-side { order: -1; }
}

.ts-block { margin-bottom: var(--s6); }
.ts-block h2 { font-size: var(--t-md); font-weight: 600; margin: 0 0 var(--s3);
  display: flex; align-items: baseline; gap: var(--s2); }
.ts-block p { margin: 0; max-width: 68ch; line-height: 1.7; }
.ts-labels { display: flex; flex-wrap: wrap; gap: var(--s2); }
.ts-label { font-size: 12.5px; padding: 4px 11px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line); color: var(--ink-soft); }
.ts-label-cpv { background: var(--seal-wash); color: var(--seal);
  border-color: color-mix(in srgb, var(--seal) 20%, transparent); }
.ts-label-region { background: var(--accent-wash); color: var(--accent-ink);
  border-color: color-mix(in srgb, var(--accent) 22%, transparent); }
.ts-docs { margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 2px; }
.ts-docs li a { display: block; padding: 7px 0; color: var(--seal);
  border-bottom: 1px dashed var(--line); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.ts-detail-provenance { font-size: 12.5px; color: var(--ink-soft); margin: 0;
  padding-top: var(--s4); border-top: 1px solid var(--line); max-width: 68ch; }

/* The aside. Sticky, because on a long notice the source link is the thing the
   reader is reaching for and it should never scroll away. */
.ts-detail-side { position: sticky; top: 84px; display: flex;
  flex-direction: column; gap: var(--s3); }
@media (max-width: 900px) { .ts-detail-side { position: static; } }
.ts-panel { background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: var(--s4) var(--s4) var(--s3); }
.ts-panel-h { font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-soft); margin: 0 0 var(--s3); }

/* Bidvane's fact row: label and value on one grid axis, dashed rule between.
   Values are right-aligned and bold so the column scans as data. */
.ts-facts-list { margin: 0 0 var(--s4); display: flex; flex-direction: column; gap: 7px; }
.ts-facts-list > div { display: grid; grid-template-columns: fit-content(52%) 1fr;
  align-items: start; column-gap: var(--s3); font-size: 13.5px;
  border-bottom: 1px dashed var(--line); padding-bottom: 7px; }
.ts-facts-list > div:last-child { border-bottom: 0; padding-bottom: 0; }
.ts-facts-list dt { color: var(--ink-soft); }
.ts-facts-list dd { margin: 0; color: var(--ink); font-weight: 700; text-align: end;
  min-width: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.ts-facts-list dd.is-urgent { color: var(--accent-ink); }
.ts-facts-list dd.is-muted { color: var(--ink-soft); font-weight: 500; }

/* The most important action on the page: full width, its destination named,
   and an out-of-box glyph so leaving the site is stated rather than implied. */
.ts-source-btn { display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); width: 100%; text-align: start; padding: 11px var(--s4);
  transition: background .12s ease, transform .12s ease, box-shadow .12s ease; }
.ts-source-copy { display: flex; flex-direction: column; line-height: 1.25; }
.ts-source-copy b { font-weight: 600; font-size: 14.5px; }
.ts-source-copy small { font-size: 11.5px; opacity: .72; margin-top: 2px; }
.ts-source-ic { flex: none; opacity: .8; }
.ts-source-btn:hover { transform: translateY(-1px);
  box-shadow: 0 8px 18px -12px rgb(28 43 51 / .5); }
.ts-source-btn:hover .ts-source-ic { opacity: 1; }
.ts-source-btn:active { transform: translateY(0); box-shadow: none; }
.ts-convert { margin: var(--s3) 0 0; font-size: 12.5px; text-align: center; }

/* ── Grids ───────────────────────────────────────────────────────────────── */
.ts-grid-h1 { font-size: 30px; font-weight: 500; margin: var(--s5) 0 var(--s2);
  text-wrap: balance; }
.ts-grid-intro { color: var(--ink-soft); margin: 0 0 var(--s5); max-width: 68ch;
  line-height: 1.65; }
.ts-grid-more { margin: var(--s4) 0 0; font-size: var(--t-xs); }
.ts-crosslinks { margin-top: var(--s6); padding-top: var(--s5);
  border-top: 1px solid var(--line); }
.ts-crosslinks h2 { font-size: var(--t-md); font-weight: 600; margin: 0 0 var(--s3); }
.ts-crosslinks ul { list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--s2); }
.ts-crosslinks li { font-size: 13px; }
.ts-crosslinks a { display: inline-flex; align-items: baseline; gap: 6px;
  padding: 5px 12px; border-radius: 999px; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line); color: var(--seal); }
.ts-crosslinks a:hover { border-color: color-mix(in srgb, var(--seal) 40%, var(--line)); }
.ts-count { color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* ── Short pages ─────────────────────────────────────────────────────────── */
.ts-page-body { max-width: 68ch; padding-block: var(--s5) var(--s6); }
.ts-page-body h1 { font-size: 30px; font-weight: 500; margin: 0 0 var(--s4); }
.ts-page-body h2 { font-size: var(--t-md); font-weight: 600; margin: var(--s5) 0 var(--s2); }
.ts-page-body p { margin: 0 0 var(--s3); line-height: 1.65; }

/* ── The holding page ────────────────────────────────────────────────────── */
.ts-holding { min-height: 70vh; display: grid; place-content: center;
  text-align: center; gap: var(--s3); }
.ts-holding h1 { font-size: clamp(36px, 7vw, 56px); font-weight: 700; margin: 0;
  letter-spacing: -.02em; }
.ts-holding p { margin: 0; color: var(--ink-soft); max-width: 42ch; }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.ts-foot { margin-top: var(--s7); border-top: 1px solid var(--line);
  padding-block: var(--s5) var(--s6); font-size: var(--t-xs); color: var(--ink-soft); }
.ts-foot-links { display: flex; flex-wrap: wrap; gap: var(--s4); margin-bottom: var(--s3); }
.ts-foot a { color: var(--ink-soft); }
.ts-foot a:hover { color: var(--ink); }
.ts-attrib { margin: 0; }
.ts-attrib a { color: var(--seal); font-weight: 500; }

/* ── Ads ─────────────────────────────────────────────────────────────────── */
/* Result-shaped and unboxed: the label is what distinguishes it, per the spec.
   An unfilled unit collapses rather than leaving a hole in the list. */
.ts-ad { margin-bottom: var(--s3); }
.ts-ad:empty { display: none; }
.ts-ad-label { display: block; font-size: 11px; font-weight: 700;
  letter-spacing: .02em; color: var(--ink-soft); margin-bottom: 2px; }

@media (max-width: 620px) {
  .ts-shell { padding-inline: 16px; }
  .ts-head-inner { padding-inline: 16px; }
  .ts-hero { padding-block: 5vh var(--s5); }
  .ts-submit { padding: 0 16px; }
  /* Chips wrap on their own; the badge stops being pushed to the end once the
     meta line itself wraps, where "the end" is a different place per card. */
  .ts-tag-planning { margin-inline-start: 0; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  .ts-card:hover { transform: none; }
}

/* ── Contact form ────────────────────────────────────────────────────────── */
.ts-contact { display: flex; flex-direction: column; gap: var(--s4);
  margin-top: var(--s5); max-width: 34rem; }
.ts-field { display: flex; flex-direction: column; gap: 5px; }
.ts-field label { font-size: var(--t-xs); font-weight: 600; color: var(--ink); }
.ts-opt { font-weight: 400; color: var(--ink-soft); }
.ts-field input, .ts-field textarea { font: inherit; font-size: 14.5px;
  padding: 10px var(--s3); border: 1px solid var(--line);
  border-radius: var(--r-control); background: var(--surface); color: var(--ink);
  transition: border-color .14s ease, box-shadow .14s ease; }
.ts-field textarea { resize: vertical; line-height: 1.55; }
.ts-field input:focus, .ts-field textarea:focus { outline: 0;
  border-color: var(--btn);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 15%, transparent); }
.ts-field .ts-filter { display: block; }
.ts-field .ts-filter select { width: 100%; border-radius: var(--r-control);
  padding-block: 10px; font-size: 14.5px; color: var(--ink); }
.ts-submit-block { padding: 12px var(--s5); justify-self: start; width: max-content; }
.ts-field-fine { margin: 0; font-size: 11.5px; color: var(--ink-soft); }
/* Off-screen rather than display:none — some bots skip hidden inputs, and a
   honeypot they skip is a honeypot that catches nothing. */
.ts-hp { position: absolute; inset-inline-start: -9999px; width: 1px; height: 1px;
  overflow: hidden; }

.ts-notice { border-radius: 14px; padding: var(--s3) var(--s4); margin-bottom: var(--s4);
  border: 1px solid var(--line); background: var(--surface); }
.ts-notice p { margin: 4px 0 0; color: var(--ink-soft); font-size: 14px; }
.ts-notice-ok { border-color: color-mix(in srgb, #1E7F4F 30%, transparent);
  background: color-mix(in srgb, #1E7F4F 5%, var(--surface)); }
.ts-notice-bad { border-color: color-mix(in srgb, var(--accent) 35%, transparent);
  background: var(--accent-wash); }
.ts-page-foot { margin-top: var(--s6); font-size: var(--t-xs); color: var(--ink-soft); }

/* Notice sections: prose and point lists, same rhythm as the rest of the page. */
.ts-points { margin: 0; padding-inline-start: 20px; display: flex;
  flex-direction: column; gap: 6px; max-width: 68ch; }
.ts-points li { line-height: 1.6; }
.ts-notanalysed { color: var(--ink-soft); font-size: 14px; padding: var(--s3) var(--s4);
  border: 1px dashed var(--line); border-radius: var(--r-card); max-width: 68ch; }
.ts-counts-note { margin: var(--s2) auto 0; max-width: 52ch; font-size: 12px;
  color: var(--ink-soft); line-height: 1.55; }

/* ── Advanced search ─────────────────────────────────────────────────────────
   Closed by default: it costs the ordinary visitor nothing and is one tap for
   the bidder who wants a filter language. */
.ts-adv { margin-top: var(--s4); }
.ts-adv > summary { display: inline-flex; align-items: baseline; gap: var(--s2);
  cursor: pointer; list-style: none; font-size: 12.5px; color: var(--ink-soft); }
.ts-adv > summary::-webkit-details-marker { display: none; }
.ts-adv-hint code, .ts-adv-panel code { font-family: ui-monospace, SFMono-Regular,
  Menlo, Consolas, monospace; font-size: .92em; background: var(--seal-wash);
  color: var(--seal); padding: 1px 5px; border-radius: 4px; }
.ts-adv-cta { color: var(--seal); font-weight: 600; text-decoration: underline;
  text-underline-offset: 3px; }
.ts-adv[open] > summary .ts-adv-cta::after { content: " ×"; }
.ts-adv { position: relative; }
/* Floating, not inline: an inline panel pushed the results down the page every
   time it opened, which made a reference card feel like a mode. */
.ts-adv-panel { position: absolute; z-index: 30; inset-inline: 0; top: calc(100% + 8px);
  padding: var(--s4); background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); text-align: start;
  box-shadow: 0 18px 40px -18px rgb(28 43 51 / .45), 0 2px 6px rgb(28 43 51 / .05); }
@media (max-width: 620px) { .ts-adv-panel { position: static; margin-top: var(--s3);
  box-shadow: none; } }
.ts-adv-lead { margin: 0 0 var(--s3); font-size: 13px; color: var(--ink-soft);
  line-height: 1.6; }
.ts-adv-fields { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--s3); }
.ts-adv-field { font-size: 12px; padding: 3px 9px; border-radius: 999px;
  background: var(--paper); border: 1px solid var(--line); color: var(--ink);
  cursor: help; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.ts-adv-try { margin: 0 0 var(--s2); font-size: 12px; color: var(--ink-soft); }
.ts-adv-examples { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: 6px; }
.ts-adv-examples a { text-decoration: none; }
.ts-adv-examples a:hover code { background: var(--accent-wash); color: var(--accent-ink); }
.ts-attrib { max-width: 78ch; line-height: 1.6; }
.ts-attrib-fine { margin: var(--s2) 0 0; font-size: 11.5px; opacity: .8; }

/* Contact: two fields per row above the message. Four stacked full-width
   inputs made a two-line message look like an application form. */
.ts-field-row { display: grid; gap: var(--s3); grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .ts-field-row { grid-template-columns: 1fr; } }
.ts-contact { gap: var(--s3); margin-top: var(--s4); max-width: 32rem; }
.ts-page-lead { margin: 0; color: var(--ink-soft); }
.ts-page-body h1 { margin-bottom: var(--s2); }

/* ── Skeleton ────────────────────────────────────────────────────────────────
   The shape of the answer, shown while the answer is fetched. Deliberately
   the SAME geometry as a real card — same padding, radius and line heights —
   so nothing moves when the rows arrive. A placeholder that reflows on
   replacement is worse than no placeholder. */
.ts-skel-card { display: flex; flex-direction: column; gap: 9px; }
.ts-skel { display: block; border-radius: 5px;
  background: linear-gradient(90deg, var(--paper) 25%, #F1F1EC 37%, var(--paper) 63%);
  background-size: 400% 100%; animation: ts-shimmer 1.3s ease-in-out infinite; }
.ts-skel-meta { block-size: 11px; inline-size: 42%; }
.ts-skel-title { block-size: 17px; inline-size: 78%; }
.ts-skel-snip { block-size: 13px; inline-size: 92%; }
.ts-skel-chips { block-size: 21px; inline-size: 54%; border-radius: 999px; }
.ts-skel-line { block-size: 13px; inline-size: 160px; border-radius: 5px;
  background: var(--paper); }
@keyframes ts-shimmer {
  0% { background-position: 100% 0; }
  100% { background-position: 0 0; }
}
/* The shimmer is the only thing saying "still working", so when motion is off
   the placeholder has to hold its own contrast rather than vanish. */
@media (prefers-reduced-motion: reduce) {
  .ts-skel { animation: none; background: var(--paper); }
}
