/* =============================================================================
   THE PUBLIC WEBSITE.

   Separate from app.css on purpose. That file dresses an operations console; a
   business owner who lands here must never feel they wandered into a control
   panel, and sharing a stylesheet is how two surfaces slowly converge.

   -------------------------------------------------------------------------
   THE BRIEF, in three words: modern, friendly, personality.

   FRIENDLY IS A SHAPE DECISION BEFORE IT IS A COLOUR ONE. Everything here is
   rounder and softer than a default: pill buttons, 20-28px card radii, shadows
   that are tinted blue rather than grey, and a lot more air than a business site
   usually takes. Sharp corners and tight grids read as competent-but-cold, which
   is the wrong answer for somebody who is nervous about hiring a developer.

   PERSONALITY comes from a handful of deliberate, repeated moves rather than
   from decoration sprinkled everywhere:
     - a hand-drawn marker sweep under one word in the headline;
     - big friendly numbered discs on the process;
     - icon tiles that rotate through a small pastel set instead of all matching;
     - soft organic blobs behind the hero, made of CSS gradients, not images;
     - a warm coral that appears rarely and always means "look here".

   COLOUR COMES FROM THE LOGO. app/static/icons/logo.png is a diagonal gradient
   from #243C85 to #4873D5 with a white mark, so those two blues ARE the brand
   and `--grad-brand` reproduces that exact sweep. The coral is the one addition,
   and it exists because a single-hue blue site reads as corporate rather than
   warm.

   MEASURED, NOT ESTIMATED. Every pair below was computed:
     ink on page        15.4:1     white on blue-600      6.6:1
     muted on page       5.4:1     white on blue-700      8.1:1
     blue-700 on page    7.7:1     blue-200 on blue-800   6.9:1
     ink on coral        6.9:1     coral-deep on peach    4.6:1

   TWO HARD LIMITS worth knowing before editing a colour:
     1. White text needs a blue no lighter than #456ECF (4.79:1). The logo's own
        #4873D5 is 4.48:1 and FAILS, so it is used only for large decorative
        fills where no text sits on it.
     2. The coral is 2.34:1 against white. It is a FILL and never a text colour;
        `--coral-ink` is the readable version.
   ============================================================================= */

@import url('../fonts/site-fonts.css');

/* ============================================================== tokens */
:root {
  /* ---- the logo, sampled ------------------------------------------------ */
  --blue-900: #16264F;
  --blue-800: #243C85;   /* logo, top-left */
  --blue-700: #2F4B98;
  --blue-600: #3859AD;   /* logo, middle */
  --blue-550: #456ECF;   /* the lightest blue that still holds white text */
  --blue-500: #4873D5;   /* logo, bottom-right. Decorative fills only */
  --blue-300: #93B0EE;
  --blue-200: #C3D5F7;
  --blue-100: #E6EDFC;
  --blue-50:  #F3F7FE;

  /* The logo's own sweep, reused wherever the brand needs to feel like itself. */
  --grad-brand: linear-gradient(135deg, #243C85 0%, #4873D5 100%);
  /* Same sweep, capped at --blue-550 so white text on it always clears AA. */
  --grad-cta:   linear-gradient(135deg, #2F4B98 0%, #456ECF 100%);

  /* ---- the one warm accent --------------------------------------------- */
  --coral:      #FF8A4C;   /* fill only, never text */
  --coral-soft: #FFEDE3;
  --coral-ink:  #C2410C;   /* the readable version, for text on peach */
  --mint:       #2FBF9F;
  --mint-soft:  #DFF6F0;
  --mint-ink:   #0F766E;

  /* ---- neutrals, cooled toward the brand hue --------------------------- */
  --page:      #F7F9FE;
  --surface:   #FFFFFF;
  --surface-2: #EEF3FD;
  --ink:       #14203A;
  --ink-muted: #5A6785;
  --ink-onblue:       #FFFFFF;
  --ink-onblue-muted: #C3D5F7;
  --border:        #E2E9F7;
  --border-strong: #CBD8EF;
  --border-onblue: rgba(255, 255, 255, 0.22);

  /* One family, weight contrast instead of a second face. */
  --font: 'Figtree', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* A modular scale at ~1.28, fluid between 375px and 1280px. */
  --t-xs:   0.8125rem;
  --t-sm:   0.9375rem;
  --t-base: 1.0625rem;
  --t-md:   clamp(1.125rem, 1.02rem + 0.44vw, 1.3125rem);
  --t-lg:   clamp(1.375rem, 1.19rem + 0.79vw, 1.75rem);
  --t-xl:   clamp(1.75rem, 1.42rem + 1.4vw, 2.5rem);
  --t-2xl:  clamp(2.125rem, 1.6rem + 2.24vw, 3.25rem);
  --t-3xl:  clamp(2.5rem, 1.7rem + 3.4vw, 4.25rem);

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;
  --section-y: clamp(4rem, 2.6rem + 5.6vw, 7.5rem);

  /* Friendly geometry. Deliberately large. */
  --r-sm:   10px;
  --r:      14px;
  --r-lg:   22px;
  --r-xl:   28px;
  --r-pill: 999px;

  /* Shadows are TINTED BLUE, not grey. A grey shadow on a blue-tinted page reads
     as dirt; a blue one reads as depth. */
  --shadow-sm: 0 2px 6px rgba(36, 60, 133, 0.06);
  --shadow:    0 8px 24px rgba(36, 60, 133, 0.09);
  --shadow-lg: 0 20px 48px rgba(36, 60, 133, 0.14);
  --shadow-cta: 0 10px 24px rgba(47, 75, 152, 0.32);

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-micro: 170ms;
  --dur-reveal: 460ms;
  --stagger: 70ms;

  /* THE STICKY HEADER'S HEIGHT, AS ONE NUMBER.
     An in-page jump (#quote, #faq, the skip link, a /services package) aligns its target with
     the top of the VIEWPORT -- which is behind a sticky bar. Landing on the quote form put
     "Your name" and "Business name" underneath it, and the first field a visitor could read
     was "Mobile number". html's scroll-padding-top reads this token, so the offset and the bar
     it is clearing can never drift apart. */
  --header-h: 74px;

  --z-header: 100;
  --z-chat: 200;
  --z-skip: 300;
}

/* ============================================================== reset + base */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Declared on the SCROLL CONTAINER, not on each target: it covers every id on the site,
     including ones that do not exist yet, and it applies to scrollIntoView and to the browser
     scrolling a focused field into view as well as to fragment links. Do not also put
     scroll-margin-top on the targets -- the two offsets ADD, and the jump overshoots by double.
     The gap on top of --header-h absorbs the bar's 1px bottom border and leaves the destination
     room to breathe rather than sitting welded to the underside of the header. */
  scroll-padding-top: calc(var(--header-h) + var(--space-5));
}

body.site {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-base);
  font-weight: 400;
  line-height: 1.68;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, picture, video { max-width: 100%; height: auto; display: block; }
.site table, .site pre { display: block; max-width: 100%; overflow-x: auto; }

.site h1, .site h2, .site h3, .site h4 {
  font-family: var(--font);
  font-weight: 800;
  letter-spacing: -0.025em;
  line-height: 1.1;
  margin: 0;
  text-wrap: balance;
}
.site h3, .site h4 { letter-spacing: -0.015em; }

.site p { margin: 0 0 var(--space-4); text-wrap: pretty; }
.site p:last-child { margin-bottom: 0; }

/* Links in FLOWING TEXT only.
   Deliberately not a blanket `.site a` rule. A global anchor selector scores (0,1,1), which
   silently outranks every single-class component rule -- .btn, .site-brand, .card -- and the
   symptoms are invisible: a brand link rendered at 1:1 against its own header, and a primary CTA
   that loses its white text. Components own their colour; prose links are styled where prose is. */
.prose a, .faq__answer a, .lede a, .legal-body a {
  color: var(--blue-700);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.on-blue .prose a, .on-blue .lede a { color: var(--blue-200); }

.site :focus-visible {
  outline: 3px solid var(--coral);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

.skip-link {
  position: absolute;
  left: var(--space-4); top: var(--space-4);
  z-index: var(--z-skip);
  padding: var(--space-3) var(--space-5);
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-lg);
  transform: translateY(-150%);
  transition: transform var(--dur-micro) var(--ease-out);
}
.skip-link:focus { transform: translateY(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;
}

/* ============================================================== layout */
.wrap {
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  padding-inline: clamp(1.25rem, 0.9rem + 1.5vw, 2.5rem);
}
.wrap--narrow { max-width: 780px; }

.section { padding-block: var(--section-y); position: relative; }
.section--tight { padding-block: clamp(3rem, 2.2rem + 2.6vw, 4.5rem); }

/* The brand block. Used for the fork, the quote band and the footer -- the three
   places the page changes register. `.band` keeps its old name so no template
   had to change; `.on-blue` is the modifier the child rules key off. */
.band, .on-blue {
  background: var(--grad-brand);
  color: var(--ink-onblue);
}
.band h1, .band h2, .band h3, .on-blue h2, .on-blue h3 { color: var(--ink-onblue); }
.band .lede, .band .muted, .on-blue .lede, .on-blue .muted { color: var(--ink-onblue-muted); }

/* A soft organic glow, so the flat brand block gains depth without an image.
   pointer-events:none because it sits above the background but below the text. */
.band::before, .hero::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(48rem 26rem at 12% -10%, rgba(255, 255, 255, 0.14), transparent 60%),
    radial-gradient(36rem 22rem at 92% 108%, rgba(255, 138, 76, 0.16), transparent 62%);
}
.band > *, .hero > * { position: relative; }

.section-head { max-width: 46rem; margin-bottom: var(--space-7); }
.section-head h2 { font-size: var(--t-2xl); }
.section-head .lede { margin-top: var(--space-4); }

.lede { font-size: var(--t-md); color: var(--ink-muted); max-width: 62ch; }
.muted { color: var(--ink-muted); }
.small { font-size: var(--t-sm); }

/* A small pill above a section heading. Used SPARINGLY -- twice on the homepage,
   not above every section, which is the scaffolding tell. */
.kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.3rem var(--space-4);
  margin-bottom: var(--space-4);
  border-radius: var(--r-pill);
  background: var(--coral-soft);
  color: var(--coral-ink);
  font-size: var(--t-xs);
  font-weight: 700;
}
.on-blue .kicker, .band .kicker {
  background: rgba(255, 255, 255, 0.16);
  color: #FFFFFF;
}

/* The marker sweep. One word in the h1 gets a hand-drawn highlight sitting
   BEHIND the text -- the single most personality-carrying element on the page,
   and it costs one span and no image. */
/* A gradient BACKGROUND on the span, not an absolutely-positioned pseudo-element.
   The pseudo-element version needs z-index:-1 to sit behind the glyphs, which
   puts it behind the section's own background layer and clips it -- and it
   cannot follow the text when the headline wraps across two lines. A background
   band paints behind the glyphs by definition, wraps with them, and needs no
   stacking context at all.

   The stops put the sweep across the lower third of the text, the way a real
   marker lands. box-decoration-break makes it continue correctly onto a second
   line instead of restarting. */
.mark {
  /* The stops are tuned against the h1's 1.1 line-height. Starting lower than ~42%
     puts the band under the glyphs instead of across them, and it stops reading as
     a highlighter and starts reading as a smudge. */
  background-image: linear-gradient(
    to bottom,
    transparent 42%,
    rgba(255, 138, 76, 0.45) 42%,
    rgba(255, 138, 76, 0.45) 90%,
    transparent 90%
  );
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  padding-inline: 0.06em;
}

/* ============================================================== buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 50px;
  padding: var(--space-3) var(--space-6);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-base);
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition:
    transform var(--dur-micro) var(--ease-out),
    box-shadow var(--dur-micro) var(--ease-out),
    background-color var(--dur-micro) var(--ease-out),
    border-color var(--dur-micro) var(--ease-out),
    filter var(--dur-micro) var(--ease-out);
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.btn:active { transform: translateY(0); }

.btn--primary {
  background: var(--grad-cta);
  border-color: transparent;
  color: #FFFFFF;
  box-shadow: var(--shadow-cta);
}
/* brightness() rather than a second gradient: one declaration, no chance of the
   hover sweep drifting out of step with the resting one. */
.btn--primary:hover { filter: brightness(1.08); box-shadow: var(--shadow-cta); }
.btn--primary:active { filter: brightness(0.96); }

.btn--ghost { background: transparent; }
.band .btn--ghost, .on-blue .btn--ghost {
  border-color: var(--border-onblue);
  color: #FFFFFF;
  background: rgba(255, 255, 255, 0.08);
}
.band .btn--ghost:hover, .on-blue .btn--ghost:hover { background: rgba(255, 255, 255, 0.18); }

/* On a blue band the primary CTA inverts: white fill, blue text. A blue gradient
   button on a blue gradient background disappears. */
.band .btn--primary, .on-blue .btn--primary {
  background: #FFFFFF;
  color: var(--blue-800);
  box-shadow: 0 10px 24px rgba(11, 24, 60, 0.28);
}
.band .btn--primary:hover, .on-blue .btn--primary:hover { filter: brightness(1); background: var(--blue-50); }

/* ...but RE-INVERT inside a white card that happens to sit on the band.
   `.band .btn--primary` above turns the CTA white so it shows against the gradient. A white
   button on the white fork panel, or inside the white quote-form card, is then invisible -- white
   fill, white background, only a shadow to hint it exists. These selectors score (0,3,0) and beat
   the (0,2,0) inversion, so the button goes back to the blue gradient wherever the surface under
   it is already white. */
.fork__panel:not(.fork__panel--secondary) .btn--primary,
.quote-form .btn--primary,
.price-card .btn--primary,
.card .btn--primary {
  background: var(--grad-cta);
  color: #FFFFFF;
  box-shadow: var(--shadow-cta);
}
.fork__panel:not(.fork__panel--secondary) .btn--primary:hover,
.quote-form .btn--primary:hover { background: var(--grad-cta); filter: brightness(1.08); }

.btn--lg { min-height: 58px; padding-inline: var(--space-7); font-size: var(--t-md); }
.btn--block { width: 100%; }

/* ============================================================== header */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  /* Translucent + blurred, so content slides UNDER it rather than hitting a wall.
     A fallback solid colour is declared first for browsers without backdrop-filter. */
  background: rgba(247, 249, 254, 0.82);
  backdrop-filter: saturate(1.6) blur(14px);
  -webkit-backdrop-filter: saturate(1.6) blur(14px);
  border-bottom: 1px solid var(--border);
}
.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  min-height: var(--header-h);
}
.site-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--ink);
  text-decoration: none;
  font-weight: 800;
  font-size: var(--t-md);
  letter-spacing: -0.025em;
}
.site-brand img {
  width: 38px; height: 38px;
  border-radius: 11px;
  box-shadow: var(--shadow-sm);
}

.site-nav { margin-left: auto; display: flex; align-items: center; gap: var(--space-1); }
.site-nav a:not(.btn) {
  position: relative;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--r-pill);
  color: var(--ink-muted);
  text-decoration: none;
  font-size: var(--t-sm);
  font-weight: 600;
  transition: color var(--dur-micro) var(--ease-out),
              background-color var(--dur-micro) var(--ease-out);
}
.site-nav a:not(.btn):hover,
.site-nav a:not(.btn)[aria-current="page"] {
  color: var(--blue-700);
  background: var(--blue-100);
}
.site-nav .btn { margin-left: var(--space-3); min-height: 44px; padding-inline: var(--space-5); }

.nav-toggle { display: none; }

@media (max-width: 900px) {
  .site-nav { display: none; }
  .nav-toggle {
    display: inline-flex;
    margin-left: auto;
    min-height: 46px; min-width: 46px;
    align-items: center; justify-content: center;
    background: var(--surface);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--ink);
    cursor: pointer;
  }
  .nav-open .site-nav {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-1);
    position: absolute;
    left: 0; right: 0; top: 100%;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    padding: var(--space-4) clamp(1.25rem, 0.9rem + 1.5vw, 2.5rem) var(--space-6);
  }
  .nav-open .site-nav a:not(.btn) { padding: var(--space-3) var(--space-4); font-size: var(--t-md); }
  .nav-open .site-nav .btn { margin-left: 0; margin-top: var(--space-3); min-height: 52px; }
}

/* ============================================================== hero */
.hero {
  padding-block: clamp(3rem, 2rem + 4.5vw, 6rem) var(--section-y);
  position: relative;
  overflow: hidden;
}
/* Two soft blobs, made of gradients. No image, no request, and they scale to any
   viewport for free. */
.hero::before {
  background:
    radial-gradient(38rem 30rem at 88% -8%, rgba(72, 115, 213, 0.18), transparent 62%),
    radial-gradient(30rem 24rem at -8% 30%, rgba(255, 138, 76, 0.14), transparent 60%);
}
.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 1.6rem + 3.4vw, 4rem);
  align-items: center;
}
@media (min-width: 940px) { .hero__grid { grid-template-columns: 1.1fr 0.9fr; } }

.hero h1 { font-size: var(--t-3xl); }
.hero .lede { margin-top: var(--space-5); font-size: var(--t-md); }
.hero__cta { margin-top: var(--space-6); display: flex; flex-wrap: wrap; gap: var(--space-3); }

.hero__media {
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  background: var(--surface);
  aspect-ratio: 4 / 3;

  /* A whisper of a tilt, so the picture feels hand-placed rather than dropped into a slot.
     `transform` does not affect layout, so the ROTATED bounding box is wider than the grid column
     and the image visibly overhangs the page's content edge -- its right edge landed 3px past the
     header and the trust card below it, which is exactly the kind of near-miss that reads as
     sloppy without being obviously wrong.

     The inset below is the geometry, not a guess. For a box of width w and height 0.75w (the
     aspect-ratio above) rotated by t, the bounding width is w(cos t + 0.75 sin t). At 1.2 degrees
     that is 1.0155w, so it overhangs by 0.78% of the width on each side. Insetting by a hair more
     keeps it inside its column at ANY viewport, because the figure is a ratio rather than a pixel
     count. */
  transform: rotate(1.2deg);
  margin-inline: 0.9%;
}
.hero__media img { width: 100%; height: 100%; object-fit: cover; }

/* ============================================================== trust bar */
/* The card below is pulled UP into the hero by a negative margin, so the section box
   ends on the card's own bottom edge. With no padding here the white card lands flush
   against the blue fork band underneath and the two read as one welded slab. The padding
   is what puts daylight between them. */
.trustbar {
  background: transparent;
  border: 0;
  padding-bottom: clamp(2.75rem, 1.9rem + 3.2vw, 5rem);
}
.trustbar__inner {
  /* A GRID, not a wrapping flex row. As flex the three facts broke onto two lines at desktop and
     left the card lopsided -- 136px tall with one item stranded underneath. Three equal columns
     keep it to a single tidy band, and it collapses to a stack on small screens. */
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4) var(--space-6);
  align-items: center;
  padding: var(--space-5) var(--space-6);
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  margin-top: calc(var(--section-y) * -0.4);
  position: relative;
  z-index: 1;
}
@media (min-width: 820px) {
  .trustbar__inner { grid-template-columns: repeat(3, 1fr); }
  /* Hairline dividers between the three facts, drawn with a border rather than an extra element. */
  .trustbar__item + .trustbar__item {
    padding-left: var(--space-6);
    border-left: 1px solid var(--border);
  }
}
/* STACKED, not baseline-aligned side by side. Beside the figure the label wrapped to two or
   three lines inside a third of the card and dragged the whole band to 142px. Number on top,
   label under it, and the item is the same height whatever the label says. */
.trustbar__item {
  display: block;
  font-size: var(--t-sm);
  color: var(--ink-muted);
  line-height: 1.45;
}
.trustbar__num {
  display: block;
  font-size: var(--t-lg);
  font-weight: 800;
  letter-spacing: -0.03em;
  background: var(--grad-brand);
  /* The ONE place a gradient touches type, and it is a NUMBER, not a heading --
     a decorative figure with its plain-text value right beside it. */
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ============================================================== the fork */
.fork__grid { display: grid; gap: var(--space-5); }
/* UNEQUAL IN WIDTH, EQUAL IN HEIGHT. The 1.35fr/1fr split is the deliberate part -- matched
   cards would tell a visitor the choice is a coin toss. Ragged BOTTOMS are not deliberate:
   the panels carry four and five bullets, so `align-items: start` left one card floating
   short of the other and the pair read as unfinished rather than as a weighted choice. */
@media (min-width: 940px) { .fork__grid { grid-template-columns: 1.35fr 1fr; align-items: stretch; } }

.fork__panel {
  /* A column, so the CTA can be pushed to the floor of whichever panel is taller and the
     two buttons sit on one line instead of wherever their own bullet list happens to end. */
  display: flex;
  flex-direction: column;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-xl);
  padding: clamp(1.75rem, 1.2rem + 2vw, 2.75rem);
  box-shadow: var(--shadow);
  transition: transform var(--dur-micro) var(--ease-out),
              box-shadow var(--dur-micro) var(--ease-out);
}
/* auto absorbs the leftover height. align-self keeps the pill its own width -- a flex
   column child stretches to full width by default, which would make it a bar, not a button. */
.fork__panel > .btn { margin-top: auto; align-self: flex-start; }
.fork__panel:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.fork__panel h3 { font-size: var(--t-xl); color: var(--ink); }

.fork__panel--secondary {
  background: rgba(255, 255, 255, 0.10);
  border: 1.5px solid var(--border-onblue);
  color: var(--ink-onblue);
  box-shadow: none;
}
.fork__panel--secondary h3 { color: var(--ink-onblue); }
.fork__panel--secondary .fork__price { color: var(--ink-onblue); }
.fork__panel--secondary .fork__blurb { color: var(--ink-onblue-muted); }
.fork__panel--secondary .fork__list li { color: var(--ink-onblue-muted); }

.fork__price {
  margin: var(--space-4) 0 var(--space-2);
  font-weight: 800;
  font-size: var(--t-2xl);
  letter-spacing: -0.035em;
  color: var(--blue-700);
  line-height: 1.05;
}
/* The "From" label sits ABOVE the figure, not inline with it. Inline, the eye reads
   "From PHP 45,000" as one long string and the number stops being the loud thing. */
.fork__price-prefix {
  display: block;
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0;
  color: var(--ink-muted);
  margin-bottom: var(--space-1);
}
.fork__panel--secondary .fork__price-prefix { color: var(--ink-onblue-muted); }

/* THE PRICE IS NOT ALWAYS ON A WHITE CARD.
   /services drops the same figure STRAIGHT onto the blue band with no panel under it, where
   blue-700 on the brand gradient is 1.4:1 -- the number simply is not there, and it is the most
   important figure on the site. The muted "From" above it disappears the same way.
   The white panel on the homepage sits INSIDE a band too, so it has to claim the dark figure
   back explicitly; that is the same shape as the .btn--primary re-assertion further up. */
.band .fork__price, .on-blue .fork__price { color: var(--ink-onblue); }
.band .fork__price-prefix, .on-blue .fork__price-prefix { color: var(--ink-onblue-muted); }
.band .fork__panel:not(.fork__panel--secondary) .fork__price { color: var(--blue-700); }
.band .fork__panel:not(.fork__panel--secondary) .fork__price-prefix { color: var(--ink-muted); }

/* The secondary panel's "price" is a PHRASE ("Quoted per project"), not a figure. At
   full price size it reads as a continuation of the heading above it; a step down
   makes it read as an answer to "how much". */
.fork__panel--secondary .fork__price {
  font-size: var(--t-lg);
  letter-spacing: -0.02em;
}
.fork__blurb { margin-top: var(--space-3); color: var(--ink-muted); }

.fork__list { list-style: none; margin: var(--space-5) 0 var(--space-6); padding: 0; display: grid; gap: var(--space-3); }
/* INSIDE A PANEL ONLY. Two auto margins -- one here, one on the CTA -- split the slack in the
   shorter panel evenly above and below the bullets instead of pooling it into one dead gap over
   the button. The minimum gap becomes PADDING: an auto margin resolves to 0 in the taller panel,
   and as a margin that would have collapsed its blurb-to-bullets gap to nothing.
   Scoped to .fork__panel because /services puts this same list in a plain column, where an auto
   margin outranks justify-content and would jam the bullets against the floor. */
.fork__panel .fork__list { margin-top: auto; padding-top: var(--space-5); }

/* /services runs the offer down one column and its bullets down the other, and the offer column
   is far the taller of the two. Left at the top the bullets hang off the heading with a drop of
   empty band under them; centred they sit opposite the price and the blurb, which is the half of
   the copy they are actually answering. */
@media (min-width: 940px) {
  .fork__points { display: flex; flex-direction: column; justify-content: center; }
  .fork__points .fork__list { margin: 0; }
}
.fork__list li { position: relative; padding-left: 2rem; font-size: var(--t-sm); }
/* A friendly filled disc with a tick, not a bare hairline check. */
.fork__list li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.15em;
  width: 1.35rem; height: 1.35rem;
  border-radius: 50%;
  background: var(--mint-soft);
}
.fork__list li::after {
  content: "";
  position: absolute;
  left: 0.42rem; top: 0.5em;
  width: 0.5rem; height: 0.26rem;
  border-left: 2.2px solid var(--mint-ink);
  border-bottom: 2.2px solid var(--mint-ink);
  transform: rotate(-45deg);
}
.fork__panel--secondary .fork__list li::before { background: rgba(255, 255, 255, 0.22); }
.fork__panel--secondary .fork__list li::after { border-color: #FFFFFF; }

/* ============================================================== cards */
.card-grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
}

.card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-micro) var(--ease-out),
              box-shadow var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); border-color: var(--blue-200); }
.card__media { aspect-ratio: 16 / 10; background: var(--surface-2); overflow: hidden; }
.card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 480ms var(--ease-out); }
.card:hover .card__media img { transform: scale(1.05); }
/* Six identical flat-grey rectangles was the single most damaging thing on the page while the
   real screenshots are still to come: it reads as broken rather than as pending. A soft brand
   wash with the label in a pill reads as "not yet", which is the truth. */
.card__media--empty {
  display: grid;
  place-items: center;
  background:
    radial-gradient(14rem 10rem at 30% 20%, rgba(72, 115, 213, 0.16), transparent 62%),
    radial-gradient(12rem 9rem at 78% 88%, rgba(255, 138, 76, 0.18), transparent 60%),
    var(--blue-50);
}
.card__media--empty::after {
  content: attr(data-label);
  padding: 0.25rem var(--space-4);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.82);
  color: var(--ink-muted);
  font-size: var(--t-xs);
  font-weight: 600;
}
.card__body { padding: var(--space-5) var(--space-5) var(--space-6); display: flex; flex-direction: column; gap: var(--space-2); flex: 1; }
.card__eyebrow {
  align-self: flex-start;
  padding: 0.15rem var(--space-3);
  border-radius: var(--r-pill);
  background: var(--blue-100);
  color: var(--blue-700);
  font-size: var(--t-xs);
  font-weight: 700;
}
.card__title { font-size: var(--t-lg); }
.card__text { color: var(--ink-muted); font-size: var(--t-sm); }
.card__foot {
  margin-top: auto;
  padding-top: var(--space-4);
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--blue-700);
}
/* The arrow steps forward on hover. transform only. */
.card__foot { transition: transform var(--dur-micro) var(--ease-out); }
.card:hover .card__foot { transform: translateX(4px); }

.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip {
  display: inline-block;
  padding: 0.3rem var(--space-4);
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1.5px solid var(--border);
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--ink-muted);
  text-decoration: none;
  transition: border-color var(--dur-micro) var(--ease-out),
              color var(--dur-micro) var(--ease-out);
}
a.chip:hover { border-color: var(--blue-300); color: var(--blue-700); }

/* ============================================================== process */
.steps {
  display: grid;
  gap: var(--space-6);
  counter-reset: step;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
}
.step { counter-increment: step; position: relative; padding-top: 4.25rem; }
/* A big friendly disc. This is the ONLY numbered sequence on the site, so it can
   afford to be the loudest thing in its section. */
.step::before {
  content: counter(step);
  position: absolute;
  top: 0; left: 0;
  width: 3rem; height: 3rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--grad-brand);
  color: #FFFFFF;
  font-size: var(--t-md);
  font-weight: 800;
  box-shadow: var(--shadow-cta);
}
.step::after {
  content: "";
  position: absolute;
  top: 1.5rem; left: 3.75rem; right: -0.75rem;
  height: 2px;
  border-top: 2px dashed var(--border-strong);
}
.step:last-child::after { display: none; }
@media (max-width: 760px) { .step::after { display: none; } }
.step h3 { font-size: var(--t-md); }
.step p { margin-top: var(--space-2); color: var(--ink-muted); font-size: var(--t-sm); }

/* ============================================================== why us */
/* TWO columns at desktop, not auto-fit.
   There are exactly four reasons, and auto-fit resolved to three at 1440px -- which left the
   fourth card alone on its own row beside a column of empty space. auto-fit is right when the
   count is unknown; here it is known and it is even, so say so. */
.reasons { display: grid; gap: var(--space-5); grid-template-columns: 1fr; }
@media (min-width: 720px) { .reasons { grid-template-columns: repeat(2, 1fr); } }
.reason {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro) var(--ease-out);
}
.reason:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.reason__icon {
  flex: none;
  width: 52px; height: 52px;
  display: grid; place-items: center;
  border-radius: var(--r);
  background: var(--blue-100);
  color: var(--blue-700);
}
/* Rotate through a small pastel set so four cards do not read as four clones.
   This is most of what "personality" means at card scale. */
.reasons .reason:nth-child(2) .reason__icon { background: var(--coral-soft); color: var(--coral-ink); }
.reasons .reason:nth-child(3) .reason__icon { background: var(--mint-soft); color: var(--mint-ink); }
.reasons .reason:nth-child(4) .reason__icon { background: #EDE7FB; color: #5B3FA8; }
.reason h3 { font-size: var(--t-md); }
.reason p { margin-top: var(--space-2); color: var(--ink-muted); font-size: var(--t-sm); }

/* ============================================================== pricing */
.pricing { display: grid; gap: var(--space-5); grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); }
.price-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro) var(--ease-out);
}
.price-card:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.price-card__name { font-size: var(--t-sm); font-weight: 700; color: var(--ink-muted); }
.price-card__figure {
  margin-top: var(--space-2);
  font-weight: 800;
  font-size: var(--t-xl);
  letter-spacing: -0.035em;
  color: var(--blue-700);
  line-height: 1.1;
}

/* ============================================================== FAQ */
.faq { display: grid; gap: var(--space-3); max-width: 56rem; }
.faq details {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro) var(--ease-out);
}
.faq details:hover { border-color: var(--blue-200); }
.faq details[open] { box-shadow: var(--shadow); border-color: var(--blue-200); }
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 68px;
  padding: var(--space-4) var(--space-5);
  cursor: pointer;
  font-weight: 700;
  font-size: var(--t-md);
  letter-spacing: -0.015em;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
/* A round plus that turns into a minus. Friendlier than a chevron and it reads
   as an affordance at a glance. */
.faq summary::after {
  content: "";
  flex: none;
  width: 30px; height: 30px;
  border-radius: 50%;
  background:
    linear-gradient(var(--blue-700), var(--blue-700)) center / 12px 2.4px no-repeat,
    linear-gradient(var(--blue-700), var(--blue-700)) center / 2.4px 12px no-repeat,
    var(--blue-100);
  transition: transform 240ms var(--ease-out), background-color var(--dur-micro) var(--ease-out);
}
.faq details[open] summary::after {
  transform: rotate(135deg);
  background:
    linear-gradient(var(--blue-700), var(--blue-700)) center / 12px 2.4px no-repeat,
    var(--blue-100);
}
.faq__answer { padding: 0 var(--space-5) var(--space-5); color: var(--ink-muted); }

/* ============================================================== forms */
.form-grid { display: grid; gap: var(--space-5) var(--space-5); }
@media (min-width: 700px) { .form-grid { grid-template-columns: 1fr 1fr; } }
.field--wide { grid-column: 1 / -1; }
/* min-width:0 or a long placeholder can push a grid column past its share and knock the
   pair out of line -- grid items refuse to shrink below their content by default. */
.field { min-width: 0; }

/* THE LABEL AND ITS REQUIRED MARKER ARE ONE ROW.
   They used to be a block <label> with a <span>*</span> after it, and a block element
   forces whatever follows onto the next line: every required field became a text-line
   taller than the optional field beside it, so "Your name" sat lower than "Business
   name" and the whole two-column form stepped out of alignment. Wrapping the pair in a
   flex row is the fix, and every field carries the wrapper -- including the ones with no
   marker -- so the columns cannot drift apart again when a marker is added to one. */
.field__head {
  display: flex;
  align-items: baseline;
  gap: 0.3em;
  margin-bottom: var(--space-2);
}
.field__head label, .field > label, .field > .label {
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1.35;
}
.field__head label { display: inline-block; margin-bottom: 0; }
.field > label, .field > .label { display: block; margin-bottom: var(--space-2); }

/* --coral is a FILL, never a text colour: it is 2.34:1 on white and fails at any size.
   --coral-ink is the readable version, which is what a marker carrying meaning needs. */
.field .req {
  color: var(--coral-ink);
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

.input, .select, .textarea {
  width: 100%;
  min-height: 52px;
  padding: var(--space-3) var(--space-4);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font);
  /* 16px minimum. Below it, iOS Safari zooms the page on focus and the visitor
     has to pinch back out to keep filling the form. */
  font-size: 1rem;
  transition: border-color var(--dur-micro) var(--ease-out),
              box-shadow var(--dur-micro) var(--ease-out);
}
.textarea { min-height: 130px; resize: vertical; line-height: 1.55; padding-block: var(--space-3); }

/* The native select arrow is a different shape, size and colour in every browser, and
   next to 14px-radius inputs it is the one control that looks borrowed from somewhere
   else. appearance:none removes it, so drawing a replacement is mandatory, not optional
   -- without the background chevron the control would have no affordance at all. */
.select {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: calc(var(--space-4) + 1.5rem);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1.5 1.75 6 6.25l4.5-4.5' stroke='%232F4B98' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-4) center;
  background-size: 12px 8px;
}
.select::-ms-expand { display: none; }

/* Placeholders are HINTS, not values. Left at the browser default they sit close enough
   to the real ink that a filled field and an empty one read the same at a glance. */
.input::placeholder, .textarea::placeholder { color: var(--ink-muted); opacity: 0.75; }

.input:hover, .select:hover, .textarea:hover { border-color: var(--blue-300); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--blue-550);
  box-shadow: 0 0 0 4px var(--blue-100);
}
.field__hint { margin-top: var(--space-2); font-size: var(--t-xs); color: var(--ink-muted); }
.band .field__hint, .on-blue .field__hint { color: var(--ink-onblue-muted); }
.field__error { margin-top: var(--space-2); font-size: var(--t-sm); font-weight: 700; color: #B42318; }
.band .field__error, .on-blue .field__error { color: #FFD9CC; }
.input.is-invalid, .select.is-invalid, .textarea.is-invalid { border-color: #B42318; }

/* The quote form sits on the blue band, so its fields need a white card under
   them or the inputs float on the gradient. */
.band .quote-form, .on-blue .quote-form {
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-xl);
  padding: clamp(1.5rem, 1.1rem + 1.8vw, 2.5rem);
  box-shadow: var(--shadow-lg);
}
.band .quote-form label, .on-blue .quote-form label { color: var(--ink); }
.band .quote-form .field__hint, .on-blue .quote-form .field__hint { color: var(--ink-muted); }
.band .quote-form .form-note, .on-blue .quote-form .form-note { color: var(--ink-muted); }
.band .quote-form .btn--primary, .on-blue .quote-form .btn--primary {
  background: var(--grad-cta);
  color: #FFFFFF;
  box-shadow: var(--shadow-cta);
}
.quote-form__foot { margin-top: var(--space-6); display: grid; gap: var(--space-3); }

/* The honeypot. NOT display:none -- a competent bot skips a hidden field. It
   stays in the layout and is moved off-screen. */
.hp-field {
  position: absolute !important;
  left: -9999px !important;
  top: auto;
  width: 1px; height: 1px;
  overflow: hidden;
}

.form-note { font-size: var(--t-sm); }

.flashes { display: grid; gap: var(--space-3); margin-bottom: var(--space-5); }
.flash {
  padding: var(--space-4) var(--space-5);
  border-radius: var(--r);
  border: 1.5px solid var(--border-strong);
  background: var(--surface);
  font-size: var(--t-sm);
  font-weight: 600;
}
.flash--success { border-color: #57A87A; background: #EAF7F0; color: #14603A; }
.flash--danger  { border-color: #E08B84; background: #FDECEA; color: #8A2A20; }
.flash--warning { border-color: #E5B168; background: #FDF3E4; color: #7A5512; }

/* ============================================================== prose */
.prose { max-width: 42rem; font-size: var(--t-md); }
.prose h2 { font-size: var(--t-xl); margin: var(--space-7) 0 var(--space-3); }
.prose h3 { font-size: var(--t-lg); margin: var(--space-6) 0 var(--space-2); }
.prose h4 { font-size: var(--t-md); margin: var(--space-5) 0 var(--space-2); }
.prose ul, .prose ol { padding-left: 1.35rem; margin: 0 0 var(--space-4); }
.prose li { margin-bottom: var(--space-2); }
.prose img { border-radius: var(--r-lg); margin: var(--space-6) 0; box-shadow: var(--shadow); }
.prose blockquote {
  margin: var(--space-6) 0;
  padding: var(--space-5) var(--space-6);
  background: var(--blue-50);
  border-radius: var(--r-lg);
  font-size: var(--t-md);
}
.prose table { width: 100%; border-collapse: collapse; margin: var(--space-5) 0; font-size: var(--t-sm); }
.prose th, .prose td { padding: var(--space-3); border: 1px solid var(--border); text-align: left; }
.prose th { background: var(--surface-2); font-weight: 700; }
.prose code { font-size: 0.9em; background: var(--surface-2); padding: 0.12em 0.4em; border-radius: var(--r-sm); }
.prose pre { padding: var(--space-5); background: var(--blue-900); color: #FFFFFF; border-radius: var(--r-lg); }
.prose pre code { background: none; padding: 0; }

/* ============================================================== footer */
.site-footer {
  background: var(--blue-900);
  color: var(--ink-onblue-muted);
  position: relative;
}
.site-footer__grid {
  display: grid;
  gap: var(--space-6);
  padding-block: var(--space-8) var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}
.site-footer h2 { font-size: var(--t-sm); color: #FFFFFF; margin-bottom: var(--space-4); font-weight: 700; }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.site-footer a { color: var(--ink-onblue-muted); text-decoration: none; font-size: var(--t-sm); }
.site-footer a:hover { color: #FFFFFF; text-decoration: underline; }
.site-footer__bar {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  padding-block: var(--space-5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  align-items: center;
  font-size: var(--t-xs);
}
.site-footer__bar a { font-size: var(--t-xs); }

/* ============================================================== chat dock */
.chat-dock {
  position: fixed;
  right: clamp(0.75rem, 2vw, 1.5rem);
  bottom: clamp(0.75rem, 2vw, 1.5rem);
  z-index: var(--z-chat);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: flex-end;
}
.chat-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 52px;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--r-pill);
  border: 0;
  background: var(--grad-cta);
  color: #FFFFFF;
  font-weight: 700;
  font-size: var(--t-sm);
  text-decoration: none;
  box-shadow: var(--shadow-cta);
  transition: transform var(--dur-micro) var(--ease-out), filter var(--dur-micro) var(--ease-out);
}
.chat-btn:hover { transform: translateY(-3px); filter: brightness(1.08); }
.chat-btn--viber { background: #7360F2; box-shadow: 0 10px 24px rgba(115, 96, 242, 0.36); }

/* ============================================================== motion */
/*
   THE CONTRACT, and the part most likely to be got wrong:

     THE CSS DEFAULT STATE IS FULLY VISIBLE.

   Elements are hidden ONLY under .js-reveal, a class an inline script adds to
   <html> before first paint. So with JavaScript disabled, in a headless render,
   in a text browser, or if the script simply fails, every section is on screen.

   Transitions do not run on a hidden tab, and an IntersectionObserver callback
   that never fires would otherwise ship a page of blank sections.
*/
.js-reveal [data-reveal] { opacity: 0; transform: translateY(14px); }
[data-reveal] {
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
[data-reveal].is-visible { opacity: 1; transform: none; }

/* ============================================================== error page

   404 and 500, wearing the site's face instead of the console's. Deliberately
   quiet: somebody who arrives here is already mildly annoyed, and the job of the
   page is to hand them a way out, not to perform.

   It borrows the hero's blob treatment so an error still looks like this site
   rather than like a fault, and nothing on it animates -- a reveal transition on
   the page that says something went wrong is the wrong kind of flourish. */
.error-page {
  position: relative;
  overflow: hidden;
  text-align: center;
  padding-block: clamp(4rem, 3rem + 5vw, 8rem);
}
.error-page::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(34rem 26rem at 50% -18%, rgba(72, 115, 213, 0.16), transparent 64%),
    radial-gradient(24rem 20rem at 8% 92%, rgba(255, 138, 76, 0.12), transparent 62%);
}
/* The status code, small and set in the brand blue on its own tinted pill. Big
   display numerals were the other option and they make the failure the loudest
   thing on the page, which is backwards -- the heading is what a person needs. */
.error-page__code {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--r-pill);
  background: var(--blue-100);
  color: var(--blue-700);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
}
.error-page__heading {
  font-size: var(--t-2xl);
  margin: var(--space-5) 0 0;
  text-wrap: balance;
}
/* .lede caps itself at 62ch and left-aligns by default; centred text needs the
   box centred too or the last line hangs off to one side. */
.error-page__body { margin: var(--space-4) auto 0; text-wrap: pretty; }
.error-page__links {
  margin-top: var(--space-7);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
}
.error-page__reach { margin-top: var(--space-6); }

@keyframes rise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
.js-reveal .hero__seq > * { opacity: 0; animation: rise 560ms var(--ease-out) forwards; }
.js-reveal .hero__seq > *:nth-child(1) { animation-delay: 0ms; }
.js-reveal .hero__seq > *:nth-child(2) { animation-delay: 90ms; }
.js-reveal .hero__seq > *:nth-child(3) { animation-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  .js-reveal [data-reveal] { opacity: 1; transform: none; }
  .js-reveal .hero__seq > * { opacity: 1; animation: none; }
  .hero__media { transform: none; }
  .card:hover, .fork__panel:hover, .btn:hover, .chat-btn:hover, .reason:hover, .price-card:hover { transform: none; }
  .card:hover .card__media img, .card:hover .card__foot { transform: none; }
}

/* Coarse pointers get no hover lift -- on a touchscreen it fires on tap and
   reads as a glitch rather than as feedback. */
@media (hover: none) {
  .card:hover, .fork__panel:hover, .btn:hover, .chat-btn:hover, .reason:hover, .price-card:hover {
    transform: none;
  }
  .card:hover .card__media img, .card:hover .card__foot { transform: none; }
}

@media print {
  .site-header, .chat-dock, .site-nav, .skip-link { display: none !important; }
  body.site { background: #FFFFFF; }
  .band, .on-blue, .site-footer { background: #FFFFFF !important; color: #000000 !important; }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}
