/* ============================================================
   Grizzly Beam — CORE (shared by all pages)
   tokens · reset · primitives · preloader · header · menu · footer
   Substitution: #0C0C0C→Ink · #040508→#121317 · #D2D2D2→stone200
   #D8D8D8→#ECE8E1 · #434343→#5C564E · white CTA→Amber
   Root: 12.8px @≥1440 · 0.889vw @768–1439 · 3.125vw @<768
   ============================================================ */

:root {
  /* --- Brand book rev2 §04 · canonical names from the book --- */
  --grizzly-ink: #16181C;   /* brand book: Grizzly Ink */
  --accent:      #D2952E;   /* brand book: Beam Amber */
  --bg-app:      #FBFAF8;   /* brand book: Paper */

  /* Short aliases used by the rest of the code (same value) */
  --ink: var(--grizzly-ink);
  --paper: var(--bg-app);
  --amber: var(--accent);

  --ink-deep: #121317;      /* Stone 900 */
  --white: #FFFFFF;
  --amber-hover: #C38B2B;   /* brand book §07: hover ~7% darker than Beam Amber */
  --amber-deep: #8A5F13;    /* Amber 800 */
  --navy: #182A40;          /* brand book §04: dark brand surface */
  --stone2: #E8E2DA;
  --stone3: #D8D1C7;
  --tx-l: #ECE8E1;      /* light text on dark (source #D8D8D8) */
  --tx-g: #5C564E;      /* gray text on light (source #434343) */
  --tx-ph: #6B655C;     /* muted text on light; the lightest shade
                           that still passes AA (4.78 on #EEE9E3 in the stats
                           section, 5.53 on #FBFAF8 in the projects rail) */

  /* --- Status · brand book §04 · product UI ONLY, never marketing --- */
  --success: #2F7D5B;
  --warning: #C77D18;
  --danger:  #C0432C;

  /* --- Typography · brand book §05 --- */
  --f-disp: 'Montserrat', 'Helvetica Neue', Arial, sans-serif;
  --f-body: 'Hanken Grotesk', sans-serif;
  --f-mono: 'JetBrains Mono', ui-monospace, monospace;
  --w-display: 700;   /* DISPLAY 60/700 */
  --w-h1: 700;        /* H1 42/700 */
  --w-h2: 700;        /* H2 30/700 */
  --w-h3: 600;        /* H3 20/600 */
  --w-body: 400;      /* BODY 16/400 · SMALL 14/400 */
  --w-body-strong: 600;  /* Hanken max per §05 ("Weights 400 to 600") */

  /* --- Spacing · brand book §06 · 4px base (rem @ root 12.8px) --- */
  --s-4: 0.313rem; --s-8: 0.625rem; --s-16: 1.25rem; --s-24: 1.875rem;
  --s-32: 2.5rem;  --s-48: 3.75rem; --s-64: 5rem;

  /* --- Radius · brand book §06 · 6/10/14/20, no pills --- */
  --r-6: 0.469rem; --r-10: 0.781rem; --r-14: 1.094rem; --r-20: 1.563rem;

  /* --- Elevation · brand book §06 · warm, ink-tinted; never harsh gray --- */
  --shadow-tint: 30, 26, 20;
  --el-sm: 0 1px 2px rgba(var(--shadow-tint), 0.06), 0 1px 3px rgba(var(--shadow-tint), 0.08);
  --el-md: 0 4px 10px rgba(var(--shadow-tint), 0.07), 0 8px 20px rgba(var(--shadow-tint), 0.09);
  --el-lg: 0 12px 24px rgba(var(--shadow-tint), 0.09), 0 24px 48px rgba(var(--shadow-tint), 0.12);
}

/* Root is the only global scale regulator: the whole system is rem-based.
   Both bands are clamped so they do not extrapolate beyond their native width.
   The desktop band is authored at 1440 (0.889vw), the mobile one at 390 (3.125vw).
   The 12px floor only kicks in below 1350, the 13.4px ceiling only above 429,
   so none of the widths the design was drawn at is shifted. */
html { font-size: 12.8px; }
@media (max-width: 1439px) and (min-width: 768px) { html { font-size: clamp(12px, 0.889vw, 12.8px); } }
@media (max-width: 767px) { html { font-size: clamp(11px, 3.125vw, 13.4px); } }

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  font-family: var(--f-disp);
  background: var(--ink);
  color: var(--tx-l);
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

/* ---------- Focus ----------
   Two-tone ring, ink + paper. No single color passes
   WCAG 1.4.11 (>=3.0) against every background on the site:
     background         amber  paper    ink
     navy #182A40        5.59  13.94   1.22
     ink  #16181C        6.84  17.04   1.00
     paper #FBFAF8       2.49   1.00  17.04
     stone2 #E8E2DA      2.02   1.23  13.81
     amber #D2952E       1.00   2.49   6.84
   The two-tone ring solves this without theme detection: on dark it shows paper,
   on light it shows ink. Amber is deliberately avoided - brand book §07/§09
   reserves it for the header CTA (see .pill--solid below).

   GEOMETRY: the box-shadow spread MUST be larger than outline-offset +
   outline-width, otherwise the outer tone gets no band of its own and the ring vanishes on a
   dark element on a light background. The bands are:
     ink 0-2px | paper 2-4px | ink 4-6px
   Measured from rendered pixels: dark button on beige 13.28, light pill
   on navy 13.94, pill next to the amber button 6.84.

   No transition, on purpose - focus must appear instantly.
   border-radius is left alone; outline and box-shadow follow the element's radius. */
:focus-visible {
  outline: 2px solid var(--paper);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--ink);
}
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

.ctr { width: 100%; padding-inline: 2.8125rem; }   /* 36px @1440 */
.body { font-family: var(--f-body); font-size: 1.266rem; line-height: 1.45; font-weight: 400; }
.label {
  font-size: 1.196rem; line-height: 1.2; letter-spacing: 0.18em;   /* brand: Eyebrow */
  text-transform: uppercase; font-weight: 400; display: inline-block;
}
.label--dim { opacity: 0.5; }
.micro {
  font-size: 0.984rem; line-height: 1.25; letter-spacing: 0.18em;   /* brand: Eyebrow */
  text-transform: uppercase; font-weight: 400; display: inline-block;
}
/* Fine print: the sentences under a button. .micro is the eyebrow style (uppercase, 0.18em
   tracking), right for a two-word label and wrong for three sentences, so those moved here
   on 2026-09-21. Sentence case, body face, SMALL 14/400 from brand book §05. Color and
   spacing come from the context class (.wpa__micro, .faqs__risk). */
.fine {
  font-family: var(--f-body); font-size: 1.094rem; line-height: 1.5; font-weight: 400;
  letter-spacing: 0; text-transform: none;
}
/* Was opacity: 0.55 on #16181C, which gave an effective rgb(119,118,116)
   and a ratio of 3.74 on the beige background - AA fail. Solid color instead of transparency. */
.ph { color: var(--tx-ph); }
.star { display: inline-block; transform: translateY(-1px); }

/* underline link (label + line + arrow) */
.ulink {
  position: relative; display: inline-flex; align-items: center; gap: 0.6em;
  font-size: 1.196rem; letter-spacing: 0.01em;   /* brand book §02: sentence case */
  font-weight: var(--w-h3);
  padding-bottom: 0.5em; white-space: nowrap;
}
.ulink::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 1px;
  width: 100%; background: currentColor; opacity: 0.7;
  transform-origin: right; transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.ulink:hover::after { transform: scaleX(0.2); }
.ulink__arr { transition: transform 0.35s ease; }
.ulink:hover .ulink__arr { transform: translateX(0.35em); }

/* per-line heading mask — content enters from below, behind the line's edge.
   the padding/margin pair exists so descenders ("p", "g") are not clipped. */
.wm { display: block; overflow: hidden; padding-bottom: 0.12em; margin-bottom: -0.12em; }
.wm__i {
  display: block; transform: translateY(105%);
  transition: transform 0.9s cubic-bezier(0.33, 0, 0.15, 1);
}
@media (prefers-reduced-motion: reduce) { .wm__i { transform: none; transition: none; } }

/* char-split hover */
.chars-wrap { position: relative; display: inline-flex; overflow: clip; }
.chars-wrap .lyr { display: inline-flex; white-space: pre; }
.chars-wrap .lyr.clone { position: absolute; left: 0; top: 0; }
.chars-wrap .ch { display: inline-block; transition: transform 0.4s cubic-bezier(0.6, 0, 0.2, 1); }
.chars-wrap .clone .ch { transform: translateY(110%); }
.chars-wrap:hover .original .ch, a:hover .chars-wrap .original .ch, button:hover .chars-wrap .original .ch { transform: translateY(-110%); }
.chars-wrap:hover .clone .ch, a:hover .chars-wrap .clone .ch, button:hover .chars-wrap .clone .ch { transform: translateY(0); }

/* ---------- Preloader ---------- */
.pl {
  position: fixed; inset: 0; z-index: 200; background: var(--ink);
  display: grid; place-items: center;
}
.pl__plate {
  display: grid; place-items: center; opacity: 0; transform: scale(0.9);
}
.pl__plate img { width: 11.7rem; height: auto; }
.pl__plus {
  position: absolute; left: 50%; top: 50%;
  font-size: 2.5rem; color: var(--tx-l); opacity: 0;
  transform: translate(-50%, -50%);
}

/* ---------- Header ---------- */
.hdr {
  position: fixed; top: 0; left: 0; right: 0; z-index: 90;
  height: 7.97rem;                                  /* 102px */
  display: flex; align-items: center; justify-content: space-between;
  padding-inline: 2.8125rem;
  pointer-events: none;
}
/* Header backdrop — transparent while the hero lasts, then appears once content starts passing underneath.
   Its tint follows .on-light, the same mechanism that already switches the logo and the Menu button.
   z-index:-1 is inside the .hdr stacking context (z-index:90), so it sits behind its own children
   but still above the page. */
.hdr::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: rgba(18, 19, 23, 0.72);
  -webkit-backdrop-filter: blur(14px) saturate(1.05);
  backdrop-filter: blur(14px) saturate(1.05);
  opacity: 0;
  transition: opacity 0.4s ease, background-color 0.4s ease;
}
.hdr.is-stuck::before { opacity: 1; }
.hdr.on-light::before {
  background: rgba(251, 250, 248, 0.78);
}
/* Menu open: the panel is white and the close button is forced to ink, so the backdrop MUST
   be light regardless of the section — otherwise the ink button is invisible on the dark bar.
   It stays visible so the logo on the left does not get lost in the text below. */
body.menu-open .hdr::before {
  opacity: 1;
  background: rgba(251, 250, 248, 0.86);
}
/* The backdrop is light at all widths while the menu is open -> the logo switches to the ink variant too. */
body.menu-open .hdr__logo-light { display: none; }
body.menu-open .hdr__logo-dark { display: block; }

.hdr > * { pointer-events: auto; }
/* height:auto + max-height, not a fixed height — when flex squeezes the anchor (375px),
   max-width:100% squashes the width and the height follows => the lockup's aspect ratio stays 4.414 */
.hdr__logo { min-width: 0; }
.hdr__logo img { height: auto; max-height: 2.95rem; width: auto; }    /* ~38px (enlarged per feedback) */
.hdr__logo-dark { display: none; }
.hdr.on-light .hdr__logo-dark { display: block; }
.hdr.on-light .hdr__logo-light { display: none; }
.hdr__right { display: flex; align-items: center; gap: 0.8rem; }
.pill {
  display: inline-flex; align-items: center; gap: 0.55rem;
  height: 2.35rem; padding-inline: 1.15rem;
  border-radius: var(--r-10);        /* brand book §06: radius scale, "No pills, ever" */
  font-size: 1.196rem; letter-spacing: 0.01em;   /* brand book §02: sentence case, no uppercase */
  font-weight: var(--w-h3);
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, transform 0.15s ease;
  transform: translate3d(var(--gb-mx, 0px), var(--gb-my, 0px), 0) scale(var(--gb-press, 1));
}
.pill:active { --gb-press: 0.97; }   /* brand book §07: press scales to 0.97 */
/* The only amber element on the page — header CTA "Book a call" (brand book §07/§09) */
.pill--solid { background: var(--amber); color: var(--ink); }
/* Primary actions in ink (brand book §09) — all other CTAs on a light background */
.pill--ink { background: var(--ink); color: var(--paper); }
/* Same role on a dark background — ink would be invisible, so it is inverted */
.pill--onDark { background: var(--tx-l); color: var(--ink); }

/* Buttons in the page BODY per brand book §07 — solid, roomy, sentence case.
   The header deliberately stays compact (pill--solid / pill--line), so this does not touch it.
   Values come from the §06 spacing scale (16 and 32), not estimated from the image. */
.pill--ink, .pill--onDark, .pill--lg {
  height: auto;
  padding: var(--s-16) var(--s-32);
  gap: var(--s-8);
  font-size: 1.25rem;              /* BODY 16 per §05 */
  letter-spacing: 0;
  line-height: 1.25;
}
.pill--line { border: 1px solid rgba(236, 232, 225, 0.35); color: var(--tx-l); }
.pill--line:hover { border-color: rgba(236, 232, 225, 0.8); }
.pill--line { transition-duration: 0.3s, 0.3s, 0.5s, 0.15s; transition-timing-function: ease, ease, var(--gb-cta-ease), ease; }   /* border 0.5s: the "outline lands first" recipe */
.hdr.on-light .pill--line { border-color: rgba(22, 24, 28, 0.35); color: var(--ink); }
.pill__icon { display: inline-flex; flex-direction: column; gap: 3px; }
.pill__icon i { display: block; width: 1.406rem;   /* §08: 18px nav */ height: 1.5px; background: currentColor; transition: transform 0.3s ease; }
.menu-open #menuBtn .pill__icon i:first-child { transform: translateY(2.2px) rotate(45deg); }
.menu-open #menuBtn .pill__icon i:last-child { transform: translateY(-2.2px) rotate(-45deg); }
/* Menu open: the white panel sits below the header (hdr z-index 90 > menu 80), so the close
   button has to switch to the ink variant — otherwise the light text is invisible on white. */
.menu-open #menuBtn { border-color: rgba(22, 24, 28, 0.35); color: var(--ink); }
.menu-open #menuBtn:hover { border-color: rgba(22, 24, 28, 0.8); }

/* ---------- Menu overlay ---------- */
.menu { position: fixed; inset: 0; z-index: 80; visibility: hidden; }
.menu.is-open { visibility: visible; }
.menu__scrim { position: absolute; inset: 0; background: rgba(18, 19, 23, 0.4); opacity: 0; transition: opacity 0.4s ease; }
.menu.is-open .menu__scrim { opacity: 1; }
.menu__panel {
  position: absolute; top: 1.1rem; right: 1.1rem; bottom: 1.1rem;
  width: 30.9rem;                                   /* 396px */
  background: var(--white); color: var(--ink);
  border-radius: var(--r-20);                       /* brand book §06: 20px */
  box-shadow: var(--el-lg);                         /* brand book §06: warm ink-tinted elevation */
  padding: 10rem 3.4rem 2.4rem;                     /* top padding clears the header band */
  display: flex; flex-direction: column; justify-content: space-between;
  transform: translateX(calc(100% + 1.5rem));
  transition: transform 0.65s cubic-bezier(0.65, 0, 0.2, 1);
  overflow-y: auto;
}
.menu.is-open .menu__panel { transform: translateX(0); }
.menu__nav { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 0.4rem; }
.menu__nav a {
  font-size: 2.53rem; line-height: 1.25; letter-spacing: -0.005em; font-weight: var(--w-h2);   /* brand: H2 */
}
.menu__foot { display: flex; flex-direction: column; gap: 2rem; margin-top: 3rem; }
/* CTA in the menu: only <=767, where the header CTA is hidden. Above that
   the header already carries "Book a call", so here it would be a duplicate. */
.menu__cta { display: none; }
.menu__block p { font-family: var(--f-body); font-size: 1.266rem; margin-top: 0.5rem; }
.menu__k { opacity: 0.45; margin-right: 0.4rem; }

/* ---------- Footer ---------- */
/* min-height: 100vh held 900px at 768+ while the content needed ~600,
   so a third of the footer (300px, 34%) was empty. On phones it was the opposite:
   the content exceeded 100vh and the last row touched the very edge, with no
   bottom spacing. The height now follows the content at every width.
   Bottom padding is 6rem versus 8rem at the top — the weight stays at the top, where the
   large wordmark sits, and the close is tighter. */
.ftr {
  position: relative; background: var(--ink-deep); color: var(--tx-l);
  display: flex; flex-direction: column; overflow: clip;
  padding-block: 8rem 6rem;
}
.ftr__smoke {
  position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; display: block;
  /* fallback when the shader does not run (reduced-motion / no WebGL) */
  background: radial-gradient(120% 80% at 50% 100%, rgba(210, 149, 46, 0.1), transparent 60%);
}
.ftr > .ctr { position: relative; z-index: 1; }
.ftr__top { display: flex; justify-content: space-between; }
.ftr__main { display: grid; grid-template-columns: 1fr auto; align-items: start; margin-top: 3.5rem; }
.ftr__display { font-size: 6.75rem; line-height: 0.917; letter-spacing: -0.01em; font-weight: var(--w-display); color: var(--tx-l); }   /* brand: Display/H1 */
.ftr__cta { padding-top: 3.2rem; }
.ftr__info { display: flex; justify-content: space-between; align-items: flex-start; margin-top: 5.5rem; gap: 4rem; }
.ftr__legal { display: flex; flex-direction: column; gap: 3.2rem; }
/* Legal links sit next to the copyright, not in the main navigation. They are underlined
   so they do not rely on color alone to be recognizable as links.
   .ftr__legal is a column with a 3.2rem gap, so the links go into their own container
   with their own spacing instead of inheriting that large one. */
.ftr__legallinks { display: flex; flex-direction: column; gap: 0.55rem; margin-top: -2.55rem; }
.ftr__legallink { text-decoration: underline; text-underline-offset: 0.25em; }
.ftr__legallink[aria-current] { color: var(--amber); }
.ftr__cols { display: flex; gap: 6rem; }
/* One centered line closing the footer: legal name and state of formation, which is what
   a bank's reviewer looks for. Mixed case as written, so deliberately NOT .micro (that is
   the uppercase eyebrow style). rgba(cream, 0.5) on ink-deep measures 4.63:1, over the
   4.5 AA floor for text this size. */
.ftr__bottom { margin-top: 5.5rem; text-align: center; }
.ftr__legalline {
  font-size: 0.984rem; line-height: 1.5; letter-spacing: 0.04em;
  color: rgba(236, 232, 225, 0.5);
}
.ftr__col p { font-family: var(--f-body); font-size: 1.266rem; margin-top: 0.6rem; }
.ftr__k { opacity: 0.45; margin-right: 0.4rem; }
/* The address is the only two-line entry in the column; the second line is indented by the width
   of the key (A. + space) so it does not run in under it. */
.ftr__addr { padding-left: 1.55rem; text-indent: -1.55rem; }
.ftr__nav { display: flex; gap: 6rem; }
.ftr__navlist { list-style: none; display: flex; flex-direction: column; gap: 0.7rem; margin-top: 0.9rem; font-family: var(--f-body); font-size: 1.266rem; }
.ftr__navlist a { opacity: 0.72; transition: opacity 0.3s ease; }
.ftr__navlist a:hover, .ftr__navlist a[aria-current="page"] { opacity: 1; }

/* ---------- Reveal init ---------- */
html.has-js [data-reveal] { opacity: 0; transform: translateY(2.2rem); }

/* ---------- Responsive ---------- */
/* 768-1023: five columns in ftr__info (legal + nav 2 + cols 2) do not fit into 700px
   of available width; .ftr__cols overflowed the viewport by 77px and clipped
   Facebook and Instagram. Legal goes on its own row, gaps tighten from 6rem to 3rem.
   (The Social column was removed 2026-09-08; the rules remain because they still keep
   the remaining columns within 700px.) */
@media (min-width: 768px) and (max-width: 1023px) {
  .ftr__info { flex-wrap: wrap; align-items: flex-start; gap: 4rem 3rem; }
  .ftr__legal { order: 2; flex: 1 0 100%; }
  .ftr__nav, .ftr__cols { gap: 3rem; }
}

/* Touch target minimum 44px (WCAG 2.5.5) for all touch devices, which means
   tablets up to 1023 too. min-height is deliberately in px: a touch target is a physical
   measure, not a typographic one, so it must not depend on the rem scale, which in this
   range varies between 11.25-13.4px. Without this the pills are 26-30px on phones
   and 28px on tablets. */
@media (max-width: 1023px) {
  .pill { min-height: 44px; padding-inline: 1.4rem; }
}
/* The width query catches phones and tablets up to 1023, but an iPad in landscape is
   1024-1194px. pointer: coarse catches every touch device regardless of
   width, while the mouse stays at the designed 28-30px. */
@media (pointer: coarse) {
  .pill { min-height: 44px; padding-inline: 1.4rem; }
}

@media (max-width: 767px) {
  /* Header on phones: the logo was touching the CTA (0px gap at 360/390/414).
     "Book a call" leaves the header and moves into the menu; the hero still carries it
     above the fold. */
  .hdr .pill--solid { display: none; }
  .menu__cta { display: inline-flex; align-self: center; margin-top: 3.4rem; }
  .ftr__display { font-size: 3.9rem; }
  .ftr__main { grid-template-columns: 1fr; }
  .ftr__info { flex-direction: column; gap: 3.5rem; }
  /* .ftr__cols stayed a row and, with .ftr__social in two columns, pushed
     "Facebook" and "Instagram" 41px outside the viewport; body has overflow-x: clip
     so the clipping happened silently, without a scrollbar. Social was removed
     2026-09-08, but the columns still stack into a single column on mobile. */
  .ftr__nav, .ftr__cols { flex-direction: column; gap: 3rem; }
  .menu__panel { width: auto; left: 1.1rem; }
  .hdr { height: 4.77rem; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  html.has-js [data-reveal] { opacity: 1 !important; transform: none !important; }
  .pl { display: none !important; }
}


/* ============================================================================
   CTA MOTION — "COLD SMOKE solid CTA"
   Ported from development.codeacting.com/motion/ (recipe `pipe-solid-cta`,
   composition `wipe-over-solid` + `magnetic-pull`), 2026-09-01.

   ONLY the behavior was ported. The skin stays Grizzly Beam: radius --r-10
   (brand book §06), sentence case (§02), Montserrat — none of pipe's
   uppercase mono or sharp corners. The recipe's own meta.ts warns about this too.

   The colors are not new: the rising fill is exactly the hover color the button already
   had, except that it now comes up from below instead of fading in place.

     .pill--solid   --amber      ->  --amber-hover
     .pill--ink     --ink        ->  --ink-deep
     .pill--onDark  --tx-l       ->  --white

   Timings are from the recipe: fill 0.6s, border 0.5s, both on cubic-bezier(.16,1,.3,1),
   so the border lands before the face.
   ========================================================================= */

.pill { --gb-cta-ease: cubic-bezier(0.16, 1, 0.3, 1); --gb-cta-dur: 0.6s; }

/* Magnet strength per location. Header: two compact buttons 10px apart,
   a full pull would merge them — so it is halved. Page body: buttons stand alone, full pull. */
.pill { --gb-mag: 1; }
.hdr .pill { --gb-mag: 0.5; }

.pill--solid  { --gb-cta-fill: var(--amber-hover); }
.pill--ink    { --gb-cta-fill: var(--ink-deep); }
.pill--onDark { --gb-cta-fill: var(--white); }

/* The fill only travels on buttons that already have a surface. .pill--line is an outline and
   stays an outline — only the magnet and the slower border above apply to it. */
.pill--solid, .pill--ink, .pill--onDark {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.pill--solid::before, .pill--ink::before, .pill--onDark::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;    /* above its own background, below the text */
  background: var(--gb-cta-fill);
  transform: translate3d(0, 101%, 0);
  transition: transform var(--gb-cta-dur) var(--gb-cta-ease);
}
.pill--solid:hover::before,  .pill--solid:focus-visible::before,
.pill--ink:hover::before,    .pill--ink:focus-visible::before,
.pill--onDark:hover::before, .pill--onDark:focus-visible::before { transform: none; }

/* Magnet: recipe.js writes transform every frame, so transform must not have a
   transition — otherwise it fights the loop and lags behind the cursor. The class is set
   by the JS itself, and only on elements it actually bound, so on touch and under
   reduced-motion press-scale keeps its 0.15s. */
.pill.gb-mag-on {
  will-change: transform;
  transition-property: background-color, color, border-color;
}

@media (prefers-reduced-motion: reduce) {
  /* Recipe fallback: the fill does not travel, the face simply appears. */
  .pill--solid::before, .pill--ink::before, .pill--onDark::before { transition: none; }
}


/* ============================================================================
   RING AND DOT CURSOR
   Ported from development.codeacting.com/mouse-animation/ (recipe
   `ring-and-dot-cursor`), 2026-09-01. A 4px dot sits exactly on the pointer,
   the ring floats after it. Over anything clickable the ring jumps from 38 to 66px,
   takes an amber border and fills to twelve percent.

   Sizes and timings are original and untouched: 38 -> 66px, dot 4px, 0.45s on
   cubic-bezier(.16,1,.3,1), lerp 0.16. Only the colors change.

   Colors: pipe used --bone 42% / --ember / --ember-hot. Ours:
     ring at rest    bone 42% on dark, ink 42% on light
     ring active     Beam Amber
     dot             Beam Amber

   Why the ring changes color: pipe is dark all the way down, so a single color
   is enough for it — the recipe itself warns that "a bone ring is invisible on anything
   lighter". The home page alternates dark and light sections, so the ring has to follow
   the background. No new attribute is introduced for this: the four light sections already
   carry data-hdr="light" for the header theme, and that same attribute is read here.

   The native cursor stays visible — pipe does not hide it, so neither do we.
   ========================================================================= */

.gbcur {
  --gbcur-ring: rgba(236, 232, 225, 0.42);     /* --tx-l 42% — restored 2026-09-02 at
                                                  Stevan's request; Paper was tried and reverted */
  --gbcur-ring-light: rgba(22, 24, 28, 0.62);  /* --ink 62%, for light sections.
                                                  42% gave 2.56-2.66, below the WCAG 1.4.11 threshold of 3.0 */
  --gbcur-ring-hot: var(--accent);             /* Beam Amber */
  --gbcur-fill-hot: rgba(210, 149, 46, 0.12);
  --gbcur-dot: var(--accent);
  --gbcur-size: 38px;
  --gbcur-size-hot: 66px;
  --gbcur-dot-size: 4px;
  --gbcur-dur: 0.45s;
  --gbcur-ease: cubic-bezier(0.16, 1, 0.3, 1);

  position: fixed;
  inset: 0;
  z-index: 95;              /* above the header (90), below the preloader (200) */
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s;
}
.gbcur.is-on { opacity: 1; }

.gbcur__ring,
.gbcur__dot {
  position: absolute; top: 0; left: 0;
  border-radius: 50%;
  will-change: transform;
}

/* The ring changes its own size, so the margin has to change with it —
   otherwise it does not grow from the center. */
.gbcur__ring {
  width: var(--gbcur-size);
  height: var(--gbcur-size);
  margin: calc(var(--gbcur-size) / -2) 0 0 calc(var(--gbcur-size) / -2);
  border: 1px solid var(--gbcur-ring);
  transition:
    width var(--gbcur-dur) var(--gbcur-ease),
    height var(--gbcur-dur) var(--gbcur-ease),
    margin var(--gbcur-dur) var(--gbcur-ease),
    border-color var(--gbcur-dur) var(--gbcur-ease),
    background-color var(--gbcur-dur) var(--gbcur-ease);
}
.gbcur.is-light .gbcur__ring { border-color: var(--gbcur-ring-light); }

.gbcur__dot {
  width: var(--gbcur-dot-size);
  height: var(--gbcur-dot-size);
  margin: calc(var(--gbcur-dot-size) / -2) 0 0 calc(var(--gbcur-dot-size) / -2);
  background: var(--gbcur-dot);
}

.gbcur.is-hot .gbcur__ring {
  width: var(--gbcur-size-hot);
  height: var(--gbcur-size-hot);
  margin: calc(var(--gbcur-size-hot) / -2) 0 0 calc(var(--gbcur-size-hot) / -2);
  border-color: var(--gbcur-ring-hot);
  background: var(--gbcur-fill-hot);
}
.gbcur.is-light.is-hot .gbcur__ring { border-color: var(--gbcur-ring-hot); }

@media (pointer: coarse) { .gbcur { display: none; } }


/* ---------- Beam field (background of the "Our services" section and the Book a call modal) ----------
   Used to be in home.css, but home.css only loads on the home page, while the modal opens from
   every page. The markup is in components/BeamField.astro. */
.hs__bg { position: absolute; inset: 0; z-index: 0; }
.hsbg { width: 100%; height: 100%; display: block; }

/* Sliding ACROSS the beams. The first attempt moved translateY inside the rotated
   group, which pushes the beam along its own length — and since the gradient travels with it,
   nothing visibly changes. translateX, in the same rotated space, is perpendicular to
   the beam, so the field actually moves across the background. */
.hsbg__layer--a { animation: hsbgSlideA 20s ease-in-out infinite alternate; }
.hsbg__layer--b { animation: hsbgSlideB 27s ease-in-out infinite alternate; }
@keyframes hsbgSlideA { from { transform: translateX(0); }   to { transform: translateX(178px); } }
@keyframes hsbgSlideB { from { transform: translateX(0); }   to { transform: translateX(-134px); } }

/* Each beam breathes on its own, with its own duration and negative delay, so the field
   is never in the same state twice. Opacity only — the cheapest thing there is. */
.hsbg__beam { animation: hsbgPulse var(--t, 18s) ease-in-out infinite alternate; animation-delay: var(--d, 0s); }
@keyframes hsbgPulse { from { opacity: 0.16; } to { opacity: 1; } }

/* The watermark stays still. The mark is the only thing in the field with a shape that
   is recognizable, so it has to be the reference point — everything else moves around it.

   It is sized as a BOX, not as a width. The section changes both width and height
   (1031px at 2560, 1178px at 390), so setting the width alone broke at both
   ends: on narrow screens the mark was a 179px blob, on wide ones its head stuck out of the
   section. With a set box and the default preserveAspectRatio="xMidYMid
   meet" the mark fits into it along its shorter side and always fits whole. */
.hsbg__mark {
  position: absolute;
  right: -2%;
  top: 50%;
  transform: translateY(-50%);
  width: 52%;
  height: 76%;
  opacity: 0.045;
  pointer-events: none;
}

.hsbg__glow { animation: hsbgGlow 11s ease-in-out infinite alternate; }
@keyframes hsbgGlow { from { opacity: 0.3; transform: translate(-130px, 26px); }
                      to   { opacity: 1;   transform: translate(120px, -22px); } }
