/* =========================================================================
   CoLateral website - shared page chrome
   -------------------------------------------------------------------------
   The sticky bar, the footer and the badge palette used to be redeclared in
   every page's own <style> block, which is how index.html ended up with an
   opaque header while pricing.html, packs.html and faq.html kept a 65 percent
   white one that body copy scrolled straight through. One rule, one token,
   every page: a page may not restate .nav or the footer, and a new page gets
   both by linking this sheet.

   index, pricing and faq link this sheet BEFORE their own <style>; on every
   other page site-navigation.mjs injects it at the end of <head>, beside
   site-header.css. The footer rules below are therefore scoped to the footer
   element and carry their own --ftr-* palette, so they read the same whichever
   side of a page's own sheet they land on. Nothing here should be restated in
   a page.

   Colours resolve against each page's own palette variables (--ink, --line,
   --blue, --muted, --quiet), so the same sheet works on every public page.
   ========================================================================= */

/* ---- Inter, self-hosted ---------------------------------------------------
   Every page names Inter first, but index, pricing and faq never loaded it
   while about, store and download pulled it from rsms.me, so the type changed
   shape between pages. One variable face (weights 100-900), served from our
   own origin (font-src 'self'), declared once here because this sheet is on
   every full page. latin-ext only downloads when a page uses one of its
   characters. Licence: fonts/OFL.txt (SIL Open Font License 1.1), from
   @fontsource-variable/inter 5.3.0. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-latin-ext-wght-normal.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: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-latin-wght-normal.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;
}

/* ---- Shared tokens ----------------------------------------------------------
   The one primary blue, one button geometry and one headline scale. The
   header reads these directly; pages should read them rather than restating
   their own. #0067c0 rather than #0078d4 for filled buttons: white on it is
   5.6:1, where #0078d4 only just clears 4.5:1. */
:root {
  --site-font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --site-primary: #0067c0;
  --site-primary-hover: #005a9e;
  --site-primary-ink: #ffffff;
  --site-primary-shadow: 0 10px 26px -12px rgba(0, 103, 192, 0.6);
  --site-btn-h: 48px;
  --site-btn-h-sm: 36px;
  --site-btn-radius: 12px;
  --site-pill-radius: 999px;
  --site-h1: clamp(2.5rem, 1.6rem + 3.6vw, 4.5rem);
  --site-h1-leading: 1.04;
  --site-h1-tracking: -0.045em;
  --site-h2: clamp(1.75rem, 1.2rem + 1.8vw, 2.75rem);
  --site-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

html[data-site-theme="dark"] {
  --site-primary: #4da6ff;
  --site-primary-hover: #7cc0ff;
  --site-primary-ink: #041018;
  --site-primary-shadow: 0 12px 30px -12px rgba(77, 166, 255, 0.55);
}

body {
  font-family: var(--site-font);
}

/* ---- Page to page ---------------------------------------------------------
   Same-origin navigations cross-fade instead of flashing white. The header
   carries its own view-transition-name so it is captured apart from the page
   and holds still while the content under it fades. Reduced motion gets no
   transition at all: the opt-in itself sits inside no-preference. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 0.2s;
    animation-timing-function: ease;
  }

  ::view-transition-group(site-header) {
    animation-duration: 0.2s;
  }

  /* The theme switch (site-prefs.js) takes a touch longer, so the change of
     palette reads as a fade rather than a flicker. */
  html.is-theme-fading::view-transition-old(root),
  html.is-theme-fading::view-transition-new(root),
  html.is-theme-fading::view-transition-group(site-header) {
    animation-duration: 0.25s;
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

:root {
  /* The bar is a surface, not a window. Fully opaque, in the canvas colour, so
     a 60px headline scrolling under it disappears rather than showing through
     the nav links. */
  --nav-bg: #eef2f7;
  --nav-line: rgba(15, 23, 42, 0.1);
  --nav-shadow: 0 1px 0 rgba(255, 255, 255, 0.6), 0 10px 26px rgba(15, 23, 42, 0.05);

  /* Boundary claims - "Coming soon", "Not for sale yet", "Available now",
     "Rented monthly", "Bought once" - are the small text a buyer is most
     entitled to read, so they are the highest contrast small text on the page
     rather than the lowest. These measure 9:1 and up against the surfaces they
     sit on; the body copy beside them measures about 6.9:1. */
  --badge-ink: #003f75;
  --badge-fill: #003f75;
  --badge-fill-soft: rgba(0, 63, 117, 0.1);
  --badge-line: rgba(0, 63, 117, 0.45);
}

/* The sticky bar puts five links and a buy button between the top of the tab
   order and the h1 on every page, so the first stop is a link past all of it.
   Visible only while focused, and it lands on <main id="main" tabindex="-1">,
   with scroll-margin clearing the fixed bar. */
.skip-link {
  position: absolute;
  z-index: 40;
  top: 8px;
  left: 8px;
  padding: 11px 16px;
  border: 1px solid var(--line-strong, rgba(0, 120, 212, 0.45));
  border-radius: 10px;
  background: var(--frame, #ffffff);
  color: var(--focus-ring, #00437c);
  font-size: 13px;
  font-weight: 800;
  /* No shadow while parked above the viewport: a 30px blur reached down
     into the top-left corner of every page as a grey smudge. */
  box-shadow: none;
  transform: translateY(-160%);
  transition: transform 0.18s ease;
}

.skip-link:focus-visible,
.skip-link:focus {
  box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
  transform: translateY(0);
  outline: 2px solid var(--blue, #0078d4);
  outline-offset: 2px;
}

/* Focused, it used to drop straight on top of the wordmark and the first nav
   link, so the first thing a keyboard visitor saw was two pieces of chrome in
   the same place. The bar steps down out of its way instead: the link sits in
   the strip the bar vacates, nothing overlaps, and the bar comes back the
   moment focus moves on. The skip link is the first element in the body on
   every page, so the sibling combinator reaches both headers. */
.skip-link:focus ~ .nav,
.skip-link:focus ~ .public-nav,
.skip-link:focus ~ * .public-nav {
  transform: translateY(58px);
}

@media (prefers-reduced-motion: reduce) {
  .skip-link {
    transition: none;
  }
}

/* One drawn focus ring for the whole site. Every interactive thing outside the
   skip link was falling through to the UA default, which is a hairline that
   changes colour between browsers and disappears against the blue buttons. This
   is the same ring everywhere: a two pixel brand-ink outline held three pixels
   clear of the element, with a soft halo behind the buttons so it survives on a
   filled blue fill. Declared here rather than per page, and no page restates
   `outline`, so a page's own sheet loading later does not take it away. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
  outline: 2px solid var(--focus-ring, #00437c);
  outline-offset: 3px;
}

.navlinks a:focus-visible,
.footer-col a:focus-visible {
  outline-offset: 2px;
}

.btn:focus-visible,
.plan-cta:focus-visible,
.text-link:focus-visible,
.navlinks a.nav-cta:focus-visible {
  outline: 2px solid var(--focus-ring, #00437c);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgba(0, 120, 212, 0.2);
}

.nav-buy:focus-visible {
  outline: 2px solid var(--focus-ring, #00437c);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgba(0, 120, 212, 0.2);
}

/* ---- Primary button micro-interactions ------------------------------------
   The filled buy and primary buttons lift 2px on hover and a single soft
   sheen crosses them once; pressed, they settle to 98 percent. The sheen is a
   gradient parked off the left edge on ::before, slid across only on the way
   in (no transition on the way out, so it never sweeps back), clipped to the
   button by border-radius: inherit and kept under the label by isolation.
   Colour and geometry stay with each page; this only adds the motion.
   Reduced motion keeps the colour change and drops the lift, sheen and
   press. */
.btn.primary,
a.btn[data-stripe-plan]:not(.btn-ghost),
.plan-card.is-featured .plan-cta,
.nav-buy {
  position: relative;
  isolation: isolate;
}

.btn.primary::before,
a.btn[data-stripe-plan]:not(.btn-ghost)::before,
.plan-card.is-featured .plan-cta::before,
.nav-buy::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, 0.32) 50%, transparent 60%) 130% 0 / 250% 100% no-repeat;
}

.btn.primary:hover,
a.btn[data-stripe-plan]:not(.btn-ghost):hover,
.plan-card.is-featured .plan-cta:hover,
.nav-buy:hover {
  filter: brightness(1.06);
}

@media (prefers-reduced-motion: no-preference) {
  .btn.primary:hover,
  a.btn[data-stripe-plan]:not(.btn-ghost):hover,
  .plan-card.is-featured .plan-cta:hover,
  .nav-buy:hover {
    transform: translateY(-2px);
  }

  .btn.primary:hover::before,
  a.btn[data-stripe-plan]:not(.btn-ghost):hover::before,
  .plan-card.is-featured .plan-cta:hover::before,
  .nav-buy:hover::before {
    background-position: -30% 0;
    transition: background-position 0.8s ease-in-out;
  }

  .btn.primary:active,
  a.btn[data-stripe-plan]:not(.btn-ghost):active,
  .plan-card.is-featured .plan-cta:active,
  .nav-buy:active {
    transform: translateY(0) scale(0.98);
    transition-duration: 0.08s;
  }
}

@media (prefers-reduced-motion: reduce) {
  .btn.primary::before,
  a.btn[data-stripe-plan]:not(.btn-ghost)::before,
  .plan-card.is-featured .plan-cta::before,
  .nav-buy::before {
    display: none;
    transition: none;
  }

  .btn.primary:hover,
  a.btn[data-stripe-plan]:not(.btn-ghost):hover,
  .plan-card.is-featured .plan-cta:hover,
  .nav-buy:hover,
  .btn.primary:active,
  a.btn[data-stripe-plan]:not(.btn-ghost):active,
  .plan-card.is-featured .plan-cta:active,
  .nav-buy:active {
    transform: none;
  }
}

main[id="main"] {
  scroll-margin-top: 80px;
}

main[id="main"]:focus {
  outline: none;
}

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  border-bottom: 1px solid var(--nav-line);
  background: var(--nav-bg);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  backdrop-filter: saturate(160%) blur(14px);
  box-shadow: var(--nav-shadow);
  transition: transform 0.18s ease;
}

@media (prefers-reduced-motion: reduce) {
  .nav {
    transition: none;
  }
}

/* ---- Footer ---------------------------------------------------------------
   One footer on every page, with the same markup: footer() in public-pages.js
   renders it, and site-navigation.mjs bakes it over whatever footer a
   hand-written page carries (.public-footer is still matched for any page
   that has not been rebuilt). It floats on the shared canvas - no panel, no
   wash - with a hairline above it: the brand and one line about the product,
   small icon buttons, three plain link columns, and a quiet legal bar.

   Contrast: every colour here is measured against the canvas it sits on.
   Light --ftr-quiet #536783 is 5.1:1 on #eef2f7; dark --ftr-quiet #8b9cb1 is
   7.0:1 on #070b15. The retired #6f8396 (4.4:1 on navy) is gone. */
:is(.site-footer, .public-footer) {
  --ftr-ink: #121c2b;
  --ftr-muted: #445a78;
  --ftr-quiet: #536783;
  --ftr-line: rgba(15, 23, 42, 0.09);
  --ftr-accent: #0062b8;
  --ftr-chip: rgba(255, 255, 255, 0.7);
  --ftr-chip-line: rgba(15, 23, 42, 0.1);
  --ftr-chip-hover: rgba(0, 103, 192, 0.08);
  --ftr-base: var(--bg, #eef2f7);
  position: relative;
  isolation: isolate;
  border-top: 1px solid var(--ftr-line);
  padding: 64px 0 34px;
  background: transparent;
  color: var(--ftr-muted);
}

/* The end of the canvas. The dot grid and the light pools are fixed layers
   that would otherwise run on behind the footer (plainly visible on a short
   page such as 404 or welcome); this wash fades them out from the hairline
   down, onto the page's own base colour, so the footer reads as the floor of
   the canvas rather than more of it (across the content column; the side
   gutters stay canvas). It only ever adds base colour behind
   the text, so it can raise the footer's contrast but never lower it. */
:is(.site-footer, .public-footer)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--ftr-base) 0%, transparent) 0%,
    color-mix(in srgb, var(--ftr-base) 72%, transparent) 48px,
    color-mix(in srgb, var(--ftr-base) 90%, transparent) 150px,
    color-mix(in srgb, var(--ftr-base) 96%, transparent) 100%);
  /* Only behind the content column: the side gutters stay open canvas, so
     the faint floating cards (site-canvas-float) run on beside the footer
     to the bottom of the page. The text sits inside the 1160px rail, which
     is always under the full wash. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, transparent calc(50% - 640px), #000 calc(50% - 590px), #000 calc(50% + 590px), transparent calc(50% + 640px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, transparent calc(50% - 640px), #000 calc(50% - 590px), #000 calc(50% + 590px), transparent calc(50% + 640px), transparent 100%);
}

html[data-site-theme="dark"] :is(.site-footer, .public-footer) {
  --ftr-base: var(--bg, #070b15);
  --ftr-ink: #e6edf5;
  --ftr-muted: #a1b1c4;
  --ftr-quiet: #8b9cb1;
  --ftr-line: rgba(148, 178, 220, 0.12);
  --ftr-accent: #9fd0ff;
  --ftr-chip: rgba(20, 29, 48, 0.6);
  --ftr-chip-line: rgba(148, 178, 220, 0.16);
  --ftr-chip-hover: rgba(77, 166, 255, 0.14);
}

/* themes.css repaints .site-footer onto a solid --s0 panel under
   body[data-site][data-theme]; the footer belongs to the canvas instead. */
html body[data-site][data-theme] :is(.site-footer, .public-footer),
html body[data-site-header] :is(.site-footer, .public-footer),
html body :is(.site-footer, .public-footer) {
  background: transparent;
  border-top-color: var(--ftr-line);
}

/* The footer shares the top bar's rail (site-header.css), whatever column
   width the page above it uses, so the chrome frames every page the same. */
:is(.site-footer, .public-footer) > .wrap {
  position: relative;
  width: min(1160px, calc(100% - 36px));
  margin-left: auto;
  margin-right: auto;
}

/* Four tracks: the brand block and three link columns. */
:is(.site-footer, .public-footer) .footer-layout {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) repeat(3, minmax(0, 1fr));
  gap: 40px;
  align-items: start;
}

/* Logo and note each take a full row; the icon buttons share the row under
   them. */
:is(.site-footer, .public-footer) .footer-brand {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  align-content: start;
  gap: 10px;
  min-width: 0;
}

:is(.site-footer, .public-footer) .footer-logo {
  justify-self: start;
  color: var(--ftr-ink);
  font-size: 22px;
  font-weight: 850;
  letter-spacing: -0.045em;
  line-height: 1.1;
  text-decoration: none;
}

:is(.site-footer, .public-footer) .footer-logo span { color: var(--ftr-accent); }

:is(.site-footer, .public-footer) .footer-note {
  max-width: 34ch;
  margin: 2px 0 12px;
  color: var(--ftr-muted);
  font-size: 14px;
  line-height: 1.6;
}

:is(.site-footer, .public-footer) .footer-brand .footer-socials {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Small square icon buttons, named by their aria-labels. A label span, on a
   page built before the footer was unified, is kept for assistive tech but
   not drawn. */
:is(.site-footer, .public-footer) .footer-brand .social-btn,
:is(.site-footer, .public-footer) .footer-brand .social-btn.social-btn-label {
  position: relative;
  display: inline-grid;
  place-items: center;
  place-content: center;
  gap: 0;
  width: 38px;
  height: 38px;
  min-height: 0;
  padding: 0;
  border: 1px solid var(--ftr-chip-line);
  border-radius: 11px;
  background: var(--ftr-chip);
  color: var(--ftr-muted);
  font-size: 0;
  line-height: 0;
  text-decoration: none;
  transition: transform 0.2s ease, border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease;
}

:is(.site-footer, .public-footer) .social-btn svg {
  width: 17px;
  height: 17px;
  display: block;
}

/* The Discord glyph sits small inside its 24px box; draw it at the same
   optical size as the envelope. */
:is(.site-footer, .public-footer) .social-btn[aria-label="Discord"] svg {
  width: 21px;
  height: 21px;
}

:is(.site-footer, .public-footer) .social-btn span {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

:is(.site-footer, .public-footer) .footer-brand .social-btn:hover {
  transform: translateY(-2px);
  border-color: var(--ftr-accent);
  color: var(--ftr-accent);
  background: var(--ftr-chip-hover);
}

:is(.site-footer, .public-footer) .footer-col {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

:is(.site-footer, .public-footer) .footer-col h3 {
  margin: 6px 0 14px;
  color: var(--ftr-quiet);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  white-space: normal;
}

:is(.site-footer, .public-footer) .footer-col a {
  display: block;
  margin: 0;
  padding: 5px 0;
  color: var(--ftr-muted);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.45;
  text-decoration: none;
  transition: color 0.18s ease;
}

:is(.site-footer, .public-footer) .footer-col a:hover { color: var(--ftr-ink); }

:is(.site-footer, .public-footer) .footer-col a:focus-visible,
:is(.site-footer, .public-footer) .footer-legal-links a:focus-visible {
  outline-offset: 2px;
  border-radius: 4px;
}

:is(.site-footer, .public-footer) .footer-legal-bar {
  position: relative;
  margin-top: 52px;
  padding-top: 22px;
  border-top: 1px solid var(--ftr-line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 28px;
}

:is(.site-footer, .public-footer) .footer-legal-bar p {
  max-width: none;
  margin: 0;
  color: var(--ftr-quiet);
  font-size: 12.5px;
  line-height: 1.6;
}

:is(.site-footer, .public-footer) .footer-legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
}

:is(.site-footer, .public-footer) .footer-legal-links a {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--ftr-quiet);
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
}

:is(.site-footer, .public-footer) .footer-legal-links a:hover {
  color: var(--ftr-ink);
  background: none;
}

@media (max-width: 980px) {
  :is(.site-footer, .public-footer) .footer-layout {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 36px 28px;
  }

  :is(.site-footer, .public-footer) .footer-brand {
    grid-column: 1 / -1;
  }
}

@media (max-width: 560px) {
  :is(.site-footer, .public-footer) {
    padding: 48px 0 28px;
  }

  /* Three narrow link columns still fit a phone, so Legal does not sit
     alone on a row of its own. */
  :is(.site-footer, .public-footer) .footer-layout {
    gap: 32px 16px;
  }

  :is(.site-footer, .public-footer) .footer-legal-bar {
    margin-top: 40px;
    flex-direction: column;
    align-items: flex-start;
  }
}

@media (max-width: 379px) {
  :is(.site-footer, .public-footer) .footer-layout {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (prefers-reduced-motion: reduce) {
  :is(.site-footer, .public-footer) .footer-brand .social-btn,
  :is(.site-footer, .public-footer) .footer-col a {
    transition: none;
  }

  :is(.site-footer, .public-footer) .footer-brand .social-btn:hover {
    transform: none;
  }
}
