/* ============================================================
   Prospect Index
   Dark by default. Light is a complete alternative, not an
   afterthought, but the dark one is the design.
   ============================================================ */

@font-face {
  font-family: 'Inter';
  src: url(/fonts/inter-latin.woff2) format('woff2');
  font-weight: 100 900;
  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;
}
@font-face {
  font-family: 'Outfit';
  src: url(/fonts/outfit-latin.woff2) format('woff2');
  font-weight: 100 900;
  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;
}

:root {
  /*
   * One palette, two values each.
   *
   * `light-dark()` resolves against the effective `color-scheme`, so the light
   * theme is not a second copy of this block that has to be kept in step with
   * it -- and the footer switcher only has to change `color-scheme` (see the
   * three rules under this block) rather than restate 18 colours.
   *
   * Dark is still what you get unless the browser asks for light.
   */
  color-scheme: light dark;

  /* Ground and surfaces. The dark ground is a charcoal grey, not a true black --
     panels and cards lift off it rather than being the only thing on the page.
     Surfaces stay alpha rather than fixed colours so a card over a sunk band
     and a card over the body ground both work; the dark alphas are lower than
     the black-ground era needed, because the same tint over a lit ground reads
     as a much milkier card. */
  --bg: light-dark(#FBFBFC, #1E2024);
  --bg-sunk: light-dark(#F2F3F5, #17191C);
  /* In dark the footer sits on the ground, not a lifted plinth -- the page ends
     in the same grey it started in, with only the hairline marking the seam.
     Light keeps its tint, where a flat slab would be a different decision. */
  --footer-bg: light-dark(var(--bg-sunk), var(--bg));
  --surface: light-dark(rgba(15, 20, 26, 0.035), rgba(255, 255, 255, 0.045));
  --surface-2: light-dark(rgba(15, 20, 26, 0.02), rgba(255, 255, 255, 0.025));
  --surface-hi: light-dark(rgba(15, 20, 26, 0.07), rgba(255, 255, 255, 0.08));

  /* Ink. --ink-2 is the lightest that still clears 4.5:1 on --bg; it carries
     body copy, so it is not allowed to drift lighter for elegance. */
  --ink: light-dark(#14181C, #ECECEC);
  --ink-2: light-dark(rgba(18, 23, 28, 0.76), rgba(255, 255, 255, 0.68));
  /* Metadata. Light 0.56 measured 4.0:1 and dark 0.50 measured 4.47:1 on a card
     over the grey ground -- the ground was never the tight case, the lifted
     surface is. */
  --ink-3: light-dark(rgba(18, 23, 28, 0.62), rgba(255, 255, 255, 0.54));

  /* Hairlines sit against a lit ground in both themes, so the dark ones do not
     need the extra weight they were given when everything behind them was
     black. */
  --line: light-dark(rgba(15, 20, 26, 0.13), rgba(255, 255, 255, 0.11));
  --line-soft: light-dark(rgba(15, 20, 26, 0.07), rgba(255, 255, 255, 0.06));
  /* The section mark. Darker than --line on purpose: a 52px tick at the
     hairline alpha reads as a smudge rather than as a mark, and this one has
     to carry a section boundary on its own. */
  --rule: light-dark(rgba(15, 20, 26, 0.32), rgba(255, 255, 255, 0.26));

  --accent: light-dark(#10668F, #4FA8DE);   /* light is darkened to clear 4.5:1 */
  --accent-ink: light-dark(#FFFFFF, #0B1218);
  --accent-soft: light-dark(rgba(16, 102, 143, 0.10), rgba(79, 168, 222, 0.14));
  --accent-line: light-dark(rgba(16, 102, 143, 0.32), rgba(79, 168, 222, 0.40));

  --warn: light-dark(#8A5B00, #E8B44C);
  --bad: light-dark(#B2382A, #E8705F);

  /* Two radii, on purpose. Big surfaces get the soft one; anything you read
     inside gets the tighter one, or the corners start eating the content. */
  --r-lg: 26px;
  --r: 16px;
  --r-sm: 10px;

  --font-display: 'Outfit', 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* Exponential ease-out. No bounce anywhere on this site. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-quart: cubic-bezier(0.25, 1, 0.5, 1);

  --maxw: 1240px;
  --gutter: clamp(1.1rem, 4.5vw, 4rem);
  /* Raised from clamp(4rem, 9vh, 7rem) when the alternating band grounds were
     dropped: with one colour top to bottom, the gap is the only thing saying
     a section ended. */
  --section: clamp(4.5rem, 10vh, 8rem);

  /* Semantic stacking order. Nothing in this file uses a bare number. */
  --z-base: 1;
  --z-sticky: 20;
  --z-dropdown: 40;
  --z-nav: 60;
  --z-overlay: 80;

  /* The select chevron is a url(), not a colour, so it is the one token
     `light-dark()` cannot carry -- it is switched by hand below instead. */
  --chev-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23AAB0B6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --chev-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%234A5158' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --chev: var(--chev-dark);
}

/*
 * The theme switch.
 *
 * Three rules, because the reader's choice has to beat the device in both
 * directions: a `data-theme` on <html> -- written server-side from the
 * `oi_theme` cookie, so the page arrives already in the chosen theme rather
 * than flashing the other one -- wins over `prefers-color-scheme`, and with no
 * cookie the device decides exactly as it did before. Everything except the
 * chevron rides on `color-scheme`, which is what `light-dark()` reads.
 */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) { --chev: var(--chev-light); }
}
:root[data-theme="light"] { color-scheme: light; --chev: var(--chev-light); }
:root[data-theme="dark"] { color-scheme: dark; --chev: var(--chev-dark); }

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

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -0.025em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.1rem, 4.6vw, 3.4rem); }
h2 { font-size: clamp(1.7rem, 3.4vw, 2.6rem); }
h3 { font-size: 1.05rem; font-weight: 500; letter-spacing: -0.01em; }

p { text-wrap: pretty; }

a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

img, svg { max-width: 100%; }
::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* First in the tab order, invisible until it matters. */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: var(--z-overlay);
  background: var(--accent); color: var(--accent-ink);
  padding: 0.7rem 1.2rem; border-radius: 0 0 var(--r-sm) 0; font-weight: 600;
}
.skip:focus { left: 0; }

/* ============================================================
   MOTION
   No scroll-triggered section reveals. They gate content on a
   class that arrives from JavaScript, and when that never
   happens -- blocked script, hidden tab, headless renderer,
   print -- the section ships blank. This page lost its whole
   middle to exactly that before it was caught.
   What is left is an entrance for the hero, which is on screen
   by definition and so can always finish, plus motion on the
   things people actually touch.
   ============================================================ */
@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {
  .hero .hwrap > * {
    animation: rise 0.75s var(--ease-quart) backwards;
  }
  .hero .eyebrow { animation-delay: 0.02s; }
  .hero h1       { animation-delay: 0.08s; }
  .hero .sub     { animation-delay: 0.16s; }
  .hero .heroact { animation-delay: 0.24s; }
}

/* ============================================================
   NAV — one centred dock, with the search built into it
   ============================================================ */
/* Three columns so the dock is centred on the *viewport*, not on whatever is
   left over beside the brand. The third column is empty and exists only to
   balance the first. */
header.site {
  position: sticky; top: 0; z-index: var(--z-nav);
  padding: 0.9rem var(--gutter) 0.55rem;
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: 0.9rem;
  background: linear-gradient(to bottom, var(--bg) 58%, transparent);
}

.brand {
  grid-column: 1; justify-self: start;
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-display);
  font-size: 1.18rem; font-weight: 500; letter-spacing: -0.02em;
  color: var(--ink);
}
.brand:hover { text-decoration: none; }
.brand .mark {
  width: 20px; height: 20px; flex-shrink: 0;
  color: var(--ink);
}
.brand .accent { color: var(--accent); }

/* The dock. Search and links share one rounded shell so it reads as a single
   control rather than a field parked next to a menu. */
.hnav {
  grid-column: 2; justify-self: center;
  display: flex; align-items: center; gap: 0.15rem;
  min-width: 0; max-width: 100%;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  padding: 0.26rem 0.3rem 0.26rem 0.26rem;
  backdrop-filter: blur(14px);
}
.hnav > a {
  padding: 0.44rem 0.85rem; border-radius: 999px;
  font-size: 0.9rem; font-weight: 500; color: var(--ink-2);
  white-space: nowrap;
  transition: color 0.25s ease, background 0.25s ease;
}
.hnav > a:hover { color: var(--ink); background: var(--surface-hi); text-decoration: none; }
.hnav > a.on { color: var(--ink); background: var(--surface-hi); }
.hnav > a.navcta { background: var(--ink); color: var(--bg); font-weight: 600; }
.hnav > a.navcta:hover { background: var(--accent); color: var(--accent-ink); }

/* The search segment: recessed into the dock, with a hairline separating it
   from the links so the two halves read as parts of one thing. */
.hsearch {
  display: flex; align-items: stretch; gap: 0.35rem;
  flex: 0 1 470px; min-width: 0;
  margin-right: 0.4rem; padding-right: 0.4rem;
  border-right: 1px solid var(--line-soft);
}
.hsearch input, .hsearch select, .hsearch button {
  font: inherit; font-size: 0.88rem; height: 34px;
  border: 0; color: var(--ink);
  border-radius: 999px;
}
.hsearch input {
  flex: 1 1 auto; min-width: 0;
  padding: 0 0.95rem;
  background: var(--bg);
  transition: box-shadow 0.2s ease;
}
.hsearch input::placeholder { color: var(--ink-3); }
.hsearch input:focus { outline: none; box-shadow: 0 0 0 1px var(--accent-line) inset; }
/*
 * `field-sizing: content` makes the select as wide as the option actually
 * chosen. Without it a native select reserves the width of its *longest*
 * option, so picking "All countries" leaves a run of dead space before the
 * arrow -- it sits where "United Kingdom" would have ended. Where the property
 * is not supported the behaviour is simply what it was before.
 */
/*
 * The arrow is ours, not the browser's.
 *
 * A native select draws its own indicator wherever the platform decides, and
 * padding does not reliably move it -- which is how it ended up sitting on the
 * rounded edge of the pill. `appearance: none` removes it; the chevron below is
 * a background image, so `background-position` puts it exactly where it should
 * be and it stays there in every engine.
 */
.hsearch select {
  flex: none; max-width: 170px; color: var(--ink-2);
  appearance: none; -webkit-appearance: none;
  background-color: var(--bg);
  background-image: var(--chev);
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 10px 6px;
  field-sizing: content;
  min-width: 6.8rem;
  /* Room for the label, then the arrow, then a clear margin to the edge. */
  padding: 0 2.15rem 0 1rem;
  cursor: pointer;
  transition: box-shadow 0.2s ease;
}
.hsearch select:hover { box-shadow: 0 0 0 1px var(--line) inset; }
.hsearch select:focus { outline: none; box-shadow: 0 0 0 1px var(--accent-line) inset; }
.hsearch select option { background: var(--bg); color: var(--ink); }
.hsearch button {
  flex: none; padding: 0 1.15rem; cursor: pointer; font-weight: 600;
  background: var(--accent); color: var(--accent-ink);
  transition: filter 0.25s ease;
}
.hsearch button:hover { filter: brightness(1.1); }

/*
 * Below this the dock cannot hold both halves on one line, so it becomes a
 * plain stacked header: search on its own row, links beneath. Still centred.
 */
/*
 * Below this the two halves cannot share a line, so the dock stops being one
 * shell and becomes two: a full-width search pill with the links underneath.
 * Keeping the outer pill here would nest one rounded control inside another
 * with a few pixels of clearance, which looks like a mistake rather than a
 * design.
 */
@media (max-width: 1080px) {
  header.site { grid-template-columns: 1fr auto; row-gap: 0.5rem; }
  .hnav {
    grid-column: 1 / -1;
    flex-wrap: wrap; justify-content: center;
    background: none; border: 0; padding: 0; backdrop-filter: none;
  }
  .hsearch {
    flex: 1 1 100%; order: -1;
    margin: 0; padding: 0; border-right: 0;
  }
  .hsearch input, .hsearch select { height: 42px; background-color: var(--surface); }
  .hsearch button { height: 42px; }
}
@media (max-width: 560px) {
  .hnav > a.hide-sm { display: none; }
  .hsearch select { display: none; }
}

/* ============================================================
   AUTOCOMPLETE
   ============================================================ */
.has-ac { position: relative; }
.acp {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0;
  z-index: var(--z-dropdown);
  background: var(--bg-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: 0 20px 50px rgb(0 0 0 / 40%);
  overflow: hidden;
}
.aci {
  display: flex; align-items: baseline; gap: 0.6rem;
  padding: 0.62rem 1rem; font-size: 0.9rem;
  border-top: 1px solid var(--line-soft);
}
.aci:first-child { border-top: 0; }
.aci:hover, .aci.on { background: var(--surface-hi); text-decoration: none; }
.aci b { font-weight: 600; color: var(--accent); }
.aci i { font-style: normal; color: var(--ink-3); font-size: 0.82rem; margin-left: auto; }

/* ============================================================
   LAYOUT PRIMITIVES
   ============================================================ */
main { position: relative; z-index: var(--z-base); }

.page {
  max-width: var(--maxw); margin: 0 auto;
  padding: clamp(1.6rem, 4vh, 3rem) var(--gutter) var(--section);
}
.page.narrow { max-width: 760px; }
.band { padding: var(--section) var(--gutter); max-width: var(--maxw); margin: 0 auto; }
/* The alternating band used to sit a shade below the page ground. The page
   now runs one continuous grey top to bottom, so `alt` only means "this
   section spans the full width" -- the inner max-width below still holds
   its content to the reading column. */
.band.alt { max-width: none; }
.band.alt > * { max-width: var(--maxw); margin-inline: auto; }

.ptitle { margin-bottom: clamp(0.65rem, 1.8vh, 1rem); }
.lede { color: var(--ink-2); font-size: 1.05rem; max-width: 62ch; margin-bottom: 1.8rem; }
.prose { max-width: 68ch; color: var(--ink-2); }
.prose p { margin-bottom: 1.1rem; }
.prose h2 { margin: 2.4rem 0 0.9rem; color: var(--ink); }
.prose a { color: var(--accent); }
.muted { color: var(--ink-3); }
.center { text-align: center; }
.hint { font-size: 0.85rem; color: var(--ink-3); }

.shead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap; margin-bottom: clamp(1.6rem, 4vh, 2.6rem);
}
.shead h2 { max-width: 20ch; }
.shead a { color: var(--accent); font-size: 0.92rem; font-weight: 500; }

/* One kicker, used once, on the hero. Not a per-section scaffold. */
.eyebrow {
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}

/* ============================================================
   HERO
   No photograph, no gradient, no ground of its own. The band
   is the page's own grey with a hairline under it; the whole
   visual weight is the type.

   The landing hero used to be a rounded panel inset from the
   gutters. The panel is gone: its ground now runs the full
   width of the viewport, edge to edge, and only the type
   inside it is held to the page's reading width.
   ============================================================ */
/* Top padding lives here rather than on the panel, because the signed-in home
   page has no panel -- its heading sat directly under the sticky nav. */
.hero {
  max-width: var(--maxw); margin: 0 auto;
  padding: clamp(1.8rem, 5vh, 3.4rem) var(--gutter) 0;
}

/* Full-bleed variant: the section itself is only a carrier, so it gives up
   its width cap and its padding to the panel underneath. */
.hero.hero-wide { max-width: none; padding: 0; }

.hero-panel {
  position: relative; overflow: hidden;
  border-bottom: 1px solid var(--line-soft);
  padding: clamp(3.2rem, 10vh, 6.5rem) var(--gutter) clamp(3.2rem, 9vh, 5.6rem);
  min-height: min(56vh, 460px);
  display: flex; align-items: flex-end;
}
.hwrap { position: relative; z-index: var(--z-base); width: 100%; }
/* Ground is full-bleed, type is not. */
.hero-panel .hwrap { max-width: var(--maxw); margin-inline: auto; }

.hero h1 {
  font-size: clamp(2.4rem, 6.2vw, 5.2rem);
  font-weight: 300; line-height: 1.02; letter-spacing: -0.03em;
  max-width: 15ch; margin: 0.55rem 0 0;
}
.hero .sub { color: var(--ink-2); max-width: 46ch; margin-top: 1rem; font-size: 1.05rem; }
.heroact { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.8rem; }

@media (max-width: 640px) {
  .hero-panel { min-height: 0; padding-block: clamp(2.4rem, 7vh, 3.4rem); }
}

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font: inherit; font-size: 0.94rem; font-weight: 600;
  padding: 0.72rem 1.5rem; border-radius: 999px;
  border: 1px solid transparent; cursor: pointer;
  background: var(--ink); color: var(--bg);
  transition: transform 0.25s var(--ease), background 0.25s ease, color 0.25s ease;
}
.btn:hover {
  background: var(--accent); color: var(--accent-ink);
  text-decoration: none; transform: translateY(-1px);
}
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn.ghost:hover { background: var(--surface-hi); color: var(--ink); border-color: var(--line); }
.btn.big { padding: 0.85rem 1.9rem; font-size: 1rem; }
.btn.sm { padding: 0.45rem 1rem; font-size: 0.85rem; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

@media (prefers-reduced-motion: reduce) { .btn:hover { transform: none; } }

/* ============================================================
   NOTICES & PILLS
   ============================================================ */
.notice {
  padding: 0.8rem 1.1rem; border-radius: var(--r-sm);
  font-size: 0.92rem; margin-bottom: 1.2rem;
  background: var(--surface); border: 1px solid var(--line);
  color: var(--ink-2);
}
.notice.err { border-color: var(--bad); color: var(--bad); background: transparent; }
.notice.ok { border-color: var(--accent-line); color: var(--accent); background: transparent; }
.notice.warn { border-color: var(--warn); color: var(--warn); background: transparent; }

.pill {
  display: inline-block; padding: 0.2rem 0.7rem; border-radius: 999px;
  font-size: 0.8rem; font-weight: 600;
  background: var(--surface-hi); color: var(--ink-2);
}
.pill.ok { background: var(--accent-soft); color: var(--accent); }
.pill.off { background: var(--surface); color: var(--ink-3); }
.pill.warn { background: rgb(232 180 76 / 16%); color: var(--warn); }

.chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.28rem 0.75rem; border-radius: 999px;
  font-size: 0.82rem; color: var(--ink-2);
  background: var(--surface); border: 1px solid var(--line-soft);
}

/* ============================================================
   FEATURES — alternating rows, not an identical card grid
   ============================================================ */
/* `.facts` (the three-across note row) went with the home page's closing band;
   `.facts2` is a different component -- the listing page's detail list. */
.facts2 { display: grid; gap: 1.6rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.facts2 h3 { margin-bottom: 0.4rem; }
.facts2 p { color: var(--ink-2); font-size: 0.94rem; max-width: 42ch; }

/* A wide feature: text on one side, an image band on the other. Alternates. */
.split {
  display: grid; gap: clamp(1.6rem, 4vw, 3.4rem); align-items: center;
  grid-template-columns: minmax(260px, 0.85fr) minmax(280px, 1.15fr);
}
.split + .split { margin-top: clamp(3rem, 7vh, 5.5rem); }
.split.flip > .split-text { order: 2; }
.split-text h2 { margin-bottom: 0.7rem; }
.split-text p { color: var(--ink-2); max-width: 46ch; }
.split-text p + p { margin-top: 0.8rem; }
.split-text a { color: var(--accent); }
@media (max-width: 780px) {
  .split { grid-template-columns: 1fr; }
  .split.flip > .split-text { order: 0; }
}

/* ============================================================
   PLANS
   ============================================================ */
.plans { display: flex; justify-content: center; }
.plans.three {
  display: grid; gap: 1.1rem; align-items: stretch;
  grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
  max-width: 1000px; margin: 0 auto;
}
.plan {
  position: relative;
  width: 100%; max-width: 420px;
  display: flex; flex-direction: column;
  padding: 1.7rem 1.5rem 1.5rem;
  border-radius: var(--r);
  border: 1px solid var(--line);
  background: var(--surface-2);
}
.plans.three .plan { max-width: none; }
.plan.featured { border-color: var(--accent-line); background: var(--surface); }
.plan.current { border-color: var(--accent-line); }
.planflag {
  position: absolute; top: -0.65rem; left: 1.4rem;
  margin: 0; padding: 0.2rem 0.7rem; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase;
}
.planflag.now { background: var(--ink); color: var(--bg); }
.planhead { padding-bottom: 1.1rem; }
.plan h3 { font-size: 1rem; color: var(--ink-2); font-weight: 500; }
.plan .price { display: flex; align-items: baseline; gap: 0.4rem; margin-top: 0.5rem; }
.plan .price b {
  font-family: var(--font-display); font-size: 2.4rem; font-weight: 300;
  letter-spacing: -0.03em; color: var(--ink);
}
.plan .price span { color: var(--ink-3); font-size: 0.88rem; }
.plan .trial { margin-top: 0.4rem; font-size: 0.85rem; color: var(--accent); }
.planblurb {
  color: var(--ink-2); font-size: 0.9rem; margin-bottom: 1.1rem;
  padding-bottom: 1.1rem; border-bottom: 1px solid var(--line-soft);
}
/* What every tier includes, said once under the three cards. A card should
   only carry what differs; this is the floor they all share. */
.plancommon {
  margin: 1.1rem auto 0; max-width: 52ch;
  font-size: 0.86rem; color: var(--ink-3);
}

/* "Everything in Essential, plus:" -- the line that lets a card list only its
   own additions instead of restating the ladder underneath it. */
.planinherit {
  margin-bottom: 0.9rem; font-size: 0.82rem; color: var(--ink-3);
}

.planlist { list-style: none; display: grid; gap: 0.55rem; margin-bottom: 1.4rem; }
.planlist li { position: relative; padding-left: 1.5rem; font-size: 0.89rem; color: var(--ink-2); }
.planlist li::before {
  content: ""; position: absolute; left: 2px; top: 7px;
  width: 5px; height: 9px; border: solid var(--accent);
  border-width: 0 1.5px 1.5px 0; transform: rotate(45deg);
}
.planlist li.no { color: var(--ink-3); }
.planlist li.no::before {
  top: 11px; left: 0; width: 9px; height: 0;
  border-width: 0 0 1.5px 0; border-color: var(--ink-3); transform: none;
}
.planact { margin-top: auto; }
/* Only two of the calls to action are wrapped in .planact (the checkout form
   and the "not on sale" note); the others are a bare link -- signed out,
   already on this tier, or changing plan. They need the same push to the
   bottom, or the shortest card's button floats in the middle of it while the
   rest sit on the floor. Invisible until Essential dropped to one row. */
.plan > .btn { margin-top: auto; }
.planact .btn, .planact button { display: flex; width: 100%; }
.planact.muted { font-size: 0.84rem; color: var(--ink-3); line-height: 1.55; }
.planact code {
  font-size: 0.78rem; background: var(--surface-hi);
  padding: 0.1rem 0.35rem; border-radius: 4px;
}

/* Comparison table */
.cmphead { margin: clamp(3rem, 7vh, 4.5rem) 0 1.2rem; text-align: center; }
.cmp { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.cmp th, .cmp td {
  padding: 0.8rem 1rem; text-align: left; border-bottom: 1px solid var(--line-soft);
}
.cmp thead th {
  font-size: 0.8rem; color: var(--ink-3); font-weight: 600; border-bottom-color: var(--line);
}
.cmp thead th:first-child { width: 34%; }
.cmp tbody th { font-weight: 400; color: var(--ink-2); }
.cmp td { color: var(--ink); font-variant-numeric: tabular-nums; }
.cmp td.no { color: var(--ink-3); }

@media (max-width: 620px) {
  .cmp, .cmp tbody, .cmp tr, .cmp td, .cmp tbody th { display: block; width: auto; }
  .cmp thead { display: none; }
  .cmp tbody tr { padding: 0.7rem 0; border-bottom: 1px solid var(--line); }
  .cmp tbody th { padding: 0 0 0.4rem; color: var(--ink); font-weight: 600; }
  .cmp td {
    padding: 0.22rem 0 0.22rem 0.9rem; border: 0;
    display: flex; justify-content: space-between; gap: 1rem;
  }
  .cmp td::before { content: attr(data-l); color: var(--ink-3); font-size: 0.84rem; }
}

.ladder {
  margin-top: 1.8rem; padding: 1.3rem 1.4rem;
  border: 1px solid var(--line-soft); border-radius: var(--r);
  background: var(--surface-2);
}
.ladder h2 { font-size: 1rem; margin-bottom: 0.6rem; }
.ladder ul { padding-left: 1.1rem; display: grid; gap: 0.35rem; margin-bottom: 0.6rem; }
.ladder li { font-size: 0.9rem; color: var(--ink-2); }

/* ============================================================
   RESULTS — dense and utilitarian. The working surface.
   ============================================================ */
.toolrow { display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap; margin-bottom: 1.3rem; }
/* This is the only thing giving the headline room to breathe from whatever
   comes next -- a plain <p> has no margin of its own once the global reset
   zeroes it, so without this every page's title, count and filter chips sat
   nearly flush against one another. */
.pcount { font-size: 0.9rem; color: var(--ink-3); margin-bottom: clamp(1.2rem, 3vh, 1.7rem); }

.perpick { display: flex; align-items: center; gap: 0.45rem; font-size: 0.86rem; color: var(--ink-3); }
.perpick select {
  font: inherit; font-size: 0.86rem; padding: 0.3rem 0.5rem;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.perpick select option { background: var(--bg); }
.perpick button { font: inherit; }
.perup { font-size: 0.82rem; color: var(--ink-3); }
.perup:hover { color: var(--accent); }

.pdfbtn {
  font-size: 0.85rem; color: var(--ink-2);
  padding: 0.36rem 0.85rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface-2);
  transition: border-color 0.25s ease, color 0.25s ease;
}
.pdfbtn:hover { border-color: var(--accent-line); color: var(--accent); text-decoration: none; }
.pdfbtn.up { border-style: dashed; color: var(--ink-3); }

.filters { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-bottom: 1.6rem; }
.fchip {
  padding: 0.34rem 0.85rem; border-radius: 999px; font-size: 0.85rem;
  background: var(--surface-2); border: 1px solid var(--line-soft); color: var(--ink-2);
  transition: border-color 0.2s ease, background 0.2s ease;
}
.fchip:hover { background: var(--surface-hi); text-decoration: none; }
.fchip.on { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }
.fchip i { font-style: normal; color: var(--ink-3); font-size: 0.78rem; margin-left: 0.25rem; }

/* A second, quieter tier of chips -- categories within a city, cities within a
   category. There can be two dozen of these, and giving them the same filled
   pill as the three or four real filters above them turned "here is the
   choice you're making" and "here is more to browse" into one undifferentiated
   wall of identical buttons. Smaller, unfilled and labelled, so the eye can
   tell them apart without reading every chip. */
.filterlabel {
  font-size: 0.74rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--ink-3); margin: 0.3rem 0 0.7rem;
}
.filters.sm { gap: 0.35rem; }
.filters.sm .fchip {
  padding: 0.28rem 0.7rem; font-size: 0.8rem;
  background: transparent; color: var(--ink-3);
}
.filters.sm .fchip:hover { background: var(--surface-2); color: var(--ink-2); }
.filters.sm .fchip.on { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }

.tablewrap { overflow-x: auto; border-radius: var(--r); border: 1px solid var(--line-soft); }
.rtable { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.rtable th, .rtable td {
  padding: 0.62rem 0.85rem; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--line-soft);
}
.rtable thead th {
  position: sticky; top: 0; z-index: var(--z-sticky);
  background: var(--bg-sunk); color: var(--ink-3);
  font-size: 0.74rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; white-space: nowrap;
}
.rtable tbody tr:last-child td { border-bottom: 0; }
.rtable tbody tr:hover { background: var(--surface-2); }
.rtable tr.isHidden { opacity: 0.42; }
.rtable a { color: var(--ink); }
.rtable a:hover { color: var(--accent); }
.bname { font-weight: 500; }
.dash { color: var(--ink-3); }
.cbcell { width: 42px; }
.hidebox { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; }

.tps {
  display: inline-block; margin-left: 0.35rem; padding: 0.05rem 0.4rem;
  border-radius: 4px; font-size: 0.7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.03em;
}
.tps.on { background: rgb(232 112 95 / 18%); color: var(--bad); }
.tps.ok { background: var(--accent-soft); color: var(--accent); }
.tps.un { background: var(--surface-hi); color: var(--ink-3); }
.tpsnote { font-size: 0.82rem; color: var(--ink-3); margin-bottom: 1.3rem; }
.tpsnote b { color: var(--ink-2); }
.tpsnote.stale { color: var(--warn); }
.tpsnote.up a { color: var(--accent); }

@media (max-width: 720px) {
  .rtable thead { display: none; }
  .rtable, .rtable tbody, .rtable tr, .rtable td { display: block; width: auto; }
  .rtable tr { padding: 0.8rem 0.9rem; border-bottom: 1px solid var(--line); }
  .rtable td { border: 0; padding: 0.12rem 0; display: flex; gap: 0.8rem; min-width: 0; }
  .rtable td::before {
    content: attr(data-l); flex: 0 0 5.5rem; color: var(--ink-3); font-size: 0.78rem;
  }
  /* A website column is full of long unbroken strings. Stacked into a narrow
     card they run past the edge and get clipped by the body, so they have to be
     allowed to break mid-token. */
  .rtable td > a, .rtable td { overflow-wrap: anywhere; }
  .rtable td[data-l="Business"] { font-size: 1rem; padding-bottom: 0.3rem; }
  .tablewrap { border: 0; }
}

.pager { display: flex; align-items: center; justify-content: center; gap: 1rem; margin-top: 1.8rem; }
.pg {
  padding: 0.5rem 1.1rem; border-radius: 999px; font-size: 0.9rem;
  border: 1px solid var(--line); color: var(--ink-2);
}
.pg:hover { border-color: var(--accent-line); color: var(--accent); text-decoration: none; }
.pg.off { opacity: 0.35; pointer-events: none; }
.pgnum { font-size: 0.88rem; color: var(--ink-3); font-variant-numeric: tabular-nums; }

.empty {
  text-align: center; padding: clamp(3rem, 9vh, 5rem) 1.5rem;
  border: 1px dashed var(--line); border-radius: var(--r);
}
.empty h2 { margin-bottom: 0.6rem; }
.empty p { color: var(--ink-2); max-width: 44ch; margin: 0 auto 1.4rem; }

/* `.tiles` lived here: a bordered box per entry, the shape /categories and
   /places both used before the A-Z index below replaced them. It had no other
   user, so it went with them. */

/* ============================================================
   A-Z INDEX
   /categories. 1,961 names in one size-ordered grid was a wall
   you had to read all of to find anything; grouped by initial
   with a bar that jumps to each one, the length stops being
   the problem and becomes the point.
   ============================================================ */
/* The filter box. Two pills with a gap, like the header search -- fused into
   one strip the button has to go square on one side and stops reading as a
   control. */
.azfind {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.4rem;
  margin: 1.6rem auto 0; max-width: 30rem;
}
.azfind input {
  flex: 1 1 12rem; min-width: 0;
  padding: 0.6rem 1rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink); font-family: var(--font); font-size: 0.92rem;
}
.azfind input::placeholder { color: var(--ink-3); }
.azfind input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.azfind button {
  padding: 0.6rem 1.2rem; border-radius: 999px; border: 1px solid transparent;
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--font); font-size: 0.92rem; font-weight: 500;
  cursor: pointer;
}
.azfind button:hover { filter: brightness(1.08); }

/* Rows and sections the filter has taken out. `.azlist a` is `display: flex`,
   which beats the user-agent rule for [hidden], so it has to be said here. */
.azlist a[hidden], .azsec[hidden] { display: none; }

.aznone {
  margin-top: 2.5rem; color: var(--ink-2);
}

.azbar {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.15rem;
  margin: 1.6rem auto 0; padding: 0.5rem 0.4rem;
  /* Full page width, not shrink-to-fit: once it is sticky its background is
     the only thing between it and the list scrolling underneath, and a centred
     58rem bar let two columns of names slide past in the gaps beside it. The
     letters stay centred within it. */
  background: var(--bg); border-block: 1px solid var(--line-soft);
}
.azbar a, .azbar span {
  min-width: 1.9rem; padding: 0.28rem 0.1rem; border-radius: var(--r-sm);
  font-family: var(--font-display); font-size: 0.92rem; font-weight: 500;
  color: var(--ink-2);
  transition: color 0.2s ease, background 0.2s ease;
}
.azbar a:hover, .azbar a:focus-visible {
  text-decoration: none; color: var(--accent-ink); background: var(--accent);
}
/* A letter nothing starts with -- either in the data (a <span> from the server)
   or after filtering (`.off`, applied by app.js). It stays in the bar so the
   bar keeps its shape as you filter: a row of 27 that reflows on every
   keystroke is impossible to aim at. */
.azbar span, .azbar a.off { color: var(--ink-3); opacity: 0.4; }
.azbar a.off { pointer-events: none; }

/* Above the breakpoint where the header dock un-nests, the bar follows you
   down the page, so a second jump does not mean scrolling back up first. The
   offset is the header's own height: it is a fixed 70.6px until the dock
   stacks, and below that the bar simply scrolls away with the rest. */
@media (min-width: 1081px) {
  .azbar { position: sticky; top: 70px; z-index: var(--z-sticky); }
}

.azsec { margin-top: 2.2rem; }
/* The sticky header, and the bar stuck under it, would otherwise land on top of
   the heading you just jumped to: 70px of header plus a 50px bar. */
.azsec { scroll-margin-top: 8.5rem; }
/* Below the breakpoint the dock stacks into two rows and the header roughly
   doubles to 143px, which ate the heading whole -- the bar is not sticky down
   here, but the header still is. */
@media (max-width: 1080px) {
  .azsec { scroll-margin-top: 10.5rem; }
}
.azletter {
  font-family: var(--font-display); font-size: 1.5rem; font-weight: 500;
  color: var(--ink); letter-spacing: 0.02em;
  padding-bottom: 0.5rem; margin-bottom: 0.9rem;
  border-bottom: 1px solid var(--line);
}
.azlist {
  display: grid; gap: 0.1rem 1.4rem;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.azlist a {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.7rem;
  padding: 0.42rem 0.5rem; border-radius: var(--r-sm);
  font-size: 0.9rem; color: var(--ink-2);
  transition: color 0.2s ease, background 0.2s ease;
}
.azlist a:hover { text-decoration: none; color: var(--ink); background: var(--surface); }
/* The name is a <span> and not a bare text node on purpose: an anonymous flex
   item gets `min-width: auto` and cannot be selected to fix, so a long name
   ("Community Services Non Profits") would push its count out of the column. */
.azlist span { min-width: 0; text-align: left; }
.azlist i {
  flex: 0 0 auto;
  font-style: normal; color: var(--ink-3); font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   TOP-OF-INDEX BOXES
   What replaced the two walls of tiles on the home page. 28
   categories and 24 cities is a lot of nothing to choose
   between; three of each, and the whole index one click away,
   says the same thing and can be read at a glance.
   ============================================================ */
.duo {
  display: grid;
  gap: clamp(1rem, 2.6vw, 1.8rem);
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  max-width: 1080px; margin-inline: auto;
}

/* The whole card is the link -- clicking anywhere in it goes where the pill
   says. That makes the pill a `<span>`, not a nested `<a>` (which is invalid
   and would swallow the card's own click target), so `.btn` has to stay a
   class-only style: it does, `.btn` never selects on the element name. The card
   inherits the page's text colour and refuses the global link underline, or
   every line inside it would underline together on hover. */
.dbox {
  display: flex; flex-direction: column;
  padding: clamp(1.8rem, 3.4vw, 2.6rem) clamp(1.2rem, 2.6vw, 2rem);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface-2);
  color: inherit;
  transition: transform 0.25s ease, border-color 0.25s ease, background 0.25s ease;
}
.dbox, .dbox:hover { text-decoration: none; }
/* The global focus ring rounds to 4px, which reads as a rectangle drawn around
   a 26px card; follow the card's own corner instead. */
.dbox:focus-visible { border-radius: var(--r-lg); }
/* A 2px lift and a warmer edge -- enough to say the whole card is live without
   the box appearing to jump. `transform` and not `margin`/`padding`, so the
   pair beside it never reflows. */
.dbox:hover {
  transform: translateY(-2px);
  border-color: var(--accent-line);
  background: var(--surface);
}
.dbox:hover .btn.ghost { border-color: var(--accent-line); color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .dbox { transition: border-color 0.25s ease, background 0.25s ease; }
  .dbox:hover { transform: none; }
}
.dbox h2 { font-size: clamp(1.3rem, 2.2vw, 1.7rem); }

/* The size of the thing you are about to browse. This is the only place the
   two totals appear now -- the hero's row of five stats is gone, and the pill
   below says "Browse them all" rather than repeating the number a second time
   inside the same box. */
.dcount {
  margin: 0.55rem 0 clamp(1.4rem, 3.2vh, 2rem);
  font-size: 0.95rem; color: var(--ink-3);
}
.dcount b {
  font-family: var(--font-display); font-weight: 500;
  font-size: 1.35rem; color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}

/* `margin-top: auto` puts the link on the box's floor, so the two boxes end
   level however tall their headings run. */
.dbox .btn { margin-top: auto; align-self: center; }

/* ============================================================
   DETAIL PAGE
   ============================================================ */
.dgrid {
  display: grid; gap: clamp(1.5rem, 4vw, 3rem);
  grid-template-columns: minmax(0, 1.25fr) minmax(260px, 0.85fr);
  align-items: start;
}
@media (max-width: 860px) { .dgrid { grid-template-columns: 1fr; } }
.acctable, .dmain dl { display: grid; gap: 0; margin: 1.5rem 0; }
.acctable > div, .dmain dl > div {
  display: grid; grid-template-columns: minmax(120px, 0.32fr) 1fr;
  gap: 1rem; padding: 0.7rem 0; border-bottom: 1px solid var(--line-soft);
}
.acctable dt, .dmain dt { color: var(--ink-3); font-size: 0.88rem; }
.acctable dd, .dmain dd { color: var(--ink); font-size: 0.94rem; }
.acctable a, .dmain dd a { color: var(--accent); }
.dactions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.6rem; }
.dside .map {
  width: 100%; aspect-ratio: 4 / 3; border: 0;
  border-radius: var(--r); background: var(--bg-sunk);
}
.mapnote { font-size: 0.78rem; color: var(--ink-3); margin-top: 0.5rem; }
.meta { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 1.2rem; }
.more { margin-top: clamp(2.5rem, 6vh, 4rem); }

/* ============================================================
   AUTH & ACCOUNT
   ============================================================ */
.authwrap {
  min-height: 70vh; display: grid; place-items: center;
  padding: clamp(1.5rem, 5vh, 3rem) var(--gutter);
}
.authcard {
  width: 100%; max-width: 400px;
  padding: clamp(1.6rem, 4vw, 2.2rem);
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface-2);
}
.authcard h1 { font-size: 1.5rem; margin-bottom: 1.2rem; }
.authlede { color: var(--ink-2); font-size: 0.92rem; margin-bottom: 1.2rem; }
.authform { display: grid; gap: 0.85rem; }
.authform label { font-size: 0.85rem; color: var(--ink-2); display: block; margin-bottom: 0.3rem; }
.authform input {
  font: inherit; width: 100%; padding: 0.65rem 0.9rem;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.authform input:focus { border-color: var(--accent-line); outline: none; }
.authform .btn { width: 100%; margin-top: 0.3rem; }
.authalt { margin-top: 1.2rem; font-size: 0.88rem; color: var(--ink-3); text-align: center; }
.authalt a { color: var(--accent); }
.acctions { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.6rem; }

.paywall { text-align: center; max-width: 620px; margin: 0 auto clamp(2rem, 5vh, 3.5rem); }
.paywall .lede { margin-inline: auto; }
.paywall .planact { display: flex; justify-content: center; }
.paywall .planact .btn { width: auto; }

/* ============================================================
   FAQ
   ============================================================ */
.faq { display: grid; gap: 0; max-width: 760px; }
.faq.wide { max-width: none; margin-top: clamp(2.5rem, 6vh, 4rem); }
/* The landing FAQ sits under the field list, in the same section, so it needs
   the gap the section boundary would otherwise have given it. */
.faq.sp { margin-top: clamp(2.5rem, 6vh, 4rem); }
.faq details { border-bottom: 1px solid var(--line-soft); }
.faq summary {
  padding: 1.05rem 2rem 1.05rem 0; cursor: pointer; list-style: none;
  font-size: 1rem; font-weight: 500; position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; position: absolute; right: 0.4rem; top: 1.3rem;
  width: 8px; height: 8px;
  border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3);
  transform: rotate(45deg); transition: transform 0.3s var(--ease);
}
.faq details[open] summary::after { transform: rotate(-135deg); }
.faq summary:hover { color: var(--accent); }
.faq p { padding: 0 2rem 1.15rem 0; color: var(--ink-2); font-size: 0.94rem; max-width: 68ch; }

/* ============================================================
   FOOTER
   ============================================================ */
footer.site {
  border-top: 1px solid var(--line-soft);
  padding: clamp(2.5rem, 6vh, 4rem) var(--gutter) 2rem;
  background: var(--footer-bg);
}
.fcols {
  max-width: var(--maxw); margin: 0 auto;
  display: grid; gap: clamp(1.6rem, 4vw, 3rem);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.fcols strong {
  display: block; margin-bottom: 0.7rem; font-size: 0.8rem;
  color: var(--ink-3); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.07em;
}
.fcols a { display: block; font-size: 0.9rem; color: var(--ink-2); padding: 0.2rem 0; }
.fcols a:hover { color: var(--accent); }
.fcols p { font-size: 0.9rem; color: var(--ink-2); max-width: 34ch; }
/* Language switcher. The current language is a <b>, not a link to the page you
   are already on; the other is an ordinary link, so it works with no script. */
.langpick {
  display: flex; flex-wrap: wrap; gap: 0.1rem 0.6rem;
  align-items: baseline; justify-content: center;
  margin-top: 1rem; font-size: 0.85rem;
}
.langpick span { color: var(--ink-3); }
.langpick b { color: var(--ink); font-weight: 500; }
.langpick a { color: var(--ink-2); }
.langpick a:hover { color: var(--accent); }

/* The theme switch. Both directions are in the markup; the rules below hide
   whichever one you are already in, so the server never has to guess.

   Two classes deep on purpose: `.fcols a` is (0,1,1) and sets `padding: 0.2rem 0`
   on every link in the footer, which flattens a pill into its own text. A bare
   `.themebtn` is (0,1,0) and loses to it. */
.themepick { display: flex; justify-content: center; margin-top: 0.7rem; }
.themepick .themebtn {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.42rem 0.9rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  font-size: 0.85rem; color: var(--ink-2);
  white-space: nowrap;          /* "Dunkler Modus" must not wrap inside a pill */
  transition: color 0.25s ease, border-color 0.25s ease, background 0.25s ease;
}
.themepick .themebtn:hover {
  text-decoration: none; color: var(--ink);
  border-color: var(--accent-line); background: var(--surface-hi);
}
.themepick .themebtn svg { width: 15px; height: 15px; flex-shrink: 0; }

/* Which one is offered. The reader's own choice has to beat the device in both
   directions, so this is the same three-way test as the palette itself -- and
   it is done in CSS rather than in the template because only the browser knows
   what `prefers-color-scheme` says. */
.themepick .to-dark { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .themepick .to-light { display: none; }
  :root:not([data-theme="dark"]) .themepick .to-dark { display: inline-flex; }
}
:root[data-theme="light"] .themepick .to-light { display: none; }
:root[data-theme="light"] .themepick .to-dark { display: inline-flex; }
:root[data-theme="dark"] .themepick .to-light { display: inline-flex; }
:root[data-theme="dark"] .themepick .to-dark { display: none; }

.legal {
  max-width: 78ch; margin: clamp(2rem, 5vh, 3rem) auto 0;
  padding-top: 1.3rem; border-top: 1px solid var(--line-soft);
  font-size: 0.78rem; color: var(--ink-3);
}

/* ============================================================
   PRINT
   ============================================================ */
@media print {
  header.site, footer.site, .toolrow, .filters, .pager, .cbcell, .hidebox { display: none; }
  body { background: #fff; color: #000; }
  .tablewrap { border: 0; }
  .rtable th, .rtable td { border-color: #ddd; }
}

/* A section head with breathing room above it, used under an image band. */
.shead.sp { margin-top: clamp(2rem, 5vh, 3rem); align-items: center; }


.dmain dl.facts2 { grid-template-columns: 1fr; gap: 0; }

/* The footer brand is a link, so it must not inherit the column-heading rules
   that sit beside it. */
.fcols .brand { display: inline-flex; font-size: 1.05rem; margin-bottom: 0.6rem; }
.fcols .brand + p { margin-top: 0; }

/* ============================================================
   DATA AS THE VISUAL
   What replaced the photography. Each device shows something
   true about the index rather than illustrating it, so all
   three look different from each other and none is a card grid.
   ============================================================ */

/* --- The field list: what every row carries, as a specimen ---------------- */
/* Multi-column rather than a grid of cells: thirteen fields never divide
   evenly into a responsive column count, and a grid leaves ragged empty boxes
   at the end of the last row. Columns just flow.
   Each item is one word now, so the block is narrower than it was when a gloss
   sat beside every name, and the rule under each name is the only ornament
   left. Three columns, not four: thirteen items balance to 5/5/3, where four
   columns leave a single word stranded in the last one. */
.fieldlist {
  list-style: none;
  max-width: 44rem; margin-inline: auto;
  columns: 3 150px;
  column-gap: clamp(1.4rem, 3.5vw, 3rem);
}
.fieldlist li {
  break-inside: avoid;
  padding: 0.72rem 0;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--font-display); font-weight: 500;
  font-size: 1.02rem; letter-spacing: -0.01em; color: var(--ink);
}

/* --- Coverage: a proportional bar drawn as discrete ticks ----------------- */
/* Three countries, three facts each, and nothing drawn between them. The gap
   below is doing the work a rule or a card border would otherwise do: at this
   size the three blocks read as three, and the page needs no more ink to say
   so. Nudge it down and the rows start belonging to each other. */
/* `.band.alt > *` caps its children at --maxw and outranks a bare `.coverage`,
   so the narrower measure has to be written at the same weight. */
.band.alt > .coverage {
  display: grid; gap: clamp(3rem, 7vh, 5rem);
  max-width: 60rem; margin-inline: auto;
}
.cov {
  display: grid; gap: 0.2rem 1.2rem;
  grid-template-columns: minmax(120px, 0.2fr) 1fr auto;
  align-items: center;
}
.cov > b { font-weight: 500; font-size: 0.98rem; }
.cov > span {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 400;
  font-variant-numeric: tabular-nums; color: var(--ink-2);
}
.ticks { display: flex; gap: 3px; align-items: stretch; min-width: 0; }
.ticks i {
  flex: 1 1 0; min-width: 0; height: 26px; border-radius: 2px;
  background: var(--surface-hi);
}
.ticks i.on { background: var(--accent); }

/* The three facts under each bar. Equal-basis columns so they line up down the
   three countries -- ragged columns would read as an accident -- and generous
   space between them instead of dividers. */
.covfacts {
  list-style: none;
  /* The row sits in a grid cell with `justify-items: center`, so without a
     width it is sized to fit its content and the third fact wraps below the
     other two. */
  width: 100%; max-width: 48rem;
  margin: clamp(1.2rem, 3vh, 1.9rem) auto 0;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 1.1rem clamp(1.4rem, 4vw, 3rem);
}
.covfacts li {
  flex: 0 1 11rem;
  display: flex; flex-direction: column; gap: 0.2rem;
}
.covfacts li > b {
  font-family: var(--font-display); font-weight: 400; font-size: 1.05rem;
  letter-spacing: -0.01em; font-variant-numeric: tabular-nums; color: var(--ink);
}
.covfacts li > span { font-size: 0.78rem; color: var(--ink-3); }
/* The city and category names are deliberately not links: their pages are
   behind the login wall, so a visitor reading the landing page would be sent
   to /login by a name that looks like it goes somewhere. */
@media (max-width: 620px) {
  .cov { grid-template-columns: 1fr auto; }
  .ticks { grid-column: 1 / -1; }
  .ticks i { height: 18px; }
}

/* --- Breadth: the taxonomy itself, sized by share of the index ------------ */
/* A narrower measure than the 74rem this had: the rag was long enough that no
   two lines were near the same length, and a shorter one evens it out. */
.catfield {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0.75rem 1.4rem;
  max-width: 54rem;
}
/* Lowercase, like the field list further up. The labels are Title Cased off
   the source slugs, which gives "Health And Medical" and "Atms" -- capitalised
   conjunctions and a shouted acronym both read as unedited output. */
.cf {
  font-family: var(--font-display); font-weight: 300;
  letter-spacing: -0.02em; line-height: 1.3;
  text-transform: lowercase;
  color: var(--ink-2);
  transition: color 0.25s ease;
}
/* Two steps at ~1.6x. The old four spanned 2.5x on one line, and the tall words
   crowded the line above them. */
.cf.s1 { font-size: clamp(1.3rem, 2.4vw, 1.8rem); color: var(--ink); }
.cf.s2 { font-size: clamp(0.95rem, 1.4vw, 1.12rem); }
.catfield:hover .cf { color: var(--ink-3); }
.catfield .cf:hover { color: var(--accent); }

/* The tail, in a sentence. It sits on the cloud's own measure, with the section
   gap's worth of space above it, so it reads as the closing line of the block
   rather than as a caption stuck to the last row of names. */
.cattail {
  max-width: 54rem; margin: clamp(2rem, 5vh, 3.2rem) auto 0;
  font-size: 0.88rem; line-height: 1.65; color: var(--ink-3);
}

/* --- The intro video ------------------------------------------------------ */
/* The poster is a dark frame, so the well behind it is dark too, or the box
   flashes white before the first frame lands. `preload="metadata"` fetches
   only the header -- the file is written with `+faststart`, so that is a
   short read from the front rather than the whole 4.9 MB -- which is what
   puts the real duration on the control bar instead of 0:00. `aspect-ratio`
   holds the space from first paint so nothing below it jumps. */
.videowrap {
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  background: #0b1017;
  line-height: 0;
}
.tourvid {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
}

/* --- The closing statement ------------------------------------------------ */
.closer {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
}
.closer h2 { max-width: 18ch; }

/* --- /about's source note ------------------------------------------------- */
.sourcebox {
  margin: clamp(2rem, 5vh, 3rem) 0;
  padding: clamp(1.5rem, 3.5vw, 2.2rem);
  border: 1px solid var(--line-soft); border-radius: var(--r);
  background: var(--surface-2);
  max-width: 68ch;
}
.sourcebox h2 { font-size: 1.35rem; margin-bottom: 0.8rem; }
.sourcebox p { color: var(--ink-2); font-size: 0.98rem; }
.sourcebox p + p { margin-top: 0.8rem; }
.sourcebox a { color: var(--accent); }

/* ============================================================
   LEGAL PAGES
   Long German prose. Hyphenation is on because the language
   produces words like "Datenschutzerklärung" that will not fit
   a 320px screen otherwise.
   ============================================================ */
.legalpage { max-width: 820px; }
.legalpage h1 { hyphens: auto; overflow-wrap: break-word; }
.legalpage h2 {
  font-size: clamp(1.15rem, 2vw, 1.45rem); font-weight: 500;
  margin: clamp(2rem, 4.5vh, 2.8rem) 0 0.7rem;
  hyphens: auto; overflow-wrap: break-word;
}
.legalpage p, .legalpage li { color: var(--ink-2); font-size: 0.95rem; line-height: 1.75; }
.legalpage p { margin-bottom: 0.9rem; max-width: 72ch; }
.legalpage a { color: var(--accent); }
.legalpage b, .legalpage strong { color: var(--ink); font-weight: 600; }
.legalpage code {
  font-family: ui-monospace, Consolas, monospace; font-size: 0.86em;
  background: var(--surface-hi); padding: 0.08rem 0.34rem; border-radius: 4px;
  overflow-wrap: anywhere;
}
.legalpage .addr { line-height: 1.7; }
.legalpage .missing {
  color: var(--bad); font-weight: 600;
  border-bottom: 1px dashed var(--bad);
}
.legalmeta {
  margin-top: clamp(2.5rem, 5vh, 3.5rem); padding-top: 1.2rem;
  border-top: 1px solid var(--line-soft);
  font-size: 0.82rem; color: var(--ink-3);
}

/* Definition rows inside a legal page. */
.legaldl { display: grid; gap: 0; margin: 0 0 1.2rem; }
.legaldl > div {
  display: grid; grid-template-columns: minmax(140px, 0.3fr) 1fr;
  gap: 0.4rem 1.2rem; padding: 0.6rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.legaldl dt { color: var(--ink-3); font-size: 0.88rem; }
.legaldl dd { color: var(--ink-2); font-size: 0.93rem; }
.legaldl dd a { color: var(--accent); }
@media (max-width: 560px) {
  .legaldl > div { grid-template-columns: 1fr; gap: 0.1rem; }
}

/* The Muster-Widerrufsformular is quoted statutory text; it reads as a form. */
.formular {
  border: 1px solid var(--line); border-radius: var(--r);
  padding: clamp(1.2rem, 3vw, 1.8rem); background: var(--surface-2);
  margin-bottom: 1.2rem;
}
.formular p { margin-bottom: 0.7rem; }

/* ------------------------------------------------ forms with legal weight */
.ctype, .cancelform fieldset {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 0.9rem 1rem; margin-top: 0.4rem;
}
.ctype legend, .cancelform legend {
  font-size: 0.85rem; color: var(--ink-2); padding: 0 0.4rem;
}
label.radio {
  display: flex; align-items: flex-start; gap: 0.6rem;
  font-size: 0.9rem; color: var(--ink-2); margin: 0.5rem 0 0; cursor: pointer;
}
/* `width: auto` because these radios sit inside forms that also carry
   `.authform`, whose `input { width: 100% }` was stretching each one across
   the whole line and pushing its own label onto a second row. */
label.radio input {
  margin-top: 0.25rem; accent-color: var(--accent);
  flex: none; width: auto;
}
label.radio b { color: var(--ink); font-weight: 600; }
.req { color: var(--bad); }

/* --- the § 356 Abs. 5 consent, shown to consumers before Stripe ------------ */

/* The tier being bought, restated so nobody agrees to early performance of a
   contract they have lost track of between the pricing page and here. */
.waivplan {
  display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap;
  margin: 1rem 0; padding: 0.7rem 0.9rem;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface);
}

/* Same shape as label.radio -- including the `width: auto`, for the same
   reason: .authform sets `input { width: 100% }`, which stretches a bare
   checkbox across the line and pushes its label onto a second row. */
label.check {
  display: flex; align-items: flex-start; gap: 0.6rem;
  font-size: 0.9rem; color: var(--ink-2); margin: 0.9rem 0 0; cursor: pointer;
}
label.check input {
  margin-top: 0.25rem; accent-color: var(--accent);
  flex: none; width: auto;
}
label.check a { color: var(--accent); }

/* This is the sentence that costs someone a legal right. It reads at full body
   contrast rather than the muted --ink-2 the surrounding hints use, and it is
   boxed, so it cannot be mistaken for the small print it sits among. */
.waivbox {
  color: var(--ink);
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--accent-line); border-radius: var(--r);
  background: var(--surface);
}

.legalconsent { margin-top: 0.9rem; text-align: center; }
.legalconsent a { color: var(--accent); }

.cancelform { max-width: 560px; margin-top: 1.5rem; }
.cancelform .btn { width: auto; }

/* The § 312j pre-contract note sits directly above the order button, which is
   where the law wants it: immediately before the click that binds. */
.prechecko {
  font-size: 0.78rem; line-height: 1.55; color: var(--ink-3);
  margin-bottom: 0.7rem;
}
.prechecko a { color: var(--accent); }

.vatnote { font-size: 0.76rem; color: var(--ink-3); margin-top: 0.25rem; }

/* ------------------------------------------- the § 312k button in the footer */
.cancelrow {
  display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap;
  border-top: 0; padding-top: 0.6rem; margin-top: 0.8rem;
}
.cancelbtn {
  display: inline-block; padding: 0.45rem 1.1rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--ink-2);
  font-size: 0.85rem; font-weight: 500; white-space: nowrap;
}
.cancelbtn:hover { border-color: var(--accent-line); color: var(--accent); text-decoration: none; }

/* A flex item will not shrink below its content unless told to: `min-width`
   defaults to `auto`. German compounds are long enough that this pushed the
   cancellation form 92px past the edge of a phone. */
label.radio > span { min-width: 0; overflow-wrap: break-word; hyphens: auto; }
.legalpage p, .legalpage li, .legalpage dd { hyphens: auto; }
/*
 * No `flex-wrap` here, deliberately. Wrapping does not make a long label wrap
 * its *text* -- it moves the whole span onto its own line below the radio,
 * which is what both of these fieldsets were doing. The span shrinking (it has
 * `min-width: 0` above) is what actually wants to happen. That does mean a
 * label whose text is a bare node rather than a wrapped span will overflow
 * again: an anonymous flex item gets `min-width: auto` and cannot be selected
 * to fix it, so wrap the text in a span. Both labels on the site do.
 */
label.radio { min-width: 0; }


/* ============================================================
   EVERYTHING ON THE CENTRE LINE
   The whole site is centred, not only the marketing pages.

   Two things this deliberately leaves alone. Form fields keep
   their normal alignment: centring the characters someone is
   typing walks the caret out from under their hand, which is a
   usability cost rather than a style. And the brand stays in
   the header's left column, because the nav dock is already
   centred on the viewport and two centred things in one bar
   have nowhere to sit.

   One problem recurs the whole way down. Every max-width in
   this design was written against a left edge, and every
   "label here, value over there" row was either a
   `justify-content: space-between` flex or a two-column grid.
   Centred text does not fix either -- each one has to be
   re-centred or collapsed into a stack by hand.
   ============================================================ */
body { text-align: center; }

/* What you type stays where you can follow it. */
input, select, textarea, option { text-align: left; }

/* Reading widths. A max-width with no auto margin stays pinned to the left
   while the text inside it floats to the middle, which looks like a bug. */
.lede, .prose, .hero h1, .hero .sub, .shead h2,
.closer h2, .catfield, .faq, .faq p, .facts2 p, .split-text p,
.sourcebox, .fcols p, .legalpage p, .cancelform { margin-inline: auto; }

/* ------------------------------------------------------------------ hero */
/* The panel used to hold its type in the bottom-left corner. Centred type
   wants the middle of the band instead. */
.hero-panel { align-items: center; }
.heroact { justify-content: center; }

/* --------------------------------------------------------------- sections */
/* A heading centred on the page with its link still shoved to the far right
   reads as a mistake, so the two stack. */
.shead { flex-direction: column; align-items: center; justify-content: center; }

/* Where a section starts. A short rule on the same centre line as the type,
   rather than a full-width one: the field list and the FAQ are already built
   out of edge-to-edge rules, and another would read as more of the same
   instead of as a boundary.

   It lives here, in the centred block, because it is a flex item -- the
   declaration above is what guarantees it lands above the heading rather
   than beside it. */
.shead::before, .faq.sp::before {
  content: ""; flex: 0 0 auto; justify-self: center;
  width: 52px; height: 1px; background: var(--rule);
  margin-bottom: clamp(1.5rem, 3.5vh, 2.3rem);
}
/* Two blocks opt out. The first section under the hero already has a boundary
   -- the hero's own hairline, a few lines of whitespace above it -- and the
   closing line ends the page, where a mark would be announcing a section that
   never arrives. */
.shead.bare::before { content: none; }

.fieldlist li { text-align: center; }

/* Country, bar, count -- one column. The label and the total are the text. */
.cov { grid-template-columns: 1fr; gap: 0.45rem; justify-items: center; }
.ticks { width: 100%; }

.catfield { justify-content: center; }
.closer { flex-direction: column; justify-content: center; }

/* The summary only had right padding, to clear its chevron. Centred text
   needs the same on both sides or it sits a whole rem off centre. */
.faq summary { padding-inline: 2rem; }
.faq p { padding-inline: 2rem; }

/* ----------------------------------------------------------------- plans */
/* The tick markers are absolutely positioned against their list item, so each
   item has to shrink to its own text and carry its own marker. Left as full
   blocks they would be a column of ticks at the card's edge pointing at
   nothing.
   `fit-content` with auto margins does the shrinking *and* keeps one item per
   line. They were inline-blocks, which flowed like words -- fine while every
   label was long, but once the labels were shortened "Up to 50 per page" and
   "Export" started sharing a row. */
.planflag { left: 50%; transform: translateX(-50%); }
.plan .price { justify-content: center; }
.planlist { display: block; }
.planlist li {
  display: block; width: fit-content; max-width: 100%;
  margin-inline: auto; text-align: left;
}
.planlist li + li { margin-top: 0.55rem; }

/* A bulleted list needs its markers inside, or they hang off to the left of
   text that is no longer there. */
.ladder ul { padding-left: 0; list-style-position: inside; }

/* --------------------------------------------------------- results chrome */
.toolrow, .filters, .meta, .dactions, .acctions,
.cancelrow, label.radio { justify-content: center; }

/* ---------------------------------------------------------------- tables */
/* Both tables, header row included: a centred page with a left-aligned grid
   of names in the middle of it looks unfinished rather than dense. */
.rtable th, .rtable td,
.cmp th, .cmp td { text-align: center; }

@media (max-width: 720px) {
  /* Stacked rows are `label: value` flex lines; the label had a fixed 5.5rem
     column, which puts the pair off centre. */
  .rtable td { justify-content: center; }
  .rtable td::before { flex: 0 0 auto; }
}
@media (max-width: 620px) {
  .cmp td { justify-content: center; }
}

/* Autocomplete rows: name and count centred as one pair. (`.tiles a` was
   centred here too, until the A-Z index replaced it.) */
.aci { justify-content: center; }
.aci i { margin-left: 0; }

/* The A-Z index is the one list that deliberately stays left-aligned. Centring
   each name would give every column a ragged left edge, and lining the initials
   up under their letter is the entire point of the page -- so the block is
   centred, and the names inside it are not. */

/* ------------------------------------------------- detail, account, legal */
/* Every definition list on the site is a label column beside a value column.
   Centred, that is two ragged edges facing each other, so they become stacks. */
.acctable > div, .dmain dl > div, .legaldl > div {
  grid-template-columns: 1fr; gap: 0.15rem; justify-items: center;
}

/* --- The operator's cancellation queue ------------------------------------
   Only ever rendered for the person running the site, so it is styled as a
   working surface rather than as part of the product: denser, plainer, and
   using the warning colour rather than the accent, because an item here is
   something owed to a customer and not a feature to be pleased about. */
.opbar {
  display: block; text-align: center; text-decoration: none;
  background: light-dark(rgba(138, 91, 0, 0.10), rgba(232, 180, 76, 0.12));
  border-bottom: 1px solid var(--line);
  color: var(--warn); font-weight: 600; font-size: 0.92rem;
  padding: 0.7rem var(--gutter);
}
.opbar:hover { background: light-dark(rgba(138, 91, 0, 0.16), rgba(232, 180, 76, 0.18)); }

.oplist { list-style: none; margin: 0 0 2.5rem; padding: 0; display: grid; gap: 1rem; }
.oplist.quiet { opacity: 0.62; }
.opitem {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 1rem 1.1rem; background: var(--surface-2);
}
.opitem.done { border-style: dashed; }

.ophead { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.9rem; }
.opref { font-family: var(--font-mono, ui-monospace, monospace); font-weight: 600; }
.opwhen { color: var(--ink-3); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.opflag {
  margin-left: auto; font-size: 0.72rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 0.15rem 0.5rem; border-radius: var(--r-sm);
}
.opflag.open { background: light-dark(rgba(138, 91, 0, 0.14), rgba(232, 180, 76, 0.16)); color: var(--warn); }
.opflag.done { background: var(--surface); color: var(--ink-3); }

.opdl { margin: 0.9rem 0 0; display: grid; gap: 0.3rem; }
.opdl > div { display: grid; grid-template-columns: 11rem 1fr; gap: 0.9rem; align-items: baseline; }
.opdl dt { color: var(--ink-3); font-size: 0.85rem; }
.opdl dd { margin: 0; overflow-wrap: anywhere; }

.opform { margin-top: 1rem; }
.opsteps { border-top: 1px solid var(--line); padding-top: 1.5rem; }
.opsteps h2 { font-size: 1.05rem; margin-bottom: 0.7rem; }
.opsteps ol { color: var(--ink-2); padding-left: 1.2rem; display: grid; gap: 0.5rem; max-width: 62ch; }
.opseen { font-size: 1rem; color: var(--ink-3); margin-bottom: 0.8rem; }

@media (max-width: 640px) {
  .opdl > div { grid-template-columns: 1fr; gap: 0; }
  .opflag { margin-left: 0; }
}
