/* ==========================================================================
   Echelon Systems LLC — site styles
   Single stylesheet for every page. Page-specific rules are scoped with a
   class on <body>:
     .page-main   index.html, services.html, contact.html
     .page-doc    shared narrow reading layout (terms, privacy, thanks, 404)
       .page-legal    terms.html, privacy.html
       .page-thanks   thanks.html
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Fonts

   Self-hosted, same origin, nothing fetched from a font host. Both faces are
   OFL 1.1, committed under assets/fonts/ with the license.

   The fallback faces carry metric overrides pulled from the real font tables
   (Space Grotesk 984/-292 per 1000upm, JetBrains Mono 1020/-300) plus a
   size-adjust, so the swap from system font to webfont does not move the
   layout. Without these, font-display:swap costs CLS.
   -------------------------------------------------------------------------- */
@font-face{
  font-family:"Space Grotesk";
  src:url("/assets/fonts/space-grotesk-latin.woff2") format("woff2");
  font-weight:400 700;
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:"Space Grotesk Fallback";
  src:local("Helvetica Neue"),local("Arial"),local("Liberation Sans");
  ascent-override:98.4%;
  descent-override:29.2%;
  line-gap-override:0%;
  size-adjust:108%;   /* measured: brings the fallback within 0.1% of the real face */
}
@font-face{
  font-family:"JetBrains Mono";
  src:url("/assets/fonts/jetbrains-mono-subset.woff2") format("woff2");
  font-weight:400 600;
  font-style:normal;
  font-display:swap;
}
/* No metric-adjusted fallback for the mono. A local()-only face reports as an
   error when none of the names resolve, and mono is only used on small
   uppercase labels where the swap shift is sub-pixel. The stack falls through
   to ui-monospace instead. */

/* --------------------------------------------------------------------------
   1. Tokens

   The palette was rebuilt in Step 03c. It is built outward from the logo mark
   gradient (#3B6BFF to #22D3EE on #0B1120) rather than around an unrelated
   accent, so the mark sits in the page instead of on top of it.

   Spacing sits on a 4px grid, split into a component ramp (--s-*) and a
   page-band ramp (--band-*) for vertical rhythm. Values still marked
   `off-ramp` are the ones Step 01 could not snap without accumulating a
   visible shift. Step 05b tracks them.
   -------------------------------------------------------------------------- */
:root{
  /* surfaces, deepest to most raised */
  --bg:#070910;
  --surface-1:#0c101a;
  --surface-2:#121826;
  --surface-3:#1a2233;

  /* borders */
  --border:#1e2637;
  --border-2:#2b3651;
  --border-hover:#3d4b6d;
  --border-card-hover:#384463;
  --rule:#2b3651;

  /* text */
  --text:#eef2f8;
  --body-dim:#c6cfdd;
  --muted:#96a2b8;
  --label-dim:#7c89a1;
  --placeholder:#6e7b91;

  /* brand ramp, from the logo mark */
  --brand-blue:#3B6BFF;
  --brand-cyan:#22D3EE;
  --accent:#6f9bff;
  --accent-strong:#4d7cff;
  --on-accent:#060a16;
  --focus-ring:rgba(111,155,255,.28);
  --grad-brand:linear-gradient(120deg,var(--brand-blue),var(--brand-cyan));
  /* Same gradient lifted at the blue end. Measured: #3B6BFF on the card
     surface is 4.00:1, which fails AA for small text. #6f9bff is 6.59:1.
     Used anywhere the gradient carries text below 24px. */
  --grad-brand-lift:linear-gradient(120deg,var(--accent),var(--brand-cyan));
  --glow-blue:rgba(59,107,255,.20);
  --glow-cyan:rgba(34,211,238,.14);

  --header-bg:rgba(7,9,16,.72);

  /* type */
  --font-display:"Space Grotesk","Space Grotesk Fallback",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --font-body:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;
  --font-mono:"JetBrains Mono",ui-monospace,Menlo,Consolas,monospace;

  /* component spacing */
  --s-1:8px;
  --s-2:12px;
  --s-3:16px;
  --s-4:20px;
  --s-5:24px;
  --s-6:32px;
  --s-7:40px;

  /* page bands, vertical rhythm */
  --band-1:44px;
  --band-2:52px;
  --band-3:64px;
  --band-4:72px;
  --band-5:84px;
  --band-6:88px;
  --band-7:96px;
  --band-8:104px;

  /* radii */
  --r-sm:8px;
  --r-md:10px;
  --r-lg:14px;
  --r-pill:100px;

  /* motion */
  --t:.15s;
  --t-slow:.5s;
  --ease-out:cubic-bezier(.16,.84,.44,1);

  /* measure */
  --max:1400px;
  --max-wide:1400px;
  --max-prose:85ch;
  --max-form:640px;
  --bar-h:72px;
}

/* --------------------------------------------------------------------------
   2. Reset and base
   -------------------------------------------------------------------------- */
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}

/* Skip link — first focusable element on every page. Parked off-screen with a
   transform (not display:none, which would drop it from the tab order) and
   slid into view on focus. */
.skip{
  position:fixed;top:var(--s-2);left:var(--s-2);z-index:30;
  background:var(--surface-2);color:var(--text);
  border:1px solid var(--accent);border-radius:var(--r-md);
  padding:var(--s-2) var(--s-3);font-size:.92rem;text-decoration:none;
  transform:translateY(calc(-100% - var(--s-5)));
  transition:transform var(--t) ease;
}
.skip:focus{transform:translateY(0)}
main:focus{outline:none}

/* One explicit focus ring for everything interactive, rather than relying on
   the UA default. The default renders as a blue ring that is hard to see
   against the darker surfaces this palette introduced. Accent at 2px with an
   offset clears every surface level. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
label.nav-toggle:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible{
  outline:2px solid var(--accent);
  outline-offset:3px;
  border-radius:var(--r-sm);
}
/* Inputs already carry a focus ring as a box-shadow, so they only need the
   border change; a second outline on top of it reads as a double ring. */
input:focus-visible,textarea:focus-visible{outline:2px solid transparent;outline-offset:0}
body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:var(--font-body);
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
}

/* --------------------------------------------------------------------------
   3. Layout
   -------------------------------------------------------------------------- */
.wrap{max-width:var(--max);margin:0 auto;padding:0 var(--s-6)}

/* Prose sits on its own measure inside the wide shell. Without this the
   1400px wrap would run body copy to an unreadable line length. */
.band,.sub,.page-doc p,.page-doc li{max-width:var(--max-prose)}

/* Narrow reading pages. Widened from 760 but deliberately short of the
   marketing pages, because these are long-form reading. */
.page-doc{--max:920px;line-height:1.7}

/* No .bleed helper is needed after all. The hero and every <section> are
   already full-width blocks with a contained .wrap inside, so the page reads
   as bands without an extra class. Noted because the commit that added the
   helper said the graphic system would use it. It did not. */

/* --------------------------------------------------------------------------
   4. Header
   -------------------------------------------------------------------------- */
header{
  border-bottom:1px solid var(--border);
  position:sticky;top:0;
  background:var(--header-bg);
  backdrop-filter:saturate(160%) blur(10px);
  z-index:10;
}
.bar{
  display:flex;align-items:center;justify-content:space-between;
  height:var(--bar-h);gap:var(--s-3);
  max-width:var(--max-wide);margin:0 auto;padding:0 var(--s-6);
}
.brand{
  display:flex;align-items:center;gap:var(--s-2);
  font-family:var(--font-display);
  font-weight:700;font-size:1.06rem;letter-spacing:-.02em;
  color:var(--text);text-decoration:none;
}
.mark{
  width:30px;height:30px;border-radius:var(--r-sm);flex:0 0 auto;
  display:block;border:1px solid var(--border);
}
.nav-cta{
  color:var(--muted);text-decoration:none;font-size:.92rem;
  border:1px solid var(--border);padding:var(--s-1) 15px;   /* off-ramp */
  border-radius:var(--r-sm);white-space:nowrap;
  transition:color var(--t) ease,border-color var(--t) ease;
}
.nav-cta:hover{color:var(--text);border-color:var(--border-hover)}

/* Main navigation.
   The mobile disclosure is a checkbox rather than <details>/<summary>: Chrome
   now renders closed details content through ::details-content with
   content-visibility:hidden, so the usual "reveal it again at desktop width"
   CSS leaves the links zero-sized and unfocusable. The checkbox behaves the
   same in every browser. It stays focusable (opacity, not display:none) so
   the menu opens from the keyboard with Space. */
.nav{position:relative;display:flex;align-items:center}
/* Removed from the tab order entirely at desktop width, where the toggle is
   hidden — otherwise it is a focusable control that does nothing. */
.nav-check{display:none}
.nav-toggle{display:none}
.nav-list{list-style:none;margin:0;padding:0;display:flex;align-items:center;gap:var(--s-5)}
.nav-list a:not(.nav-cta){
  color:var(--muted);text-decoration:none;font-size:.92rem;white-space:nowrap;
  border-bottom:1px solid transparent;
  transition:color var(--t) ease,border-color var(--t) ease;
}
.nav-list a:not(.nav-cta):hover{color:var(--text)}

/* Current-page treatment. Every nav item is a real page after the 03b split,
   so aria-current drives this on all of them. The .page-main hook that used to
   highlight Services on the home page is gone with it. */
.nav-list a[aria-current="page"]{color:var(--text);border-bottom-color:var(--accent)}
/* Contact is the button, so its whole border goes accent rather than just the
   bottom edge that the rule above would otherwise set. */
.nav-list .nav-cta[aria-current="page"]{border-color:var(--accent);color:var(--text)}

/* --------------------------------------------------------------------------
   4b. Graphic system

   One idea carried across every page: a signal grid. The product is message
   flow, so the page is built on a faint technical lattice with light pushing
   through it from behind. Everything here is CSS gradients. No images, no
   canvas, no libraries, so it costs bytes in the stylesheet and nothing in
   requests.
   -------------------------------------------------------------------------- */

/* The lattice itself, painted on <body> so it runs the whole page. */
body{
  background-image:
    linear-gradient(to right,rgba(120,150,220,.045) 1px,transparent 1px),
    linear-gradient(to bottom,rgba(120,150,220,.045) 1px,transparent 1px);
  background-size:72px 72px;
  background-position:center top;
}

/* Light source behind the hero. Two offset radials, one per brand stop. */
.hero::before{
  content:"";position:absolute;inset:-30% -10% auto -10%;height:150%;
  background:
    radial-gradient(58% 52% at 22% 18%,var(--glow-blue),transparent 70%),
    radial-gradient(46% 44% at 78% 6%,var(--glow-cyan),transparent 72%);
  pointer-events:none;z-index:0;
}
/* A horizon line where the hero meets the next band. */
.hero::after{
  content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;
  background:linear-gradient(90deg,transparent,var(--brand-blue),var(--brand-cyan),transparent);
  opacity:.5;pointer-events:none;
}
.hero > .wrap{position:relative;z-index:1}

/* Headline gets the brand gradient on its last line via a span, and falls
   back to plain --text anywhere background-clip is unsupported. */
.grad{
  background:var(--grad-brand);
  -webkit-background-clip:text;background-clip:text;
  color:transparent;
}
@supports not (background-clip:text){ .grad{color:var(--text)} }

/* Section labels get a mono marker rather than being bare small caps. */
.label,h2.label{display:flex;align-items:center;gap:var(--s-2)}
.label::before{
  content:"";flex:0 0 auto;width:22px;height:2px;border-radius:2px;
  background:var(--grad-brand);
}

/* Cards sit on a raised surface with a gradient hairline that lights up on
   hover. The lift is kept small so it reads as depth, not as a toy. */
.card,.step,.stat,.qa,.cbox{position:relative;overflow:hidden}
.card::before,.step::before,.stat::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:var(--grad-brand);opacity:.35;
  transition:opacity var(--t) ease;
}
.card:hover::before{opacity:1}
.card:hover{
  border-color:var(--border-card-hover);
  transform:translateY(-3px);
  box-shadow:0 18px 40px -24px rgba(0,0,0,.9),0 0 0 1px rgba(111,155,255,.10);
}

/* Numerals carry the gradient. This is the strongest single cue that the
   four services, the four process steps and the stats are one system. */
.num,.step h3::before{
  background:var(--grad-brand-lift);
  -webkit-background-clip:text;background-clip:text;
  color:transparent;
  font-weight:700;
}
@supports not (background-clip:text){
  .num,.step h3::before{color:var(--accent)}
}

/* Buttons get the brand gradient and a glow that tightens on hover. */
.btn{
  /* Lifted ramp again. --on-accent on #3B6BFF measures 4.45:1, just under AA.
     On --accent-strong it is 5.31:1. */
  background:linear-gradient(120deg,var(--accent-strong),var(--brand-cyan));
  color:var(--on-accent);
  box-shadow:0 10px 30px -12px var(--glow-blue);
  font-weight:700;letter-spacing:-.01em;
}
.btn:hover{filter:none;box-shadow:0 14px 34px -10px rgba(59,107,255,.55);transform:translateY(-1px)}

/* Stat values pick up the gradient too, so the proof band reads as data. */
.stat-val{
  background:var(--grad-brand-lift);
  -webkit-background-clip:text;background-clip:text;
  color:transparent;font-weight:700;
}
@supports not (background-clip:text){ .stat-val{color:var(--text)} }

/* The process column gets a spine connecting the steps. */
/* A spine down the process column, drawn on the list so it connects the
   steps rather than repeating on each one. */
.steps{position:relative;padding-left:var(--s-6)}
.steps::before{
  content:"";position:absolute;left:6px;top:var(--s-5);bottom:var(--s-5);width:2px;
  background:linear-gradient(180deg,var(--brand-blue),var(--brand-cyan),transparent);
  opacity:.4;
}

/* Footer sits under a hairline of the brand gradient. */
footer{position:relative}
footer::before{
  content:"";position:absolute;left:0;right:0;top:-1px;height:1px;
  background:linear-gradient(90deg,transparent,var(--border-2),transparent);
}

/* --------------------------------------------------------------------------
   5. Hero — home
   -------------------------------------------------------------------------- */
.hero{position:relative;overflow:hidden;padding:calc(var(--band-8) + var(--s-7)) 0 var(--band-8);border-bottom:1px solid var(--border)}
.eyebrow{
  display:inline-block;font-family:var(--font-mono);font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);border:1px solid var(--border);border-radius:var(--r-pill);
  padding:5px 13px;margin-bottom:26px;                 /* off-ramp */
}
.page-main h1{
  font-family:var(--font-display);
  font-size:clamp(2.5rem,6vw,4.6rem);
  line-height:1.02;letter-spacing:-.035em;font-weight:700;
  margin:0 0 var(--s-5);max-width:18ch;
}
.sub{font-size:clamp(1.08rem,1.5vw,1.3rem);color:var(--muted);margin:0}
.sub-lead{margin-bottom:34px}      /* off-ramp; was an inline style= on #contact */
.hero-cta{margin:var(--s-6) 0 0}

/* --------------------------------------------------------------------------
   6. Sections — home
   -------------------------------------------------------------------------- */
section{padding:var(--band-7) 0}
.page-main h2{
  font-family:var(--font-display);
  font-size:clamp(1.6rem,3vw,2.4rem);letter-spacing:-.025em;
  margin:0 0 var(--s-3);font-weight:700;
}
/* Section labels are h2 elements so the page has a real heading outline
   (h1 > h2 section > h3 card). The h2.label selector matches .page-main h2 on
   specificity and wins on order, so the small-caps label styling holds. */
.label,h2.label{
  font-family:var(--font-mono);font-size:.72rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--muted);margin:0 0 34px;font-weight:600;  /* off-ramp */
}

/* Who this is for — a prose band, wider measure than the hero sub */
.band{max-width:var(--max-prose)}
.band p{margin:0 0 var(--s-4);color:var(--body-dim);font-size:1.04rem}
.band p:last-child{margin-bottom:0}

/* --------------------------------------------------------------------------
   7. Cards — home
   -------------------------------------------------------------------------- */
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-4)}
.card{
  background:var(--surface-2);
  border:1px solid var(--border);
  border-radius:var(--r-lg);
  padding:30px 26px var(--s-6);                        /* off-ramp: 30, 26 */
  transition:border-color var(--t) ease,transform var(--t) ease;
  scroll-margin-top:calc(var(--bar-h) + var(--s-4));   /* clears the sticky header */
}
.card:hover{border-color:var(--border-card-hover);transform:translateY(-2px)}
.num{font-family:var(--font-mono);font-size:.78rem;color:var(--accent);font-weight:700;letter-spacing:.06em;margin-bottom:var(--s-3)}
.card h3{font-family:var(--font-display);font-size:1.2rem;margin:0 0 10px;letter-spacing:-.012em;font-weight:640;line-height:1.35} /* off-ramp */
.card p{margin:0;color:var(--muted);font-size:.955rem}

/* Service cards — same card treatment, three beats each. Two up, so the
   fourth line sits beside the others rather than trailing them. */
.svc-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-5)}
.svc p{margin:0 0 var(--s-3)}
/* The outcome line reads as a readout rather than a pull quote. */
.svc .svc-result{
  color:var(--text);margin:var(--s-4) 0 0;
  padding-top:var(--s-4);border-top:1px solid var(--border);
}
.svc .svc-result::before{
  content:"";display:inline-block;width:6px;height:6px;border-radius:50%;
  margin-right:var(--s-2);vertical-align:middle;
  background:var(--brand-cyan);box-shadow:0 0 10px var(--brand-cyan);
}
.svc h3 a{color:inherit;text-decoration:none;border-bottom:1px solid transparent}
.svc h3 a:hover{border-bottom-color:var(--accent)}
.svc .svc-more{margin:var(--s-4) 0 0;font-size:.94rem}
/* Same treatment for a link paragraph standing on its own outside a card. */
.more-link{margin:var(--s-4) 0 0;font-size:.98rem}
.more-link a{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent}
.more-link a:hover{border-bottom-color:var(--accent)}
.svc .svc-more a{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent}
.svc .svc-more a:hover{border-bottom-color:var(--accent)}

/* Process steps */
.steps{list-style:none;margin:0;padding:0;display:grid;gap:var(--s-4);max-width:960px;counter-reset:step}
.step{
  background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-lg);
  padding:var(--s-6);counter-increment:step;
}
.step h3{font-family:var(--font-display);margin:0 0 var(--s-2);font-size:1.15rem;font-weight:640;letter-spacing:-.012em;line-height:1.35}
.step h3::before{
  content:counter(step,decimal-leading-zero);font-family:var(--font-mono);
  display:block;margin-bottom:var(--s-2);
  color:var(--accent);font-size:.75rem;font-weight:700;letter-spacing:.06em;
}
.step p{margin:0;color:var(--muted);font-size:.965rem}
.step-time{
  display:block;margin-top:var(--s-3);
  font-family:var(--font-mono);font-size:.74rem;letter-spacing:.09em;text-transform:uppercase;color:var(--label-dim);
}

/* Proof — measured figures about this page. Values stay TODO until Step 07. */
.stat-row{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:var(--s-4);margin-top:var(--s-7);max-width:1100px;
}
.stat{
  background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-lg);
  padding:var(--s-5);display:flex;flex-direction:column;
}
/* flex:1 keeps the values on one baseline when a label wraps to two lines */
.stat-key{
  font-family:var(--font-mono);font-size:.7rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);margin-bottom:var(--s-2);flex:1;
}
.stat-val{font-family:var(--font-mono);font-size:1.15rem;font-weight:640;color:var(--text)}

/* FAQ — native <details>, so it works with no JS. Unlike the nav there is no
   desktop state to force open, which is what made <details> unusable there. */
.faq{display:grid;gap:var(--s-2);max-width:960px}
.qa{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-lg)}
.qa summary{font-family:var(--font-display);
  list-style:none;cursor:pointer;
  padding:var(--s-4) var(--s-5);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  font-size:1.02rem;font-weight:640;color:var(--text);
  border-radius:var(--r-lg);
}
.qa summary::-webkit-details-marker{display:none}
/* Chevron drawn from borders — empty content, so nothing is announced */
.qa summary::after{
  content:"";flex:0 0 auto;width:9px;height:9px;margin-right:3px;
  border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);
  transform:rotate(45deg);transition:transform var(--t) ease;
}
.qa[open] summary::after{transform:rotate(-135deg)}
.qa summary:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.qa p{margin:0;padding:0 var(--s-5) var(--s-5);color:var(--muted);font-size:.965rem}

/* --------------------------------------------------------------------------
   8. Forms — home
   -------------------------------------------------------------------------- */
#contact{border-top:1px solid var(--border)}
.form{max-width:var(--max-form);margin:0 0 46px}       /* off-ramp */
.frow{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}  /* off-ramp */
.field{margin-bottom:18px}                             /* off-ramp */
.flabel{
  display:block;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);font-weight:600;margin-bottom:var(--s-1);
}
.opt{text-transform:none;letter-spacing:0;font-weight:500;color:var(--label-dim)}

.form input[type=text],
.form input[type=email],
.form input[type=tel],
.form textarea{
  width:100%;background:var(--surface-2);
  border:1px solid var(--border);border-radius:var(--r-md);
  padding:var(--s-2) 14px;                             /* off-ramp: 14 */
  color:var(--text);font-family:inherit;font-size:1rem;line-height:1.5;
  transition:border-color var(--t) ease,box-shadow var(--t) ease;
}
.form input:focus,.form textarea:focus{
  outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px var(--focus-ring);
}
.form input::placeholder,.form textarea::placeholder{color:var(--placeholder)}
.form textarea{resize:vertical;min-height:var(--band-8)}

/* SMS consent block — A2P 10DLC compliance markup. Restyled only; the wording
   and the recorded consent fields are untouched. */
.consent{
  display:flex;gap:13px;align-items:flex-start;        /* off-ramp */
  background:var(--surface-2);border:1px solid var(--border);
  border-radius:var(--r-lg);padding:17px 19px;margin:0 0 10px;  /* off-ramp */
}
.consent input[type=checkbox]{
  width:18px;height:18px;margin:3px 0 0;flex:0 0 auto;
  accent-color:var(--accent);cursor:pointer;
}
.consent label{font-size:.93rem;line-height:1.62;color:var(--body-dim);cursor:pointer}
/* Underlined by default, not just on hover. These two links sit inside a
   paragraph of body text, so color alone is not a sufficient distinguisher
   (WCAG 1.4.1). Lighthouse flags exactly this. Styling only. The consent
   wording and the fields it records are untouched. */
.consent a{color:var(--accent);text-decoration:none;border-bottom:1px solid var(--accent)}
.consent a:hover{color:var(--text);border-bottom-color:var(--text)}
.consent-note{font-size:.83rem;color:var(--muted);margin:0 0 var(--s-5)}

.btn{
  background:var(--accent);color:var(--on-accent);border:0;
  border-radius:var(--r-md);padding:13px 27px;          /* off-ramp */
  font-family:inherit;font-size:.98rem;font-weight:650;cursor:pointer;
  transition:filter var(--t) ease;
}
.btn:hover{filter:brightness(1.07)}
a.btn{display:inline-block;text-decoration:none;border-bottom:0}
a.btn:hover{color:var(--on-accent)}

/* Visually hidden but read by assistive tech and counted by crawlers. Used for
   the h1 on pages whose visible design has no headline. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* Netlify honeypot — off-screen but still reachable to bots */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.alt-head{
  font-family:var(--font-mono);font-size:.7rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);margin:0 0 18px;font-weight:600;  /* off-ramp */
}
.contact-grid{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--s-4);max-width:var(--max-form);
}
.cbox{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-lg);padding:26px} /* off-ramp */
.ckey{font-family:var(--font-mono);font-size:.7rem;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);margin-bottom:9px} /* off-ramp */
.cval{font-size:1.03rem;word-break:break-word}
.cval a{color:var(--text);text-decoration:none;border-bottom:1px solid var(--rule)}
.cval a:hover{border-bottom-color:var(--accent);color:var(--accent)}

/* Code sample. Used on /websites to show the page's own source rather than
   claiming it is hand-written. Scrolls on its own so a long line cannot push
   the page sideways on a phone. */
.code-lead{margin:var(--s-7) 0 var(--s-3);color:var(--muted);max-width:var(--max-prose)}
.codeblock{
  margin:0;padding:var(--s-5);max-width:960px;overflow-x:auto;
  background:var(--surface-1);border:1px solid var(--border);border-radius:var(--r-lg);
  position:relative;
}
.codeblock::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:var(--grad-brand);opacity:.35;
}
.codeblock code{
  font-family:var(--font-mono);font-size:.82rem;line-height:1.7;
  color:var(--body-dim);white-space:pre;
}

/* About page. The quietest page on the site by intent, so the hero drops the
   glow and the headline keeps a single weight. */
.hero-quiet::before{opacity:.45}
/* max-width in px, not ch. A ch-based max-width is measured in the current
   font, so when Space Grotesk swaps in, the box resizes, the headline rewraps,
   the hero gets taller and the percentage-positioned glow moves with it.
   Measured at 0.038 CLS on this page. In px it is 0. */
.page-about h1{font-size:clamp(2.1rem,4.4vw,3.4rem);max-width:800px}

/* An unanswered TODO should look unfinished, because it is. This is meant to
   be impossible to deploy past without noticing. */
.todo-block{
  color:var(--text);font-family:var(--font-mono);font-size:.9rem;line-height:1.7;
  border:1px dashed var(--border-hover);border-radius:var(--r-lg);
  padding:var(--s-5);background:var(--surface-1);
}

/* The details row reuses the stat card but holds sentences, not figures, so it
   is not gradient-clipped and wraps normally. */
.fact-val{font-size:.98rem;color:var(--body-dim);line-height:1.6}
.fact-val a{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent;word-break:break-word}
.fact-val a:hover{border-bottom-color:var(--accent)}

/* --------------------------------------------------------------------------
   9. Document pages — terms, privacy, thanks
   -------------------------------------------------------------------------- */
.page-doc main{padding:var(--band-4) 0 var(--s-4)}
.page-thanks main,.page-404 main{padding:var(--band-7) 0 var(--s-7)}

.page-doc h1{
  font-family:var(--font-display);
  font-size:clamp(2rem,4.4vw,2.9rem);
  letter-spacing:-.03em;margin:0 0 var(--s-2);font-weight:700;
}
.page-thanks h1,.page-404 h1{margin-bottom:var(--s-3)}
.page-legal h2{font-family:var(--font-display);font-weight:700;font-size:1.2rem;letter-spacing:-.012em;margin:var(--band-1) 0 var(--s-2);font-weight:650}

.page-doc p,.page-doc li{color:var(--body-dim);font-size:1rem}
.page-doc p{margin:0 0 15px}                           /* off-ramp */
.page-legal ul{margin:0 0 15px;padding-left:22px}      /* off-ramp */
.page-legal li{margin-bottom:var(--s-1)}
.page-legal strong{color:var(--text)}
.page-legal .eff{color:var(--muted);font-size:.93rem;margin:0 0 var(--band-1)}

/* :where() keeps these at bare-element specificity, matching the original
   `a { }` rule. Scoping them as `.page-doc a` would outrank .brand, .nav-cta
   and .back and turn all three accent-blue. */
:where(.page-doc) a{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent}
:where(.page-doc) a:hover{border-bottom-color:var(--accent)}

.page-legal .callout{
  background:var(--surface-2);border:1px solid var(--border);
  border-radius:var(--r-lg);
  padding:22px var(--s-5);margin:var(--s-4) 0 26px;    /* off-ramp: 22, 26 */
}
.page-legal .callout p:last-child{margin-bottom:0}

.back{
  display:inline-block;margin-top:14px;                /* off-ramp */
  border:1px solid var(--border);border-radius:var(--r-md);
  padding:11px var(--s-4);                             /* off-ramp: 11 */
  color:var(--muted);font-size:.95rem;
}
.back:hover{color:var(--text);border-color:var(--border-hover);border-bottom-color:var(--border-hover)}

/* --------------------------------------------------------------------------
   10. Footer
   Inner selectors are prefixed with `.foot ` on purpose. The page-scope rules
   in section 9 (.page-doc p, .page-doc li, .page-legal ul, .page-legal h2)
   would otherwise reach into the footer and reset its type scale.
   -------------------------------------------------------------------------- */
footer{border-top:1px solid var(--border);padding:var(--band-2) 0 var(--band-1)}
.page-doc footer{margin-top:var(--band-3)}
.foot{
  max-width:var(--max-wide);margin:0 auto;padding:0 var(--s-6);
  color:var(--muted);font-size:.9rem;
}
.foot-cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-7)}
.foot .foot-head{
  font-family:var(--font-mono);font-size:.7rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);font-weight:600;margin:0 0 var(--s-3);
}
.foot .foot-col ul{list-style:none;margin:0;padding:0}
.foot .foot-col li{margin:0 0 var(--s-2);font-size:inherit;color:inherit}
.foot .foot-col p{margin:0 0 var(--s-2);font-size:inherit;color:inherit}
.foot .foot-legal{
  margin:var(--band-1) 0 0;padding-top:var(--s-4);
  border-top:1px solid var(--border);font-size:inherit;color:inherit;
}
.foot a{color:var(--muted);text-decoration:none;border-bottom:1px solid transparent}
/* Step 02: unified. Footer links used to hover grey (--rule) on the home page
   and accent on the document pages; accent everywhere now, matching every
   other link on the site. */
.foot a:hover{color:var(--text);border-bottom-color:var(--accent)}

/* --------------------------------------------------------------------------
   11b. Motion

   Nothing here hides content by default. The reveal start-state is scoped to
   .js, which is set by an inline script in <head>. With JavaScript off the
   class is never added, the rules never match, and every section renders in
   its final position. That is the whole no-JS story.
   -------------------------------------------------------------------------- */

/* Hero entrance, on load, no JS needed. */
.hero .eyebrow,.hero h1,.hero .sub,.hero .hero-cta{
  animation:rise var(--t-slow) var(--ease-out) both;
}
.hero h1{animation-delay:.06s}
.hero .sub{animation-delay:.12s}
.hero .hero-cta{animation-delay:.18s}
@keyframes rise{
  from{opacity:0;transform:translateY(14px)}
  to{opacity:1;transform:none}
}

/* The light behind the hero drifts. This animates transform on a pseudo
   element, which the compositor handles on its own thread. Animating the
   lattice's background-position instead would repaint the full page every
   frame and cost the performance score. */
.hero::before{animation:drift 26s ease-in-out infinite alternate;will-change:transform}
@keyframes drift{
  from{transform:translate3d(0,0,0) scale(1)}
  to{transform:translate3d(3%,2%,0) scale(1.08)}
}

/* Scroll reveals. Start-state only applies once JS has confirmed it can
   remove it again. */
.js [data-reveal]{opacity:0;transform:translateY(18px)}
.js [data-reveal].in{
  opacity:1;transform:none;
  transition:opacity var(--t-slow) var(--ease-out),transform var(--t-slow) var(--ease-out);
}

/* --------------------------------------------------------------------------
   11c. Reduced motion

   Disabled, not shortened. Anything that animates in is pinned to its final
   state so no content can be left hidden.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
  .hero::before{animation:none;transform:none}
  .hero .eyebrow,.hero h1,.hero .sub,.hero .hero-cta{animation:none;opacity:1;transform:none}
  .js [data-reveal],.js [data-reveal].in{opacity:1;transform:none;transition:none}
  .card:hover{transform:none}
  .btn:hover{transform:none}
}

/* --------------------------------------------------------------------------
   11. Media queries
   -------------------------------------------------------------------------- */
@media (max-width:860px){
  .grid{grid-template-columns:1fr}
  .svc-grid{grid-template-columns:1fr}
  .stat-row{grid-template-columns:repeat(2,minmax(0,1fr))}
  .page-about .stat-row{grid-template-columns:1fr}
  .hero{padding:74px 0 62px}                           /* off-ramp */
  section{padding:62px 0}                              /* off-ramp */
  .page-doc main{padding:var(--band-2) 0 var(--s-2)}
  .page-thanks main,.page-404 main{padding:var(--band-3) 0 var(--s-5)}

  /* Nav collapses to a disclosure below 860px. The checkbox is visible to the
     tab order but not to the eye — opacity rather than display:none, so Space
     still opens the menu. */
  .nav-check{
    display:block;position:absolute;top:50%;left:0;
    width:1px;height:1px;margin:0;padding:0;opacity:0;pointer-events:none;
  }
  .nav-toggle{
    display:inline-flex;align-items:center;
    color:var(--muted);font-size:.92rem;
    border:1px solid var(--border);border-radius:var(--r-sm);
    padding:var(--s-1) 15px;white-space:nowrap;cursor:pointer;
    transition:color var(--t) ease,border-color var(--t) ease;
  }
  .nav-toggle:hover{color:var(--text);border-color:var(--border-hover)}
  .nav-check:focus + .nav-toggle{
    color:var(--text);border-color:var(--accent);
    outline:2px solid var(--accent);outline-offset:2px;
  }
  .nav-list{
    display:none;
    position:absolute;right:0;top:calc(100% + var(--s-2));
    flex-direction:column;align-items:stretch;gap:0;
    min-width:212px;padding:var(--s-1);
    background:var(--surface-2);border:1px solid var(--border);
    border-radius:var(--r-lg);box-shadow:0 12px 32px rgba(0,0,0,.45);
  }
  .nav-check:checked ~ .nav-list{display:flex}
  .nav-list a:not(.nav-cta){display:block;padding:var(--s-2);border-bottom:0}
  .nav-list a[aria-current="page"]{border-bottom:0;box-shadow:inset 2px 0 0 var(--accent)}
  .nav-list .nav-cta{display:block;margin:var(--s-1);text-align:center}

  .foot-cols{grid-template-columns:1fr;gap:var(--s-6)}
}

@media (min-width:1500px){
  .svc-grid{gap:var(--s-6)}
}

/* /websites has six deliverable cards rather than four, so it goes three-up
   where there is room. */
@media (min-width:1100px){
  .page-websites .svc-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
}

@media (max-width:520px){
  .contact-grid{grid-template-columns:1fr}
}
