/* ===================================================================
   UNCLAIMED — liquid glass.

   Dark, cinematic, near-invisible panels whose edges are drawn by a
   masked gradient stroke rather than a border. The canvas is a soft warm
   slate rather than pure black; sections butt against each other and the
   ambient gradient field does the separating.

   The class API is unchanged from the previous light theme on purpose —
   4,300 generated pages use these names, so restyling here restyles the
   whole site without touching the generator.

   Rules that matter, in order:
     - fill is ~1% white. Past 0.02 it reads as milky 2016 glassmorphism.
     - no `border` anywhere. The edge is a ::before ring, bright at top
       and bottom, gone at the sides — light catching the lip of a pane.
     - an editorial serif carries the headlines, a UI sans the body.
     - motion is entry-only and never bounces.
   =================================================================== */

/* Two faces, one job each.

   Instrument Serif sets the headlines: a high-contrast editorial serif, which
   is the register a page about public money and entitlements has to be read
   in. Inter sets everything else, because the body of this site is dense —
   rule tables, amounts, eligibility conditions — and Inter is drawn for
   exactly that at small sizes.

   Both carry a full fallback stack, so a visitor whose browser blocks Google
   Fonts gets Georgia and their platform's UI sans rather than a cursive
   default, and the page still reads as the same page. */
@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600;700&display=swap');

:root {
  /* One focus colour, and not terracotta.
     Terracotta is simultaneously the error family (.notice--error,
     .ticks--no li::before, .status--closing) and the eyebrow accent, so a
     focus ring in it reads as "you have done something wrong". Four local
     overrides used slate rgba(29,36,48,.6) instead, which meant 14 consecutive
     focus stops in the wizard were slate while the rest of the site was
     terracotta. One variable, one colour, no local overrides. */
  --focus: #1f5fa8;

  --lg-fill: rgba(255, 255, 255, 0.72);
  --lg-blur: 4px;
  --lg-blur-strong: 50px;
  --lg-stroke: 1.4px;

  --font-heading: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  --ink:   #12303a;
  --ink-2: rgba(18, 48, 58, 0.82);
  /* The secondary ramp, darkened one notch each.
     --ink-4 was .54, which composites to 3.25–3.34:1 against the real card
     and paper backgrounds at 11.5–14.7px — under AA 1.4.3 everywhere it is
     used, and it is used by 23 rules: the footer legal disclaimer on ~5,900
     pages, the monthly price on /pricing/, the programme count on every
     /check/ country tile, every .tiny and .eyebrow on the site. It is not a
     decorative grey; it is the label layer. .68 lands at ≈4.75:1 on --paper
     and higher on a card. --ink-3 moves with it so the ramp keeps three
     distinguishable steps (≈12.8 / 6.5 / 4.75) rather than collapsing into
     two. */
  --ink-3: rgba(18, 48, 58, 0.78);
  --ink-4: rgba(18, 48, 58, 0.68);
  --line:   rgba(18, 48, 58, 0.13);
  --line-2: rgba(18, 48, 58, 0.19);

  /* Turquoise through blue to green, kept pale so text carries the contrast
     and the colour is atmosphere rather than decoration. */
  --paper:   #eef7f7;
  --cream:   #f6fbfa;
  --paper-2: #ffffff;
  --teal:    #0f6f76;
  --teal-2:  #14868d;
  --blue:    #1f5fa8;
  /* #1f7a52 measured 4.44:1 behind .badge-verified, whose chip is a tinted
     rgb(227,237,238) rather than the paper it was picked against — under the
     4.5 floor on the one badge whose whole job is to be believed. Darkened to
     5.09:1 on that chip and 5.82:1 on paper. --green moves with it; they were
     the same colour and drifting them apart would be two greens, not one. */
  --green:   #1c7049;
  --terracotta: #a8442a;
  --sage: #1c7049;
  --gold: #715c0d;

  --shell: 1180px;
  --r-card: 1.25rem;
  --r-media: 1.5rem;
}

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

/* The masthead is sticky, so an anchored target scrolls to y=0 and arrives
   underneath it: 83px of the heading hidden on desktop, 129px on a phone —
   the whole heading and the first line of what follows. scroll-padding-top is
   the only thing that fixes it for every route in (a #link, the skip link
   from a scrolled position, scrollIntoView, a page loaded on a #fragment);
   a scroll-margin on each target only fixes the targets somebody remembered.
   The values clear the two heights the masthead actually has — measured, not
   assumed — with a little over. The 560px breakpoint is where it grows. */
html { scroll-behavior: smooth; scroll-padding-top: 6rem; }
@media (max-width: 560px) { html { scroll-padding-top: 8.5rem; } }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Ambient field. Glass over flat black is invisible — it needs something
   behind it. Two slow, very dark colour washes give the panels something
   to refract without ever reading as a "gradient background". */
body::before {
  content: '';
  position: fixed;
  inset: -20%;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(52rem 52rem at 10% -10%, rgba(45, 190, 190, 0.30), transparent 62%),
    radial-gradient(46rem 46rem at 92% 2%, rgba(80, 170, 235, 0.24), transparent 64%),
    radial-gradient(58rem 58rem at 46% 106%, rgba(60, 185, 145, 0.24), transparent 62%),
    linear-gradient(165deg, #f2fbfa 0%, #eef7f7 48%, #e7f2f4 100%);
  animation: drift 34s ease-in-out infinite alternate;
}
@keyframes drift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-2%, 2%, 0) scale(1.08); }
}

/* Fine grain stops the large dark areas from banding on cheap panels. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .55;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.035'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------------ */
/* Liquid glass                                                        */
/* ------------------------------------------------------------------ */

.liquid-glass, .card, .callout, .pill, .badge, .tag, .btn, .stat, .masthead__inner, .flag-chip {
  position: relative;
  border: none;
  background: var(--lg-fill);
  backdrop-filter: blur(var(--lg-blur));
  -webkit-backdrop-filter: blur(var(--lg-blur));
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.9), 0 1px 2px rgba(29, 36, 48, 0.05);
}

.liquid-glass::before, .card::before, .callout::before, .pill::before,
.badge::before, .tag::before, .btn::before, .stat::before,
.masthead__inner::before, .flag-chip::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: var(--lg-stroke);
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(255,255,255,.45) 0%, rgba(29, 36, 48, 0.14) 20%,
    rgba(255,255,255,0) 40%, rgba(255,255,255,0) 60%,
    rgba(29, 36, 48, 0.14) 80%, rgba(255,255,255,.45) 100%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
}

.liquid-glass-strong {
  backdrop-filter: blur(var(--lg-blur-strong));
  -webkit-backdrop-filter: blur(var(--lg-blur-strong));
}

/* ------------------------------------------------------------------ */
/* Type                                                                */
/* ------------------------------------------------------------------ */

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 400;
  letter-spacing: -.02em;
  line-height: 1.02;
  margin: 0 0 .6rem;
  color: var(--ink);
}
h1, h2.h1-lg { font-size: clamp(2.6rem, 6.4vw, 5rem); letter-spacing: -.035em; line-height: .95; }
h2 { font-size: clamp(1.9rem, 3.6vw, 3rem); letter-spacing: -.028em; }
h3 { font-size: 1.3rem; letter-spacing: -.018em; }
/* The uppercase eyebrow is a LOOK, not a heading level.
   Binding it to `h4` meant the tag was picked for its typography, so the
   footer jumped h2 → h4 and /account/ and /check/ ran h1 → h4 with nothing in
   between — a screen-reader user hears two missing levels and assumes content
   was skipped. The class carries the treatment now; the tag carries the
   level. `h4` keeps the same skin only so any call site not yet converted
   does not change appearance mid-migration. */
h4, .h-eyebrow {
  font-size: .82rem; font-family: var(--font-body); font-weight: 500;
  text-transform: uppercase; letter-spacing: .18em; color: var(--ink-4);
  line-height: 1.3;
}
h2.h-eyebrow, h3.h-eyebrow { letter-spacing: .18em; }

/* The signature move: one clause upright, one italic at reduced opacity. */
.serif-italic, em.display { font-style: italic; color: var(--ink-3); }

p { margin: 0 0 1rem; color: var(--ink-2); }
.lede { font-size: clamp(1.05rem, 1.7vw, 1.3rem); color: var(--ink-2); line-height: 1.5; }
.small { font-size: .92rem; color: var(--ink-3); line-height: 1.6; }
.tiny  { font-size: .8rem;  color: var(--ink-4); line-height: 1.6; }

a { color: var(--teal); text-decoration: none; transition: color .2s ease, opacity .2s ease; }
a:hover { opacity: .72; }
.link-underline { text-decoration: underline; text-underline-offset: 3px;
                  text-decoration-color: rgba(29,36,48,.35); }
.link-underline:hover { text-decoration-color: rgba(29,36,48,.85); }

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

/* ------------------------------------------------------------------ */
/* Layout                                                              */
/* ------------------------------------------------------------------ */

.shell { width: min(100% - 2.5rem, var(--shell)); margin-inline: auto; }
/* A reading column. Used by /methodology/, /privacy/ and every blog post —
   and never defined, so all three rendered flush to the left edge of the
   viewport with lines running the full width of a desktop screen. */
.shell-narrow { width: min(100% - 2.5rem, 72ch); margin-inline: auto; }
.shell-narrow p, .shell-narrow li { max-width: 68ch; }
/* Vertical rhythm, measured rather than asserted.

   Both `.section` and `.section-tight` paid full padding on both edges, so at
   1536px two adjacent home-page sections left 256px of nothing between them,
   /us/ left a 144px dead band between the stat strip and the filters that
   filter it, and every page left 145px above the footer. The old comment
   claimed "the ambient gradient field does the separating"; that gradient
   runs at 24–30% alpha and separates nothing. Adjacent sections of the same
   kind now share one gap instead of stacking two. */
.section { padding-block: clamp(3rem, 6vw, 5rem); }
.section-tight { padding-block: clamp(2.5rem, 5vw, 4.5rem); }
.section + .section,
.section-tight + .section-tight,
/* The home page's sequence is hero-centre → section-tight → section → …, and
   only like-with-like collapsed — so the first two joints measured 119px and
   139px while the last three measured 56–58px. Every adjacency between these
   four, not just the matching pairs. */
.section-tight + .section,
.section + .section-tight,
.hero-centre + .section,
.hero-centre + .section-tight { padding-top: 0; }

/* And the other half of the same arithmetic: a trailing margin on the LAST
   thing inside a section stacks on top of the section's own padding-bottom.
   /startups/gb/ ends in a `.locked-bucket--inline`, whose `margin: 2.6rem 0`
   put 42px under the last row before the section's 72px even began — 195px
   of blank above the footer, against a 152px budget the two blocks actually
   declare. The margin is right between siblings and wrong at the end.

   Written three levels deep on purpose: the offending margin is not on the
   section's own last child but on the last child of the last child of it, and
   because the wrapper has neither padding nor a border the inner margin
   COLLAPSES OUT through it — the wrapper's own computed margin-bottom reads
   0px while the space is still there. Zeroing only the outer element
   therefore changes nothing at all, which is exactly what the first version
   of this rule did.

   And `main > *`, not `main > section`: /dashboard/ ends in a bare
   <div class="shell"> and so matched none of these, which is the entire
   16px by which its pre-footer joint differed from every other page. The
   block is a <section> now, but the selector is broadened as well — either
   fix alone leaves the trap set for the next top-level <div> anyone adds. */
main > * > *:last-child,
main > * > *:last-child > *:last-child,
main > * > *:last-child > *:last-child > *:last-child { margin-bottom: 0; }
/* The deadline-alerts slot (src/pages/alerts.mjs) stays hidden unless the
   Worker says alerts are configured. While hidden it is still the section's
   last child, so the block the reader actually sees last would miss the rule
   above and leave its bottom margin hanging over the footer. */
.alerts-slot { margin-top: 2rem; }
main > * > *:has(+ .alerts-slot[hidden]:last-child),
main > * > *:has(+ .alerts-slot[hidden]:last-child) > *:last-child { margin-bottom: 0; }

.section-rule { border-top: 1px solid var(--line); }

.grid { display: grid; gap: 1.1rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
/* A genuine two-up. .grid-2 is auto-fit at 280px, which becomes four columns
   on a wide screen — right for cards, wrong for a paragraph, which turns into
   a 200px-wide column of two-word lines. */
.grid-2x { grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.row { display: flex; align-items: center; flex-wrap: wrap; gap: .6rem; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: .8rem; flex-wrap: wrap; }
.spread { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }

/* ------------------------------------------------------------------ */
/* Components                                                          */
/* ------------------------------------------------------------------ */

.card {
  border-radius: var(--r-card);
  padding: 1.4rem;
  overflow: hidden;
  transition: background .3s ease, transform .3s ease;
}
.card:hover { background: #fff; }
.card-link { display: block; }
.card-link:hover { transform: translateY(-2px); opacity: 1; }
/* The "Check what you're owed →" line at the foot of each home-page product
   card was an inline `color:#fff` left over from the dark theme: 1.02:1
   against the composited rgb(250,253,253) card, so on all seven home pages
   the only call to action inside the card was invisible. It is a real
   component now, in --teal, and the arrow slides on hover the way a link
   should rather than the text disappearing. */
.card-link__go {
  color: var(--teal); font-size: .9rem; margin-top: .8rem;
  display: inline-flex; align-items: center; gap: .35rem;
  transition: gap .2s ease;
}
.card-link__go svg { width: 14px; height: 14px; }
.card-link:hover .card-link__go { gap: .6rem; }

.card-flat { background: transparent; box-shadow: none; }
.card-flat::before { display: none; }

.callout { border-radius: var(--r-card); padding: 1.2rem 1.4rem; }
.callout p:last-child { margin-bottom: 0; }
.callout--sage::before {
  background: linear-gradient(180deg, rgba(127,209,174,.6) 0%, rgba(127,209,174,.18) 22%,
    transparent 42%, transparent 58%, rgba(127,209,174,.18) 78%, rgba(127,209,174,.6) 100%);
}

.pill, .badge, .tag {
  display: inline-flex; align-items: center; gap: .4rem;
  border-radius: 9999px; padding: .3rem .8rem;
  font-size: .78rem; font-weight: 400; color: var(--ink-2);
  letter-spacing: .01em; white-space: nowrap;
}
/* One rule used to draw both `a.tag` (navigates to a category listing) and
   `span.badge-neutral` (inert, same label text, same page family), so the two
   were pixel-identical glass pills. A reader who learns the pill is clickable
   on /us/ clicks a dead one on the programme page and concludes the site is
   broken. The affordance now lives in the skin: a tag lifts and turns teal
   under the pointer, a badge is flat and cannot. */
a.tag { cursor: pointer; }
a.tag:hover, a.tag:focus-visible { background: #fff; color: var(--teal); opacity: 1; }
.tag__n { font-variant-numeric: tabular-nums; color: var(--ink-3); margin-left: .4rem; }
a.tag:hover .tag__n, a.tag:focus-visible .tag__n { color: var(--teal); }
.badge { cursor: auto; box-shadow: none; background: rgba(18,48,58,.05); }
/* The recommended tier, said in words. `.card--featured`'s 2px outline is the
   same visual device as :focus-visible, so on a keyboard pass it read as a
   focus ring on whatever card happened to be featured. */
.badge-pick { background: rgba(15,111,118,.10); color: var(--teal); }

/* The programme page's primary action, repeated for narrow screens only.

   Below 900px `.sticky-side` goes static and lands about 4,000px down, after
   three locked panels — so on a phone the only conversion on the page was the
   last thing on it. Exactly one of the two is ever in the layout, so a test
   counting visible primaries still finds one. */
.prog-hoist { display: none; margin: 1.2rem 0 0; }
@media (max-width: 900px) {
  .prog-hoist { display: block; }
  .prog-hoist > .btn { width: 100%; justify-content: center; }
  .sticky-side .btn-primary { display: none; }
}
.pill-accent, .badge-verified { color: var(--sage); }
.badge-auto { color: var(--ink-4); }
.eyebrow {
  display: inline-block; font-size: .72rem; text-transform: uppercase;
  letter-spacing: .2em; color: var(--ink-4); margin-bottom: .7rem;
}
.eyebrow-accent { color: var(--terracotta); }

/* `hidden` must actually hide.

   The user-agent rule is `[hidden] { display: none }`, and any author rule
   that sets `display` beats it — so `.btn { display: inline-flex }` meant
   every button in this codebase ignored the attribute. The account page set
   `#acct-check-free.hidden = true` for a subscriber and the duplicate "Go to
   my check" button stayed on the screen next to the real one. Nothing errors;
   the state machine is correct and the page disagrees with it.

   First rule in this section so it is impossible to miss, and `!important`
   because the point is to beat every author rule that follows. */
[hidden] { display: none !important; }

/* justify-content is a no-op on an auto-width button and the whole fix on a
   full-width one. Without it /account/'s "Send me a code" was a 499px pill
   with its label 22px from the left and 366px of empty space to the right, and
   both sidebar primaries on all 2,216 programme pages did the same. The
   stacked-button rules below used text-align: center, which does nothing to a
   flex container — .prog-hoist > .btn already carried the real fix. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  border-radius: 9999px; padding: .72rem 1.4rem;
  font-family: var(--font-body); font-size: .95rem; font-weight: 400;
  color: var(--ink); cursor: pointer; transition: background .2s ease, transform .2s ease;
}
.btn:hover { background: #fff; transform: scale(1.02); opacity: 1; }
.btn-sm { padding: .48rem 1rem; font-size: .87rem; }
.btn-primary { background: var(--teal); color: #fff; box-shadow: none; }
.btn-primary::before {
  background: linear-gradient(180deg, rgba(255,255,255,.9), rgba(255,255,255,.4));
}
.btn-primary:hover { background: var(--teal-2); }
.btn-ghost { background: transparent; }

/* A disabled button must stop behaving like a button.

   There was no `:disabled` rule anywhere in this file, while `.btn` sets
   `cursor:pointer` and `.btn:hover` turns the surface white and scales it up
   unconditionally. Watched with a MutationObserver against a slow
   /auth/request, a button correctly showing "Sending…" with `disabled=true`
   still lit up and grew under the pointer — the exact feedback that means
   "press me", offered to someone whose press will be swallowed. Hence the
   per-variant :hover overrides as well: the generic rule alone loses to
   `.btn-primary:hover`. */
.btn:disabled, .btn[aria-disabled='true'] {
  opacity: .55; cursor: default; transform: none;
}
.btn:disabled:hover, .btn[aria-disabled='true']:hover {
  transform: none; background: var(--lg-fill);
}
.btn-primary:disabled:hover, .btn-primary[aria-disabled='true']:hover { background: var(--teal); }
.btn-ghost:disabled:hover, .btn-ghost[aria-disabled='true']:hover { background: transparent; }

/* Pending work needs a sign that something is happening, not just a label
   that stopped responding. 14px so it sits on the cap height of .btn text. */
.btn__spinner {
  width: 14px; height: 14px; flex: none; border-radius: 50%;
  border: 2px solid currentColor; border-right-color: transparent;
  opacity: .7; animation: btn-spin .7s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn__spinner { animation-duration: 2.4s; } }

.stat { border-radius: var(--r-card); padding: 1.1rem 1.2rem; }
.stat__n { font-family: var(--font-heading); font-size: 2.4rem; line-height: 1; color: var(--ink); display: block; }
.stat__l { font-size: .8rem; color: var(--ink-4); text-transform: uppercase; letter-spacing: .14em; }
.figure-sm { font-family: var(--font-heading); font-size: 2rem; line-height: 1.05; color: var(--ink); margin: .3rem 0 .5rem; }

.flag-chip { display: inline-flex; align-items: center; gap: .5rem; border-radius: 9999px; padding: .3rem .8rem .3rem .4rem; }
.flag-chip__flag { font-size: 1.05rem; }

/* Programme rows. Deliberately not cards — a list of 200 wants density. */
.list-rows { display: flex; flex-direction: column; }
.list-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: .95rem .2rem;
  border-bottom: 1px solid var(--line);
  transition: background .2s ease, padding-left .2s ease;
}
/* Only rows that go somewhere. This slid the inert "What blocked you" and
   "Why N ruled you out" tables 8px right and filled them solid white on hover
   — the markup at src/app.js already patched style="cursor:default" onto them
   because someone knew they were inert. An affordance on a dead row is a
   promise the row cannot keep. */
a.list-row:hover { background: #fff; padding-left: .7rem; }
.list-row__name { font-size: 1.02rem; color: var(--ink); }
.list-row__meta { font-size: .82rem; color: var(--ink-4); margin-top: .15rem; }
/* The templates emit `</span><span` with no whitespace between the count and
   its unit, so /countries/, /browse/, the results screen and 46 built pages
   rendered "16programmes". A gap, not a literal space: a space in the markup
   is one edit away from being lost again and says nothing about intent. */
.list-row__right {
  display: flex; align-items: baseline; justify-content: flex-end;
  gap: .35rem; text-align: right; flex-shrink: 0;
}
.list-row__amount { font-family: var(--font-heading); font-size: 1.25rem; color: var(--ink); }

/* ------------------------------------------------------------------ */
/* Chrome                                                              */
/* ------------------------------------------------------------------ */

.masthead { position: sticky; top: 0; z-index: 60; padding: .9rem 0; }
.masthead__inner {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  border-radius: 9999px; padding: .5rem .5rem .5rem 1.2rem;
  backdrop-filter: blur(var(--lg-blur-strong));
  -webkit-backdrop-filter: blur(var(--lg-blur-strong));
  /* Wider than the text column. The bar carries a wordmark, six links, an
     account chip, a language menu and a call to action; at --shell (1180px)
     with Inter that is 40px too much and the links wrapped mid-phrase —
     "Grants / workspace" over two lines, and the bar 20px taller than it
     should be. The copy below still measures at --shell. */
  width: min(100% - 2.5rem, 1320px); margin-inline: auto;
}
.wordmark { display: inline-flex; align-items: center; gap: .55rem;
            font-family: var(--font-heading); font-size: 1.3rem; letter-spacing: -.02em; color: var(--ink); }
.wordmark__seal { width: 1.35rem; height: 1.35rem; opacity: .9; }
/* .35rem left the account chip 6px from the primary button beside it — close
   enough that they read as one control and, on a phone, close enough to
   mis-tap. */
.nav { display: flex; align-items: center; gap: .7rem; }
.nav--links { gap: 1.15rem; font-size: .88rem; color: var(--ink-3); }
/* A menu item is a label, not a sentence: it wraps as a whole or not at all. */
.nav--links a { color: var(--ink-3); white-space: nowrap; }
.nav--links a:hover { color: var(--ink); opacity: 1; }

/* Site-wide search box, in every masthead — an icon pill that expands to a
   readable field only on focus (:focus-within, no script), so the header's
   resting width barely changes: the row already carries a wordmark, six
   links, a language menu, an account chip and a CTA at --shell, and an
   always-open ~11rem field here is what wrapped "Check what you're owed"
   onto two lines at 1536px and overlapped the sign-in link with the home
   page's audience switch at 1280px (scripts/qa-screens.mjs). */
.header-search {
  position: relative; display: flex; align-items: center; flex: 0 0 auto;
  width: 2.3rem; margin: 0; border-radius: 9999px;
  background: rgba(255,255,255,.55); border: 1px solid var(--line);
  transition: width .18s ease;
}
.header-search:focus-within { width: 11rem; }
.header-search__icon {
  position: absolute; left: .6rem; top: 50%; transform: translateY(-50%);
  color: var(--ink-3); display: flex; pointer-events: none;
}
.header-search__input {
  width: 100%; min-width: 0; font: inherit; font-size: .85rem; color: var(--ink);
  background: none; border: 0; border-radius: inherit; padding: .4rem .7rem .4rem 1.9rem;
}
.header-search__input::placeholder { color: var(--ink-3); }
.header-search:focus-within { outline: 2px solid var(--teal); outline-offset: 1px; }
@media (max-width: 620px) { .header-search { display: none; } }

/* The account chip. Deliberately the one thing in the masthead with a border:
   "what am I paying for, and how do I change it" is the question a paywalled
   product must always have a visible answer to, and this one had none. */
/* A row of buttons, with room between them.
   
   Buttons were being laid out by whitespace between inline-block elements,
   which collapses to a 4-6px sliver — measured 5px between "Open the
   workspace" and "What it does" on the pricing page. Two controls that close
   together read as one control, and on a touch screen they are a mis-tap
   waiting to happen: the WCAG target-spacing guidance is 24px, and 8px of gap
   plus the buttons' own padding clears it comfortably. */
.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem .7rem;
  align-items: center;
}
.btn-row > .btn { margin: 0; }
@media (max-width: 560px) {
  /* Stacked, full width, so a thumb cannot land between two of them. */
  .btn-row { flex-direction: column; align-items: stretch; }
  .btn-row > .btn { width: 100%; }
}

/* The hero's pair of calls to action is the same row, and had no layout rule
   at all — `.hero__cta` appeared in the markup on every country page and
   nowhere in this file, so its two buttons sat on the 4px sliver between two
   inline-blocks. */
.hero__cta { display: flex; flex-wrap: wrap; gap: .6rem .7rem; align-items: center; }
@media (max-width: 560px) {
  .hero__cta { flex-direction: column; align-items: stretch; }
  .hero__cta > .btn { width: 100%; }

  /* The hero was not the only place two buttons sit in a row. The closing
     block on the home page, /enterprise/'s pair and the workspace's empty
     state all use the generic .row, and at 390px they split the width two
     ways — "Voir les tarifs" came out 132px wide. Same treatment, scoped with
     :has() so it only reaches a row that actually holds a button pair and
     leaves the many .row elements holding chips, badges and meta text alone.
     .wizard-nav is exempt: back-and-forward genuinely belong side by side. */
  .row:has(> .btn + .btn):not(.wizard-nav .row) { flex-direction: column; align-items: stretch; }
  .row:has(> .btn + .btn):not(.wizard-nav .row) > .btn { width: 100%; margin-left: 0; }
}

/* ------------------------------------------------------------------ */
/* The rules these class names were already asking for                 */
/*                                                                      */
/* Every name below appears in the built HTML — some on thousands of    */
/* pages — and appeared nowhere in this file. A class with no rule does  */
/* not warn, it just renders as an unstyled <div>, which is why the      */
/* programme page had no two-column layout, breadcrumbs ran together,    */
/* and every badge was the same grey.                                    */
/* ------------------------------------------------------------------ */

/* Breadcrumbs. Separators are drawn by CSS rather than written into the
   markup, so a screen reader reads the trail as links and not as slashes. */
.breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: .1rem .55rem;
              font-size: .85rem; color: var(--ink-4); margin-bottom: 1.4rem; }
.breadcrumb a { color: var(--ink-3); }
.breadcrumb a:hover { color: var(--ink); }
.breadcrumb > * + *::before { content: '›'; margin-right: .55rem; color: var(--ink-4); }

/* The programme page: content beside the sticky sidebar. */
.detail-grid { display: grid; grid-template-columns: minmax(0, 1fr) 20rem; gap: 2.5rem; align-items: start; }
@media (max-width: 900px) { .detail-grid { grid-template-columns: minmax(0, 1fr); gap: 1.8rem; } }

/* Where a programme's facts came from — quoted, so it reads as evidence. */
.source-block { border-left: 3px solid var(--line-2); padding: .1rem 0 .1rem 1rem; }
.source-block blockquote { margin: 0; font-style: italic; color: var(--ink-2); }
.source-block a { overflow-wrap: anywhere; }

.serif-italic { font-style: italic; }
/* The programme's name in its own language, under the English one. */
.serif { font-style: italic; color: var(--ink-3); }

/* The wrapper around the two-panel pricing switch. Present on the pricing and
   account pages; the panels and the tabs were styled, the box holding them
   was not, so the two sat with no separation from the copy above. */
.audience { margin-top: 1.6rem; }

/* Badges. Three states that must be distinguishable at a glance: this pays
   out on its own, you have to claim it, or it is a neutral fact. */
.badge-neutral { color: var(--ink-3); }
.badge-auto-apply { color: #1c5b3a; background: rgba(47, 111, 79, .12); }
.badge-action { color: #6a4a12; background: rgba(160, 110, 20, .12); }

/* The one callout that carries a warning rather than a note. */
.callout--terracotta { background: rgba(176, 88, 56, .09); }
.callout--terracotta::before { background: linear-gradient(180deg, rgba(176, 88, 56, .55), transparent 45%, rgba(176, 88, 56, .35)); }

/* Four numbers under a country hero, and the category tags under those. */
.stat-strip { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 2rem; margin-bottom: 1.75rem; }
.stat-strip > .stat { flex: 1 1 8rem; padding: .9rem 1.1rem; border-radius: 1.1rem; }
/* The category filters belong to the numbers above them; 144px of air said
   they were a separate thing. */
.filters { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.25rem; }

.center { text-align: center; }

/* The programme sidebar.

   `.sticky-side` and `.stack` are in the markup of every one of the 2,216
   programme pages and neither appears anywhere else in this file, so the aside
   was not sticky and its three full-width buttons sat flush against each other
   with no gap at all. Class names that nothing styles are the quietest bug in
   this codebase: the page renders, it is simply wrong. */
.stack { display: flex; flex-direction: column; gap: .55rem; }
.sticky-side { position: sticky; top: 5.5rem; align-self: start; }
.stack > .btn + .btn { margin-left: 0; }
@media (max-width: 900px) { .sticky-side { position: static; } }

/* Tables.

   `.rule-table` is used on every programme page, in the unlocked panels and in
   the API reference, and had no rule in this file — so it inherited the
   browser default, ignored the width of the screen, and ran 5px past the right
   edge of a phone. A two-column table of "label / value" does not need to
   scroll; it needs the value column to wrap. */
.rule-table { width: 100%; max-width: 100%; border-collapse: collapse; table-layout: fixed; }
.rule-table th, .rule-table td {
  text-align: left; vertical-align: top; padding: .55rem .7rem .55rem 0;
  font-weight: 400; overflow-wrap: anywhere;
}
.rule-table th { color: var(--ink-3); font-size: .92rem; width: 38%; }
.rule-table tr + tr th, .rule-table tr + tr td { box-shadow: inset 0 1px 0 var(--line); }
@media (max-width: 560px) {
  .rule-table, .rule-table tbody, .rule-table tr, .rule-table th, .rule-table td { display: block; width: auto; }
  .rule-table td { padding-top: .1rem; padding-bottom: .8rem; }
  .rule-table tr + tr th { box-shadow: none; border-top: 1px solid var(--line); padding-top: .8rem; }
  .rule-table tr + tr td { box-shadow: none; }
}

/* The safety net under both of the above.

   Every time a pair of buttons is written into a plain <p>, they end up 4px
   apart and nobody notices until it is on the screen in front of somebody who
   matters. A rule that only fires between two adjacent buttons costs nothing
   where the container already has a gap — `.btn-row > .btn` and
   `.hero__cta > .btn` zero it — and it catches the next one automatically. */
.btn + .btn { margin-left: .7rem; }
.btn-row > .btn + .btn, .hero__cta > .btn + .btn { margin-left: 0; }

.nav__account {
  font-size: .85rem;
  padding: .32rem .8rem;
  border: 1px solid var(--rule, rgba(0,0,0,.14));
  border-radius: 999px;
  color: var(--ink-2);
  white-space: nowrap;
}
.nav__account:hover { color: var(--ink); border-color: var(--ink-3); }

/* The account menu.

   Anchored to the chip rather than to the masthead, so it lands under the
   control that opened it at every width — including below 620px, where the bar
   wraps onto two rows and a menu positioned against the header would detach
   from its own button. */
.acct { position: relative; display: inline-flex; }
.acct__menu {
  position: absolute; top: calc(100% + .5rem); right: 0; z-index: 70;
  display: flex; flex-direction: column; min-width: 13.5rem;
  padding: .45rem; border-radius: 1rem;
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(var(--lg-blur-strong));
  -webkit-backdrop-filter: blur(var(--lg-blur-strong));
  box-shadow: 0 1px 2px rgba(29, 36, 48, .06), 0 12px 32px rgba(29, 36, 48, .13);
}
.acct__who {
  padding: .5rem .7rem .45rem; font-size: .78rem; color: var(--ink-4);
  overflow-wrap: anywhere; box-shadow: inset 0 -1px 0 var(--line);
  margin-bottom: .3rem;
}
.acct__menu a {
  padding: .5rem .7rem; border-radius: .6rem;
  font-size: .9rem; color: var(--ink-2); white-space: nowrap;
}
.acct__menu a:hover { background: rgba(18, 48, 58, .06); color: var(--ink); }
/* The way out is not a destructive action, but it is a different KIND of
   action from the two above it, and a reader scanning for it should not have
   to read all three. */
.acct__menu #acct-menu-signout {
  margin-top: .3rem; box-shadow: inset 0 1px 0 var(--line); border-radius: 0 0 .6rem .6rem;
}
.nav__account--on { border-color: var(--accent, #2f6f4f); color: var(--accent, #2f6f4f); }
.nav__account--off { border-color: var(--accent, #2f6f4f); background: var(--accent, #2f6f4f); color: #fff; }
@media (max-width: 720px) { .nav__account { padding: .25rem .6rem; font-size: .8rem; } }
@media (max-width: 1080px) { .nav--links { display: none; } }

/* On a phone the masthead did not fit and did not admit it.

   Measured at 390px: the bar ran to 411px, so the primary call to action —
   "Check what you're owed", the one thing every visitor is meant to press —
   hung off the right edge of the screen on every page of the site, and the
   page scrolled sideways. The row cannot shrink far enough to fit, so it stops
   pretending to be one row: the wordmark keeps the top line and the controls
   take the second, full width. The language menu goes; the footer carries the
   same one and a phone does not need it twice. */
@media (max-width: 620px) {
  .masthead__inner {
    flex-wrap: wrap;
    row-gap: .55rem;
    border-radius: 1.5rem;
    padding: .6rem .75rem;
  }
  .masthead__inner > .nav { flex: 1 1 100%; justify-content: space-between; gap: .5rem; }
  .masthead .lang-select { display: none; }
  .masthead .btn { flex: 0 1 auto; min-width: 0; }
  .masthead .btn, .masthead .nav__account { white-space: normal; }
}

.disclaimer-bar {
  background: rgba(255,255,255,.75);
  border-block: 1px solid var(--line);
  padding: .5rem 0; text-align: center; font-size: .8rem; color: var(--ink-3);
}

footer.site { padding: 3rem 0 2.5rem; margin-top: 2rem; border-top: 1px solid var(--line); }
footer.site ul { list-style: none; padding: 0; margin: .6rem 0 0; }
/* 14 inline footer anchors per page were 16px-tall targets 22.4px apart at
   390px. Padding on the anchor, not margin on the li, so the target itself
   grows rather than the space between targets. */
footer.site li { margin-bottom: .1rem; font-size: .9rem; }
footer.site a { display: inline-block; padding-block: .3rem; }
footer.site a { color: var(--ink-3); }
footer.site a:hover { color: var(--ink); opacity: 1; }

/* ------------------------------------------------------------------ */
/* Language switcher                                                   */
/* ------------------------------------------------------------------ */

.langbar { display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
.langbar a {
  border-radius: 9999px; padding: .25rem .7rem;
  font-size: .8rem; color: var(--ink-4);
  transition: color .2s ease, background .2s ease;
}
.langbar a:hover { color: var(--ink); background: #fff; opacity: 1; }
.langbar a[aria-current="true"] { color: var(--ink); background: #fff; }

.lang-select {
  appearance: none; border: none; cursor: pointer;
  background: #fff; color: var(--ink);
  border-radius: 9999px; padding: .4rem 2rem .4rem .9rem;
  font-family: var(--font-body); font-size: .85rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23fff' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .8rem center;
}
.lang-select option { background: #fff; color: var(--ink); }

/* ------------------------------------------------------------------ */
/* Deadline status                                                     */
/* ------------------------------------------------------------------ */

.status { display: inline-flex; align-items: center; gap: .4rem; font-size: .78rem;
          border-radius: 9999px; padding: .26rem .7rem; white-space: nowrap; }
.status::after { content: ''; width: 6px; height: 6px; border-radius: 50%; order: -1; }
.status--closing { color: #8c3413; background: rgba(180,73,31,.12); }
.status--closing::after { background: var(--terracotta); animation: pulse 2s ease-in-out infinite; }
.status--open    { color: var(--sage); background: rgba(46,125,91,.12); }
.status--open::after { background: var(--sage); }
.status--soon    { color: #7a5a05; background: rgba(227,178,60,.16); }
.status--soon::after { background: var(--gold); }
.status--later   { color: var(--ink-3); background: rgba(29,36,48,.06); }
.status--later::after { background: var(--ink-4); }
.status--stalled { color: var(--ink-4); background: rgba(29,36,48,.05); }
.status--stalled::after { background: var(--ink-4); }
.status--unknown { color: var(--ink-4); background: rgba(29,36,48,.04); }
.status--unknown::after { background: var(--ink-4); }
@keyframes pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .45; transform: scale(.8); } }

/* ------------------------------------------------------------------ */
/* Motion — entry only, staggered, never on scroll-up                  */
/* ------------------------------------------------------------------ */

.reveal { opacity: 0; filter: blur(10px); transform: translateY(20px);
          transition: opacity .8s ease-out, filter .8s ease-out, transform .8s ease-out; }
.reveal.in { opacity: 1; filter: blur(0); transform: none; }

.blur-word { display: inline-block; opacity: 0; filter: blur(12px); transform: translateY(14px);
             transition: opacity .7s ease-out, filter .7s ease-out, transform .7s ease-out; }
.blur-word.in { opacity: 1; filter: blur(0); transform: none; }

/* How-it-works: a line that draws itself as the steps arrive. */
.flow { position: relative; display: grid; gap: 1.1rem; }
.flow::before {
  content: ''; position: absolute; left: 1.35rem; top: 1.6rem; bottom: 1.6rem;
  width: 1px; background: linear-gradient(180deg, transparent, var(--line-2) 12%, var(--line-2) 88%, transparent);
  transform: scaleY(0); transform-origin: top; transition: transform 1.4s ease-out .2s;
}
.flow.in::before { transform: scaleY(1); }
.flow__step { display: flex; gap: 1.1rem; align-items: flex-start; }
.flow__dot {
  flex: 0 0 auto; width: 2.7rem; height: 2.7rem; border-radius: 9999px;
  display: grid; place-items: center; font-family: var(--font-heading); font-size: 1.05rem;
  background: #fff; box-shadow: inset 0 0 0 1px var(--line-2); position: relative; z-index: 1;
}
.flow__step.in .flow__dot { box-shadow: inset 0 0 0 1px rgba(255,255,255,.4); }

/* Counting figure for the hero. */
.tally { font-family: var(--font-heading); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------ */
/* Audience switch — individual vs company                             */
/*                                                                     */
/* Radio inputs and sibling selectors, not a click handler. Both panels */
/* are real HTML, so the page works with scripting off, both halves are */
/* indexed, and no price ever flashes wrong while a script boots.       */
/* ------------------------------------------------------------------ */

.audience__radio { position: absolute; opacity: 0; pointer-events: none; }

.audience__switch {
  display: inline-flex; gap: .25rem; margin-top: 2.4rem; padding: .28rem;
  border-radius: 9999px; position: relative; isolation: isolate;
  background: rgba(255, 255, 255, 0.6);
}
/* Same masked gradient ring as every other surface — never a border. */
.audience__switch::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(140deg, rgba(15,111,118,.26), rgba(15,111,118,.08) 42%, rgba(15,111,118,.18));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}

.audience__tab {
  padding: .62rem 1.35rem; border-radius: 9999px; cursor: pointer;
  font-size: .92rem; letter-spacing: .01em; color: var(--ink-4);
  transition: color .3s ease, background .3s ease;
  white-space: nowrap;
}
.audience__tab:hover { color: var(--ink); }
/* The unselected half of a two-state control is a control, not a label: it
   needs a boundary the eye can find. 1.4.11 asks 3:1 for the shape itself,
   which a colour-only distinction between the two halves does not give. */
.audience__tab { box-shadow: inset 0 0 0 1px rgba(18,48,58,.42); }
/* The selected tab reads as the active surface.

   Matched through `~ *` as well as `~`, because the switch now lives inside
   the hero block rather than as a bare sibling of the radios. The earlier
   sibling-only selector silently stopped matching the moment the switch was
   nested, which looks exactly like "the toggle does nothing". */
#aud-me:checked ~ .audience__switch label[for="aud-me"],
#aud-ent:checked ~ .audience__switch label[for="aud-ent"],
#aud-biz:checked ~ .audience__switch label[for="aud-biz"],
#acct-me:checked ~ .audience__switch label[for="acct-me"],
#acct-biz:checked ~ .audience__switch label[for="acct-biz"],
#aud-me:checked ~ * .audience__switch label[for="aud-me"],
#aud-ent:checked ~ * .audience__switch label[for="aud-ent"],
#aud-biz:checked ~ * .audience__switch label[for="aud-biz"],
#acct-me:checked ~ * .audience__switch label[for="acct-me"],
#acct-biz:checked ~ * .audience__switch label[for="acct-biz"] {
  /* The ring belongs to the unselected half only: the selected one is a
     filled teal surface and already has all the edge it needs. */
  background: var(--teal); color: #fff; box-shadow: none;
}

/* Keyboard focus has to land somewhere visible, since the input itself is
   off-screen — without this the switch is unusable without a mouse. */
#aud-me:focus-visible ~ .audience__switch label[for="aud-me"],
#aud-ent:focus-visible ~ .audience__switch label[for="aud-ent"],
#aud-biz:focus-visible ~ .audience__switch label[for="aud-biz"],
#acct-me:focus-visible ~ .audience__switch label[for="acct-me"],
#acct-biz:focus-visible ~ .audience__switch label[for="acct-biz"],
#aud-me:focus-visible ~ * .audience__switch label[for="aud-me"],
#aud-ent:focus-visible ~ * .audience__switch label[for="aud-ent"],
#aud-biz:focus-visible ~ * .audience__switch label[for="aud-biz"],
#acct-me:focus-visible ~ * .audience__switch label[for="acct-me"],
#acct-biz:focus-visible ~ * .audience__switch label[for="acct-biz"] {
  outline: 2px solid var(--focus); outline-offset: 3px;
}

/* The hero placement: centred under the lede, and the thing you notice next
   after the headline rather than a tab strip further down the page. */
.audience__switch--hero { margin: 1.8rem auto 0; }

.audience__panel { display: none; }
#aud-me:checked  ~ .audience__panel--me,
#aud-biz:checked ~ .audience__panel--biz,
#aud-ent:checked ~ .audience__panel--ent,
#aud-me:checked  ~ * .audience__panel--me,
#aud-biz:checked ~ * .audience__panel--biz,
#aud-ent:checked ~ * .audience__panel--ent,
#acct-me:checked  ~ .audience__panel--me,
#acct-biz:checked ~ .audience__panel--biz { display: block; }

@media (max-width: 560px) {
  .audience__switch { display: flex; width: 100%; }
  .audience__tab { flex: 1; text-align: center; padding: .62rem .5rem; font-size: .84rem; }
}

/* Printing a page whose alternative half is hidden loses half the pricing. */
@media print {
  .audience__panel { display: block !important; }
  .audience__switch { display: none; }
}

/* ------------------------------------------------------------------ */
/* Form fields on the marketing site                                   */
/*                                                                     */
/* .field is defined in the app stylesheet, which the site does not     */
/* load — so the sign-in input rendered as a browser-default white box  */
/* on a black page. Restated here rather than importing the app CSS,    */
/* which would drag the whole app layer onto every marketing page.     */
/* ------------------------------------------------------------------ */

/* Scoped to real form controls: the wizard also uses .field on the DIV that
   wraps a label and its input, and styling that as an input box produced a
   box-inside-a-box with the label sitting inside the field. */
input.field, select.field, textarea.field {
  width: 100%; min-height: 50px; border-radius: .9rem; padding: .8rem 1rem;
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  background: #fff;
  border: 0; box-shadow: inset 0 0 0 1px var(--line-2);
  transition: box-shadow .25s ease, background .25s ease;
  -webkit-appearance: none; appearance: none;
}

/* The wrapper form: a label above its control. */
.field:not(input):not(select):not(textarea) {
  display: block; margin-bottom: 1.4rem;
}
.field > label {
  display: block; margin-bottom: .4rem;
  font-size: .82rem; letter-spacing: .04em; color: var(--ink-3);
}
.field > input, .field > select, .field > textarea {
  width: 100%; min-height: 50px; border-radius: .9rem; padding: .8rem 1rem;
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  background: #fff;
  border: 0; box-shadow: inset 0 0 0 1px var(--line-2);
  -webkit-appearance: none; appearance: none;
}
.field > input::placeholder { color: rgba(29,36,48,.38); }
.field > input:focus, .field > select:focus {
  outline: none; background: #fff;
  box-shadow: inset 0 0 0 2px rgba(29,36,48,.5);
}
input.field::placeholder, textarea.field::placeholder { color: rgba(29, 36, 48, 0.38); }
input.field:focus, select.field:focus, textarea.field:focus {
  outline: none;
  background: #fff;
  box-shadow: inset 0 0 0 2px rgba(29, 36, 48, 0.5);
}
/* Chrome paints a blue-white block over autofilled inputs; the inset
   shadow is the only way to repaint it without !important on colour. */
input.field:-webkit-autofill,
input.field:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: inset 0 0 0 1px var(--line-2), inset 0 0 0 100px #fff;
  caret-color: var(--ink);
}
select.field option, .field select option { background: #fff; }

/* An honest failure state, styled like the rest of the system. */
.notice {
  border-radius: .9rem; padding: .85rem 1rem; margin-top: 1rem;
  font-size: .92rem; line-height: 1.55;
  background: #fff;
  box-shadow: inset 0 0 0 1px var(--line-2);
}
/* `.notice` was dead — nothing in src/ carried the class — and /account/ drew
   "Could not send the code." as a `p.small` in --ink-3, typographically
   identical to the marketing sentence beside it. A failure the reader has to
   notice cannot be styled like prose. Terracotta family, no new hues. */
.notice--error {
  background: rgba(176,88,56,.09);
  color: #8c3413;
  box-shadow: inset 0 0 0 1px rgba(176,88,56,.35);
}
.notice--error a { color: #8c3413; text-decoration: underline; }
.notice:focus-visible { outline: 2px solid var(--terracotta); outline-offset: 3px; }

@media print {
  body::before, body::after { display: none; }
  body { background: #fff; color: #111; }
  .masthead, footer.site, .btn { display: none; }
  /* The programme sidebar is three buttons and a summary card; the buttons go
     with the rule above, and `.no-print` is what the markup asks for on the
     rest. It was in the HTML of 2,216 pages with no rule behind it. */
  .no-print { display: none !important; }
}

/* ==================================================================== */
/* The eligibility check                                                */
/*                                                                      */
/* These classes had no rules at all. The wizard was rendering with      */
/* default browser buttons on a dark page — which is why the country     */
/* picker read as "Australia83 programmes" with the label and the count  */
/* jammed together, and why filling the form felt like filling a form    */
/* from 1998. Everything here is layout and spacing in the existing      */
/* glass language; no new colours, no new components.                    */
/* ==================================================================== */

.wizard { max-width: 62rem; margin-inline: auto; }

/* This rule was written for the step counter. The counter is emitted as
   `<p class="tiny progress-caption">`, and `.step` only ever matched each
   wizard view's WRAPPER div — so every question screen, every field label and
   every .q-why in both wizards rendered at 13.12px in 68% ink, and .q-why's
   62ch max-width resolved to 452px under a 992px headline. The wrapper is
   `.wizard-step` now and this points where it always meant to. */
.progress-caption {
  font-size: .82rem; letter-spacing: .04em; color: var(--ink-4);
}

.wizard-step { margin-bottom: 1.6rem; }

/* .q is the question HEADING in this wizard, not a wrapper. */
h1.q, h2.q { margin: 0 0 .45rem; }
div.q { margin-bottom: 2.4rem; }
.q-why, .hint { color: var(--ink-3); margin: 0 0 1.6rem; max-width: 62ch; }
/* Body copy under a question, not fine print. */
.q-why { font-size: 1.05rem; color: var(--ink-2); max-width: 58ch; }

/* A value the wizard silently rewrote. Type 99 into household size and you get
   20 — and the sentence explaining that was styled identically to the static
   help text beside it, so the correction read as instructions. */
.hint--corrected { color: #8c3413; font-weight: 500; }
.hint--corrected::before { content: '⚠ '; }
.field input[aria-invalid='true'] { box-shadow: inset 0 0 0 2px var(--terracotta); }

/* --- options ------------------------------------------------------- */

.opts {
  display: grid; gap: .75rem; margin: 0 0 1.6rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
/* A short list of two- or three-word answers should not be forced into a
   15rem grid — it leaves a shelf of empty space beside each one. */
.opts--tight { grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); }

.opt {
  position: relative; isolation: isolate;
  display: flex; align-items: center; gap: .7rem;
  width: 100%; min-height: 60px;
  padding: .85rem 1.05rem;
  border: 0; border-radius: .95rem;
  background: rgba(255, 255, 255, 0.75);
  color: var(--ink); font-family: var(--font-body); font-size: 1rem;
  text-align: left; cursor: pointer;
  transition: background .22s ease, transform .22s ease;
}
.opt::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(140deg, rgba(15,111,118,.22), rgba(15,111,118,.07) 45%, rgba(15,111,118,.18));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}
.opt:hover { background: #fff; }
/* No local colour: the global :focus-visible rule is the one voice. */
.opt:focus-visible { outline-offset: 3px; }
.opt[aria-pressed="true"] { background: var(--teal); color: #fff; }
.opt[aria-pressed="true"]::before { opacity: 0; }

/* A tile that commits your answer and moves you on, as distinct from one that
   only ticks. Four behaviours shared one appearance; this is the one that
   navigates. */
.opt--go::after {
  content: '›'; margin-left: auto; padding-left: .6rem;
  font-size: 1.3rem; line-height: 1; color: var(--ink-4);
}
.opt--go[aria-pressed="true"]::after { color: #fff; }

/* The label and its count were running together as one string. */
.opt > span { display: flex; flex-direction: column; gap: .18rem; min-width: 0; }
.opt__sub {
  display: block; font-size: .8rem; line-height: 1.35;
  color: var(--ink-4); font-variant-numeric: tabular-nums;
}
.opt[aria-pressed="true"] .opt__sub { color: var(--ink-3); }

/* The two-letter country code, set apart rather than glued to the name. */
.opt__key {
  flex: 0 0 auto; display: grid; place-items: center;
  min-width: 2.1rem; height: 1.55rem; padding: 0 .4rem;
  border-radius: .42rem; font-size: .74rem; letter-spacing: .06em;
  background: rgba(29, 36, 48, 0.08); color: var(--ink-3);
}
.opt[aria-pressed="true"] .opt__key { background: rgba(255,255,255,.18); color: var(--ink-3); }

/* --- inputs and nav ------------------------------------------------ */

.field-row { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); margin-bottom: 1.4rem; }

.wizard-nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-top: 2.4rem; padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  flex-wrap: wrap;
}
.wizard-nav .row { display: flex; gap: .6rem; align-items: center; }

.progress-rail { display: flex; gap: .35rem; margin-bottom: 2rem; }
/* The segments are `<span>`s.
   These three rules were written against `.progress-rail i`, which nothing in
   src/app.js or src/pwa/startup-check.js has ever emitted, so the rail
   measured 0px tall on every step of a six-screen income form: the .done and
   .current classes were computed correctly and painted nothing. Matched on
   the child rather than the tag so the next markup change cannot silently
   unstyle it again. */
.progress-rail > * {
  flex: 1; height: 3px; border-radius: 2px; background: rgba(29,36,48,.12);
  transition: background .3s ease;
}
.progress-rail > *.done { background: rgba(15,111,118,.45); }
.progress-rail > *.current { background: var(--teal); }

/* --- results ------------------------------------------------------- */

.result-hero { margin-bottom: 2.6rem; }
.figure { font-family: var(--font-heading); font-size: clamp(2.6rem, 9vw, 5.5rem);
          line-height: 1.02; margin: .6rem 0 0; }
.figure-unit { color: var(--ink-3); max-width: 46ch; }

/* The hero's one sentence. It used to inherit .figure-unit's 46ch, which is
   the right measure for the two words under the number and the wrong one for
   the claim — and an inline max-width:60ch on the deleted paragraph fought it
   for the same space. One rule, one owner. */
.hero-claim { max-width: 36rem; color: var(--ink-2); font-size: 1.02rem;
              line-height: 1.55; margin: 1rem 0 0; }

/* --- the hero and the map of the page, side by side ----------------- *
 *
 * Measured before this existed: `.results` is 1180px wide at BOTH 1536 and
 * 1280 (the container is capped, so it is one problem at two widths), the
 * widest painted run in the hero was 748px, and the remaining 432px — 37% of
 * the container — was empty, while the reader's only map of a 14,800px page
 * was nowhere.
 *
 * The index is a SIBLING of .result-hero, not a child. Nesting it inside the
 * hero makes it part of the hero's measured height, which at 390px adds
 * ~264px to the one box this round exists to shrink. */
/* Below 1100px this is one column and the index follows the hero — stated
   rather than left to an empty rule, so it is clear that the single column is
   a decision. */
.result-top { display: block; }
@media (min-width: 1100px) {
  .result-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 22rem);
    column-gap: 3rem;
    align-items: start;
  }
}
/* The index is a <ul> for screen readers, not for the bullets. Without this
   reset every row paints a disc in the gutter and the column reads as a
   bulleted list of links rather than a table of contents with counts. */
.result-index ul { list-style: none; margin: 0; padding: 0; }
.result-index li { margin: 0; }
.result-index a {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: .55rem 0; border-bottom: 1px solid var(--line-2);
  /* 44px because this is the only navigation on the screen and it is used on
     a phone with a thumb. */
  min-height: 44px; align-items: center; text-decoration: none;
}
.result-index b { font-variant-numeric: tabular-nums; }
.result-index__total { display: block; padding: .55rem 0; color: var(--ink-4); font-size: .86rem; }

.bucket { margin: 2.6rem 0; }
.bucket__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: .9rem;
}
.bucket__head h2 { margin: 0; }
.bucket__count { font-size: .85rem; color: var(--ink-4); }
.bucket-list { display: grid; gap: .8rem; }
/* Two columns for the cards that carry money, three for the ones that carry a
   question. 1000px rather than 900: at a 900px viewport `.results` measures
   860px, and two 415px columns for a card with three bullets and three chips
   is tighter than the layout wants. */
@media (min-width: 1000px) {
  .bucket-list--lead { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}
@media (min-width: 1100px) {
  .bucket-list--ask { grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: start; }
}
@media (min-width: 700px) and (max-width: 1099px) {
  .bucket-list--ask { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}

.prog-card { position: relative; isolation: isolate; border-radius: 1rem; padding: 1.1rem 1.2rem;
             background: rgba(255,255,255,.7); }
.prog-card::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(140deg, rgba(15,111,118,.26), rgba(15,111,118,.08) 45%, rgba(29,36,48,.14));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}
.prog-card__head { display: flex; justify-content: space-between; column-gap: 1rem; gap: 1rem; align-items: flex-start; flex-wrap: wrap; }
/* The eye is meant to land on the name of the thing you can claim. It landed
   on the amount: 1.35rem in the heading face against 1.08rem of body face for
   the programme name, on every one of seventy-nine identically weighted
   boxes. */
.prog-card__title { margin: 0; font-size: 1.15rem; font-weight: 600; color: var(--ink); }
/* A link is a target for a finger as well as a pointer; 19px was under the
   24px minimum. */
.prog-card__title a { min-height: 24px; display: inline-block; }
.prog-card__sub, .prog-card__meta { font-size: .84rem; color: var(--ink-4); margin: .25rem 0 0; }
.prog-card__amount { text-align: right; white-space: nowrap; }
.prog-card__value { font-family: var(--font-heading); font-size: 1.05rem; color: var(--ink-2); flex: 0 0 auto; }

/* A card that says one thing and asks one question, dressed as one. The 20
   conditional cards carried 94 chips and 58 buttons across 5,933px for one
   sentence of content each. */
/* "Only if this is you" and "one answer away", gathered under the thing they
   have in common instead of repeated on every card. The head is a label for a
   group of one-line cards, so it sits between a section heading and body
   text — bigger than the cards it introduces, smaller than the section. */
.ask-group { margin-top: 1.4rem; }
.ask-group__head {
  margin: 0; font-family: var(--font-heading); font-size: 1.02rem;
  font-weight: 600; color: var(--ink-2);
}

.prog-card--ask { padding: .85rem 1rem; }
.prog-card--ask .prog-card__ask { margin: .45rem 0 .6rem; font-size: .9rem; color: var(--ink-2); }

.why { margin: .8rem 0 0; padding: .7rem .9rem; border-radius: .7rem; font-size: .87rem;
       background: rgba(29,36,48,.04); color: var(--ink-3); }
.why--fail { color: #8c3413; background: rgba(180,73,31,.09); }
.why--ask  { color: #1f5a41; background: rgba(46,125,91,.1); }
.q-why { margin-top: .5rem; }

.fold { margin-top: 2.6rem; }
.fold > summary { cursor: pointer; color: var(--ink-3); padding: .6rem 0; }

.locked-bucket { margin: 2.6rem 0; padding: 1.4rem; border-radius: 1.1rem;
                 position: relative; isolation: isolate; background: rgba(255,255,255,.72); }
.locked-bucket::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(140deg, rgba(15,111,118,.26), rgba(15,111,118,.08) 45%, rgba(29,36,48,.14));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}
.locked-bucket .btn { margin-right: .5rem; }

.locked__rows { display: grid; gap: .5rem; margin: 1.1rem 0 1.3rem; }

/* A withheld record, not a loading skeleton.

   `.locked__row` used to be a shimmer gradient bar with a descending
   nth-child opacity ramp (.72 / .48 / .26). That is the universal signifier
   for "content is on its way" — a fade-out reads as still arriving — and it
   sat next to `.lock-chip`, which draws ●●●● in the slot a real amount
   occupies and is the correct idiom. On one load of /us/ the reader met 18 of
   one and 29 of the other. One pattern now: the shape of the record is shown
   — a blurred name bar on the left, the redacted amount right-aligned in the
   same column real amounts use — and every row is at full strength, because
   nothing here is loading. */
.locked__row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; height: 3.4rem; padding: 0 1rem; border-radius: .8rem;
  background: rgba(18,48,58,.055);
  box-shadow: inset 0 0 0 1px rgba(18,48,58,.09);
}
.locked__row::before {
  content: ''; display: block; height: .7rem; width: min(58%, 16rem);
  border-radius: 999px; background: rgba(18,48,58,.16);
  /* Blur, not fade: the name is present and obscured, which is what a
     redaction looks like. */
  filter: blur(2.5px);
}
.locked__row::after {
  content: '●●●●'; letter-spacing: .18em; font-size: .8rem;
  font-family: var(--font-body); color: var(--ink-3); white-space: nowrap;
}
/* The first row says the word out loud, once, so the pattern is taught
   rather than guessed at. */
.locked__row:first-child { position: relative; }
/* A withheld row that DOES carry a name: the free-tier conversion feature
   keeps the programme's name, funder and public-page link on every locked
   row now (only the application link, documents, procedure and source quote
   are sold) — so the CSS-drawn "shape of the record" bar and dots are
   switched off in favour of the real link, reusing .list-row__name and
   .lock-chip exactly as the static list rows do (src/ui.mjs's listRow()).
   One voice for "this is withheld", named or not. */
.locked__row--named { text-decoration: none; }
.locked__row--named::before, .locked__row--named::after { content: none; }
.locked__row--named .list-row__name { font-size: 1rem; }
.locked__row--named:hover { background: rgba(18,48,58,.09); }
.locked__row__lock {
  position: absolute; left: 1rem; top: -.62rem;
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .1rem .5rem; border-radius: 999px;
  background: var(--paper-2); box-shadow: inset 0 0 0 1px rgba(18,48,58,.14);
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-3);
}
.locked__row__lock svg { width: 10px; height: 10px; }

/* The marker every withheld value carries — the redacted rows above and the
   ●●●● amount chip in a list row. One class so a test can enumerate them and
   confirm the site speaks about withheld content in exactly one voice. A
   redaction is not selectable text; saying so is the honest declaration. */
.withheld { user-select: none; -webkit-user-select: none; }

/* The control for a secondary locked panel lives in its heading row.
   `.bucket__head` is already `display:flex; justify-content:space-between`,
   so this needs no layout of its own. */
.bucket__unlock {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: .9rem; color: var(--teal); white-space: nowrap;
}
.bucket__unlock svg { width: 14px; height: 14px; transition: transform .2s ease; }
.bucket__unlock:hover svg { transform: translateX(2px); }

@media (max-width: 640px) {
  .opts { grid-template-columns: 1fr; }
  .wizard-nav { flex-direction: column-reverse; align-items: stretch; }
  .wizard-nav .row { justify-content: space-between; }
}

/* The locked amount on a browse row: reads as a value that exists and is
   withheld, rather than as missing data. */
.lock-chip {
  letter-spacing: .18em; color: var(--ink-3);
  font-family: var(--font-body); font-size: .8rem;
}

/* ==================================================================== */
/* Hero: centred, with the rotating orbit behind it                     */
/* ==================================================================== */

.hero-centre { text-align: center; }
/* The headline must sit above the graphic, not inside it. */
.hero-centre .shell > *:not(.orbit) { position: relative; z-index: 1; }
.hero-centre .eyebrow { display: block; }

.orbit {
  position: absolute; inset: 0; margin: auto;
  width: min(52rem, 96vw); height: min(52rem, 96vw);
  top: -8%; z-index: -1; pointer-events: none;
  /* Low enough that the headline is never competing with it. This started at
     .85 and the marks read as foreground objects sitting on the words, then
     .55, where the currency chips still crossed the eyebrow line and the stat
     row and made both harder to read than a decoration is ever worth. */
  opacity: .38;
  /* And cleared out of the middle entirely. z-index put the text in front, but
     "in front of a busy graphic" is not the same as legible — the chips run
     straight through the words. The mask fades the ring out across the column
     the copy occupies, so the decoration lives in the margins where it belongs
     and the centre is plain background. */
  -webkit-mask-image: radial-gradient(ellipse 44% 62% at 50% 48%, transparent 0%, transparent 68%, #000 100%);
  mask-image: radial-gradient(ellipse 44% 62% at 50% 48%, transparent 0%, transparent 68%, #000 100%);
}
.orbit svg { width: 100%; height: 100%; overflow: visible; }

.orbit__ring {
  fill: none; stroke: url(#orbitStroke); stroke-width: 1.4;
  stroke-dasharray: 3 10; transform-origin: 160px 160px;
}
/* Three speeds, two directions — a single rotating ring reads as a loading
   spinner, which is the last thing a landing page should suggest. */
.orbit__ring--slow { animation: spin 68s linear infinite; }
.orbit__ring--mid  { animation: spin 44s linear infinite reverse; stroke-dasharray: 2 14; }
.orbit__ring--fast { animation: spin 30s linear infinite; stroke-dasharray: 1 9; }

.orbit__marks { transform-origin: 160px 160px; }
.orbit__chip {
  fill: rgba(255,255,255,.34);
  stroke: rgba(15,111,118,.13); stroke-width: 1;
}
.orbit__mark {
  font-family: var(--font-body); font-size: 12px; font-weight: 700;
  fill: rgba(15,111,118,.38);
}


@keyframes spin { to { transform: rotate(360deg); } }

@media (max-width: 720px) { .orbit { opacity: .2; top: -1%; } }

/* ==================================================================== */
/* Sections in boxes, with a slow float                                 */
/* ==================================================================== */

.panel {
  position: relative; isolation: isolate;
  border-radius: 1.5rem; padding: clamp(1.6rem, 3.5vw, 3rem);
  margin: clamp(1.4rem, 3vw, 2.4rem) 0;
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(6px);
}
.panel::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(140deg, rgba(15,111,118,.26), rgba(15,111,118,.07) 45%, rgba(31,95,168,.2));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}

/* The float is deliberately tiny and slow. Anything faster than ~11s or
   further than ~7px stops reading as "floating" and starts reading as
   "drifting", which makes text harder to track while reading. */
@media (prefers-reduced-motion: no-preference) {
  .panel--float { animation: float 13s ease-in-out infinite; }
  .panel--float:nth-of-type(even) { animation-duration: 16s; animation-delay: -4s; }
}
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}

/* ==================================================================== */
/* The four steps                                                       */
/* ==================================================================== */

.steps4 { display: grid; gap: 1rem; margin-top: 1.8rem;
          grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); position: relative; }

.step4 {
  position: relative; isolation: isolate;
  border-radius: 1.15rem; padding: 1.4rem 1.3rem 1.5rem;
  background: rgba(255,255,255,.66);
  opacity: 0; transform: translateY(22px);
  transition: opacity .7s ease-out, transform .7s ease-out;
}
.step4::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(140deg, rgba(15,111,118,.24), rgba(15,111,118,.06) 45%, rgba(31,122,82,.2));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}
.steps4.in .step4 { opacity: 1; transform: none; }
/* Staggered so the eye is walked 1 → 4 rather than shown four things at once. */
.steps4.in .step4:nth-child(1) { transition-delay: .05s; }
.steps4.in .step4:nth-child(2) { transition-delay: .22s; }
.steps4.in .step4:nth-child(3) { transition-delay: .39s; }
.steps4.in .step4:nth-child(4) { transition-delay: .56s; }

.step4__n {
  display: grid; place-items: center;
  width: 2.6rem; height: 2.6rem; border-radius: 9999px;
  font-family: var(--font-heading); font-size: 1.15rem;
  color: #fff; background: var(--teal);
  margin-bottom: .9rem;
  transform: scale(.6); opacity: 0;
  transition: transform .55s cubic-bezier(.2,.9,.3,1.2), opacity .45s ease-out;
}
.steps4.in .step4 .step4__n { transform: scale(1); opacity: 1; }
.steps4.in .step4:nth-child(2) .step4__n { transition-delay: .3s; }
.steps4.in .step4:nth-child(3) .step4__n { transition-delay: .47s; }
.steps4.in .step4:nth-child(4) .step4__n { transition-delay: .64s; }

.step4 h3 { margin: 0 0 .35rem; font-size: 1.12rem; }
.step4 p  { margin: 0; font-size: .93rem; color: var(--ink-3); }

@media (prefers-reduced-motion: reduce) {
  .step4, .step4__n { opacity: 1; transform: none; }
}

/* Numbered job rows on /enterprise/ — a grants team buys a way through
   find → apply → manage → report, so the page is arranged that way and the
   number is the thing the eye lands on first. */
.jobrow { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 1.2rem; align-items: start; }
.jobrow__n {
  font-family: var(--font-heading);
  font-size: 2.2rem;
  line-height: 1;
  color: var(--teal);
  opacity: .35;
}
@media (max-width: 620px) {
  .jobrow { grid-template-columns: 1fr; gap: .4rem; }
  .jobrow__n { font-size: 1.6rem; }
}

/* An inline lock, for the remainder of a list whose first two rows are shown.
   Flatter than .locked-bucket because it sits directly under real rows and
   should read as the same list continuing, not as a separate advert. */
.locked-bucket--inline {
  margin-top: 0;
  border-top: 0;
  border-radius: 0 0 12px 12px;
  padding-top: .8rem;
}


/* Feature lists on the pricing cards. A tick and a cross rather than two
   bullet lists, because "what you do not get" is the half people scan for and
   an unmarked list buries it. */
.ticks { list-style: none; margin: 1.2rem 0 0; padding: 0; }
.ticks li {
  position: relative; padding-left: 1.5rem; margin-bottom: .45rem;
  font-size: .9rem; line-height: 1.45;
}
.ticks li::before {
  content: '✓'; position: absolute; left: 0; top: -.05em;
  color: var(--green); font-weight: 700;
}
.ticks--no { margin-top: .9rem; padding-top: .8rem; border-top: 1px solid var(--line); }
.ticks--no li { color: var(--ink-4); }
.ticks--no li::before { content: '×'; color: var(--terracotta); }

/* The recommended card, lifted just enough to be the default read. */
.card--featured { outline: 2px solid rgba(15,111,118,.35); outline-offset: -1px; }

/* ------------------------------------------------------------------ */
/* Audience — one switch, whole site                                   */
/* ------------------------------------------------------------------ */

/* `.aud-me` and `.aud-biz` mark the two halves of every page that differs.
   The default, and the state with JavaScript disabled or a cookie blocked, is
   the individual site: it is the one a stranger arriving from a search result
   almost always wants, and it is the half that works without an account.

   Both halves are in the HTML. That is deliberate — it means the switch is
   instant, both are indexable, and there is nothing to fetch. The cost is page
   weight, which for two navs and two headlines is nothing. */
/* Only ever hide. Never say anything about the half that is showing.

   This is a rule, not a preference. It used to read
   `html[data-audience='biz'] .aud-biz { display: revert }`, and `revert` means
   "go back to the user-agent default for this tag" — not "go back to what my
   own stylesheet said". So every shown enterprise element lost its own
   display: the nav <span> that is `display:flex` with a 1.3rem gap fell back
   to `inline`, and its six links closed up to a 4px word space and read as one
   run-on sentence; panels laid out as grids fell back to inline and pushed
   their text sideways. None of it showed in the individual view, because that
   half is never the one being reverted.

   Hiding only, and the shown half is simply not selected, so whatever the rest
   of this file gave it survives untouched. Printing wants both halves — a
   printed pricing sheet that silently omits the enterprise tier is worse than
   one that shows both — and because the only rules here are hiding rules,
   scoping them to `screen` is all that takes. */
@media screen {
  html:not([data-audience='biz']) .aud-biz { display: none; }
  html[data-audience='biz']       .aud-me  { display: none; }
}

/* The hero switch. Same pill as the pricing tabs, but these are real buttons
   rather than labels over hidden radios, because this one has to persist a
   cookie and tell the rest of the page — which a radio cannot do on its own. */
.audswitch {
  display: inline-flex; gap: .25rem; padding: .28rem; margin: 1.8rem auto 0;
  border-radius: 9999px; position: relative; isolation: isolate;
  background: rgba(255, 255, 255, 0.6);
}
.audswitch::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(140deg, rgba(15,111,118,.26), rgba(15,111,118,.08) 42%, rgba(15,111,118,.18));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}
.audswitch__tab {
  padding: .62rem 1.35rem; border-radius: 9999px; cursor: pointer; border: 0;
  background: transparent; font-family: var(--font-body); font-size: .92rem;
  color: var(--ink-4); white-space: nowrap;
  transition: color .3s ease, background .3s ease;
}
.audswitch__tab:hover { color: var(--ink); }
/* Same reasoning as .audience__tab: the unselected half must have a visible
   edge of its own, or it reads as static text next to a button. */
.audswitch__tab { box-shadow: inset 0 0 0 1px rgba(18,48,58,.42); }
.audswitch__tab[aria-selected='true'] { background: var(--teal); color: #fff; box-shadow: none; }
.audswitch__tab:focus-visible { outline-offset: 3px; }

@media (max-width: 560px) {
  .audswitch { display: flex; width: 100%; }
  .audswitch__tab { flex: 1; text-align: center; padding: .62rem .5rem; font-size: .84rem; }
}

/* The authenticator setup key, on /admin/.
 *
 * A bare <code> inherits the page's body size and the browser's monospace,
 * which puts a 32-character key at a size somebody has to squint at while
 * typing it into a phone. It is typed exactly once and getting a character
 * wrong means starting over. */
.admin-key {
  display: block;
  font: 500 1.05rem/1.7 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: .08em;
  word-break: break-all;
  color: var(--ink);
  background: rgba(255, 255, 255, .7);
  border: 1px solid rgba(15, 111, 118, .2);
  border-radius: 10px;
  padding: .7rem .9rem;
  margin: 0 0 .9rem;
  user-select: all;
}

/* Programme names under a locked list: plain links, so they stay crawlable. */
.tease-names { margin: .8rem 0 0; padding: 0; list-style: none; columns: 2 16rem; column-gap: 1.5rem; }
.tease-names li { margin: 0 0 .3rem; break-inside: avoid; }
/* Provenance line on every programme page: "Last checked against the official
   page on …" (green) or "Not yet re-checked …" (terracotta, bold). The two must
   not look alike. Both colours are the existing AA-contrast tokens. */
.prov-line { color: var(--ink-2); }
.prov-line--checked { color: var(--green); }
.prov-line--stale { color: var(--terracotta); }
