/* ==========================================================================
   site.css - the cross-cutting UX bits, on EVERY page including index.html.

   WHY THIS FILE EXISTS AT ALL, since `index.html` is deliberately self-contained
   and `README.md` says so. These three things - the scroll progress bar, the
   back-to-top button and the print rules - belong on the marketing page AND on
   the five secondary pages. Putting them in `index.html`'s <style> and again in
   `legal.css` would be a second copy of the same rules, which is the failure this
   repository has lost four separate bets on. One file, linked everywhere.

   🔑 IT DEFINES NO COLOURS. Every value here is a `var()` that BOTH contexts
   already declare - `--navy`, `--orange`, `--orange-ink`, `--line`, `--ease` are
   in `index.html`'s :root and in `legal.css`'s, identically. So this is not a
   fourth copy of the palette; it cannot drift from one, because it holds none.
   If you add a rule here, use a token that exists in both or it will silently
   render unstyled on half the site.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. SCROLL PROGRESS

   Transform-only, so it never triggers layout during a scroll. The width is
   fixed at 100% and `scaleX` is driven from JS; animating `width` here would
   reflow the whole page on every scroll event on a phone.

   It sits ABOVE the sticky header (z-index) but is only 3px, so it does not
   cover anything. `aria-hidden` in the markup: it is a picture of the scrollbar
   the browser already provides, and announcing it is noise.
   -------------------------------------------------------------------------- */
.progress{
  position:fixed; inset:0 0 auto 0; height:3px; z-index:2000;
  background:transparent; pointer-events:none;
}
.progress > i{
  display:block; height:100%; width:100%; transform-origin:0 50%;
  transform:scaleX(var(--p,0));
  background:linear-gradient(90deg,var(--orange),var(--orange-ink));
}

/* --------------------------------------------------------------------------
   2. BACK TO TOP

   ⚠️ 48px, NOT 44. `layout.mjs` fails anything under 44px and this would sit
   exactly on the line - a control that passes its own check by zero pixels is
   one rounding change away from failing it. The extra four is deliberate slack.

   Hidden with `[hidden]` rather than opacity so it leaves the tab order when it
   is not on screen. A focusable control floating invisibly over the page is the
   kind of thing only a keyboard user finds, and they find it by getting stuck.
   -------------------------------------------------------------------------- */
.to-top{
  position:fixed; right:18px; bottom:18px; z-index:1900;
  width:48px; height:48px; border-radius:50%; border:0;
  display:grid; place-items:center; cursor:pointer;
  background:var(--navy); color:#fff;
  box-shadow:0 10px 30px -10px rgba(8,18,41,.6);
  opacity:0; transform:translateY(10px);
  transition:opacity .25s var(--ease), transform .25s var(--ease), background .2s;
}
.to-top.is-in{opacity:1; transform:none}
.to-top:hover{background:var(--orange); color:var(--navy)}
.to-top:focus-visible{outline:3px solid var(--orange); outline-offset:3px}
.to-top svg{width:20px; height:20px}

@media (max-width:700px){ .to-top{right:14px; bottom:14px} }

/* --------------------------------------------------------------------------
   3. PRINT

   An owner prints the terms, or a page to read away from the screen. Everything
   that is navigation, decoration or a call to action is noise on paper.

   🔑 LINK HREFS ARE PRINTED after the link text. On paper a link is invisible -
   "see the pricing page" with no address is a dead end. Only absolute http(s)
   links are expanded; printing "(#contact)" after every in-page anchor is worse
   than printing nothing.
   -------------------------------------------------------------------------- */
@media print{
  .progress, .to-top, .nav, .navbar, .burger, .skip,
  .carousel-btn, .dots, .btn, .cta, footer .social{ display:none !important }

  *{ background:transparent !important; color:#000 !important;
     box-shadow:none !important; text-shadow:none !important }

  body{ font-size:11pt; line-height:1.45 }
  .wrap{ max-width:none; padding:0 }

  a[href^="http"]::after{ content:" (" attr(href) ")"; font-size:9pt; word-break:break-all }

  /* Never split a heading from what it introduces, and never strand one line. */
  h1,h2,h3,h4{ break-after:avoid-page; page-break-after:avoid }
  img,figure,.panel,.card{ break-inside:avoid; page-break-inside:avoid }
  p,li{ orphans:3; widows:3 }

  section{ padding:12pt 0 !important }
}

/* --------------------------------------------------------------------------
   4. REDUCED MOTION

   The site already carries its own global rule; this repeats it for the two
   controls above, because `site.css` may load on a page whose own stylesheet
   does not. The bar still fills and the button still appears - motion is
   removed, never the information.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  .to-top{ transition:none }
  .progress > i{ transition:none }
}

/* --------------------------------------------------------------------------
   5. THE "LAST UPDATED" STAMP

   ⚠️ IT HAS NO BOX OF ITS OWN AND THAT IS DELIBERATE. The span ships EMPTY and
   is filled by `scripts/publish-site.mjs` at publish time, so a local checkout
   renders nothing rather than a wrong date. `:empty` therefore has to collapse
   completely - a margin or a bullet on an empty span leaves a gap in the footer
   of every page anybody serves from the repo.
   -------------------------------------------------------------------------- */
.updated{ font-size:.86rem; opacity:.72 }
.updated:empty{ display:none }

/* --------------------------------------------------------------------------
   6. SCREEN-READER-ONLY TEXT

   ⚠️ ADDED 2026-09-15 BECAUSE `compare.html` USED `.sr-only` AND NOTHING DEFINED
   IT. The table caption would have rendered as visible body text above the
   comparison - a caption written for a screen reader, printed for everybody.
   Caught by grepping for the class before trusting it, which is cheaper than
   noticing it in a screenshot.

   🔑 It is clipped rather than `display:none`, because `display:none` removes it
   from the accessibility tree too - which defeats the entire purpose.
   -------------------------------------------------------------------------- */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}
