/* =============================================================================
   TravelPdfPro — THE WHOLE MARKETING SITE, ONE STYLESHEET
   =============================================================================
   Eight pages share this file: the umbrella landing at `/`, the Visa PDF
   Compressor's landing at `/tools/visa-pdf-compressor/`, `/privacy`, `/terms`,
   `/contact`, the `/guides/` index and the two guide articles under
   `/guides/<topic>/` (T3). It was
   named "the landing page" when there was one of them; T2b (D11) made the site a
   suite, and the header said so a build too late — the brand-name check in
   build-site.js caught this comment, which is the smallest possible
   demonstration of why that check exists.

   Lifted from docs/visapdfpro-landing-mockup.html, which is the locked pixel
   reference (Blueprint §3) — the filename keeps the old name because it is an
   internal identifier, and those deliberately did not move in the rename.
   Everything here that is not in that file is marked ADDED and says why.

   WHY THIS DOES NOT IMPORT src/styles/tokens.css
   It is the same palette, deliberately duplicated, and that is the point of the
   Phase 7 structure: the marketing site is a static artefact with no build step
   and no dependency on the app, and the app never serves marketing content. Two
   worlds, one deploy. A shared stylesheet would put the landing page's rendering
   behind webpack and make `/` depend on the thing `/` exists to compensate for.

   The values below and tokens.css both come from the same locked mockups, so
   they cannot disagree about what navy is — but if one ever moves, BOTH change.
   ============================================================================= */

/* =============================================================================
   FONTS — SELF-HOSTED                                       (ADDED, Phase 7 T2)
   =============================================================================
   These used to come from fonts.googleapis.com. On a page whose headline is
   "Your documents never leave your device", a third-party font request is a
   contradiction the visitor cannot see: it hands their IP address and user-agent
   to Google before a word of the privacy copy has been read, and it would have
   had to be disclosed in the very policy this task writes. Self-hosting deletes
   the disclosure and the request together — the marketing site now makes NO
   third-party network call of any kind.

   ⚠ THE APP AT /app STILL LOADS THESE FROM GOOGLE. `public/index.html` has its
   own <link> to fonts.googleapis.com, it is out of this task's scope (site/ and
   scripts/ only), and so /privacy discloses it — narrowed to the app, which is
   where it is still true. Fixing that is a two-line change plus a copy of this
   folder into public/; until then the disclosure is the honest half of this.

   WHERE THE FILES CAME FROM — reproduce with:
     curl -A "<a modern browser UA>" \
       "https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=IBM+Plex+Mono:wght@500;600&display=swap"
   and fetch the woff2 URLs it names. The UA matters: Google serves woff2 only to
   browsers that say they can read it, and ancient formats to anything else.

   INTER IS THE VARIABLE FONT — one file covers weights 100–900, which is why six
   static weights (400/500/600/700/800/900) became one 48 KB request. IBM Plex
   Mono has no variable build on Google Fonts, so its two weights are two files.
   Both are SIL OFL 1.1; the licence text ships beside the files in this folder,
   which is what that licence asks for.

   latin AND latin-ext are both here, and only the ones a page actually needs are
   ever downloaded — that is what `unicode-range` is for. An English page pulls
   inter-var-latin + the two ibm-plex-mono-*-latin files, ~78 KB, and never asks
   for the -ext files at all. They cost repository bytes, not page weight, and
   T3's articles are the reason to keep them.

   `font-display: swap` is Google's own default for these and is kept: text
   renders immediately in the fallback and re-renders when the font lands. The
   alternative, `block`, hides the hero headline for up to 3 seconds.

   NOT PRELOADED, deliberately. A <link rel="preload"> would shave a round trip
   off the first paint and would put these file paths in all four HTML files,
   where a typo is a silent no-op that only shows up in a console warning. One
   copy of the path, in the stylesheet that uses it.
   ============================================================================= */

/* Inter — variable, 100–900 */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/inter-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/inter-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono — 500 and 600, the only two weights this site uses */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/ibm-plex-mono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/ibm-plex-mono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --navy: #0f2557;
  --navy-deep: #0a1a3f;
  --ink: #1b2437;
  --slate: #5a6478;
  --line: #e3e7ef;
  --bg: #f6f8fb;
  --card: #ffffff;
  --green: #0fa36b;
  --green-soft: #e6f6ef;
  --red: #d64545;
  --red-soft: #fbeaea;
  --amber: #b97a0f;
  --radius: 14px;
  --mono: 'IBM Plex Mono', ui-monospace, Consolas, monospace;

  /* ADDED — from src/styles/tokens.css, which has it and the mockup's :root
     does not. Used only by the "arriving later" footer items, which have to sit
     visibly below --slate or they read as ordinary links. */
  --muted: #9aa3b5;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html {
  scroll-behavior: smooth;
}
body {
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  background: #fff;
  color: var(--ink);
}

/* ADDED — the mockup animates nothing, but it does ask the page to scroll
   smoothly, and that is a motion preference somebody may have turned off. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* ADDED — the mockup has no focus styles at all. Every CTA on this page is a
   link, so without this the entire page is unusable from a keyboard. */
a:focus-visible,
summary:focus-visible {
  outline: 3px solid rgba(15, 37, 87, 0.35);
  outline-offset: 2px;
  border-radius: 6px;
}

.wrap {
  max-width: 1040px;
  margin: 0 auto;
  padding: 0 24px;
}

/* nav */
header {
  position: sticky;
  top: 0;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
  z-index: 20;
}
.nav {
  height: 66px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.logo {
  display: flex;
  align-items: center;
  font-weight: 800;
  font-size: 19px;
  letter-spacing: -0.02em;
  color: var(--navy);
}

/* THE WORDMARK — "Travel" navy, "PDF" green, "Pro" reversed out of a green
   badge. There is no tile: the lockup on every marketing surface is the word
   alone. The mark is not retired — it is the favicon set, assets/mark.svg, and
   the app's admin sidebar, which draws its own 30px tile because it collapses
   to an icon rail with no wordmark beside it. This is the CSS twin of the
   lockup drawn in scripts/generate-brand-assets.js; the app draws it a THIRD
   time in src/components/Logo.css, because the two worlds share no stylesheet
   by design (D10). If one moves, move all three. Every ratio below has a named
   constant over there.

   `.word` IS STILL LOAD-BEARING — for a different reason than the one it was
   written for, so do not read it as a leftover and delete it.

   It was written for the GAP. `.logo` carried `gap: 10px` to space the tile,
   and flex wraps each contiguous run of child text in an anonymous item — so
   the bare "TravelPdf" text node and `.pro` were two flex items and the header
   SHIPPED reading "TravelPdf Pro", live, on every page, through two owner
   passes. Splitting the word into three coloured parts would have made it two
   such holes.

   That gap is now DELETED rather than zeroed or left standing with nothing to
   space: a dormant `gap` on this container is the same trap re-armed for
   whoever adds the next element to the lockup, and it would spring the same
   way — silently, looking like letter-spacing somebody chose.

   What keeps `.word` here is `align-items: center` on this rule. The badge is a
   flex item of the WORD, so it is centred on the wordmark's own box — the cap
   band — rather than on whatever else the lockup row comes to contain. */
.logo .word {
  display: flex;
  align-items: center;
}
.logo .pdf {
  color: var(--green);
}

/* The badge fills the wordmark's CAP BAND: top on the cap line, bottom on the
   baseline. `align-items: center` on `.word` is what puts it there — for Inter
   the em box's centre and the cap band's centre are 0.0005em apart, so centring
   the box centres the caps and no vertical nudge is needed at any size.

   ⚠ `em` in this rule resolves against THIS rule's font-size — 0.68 of the
   wordmark's — so each length is the wordmark figure ÷ 0.68. The comments give
   the wordmark figure, which is the one that matches the generator. */
.logo .pro {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.68em; /* "slightly smaller" than the wordmark */
  height: 1.069em; /* 0.727 wordmark-em — Inter's cap height */
  padding: 0 0.34em;
  margin-left: 0.206em; /* 0.14 wordmark-em */
  border-radius: 0.3em; /* 0.28 of the badge's own height */
  background: var(--green);
  color: #fff;
}
.nav-links {
  display: flex;
  gap: 26px;
  align-items: center;
}
.nav-links a {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--slate);
  text-decoration: none;
}
.nav-links a:hover {
  color: var(--navy);
}
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: 10px;
  font-family: inherit;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.15s;
  text-decoration: none;
}
.btn-sm {
  font-size: 13.5px;
  padding: 9px 18px;
}
.btn-lg {
  font-size: 15.5px;
  padding: 15px 30px;
}
.btn-navy {
  background: var(--navy);
  color: #fff;
}
.btn-navy:hover {
  background: var(--navy-deep);
}
.nav-links a.btn-navy {
  color: #fff;
}
.btn-outline {
  background: #fff;
  border: 1.5px solid var(--line);
  color: var(--navy);
}
.btn-outline:hover {
  border-color: var(--navy);
}

/* hero */
.hero {
  padding: 76px 0 60px;
  text-align: center;
  background: linear-gradient(180deg, #fff 0%, var(--bg) 100%);
}
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--green);
  background: var(--green-soft);
  border-radius: 999px;
  padding: 7px 16px;
  margin-bottom: 22px;
}
h1 {
  font-size: clamp(30px, 5vw, 52px);
  font-weight: 900;
  letter-spacing: -0.035em;
  color: var(--navy);
  line-height: 1.08;
  max-width: 820px;
  margin: 0 auto 18px;
}
h1 .hl {
  color: var(--green);
}
.hero-sub {
  font-size: clamp(15px, 2vw, 18px);
  color: var(--slate);
  max-width: 640px;
  margin: 0 auto 30px;
  line-height: 1.6;
}
.hero-ctas {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.hero-note {
  font-size: 12.5px;
  color: var(--slate);
}

/* hero demo visual */
.demo-card {
  max-width: 640px;
  margin: 44px auto 0;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 20px 50px -12px rgba(15, 37, 87, 0.18);
  padding: 22px;
  text-align: left;
}
.demo-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  margin-bottom: 9px;
}
.demo-row:last-child {
  margin-bottom: 0;
}
.demo-check {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--green);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  flex-shrink: 0;
}
.demo-name {
  flex: 1;
  font-size: 13px;
  font-weight: 600;
  font-family: var(--mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--navy);
}
.demo-size {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.demo-size .old {
  color: var(--slate);
  /* DEVIATION — the mockup uses `text-decoration: line-through`. The strike is
     drawn by the ::after rule below instead, because a text-decoration cannot
     be made to sweep across the figure and that sweep is the beat that reads as
     "this file just got compressed". At rest the two look the same: a 1px rule
     in the text's own colour across the middle of the box. inline-block is what
     makes both the absolute positioning and the transform apply — neither does
     anything to a plain inline element. */
  position: relative;
  display: inline-block;
}
.demo-size .old::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: calc(50% - 0.5px);
  height: 1px;
  background: currentColor;
  transform-origin: left center;
}
.demo-size .arrow,
.demo-size .new {
  display: inline-block;
}
.demo-size .new {
  color: var(--green);
}
.demo-gauge {
  height: 5px;
  background: var(--line);
  border-radius: 3px;
  position: relative;
  margin-top: 14px;
}
.demo-gauge-fill {
  position: absolute;
  height: 100%;
  width: 34%;
  background: var(--green);
  border-radius: 3px;
}
.demo-gauge-limit {
  position: absolute;
  top: -4px;
  bottom: -4px;
  left: 42%;
  width: 2px;
  background: var(--navy);
}
.demo-caption {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--slate);
  margin-top: 8px;
  font-family: var(--mono);
}

/* -----------------------------------------------------------------------------
   .demo-card — THE ONE ANIMATION ON THIS PAGE            (ADDED, owner-requested)
   -----------------------------------------------------------------------------
   The card illustrates a finished run. Animated, it illustrates the run itself:
   each row arrives, its old size is struck out, the new green figure lands, the
   tick confirms it, and the gauge finally sweeps up to just under the portal
   limit. Plays ONCE on load, then holds. Nothing loops — the card sits directly
   beside the primary CTA and a permanently moving element competes with it.
   (To loop instead: add `infinite` to each shorthand and pad the last keyframe
   with a hold. Deliberately not done.)

   THIS IS CSS ONLY, AND THAT IS NOT A STYLE PREFERENCE — see the header of
   site/index.html. The page must render complete with JavaScript disabled,
   which is the entire reason it exists as a separate artefact, and
   scripts/build-site.js fails the build on a <script> tag in it.

   TWO RULES THIS IS BUILT ON, both of which are ways it could silently break:

   1. THE RESTING STATE IS THE FINISHED STATE. Every element below is already
      complete and visible in ordinary CSS; the *starting* state exists only
      inside the @keyframes, applied during the delay by `backwards` fill. Write
      it the other way round — `opacity: 0` in the rule, `1` in the keyframe —
      and the moment animations are off (reduced motion, an old engine, a print
      stylesheet) the hero's centrepiece is a blank card. That failure has no
      symptom for anybody whose machine plays the animation.
   2. ONLY `opacity` AND `transform` MOVE. Nothing here changes width, height or
      layout, so nothing reflows and the card cannot shift the page while it
      plays. The hidden elements keep their boxes the whole time.

   The stagger is one custom property per row; everything inside a row derives
   its delay from it, so the whole sequence retimes from three numbers.

        row 1  --step 0s      row 2  --step 0.42s     row 3  --step 0.84s
        +0.10  row slides in
        +0.42  strike sweeps across the old size
        +0.64  arrow fades in
        +0.72  green figure lands
        +0.94  tick pops
        2.05   gauge fills to 34% (the limit tick at 42% never moves)
        2.60   "ALL FILES UNDER LIMIT" fades in — the conclusion, so it is last
   -------------------------------------------------------------------------- */
.demo-row:nth-child(1) {
  --step: 0s;
}
.demo-row:nth-child(2) {
  --step: 0.42s;
}
.demo-row:nth-child(3) {
  --step: 0.84s;
}

.demo-row {
  animation: demo-row-in 0.45s cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
  animation-delay: calc(var(--step) + 0.1s);
}
.demo-row .demo-check {
  animation: demo-tick-in 0.42s cubic-bezier(0.2, 1.3, 0.4, 1) backwards;
  animation-delay: calc(var(--step) + 0.94s);
}
.demo-row .old::after {
  animation: demo-strike 0.3s ease-out backwards;
  animation-delay: calc(var(--step) + 0.42s);
}
.demo-row .arrow {
  animation: demo-fade-in 0.24s ease-out backwards;
  animation-delay: calc(var(--step) + 0.64s);
}
.demo-row .new {
  animation: demo-land 0.34s cubic-bezier(0.2, 0.9, 0.3, 1) backwards;
  animation-delay: calc(var(--step) + 0.72s);
}
.demo-gauge-fill {
  transform-origin: left center;
  animation: demo-sweep 0.85s cubic-bezier(0.3, 0.8, 0.3, 1) 2.05s backwards;
}
/* The left half of the caption only. The right half labels the limit tick,
   which is drawn from the start, so its label is too. */
.demo-caption span:first-child {
  animation: demo-fade-in 0.5s ease-out 2.6s backwards;
}

@keyframes demo-row-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}
@keyframes demo-fade-in {
  from {
    opacity: 0;
  }
}
@keyframes demo-strike {
  from {
    transform: scaleX(0);
  }
}
@keyframes demo-land {
  from {
    opacity: 0;
    transform: translateY(-4px) scale(0.94);
  }
}
@keyframes demo-sweep {
  from {
    transform: scaleX(0);
  }
}
@keyframes demo-tick-in {
  from {
    opacity: 0;
    transform: scale(0.3);
  }
}

/* ADDED — the opt-out, kept next to the animation rather than in the global
   reduced-motion block at the top of this file, so that anyone editing the
   sequence above sees what they also have to switch off. Because of rule 1
   above, `animation: none` does not hide anything: it leaves the card in the
   finished state it is written in. */
@media (prefers-reduced-motion: reduce) {
  .demo-row,
  .demo-row .demo-check,
  .demo-row .old::after,
  .demo-row .arrow,
  .demo-row .new,
  .demo-gauge-fill,
  .demo-caption span:first-child {
    animation: none;
  }
}

/* sections */
section {
  padding: 72px 0;
}
.sec-eyebrow {
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--green);
  text-align: center;
  margin-bottom: 10px;
}
h2 {
  font-size: clamp(24px, 3.4vw, 34px);
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--navy);
  text-align: center;
  margin-bottom: 14px;
}
.sec-sub {
  font-size: 15px;
  color: var(--slate);
  text-align: center;
  max-width: 560px;
  margin: 0 auto 44px;
  line-height: 1.6;
}

/* benefits grid */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
}
.feat {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
}
.feat-icon {
  width: 44px;
  height: 44px;
  border-radius: 11px;
  background: var(--navy);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 19px;
  margin-bottom: 16px;
}
.feat-icon.g {
  background: var(--green);
}
.feat h3 {
  font-size: 16px;
  font-weight: 800;
  color: var(--navy);
  margin-bottom: 8px;
  letter-spacing: -0.01em;
}
.feat p {
  font-size: 13.5px;
  color: var(--slate);
  line-height: 1.6;
}

/* privacy band */
.privacy {
  background: var(--navy);
  color: #fff;
  text-align: center;
}
.privacy .sec-eyebrow {
  color: #4ade9c;
}
.privacy h2 {
  color: #fff;
}
.privacy p {
  color: rgba(255, 255, 255, 0.75);
  font-size: 15px;
  max-width: 620px;
  margin: 0 auto;
  line-height: 1.7;
}
.privacy-points {
  display: flex;
  justify-content: center;
  gap: 34px;
  margin-top: 34px;
  flex-wrap: wrap;
}
.privacy-point {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.9);
}
.privacy-point .dot {
  color: #4ade9c;
}

/* steps */
.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
  counter-reset: step;
}
.step {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
  position: relative;
}
.step::before {
  counter-increment: step;
  content: counter(step);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--green-soft);
  color: var(--green);
  font-family: var(--mono);
  font-weight: 600;
  font-size: 15px;
  margin-bottom: 14px;
}
.step h3 {
  font-size: 15.5px;
  font-weight: 800;
  color: var(--navy);
  margin-bottom: 7px;
}
.step p {
  font-size: 13.5px;
  color: var(--slate);
  line-height: 1.6;
}

/* faq */
.faq {
  max-width: 680px;
  margin: 0 auto;
}
details {
  border: 1px solid var(--line);
  border-radius: 12px;
  margin-bottom: 10px;
  background: var(--card);
}
summary {
  padding: 16px 20px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--navy);
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
/* Safari still needs the vendor pseudo-element to drop the disclosure triangle. */
summary::-webkit-details-marker {
  display: none;
}
summary::after {
  content: '+';
  font-size: 18px;
  color: var(--slate);
  font-weight: 400;
}
details[open] summary::after {
  content: '–';
}
details p {
  padding: 0 20px 16px;
  font-size: 13.5px;
  color: var(--slate);
  line-height: 1.65;
}

/* final cta */
.final {
  background: var(--bg);
  text-align: center;
}

footer {
  border-top: 1px solid var(--line);
  padding: 28px 0;
  background: #fff;
}
.foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.foot-links {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  align-items: center;
}
.foot a {
  font-size: 12.5px;
  color: var(--slate);
  text-decoration: none;
}
.foot a:hover {
  color: var(--navy);
}
.foot-copy {
  font-size: 12.5px;
  color: var(--slate);
}

/* ADDED — the honest treatment for a footer item that is NOT built.
   The mockup renders those as <a href="#">, which is a live-looking control for
   something that goes nowhere (rule #54, the same call the Google button made
   in Phase 1). This is not a link at all: no href, visibly quieter than the
   real ones, and it carries the word "soon" so the page says out loud what it
   is doing.

   ⚠ NOTHING WEARS IT TODAY, and the rule is kept anyway. Privacy, Terms and
   Contact were three of these until T2; "Blog" was the last one, and T3 turned
   it into a real "Guides" anchor pointing at the index that now exists. So this
   block is the pattern the NEXT unbuilt footer item uses, not dead weight — and
   `build-site.js` fails the build if any live destination goes back to wearing
   it, on any of the eight pages. Delete it only when the site stops promising
   things before it ships them, which is not a state a site reaches. */
.foot-soon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--muted);
  cursor: default;
}
.foot-soon .soon {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--slate);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 7px;
}

@media (max-width: 640px) {
  .nav-links a:not(.btn) {
    display: none;
  }
  section {
    padding: 52px 0;
  }
}

/* =============================================================================
   DOCUMENT PAGES — /privacy, /terms, /contact               (ADDED, Phase 7 T2)
                    and /guides/<topic>/                     (WIDENED, Phase 7 T3)
   =============================================================================
   The same shell as the landing page and deliberately the same stylesheet: same
   header, same footer, same tokens, one file (D10 — the marketing site is one
   static artefact). What changes is the body. The landing page is a sales
   sequence of centred, full-width sections; these are documents, so they are a
   single column of left-aligned prose at a readable measure.

   THAT IS WHY EVERY HEADING RULE BELOW IS AN OVERRIDE. `h1`, `h2` and `section`
   are styled as bare elements at the top of this file, because the landing page
   has exactly one of each per band and they are all centred. A document page has
   to say otherwise explicitly — and `.wrap.legal` is two classes on purpose, so
   it beats `.wrap`'s own max-width rather than relying on source order.

   `.guide` RIDES ALONG ON EVERY RULE WHERE IT WANTS THE IDENTICAL VALUE. T3's
   articles are the same problem this block already solved, so they are a second
   selector here rather than a second copy of it further down — two blocks with
   the same numbers in them drift, and the drift is invisible because each page
   looks right on its own. What an ARTICLE needs and a policy does not (numbered
   steps, tables, inline code, a pull-quote, a CTA) is in the GUIDE ARTICLES
   block at the foot of this file. The one rule that does NOT widen is the list
   bullet: `.guide` has ordered lists, and they take numbers instead of dots.
   ============================================================================= */
.legal-page,
.guide-page {
  padding: 54px 0 76px;
  background: linear-gradient(180deg, var(--bg) 0%, #fff 220px);
}
.wrap.legal,
.wrap.guide {
  max-width: 760px;
}
.legal h1,
.guide h1 {
  font-size: clamp(28px, 4vw, 40px);
  text-align: left;
  max-width: none;
  margin: 0 0 10px;
}
.legal-updated,
.guide-meta {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--slate);
  margin-bottom: 26px;
}
/* ⚠ THIS RULE LOSES TO `.legal p` BELOW AND HAS DONE SINCE T2. `.legal p` is a
   class plus an element (0,1,1) and this is a bare class (0,1,0), so on a <p> —
   which is the only element /privacy puts it on — the size, the colour and the
   margin here are all overridden and the lead renders as body text. Left alone
   deliberately: fixing it changes how a live page looks, which is the owner's
   call and not a conversion task's. The guides hit the identical trap and solve
   it with `p.guide-lead` in the GUIDE ARTICLES block, where the reasoning is
   written out in full. */
.legal-lead {
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink);
  margin-bottom: 28px;
}
.legal section {
  padding: 0;
  margin-bottom: 30px;
}
.legal section:last-child,
.guide section:last-child {
  margin-bottom: 0;
}
.legal h2,
.guide h2 {
  font-size: clamp(18px, 2.4vw, 22px);
  text-align: left;
  margin: 0 0 12px;
}
.legal h3,
.guide h3 {
  font-size: 15px;
  font-weight: 800;
  color: var(--navy);
  margin: 20px 0 7px;
}
.legal p,
.guide p {
  font-size: 14.5px;
  line-height: 1.75;
  color: var(--slate);
  margin-bottom: 12px;
}
.legal p:last-child,
.guide p:last-child {
  margin-bottom: 0;
}
.legal strong,
.guide strong {
  color: var(--navy);
  font-weight: 700;
}
.legal a,
.guide a {
  color: var(--navy);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.legal a:hover,
.guide a:hover {
  color: var(--green);
}
.legal ul,
.guide ul,
.guide ol {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
}
.legal li,
.guide li {
  position: relative;
  padding-left: 20px;
  margin-bottom: 9px;
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--slate);
}
.legal li:last-child,
.guide li:last-child {
  margin-bottom: 0;
}
.legal li::before,
.guide ul li::before {
  content: '';
  position: absolute;
  left: 3px;
  top: 10px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--green);
}

/* The one thing on /privacy that a visitor should read even if they read
   nothing else. Green rather than navy because it is the good news. */
.legal-callout {
  background: var(--green-soft);
  border: 1px solid rgba(15, 163, 107, 0.25);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-bottom: 30px;
}
.legal-callout-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--green);
  margin-bottom: 10px;
}
.legal-callout p {
  color: var(--ink);
}

/* A neutral box for the same job on /terms, where the news is a warning rather
   than a reassurance — navy, not red: it is important, not an error. */
.legal-note {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--navy);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-bottom: 30px;
}
.legal-note .legal-callout-title {
  color: var(--navy);
}
.legal-note p {
  color: var(--ink);
}

/* The header logo is a link back to / on every page except / itself, where it
   would be a self-link. `.logo` is a flex container carrying its own colour, so
   as an <a> it needs only the underline removed. */
a.logo {
  text-decoration: none;
}
/* The badge is the hover affordance now that "Pro" is a fill rather than a
   coloured word — the fill goes navy and the white text stays white, so the
   feedback is the same gesture the old rule made and the label never drops out. */
a.logo:hover .pro {
  background: var(--navy);
}

/* /contact — the address is the page, so it is the largest thing on it. */
.contact-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
  margin-bottom: 18px;
}
.contact-label {
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--green);
  margin-bottom: 10px;
}
a.contact-email {
  display: inline-block;
  font-family: var(--mono);
  font-size: clamp(17px, 3.4vw, 23px);
  font-weight: 600;
  color: var(--navy);
  text-decoration: none;
  letter-spacing: -0.01em;
  /* An address is one token to a reader and an unbreakable string to a layout
     engine. Without this it overflows the card on a narrow phone instead of
     wrapping — and this address is 21 characters of it. */
  word-break: break-word;
}
a.contact-email:hover {
  color: var(--green);
}
.contact-meta {
  font-size: 13px;
  color: var(--slate);
  margin-top: 12px;
  line-height: 1.6;
}

@media (max-width: 640px) {
  .legal-page,
  .guide-page {
    padding: 36px 0 56px;
  }
  .legal-callout,
  .legal-note,
  .guide-callout,
  .guide-cta,
  .contact-card {
    padding: 18px;
  }
}

/* =============================================================================
   THE UMBRELLA LANDING — `/`                                (ADDED, Phase 7 T2b)
   =============================================================================
   TravelPdfPro became a suite (D11), so `/` is now a tool directory and the visa
   compressor's sales page moved to /tools/visa-pdf-compressor/. This block is what the
   directory needed and nothing more — the hero, the section rhythm, the privacy
   band and the whole footer are the SAME rules the other pages use, unchanged.

   `.tool` is deliberately a sibling of `.feat` rather than a variant of it. They
   look related because they share `.grid` and `.feat-icon`, but a benefit is a
   statement and a tool is a door: this one is an <a>, it has a hover state, and
   it grows a go-line. Building it as `.feat--linky` would have put those three
   behaviours behind a modifier on a class whose job is to be inert.

   `.guide-card` AND `.grid-guides` RIDE ALONG, and they are second selectors
   rather than a second copy of these rules — the same call the DOCUMENT PAGES
   block makes for `.guide`, for the same reason: two blocks holding the same
   numbers drift, and the drift is invisible because each page looks right on
   its own. /guides/ lists articles in exactly this card: a title, a line, and a
   go-line, in a grid that caps its columns instead of filling them. They are
   NOT called `.tool`, because a class name that disagrees with the page it is
   on is one more thing to hold in your head — the same reason the articles are
   `.guide` and not `.article`. `.tool-go` is shared as-is: it is the go-line,
   and both kinds of card grow the same one.
   ============================================================================= */

/* One tool today, two guides. `auto-fit` would stretch a single card to the full
   1040px, which reads as an empty row rather than a deliberate one — so these
   grids cap their columns instead of filling them. A second tool sits beside the
   first; a fourth wraps. Nothing here needs revisiting when that happens. */
.grid-tools,
.grid-guides {
  grid-template-columns: repeat(auto-fit, minmax(280px, 420px));
  justify-content: center;
}

/* THE /guides/ INDEX HEAD, AND THE RULE THAT WAS NOT THERE.
   Bare `h1` sets a size and `margin: 0 auto`, and it sets NO text-align — every
   page that looks centred is centred by an ancestor (`.hero` on `/`, the section
   bands elsewhere). The guides index has neither, so its <h1> rendered
   left-aligned inside a centred 820px box while `.sec-sub` underneath it — which
   DOES carry text-align — sat centred: two headings on one page disagreeing about
   where the page's axis is. Nothing could have reported that. Every check passed,
   the markup reads correctly, and it took a screenshot, which is the third time
   this stylesheet has learned that (`.legal-lead`, the navy-on-navy CTA button).

   The centring is on this wrapper and not on `.wrap`, deliberately: the cards are
   a sibling of it, and text-align inherits — centring the container would have
   centred the copy inside every card too.

   AND THE SIZE COMES DOWN. `h1`'s clamp tops out at 52px, which is hero scale for
   a page that is a directory, not a pitch. This is the section-heading scale the
   rest of the site uses for the same job. */
.guides-head {
  text-align: center;
}
.guides-head h1 {
  font-size: clamp(26px, 3.4vw, 34px);
  margin-bottom: 12px;
}

.tool,
.guide-card {
  display: block;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
  text-decoration: none;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.tool:hover,
.guide-card:hover {
  border-color: var(--green);
  box-shadow: 0 6px 22px rgba(15, 37, 87, 0.08);
  transform: translateY(-2px);
}
/* The card is one link, so the keyboard focus ring belongs to the whole card
   and not to the go-line inside it. */
.tool:focus-visible,
.guide-card:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

.tool-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 14px;
}
/* .feat-icon carries a 16px bottom margin for the benefits grid, where it sits
   above its heading. Here it sits beside one. */
.tool-head .feat-icon {
  margin-bottom: 0;
  flex-shrink: 0;
}
.tool h3,
.guide-card h3 {
  font-size: 17px;
  font-weight: 800;
  color: var(--navy);
  letter-spacing: -0.01em;
  margin-bottom: 3px;
}
/* A guide card has no `.tool-head` above its title — there is no icon and no tag
   line, because an article's identity is its title. The 3px that separates a
   tool's title from its tag line has nothing under it here, so the gap the
   `.tool-head` block would have provided is set on the heading instead. */
.guide-card h3 {
  margin-bottom: 10px;
}
/* THE GO-LINE SITS ON THE FLOOR OF THE CARD, NOT UNDER THE PARAGRAPH.
   Article titles wrap to different numbers of lines — one of these two does and
   one does not — so a block-flow card puts "Read the guide" 20px lower in one
   card than its neighbour, and the row reads as two cards that were laid out
   separately. The tool grid never showed this because there is one tool. Column
   flex plus an auto top margin pins the go-lines to a shared baseline; the cards
   were already equal height (grid stretches them), so nothing else moves. */
.guide-card {
  display: flex;
  flex-direction: column;
}
.guide-card .tool-go {
  margin-top: auto;
  align-self: flex-start;
}
.tool-tag {
  font-size: 12px;
  font-weight: 700;
  color: var(--green);
  letter-spacing: 0.01em;
}
.tool p,
.guide-card p {
  font-size: 13.5px;
  color: var(--slate);
  line-height: 1.6;
  margin-bottom: 16px;
}
.tool-go {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--navy);
}
.tool:hover .tool-go,
.guide-card:hover .tool-go {
  color: var(--green);
}
/* The arrow nudges on hover. It is inside the transition budget the demo card
   already set, and it is the only motion on this page — `prefers-reduced-motion`
   is honoured below with everything else. */
.tool-go span {
  transition: transform 0.15s;
}
.tool:hover .tool-go span,
.guide-card:hover .tool-go span {
  transform: translateX(3px);
}

/* "More tools are coming" is a paragraph, not a grid — see the note in
   index.html's header for why there are no placeholder cards. It only needs its
   inline link to be visible as one. */
.more .sec-sub {
  margin-bottom: 0;
}
.more .sec-sub a {
  color: var(--green);
  font-weight: 700;
  text-decoration: none;
}
.more .sec-sub a:hover {
  text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
  .tool,
  .guide-card,
  .tool-go span {
    transition: none;
  }
  .tool:hover,
  .guide-card:hover {
    transform: none;
  }
  .tool:hover .tool-go span,
  .guide-card:hover .tool-go span {
    transform: none;
  }
}

/* =============================================================================
   GUIDE ARTICLES — /guides/<topic>/                          (ADDED, Phase 7 T3)
   =============================================================================
   The first pages on this site that exist to be READ rather than to sell, and
   they are the same artefact as everything else here: hand-written HTML, no
   build step, no script. `build-site.js` asserts that on them exactly as it does
   on the other five.

   MOST OF AN ARTICLE IS ALREADY STYLED, up in the DOCUMENT PAGES block, which
   `.guide` joins as a second selector — see the note there. What is below is only
   what an article has and a policy does not: numbered steps, comparison tables,
   inline code, a pull-quote, a call to action and a related-reading foot.

   `.guide` AND NOT `.article`, because the URL namespace is `/guides/<topic>/`
   (D11) and a class name that disagrees with the path is one more thing to hold
   in your head. It also keeps `<article>` free as the element it is.
   ============================================================================= */

/* An article is a longer read than a policy — 10-ish sections against 8 — so its
   sections carry more air. Everything else about the rhythm is the shared rule. */
.guide section {
  padding: 0;
  margin-bottom: 38px;
}

/* THE STANDFIRST, AND WHY IT IS TWO SELECTORS DEEP.
   `.guide p` is a class plus an element (0,1,1) and beats a bare `.guide-lead`
   (0,1,0) on the same paragraph no matter which is written first — so the
   obvious rule loses silently and the lead renders as body text. That is not
   hypothetical: `.legal-lead` has been losing this exact race on /privacy since
   T2, which is where this was found (a screenshot, not a reading — the CSS looks
   correct). Left broken there on purpose and fixed here; see the note beside it. */
.guide p.guide-lead {
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink);
  margin-bottom: 28px;
}

/* NUMBERED STEPS. The shared `li` rule positions a marker at the left edge; for
   `ul` that marker is the green dot, and here it is the step number. Mono,
   because these are figures in a procedure and the site sets figures in mono
   everywhere else (the demo card, the step cards, the "last updated" line).
   `top` nudges the smaller mono cap-height onto the prose baseline. */
.guide ol {
  counter-reset: guide-step;
}
.guide ol > li {
  padding-left: 30px;
}
.guide ol > li::before {
  counter-increment: guide-step;
  content: counter(guide-step) ".";
  position: absolute;
  left: 0;
  top: 2px;
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--green);
}

/* File names, in prose. The articles name three of them and they have to read as
   strings a person types, not as emphasis. */
.guide code {
  font-family: var(--mono);
  font-size: 0.88em;
  color: var(--navy);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1px 5px;
  /* A file name is one token to a reader and an unbreakable string to a layout
     engine — the same trap `.contact-email` fell into, one line long. */
  word-break: break-word;
}

/* THE COMPARISON TABLES, AND THE ONE THING THEY MUST NOT DO ON A PHONE.
   A three-column table of sentences cannot reflow to 360px, and the failure mode
   is not the table looking cramped — it is the table widening the BODY, so the
   whole article scrolls sideways and every paragraph on the page runs off the
   screen. So the table keeps a usable minimum width and scrolls inside its own
   box. The wrapper is what scrolls; the border and the radius live on it, which
   is also what clips the table's corners to it. */
.guide-table {
  overflow-x: auto;
  margin: 0 0 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
}
.guide table {
  width: 100%;
  min-width: 520px;
  border-collapse: collapse;
}
.guide th,
.guide td {
  text-align: left;
  vertical-align: top;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--slate);
}
.guide th {
  font-weight: 700;
  color: var(--navy);
  background: var(--bg);
}
.guide tbody tr:last-child td {
  border-bottom: none;
}

/* THE PULL-QUOTE. Navy and not green, for the reason `.legal-note` is navy: one
   of the two in these drafts is a rule to follow and the other is a warning, and
   a box that changes colour with the mood of the sentence inside it stops
   meaning anything. This one means "if you read one line in this section, read
   this one". */
.guide-callout {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--navy);
  border-radius: var(--radius);
  padding: 18px 22px;
  margin: 0 0 16px;
}
.guide-callout p {
  color: var(--ink);
  margin-bottom: 0;
}

/* THE CALL TO ACTION. The landing page's closing band, shrunk into the column:
   light panel, navy button, the same reassurance line underneath. Deliberately
   NOT a navy card — a full-strength block mid-article reads as an advertisement
   somebody dropped into the page, which is exactly what these guides must not
   look like if they are to be worth reading. */
.guide-cta {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
  margin: 22px 0 0;
  text-align: center;
}
/* THE BUTTON IS AN <a> INSIDE `.guide`, WHICH IS THE SAME SPECIFICITY TRAP AS
   THE LEAD AND A LOUDER FAILURE. `.guide a` (0,1,1) outranks `.btn-navy` (0,1,0),
   so the navy button shipped with navy text on a navy fill — an invisible label,
   underlined — and it was invisible in the markup too: every rule involved reads
   correctly on its own. A screenshot found it. The header solved the identical
   collision with `.nav-links a.btn-navy`, which is why that rule exists and
   looks redundant; this is its twin. Any button added to a guide needs to be
   inside `.btn` for this to hold. */
.guide a.btn {
  text-decoration: none;
}
.guide a.btn-navy {
  color: #fff;
}
.guide-cta-title {
  font-size: 17px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--navy);
  margin-bottom: 8px;
}
.guide-cta p {
  max-width: 460px;
  margin: 0 auto 18px;
}
.guide-cta .hero-note {
  margin-top: 14px;
}

/* The FAQ accordions are the landing page's `<details>`, unchanged — this only
   settles the two rules that meet inside them: `details p` supplies the padding,
   `.guide p` the size, and the margin has to go or the answers sit low in their
   box. Every answer in both drafts is a single paragraph. */
.guide details {
  margin-bottom: 10px;
}
.guide details p {
  margin: 0;
  padding: 0 20px 16px;
  font-size: 14px;
}

/* The foot of the article — sources, or the next thing to read. Set off by a
   rule because it is apparatus rather than argument. */
.guide-related {
  border-top: 1px solid var(--line);
  padding-top: 26px;
}
