/* ==========================================================================
   BASE — loaded by every shell page. Tokens, reset, layout, type, buttons and
   the glass material, header and mega-nav, footer, legal, reveal, reduced
   motion, aurora, and the section-join blend bands.

   Order follows site.css's original source order with two exceptions, both
   made during the split: the mobile-sheet reduced-motion suppression sits
   beside the animation it suppresses, and the blend-band rule sits with the
   aurora. Neither now falls after anything sharing its selector AND at-rule
   context, so neither can change a computed value — but note that the split's
   lossless check was an order-insensitive comparison, so base-internal
   reordering is only covered by the rendered diff. Move blocks in here
   sparingly.

   Page files load AFTER this one, so a page rule beats a base rule at equal
   specificity.
   ========================================================================== */
/* ==========================================================================
   Loredo Labs — global stylesheet
   Ported from tailwind.config.js + src/index.css of the Base44 export.
   Every brand value lives in :root below; nothing downstream hardcodes hex.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Self-hosted typefaces — Clash Display + Satoshi (Fontshare, OFL)
   Clash carries every heading, Satoshi every sentence. Both are local; the
   only remaining Google Fonts request is Outfit, for the logo wordmark.
   The landing pages declare the same two faces in landing.css, which does not
   load this file — the declarations are duplicated on purpose, the file paths
   are not.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Clash Display";
  src: url("../fonts/ClashDisplay-500.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
  font-style: normal;
}
@font-face {
  font-family: "Clash Display";
  src: url("../fonts/ClashDisplay-600.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
  font-style: normal;
}
@font-face {
  font-family: "Clash Display";
  src: url("../fonts/ClashDisplay-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
  font-style: normal;
}
@font-face {
  font-family: "Clash Display";
  src: url("../fonts/ClashDisplay-700.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
  font-style: normal;
}

@font-face {
  font-family: "Satoshi";
  src: url("../fonts/Satoshi-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
  font-style: normal;
}
@font-face {
  font-family: "Satoshi";
  src: url("../fonts/Satoshi-Medium.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
  font-style: normal;
}
@font-face {
  font-family: "Satoshi";
  src: url("../fonts/Satoshi-Bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
  font-style: normal;
}

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* Ink (text / dark surfaces) */
  --ink-950: #0b1220;
  --ink-700: #293241;
  --ink-500: #5b6472;
  --ink-300: #9aa3b2;
  /* Body copy on a --surface-deep ground (12.01:1). The value was hardcoded in
     three page sheets before it was a token; servicearea.css now uses this, and
     process.css:430 / services.css:370 still carry the literal and should be
     migrated to it. The system's rule is that the only literal below :root is
     plain #fff. */
  --ink-on-deep: #c3cbdb;

  /* Surfaces */
  --surface-0: #ffffff;
  --surface-50: #f7f8fa;
  --surface-100: #f0f2f5;
  /* The one shared "deep" dark, for the features and contact sections. Same
     hue as --ink-950 (220deg), two stops darker. Replaces the Tailwind zinc
     #09090b and the near-black #050507, which were unrelated to the brand
     navy and to each other, and made the contact -> footer join read as a
     seam. Hero and footer stay on --ink-950. */
  --surface-deep: #070c16;

  /* Lines */
  --border: #e4e7ec;
  --border-dark: #293241;

  /* Brand */
  --blue-brand: #1657ff;
  --blue-hover: #3b74ff;
  --blue-100: #e8efff;
  --blue-accent: #007aff;

  /* Status */
  --success: #1f9d6b;
  --warning: #c6821f;

  /* Type — two roles, two faces, matching the landing pages exactly.
     Clash Display for every heading, Satoshi for every sentence. Space
     Grotesk is left in each stack as the swap-time fallback name only; it is
     no longer requested from Google Fonts, so it resolves to the system stack
     unless the visitor happens to have it installed. */
  --font-heading: "Clash Display", "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --font-body: "Satoshi", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-logo: "Outfit", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-display: "Clash Display", "Space Grotesk", ui-sans-serif, system-ui, sans-serif;

  /* Ribbon field palette (hero background) */
  --rf-white: #ffffff;
  --rf-sky: #78b8f9;
  --rf-ultramarine: #5667ff;
  --rf-iris: #4d2ff9;

  /* Fluid type scale */
  --fs-h1: clamp(2.75rem, 5vw, 4.5rem);
  --fs-h2: clamp(2rem, 3.5vw, 3rem);
  --fs-h3: 1.375rem;
  --fs-lead: clamp(1.125rem, 1.5vw, 1.25rem);
  --fs-body: 1.0625rem;
  --fs-sm: 0.9375rem;
  --fs-xs: 0.8125rem;
  /* A numeric readout inside a dial — a type role the ramp did not have. Sits
     between --fs-h3 and --fs-h2 on purpose: it must dominate its own 15rem dial
     without matching the section heading beside it, which --fs-h2 would. Added
     as a documented step rather than left as a literal in the Process CSS. */
  --fs-readout: clamp(1.75rem, 2.6vw, 2.5rem);

  /* Elevation — one geometric ramp, three steps. Ink and alpha are separate
     tokens so the same three shadows work on dark surfaces, where the light
     alphas are invisible; dark sections override the four values below rather
     than declaring shadows of their own. */
  --shadow-rgb: 11, 18, 32;
  --shadow-a1: 0.04;
  --shadow-a2: 0.06;
  --shadow-a3: 0.10;
  --shadow-card: 0 1px 2px rgba(var(--shadow-rgb), var(--shadow-a1));
  --shadow-card-md: 0 8px 24px rgba(var(--shadow-rgb), var(--shadow-a2));
  --shadow-card-lg: 0 16px 40px rgba(var(--shadow-rgb), var(--shadow-a3));

  /* Glass — the shared button material. One set of values for light surfaces
     here, re-declared for dark sections alongside the shadow ink below, so a
     button is frosted the same way everywhere without each section inventing
     its own translucency. Alphas are deliberately high on the primary fills:
     a button whose label drops below 4.5:1 is not a style choice. */
  --glass-blur: 14px;
  --glass-border: rgba(11, 18, 32, 0.14);
  --glass-hi: rgba(255, 255, 255, 0.65);   /* inner top edge, the "lit" rim */
  --glass-fill: rgba(255, 255, 255, 0.55);
  --glass-fill-hover: rgba(255, 255, 255, 0.78);

  /* Radii */
  --radius-xs: 6px;            /* the shadcn rounded-md the ported components use */
  --radius-sm: 8px;
  --radius: 12px;
  --radius-md: 16px;
  --radius-lg: 24px;
  --radius-pill: 999px;

  /* Layout */
  --max-content: 1280px;
  --max-prose: 60ch;
  --nav-h: 3.5rem;   /* header-3 h-14 */
  --gutter: 20px;
  /* Shared vertical rhythm. Every full-width content section uses this, so
     section spacing scales together at the one breakpoint instead of each
     section carrying its own pair of numbers. */
  --section-y: 88px;
  /* Depth of the blend band at each section join. */
  --fade-h: clamp(88px, 9vw, 160px);

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-snap: cubic-bezier(0.16, 1, 0.3, 1);

  /* shadcn surface tokens for .ui-btn and the nav components. These live here
     rather than only on .hdr so a .ui-btn works anywhere on the page; .hdr,
     .hdr--over-dark, .mobilesheet and .hero3__actions override them locally. */
  --ui-bg: #ffffff;
  --ui-fg: var(--ink-950);
  --ui-muted-fg: var(--ink-500);
  --ui-border: var(--border);
  --ui-input: var(--border);
  --ui-accent: var(--surface-50);
  --ui-popover: #ffffff;
  --ui-card: rgba(255, 255, 255, 0.6);
  --ui-primary: rgba(22, 87, 255, 0.90);         /* --blue-brand, frosted */
  --ui-primary-hover: rgba(59, 116, 255, 0.95);  /* --blue-hover, frosted */
  --ui-primary-fg: #ffffff;
  --ui-ring: var(--blue-brand);
}

@media (min-width: 768px) {
  :root { --gutter: 40px; --section-y: 120px; }
}

/* The dark-field end of the elevation ramp. Same three shadows, re-inked:
   on a dark surface a 4% shadow is not there at all. .vid__facade is included
   because the play button sits on the dark poster, not on the light section. */
.hero3,
.feat,
.ct,
.site-footer,
.vid__facade {
  --shadow-rgb: 5, 9, 38;
  --shadow-a1: 0.22;
  --shadow-a2: 0.35;
  --shadow-a3: 0.50;

  /* Glass on a dark surface: the frost is a white veil rather than a white
     wash, and the rim carries the edge the hairline border no longer can. */
  --glass-border: rgba(255, 255, 255, 0.22);
  --glass-hi: rgba(255, 255, 255, 0.28);
  --glass-fill: rgba(255, 255, 255, 0.10);
  --glass-fill-hover: rgba(255, 255, 255, 0.18);
}
@media (min-width: 1024px) {
  :root { --gutter: 80px; }
}

/* --------------------------------------------------------------------------
   2. Reset / base
   -------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 16px);
}

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--ink-950);
  background: var(--surface-0);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

h1 { font-size: var(--fs-h1); line-height: 1.05; letter-spacing: -0.02em; }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); line-height: 1.2; }

p { text-wrap: pretty; }

a { color: inherit; text-decoration: none; }

img, svg, video, iframe {
  display: block;
  max-width: 100%;
  height: auto;
}

/* The rule above sets display on svg, which would otherwise beat the `hidden`
   attribute's UA `display: none`. Keep `hidden` authoritative. */
[hidden] { display: none !important; }

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

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

/* --------------------------------------------------------------------------
   3. Layout helpers
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--max-content);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: 64px; }
@media (min-width: 768px) { .section { padding-block: 96px; } }
@media (min-width: 1024px) { .section { padding-block: 128px; } }

.section--tight { padding-block: 64px; }
@media (min-width: 768px) { .section--tight { padding-block: 80px; } }

.section--alt { background: var(--surface-50); }
.section--dark { background: var(--ink-950); color: #fff; }
.section--bordered { border-top: 1px solid var(--border); }

.prose-width { max-width: var(--max-prose); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 12px 20px;
  background: var(--ink-950);
  color: #fff;
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   4. Shared text elements
   -------------------------------------------------------------------------- */
.eyebrow {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-300);
  margin-bottom: 16px;
}

.lead {
  font-size: var(--fs-lead);
  line-height: 1.5;
  color: var(--ink-500);
  max-width: var(--max-prose);
}

.muted { color: var(--ink-500); }

/* Diagonal brand stroke (DiagonalStroke.jsx) */
.stroke {
  display: inline-block;
  width: 4px;
  background: var(--blue-brand);
  transform: skewX(-18deg);
  border-radius: 1px;
}
.stroke--sm { height: 16px; }
.stroke--lg { height: 28px; }

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-sm);
  padding: 14px 24px;
  border-radius: var(--radius-md);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: inset 0 1px 0 var(--glass-hi);
  transition: background-color 0.2s var(--ease-out),
              transform 0.2s var(--ease-out),
              box-shadow 0.2s var(--ease-out),
              border-color 0.2s var(--ease-out);
}

.btn--lg { padding: 16px 32px; font-size: 1.0625rem; }

.btn .btn__arrow { transition: transform 0.2s var(--ease-out); }
.btn:hover .btn__arrow { transform: translateX(2px); }

.btn--primary {
  background: rgba(22, 87, 255, 0.90);
  color: #fff;
}
.btn--primary:hover {
  background: rgba(59, 116, 255, 0.95);
  transform: translateY(-1px);
  box-shadow: var(--shadow-card-md);
}
.btn--primary:active { transform: scale(0.98); }

.btn--secondary {
  color: var(--ink-950);
  background: var(--glass-fill);
}
.btn--secondary:hover {
  border-color: var(--ink-300);
  transform: translateY(-1px);
}
.btn--secondary:active { transform: scale(0.98); }

.btn--sm { padding: 9px 16px; font-size: 0.8125rem; border-radius: var(--radius); }

.btn--ghost {
  color: var(--ink-700);
  background: var(--glass-fill);
}
.btn--ghost:hover { color: var(--ink-950); border-color: var(--ink-300); }
.btn--ghost:active { transform: scale(0.98); }


/* --------------------------------------------------------------------------
   6. Logo mark
   -------------------------------------------------------------------------- */
.logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  transition: transform 0.3s var(--ease-out);
}
.logo:hover { transform: scale(1.02); }
.logo:active { transform: scale(0.98); }

.logo__img { width: 32px; height: 32px; object-fit: contain; }

.logo__text { display: flex; flex-direction: column; line-height: 1; }

.logo__name {
  font-family: var(--font-logo);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ink-950);
}

.logo__sub {
  font-family: var(--font-logo);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--blue-brand);
}

.logo--light .logo__name { color: #fff; }
.logo--light .logo__img {
  background: #fff;
  border-radius: var(--radius-xs);
  padding: 3px;
}

/* --------------------------------------------------------------------------
   7. shadcn Button port
   A literal translation of the supplied shadcn/button buttonVariants, used by
   header-3 and hero-3. Separate from the site's own `.btn` (footer, legal
   pages), which follows a different scale.
   -------------------------------------------------------------------------- */
.ui-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  height: 2.5rem;              /* h-10 */
  padding: 0.5rem 1rem;        /* px-4 py-2 */
  font-family: var(--font-body);
  font-size: 0.875rem;         /* text-sm */
  font-weight: 500;
  /* Glass: rounded, frosted, hairline-bordered. The radius moves off the
     spec's rounded-md (6px) onto the shared 16px so .ui-btn and .btn are one
     shape — they sit on the same pages and read as two controls otherwise. */
  border-radius: var(--radius-md);
  background: var(--ui-primary);
  color: var(--ui-primary-fg);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: inset 0 1px 0 var(--glass-hi);
  transition: background-color 0.15s var(--ease-out), color 0.15s var(--ease-out),
              border-color 0.15s var(--ease-out), box-shadow 0.15s var(--ease-out);
}
.ui-btn:hover { background: var(--ui-primary-hover); }
.ui-btn:focus-visible {
  outline: 2px solid var(--ui-ring);
  outline-offset: 2px;
}

/* The secondary is the true glass: it is mostly backdrop. */
.ui-btn--outline {
  background: var(--glass-fill);
  color: var(--ui-fg);
  border-color: var(--glass-border);
}
.ui-btn--outline:hover { background: var(--glass-fill-hover); color: var(--ui-fg); }

.ui-btn--icon { width: 2.5rem; height: 2.5rem; padding: 0; }

.ui-btn svg { flex: none; }
.ui-btn__icon-start { width: 1rem; height: 1rem; margin-right: 0.5rem; }  /* size-4 mr-2 */
.ui-btn__icon-end   { width: 1rem; height: 1rem; margin-left: 0.5rem; }   /* size-4 ml-2 */

/* --------------------------------------------------------------------------
   8. header-3
   sticky, transparent until scrolled past 10px, then a blurred translucent
   surface with a bottom border. h-14 bar inside a max-w-5xl container. The
   desktop nav appears at md; below that it is the toggle + sheet.
   -------------------------------------------------------------------------- */
.hdr {
  position: sticky;
  top: 0;
  z-index: 50;
  width: 100%;
  border-bottom: 1px solid transparent;
  transition: background-color 0.25s var(--ease-out),
              border-color 0.25s var(--ease-out);

  /* shadcn surface tokens, scoped. The over-dark variant below swaps only
     these — no structural rule is conditional on the page. */
  --ui-bg: #fff;
  --ui-fg: var(--ink-950);
  --ui-muted-fg: var(--ink-500);
  --ui-border: var(--border);
  --ui-input: var(--border);
  --ui-accent: var(--surface-50);
  --ui-popover: #fff;
  --ui-card: rgba(255, 255, 255, 0.6);
  --ui-primary: rgba(22, 87, 255, 0.90);         /* --blue-brand, frosted */
  --ui-primary-hover: rgba(59, 116, 255, 0.95);  /* --blue-hover, frosted */
  --ui-primary-fg: #fff;
  --ui-ring: var(--blue-brand);
}

.hdr.is-scrolled {
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom-color: var(--ui-border);
}

/* The home hero is a dark ribbon field and the header is transparent over it
   until scroll, so the token set inverts. Colours only. */
.hdr--over-dark:not(.is-scrolled) {
  --ui-fg: #fff;
  --ui-muted-fg: rgba(255, 255, 255, 0.72);
  --ui-border: rgba(255, 255, 255, 0.22);
  --ui-input: rgba(255, 255, 255, 0.32);
  --ui-accent: rgba(255, 255, 255, 0.12);
  --ui-bg: transparent;
  --ui-ring: #fff;

  /* Ink-tinted glass, not white-tinted. Everywhere else the glass veil is
     white because the label is either dark (light sections) or the backdrop is
     reliably dark (features/contact/footer). Here the label is white and the
     backdrop is the ribbon field, whose pale end is literally #ffffff — a
     white veil there measures 2.31:1. Same material, same blur, border, rim
     and radius; only the tint of the veil inverts, which is what keeps the
     label legible across the whole gradient (6.0:1 at the palest point,
     14.6:1 at the deepest). */
  --glass-fill: rgba(6, 10, 38, 0.42);
  --glass-fill-hover: rgba(6, 10, 38, 0.55);
  --glass-border: rgba(255, 255, 255, 0.28);
  --glass-hi: rgba(255, 255, 255, 0.20);
}

.hdr__nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  max-width: 64rem;            /* max-w-5xl */
  height: 3.5rem;              /* h-14 */
  margin-inline: auto;
  padding-inline: 1rem;        /* px-4 */
}

.hdr__left { display: flex; align-items: center; gap: 1.25rem; }  /* gap-5 */

/* Wordmark slot: hover:bg-accent rounded-md p-2 */
.hdr__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem;
  border-radius: var(--radius-xs);
  color: var(--ui-fg);
  transition: background-color 0.15s var(--ease-out);
}
.hdr__brand:hover { background: var(--ui-accent); }
.hdr__brand-img { display: block; width: 18px; height: 18px; border-radius: 4px; }
.hdr__brand-text {
  font-family: var(--font-logo);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1;
}
.hdr__brand-text span { font-weight: 500; opacity: 0.72; }

/* --- Desktop navigation menu -------------------------------------------- */
.navmenu { display: none; position: relative; }
@media (min-width: 768px) { .navmenu { display: flex; } }

.navmenu__list {
  display: flex;
  align-items: center;
  gap: 0.25rem;                /* space-x-1 */
  list-style: none;
}

.navmenu__item { position: static; }

/* navigationMenuTriggerStyle: h-9 w-max px-4 py-2 text-sm font-medium rounded-md */
.navmenu__trigger,
.navmenu__plain {
  display: inline-flex;
  align-items: center;
  height: 2.25rem;
  padding: 0.5rem 1rem;
  font-family: var(--font-body);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ui-fg);
  background: transparent;
  border-radius: var(--radius-xs);
  transition: background-color 0.15s var(--ease-out);
}
.navmenu__trigger:hover,
.navmenu__plain:hover,
.navmenu__trigger[aria-expanded="true"] { background: var(--ui-accent); }

.navmenu__chev {
  position: relative;
  top: 1px;
  margin-left: 0.25rem;
  width: 0.75rem;
  height: 0.75rem;
  transition: transform 0.3s var(--ease-out);
}
.navmenu__trigger[aria-expanded="true"] .navmenu__chev { transform: rotate(180deg); }

/* Viewport: centred under the bar, scales in from 90%. */
.navmenu__content {
  position: absolute;
  top: 100%;
  left: 50%;
  margin-top: 0.375rem;        /* mt-1.5 */
  transform: translateX(-50%);
  z-index: 60;
  overflow: hidden;
  background: var(--ui-popover);
  color: var(--ui-fg);
  border: 1px solid var(--ui-border);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-card-lg);

  --ui-accent: var(--surface-50);
  --ui-muted-fg: var(--ink-500);
  --ui-fg: var(--ink-950);
  --ui-border: var(--border);
}
.navmenu__content[hidden] { display: none; }
.navmenu__content {
  opacity: 0;
  transform: translateX(-50%) scale(0.9);
  transition: opacity 0.2s var(--ease-out), transform 0.2s var(--ease-out);
}
.navmenu__content.is-open { opacity: 1; transform: translateX(-50%) scale(1); }

.navmenu__inner { padding: 0.25rem 0.375rem 0.25rem 0.25rem; }

/* Product panel: ul grid w-lg grid-cols-2 gap-2 rounded-md border p-2 shadow */
.navpanel__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5rem;
  width: 32rem;                /* w-lg */
  padding: 0.5rem;
  list-style: none;
  background: var(--ui-popover);
  border: 1px solid var(--ui-border);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-card);
}

/* Company panel: grid w-lg grid-cols-2 gap-2 — bordered card list + plain list */
.navpanel__split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5rem;
  width: 32rem;
}

.navpanel__stack {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;                 /* space-y-2 */
  padding: 0.5rem;
  list-style: none;
  background: var(--ui-popover);
  border: 1px solid var(--ui-border);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-card);
}

.navpanel__aside {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.75rem;            /* p-3 */
  list-style: none;
}

.navpanel__foot {
  padding: 0.5rem;
  font-size: 0.875rem;
  color: var(--ui-muted-fg);
}
.navpanel__foot a { font-weight: 500; color: var(--ui-fg); }
.navpanel__foot a:hover { text-decoration: underline; }

/* ListItem: w-full flex flex-row gap-x-2 rounded-sm p-2 hover:bg-accent */
.listitem {
  display: flex;
  flex-direction: row;
  gap: 0.5rem;
  width: 100%;
  padding: 0.5rem;
  border-radius: 0.25rem;
  transition: background-color 0.15s var(--ease-out);
}
.listitem:hover { background: var(--ui-accent); }

.listitem__icon {
  display: grid;
  place-items: center;
  flex: none;
  aspect-ratio: 1;
  width: 3rem;                 /* size-12 */
  background: var(--ui-card);
  border: 1px solid var(--ui-border);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-card);
  color: var(--ui-fg);
}
.listitem__icon svg { width: 1.25rem; height: 1.25rem; }  /* size-5 */

.listitem__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}
.listitem__title { font-size: 0.875rem; font-weight: 500; color: var(--ui-fg); }
.listitem__desc { font-size: 0.75rem; line-height: 1.45; color: var(--ui-muted-fg); }

/* Secondary list rows: flex p-2 rounded-md items-center gap-x-2 hover:bg-accent */
.navrow {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ui-fg);
  border-radius: var(--radius-xs);
  transition: background-color 0.15s var(--ease-out);
}
.navrow:hover { background: var(--ui-accent); }
.navrow svg { width: 1rem; height: 1rem; flex: none; }

/* --- Header actions ------------------------------------------------------ */
.hdr__actions { display: none; align-items: center; gap: 0.5rem; }
@media (min-width: 768px) { .hdr__actions { display: flex; } }

.hdr__toggle { display: inline-flex; }
@media (min-width: 768px) { .hdr__toggle { display: none; } }

/* MenuToggleIcon: the long path's dash pattern morphs while the whole glyph
   counter-rotates 45deg, so the bar and the hook resolve into a cross. */
.menutoggle__svg {
  width: 1.25rem;
  height: 1.25rem;
  transition: transform 300ms ease-in-out;
}
.menutoggle__morph {
  stroke-dasharray: 12 63;
  transition: stroke-dasharray 300ms ease-in-out, stroke-dashoffset 300ms ease-in-out;
}
[aria-expanded="true"] .menutoggle__svg { transform: rotate(-45deg); }
[aria-expanded="true"] .menutoggle__morph {
  stroke-dasharray: 20 300;
  stroke-dashoffset: -32.42px;
}

/* --- Mobile sheet -------------------------------------------------------- */
.mobilesheet {
  position: fixed;
  top: 3.5rem;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 40;
  display: none;
  flex-direction: column;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);

  /* The sheet is a light surface on every page, so it must not inherit the
     over-dark token set from the header it lives inside. */
  --ui-bg: #fff;
  --ui-fg: var(--ink-950);
  --ui-muted-fg: var(--ink-500);
  --ui-border: var(--border);
  --ui-input: var(--border);
  --ui-accent: var(--surface-50);
  --ui-card: rgba(255, 255, 255, 0.6);
}
.mobilesheet.is-open { display: flex; }
@media (min-width: 768px) { .mobilesheet { display: none !important; } }

.mobilesheet__inner {
  width: 100%;
  height: 100%;
  padding: 1rem;
  animation: sheetZoomIn 200ms var(--ease-out);
}

@keyframes sheetZoomIn {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: none; }
}

/* The suppression sits beside the animation it suppresses, and in BASE, because
   the mobile sheet is the site-wide header's. It briefly lived in home.css
   during the per-page split. That was a filing error with NO rendering
   consequence — the global `*` reduced-motion rule further down already caps
   every animation at 0.01ms, so the sheet was never actually animating. It is
   fixed anyway: a page file must not hold a rule the whole site leans on, and
   here the only thing making it harmless was a second rule it does not know
   about. */
@media (prefers-reduced-motion: reduce) {
  .mobilesheet__inner { animation: none; }
}

.mobilesheet__content {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 0.5rem;
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.mobilesheet__nav { display: flex; flex-direction: column; gap: 0.5rem; width: 100%; }
.mobilesheet__group { font-size: 0.875rem; color: var(--ink-500); }
.mobilesheet__actions { display: flex; flex-direction: column; gap: 0.5rem; }
.mobilesheet__actions .ui-btn { width: 100%; }

body.is-locked { overflow: hidden; }

/* The header is in flow, so pages do not offset for it. */
.site-main { display: block; }

/* --------------------------------------------------------------------------
   8. Footer
   -------------------------------------------------------------------------- */
/* The footer is the tail of the contact section, not a separate slab: same
   --surface-deep ground, same --section-y rhythm, same ambient. Because the
   two now share a background there is no colour step at the join at all — the
   continuity is carried by the glow below rather than by a divider. */
.site-footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--surface-deep);
  color: #fff;
  padding-block: var(--section-y);
}

/* Content sits above the ambient layers. */
.site-footer > .container { position: relative; }

/* The shared .aurora masks to its TOP-right corner, which is correct for every
   other section but puts this one's brightest point exactly on the contact
   join — the ambient would appear from nothing at the boundary, which is the
   seam under another name. Anchored to the bottom-right here so it fades
   *toward* the join and the two sections meet on flat --surface-deep. */
.site-footer > .aurora {
  --aurora-mask: radial-gradient(ellipse at 100% 100%, #000 22%, transparent 88%);
  opacity: 0.34;
}

/* Continuation of the contact glow-horizon. Same technique — oversized arcs,
   heavy blur, and a mask arc painted LAST so only a lit rim survives — but two
   lit layers instead of three, no white core, and pushed mostly above the
   section so only the bottom of the rim shows. Contact holds the light source;
   this is the far end of the same field, which is what stops the ambient
   dying at the section boundary. Entrance reuses contact's glowIn keyframes;
   the global prefers-reduced-motion rule collapses it to its end state. */
.site-footer__glow {
  position: absolute;
  top: 0;
  left: 50%;
  z-index: -1;
  width: 165%;
  height: 30rem;
  /* Seated far enough down that the arcs' blur has fully fallen off before the
     section's top edge. The section must clip horizontally — the arc is 165%
     wide and would otherwise push the page sideways — and anything still lit
     when it reaches that clip becomes a hard line exactly at the join, which
     is the seam this section exists to remove.

     Do NOT solve that with mask-image on this element: a mask rasterises the
     group at its border box, and the arcs are scaled 1.16-1.21 with a 38-46px
     blur, so they get cut into visible rectangles. Move the glow, don't mask
     it. */
  transform: translate(-50%, 12%);
  pointer-events: none;
  isolation: isolate;
  animation: glowIn 2s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Defined in BASE because the footer that uses it is on every page. It lived
   in home.css during the per-page split, and an animation-name that resolves
   to nothing is silent: the footer glow simply never faded in on the twelve
   non-home shell pages, with no error anywhere. Keyframes must sit wherever
   their earliest reference does. */
@keyframes glowIn {
  from { transform: translateY(-100%) scaleY(1.5); opacity: 0; filter: blur(15px); }
  to   { transform: translateY(-50%) scaleY(1);    opacity: 1; filter: blur(0px); }
}


.site-footer__arc {
  position: absolute;
  inset: 0;
  border-radius: 100%;
}
.site-footer__arc--mid  { transform: scale(1.16); background: var(--rf-ultramarine); filter: blur(46px); opacity: 0.26; }
.site-footer__arc--deep { transform: scale(1.21); background: var(--rf-iris);        filter: blur(38px); opacity: 0.22; }
/* Masks to the section's own colour, not to black — same reason as
   .glow__arc--mask over in contact. */
.site-footer__arc--mask { transform: scale(1.14); background: var(--surface-deep);   filter: blur(56px); }

.site-footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 48px;
}
@media (min-width: 768px) {
  .site-footer__grid {
    grid-template-columns: 2fr repeat(4, 1fr);
    gap: 32px;
  }
}

.site-footer__tagline {
  margin-top: 16px;
  font-size: 0.875rem;
  color: var(--ink-300);
}

/* NAP phone. Sits in the brand column under the tagline rather than in a link
   column: it is contact information, not navigation, and anything reading the
   footer for NAP should find it beside the locality line, not among the links. */
.site-footer__phone {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  font-size: 0.9375rem;
  font-weight: 600;
  color: #fff;
  text-decoration: none;
}
.site-footer__phone svg { width: 16px; height: 16px; color: var(--ink-300); }
.site-footer__phone-label {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-500);
}
.site-footer__phone:hover .site-footer__phone-num { text-decoration: underline; }

.site-footer__col-title {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-500);
  margin-bottom: 16px;
}

.site-footer__links { display: flex; flex-direction: column; gap: 4px; }

/* Glass hover chips, on the shared radius and fill tokens. The negative inline
   start margin keeps the label flush with its column title while giving the
   link a real target rather than text-only. */
.site-footer__link {
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  margin-inline-start: -10px;
  font-size: 0.875rem;
  color: var(--ink-300);
  transition: color 0.2s var(--ease-out), background-color 0.2s var(--ease-out);
}
.site-footer__link:hover {
  color: #fff;
  background: var(--glass-fill);
}
.site-footer__link:focus-visible {
  outline: 2px solid var(--glass-border);
  outline-offset: 2px;
}

.site-footer__bottom {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 64px;
  padding-top: 32px;
  border-top: 1px solid var(--glass-border);
  font-size: var(--fs-xs);
  color: var(--ink-300);
}
@media (min-width: 768px) {
  .site-footer__bottom { flex-direction: row; }
}

/* --------------------------------------------------------------------------
   9. Persistent CTAs
   -------------------------------------------------------------------------- */
.mobile-cta {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 40;
  display: block;
  padding: 12px var(--gutter) calc(12px + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-top: 1px solid var(--border);
  transform: translateY(110%);
  transition: transform 0.3s var(--ease-out);
}
.mobile-cta.is-visible { transform: translateY(0); }
.mobile-cta .btn { width: 100%; justify-content: center; }

@media (min-width: 768px) { .mobile-cta { display: none; } }

.estimator-fab {
  position: fixed;
  right: 24px;
  bottom: 96px;
  z-index: 40;
  display: none;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  background: var(--ink-950);
  color: #fff;
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-card-lg);
  transform: translateY(16px);
  opacity: 0;
  transition: transform 0.3s var(--ease-out), opacity 0.3s var(--ease-out),
              background-color 0.2s;
}
.estimator-fab.is-visible { opacity: 1; transform: translateY(0); }
.estimator-fab:hover { background: var(--blue-brand); }

@media (min-width: 768px) { .estimator-fab { display: flex; } }

/* --------------------------------------------------------------------------
   10. Legal documents (privacy policy, terms)
   -------------------------------------------------------------------------- */
.legal__inner { max-width: 48rem; }

.legal__eyebrow {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--blue-brand);
  margin-bottom: 16px;
}

.legal h1 { font-size: clamp(2rem, 4vw, 3rem); margin-bottom: 16px; }

.legal__meta {
  font-size: var(--fs-sm);
  color: var(--ink-500);
  margin-bottom: 32px;
}

.legal h2 {
  font-size: 1.375rem;
  margin-top: 40px;
  margin-bottom: 16px;
}

.legal p,
.legal li {
  font-size: 1rem;
  line-height: 1.7;
  color: var(--ink-700);
}

.legal p + p { margin-top: 12px; }

.legal ul {
  margin-top: 12px;
  padding-left: 1.25rem;
  display: grid;
  gap: 6px;
}

.legal a { color: var(--blue-brand); }
.legal a:hover { text-decoration: underline; }

.legal__lead { margin-bottom: 12px; }

/* Translation disclaimer — Spanish legal pages only */
.legal__notice {
  margin: 32px 0 8px;
  padding: 16px 20px;
  background: var(--surface-50);
  /* 1px, not the 3px this shipped with. A thick colored border down one side of
     a callout is the most recognisable tell of generated UI, and it is the one
     pattern the craft floor bans outright above 1px. The aside still reads as an
     aside: the --surface-50 ground carries it, and the hairline marks the edge
     in the same weight every other rule on this site uses. Ricardo's call,
     2026-08-12 — keep the accent, lose the slab. */
  border-left: 1px solid var(--blue-brand);
  border-radius: var(--radius);
}

.legal__notice p {
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-500);
}

.legal__back {
  margin-top: 64px;
  padding-top: 32px;
  border-top: 1px solid var(--border);
}

.legal__back a { font-weight: 500; }

/* --------------------------------------------------------------------------
   11. Scroll reveal
   Replaces the ScrollReveal.jsx wrapper. Any element can opt in with
   `data-reveal`, and stagger with `style="--reveal-delay: 160ms"`.
   -------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 500ms var(--ease-out) var(--reveal-delay, 0ms),
              transform 500ms var(--ease-out) var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* If JS never runs, content must still be visible. */
.no-js [data-reveal] {
  opacity: 1;
  transform: none;
}

/* --------------------------------------------------------------------------
   12. Reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  [data-reveal] { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   20. Aurora background
   The 21st.dev aurora-background ported out of Tailwind arbitrary values into
   plain CSS. Same mechanism: two stacked repeating-linear-gradients at 100deg,
   blurred, with a second copy on ::after that scrolls its background-position
   from 50% to 350% over 60s and composites with mix-blend-mode: difference.
   The radial mask from the top-right corner is the component's showRadialGradient.

   Tailwind's colour variables are written out literally — blue-500 #3b82f6,
   indigo-300 #a5b4fc, blue-300 #93c5fd, violet-200 #ddd6fe, blue-400 #60a5fa.

   Two host requirements: the section needs `position: relative` and
   `overflow: hidden`, and its content needs to sit above z-index -2.
   -------------------------------------------------------------------------- */
.aurora {
  position: absolute;
  inset: -10px;
  z-index: -2;
  pointer-events: none;
  overflow: hidden;
  opacity: 0.62;

  /* isolation + contain are load-bearing for frame rate, not for looks. The
     ::after blends with `difference`, and without isolation its blend backdrop
     is everything painted beneath it in the section — on the hero that is the
     entire .dash panel subtree, which then re-composites every frame. Measured:
     32fps without these, 60fps with. */
  isolation: isolate;
  contain: paint;

  --aurora: repeating-linear-gradient(
    100deg,
    #3b82f6 10%, #a5b4fc 15%, #93c5fd 20%, #ddd6fe 25%, #60a5fa 30%
  );
  --white-gradient: repeating-linear-gradient(
    100deg, #fff 0%, #fff 7%, transparent 10%, transparent 12%, #fff 16%
  );
  --dark-gradient: repeating-linear-gradient(
    100deg, #000 0%, #000 7%, transparent 10%, transparent 12%, #000 16%
  );

  background-image: var(--white-gradient), var(--aurora);
  background-size: 300%, 200%;
  background-position: 50% 50%, 50% 50%;
  filter: blur(10px) invert(1);

  /* Two mask layers, intersected. The radial is the original shape. The linear
     one fades the ambient in over --fade-h from the section's top edge, and it
     is what makes the blend bands actually work: without it the aurora is at
     full strength on the first pixel of the section, so the band above ramps
     to flat --surface-deep while the section itself starts at deep + ambient,
     and the step just moves rather than disappearing. Same root cause as the
     footer's mask anchor, generalised to every join. */
  --aurora-mask: radial-gradient(ellipse at 100% 0%, #000 22%, transparent 88%);
  -webkit-mask-image: var(--aurora-mask), linear-gradient(180deg, transparent 0%, #000 var(--fade-h));
          mask-image: var(--aurora-mask), linear-gradient(180deg, transparent 0%, #000 var(--fade-h));
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

.aurora::after {
  content: "";
  position: absolute;
  background-image: var(--white-gradient), var(--aurora);
  /* Twice as wide as the host, translated by half its own width, instead of a
     100%-wide layer with its background-position animated. Same sweep, same
     60s loop — but background-position repaints a blurred, difference-blended
     layer every single frame, where transform is compositor-only. This is the
     single biggest frame-budget win on the page (29 -> 60fps during scroll).
     background-attachment: fixed went with it: it forces a repaint on scroll. */
  top: 0;
  left: 0;
  bottom: 0;
  width: 200%;
  background-size: 100% 100%, 50% 100%;
  mix-blend-mode: difference;
  will-change: transform;
  animation: aurora 60s linear infinite;
}

/* Dark hosts skip the invert and mask with black instead of white. */
.aurora--dark {
  background-image: var(--dark-gradient), var(--aurora);
  filter: blur(10px);
  opacity: 0.48;
}
.aurora--dark::after {
  background-image: var(--dark-gradient), var(--aurora);
}

@keyframes aurora {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Off-screen sections drop out of the frame rather than merely pausing: pausing
   was measured to save nothing, since a paused layer still costs its GPU render
   pass every frame.

   Be honest about what this buys during scroll: MEASURED AS NO IMPROVEMENT.
   Paired A/B, cull vs pause, 3 runs each: 5.66-6.08 render passes per frame vs
   6.19-6.33, dropped frames 10.3/18.6/13.1% vs 11.0/12.0/14.8% — inside the
   run-to-run noise. It is kept because it is strictly less compositing work
   where nothing is visible (six of these exist, and it matters more on a phone
   than on the machine this was profiled on), not because it fixed scrolling.
   The aurora's real scroll cost is per-VISIBLE-layer; see initAurora().

   The observer's margin is far larger than --fade-h's 160px ceiling, so the
   cull boundary never lands inside the top-fade region the blend bands depend
   on, and joins cannot pop. */
.aurora.is-idle { display: none; }

@media (prefers-reduced-motion: reduce) {
  .aurora::after { animation: none; }
}

/* Hosts: each needs a positioning context and a clip, and content above the
   aurora's z-index. */
.why,
.vid,
.abt,
.feat,
.section,
.legal { position: relative; overflow: hidden; }

/* `:not(.aurora)` matters: without it this rule overrides the layer's own
   `position: absolute`, `inset: -10px` stops sizing it, and it collapses to
   zero height — present in the DOM, invisible on screen. */
.why > *:not(.aurora),
.vid > *:not(.aurora),
.abt > *:not(.aurora),
.feat > *:not(.aurora),
.ct > *:not(.aurora),
.section > *:not(.aurora),
.legal > *:not(.aurora) { position: relative; }


/* This is in BASE, not home.css, because the selector list spans pages: .why,
   .feat, .vid and .ct are the homepage, but .svc and .svcf are Services. The
   per-page split first put the whole rule in home.css, which silently removed
   both of Services blend bands in EN and ES. Splitting the selector across two
   files instead would work and was rejected — two copies of one gradient is
   exactly the drift the single-source-of-truth rule exists to prevent. A rule
   whose selectors span pages belongs here. */
/* --------------------------------------------------------------------------
   Section joins — blend bands
   Each band sits in the LOWER edge of the upper section and ramps to the
   colour of the section below, so a cut reads as a transition rather than a
   line. It lives on the upper side deliberately: the lower side of three of
   these joins is a dark section whose own top carries something (contact's
   glow horizon, features' heading), and a band there would wash it out.

   The band always falls inside the section's --section-y bottom padding, so
   it never overlaps content, and it is a static gradient on an existing
   element — no extra node, no paint cost during scroll.

   Not applied to hero -> why: the hero's bottom is the dashboard panel, which
   bleeds close to the edge, and a white ramp there reads as the panel
   dissolving rather than as a section join.
   -------------------------------------------------------------------------- */
.why::after,
.feat::after,
.vid::after,
.ct::after,
.svc::after,
.svcf::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--fade-h);
  z-index: 3;
  pointer-events: none;
  /* Eased, not a two-stop linear ramp. A linear gradient has a discontinuous
     derivative at its endpoint, which the eye reads as a band right where the
     join is — the exact artefact this is meant to remove. These stops
     approximate a smoothstep so the ramp arrives at the section colour with
     zero slope. Colours are carried as RGB triplets for the same reason the
     shadow ramp is: the stops need alpha, not a solid. */
  background: linear-gradient(
    180deg,
    rgba(var(--fade-rgb), 0)    0%,
    rgba(var(--fade-rgb), 0.06) 22%,
    rgba(var(--fade-rgb), 0.24) 44%,
    rgba(var(--fade-rgb), 0.56) 65%,
    rgba(var(--fade-rgb), 0.85) 83%,
    rgba(var(--fade-rgb), 1)    100%
  );
}

/* ===================================================== liquid-glass buttons

   Ported from the 21st.dev/shadcn `LiquidButton` to plain CSS, same treatment
   hero-3 and header-3 received: nothing installed, no React, no Tailwind.

   Appended deliberately rather than edited into the .ui-btn / .btn blocks
   above, so the ported button spec stays readable as what it was and this
   material reads as what it is. Two departures from the source, both
   load-bearing and both already proven on the landing pages:

   1. No `backdrop-filter: url("#container-glass")`. SVG filter refs in
      backdrop-filter resolve only in Firefox; Chrome and Safari drop them.
   2. Fills are KEPT. The source default is `bg-transparent`; over the hero's
      ribbon field a translucent button measures 2.31:1 (see "Buttons are
      glass" above). The rim goes on top of the fill, never instead of it.  */

.ui-btn,
.btn {
  border-radius: var(--radius-pill);
  box-shadow:
    0 0 8px rgba(0, 0, 0, 0.03),
    0 2px 6px rgba(0, 0, 0, 0.08),
    inset 3px 3px 0.5px -3.5px rgba(255, 255, 255, 0.09),
    inset -3px -3px 0.5px -3.5px rgba(255, 255, 255, 0.85),
    inset 1px 1px 1px -0.5px rgba(255, 255, 255, 0.60),
    inset -1px -1px 1px -0.5px rgba(255, 255, 255, 0.60),
    inset 0 0 6px 6px rgba(255, 255, 255, 0.09),
    inset 0 0 2px 2px rgba(255, 255, 255, 0.06);
  transition:
    transform 200ms cubic-bezier(0.23, 1, 0.32, 1),
    filter 200ms ease,
    background-color 200ms ease;
}

/* On light surfaces the white inner rims are invisible and the dark ones do
   the work, so the stack inverts rather than disappearing. */
.site-main .ui-btn:not(.ui-btn--primary),
.btn:not(.btn--primary) {
  box-shadow:
    0 0 6px rgba(0, 0, 0, 0.03),
    0 2px 6px rgba(0, 0, 0, 0.08),
    inset 3px 3px 0.5px -3px rgba(0, 0, 0, 0.20),
    inset -3px -3px 0.5px -3px rgba(0, 0, 0, 0.16),
    inset 1px 1px 1px -0.5px rgba(255, 255, 255, 0.70),
    inset 0 0 6px 6px rgba(255, 255, 255, 0.10);
}

@media (hover: hover) and (pointer: fine) {
  .ui-btn:hover,
  .btn:hover { filter: brightness(1.10); transform: scale(1.02); }
}

.ui-btn:active,
.btn:active { transform: scale(0.97); filter: brightness(0.94); }

@media (prefers-reduced-motion: reduce) {
  .ui-btn, .btn { transition: filter 200ms ease, background-color 200ms ease; }
  .ui-btn:hover, .btn:hover, .ui-btn:active, .btn:active { transform: none; }
}

