/* Brand fonts: headings = Helvetica Now Display Black (self-hosted), body = Montserrat (Google). */
@font-face{font-family:"Helvetica Now Display";src:url("/HelveticaNowDisplay-Blk.woff2") format("woff2");font-weight:900;font-style:normal;font-display:swap;}

/* ═══ DESIGN SYSTEM ═══════════════════════════════════════════════════════════
   ⚠️⚠️ KEPT IN STEP WITH /opt/aspire-signup/index.html. The whole point of this
   rebuild is that the site and the registration flow read as ONE product — the owner's
   words: "considering how beautiful we have the registration flow working ... we could
   make this website absolute state of the art best in class for an ISP surrounding the
   registration flow." A visitor crosses from here to signup.aspirefiber.com mid-sentence;
   if the type or the blue shifts at that boundary, it reads as a third-party form.
   Change a value here only by changing it there in the same commit.

   The palette is an engineering-drawing palette, and that was a deliberate rejection of
   the dark navy→purple gradient the signup flow started with: handsome, but it is the
   default SaaS gradient and it fought the wordmark. Cool paper, navy ink, structural
   blue, and magenta in exactly ONE role — the thing to click. */
:root{
  --navy:#243C7F; --navy-2:#1b2e60; --navy-3:#111f45; --ink-deep:#0a1330;
  --magenta:#CE358A; --magenta-lo:#f0a7cd; --blue:#3D9BE9; --blue-lo:#8fc4f4;
  --paper:#f7f8fb;            /* cool paper, not cream — cream+serif is the AI tell */
  --paper-2:#f1f3f9;
  --card:#fff; --ink:#141b31; --body:#404a66; --muted:#6a7391;
  --line:#e3e7f0; --line-2:#eef1f7;
  --rule:#ccd6e8;             /* the drafting-line blue used for structure */
  --ok:#0e7a52; --warn:#8a5a06;

  --sans:"Montserrat",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --display:"Helvetica Now Display","Montserrat",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --r:16px;

  /* ⚠️ ONE SPACING SCALE. The old site's sections were spaced by whatever the Wix editor
     produced, which is why it never felt composed. Everything vertical is one of these. */
  --s1:8px; --s2:14px; --s3:22px; --s4:34px; --s5:52px; --s6:80px; --s7:112px;
  --wrap:1180px;              /* content max-width */
  --wrap-text:68ch;           /* prose max-width — never let a paragraph run wider */
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important}
}
body{margin:0;background:var(--paper);color:var(--body);font-family:var(--sans);
  font-size:16.5px;line-height:1.62;-webkit-font-smoothing:antialiased}
img{max-width:100%;height:auto;display:block}
a{color:var(--navy)}
h1,h2,h3,h4{font-family:var(--display);color:var(--ink);letter-spacing:-.022em;
  line-height:1.12;margin:0;text-wrap:balance}
h1{font-size:clamp(34px,5.4vw,62px);font-weight:800}
h2{font-size:clamp(26px,3.4vw,40px);font-weight:800}
h3{font-size:clamp(19px,2.1vw,24px);font-weight:700}
p{margin:0 0 var(--s2)}
:focus-visible{outline:2.5px solid var(--magenta);outline-offset:3px;border-radius:4px}

/* ⚠️ A SKIP LINK, because the nav is the first thing in the DOM on every page. */
.skip{position:absolute;left:-9999px;top:0;background:var(--card);color:var(--ink);
  padding:12px 18px;z-index:200;border-radius:0 0 10px 0;font-weight:700}
.skip:focus{left:0}

.wrap{max-width:var(--wrap);margin:0 auto;padding:0 22px}
.sect{padding:var(--s6) 0}
.sect-tight{padding:var(--s5) 0}
.eyebrow{font-family:var(--display);font-size:12px;font-weight:800;letter-spacing:.13em;
  text-transform:uppercase;color:var(--magenta);margin:0 0 var(--s2)}
.lede{font-size:clamp(17px,1.9vw,20px);color:var(--body);max-width:var(--wrap-text)}

/* ═══ COMPONENTS ══════════════════════════════════════════════════════════════ */

/* ── buttons ─────────────────────────────────────────────────────────────────
   ⚠️ MAGENTA IS RESERVED. One primary action per screen, and it is always the thing
   that moves you toward service. Everything else is a ghost or a link. The old site had
   "Save Now" three times above the fold in the same colour as "Check Availability",
   which is how a page ends up with no call to action at all. */
.btn{display:inline-flex;align-items:center;gap:9px;font-family:var(--display);
  font-size:15.5px;font-weight:700;padding:14px 26px;border-radius:12px;border:1.5px solid transparent;
  text-decoration:none;cursor:pointer;transition:transform .12s ease,box-shadow .18s ease,background .18s}
.btn.primary{background:var(--magenta);color:#fff;box-shadow:0 10px 26px -12px rgba(206,53,138,.75)}
.btn.primary:hover{background:#b92d7a;transform:translateY(-1px)}
.btn.ghost{background:transparent;color:var(--navy);border-color:var(--rule)}
.btn.ghost:hover{border-color:var(--navy);background:var(--paper-2)}
.btn.navy{background:var(--navy);color:#fff}
.btn.navy:hover{background:var(--navy-2);transform:translateY(-1px)}
.btn.blue{background:var(--blue);color:#fff;box-shadow:0 10px 26px -12px rgba(61,155,233,.6)}
.btn.blue:hover{background:#2f86d0;transform:translateY(-1px)}
.btn.onnavy{background:#fff;color:var(--navy)}
.btn.onnavy:hover{background:var(--paper)}
.btn-lg{font-size:17px;padding:17px 32px}

/* ── nav ─────────────────────────────────────────────────────────────────────
   ⚠️ Two actions, not five. The old header carried Check Availability, Customer Portal
   and Refer & Earn side by side; the one that earns revenue was competing with a login. */
.nav{position:sticky;top:0;z-index:80;background:rgba(247,248,251,.86);
  backdrop-filter:saturate(140%) blur(10px);border-bottom:1px solid var(--line)}
.nav-in{display:flex;align-items:center;gap:var(--s3);height:72px}
.nav-logo img{height:30px;width:auto}
.nav-links{display:flex;gap:var(--s3);margin-left:auto;align-items:center}
.nav-links a{color:var(--body);text-decoration:none;font-size:15px;font-weight:600;font-family:var(--display);letter-spacing:-.01em;
  padding:6px 0;border-bottom:2px solid transparent}
.nav-links a:hover,.nav-links a[aria-current=page]{color:var(--ink);border-bottom-color:var(--magenta)}
.nav-cta{display:flex;gap:18px;align-items:center;margin-left:var(--s3)}
.nav-portal{font-size:14px;font-weight:600;color:var(--muted);text-decoration:none;font-family:var(--display);letter-spacing:-.01em}
.nav-portal:hover{color:var(--navy)}
.nav-toggle{display:none;margin-left:auto;background:none;border:1.5px solid var(--rule);
  border-radius:10px;padding:9px 11px;cursor:pointer;line-height:0}
.nav-toggle span{display:block;width:18px;height:2px;background:var(--ink);border-radius:2px}
.nav-toggle span+span{margin-top:4px}
/* The panel is tall with seven items; locking the page behind it stops the content
   scrolling away underneath while the menu is open. */
/* Mobile menu scroll-lock. The old `overflow:hidden` on the <html> element froze
   iOS Safari when the menu was opened while scrolled to the bottom of a page. Instead
   we pin <body> with position:fixed (preserving the scroll offset in JS) and switch
   the sticky nav to fixed so the menu stays at the top of the viewport. Mobile only:
   .nav-locked is never set on desktop, where the toggle is hidden. */
.nav-locked body{position:fixed;left:0;right:0;width:100%;overflow:hidden}
.nav-locked .nav{position:fixed;top:0;left:0;right:0;z-index:200;background:var(--paper)}
/* ⚠️ 1181px, and it MUST match the matchMedia in _layout.html. Seven nav items plus the
   logo and the CTA stop fitting well before the old 940px — the row went ragged and the
   links crowded the button. */
@media (max-width:1180px){
  .nav-toggle{display:block}
  .nav-links,.nav-cta{display:none;width:100%}
  .nav.open .nav-in{flex-wrap:wrap;height:auto;padding-top:max(env(safe-area-inset-top),12px);padding-bottom:var(--s3)}
  .nav.open .nav-links{display:flex;flex-direction:column;align-items:flex-start;gap:2px;margin:var(--s2) 0 0}
  .nav.open .nav-links a{padding:11px 0;width:100%;border-bottom:1px solid var(--line-2)}
  .nav.open .nav-cta{display:flex;margin:var(--s2) 0 0}
  .nav.open .nav-cta .btn{flex:1;justify-content:center}
}

/* ── hero ────────────────────────────────────────────────────────────────────
   ⚠️ NOT 100vh. A viewport-height opener pushes the page itself out of the first frame,
   which is the thing a visitor is actually deciding on. It is sized to what it holds. */
/* ⚠️ v3 — DEPTH, NOT A FLAT WASH. Owner: "the header section is not something that jumps
   out and looks amazing." The old hero was one linear gradient with two soft radials, which
   is the default SaaS header. This one is built in layers: a deep ink base so the type has
   somewhere to sit, a broad magenta bloom behind the map, a cool blue rake across the top,
   and a fine grid that gives the whole thing the drafting-table character the rest of the
   identity has. The grid is the piece that stops it reading as a stock gradient. */
.hero{position:relative;overflow:hidden;color:#fff;
  background:
    radial-gradient(1100px 620px at 76% 26%,rgba(206,53,138,.30),transparent 62%),
    radial-gradient(900px 520px at 8% 4%,rgba(61,155,233,.28),transparent 60%),
    radial-gradient(700px 500px at 50% 112%,rgba(61,155,233,.18),transparent 66%),
    linear-gradient(166deg,#070e24 0%,var(--navy-3) 34%,#1d326d 74%,#24407f 100%)}
.hero::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.5;
  background-image:linear-gradient(rgba(143,196,244,.055) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(143,196,244,.055) 1px,transparent 1px);
  background-size:62px 62px;
  -webkit-mask-image:radial-gradient(120% 90% at 50% 30%,#000 30%,transparent 78%);
          mask-image:radial-gradient(120% 90% at 50% 30%,#000 30%,transparent 78%)}
/* a hairline of light where the hero meets the page — reads as a horizon, and stops the
   section ending on a muddy edge */
.hero::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;z-index:2;
  background:linear-gradient(90deg,transparent,rgba(143,196,244,.5),rgba(206,53,138,.45),transparent)}
/* ⚠️ padding-BLOCK, not the `padding` shorthand. This element carries BOTH `.wrap` (which
   sets padding:0 22px) and `.hero-in`; a shorthand here resets left/right to 0 and the
   hero text sits flush against the screen edge on a phone. Every rule in this file that
   adds vertical space to a `.wrap` element must use padding-block for the same reason. */
/* The motif sits between the gradient and the content: above ::after so the strands are
   not washed out by the radial glows, below everything readable. Never interactive. */
.hero-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;
  pointer-events:none;opacity:.85}
.hero-in{position:relative;z-index:1;padding-block:var(--s7) var(--s6);display:grid;
  grid-template-columns:1.06fr .94fr;gap:var(--s5);align-items:center}
/* ⚠️ SIZED SO THE HEADLINE LANDS ON TWO LINES, NOT THREE. "Experience the Aspire Fiber
   difference." is a longer line than what it replaced, and at 80px in a 660px column it
   broke into three — which pushed the lede and the address field most of the way off the
   first screen. The cap comes down a little and the column widens; the headline is still
   the biggest thing on the page by a distance. */
.hero h1{color:#fff;font-size:clamp(38px,5.6vw,70px);line-height:1.04;letter-spacing:-.035em}
.hero h1 em{font-style:normal;
  background:linear-gradient(96deg,var(--blue-lo),#fff 40%,var(--magenta-lo));
  -webkit-background-clip:text;background-clip:text;color:transparent}
.hero .lede{color:#c9d4ee;margin:var(--s3) 0 var(--s4);font-size:clamp(17px,2vw,21px);max-width:60ch}
.hero-cta{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.hero-note{margin:var(--s3) 0 0;font-size:13.5px;color:#9fb0d6}
@media (max-width:880px){
  .hero-in{grid-template-columns:1fr;padding-block:var(--s5)}
  /* ⚠️⚠️ THE RADIALS ARE SIZED IN PIXELS, SO THEY DO NOT SCALE DOWN — and a 1100px-wide
     magenta glow centred at 76% covers a 390px viewport completely. On desktop it is a
     highlight in one corner; on a phone it was the entire hero, and the page read as
     purple rather than navy. Owner: "on my phone home screen looks super purple."
     Restated here at a size that is actually local to a phone. */
  /* ⚠️ SECOND CORRECTION. The first fix cut the magenta blob down to 360px, which killed
     the purple — and left a near-black slab. Owner: "looks less purple now but it's now
     soooo dark." The answer is not less colour, it is a LIGHTER BASE with the colour led
     by blue: bigger, weaker washes over a mid-navy rather than one saturated blob over
     near-black. Magenta stays as a corner accent only. */
  .hero{background:
    radial-gradient(540px 400px at 96% 2%,rgba(206,53,138,.22),transparent 66%),
    radial-gradient(560px 430px at 0% 10%,rgba(61,155,233,.30),transparent 68%),
    radial-gradient(620px 440px at 46% 104%,rgba(61,155,233,.20),transparent 72%),
    linear-gradient(172deg,#14264f 0%,#1b3568 42%,#22427f 80%,#27508f 100%)}
}

/* ── the address check, in the hero ──────────────────────────────────────────
   ⭐ THE ONE THING THIS PAGE IS FOR. The old homepage's primary action was a link to a
   separate /check-availability page — a click spent on navigation before the visitor has
   learned anything. The field is here, and it hands straight to the real flow. */
.check{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.18);
  border-radius:var(--r);padding:var(--s3);backdrop-filter:blur(6px)}
.check label{display:block;font-family:var(--display);font-weight:700;font-size:14px;
  color:#fff;margin-bottom:10px}
.check-row{display:flex;gap:10px}
.check input{flex:1;min-width:0;border:1.5px solid rgba(255,255,255,.28);background:rgba(255,255,255,.97);
  border-radius:11px;padding:14px 16px;font-size:16px;font-family:var(--sans);color:var(--ink)}
.check input::placeholder{color:var(--muted)}
.check-fine{margin:11px 0 0;font-size:12.5px;color:#a9b8dc}
@media (max-width:520px){.check-row{flex-direction:column}.check-row .btn{justify-content:center}}

/* ── live proof strip ────────────────────────────────────────────────────────
   ⭐ BUILD NUMBERS ARE THE SHIPPED SNAPSHOT (the edge fetch was removed 2026-09-25, F327).
   An ISP homepage that says "blazing fast" says nothing; one that says 103 cabinets and
   576 miles of conduit is making a claim it can be held to. */
/* ⚠️ FOUR PILLARS, NOT FOUR NUMBERS (owner 2026-09-13: "don't want to list specifics here
   in case competitors are snooping ... think we could use that to highlight 4 main things,
   100% fiber, locally built and operated"). Homes-in-plan, cabinet count and conduit miles
   are genuinely useful to a competitor sizing us up, and they are the first thing one would
   screenshot. They still appear where they earn their keep — the build schedule further
   down is about proving a claim, not advertising our footprint. */
.proof{border-top:1px solid rgba(255,255,255,.13);background:rgba(0,0,0,.17);
  position:relative;z-index:1}
.proof-in{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s3);padding-block:var(--s3)}
.pill4{display:flex;align-items:flex-start;gap:12px}
.pill4 i{flex:none;width:34px;height:34px;border-radius:10px;display:grid;place-items:center;
  background:rgba(143,196,244,.14);border:1px solid rgba(143,196,244,.26);color:#bcd8f8}
.pill4 b{display:block;font-family:var(--display);font-size:15px;font-weight:800;color:#fff;
  letter-spacing:-.01em;line-height:1.25}
.pill4 span{display:block;font-size:12.5px;color:#a9b8dc;margin-top:3px;line-height:1.4}
@media (max-width:900px){.proof-in{grid-template-columns:repeat(2,1fr);gap:var(--s3)}}
@media (max-width:480px){.proof-in{grid-template-columns:1fr}}

/* ── cards & grids ───────────────────────────────────────────────────────────
   ⚠️ NOT EVERYTHING IS A CARD. Border, fill and shadow each say "separate object";
   spending them on every block flattens the hierarchy. `.card` is for things you can act
   on; `.feat` is a text block with a rule, which is most of the page. */
.grid{display:grid;gap:var(--s3)}
.g2{grid-template-columns:repeat(2,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.g4{grid-template-columns:repeat(4,1fr)}
@media (max-width:960px){.g3,.g4{grid-template-columns:repeat(2,1fr)}}
@media (max-width:640px){.g2,.g3,.g4{grid-template-columns:1fr}}

/* ⚠️⚠️ A SURFACE MUST SET ITS OWN COLOUR. `.card` painted a white background and
   inherited its text colour — which is fine until a card is placed inside `.hero`, where
   `color:#fff` is in force. The plan table then rendered WHITE TEXT ON WHITE: the prices
   were in the DOM, laid out, and invisible. It survived a screenshot review because the
   rows still looked plausible; it was caught by reading getComputedStyle, not by looking.
   Every element that paints a background here now also states its text colour, so it can
   be dropped on any section without inheriting that section's ink. */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  padding:var(--s3);box-shadow:0 1px 2px rgba(20,32,68,.04);color:var(--body)}
.feat{padding-left:var(--s2);border-left:2.5px solid var(--rule)}
.feat h3{margin-bottom:8px}
.feat p{color:var(--body);margin:0}
.feat.on{border-left-color:var(--magenta)}

.ico{width:40px;height:40px;border-radius:11px;display:grid;place-items:center;
  background:rgba(61,155,233,.12);color:#1668ad;margin-bottom:var(--s2)}

/* ── plans ───────────────────────────────────────────────────────────────────
   ⚠️ PRICES COME FROM THE SIGNUP SERVICE, never from this file. The site and the flow
   quoting different numbers is the single worst thing this rebuild could do. */
.plan{display:flex;flex-direction:column;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r);padding:var(--s3);position:relative;color:var(--body)}
.plan.pop{border-color:var(--magenta);box-shadow:0 14px 40px -26px rgba(206,53,138,.55)}
/* ⚠️ TWO HIGHLIGHTED TILES, TWO COLOURS. 1 Gig is the recommendation (magenta, the brand's
   emphasis colour); 2 Gig is the other tier worth steering to, so it gets a lift in blue
   rather than a second magenta card competing with the first. */
.plan.pop2{border-color:var(--blue);box-shadow:0 14px 40px -26px rgba(61,155,233,.5)}
.plan-tag.blue{background:var(--blue)}
.plan-tag{position:absolute;top:-11px;left:var(--s3);background:var(--magenta);color:#fff;
  font-family:var(--display);font-size:11px;font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;padding:4px 10px;border-radius:99px}
.plan-sp{font-family:var(--display);font-size:29px;font-weight:800;color:var(--ink);letter-spacing:-.02em}
.plan-sub{font-size:13.5px;color:var(--muted);margin:2px 0 var(--s3)}
.plan-px{font-family:var(--display);font-size:34px;font-weight:800;color:var(--navy);
  letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.plan-px small{font-size:14px;font-weight:600;color:var(--muted);letter-spacing:0}
.plan ul{list-style:none;margin:var(--s3) 0 var(--s3);padding:0;font-size:14.5px}
.plan li{padding:6px 0 6px 24px;position:relative;color:var(--body)}
.plan li::before{content:"";position:absolute;left:2px;top:13px;width:11px;height:6px;
  border-left:2px solid var(--ok);border-bottom:2px solid var(--ok);transform:rotate(-45deg)}
.plan .btn{margin-top:auto;justify-content:center}

/* ── comparison ──────────────────────────────────────────────────────────────
   The same argument the signup flow makes, in the same words. */
.vs{border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  background:var(--card);color:var(--body)}
.vs-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.25fr) minmax(0,1.25fr);
  gap:6px 18px;padding:14px var(--s3);border-bottom:1px solid var(--line-2);align-items:start}
.vs-row:last-child{border-bottom:0}
.vs-head{background:var(--paper-2);font-family:var(--display);font-size:11px;font-weight:800;
  letter-spacing:.09em;text-transform:uppercase}
.vs-head .us{color:#1668ad}.vs-head .them{color:var(--muted)}
.vs-row b{font-family:var(--display);font-size:14.5px;color:var(--ink)}
.vs .us{color:var(--ink);font-weight:600;font-size:14.5px}
.vs .them{color:var(--muted);font-size:14.5px}
.vs .wmr{display:none}
@media (max-width:760px){
  .vs-head{display:none}
  .vs-row{grid-template-columns:1fr;gap:4px}
  /* The per-row label is now a real two-tone wordmark element (.wmr) carried in the
     markup, so drop the old single-colour ::before label. */
  .vs .us::before{content:none}
  .vs .wmr{display:inline;font-family:var(--display);font-weight:800;letter-spacing:-.01em;
    text-transform:none;color:var(--magenta)}
  .vs .wmr span{color:var(--navy)}
  .vs .wmr::after{content:" · ";color:var(--muted)}
  .vs .them::before{content:"Typical cable · ";font-weight:800;color:var(--muted)}
}

/* ── band, quote, footer ─────────────────────────────────────────────────── */
.band{background:var(--navy);color:#fff;border-radius:var(--r);padding:var(--s5) var(--s4);
  text-align:center}
.band h2{color:#fff}
.band p{color:#c9d4ee;max-width:60ch;margin:var(--s2) auto var(--s4)}
.quote{background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  padding:var(--s3);color:var(--body)}
.quote p{font-size:16px;color:var(--ink);margin:0 0 var(--s2)}
.quote cite{font-style:normal;font-size:13.5px;color:var(--muted);font-weight:600}
.stars{color:#e8a317;letter-spacing:2px;font-size:14px;margin-bottom:8px}

.foot{background:var(--ink-deep);color:#8fa0c8;padding:var(--s6) 0 var(--s4);margin-top:var(--s6)}
.foot a{color:#c3cee8;text-decoration:none}
.foot a:hover{color:#fff;text-decoration:underline}
.foot-in{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:var(--s4)}
.foot h4{color:#fff;font-size:13px;letter-spacing:.09em;text-transform:uppercase;margin-bottom:var(--s2)}
.foot ul{list-style:none;margin:0;padding:0;font-size:14.5px;line-height:2}
.foot-logo img{height:28px;margin-bottom:var(--s2)}
.foot-bot{border-top:1px solid rgba(255,255,255,.1);margin-top:var(--s4);padding-top:var(--s3);
  display:flex;flex-wrap:wrap;gap:var(--s2) var(--s3);font-size:13.5px}
.foot-bot .sp{margin-left:auto}
@media (max-width:820px){.foot-in{grid-template-columns:1fr 1fr}}
@media (max-width:520px){.foot-in{grid-template-columns:1fr}}

/* ── prose pages (policies, faq, posts) ──────────────────────────────────── */
.prose{max-width:var(--wrap-text)}
.prose h2{margin:var(--s4) 0 var(--s2)}
.prose h3{margin:var(--s3) 0 8px}
.prose ul,.prose ol{padding-left:22px;margin:0 0 var(--s2)}
.prose li{margin-bottom:6px}
.faq-q{border-bottom:1px solid var(--line);padding:var(--s2) 0}
.faq-q summary{font-family:var(--display);font-weight:700;color:var(--ink);cursor:pointer;
  font-size:16.5px;list-style:none;display:flex;gap:12px;align-items:flex-start}
.faq-q summary::-webkit-details-marker{display:none}
.faq-q summary::before{content:"+";color:var(--magenta);font-size:20px;line-height:1.1;flex:none}
.faq-q[open] summary::before{content:"\2212"}
.faq-q p{margin:var(--s2) 0 0 24px;color:var(--body)}

/* ── the asymmetry diagram, v2 ───────────────────────────────────────────────
   ⭐ THE SINGLE MOST PERSUASIVE TRUE THING WE CAN SAY, so it gets the loudest treatment
   on the page. v1 drew it as 12px bars in two white cards and it read as a footnote.
   Now: dark ground so it separates from everything around it, 30px bars, the numbers at
   display size, and the cable upload rendered as the stub it actually is — the length
   difference IS the argument, so everything here exists to make that length obvious.
   ⚠️ Numbers are illustrative and the caption says so. A specific competitor's current
   ratio is a claim that changes without telling us; "around a twentieth" is defensible
   about coax as a technology. */
.asym{display:grid;gap:var(--s3)}
.asym-card{position:relative;border-radius:18px;padding:clamp(20px,3vw,32px);
  background:var(--card);border:1px solid var(--line);color:var(--body)}
.asym-card.us{background:linear-gradient(160deg,var(--navy-3),var(--navy) 72%,#2c4a96);
  border-color:transparent;color:#dbe4f8;
  box-shadow:0 26px 60px -34px rgba(36,60,127,.85)}
.asym-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:var(--s3)}
.asym-h b{font-family:var(--display);font-size:clamp(17px,2.2vw,22px);color:var(--ink);
  letter-spacing:-.02em}
.asym-card.us .asym-h b{color:#fff}
.asym-h span{font-size:13px;color:var(--muted);letter-spacing:.04em;text-transform:uppercase;
  font-weight:700}
.asym-card.us .asym-h span{color:#8fc4f4}
.asym-row{display:grid;grid-template-columns:52px 1fr;gap:6px 14px;align-items:center;
  margin-bottom:var(--s2)}
.asym-row i{font-style:normal;font-size:11px;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.asym-card.us .asym-row i{color:#8fc4f4}
/* ⚠️ NOT overflow:hidden any more — the stub's label is positioned OUTSIDE the bar but
   inside the track, and clipping it cut "~50 Mbps" in half once the label started
   following --w. The bars have their own radius, so nothing needs clipping here. */
.asym-track{height:30px;border-radius:8px;background:var(--paper-2);position:relative}
.asym-card.us .asym-track{background:rgba(255,255,255,.08)}
/* ⚠️ THE RESTING STATE IS THE FULL BAR. assets/asym.js opts INTO the animation by adding
   .draw; nothing here depends on a script having run, so a blocked asym.js leaves a
   correct, complete graphic rather than four empty tracks. */
.asym-track em{display:block;height:30px;border-radius:8px;background:#b3bccd;
  width:var(--w)}
.asym.draw .asym-track em{width:0;
  transition:width 1s cubic-bezier(.22,.85,.26,1)}
.asym.draw .asym-track em.on{width:var(--w)}
.asym-card.us .asym-track em{background:linear-gradient(90deg,var(--blue),var(--magenta));
  box-shadow:0 0 28px -4px rgba(206,53,138,.6)}
.asym-val{position:absolute;top:0;right:12px;height:30px;display:flex;align-items:center;
  font-family:var(--display);font-size:15px;font-weight:800;color:var(--ink);
  font-variant-numeric:tabular-nums}
.asym-card.us .asym-val{color:#fff}
/* The stub is too short to hold its own label, so the number sits outside it.
   ⚠️ Positioned from --w, not a hardcoded 5% — the two drifted apart the moment the bar
   width moved into a custom property, and the label would have sat over a bar of a
   different length with nothing to show it was wrong. */
.asym-val.out{right:auto;left:calc(var(--w,5%) + 12px);color:var(--muted)}

/* The gap, said as a number. Sits on the cable card's corner and arrives after the bars
   have drawn, so it lands as the conclusion rather than as a badge. */
/* ⚠️ IT LIVES ON THE ASPIRE CARD NOW, so it is a claim about us and has to be legible
   rather than a watermark. On the cable card it was a 16%-opacity number in the corner;
   here it reads as the headline figure it is. */
.asym-gap{position:absolute;top:clamp(18px,2.6vw,26px);right:clamp(20px,3vw,32px);
  font-family:var(--display);font-weight:800;font-size:clamp(24px,3.4vw,34px);
  letter-spacing:-.03em;color:#fff;line-height:1;pointer-events:none;text-align:right}
.asym-gap em{display:block;font-style:normal;font-size:11px;font-weight:800;
  letter-spacing:.13em;text-transform:uppercase;color:#8fc4f4;margin-top:3px}
.asym.draw .asym-gap{opacity:0;transform:translateY(6px);
  transition:opacity .5s ease .95s,transform .5s ease .95s}
.asym.draw.shown .asym-gap{opacity:1;transform:none}
.asym-note{margin:var(--s3) 0 0;font-size:14px}
.asym-card:not(.us) .asym-note{color:var(--muted)}
.asym-card.us .asym-note{color:#c9d4ee}
.asym-note b{font-family:var(--display)}
.asym-card:not(.us) .asym-note b{color:var(--crit,#b3255f)}
.asym-card.us .asym-note b{color:#fff}
@media (min-width:900px){.asym{grid-template-columns:1fr 1fr;gap:var(--s4)}}
/* ⚠️ A COMPARISON YOU HAVE TO SCROLL BETWEEN IS NOT A COMPARISON. Stacked at full padding
   the two cards ran to ~500px and a phone could only hold one at a time, which is exactly
   the wrong thing to do to a side-by-side. Tightened so both fit one screen together. */
@media (max-width:640px){
  .asym{gap:12px}
  .asym-card{padding:17px 16px;border-radius:15px}
  .asym-h{margin-bottom:13px}
  .asym-row{grid-template-columns:40px 1fr;gap:5px 10px;margin-bottom:9px}
  .asym-track,.asym-track em,.asym-val{height:26px}
  .asym-val{font-size:14px;right:10px}
  .asym-note{margin-top:12px;font-size:13.5px}
  .asym-gap{top:14px;right:13px;font-size:19px}
  .asym-gap em{font-size:9px;letter-spacing:.09em;margin-top:1px}
}

/* ── touch targets ───────────────────────────────────────────────────────────
   ⚠️ FOUND BY AUDIT, NOT BY EYE. On a phone the footer link rows measured 17px tall, the
   FAQ summaries 27px, and several standalone links about 20px. They look fine — the text
   is a comfortable size — but a 17px target is a miss-tap, and the footer is where people
   go when they are already slightly lost.
   ⚠️ ONLY ON TOUCH WIDTHS. Padding these on a desktop would loosen a footer that is
   correctly dense there, so the fix is scoped rather than global.
   ⚠️ A LINK INSIDE A SENTENCE IS DELIBERATELY EXCLUDED — WCAG 2.2 SC 2.5.8 exempts it,
   and padding inline links would wreck the line rhythm of every paragraph on the site. */
@media (max-width:940px){
  .foot ul li{line-height:1.4}
  .foot ul a{display:inline-block;padding:9px 0;min-height:44px;
    display:flex;align-items:center}
  .faq-q summary{padding:11px 0;min-height:44px;align-items:center}
  .faq-q{padding:4px 0}
  /* A link that is the whole of its paragraph is a control, not prose. */
  /* ⚠️ NOT scoped to a section class — the first cut used `.sect p > a` and missed every
     one of these inside `.sect-tight`, which is most of them. The rule that matters is
     structural: a link that is the entire contents of its paragraph is a control. */
  /* ⚠️ :only-child also matches a link that is the only ELEMENT amid text, so the old
     inline-block + min-height turned inline links inside sentences into 44px blocks and
     double-spaced their line. Keep them inline (padding still gives a tap area) and skip
     .btn, which carries its own size — the inline-block was zeroing button side padding. */
  p > a:only-child:not(.btn){display:inline;padding:10px 0}
  .quote a{display:inline-block;padding:6px 2px}
  /* The footer's bottom bar (Broadband facts / Privacy / Terms) and any standalone
     "→" link at the end of a section. Both are single links in their own block, so the
     inline exemption does not apply to them. */
  .foot-bot a{display:inline-block;padding:11px 0;min-height:44px;
    display:inline-flex;align-items:center}
  .foot-bot{gap:2px var(--s3)}
}

/* ═══════════════════════════════════════════════════════════════════════════
   v2 — bringing the signup flow's confidence to the site
   The v1 site was typographically competent and visually flat: hairlines, air and
   no colour. The flow next door has a map, a connected step rail, a gradient promo
   bar and prices at 40px with the old price struck through. Same company, two
   different levels. These components close that gap.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── full-bleed media ────────────────────────────────────────────────────────
   Both Sonic and Google Fiber are BUILT on photography — full-width image blocks
   alternating with content is the backbone of each. v1 had no images at all, which is
   the single biggest reason it read as a document. */
.media{position:relative;overflow:hidden;background:var(--navy-3)}
.media img{width:100%;height:100%;object-fit:cover;display:block}
.media-band{height:clamp(240px,34vw,460px)}
.media-hero{height:clamp(420px,52vw,660px)}
/* A scrim, so overlaid type is legible on ANY photograph that eventually lands here —
   including a bright one. Without it the first real photo breaks every caption. */
.media::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(10,19,48,.62) 0%,rgba(10,19,48,.22) 42%,rgba(10,19,48,.78) 100%)}
.media-cap{position:absolute;z-index:2;left:0;right:0;bottom:0;padding:var(--s4) 0}
.media-cap .wrap{color:#fff}
.media-cap h2,.media-cap h3{color:#fff}
.media-cap p{color:#cfd9ef;max-width:56ch;margin:var(--s2) 0 0}

/* ── the step rail ───────────────────────────────────────────────────────────
   ⭐ Modelled on the signup flow's, deliberately. Connected segments, filled markers,
   the current one accented — so a visitor arriving from the site recognises the flow
   when they get there. v1 rendered this as four bordered text blocks. */
.rail{counter-reset:r;display:grid;gap:0;margin-top:var(--s4)}
.rail-step{display:grid;grid-template-columns:56px 1fr;gap:var(--s3);position:relative;
  padding-bottom:var(--s4)}
.rail-step:last-child{padding-bottom:0}
/* the connecting line, drawn behind the marker */
.rail-step::before{content:"";position:absolute;left:27px;top:44px;bottom:0;width:2px;
  background:linear-gradient(180deg,var(--rule),var(--line))}
.rail-step:last-child::before{display:none}
.rail-mark{width:56px;height:56px;border-radius:50%;display:grid;place-items:center;
  background:var(--card);border:2px solid var(--rule);font-family:var(--display);
  font-size:19px;font-weight:800;color:var(--navy);position:relative;z-index:1}
.rail-step.on .rail-mark{background:var(--magenta);border-color:var(--magenta);color:#fff;
  box-shadow:0 10px 26px -12px rgba(206,53,138,.8)}
.rail-step h3{margin-bottom:6px}
.rail-step p{margin:0;color:var(--body)}
.rail-when{display:inline-block;margin-top:8px;font-size:12px;font-weight:800;
  letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}

/* ── emphasis bar ────────────────────────────────────────────────────────────
   The signup flow's promo bar: full-width navy→magenta, one claim, one pill. It is the
   loudest thing on that page and it works. */
.bar{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--s2);flex-wrap:wrap;
  background:linear-gradient(92deg,var(--navy) 0%,var(--navy) 34%,var(--magenta) 100%);
  color:#fff;border-radius:14px;padding:16px var(--s3)}
.bar b{font-family:var(--display);font-size:clamp(15px,1.8vw,19px);font-weight:800}
.bar span{font-size:14.5px;color:#f0d7e8}
.bar .pill{background:rgba(255,255,255,.17);border:1px solid rgba(255,255,255,.34);
  border-radius:99px;padding:6px 14px;font-size:11.5px;font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;white-space:nowrap}
@media (max-width:640px){.bar .pill{margin-left:0}}

/* ── the promo line on a plan tile (.tier-promo) ─────────────────────────────
   Sits UNDER the standard price, deliberately smaller than it: the big number is the
   ongoing rate somebody budgets against, this is the 3-month intro on top. Reversing that
   weighting would advertise an intro as the price. */
.tier-promo{display:block;margin:10px 0 0;font-size:14px;line-height:1.5;
  padding:9px 12px;border-radius:10px;
  background:color-mix(in srgb,var(--magenta) 8%,transparent);
  border:1px solid color-mix(in srgb,var(--magenta) 26%,transparent);
  color:var(--ink)}
.tier-promo b{font-weight:800;color:var(--magenta)}
/* On the navy tile the magenta tint and ink colour both disappear — give it its own
   treatment rather than letting it inherit an unreadable pair. */
.tier-promo.onnavy{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.26);
  color:#e6ecf9}
.tier-promo.onnavy b{color:#ffb3dd}

/* ── the running-promo callout (.deal) ───────────────────────────────────────
   Owner 2026-09-15: make the promo pronounced, because a customer reading the standard
   $79.99 tile has no idea a $40 intro is running on their street.
   ⚠️ IT MUST NOT OUT-SHOUT THE PRICE TILES BELOW IT. Those carry the ongoing rate, which
   is the number somebody budgets against; this carries a 3-month intro. So: full-width
   band, magenta rule and a tint — loud enough to read first, not a second set of prices
   competing with the real ones.
   ⚠️ The term ("first 3 months") is part of .deal-h, not fine print, and must stay in the
   same visual weight as the number. See assets/promo.js for why. */
.deal{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;
  background:color-mix(in srgb,var(--magenta) 7%,var(--card));
  border:1px solid color-mix(in srgb,var(--magenta) 28%,var(--line));
  border-left:4px solid var(--magenta);
  border-radius:14px;padding:18px var(--s3);margin:0 0 var(--s4)}
.deal-l{flex:1 1 320px;min-width:0}
.deal-kicker{display:inline-block;font-size:11px;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--magenta);margin-bottom:6px}
.deal-h{font-family:var(--display);font-size:clamp(18px,2.2vw,24px);font-weight:800;
  line-height:1.25;margin:0;color:var(--ink)}
.deal-h b{color:var(--magenta)}
.deal-s{font-size:14.5px;line-height:1.55;color:var(--muted);margin:6px 0 0;max-width:62ch}
.deal-cta{flex:0 0 auto;align-self:center;white-space:nowrap}
@media (max-width:640px){
  .deal{padding:16px var(--s2)}
  .deal-cta{width:100%;text-align:center}
}

/* ── price treatment ─────────────────────────────────────────────────────────
   ⚠️ The v1 price was 34px, flat navy, with nothing to compare it to. The flow shows
   $20 at 40px with $59.99 struck through beside it and a green "save 67%" pill. The
   comparison is most of the persuasion. */
.px{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:2px 0 var(--s2)}
.px b{font-family:var(--display);font-size:clamp(34px,4vw,44px);font-weight:800;
  color:var(--ink);letter-spacing:-.035em;line-height:1;font-variant-numeric:tabular-nums}
.px b small{font-size:15px;font-weight:600;color:var(--muted);letter-spacing:0}
/* ⚠️ THE RETAIL FIGURE HAS TO BE READABLE (owner: "make sure the markdown pricing is
   clear, can really see the true retail rate figure"). At 17px in --muted it was quieter
   than the plan's own subtitle — the discount was being made and then whispered. It now
   sits in a bordered chip at 19px with the word "retail" attached, so it reads as a
   comparison rather than as a stray number. */
/* ⚠️ NEUTRAL, NOT RED. A red strikethrough on a retail price is infomercial grammar —
   it shouts about the discount instead of stating it, and this site's whole argument is
   that our pricing needs no theatre. The comparison reads perfectly well in the body
   colour at a size you cannot miss; the saving pill does the celebrating, once.
   No chip, no border: the label sits on the baseline with the price, which is cleaner
   and stops the row turning into a strip of badges. */
.px s{color:var(--muted);font-size:19px;font-weight:600;text-decoration-thickness:1.5px;
  text-decoration-color:rgba(106,115,145,.75);font-variant-numeric:tabular-nums}
.px .was{display:inline-flex;align-items:baseline;gap:6px}
.px .was em{font-style:normal;font-size:12px;font-weight:600;letter-spacing:.02em;
  color:var(--muted)}
/* on the dark cards the chip has to invert or it disappears */
.asym-card.us .px s{color:#a9c8f2;text-decoration-color:rgba(169,200,242,.7)}
.asym-card.us .px .was em{color:#8fa6d6}
.save{background:rgba(14,122,82,.10);color:var(--ok);border-radius:6px;padding:3px 9px;
  font-size:12px;font-weight:700;letter-spacing:.02em;white-space:nowrap}
.tier{display:inline-grid;place-items:center;width:38px;height:38px;border-radius:11px;
  background:linear-gradient(140deg,rgba(61,155,233,.16),rgba(206,53,138,.14));
  color:var(--navy);font-family:var(--display);font-weight:800;font-size:12.5px;
  letter-spacing:-.02em;margin-bottom:10px}

/* ── stat row ────────────────────────────────────────────────────────────────
   The numbers from the build, on a light ground so they can sit mid-page rather than
   only in the hero. */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s3);
  border-block:1px solid var(--line);padding:var(--s4) 0}
.stats b{display:block;font-family:var(--display);font-size:clamp(26px,3.4vw,38px);
  font-weight:800;color:var(--ink);letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.stats span{font-size:12.5px;color:var(--muted);letter-spacing:.05em;text-transform:uppercase;
  font-weight:700}
@media (max-width:760px){.stats{grid-template-columns:repeat(2,1fr);gap:var(--s3) var(--s2)}}

/* ── quote, v2 ───────────────────────────────────────────────────────────────
   Owner: "I like what your neighbours are saying but need to make it better." It was a
   bordered box with small text. A single real quote deserves to be treated as the
   statement it is — large, on its own ground, with the mark it earns. */
.qbig{position:relative;background:linear-gradient(168deg,var(--navy-3),var(--navy) 70%,#2c4a96);
  color:#fff;border-radius:20px;padding:clamp(28px,5vw,56px);overflow:hidden}
.qbig::before{content:"\201C";position:absolute;top:-26px;left:18px;font-family:var(--display);
  font-size:200px;line-height:1;color:rgba(255,255,255,.09);pointer-events:none}
.qbig blockquote{margin:0;position:relative;z-index:1;font-family:var(--display);
  font-size:clamp(21px,3vw,34px);font-weight:700;line-height:1.3;letter-spacing:-.02em;
  color:#fff;text-wrap:balance}
.qbig .who{margin:var(--s3) 0 0;position:relative;z-index:1;color:#c9d4ee;font-size:15px}
.qbig .who b{color:#fff;font-family:var(--display)}
.qbig .stars{color:#ffc857;letter-spacing:4px;font-size:17px;margin-bottom:var(--s2);
  position:relative;z-index:1}

/* ── the build calendar ──────────────────────────────────────────────────────
   ⭐ The one thing on this site a national ISP structurally cannot publish, and v1 drew
   it with 7px bars and 13px grey text — a footnote treatment for the lead asset. */
.cal-head{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;
  padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);background:var(--paper-2)}
.cal-head b{display:block;font-family:var(--display);font-size:clamp(24px,3vw,34px);
  font-weight:800;color:var(--ink);letter-spacing:-.03em;line-height:1;
  font-variant-numeric:tabular-nums}
.cal-head span{font-size:12px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--muted)}
.cal-live{margin-left:auto;display:flex;align-items:center;gap:8px;font-size:12.5px;
  font-weight:700;color:var(--ok);letter-spacing:.03em}
.cal-live i{width:8px;height:8px;border-radius:50%;background:var(--ok);flex:none;
  box-shadow:0 0 0 4px rgba(14,122,82,.15)}
.cal-rows{padding:6px 0}
.cal-row{padding:var(--s3) var(--s4);border-bottom:1px solid var(--line-2)}
.cal-row:last-child{border-bottom:0}
.cal-row.next{background:linear-gradient(90deg,rgba(206,53,138,.05),transparent 60%)}
.cal-top{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:11px}
.cal-month{font-family:var(--display);font-size:clamp(17px,2.1vw,21px);font-weight:800;
  color:var(--ink);letter-spacing:-.02em}
.cal-tag{background:var(--magenta);color:#fff;border-radius:99px;padding:3px 10px;
  font-size:10.5px;font-weight:800;letter-spacing:.09em;text-transform:uppercase}
.cal-n{margin-left:auto;font-family:var(--display);font-size:clamp(19px,2.4vw,25px);
  font-weight:800;color:var(--navy);letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.cal-n small{font-size:12px;font-weight:700;color:var(--muted);margin-left:6px;
  letter-spacing:.04em;text-transform:uppercase}
.cal-track{height:14px;border-radius:99px;background:var(--paper-2);overflow:hidden}
.cal-track em{display:block;height:14px;border-radius:99px;
  background:linear-gradient(90deg,var(--blue),var(--magenta))}
.cal-chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:11px}
.cal-chip{font-size:12.5px;color:var(--body);background:var(--paper-2);
  border:1px solid var(--line);border-radius:99px;padding:4px 11px}
.cal-chip b{color:var(--ink);font-variant-numeric:tabular-nums}
@media (max-width:640px){
  .cal-head{gap:var(--s3);padding:var(--s3)}
  .cal-live{margin-left:0;width:100%}
  .cal-row{padding:var(--s3)}
  .cal-n{margin-left:0;width:100%}
}

/* ── the sticky availability bar ─────────────────────────────────────────────
   ⭐ Owner: "maybe add a pop up banner to CTA to check availability so see if they qualify."
   ⚠️ A BAR, NOT A MODAL. A dialog that covers the page you are reading is the pattern
   everyone has learned to close without reading, and on a phone it is worse. This sits on
   the bottom edge, never covers content you are mid-sentence in, and carries the actual
   field rather than a button that leads to a field.
   ⚠️ Appears only AFTER the hero has scrolled away — while the hero checker is on screen
   there are two identical asks competing, which is how you get neither used.
   ⚠️ Dismissible, and the dismissal STICKS for the session. Being asked twice is a
   reminder; being asked on every page is nagging. */
.avail{position:fixed;left:0;right:0;bottom:0;z-index:90;transform:translateY(110%);
  transition:transform .32s cubic-bezier(.16,.8,.3,1);
  background:linear-gradient(96deg,var(--navy-3) 0%,var(--navy) 46%,#6c2f7a 100%);
  color:#fff;box-shadow:0 -18px 46px -22px rgba(10,19,48,.7);
  padding:14px 0 calc(14px + env(safe-area-inset-bottom,0px))}
.avail.on{transform:none}
.avail-in{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap}
.avail-txt{flex:1 1 260px;min-width:0}
.avail-txt b{display:block;font-family:var(--display);font-size:16.5px;font-weight:800;
  letter-spacing:-.015em}
.avail-txt span{font-size:13.5px;color:#c4cfe9}
.avail form{display:flex;gap:9px;flex:1 1 420px;min-width:0}
.avail input{flex:1;min-width:0;border:1.5px solid rgba(255,255,255,.3);border-radius:11px;
  padding:12px 14px;font-size:15.5px;font-family:var(--sans);background:#fff;color:var(--ink)}
.avail .btn{white-space:nowrap}
.avail-x{background:transparent;border:0;color:#a9b8dc;font-size:26px;line-height:1;
  cursor:pointer;padding:6px 10px;min-width:44px;min-height:44px;flex:none}
.avail-x:hover{color:#fff}
@media (max-width:760px){
  .avail{padding:12px 0 calc(12px + env(safe-area-inset-bottom,0px))}
  .avail-in{gap:10px}
  .avail-txt{flex:1 1 100%;padding-right:40px}
  .avail-txt b{font-size:15px}
  .avail form{flex:1 1 100%}
  .avail .btn{padding:12px 18px}
  .avail-x{position:absolute;top:8px;right:10px}
}

/* ── the evening congestion chart ────────────────────────────────────────────
   ⭐ The "your own line" argument, made visible. People do not experience "shared
   segment" as a concept — they experience the 7pm dip. This draws exactly that, and it is
   the chart most likely to make someone recognise their own house.
   ⚠️ Shape, not numbers. The axis is deliberately unlabelled in Mbps: the honest claim is
   that a shared medium degrades at peak and a dedicated one does not, and putting a
   specific competitor's evening figure on it would be a claim we cannot source. */
/* ⚠️⚠️ A DARK PANEL, AND THE SVG'S COLOURS ASSUME IT. The chart's text is set in light
   blues (#94a3c8, #9ecbf6) so the brand-gradient fiber line can carry the top of the
   frame; on the light `--card` surface this was here before, every axis label and the
   "flat, all day" caption rendered as pale grey on white. Both halves have to move
   together — if this ever goes back to a light surface, the fills in
   partials/tod-chart.html have to change in the same edit. */
.tod{background:linear-gradient(165deg,#0d1733,var(--navy-3) 60%,#1c3068);
  border:1px solid rgba(143,196,244,.16);border-radius:18px;color:#dbe4f8;
  padding:clamp(20px,3vw,32px);box-shadow:0 26px 60px -38px rgba(16,24,54,.8)}
/* Desktop: the box matches the viewBox, so `slice` crops nothing. */
.tod svg{width:100%;aspect-ratio:760/300;height:auto;display:block}
/* ⚠️ A 760×260 CHART INSIDE A 304px COLUMN IS 104px TALL. Measured on a phone: the axis
   labels, the 7–10PM callout and the "when you need it" annotation were all rendering at
   roughly five pixels. Scaling a chart down until it is unreadable is worse than making
   the reader swipe, so on narrow screens it keeps a legible size and scrolls sideways
   inside its own container — the page itself never scrolls horizontally.
   The hint is there because a chart that scrolls with no indication that it scrolls is a
   chart people see two thirds of. */
/* ⚠️ NO SIDEWAYS SCROLL. It used to scroll, which showed a phone 6am-to-noon — the flat
   half — and hid the dip behind a swipe most people never make. The box is now taller and
   narrower than the viewBox, so `xMaxYMid slice` crops the morning away and the evening
   fills the frame. Nothing is lost that the chart was actually about. */
@media (max-width:720px){
  .tod{padding-inline:var(--s3)}
  .tod svg{aspect-ratio:auto;height:230px}
  .tod-cap{display:none}
  /* ⚠️ NOT inline-flex on a phone. The legend row is `<i><b>Aspire Fiber</b> — text`, and
     as a flex container the <b> and the text become separate ITEMS, so the name broke onto
     its own line with a gap beside it. Block with an absolutely-placed swatch lets the
     sentence wrap as one sentence. */
  .tod-key{font-size:13.5px;gap:10px;flex-direction:column}
  .tod-key span{display:block;position:relative;padding-left:32px;line-height:1.45}
  .tod-key i{position:absolute;left:0;top:9px}
}
.tod-key{display:flex;gap:var(--s3);flex-wrap:wrap;margin-top:var(--s3);font-size:14px}
.tod-key span{display:inline-flex;align-items:center;gap:8px;color:#b9c4e0}
.tod-key b{color:#fff;font-family:var(--display)}
.tod-key i{width:22px;height:4px;border-radius:99px;display:inline-block}

/* ── the coverage map, as the hero's ground ──────────────────────────────────
   ⚠️ FULL-BLEED, NOT A PANEL. The real footprint is roughly 2:1 (about 32 miles by 16),
   which boxed into a column would be a letterboxed strip. Fades in when the data lands so
   the hero never flashes an empty rectangle.
   ⚠️ Sits UNDER the drafting grid and the text; opacity is deliberately low, because a
   map nobody can read the headline over is a worse map. */
/* ⚠️⚠️ width/height ARE REQUIRED — `inset:0` ALONE DOES NOT SIZE A CANVAS. A <canvas> is a
   REPLACED element with intrinsic dimensions (300×150 by default), so `width:auto` resolves
   to that intrinsic width rather than to the inset box. The map was drawing into a 300×150
   backing store stretched across a 1440px hero, and nothing about the markup or the JS
   looked wrong — the element reported position:absolute and inset:0px and was still 300×150.
   Found by reading getBoundingClientRect, not by reading the CSS. */
.covermap{position:absolute;inset:0;width:100%;height:100%;z-index:0;opacity:0;
  transition:opacity 1.1s ease;pointer-events:none}
.covermap.ready{opacity:.72}
/* Mask it away from the left column, where the headline and the address field live. */
/* ⚠️ THE MASK HAS TO CLEAR THE TEXT COLUMN, AND THE GEOGRAPHY FIGHTS IT. Moorpark sits at
   the far WEST of the footprint, which puts it bottom-left — directly under the headline and
   the address field. A simple left-to-right fade was not enough. Two masks composited: a
   diagonal that clears the left column, and an ellipse that specifically protects the
   bottom-left where the form sits. */
.hero .covermap{
  -webkit-mask-image:linear-gradient(104deg,transparent 30%,#000 62%),
                     radial-gradient(72% 78% at 22% 82%,transparent 55%,#000 86%);
          mask-image:linear-gradient(104deg,transparent 30%,#000 62%),
                     radial-gradient(72% 78% at 22% 82%,transparent 55%,#000 86%);
  -webkit-mask-composite:source-in; mask-composite:intersect}
.mapkey{position:absolute;right:22px;bottom:18px;z-index:2;display:flex;gap:14px;
  flex-wrap:wrap;font-size:11.5px;color:#a9b8dc;letter-spacing:.03em}
.mapkey span{display:inline-flex;align-items:center;gap:6px}
.mapkey i{width:8px;height:8px;border-radius:50%;display:inline-block;flex:none}
@media (max-width:880px){
  .hero .covermap{-webkit-mask-image:linear-gradient(180deg,transparent 4%,#000 42%);
                          mask-image:linear-gradient(180deg,transparent 4%,#000 42%)}
  .covermap.ready{opacity:.4}
  .mapkey{position:static;padding:0 22px var(--s3);justify-content:flex-start}
}

/* ── the hero network map (superseded — kept for the netmap partial if reused) ────────
   Superseded; assets/netmap.js was deleted 2026-09-14 once nothing referenced it. */
.netmap{position:relative;border-radius:20px;overflow:hidden;
  background:radial-gradient(120% 100% at 70% 20%,rgba(61,155,233,.14),transparent 60%),
             linear-gradient(160deg,rgba(10,19,48,.55),rgba(17,31,69,.35));
  border:1px solid rgba(255,255,255,.14);aspect-ratio:1/.86;min-height:280px}
.netmap canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
.netmap-key{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:16px 18px;
  background:linear-gradient(180deg,transparent,rgba(10,19,48,.72));
  display:flex;gap:16px;flex-wrap:wrap;font-size:12.5px;color:#c9d4ee}
.netmap-key span{display:inline-flex;align-items:center;gap:7px}
.netmap-key i{width:9px;height:9px;border-radius:50%;display:inline-block;flex:none}
@media (max-width:880px){.netmap{aspect-ratio:1/.62;min-height:240px}}

/* ── review carousel ─────────────────────────────────────────────────────────
   The slides are stacked and cross-faded rather than translated, so the block never
   changes height mid-rotation — a carousel that jumps the page as it advances is worse
   than one that does not move at all. */
.qslides{position:relative}
.qslide{margin:0;position:absolute;inset:0;opacity:0;transition:opacity .5s ease;
  pointer-events:none}
.qslide.on{position:relative;opacity:1;pointer-events:auto}
.qcar .qslides{min-height:1px}
.qnav{display:flex;align-items:center;gap:14px;margin-top:var(--s4);position:relative;z-index:2}
/* ⚠️ THE DOT IS 9px; THE TARGET MUST NOT BE. These only became reachable at all once the
   carousel had more than one review, and the audit immediately flagged them at 9px on a
   phone. The hit area is padding on the button with the dot drawn by ::before, so the row
   still looks like small dots while every one of them is a 44px target. Negative margin
   keeps the padded buttons from spreading the row apart. */
.qdots{display:flex;gap:0;margin:0 auto}
.qdot{width:44px;height:44px;border:0;padding:0;margin:0 -6px;cursor:pointer;
  background:none;display:grid;place-items:center;border-radius:50%;
  -webkit-tap-highlight-color:transparent}
.qdot::before{content:"";display:block;width:9px;height:9px;border-radius:50%;
  background:rgba(255,255,255,.3);transition:width .18s ease,background .18s ease}
.qdot.on::before{background:#fff;width:26px;border-radius:99px}
.qdot:focus-visible{outline:2px solid #fff;outline-offset:-6px}
.qarrow{width:44px;height:44px;border-radius:50%;border:1px solid rgba(255,255,255,.28);
  background:rgba(255,255,255,.07);color:#fff;font-size:22px;line-height:1;cursor:pointer;
  transition:.18s}
.qarrow:hover{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.5)}

/* ── milestone timeline ──────────────────────────────────────────────────────
   For a company whose whole argument is "we built this ourselves", the sequence IS the
   story. A bulleted list of the same facts carries none of the momentum. */
.tl{position:relative;margin-top:var(--s4);padding-left:0}
.tl-item{display:grid;grid-template-columns:92px 1fr;gap:var(--s3);position:relative;
  padding-bottom:var(--s4)}
.tl-item:last-child{padding-bottom:0}
.tl-item::before{content:"";position:absolute;left:99px;top:26px;bottom:-6px;width:2px;
  background:linear-gradient(180deg,var(--rule),var(--line))}
.tl-item:last-child::before{display:none}
.tl-when{text-align:right;font-family:var(--display);font-size:14px;font-weight:800;
  color:var(--navy);letter-spacing:-.01em;padding-top:2px;position:relative}
.tl-when::after{content:"";position:absolute;right:-19px;top:5px;width:12px;height:12px;
  border-radius:50%;background:var(--card);border:2.5px solid var(--rule);z-index:1}
.tl-item.on .tl-when::after{background:var(--magenta);border-color:var(--magenta);
  box-shadow:0 0 0 5px rgba(206,53,138,.14)}
.tl-body{padding-left:var(--s2)}
.tl-body h3{font-size:19px;margin-bottom:6px}
.tl-body p{margin:0;color:var(--body)}
@media (max-width:560px){
  .tl-item{grid-template-columns:1fr;gap:4px}
  .tl-item::before{display:none}
  .tl-when{text-align:left}
  .tl-when::after{display:none}
  .tl-body{padding-left:0;border-left:2.5px solid var(--rule);padding-left:var(--s2)}
}

/* ── triage cards ────────────────────────────────────────────────────────────
   Support's three checks are a decision, not a list — so they are numbered, weighted,
   and each one says what the ANSWER means rather than just what to do. */
.triage{display:grid;gap:var(--s3);counter-reset:t}
.tri{background:var(--card);border:1px solid var(--line);border-radius:16px;
  padding:var(--s3);position:relative;overflow:hidden;color:var(--body)}
/* ⚠️ WAS var(--paper-2) ON WHITE — luminance difference of about 4, which is to say
   invisible. A decorative numeral still has to be legible enough to do its job, and this
   one is the only thing telling you these three cards are a SEQUENCE. Now a tinted blue
   with enough weight to read as a step number without competing with the heading.
   ⚠️ This is a ::before, which is exactly why the contrast audit missed it — pseudo-element
   content is not in the DOM. The audit now inspects ::before/::after too. */
.tri::before{counter-increment:t;content:counter(t);position:absolute;right:14px;top:4px;
  font-family:var(--display);font-size:64px;font-weight:800;
  color:rgba(61,155,233,.20);line-height:1;pointer-events:none}
.tri:nth-child(2)::before{color:rgba(124,108,224,.20)}
.tri:nth-child(3)::before{color:rgba(206,53,138,.20)}
.tri h3{position:relative;margin-bottom:8px;padding-right:58px}
.tri p{position:relative;margin:0 0 var(--s2);padding-right:58px}
.tri-out{position:relative;display:grid;gap:7px;margin-top:var(--s2);padding-top:var(--s2);
  border-top:1px solid var(--line-2);font-size:14px}
.tri-out span{display:flex;gap:9px;align-items:flex-start}
.tri-out b{flex:none;font-family:var(--display);font-size:12px;font-weight:800;
  letter-spacing:.05em;text-transform:uppercase;padding:2px 8px;border-radius:6px;
  background:var(--paper-2);color:var(--navy);min-width:102px;text-align:center}
.tri-out .ok b{background:rgba(14,122,82,.12);color:var(--ok)}
.tri-out .us b{background:rgba(206,53,138,.12);color:var(--magenta)}
@media (min-width:820px){.triage{grid-template-columns:repeat(3,1fr)}}

/* ── concurrency ─────────────────────────────────────────────────────────────
   What a gigabit actually buys, in things people do rather than in megabits. */
.conc{display:grid;gap:var(--s2);margin-top:var(--s3)}
.conc-row{display:grid;grid-template-columns:minmax(140px,1fr) auto;gap:var(--s3);
  align-items:center;padding:12px 0;border-bottom:1px solid var(--line-2)}
@media (max-width:560px){
  .conc-row{grid-template-columns:1fr;gap:10px}
  .conc-dots{justify-content:start}
}
.conc-row:last-child{border-bottom:0}
.conc-row b{font-family:var(--display);font-size:15px;color:var(--ink)}
.conc-row span{font-size:13px;color:var(--muted);display:block;margin-top:2px}
/* ⚠️ A FIXED 10-WIDE GRID, NOT WRAPPING FLEX. Flex-wrapped at max-width:320px the twenty
   squares broke 18 + 2, leaving two orphans on their own line and making the rows look
   broken rather than comparable. Ten per row means every tier is exactly two rows and the
   difference between them is the thing you see. */
.conc-dots{display:grid;grid-template-columns:repeat(10,13px);gap:5px;justify-content:end}
.conc-dots i{width:13px;height:13px;border-radius:4px;display:block;
  background:linear-gradient(140deg,var(--blue),var(--magenta))}
.conc-dots i.off{background:var(--paper-2);border:1px solid var(--line)}

/* ── promo banner ────────────────────────────────────────────────────────────
   ⚠️ Ships EMPTY and is filled from /api/offer — see assets/promo.js. A promo typed into
   the HTML keeps advertising after it ends, which is the one kind of stale this site
   cannot afford. */
.promo{display:none;background:linear-gradient(92deg,#16143a 0%,var(--navy) 30%,var(--magenta) 100%);
  color:#fff;position:relative;z-index:95;overflow:hidden}
/* The leaf drift. Behind the content and clipped to the bar; .promo-in lifts above it so
   nothing ever draws over the price. */
.leafc{position:absolute;inset:0;pointer-events:none;z-index:0}
.promo.on{display:block}
/* ⚠️ padding-BLOCK. This element carries BOTH `.wrap` (padding:0 22px) and `.promo-in`;
   the `padding:11px 0` shorthand that was here reset left/right to 0, so on a phone the
   promo text started flush at x=0 and "save 56%" was clipped off the right edge. Exactly
   the same bug `.hero-in` carries a warning about — it recurs because the shorthand looks
   harmless. */
.promo-in{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding-block:11px;
  position:relative;z-index:1}
.promo-tag{font-family:var(--display);font-size:10.5px;font-weight:800;letter-spacing:.11em;
  text-transform:uppercase;background:rgba(255,255,255,.18);border:1px solid rgba(255,255,255,.3);
  border-radius:99px;padding:4px 11px;white-space:nowrap}
.promo-txt{font-size:14.5px;color:#f2dcec}
.promo-txt b{font-family:var(--display);font-size:16px;font-weight:800;color:#fff}
.promo-txt s{opacity:.7}
.promo-txt i{font-style:normal;font-weight:800;font-size:12.5px;background:rgba(255,255,255,.2);
  border-radius:99px;padding:3px 9px;margin-left:4px}
/* ⚠️ A 44px target. It measured 26px — it is a standalone link in its own block, so the
   WCAG inline exemption does not cover it. Padding rather than a bigger font, so the bar
   keeps its height. */
.promo-cta{margin-left:auto;color:#fff;font-weight:800;font-size:14.5px;text-decoration:none;
  white-space:nowrap;
  display:inline-flex;align-items:center;min-height:44px;padding:0 2px}
.promo-cta:hover{opacity:.85}
.promo-ends{font-size:12px;color:#f0d0e4;letter-spacing:.04em;white-space:nowrap}
.promo-x{background:transparent;border:0;color:#e7c9dd;font-size:22px;line-height:1;
  cursor:pointer;padding:4px 8px;min-width:40px;min-height:40px}
.promo-x:hover{color:#fff}
/* The nav sticks below the banner rather than over it. */
.has-promo .nav{top:0}
@media (max-width:760px){
  .promo-in{gap:9px;padding-block:10px}
  /* ⚠️ A 92deg navy→magenta ramp across 1440px puts magenta in the last third; across
     390px it puts magenta across most of the bar, and the banner plus the hero together
     made the whole first screen read purple. Hold the navy far longer at this width. */
  .promo{background:linear-gradient(103deg,#141235 0%,var(--navy) 46%,#7a2a72 84%,var(--magenta) 100%)}
  .promo-cta{margin-left:0;width:100%;order:5}
  .promo-x{position:absolute;top:4px;right:6px}
  .promo-ends{order:4}
}

/* ── heading colour ──────────────────────────────────────────────────────────
   ⚠️ SECTION HEADINGS ONLY. h1 stays ink (it is already the loudest thing on the page and
   the hero's is white anyway), and headings inside cards stay ink so they do not compete
   with the card's own accent. If every heading were navy the distinction would carry no
   information — this is meant to mark "new section", not "heading". */
.sect > .wrap > h2,
.sect-tight > .wrap > h2{color:var(--navy)}

/* ── the lanes centrepiece ───────────────────────────────────────────────────
   Dark panel so the packets glow and the whole thing separates from the page. It is a
   mechanism diagram, so it gets the space a diagram needs rather than sitting in a card. */
/* ⚠️ THE HEADINGS AND LEGEND INSIDE THIS PANEL ARE DRAWN ON THE CANVAS by assets/lanes.js,
   not in the DOM — CSS cannot restyle them. Anything here can only touch the container and
   the footer, which is why the polish below stays there. Owner 2026-09-15: "looks super
   solid already ... just want it to pop slightly more", so this is deliberately restrained:
   a brand hairline, a deeper lift, and a footer that colour-codes to the two halves of the
   diagram. Do not start competing with the drawing. */
.lanes{position:relative;border-radius:20px;overflow:hidden;
  background:
    radial-gradient(90% 70% at 80% 10%,rgba(206,53,138,.16),transparent 60%),
    radial-gradient(80% 70% at 10% 90%,rgba(61,155,233,.18),transparent 62%),
    linear-gradient(160deg,#070e24,var(--navy-3) 60%,#1a2d61);
  border:1px solid rgba(143,196,244,.22);
  box-shadow:0 38px 84px -44px rgba(10,19,48,.95), 0 0 0 1px rgba(143,196,244,.06) inset}
/* The brand ramp along the top edge: reads as a designed object rather than a screenshot. */
.lanes::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;z-index:2;
  background:linear-gradient(90deg,var(--navy) 0%,#3d9be9 46%,var(--magenta) 100%)}
.lanes canvas{display:block;width:100%;height:clamp(370px,34vw,420px)}
.lanes-foot{display:flex;gap:clamp(18px,2.6vw,34px);flex-wrap:wrap;
  justify-content:space-between;
  padding:clamp(16px,2vw,22px) clamp(18px,2.4vw,26px) clamp(18px,2.4vw,26px);
  color:#c9d4ee;font-size:14.5px;line-height:1.55;
  border-top:1px solid rgba(143,196,244,.14)}
.lanes-foot b{color:#fff;font-family:var(--display);display:block;margin-bottom:3px;
  font-size:15.5px;letter-spacing:-.005em}
/* Each note carries the colour of the half it describes, so the words and the drawing
   connect without a legend. First = the shared pipe (magenta), second = your own lane. */
.lanes-foot div{flex:1 1 260px;padding-left:14px;border-left:3px solid rgba(143,196,244,.3)}
.lanes-foot div:first-child{border-left-color:var(--magenta)}
.lanes-foot div:first-child b{color:#ffb3dd}
.lanes-foot div:last-child{border-left-color:#3d9be9}
.lanes-foot div:last-child b{color:#9ed0ff}

/* Single-column hero: the map is the whole ground now, so the text column no longer
   shares the row with a panel. Capped so the lede does not run the full width. */
.hero-solo{grid-template-columns:1fr}
.hero-solo > div{max-width:820px}

/* ── fiber to the home vs fiber to the node ─────────────────────────────────── */
.ftth{background:var(--card);border:1px solid var(--line);border-radius:18px;
  padding:clamp(20px,3vw,30px);display:grid;gap:var(--s3)}
.ftth-row{display:grid;grid-template-columns:190px 1fr;gap:var(--s3);align-items:center}
.ftth-row.is-us{background:linear-gradient(96deg,rgba(61,155,233,.07),rgba(206,53,138,.05));
  border-radius:14px;padding:12px;margin:-12px}
.ftth-lab b{display:block;font-family:var(--display);font-size:16px;color:var(--ink)}
.ftth-lab span{font-size:12.5px;color:var(--muted)}
.ftth svg{width:100%;height:auto;display:block}
.ftth-note{margin:var(--s3) 0 0;font-size:13.5px;color:var(--muted);max-width:none}
@media (max-width:680px){
  .ftth-row{grid-template-columns:1fr;gap:8px}
  /* Scale the diagram to the screen rather than forcing a 460px min-width that made it
     scroll sideways — visitors could not tell it scrolled and read it as cut off. */
  .ftth svg{width:100%}
}

/* ── seesaw ─────────────────────────────────────────────────────────────────── */
.seesaw{display:grid;gap:var(--s3)}
.see{background:var(--card);border:1px solid var(--line);border-radius:18px;
  padding:clamp(18px,2.6vw,26px);text-align:center}
.see.is-us{border-color:rgba(61,155,233,.4);
  box-shadow:0 16px 40px -30px rgba(61,155,233,.7)}
.see-cap{display:block;font-family:var(--display);font-size:14px;font-weight:800;
  color:var(--ink);margin-bottom:6px}
.see svg{width:100%;max-width:300px;height:auto;margin:0 auto;display:block}
@media (min-width:720px){.seesaw{grid-template-columns:1fr 1fr}}

/* ── coverage market toggle ─────────────────────────────────────────────────── */
.mkt{border:1px solid var(--line);border-radius:20px;overflow:hidden;
  background:linear-gradient(160deg,#070e24,var(--navy-3) 62%,#1a2d61)}
.mkt-tabs{display:flex;gap:4px;padding:14px 16px 0}
.mkt-tab{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.16);
  color:#c9d4ee;font-family:var(--display);font-size:14px;font-weight:700;
  padding:10px 18px;border-radius:11px 11px 0 0;cursor:pointer;min-height:44px}
.mkt-tab:hover{background:rgba(255,255,255,.13);color:#fff}
.mkt-tab.on{background:rgba(255,255,255,.15);border-bottom-color:transparent;color:#fff}
.mkt-stage{position:relative;height:clamp(320px,40vw,520px);margin-top:-1px;
  border-top:1px solid rgba(255,255,255,.16)}
/* ⚠️ The hero masks the map away from its text column; here the map IS the content, so the
   mask must not follow it in. Explicitly none. */
.mkt-canvas{-webkit-mask-image:none!important;mask-image:none!important;opacity:0}
.mkt-canvas.ready{opacity:1}
.mkt-hint{position:absolute;left:0;right:0;bottom:0;margin:0;padding:14px 18px;
  background:linear-gradient(180deg,transparent,rgba(7,14,36,.8));
  color:#c9d4ee;font-size:13.5px}
@media (max-width:560px){.mkt-tab{flex:1;padding:10px 8px;font-size:13px}}

/* ---- Address autocomplete (assets/ac.js) -------------------------------------
   Anchored to the input's row. The hero sits on navy and the availability bar on
   white, so the menu declares its OWN surface and ink rather than inheriting —
   a card inside .hero once inherited color:#fff and rendered white on white. */
.ac{position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:120;
  background:#fff;color:var(--ink);border:1px solid var(--line);border-radius:14px;
  box-shadow:0 18px 44px rgba(16,24,54,.22);overflow:auto;max-height:min(46vh,320px);
  text-align:left;padding:5px}
.ac.up{top:auto;bottom:calc(100% + 6px)}
.ac-row{display:flex;align-items:baseline;gap:10px;justify-content:space-between;
  padding:10px 12px;border-radius:10px;cursor:pointer;min-height:44px}
.ac-row.on,.ac-row:hover{background:var(--paper)}
.ac-main{font-size:15px;font-weight:600;color:var(--ink)}
.ac-main b{font-weight:800;color:var(--navy)}
.ac-sub{font-size:12.5px;color:var(--muted);white-space:nowrap;flex:none}
.ac-note{margin:0;padding:11px 12px;font-size:13.5px;color:var(--muted)}
@media (max-width:760px){
  .ac{max-height:min(40vh,260px)}
  /* Side by side, a long street name wraps under a right-aligned city and the row reads
     as two columns of nothing. Stack it: address first, city as its own quiet line. */
  .ac-row{flex-direction:column;align-items:flex-start;gap:1px;padding:9px 11px}
  .ac-sub{white-space:normal}
}

/* ---- Support ticket form (support.html + assets/ticket.js) --------------------
   ⚠️ Every surface here declares its own ink. A card that inherits colour was how
   white-on-white shipped once already. */
.tkt{display:grid;grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);gap:var(--s5);
  align-items:start}
.tkt-form{background:#fff;color:var(--ink);border:1px solid var(--line);border-radius:20px;
  padding:clamp(20px,3vw,34px);box-shadow:0 12px 34px rgba(16,24,54,.06)}
.tkt-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.fld{display:block}
.fld>span{display:block;font-size:12.5px;font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;color:var(--muted);margin:0 0 6px}
.fld input,.fld textarea,.fld select{width:100%;box-sizing:border-box;font:inherit;
  font-size:15.5px;color:var(--ink);background:var(--paper);border:1px solid var(--line);
  border-radius:12px;padding:12px 14px;min-height:46px}
.fld textarea{resize:vertical;line-height:1.5;min-height:120px}
.fld input:focus,.fld textarea:focus{outline:2px solid var(--blue);outline-offset:1px;
  background:#fff}
.fld-hint{margin:8px 0 var(--s3);font-size:13px;color:var(--muted)}
.tkt-form>.fld{margin-top:var(--s3)}
/* A field the API rejected. Colour is never the only signal — the message says which. */
.fld.bad input,.fld.bad textarea{border-color:var(--magenta);background:#fff7fb}

.chips{border:0;padding:0;margin:var(--s3) 0 0;min-width:0}
.chips legend{font-size:12.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted);padding:0;margin-bottom:9px}
.chips legend i{font-style:normal;font-weight:600;letter-spacing:0;text-transform:none;
  color:var(--muted);opacity:.8}
.chips label{display:inline-flex;align-items:center;gap:8px;margin:0 8px 8px 0;
  background:var(--paper);border:1px solid var(--line);border-radius:99px;
  padding:9px 14px;min-height:44px;cursor:pointer;font-size:14.5px;color:var(--ink)}
.chips input{accent-color:var(--magenta);margin:0;width:16px;height:16px}
.chips label:has(input:checked){border-color:var(--magenta);background:#fdeef7;
  box-shadow:inset 0 0 0 1px var(--magenta)}
.chips label:focus-within{outline:2px solid var(--blue);outline-offset:2px}
.chips.tight label{font-size:14px;padding:8px 13px}

.urg{display:flex;align-items:center;gap:10px;margin:var(--s3) 0 0;padding:13px 15px;
  border:1px solid var(--line);border-radius:14px;background:var(--paper);cursor:pointer;
  color:var(--ink);font-size:14.5px}
.urg input{accent-color:var(--magenta);width:18px;height:18px;margin:0;flex:none}
.urg:has(input:checked){border-color:var(--magenta);background:#fdeef7}

/* ⚠️ Off-screen, not display:none — some bots skip hidden inputs, and `hidden` is the
   first thing a scripted filler learns to check. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.tkt-go{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:var(--s4)}
.tkt-msg{font-size:14px;color:var(--muted)}
.tkt-msg.err{color:var(--magenta);font-weight:700}

.tkt-side{background:var(--navy);color:#fff;border-radius:20px;
  padding:clamp(20px,2.6vw,30px)}
.tkt-side h3{margin:0 0 var(--s3);color:#fff;font-size:18px}
.tkt-next{margin:0;padding:0;list-style:none;counter-reset:n}
.tkt-next li{counter-increment:n;position:relative;padding:0 0 var(--s3) 40px;
  font-size:14.5px;line-height:1.55;color:#d5ddf1}
.tkt-next li:last-child{padding-bottom:0}
.tkt-next li b{color:#fff}
.tkt-next li::before{content:counter(n);position:absolute;left:0;top:-2px;width:27px;
  height:27px;border-radius:50%;display:grid;place-items:center;
  font-family:var(--display);font-weight:800;font-size:13px;color:#fff;
  background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.3)}
.tkt-fine{margin:var(--s3) 0 0;font-size:13px;color:#b9c4e0;
  border-top:1px solid rgba(255,255,255,.16);padding-top:var(--s3)}

/* The sent state replaces the form entirely — a reference the visitor can copy. */
.tkt-done{text-align:center;padding:var(--s5) var(--s3)}
.tkt-done h2{margin:0 0 var(--s2)}
.tkt-ref{display:inline-block;font-family:var(--display);font-weight:800;
  font-size:clamp(26px,4vw,38px);letter-spacing:.06em;color:var(--navy);
  background:var(--paper);border:2px dashed var(--line);border-radius:14px;
  padding:12px 22px;margin:var(--s3) 0}
.tkt-done p{color:var(--muted);max-width:52ch;margin-inline:auto}

.urgent{display:flex;align-items:center;justify-content:space-between;gap:var(--s5);
  flex-wrap:wrap}
.urgent-go{display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.urgent-go span{font-size:13px;color:var(--muted)}

@media (max-width:920px){
  .tkt{grid-template-columns:1fr}
  .tkt-side{order:2}
}
@media (max-width:560px){
  .tkt-grid{grid-template-columns:1fr}
  .urgent-go{width:100%}
  .urgent-go .btn{width:100%;justify-content:center}
}

/* ---- the 1G/2G pick tiles — ONE component, used on / and /plans -------------
   ⚠️⚠️ THE TWO PAGES HAD DIVERGED AND THE /plans PAIR LOOKED WRONG FOR IT (owner
   2026-09-15: "they dont look great on plans page but i think they should just match
   the home page ones"). The /plans version used two-clause headings that wrapped to
   two lines on 2 Gig and one on 1 Gig, so the prices and the buttons never lined up
   across the pair. Rendered from one template now — see pickTilesMarkup() — so the
   only way they can differ again is a deliberate edit in one place.
   ⚠️ SPACING LIVES HERE, NOT IN INLINE STYLES. Both pages used to carry their own
   `style="padding:clamp(...)"` on each card, which is how they drifted. */
.pickt-grid{gap:var(--s4)}
.pickt{padding:clamp(24px,3.4vw,38px);display:flex;flex-direction:column;
  align-items:flex-start}
.pickt h3{margin:var(--s2) 0 0;font-size:clamp(21px,2.6vw,28px)}
.pickt .px{margin:var(--s3) 0}
/* ⚠️ The blurb takes the free space so the promo note, the button and the link sit on
   the SAME baseline in both cards however long the two descriptions are. That is the
   alignment the /plans pair never had. */
.pickt-b{margin:0 0 var(--s3);flex:1 1 auto}
.pickt .tier-promo{margin:0 0 var(--s3);align-self:stretch}
.pickt .plan-cta{margin:0}
.pickt .plan-alt{margin:var(--s2) 0 0}
@media (max-width:600px){
  .pickt{padding:20px}
  .pickt-b{flex:0 0 auto}
}

/* ---- /construction — the conduit running down the page -----------------------
   ⭐ The spine is ONE continuous line through all eight stages, and its character
   changes with the phase: dashed and faint while nothing is visible on the street,
   solid once the ground is open, blue once there is glass in the duct, alight at
   the end. Built from per-stage segments so the line can change mid-run without
   a single tall element trying to be four different things.
   ⚠️ Everything is visible at rest. Nothing here waits on scroll. */
.bld{list-style:none;margin:0;padding:0;counter-reset:none}
.bld-s{position:relative;display:grid;
  grid-template-columns:62px minmax(0,1fr) minmax(0,0.92fr);
  gap:0 var(--s4);align-items:start;padding:0 0 var(--s5)}
.bld-s:last-child{padding-bottom:0}

/* the conduit itself: a segment per stage, joined end to end */
.bld-s::before{content:"";position:absolute;left:20px;top:34px;bottom:-6px;width:3px;
  border-radius:2px}
.bld-s:last-child::before{display:none}
.phase-plan::before{background:repeating-linear-gradient(180deg,
  rgba(36,60,127,.34) 0 7px,transparent 7px 15px)}
.phase-dig::before{background:linear-gradient(180deg,var(--navy),var(--navy))}
.phase-glass::before{background:linear-gradient(180deg,var(--navy),var(--blue));width:4px;left:19.5px}
.phase-live::before{display:none}

/* the marker */
.bld-mark{position:relative;z-index:1;width:43px;height:43px;border-radius:50%;
  display:grid;place-items:center;font-family:var(--display);font-weight:800;
  font-size:16px;background:#fff;border:2px solid var(--line);color:var(--navy)}
.phase-dig .bld-mark{border-color:var(--navy);background:var(--navy);color:#fff}
.phase-glass .bld-mark{border-color:var(--blue);background:var(--blue);color:#fff}
.phase-live .bld-mark{border:0;color:#fff;
  background:linear-gradient(135deg,var(--blue),var(--magenta));
  box-shadow:0 0 0 6px rgba(206,53,138,.14)}

.bld-eye{margin:0 0 4px;font-family:var(--display);font-size:11px;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;color:var(--magenta)}
.bld-body h2{margin:0 0 var(--s2);font-size:clamp(20px,2.5vw,27px)}
.bld-body p{margin:0 0 var(--s2);max-width:58ch}
.bld-body>p:last-child{margin-bottom:0}
/* What a resident actually sees — the most useful line on the page, so it is set
   apart rather than buried in the paragraph. */
.bld-street{font-size:14.5px;color:var(--muted);border-left:3px solid var(--line);
  padding-left:13px}
.bld-street b{color:var(--ink);font-family:var(--display);font-size:12px;
  letter-spacing:.07em;text-transform:uppercase;display:block;margin-bottom:2px}

.bld-shot{margin:0;border-radius:16px;overflow:hidden;border:1px solid var(--line);
  background:var(--card);aspect-ratio:4/3}
.bld-shot img{display:block;width:100%;height:100%;object-fit:cover}

@media (max-width:900px){
  .bld-s{grid-template-columns:48px minmax(0,1fr);gap:0 var(--s3);padding-bottom:var(--s5)}
  .bld-s::before{left:15px;top:30px}
  .bld-mark{width:34px;height:34px;font-size:14px}
  .phase-glass::before{left:14.5px}
  /* Photo drops under the copy, still inside the stage, so the conduit stays unbroken. */
  .bld-shot{grid-column:2;margin-top:var(--s3)}
}

/* ⚠️⚠️ AT PHONE WIDTH THE STAGE STACKS. THE TWO-COLUMN VERSION ABOVE COLLAPSED THE TEXT TO
   ZERO WIDTH. Measured at 390px, 2026-09-15: `.bld-shot{grid-column:2}` took row 1 of
   column 2, which pushed the auto-placed `.bld-body` into the 48px RAIL column — so the
   marker and the entire body computed to width 0 while the photo got 324px. The copy was
   not merely cramped, it was unreachable (owner: "the how we build page on the mobile needs
   to be restructured and read anything").
   ⚠️ Single column, explicit placement, no auto-flow. A 48px rail beside prose does not work
   at this width even when it does lay out, so the rail becomes a horizontal header with the
   number inline and the conduit line is dropped — a vertical line down a one-column stack
   is decoration with nothing to connect.
   ⚠️ Do NOT reintroduce grid-column on a child here. That is exactly what broke it. */
@media (max-width:600px){
  .bld-s{display:block;padding-bottom:var(--s4)}
  .bld-s::before{display:none}
  .bld-mark{width:30px;height:30px;font-size:13.5px;margin-bottom:var(--s2)}
  .bld-body,.bld-shot{width:auto;grid-column:auto}
  .bld-body p{max-width:none}
  .bld-shot{margin-top:var(--s3)}
  .bld-body h2{font-size:clamp(19px,5.4vw,23px);line-height:1.22}
  /* The tab rail scrolls rather than wrapping into four ragged rows. */
  .bld-rail{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .bld-rail::-webkit-scrollbar{display:none}
}

/* The eight-stage teaser on /coverage. A compact chain, not a repeat of the real page. */
.asym-lead{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
  gap:var(--s5);align-items:center}
.mini-flow{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:7px}
.mini-flow li{position:relative;font-family:var(--display);font-weight:700;font-size:13.5px;
  color:var(--navy);background:var(--card);border:1px solid var(--line);
  border-radius:99px;padding:8px 14px}
.mini-flow li:not(:last-child)::after{content:"";position:absolute;right:-7px;top:50%;
  width:7px;height:1px;background:var(--line)}
.mini-flow li.on{background:var(--navy);border-color:var(--navy);color:#fff}
@media (max-width:820px){.asym-lead{grid-template-columns:1fr;gap:var(--s4)}}

/* ---- /construction part two — the install at the house ----------------------
   ⚠️ NOT the eight-stage stepper, but the same PROGRESSIVE contract: all three photo
   panels and all three steps ship visible; assets/bld.js turns the steps into tabs and
   hides the two inactive panels. Script blocked ⇒ the full process, in order.
   ⚠️ THE STEP COPY NEVER HIDES. The click swaps the PICTURE. Hiding two thirds of a
   three-step process to save vertical space on the one section the customer is actually
   in the room for would be the stepper's trade made in the wrong place. */
.ins{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,0.92fr);
  gap:var(--s5);align-items:start}

.ins-stage{display:grid;gap:var(--s3)}
.ins-f figure{margin:0;border-radius:16px;overflow:hidden;
  border:1px solid var(--line);background:var(--bg)}
/* ⚠️ THE PAIR IS PORTRAIT AND THE SINGLES ARE LANDSCAPE ON PURPOSE. Two 1:1 frames
   side by side are half the width and so half the height of one 1:1 frame, which made
   the stage jump ~280px every time you clicked off step one. Portrait pair + 4:3
   single lands the two panel heights within ~50px of each other. */
.ins-f img{display:block;width:100%;aspect-ratio:4/3;object-fit:cover}
.ins-pair img{aspect-ratio:3/4}
.ins-f figcaption{padding:11px 14px;font-size:13px;line-height:1.45;
  color:var(--muted);border-top:1px solid var(--line)}
.ins-f figcaption b{display:block;font-family:var(--display);font-size:11px;
  font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--magenta);
  margin-bottom:3px}
/* ⚠️ The panels are grid items, so they need their OWN [hidden] rule — an author
   `display` beats the UA's [hidden]{display:none}. Same trap as .bld-s[hidden]. */
.ins-f[hidden]{display:none!important}
/* The before/after pair is ONE panel holding two frames, side by side and STAYING side
   by side on a phone: a before you have to scroll away from to see the after is not a
   comparison. */
.ins-pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3)}

.ins-steps{list-style:none;margin:0;padding:0;display:grid;gap:10px;
  counter-reset:none}
.ins-steps li{display:grid;grid-template-columns:38px minmax(0,1fr);gap:var(--s3);
  align-items:start;padding:13px 15px 14px;border-radius:14px;
  border:1px solid transparent;transition:background .16s,border-color .16s}
.ins-n{width:38px;height:38px;border-radius:50%;display:grid;place-items:center;
  font-family:var(--display);font-weight:800;font-size:15px;color:var(--navy);
  background:var(--bg);border:2px solid var(--line);transition:all .16s}
.ins-steps h3{margin:5px 0 5px;font-size:clamp(17px,2vw,20px)}
.ins-steps p{margin:0;font-size:14.5px;color:var(--muted);max-width:56ch}
/* Only once the script has made them tabs do they look pressable. */
.ins-steps.tabbed li{cursor:pointer}
.ins-steps.tabbed li:hover{background:var(--bg)}
.ins-steps li.on{background:var(--bg);border-color:var(--line)}
.ins-steps li.on .ins-n{color:#fff;border-color:transparent;
  background:linear-gradient(135deg,var(--blue),var(--magenta));
  box-shadow:0 0 0 5px rgba(206,53,138,.13)}
.ins-steps li:focus-visible{outline:2px solid var(--blue);outline-offset:2px}

@media (max-width:900px){
  .ins{grid-template-columns:1fr;gap:var(--s4)}
  /* Picture first: "drop bury" is a term, the photograph is the definition. */
  .ins-stage{order:-1}
}
@media (max-width:600px){
  .ins-pair{gap:9px}
  .ins-f figcaption{padding:9px 11px;font-size:12px}
  .ins-steps li{grid-template-columns:30px minmax(0,1fr);gap:var(--s2);padding:11px 12px}
  .ins-n{width:30px;height:30px;font-size:13.5px}
  .ins-steps p{max-width:none}
}

/* ---- /construction stepper rail ---------------------------------------------
   The conduit moved from a vertical spine between stages to a horizontal run
   through the rail — same idea, a fraction of the height. Phase colouring is
   unchanged: dashed while nothing is visible on the street, solid once the
   ground is open, blue once there is glass in the duct, alight at the end. */
.bld-rail{display:flex;gap:0;align-items:flex-start;margin:0 0 var(--s5);
  overflow-x:auto;overflow-y:hidden;padding:16px 2px 12px;scrollbar-width:thin}
.bld-t{position:relative;flex:1 1 0;min-width:104px;background:none;border:0;padding:0;
  cursor:pointer;font:inherit;display:flex;flex-direction:column;align-items:center;
  gap:7px;color:var(--muted);text-align:center}
/* the conduit, drawn between the markers */
.bld-t::before{content:"";position:absolute;top:20px;left:calc(-50% + 21px);
  width:calc(100% - 42px);height:3px;border-radius:2px;transform:translateX(50%)}
.bld-t:first-child::before{display:none}
.bld-t.phase-plan::before{background:repeating-linear-gradient(90deg,
  rgba(36,60,127,.34) 0 7px,transparent 7px 15px)}
.bld-t.phase-dig::before{background:var(--navy)}
.bld-t.phase-glass::before{background:linear-gradient(90deg,var(--navy),var(--blue))}
.bld-t.phase-live::before{background:linear-gradient(90deg,var(--blue),var(--magenta))}
.bld-t i{position:relative;z-index:1;width:41px;height:41px;border-radius:50%;
  display:grid;place-items:center;font-style:normal;font-family:var(--display);
  font-weight:800;font-size:15px;background:#fff;border:2px solid var(--line);
  color:var(--navy);transition:transform .16s ease}
.bld-t.phase-dig i{border-color:var(--navy)}
.bld-t.phase-glass i{border-color:var(--blue)}
.bld-t.phase-live i{border-color:var(--magenta)}
.bld-t span{font-size:12.5px;font-weight:700;letter-spacing:.01em;line-height:1.25}
.bld-t:hover i{transform:translateY(-2px)}
.bld-t.on{color:var(--ink)}
.bld-t.on i{transform:translateY(-2px);animation:bld-glow 2s ease-in-out infinite}
.bld-t.phase-plan.on i{background:var(--navy);border-color:var(--navy);color:#fff}
.bld-t.phase-dig.on i{background:var(--navy);color:#fff}
.bld-t.phase-glass.on i{background:var(--blue);color:#fff}
.bld-t.phase-live.on i{border:0;color:#fff;
  background:linear-gradient(135deg,var(--blue),var(--magenta));
  box-shadow:0 0 0 6px rgba(206,53,138,.14)}
.bld-t:focus-visible i{outline:2px solid var(--blue);outline-offset:3px}
/* Active step glows + gently pulses to signal the rail is clickable. */
.bld-t:hover i{box-shadow:0 4px 12px -2px rgba(36,60,127,.26)}
.bld-t.phase-live.on i{animation-name:bld-glow-m}
@keyframes bld-glow{0%,100%{box-shadow:0 0 0 3px rgba(36,60,127,.22),0 4px 16px 1px rgba(36,60,127,.42)}50%{box-shadow:0 0 0 6px rgba(36,60,127,.12),0 6px 22px 3px rgba(36,60,127,.58)}}
@keyframes bld-glow-m{0%,100%{box-shadow:0 0 0 3px rgba(206,53,138,.24),0 4px 16px 1px rgba(206,53,138,.42)}50%{box-shadow:0 0 0 6px rgba(206,53,138,.12),0 6px 22px 3px rgba(206,53,138,.58)}}
@media (prefers-reduced-motion:reduce){.bld-t.on i{animation:none;box-shadow:0 0 0 4px rgba(36,60,127,.2),0 5px 18px rgba(36,60,127,.5)}.bld-t.phase-live.on i{box-shadow:0 0 0 4px rgba(206,53,138,.2),0 5px 18px rgba(206,53,138,.5)}}

/* ⚠️ EXPLICIT, because .bld-s sets `display:grid` inside a max-width media query and an
   author display beats the UA's [hidden]{display:none}. Without this the panel stays on
   screen with the attribute set and the stepper silently does nothing on phones. */
.bld-s[hidden]{display:none!important}

/* With the stepper on, a panel is on its own — it no longer needs the spine or the
   number beside it, both of which now live in the rail. */
.bld-rail:not([hidden])~.bld .bld-s{padding-bottom:0}
.bld-rail:not([hidden])~.bld .bld-s::before{display:none}
.bld-rail:not([hidden])~.bld .bld-mark{display:none}
.bld-rail:not([hidden])~.bld .bld-s{grid-template-columns:minmax(0,1fr) minmax(0,0.92fr)}

@media (max-width:900px){
  .bld-rail{gap:4px}
  .bld-t{min-width:82px}
  .bld-t span{font-size:11.5px}
  .bld-rail:not([hidden])~.bld .bld-s{grid-template-columns:minmax(0,1fr)}
}

/* ---- /plans "which one to buy" ----------------------------------------------
   Two cards, the recommended one carried forward. The concurrency squares are
   evidence inside each card rather than a chart of their own. */
.pick{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3);margin-top:var(--s4)}
.pick-card{background:var(--card);border:1px solid var(--line);border-radius:18px;
  padding:clamp(20px,2.6vw,28px);color:var(--body);display:flex;flex-direction:column}
.pick-card.on{background:linear-gradient(160deg,var(--navy-3),var(--navy) 72%,#2c4a96);
  border-color:transparent;color:#dbe4f8;box-shadow:0 26px 60px -34px rgba(36,60,127,.85)}
.pick-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:var(--s2)}
.pick-h b{font-family:var(--display);font-size:clamp(21px,2.6vw,27px);color:var(--ink);
  letter-spacing:-.02em}
.pick-card.on .pick-h b{color:#fff}
.pick-h span{font-size:12.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted)}
.pick-card.on .pick-h span{color:#8fc4f4}
.pick-card p{margin:0 0 var(--s3);font-size:15px}
.pick-card.on p{color:#c9d4ee}
.pick-card .conc-dots{margin-top:auto;justify-content:start}
.pick-card.on .conc-dots i.off{background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.18)}
.pick-n{display:block;margin-top:10px;font-size:12.5px;font-weight:700;color:var(--muted)}
.pick-card.on .pick-n{color:#8fc4f4}
@media (max-width:760px){.pick{grid-template-columns:1fr}}

/* ---- /solutions -------------------------------------------------------------
   ⚠️ The previous version used `.tier` — a 38px square with the word HOME or BIZ
   crammed in at 12.5px. Drawn icons instead, one stroke weight, each one the
   actual thing. The cards carry proof points because a page of four one-paragraph
   cards is what made this read as bare. */
.sol{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3)}
.sol-card{display:flex;flex-direction:column;background:var(--card);color:var(--body);
  border:1px solid var(--line);border-radius:20px;padding:clamp(22px,2.8vw,32px);
  text-decoration:none;transition:border-color .16s ease,transform .16s ease,box-shadow .16s ease}
a.sol-card:hover{border-color:var(--blue);transform:translateY(-2px);
  box-shadow:0 18px 44px -30px rgba(16,24,54,.5)}
a.sol-card:focus-visible{outline:2px solid var(--blue);outline-offset:3px}
.sol-ic{width:46px;height:46px;border-radius:13px;display:grid;place-items:center;
  background:linear-gradient(140deg,rgba(61,155,233,.16),rgba(206,53,138,.13));
  color:var(--navy);margin-bottom:var(--s3)}
.sol-ic svg{width:24px;height:24px;display:block}
.sol-card h2{font-size:clamp(20px,2.4vw,25px);margin:0 0 var(--s2)}
.sol-card>p{margin:0 0 var(--s3);font-size:15px}
.sol-pts{list-style:none;margin:0 0 var(--s3);padding:0;font-size:14.5px}
.sol-pts li{position:relative;padding:5px 0 5px 25px;color:var(--body)}
.sol-pts li::before{content:"";position:absolute;left:2px;top:12px;width:11px;height:6px;
  border-left:2px solid var(--ok);border-bottom:2px solid var(--ok);transform:rotate(-45deg)}
.sol-go{margin-top:auto;font-family:var(--display);font-weight:800;font-size:15px;
  color:var(--navy);display:inline-flex;align-items:center;gap:7px;min-height:28px}
.sol-go::after{content:"\2192";transition:transform .16s ease}
a.sol-card:hover .sol-go::after{transform:translateX(3px)}
/* The voice card is not a link (nothing to link to), so its call-to-action is the phone. */
.sol-go.as-tel::after{content:none}
.sol-go.as-tel a{color:var(--navy);text-decoration:none;border-bottom:2px solid var(--rule);
  display:inline-flex;align-items:center;min-height:44px}
.sol-go.as-tel a:hover{border-bottom-color:var(--magenta)}

.und{border-left:2.5px solid rgba(143,196,244,.4);padding-left:var(--s2)}
.und b{display:block;font-family:var(--display);font-size:15.5px;color:#fff;
  letter-spacing:-.01em;margin-bottom:5px}
.und span{display:block;font-size:14px;color:#b9c4e0;line-height:1.5}

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

/* ---- FCC Broadband Facts label (assets/bbf.js) -------------------------------
   ⚠️ THE LAYOUT IS THE FCC'S, NOT OURS. The black rules, the section order and the
   heavy top border are what makes it recognisable as the federal label rather than
   as marketing. Do not restyle it to match the rest of the site. */
.bbf-slot{margin-top:10px}
.bbf-link{background:none;border:0;padding:6px 0;font:inherit;font-size:13px;
  font-weight:700;color:var(--navy);text-decoration:underline;text-underline-offset:3px;
  cursor:pointer;min-height:32px}
.bbf-link:hover{color:var(--magenta)}
.bbf-link:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:4px}

.bbf-wrap{position:fixed;inset:0;z-index:300;background:rgba(10,16,36,.62);
  display:grid;place-items:center;padding:20px;overflow:auto}
.bbf-card{position:relative;background:#fff;color:#000;border-radius:10px;
  max-width:420px;width:100%;max-height:92vh;overflow:auto;
  box-shadow:0 30px 80px rgba(0,0,0,.45)}
.bbf-x{position:absolute;top:6px;right:8px;background:none;border:0;font-size:26px;
  line-height:1;color:#555;cursor:pointer;width:44px;height:44px}
.bbf-x:hover{color:#000}

/* The label itself — deliberately plain, deliberately not our brand. */
.bbf{font-family:Helvetica,Arial,sans-serif;color:#000;padding:16px 18px 18px}
.bbf h3{font-size:26px;font-weight:800;margin:0;letter-spacing:-.02em;color:#000;
  font-family:Helvetica,Arial,sans-serif}
.bbf-rule{border-bottom:5px solid #000;margin:6px 0 8px}
.bbf-rule.thin{border-bottom-width:2px;margin:8px 0}
.bbf-hair{border-bottom:1px solid #999;margin:5px 0}
.bbf-prov{font-size:15px;font-weight:700}
.bbf-plan{font-size:15px}
.bbf-disc{font-size:12px;margin-top:2px}
.bbf-sec{font-size:13px;font-weight:700;margin:4px 0 2px}
.bbf-row{display:flex;justify-content:space-between;gap:14px;font-size:13px;
  padding:2px 0;align-items:baseline}
.bbf-row.big{font-size:17px;font-weight:700}
.bbf-row span{flex:1}
.bbf-row b{font-weight:700;text-align:right;white-space:nowrap}
.bbf-row a{color:#0645ad}
.bbf-note{font-size:12px;margin:4px 0;line-height:1.45}
.bbf-note a{color:#0645ad}
.bbf-fcc{font-size:11px;margin:4px 0 0;line-height:1.4}
.bbf-fcc a{color:#0645ad}
.bbf-pid{font-size:11px;margin-top:8px;letter-spacing:.04em}

/* Emphasis inside a hero lede. Light blue rather than magenta: the lede sits under a
   headline that already carries the brand gradient, and a second magenta on the same
   screen competes with the CTA. */
.hero .lede .hi{color:#8fc4f4;font-weight:800}

/* ---- SECTION SURFACES -------------------------------------------------------
   ⭐ THE PAGE NEEDS A RHYTHM. Measured 2026-09-13: /plans ran four consecutive
   sections on the identical background and /why-fiber had two runs of three, so
   those pages read as one unbroken grey scroll with headings floating in it.
   Owner: "some of the pages don't really have good section breaks and just run
   long with same white/grey background."

   Four surfaces, used in alternation. Every one declares its OWN ink — a surface
   that inherits its text colour is how white-on-white shipped once already.
   ⚠️ audit.mjs FAILS on three consecutive sections sharing a background, so the
   rhythm cannot quietly rot back. */
.surf-paper{background:var(--paper);color:var(--body)}
.surf-card{background:var(--card);color:var(--body);
  border-block:1px solid var(--line)}
.surf-tint{color:var(--body);
  background:linear-gradient(180deg,var(--paper-2),var(--paper));
  border-block:1px solid var(--line)}
/* The dark break. Used sparingly — one per page at most, where a page needs a
   change of register rather than another tint. */
.surf-navy{background:linear-gradient(165deg,#111f45,var(--navy) 55%,#2c4a96);
  color:#dbe4f8}
.surf-navy h1,.surf-navy h2,.surf-navy h3{color:#fff}
.surf-navy .lede,.surf-navy p{color:#c9d4ee}
.surf-navy a:not(.btn){color:#9ecbf6}
.surf-navy .feat{border-left-color:rgba(143,196,244,.4)}
.surf-navy .feat h3{color:#fff}
.surf-navy .feat p{color:#c9d4ee}
.surf-navy .card{background:rgba(255,255,255,.06);border-color:rgba(143,196,244,.22);
  color:#dbe4f8}
.surf-navy .card h3{color:#fff}

/* The two home-page plan cards. ⚠️ Both CTAs are the same action and must sit on the same
   baseline — the cards carry different amounts of copy, so the button is pushed to the
   bottom rather than spaced off the paragraph above it. */
.asym-card{display:flex;flex-direction:column}
.plan-cta{margin-top:auto;align-self:flex-start}
.plan-alt{margin:12px 0 0;font-size:14px}
.plan-alt a{color:var(--navy);font-weight:700}
/* ⚠️ 2 Gig was a plain white card beside a navy one and disappeared next to it. Same blue
   lift the /plans grid uses, so the two pages agree on which tier is being highlighted. */
.asym-card.pick2{position:relative;border-color:var(--blue);
  box-shadow:0 18px 46px -28px rgba(61,155,233,.55)}
.asym-card.pick2 .plan-tag{top:-11px;left:clamp(24px,3.4vw,38px)}
.plan-alt.onnavy a{color:#9ecbf6}

/* The optional "already tried" block, collapsed. */
.tkt-more{margin-top:var(--s3);border:1px solid var(--line);border-radius:12px;
  background:var(--paper)}
.tkt-more>summary{cursor:pointer;padding:12px 14px;font-size:14.5px;font-weight:700;
  color:var(--ink);list-style:none;min-height:44px;display:flex;align-items:center;gap:6px}
.tkt-more>summary::-webkit-details-marker{display:none}
.tkt-more>summary::after{content:"+";margin-left:auto;font-size:19px;color:var(--muted);
  line-height:1}
.tkt-more[open]>summary::after{content:"\2212"}
.tkt-more>summary i{font-style:normal;font-weight:600;color:var(--muted)}
.tkt-more>summary:focus-visible{outline:2px solid var(--blue);outline-offset:-2px;
  border-radius:12px}
.tkt-more .chips{margin:0;padding:0 14px 12px}
/* Visually hidden but read aloud — the fieldset still needs a legend. */
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
  white-space:nowrap}
/* The urgent checkbox now shares a row with the topic select, so it has to stop
   stretching to the full width of the form. */
.tkt-grid .urg{margin:0;align-self:end;min-height:46px}

/* The urgent-contact card, in the ticket form's side column. */
.tkt-urgent{background:var(--card);border:1px solid var(--line);border-radius:20px;
  padding:clamp(20px,2.6vw,28px);color:var(--body);grid-column:2;
  display:flex;flex-direction:column;align-items:flex-start;gap:10px}
.tkt-urgent h3{margin:0;font-size:18px}
.tkt-urgent p{margin:0;font-size:14.5px}
.tkt-urgent .btn{width:100%;justify-content:center}
.tkt-urgent span{font-size:13px;color:var(--muted)}
/* ⚠️ The form spans both side cards, so the grid needs an explicit placement — otherwise
   the second aside lands under the form in column one. */
.tkt{grid-template-areas:"form side" "form urgent"}
.tkt-form{grid-area:form}
.tkt-side{grid-area:side}
.tkt-urgent{grid-area:urgent}
@media (max-width:920px){
  .tkt{grid-template-areas:"form" "side" "urgent"}
  .tkt-urgent{grid-column:1}
}
/* The navy card's tag needs its own placement — the card has its own padding scale. */
.asym-card.us{position:relative}
.asym-card.us .plan-tag{top:-11px;left:clamp(24px,3.4vw,38px)}

/* ── PHONE CONDENSING (2026-09-13, owner: "take a hard look at the mobile version… condense
   stuff even further… the footer is one example"). Measured at 390px before this block: the
   footer was 1,408px tall (17 links in ONE column at 44px tap targets), the promo bar 158px
   and the fixed availability bar 154px — together 37% of an 844px viewport — and every plan
   tile repeated "Free professional install / No contract, no data caps". Tap targets stay
   ≥44px (audit.mjs checks them); what changes is columns, repeated copy, and padding. */
@media (max-width:520px){
  /* section rhythm: the desktop 80/52px bands read as dead space on a phone */
  .sect{padding:44px 0}
  .sect-tight{padding:30px 0}
  /* footer: brand block full width, then the three link groups in two columns */
  .foot{padding:40px 0 26px;margin-top:44px}
  .foot-in{grid-template-columns:1fr 1fr;gap:18px 20px}
  .foot-in > div:first-child{grid-column:1 / -1}
  .foot-in > div:first-child p{font-size:13.5px;margin-bottom:8px}
  /* the third group (Help) would sit alone under Service; span it and run its list in two columns */
  .foot-in > div:last-child{grid-column:1 / -1}
  .foot-in > div:last-child ul{columns:2;column-gap:20px}
  .foot-in > div:last-child ul li{break-inside:avoid}
  .foot h4{margin-bottom:4px}
  .foot ul{font-size:14px}
  .foot ul a{padding:7px 0}
  .foot-bot{margin-top:22px;padding-top:14px;font-size:12.5px}
  /* promo bar: one compact row — plan + price + CTA; the strike-through and save chip go */
  .promo-in{padding-block:8px;gap:4px 12px}
  .promo-tag{display:none}
  .promo-txt{font-size:13.5px}
  .promo-txt b{font-size:14.5px}
  .promo-txt s,.promo-txt i{display:none}
  .promo-cta{margin-left:0;font-size:13.5px}
  /* fixed availability bar: heading only, single input row */
  .avail{padding:8px 0 calc(8px + env(safe-area-inset-bottom,0px))}
  .avail-in{gap:6px}
  .avail-txt b{font-size:14px}
  .avail-txt span{display:none}
  .avail input{padding:9px 11px;font-size:14px;border-radius:9px}
  .avail .btn{padding:10px 14px;font-size:14px}
  /* plan tiles: the two bullets every tile repeats collapse into one line above the grid */
  .plan{padding:18px 16px}
  .plan li.std{display:none}
  .plan-std-note{display:block;margin:0 0 14px;font-size:13.5px;color:var(--muted)}
}
.plan-std-note{display:none}

/* The hero motif — light travelling through glass (assets/strand.js). Replaces the
   coverage map, which moved to /coverage.
   ⚠️ Explicit width/height: `inset:0` does NOT size a canvas — it is a replaced element
   with an intrinsic 300×150, and stretching that over a 1440px hero is how the last one
   shipped blurred. */
/* ⚠️ MASKED AWAY FROM THE TEXT COLUMN. Unmasked, a pulse ran straight through the
   headline and the lede — decoration competing with the one thing the page has to say.
   The motif now lives on the right, which is also where the map it replaced sat. */
.strands{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;
  opacity:0;transition:opacity .6s ease;
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.18) 34%,#000 62%);
          mask-image:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.18) 34%,#000 62%)}
/* On a phone the text spans the full width, so fade from the top instead of the side. */
@media (max-width:880px){
  .strands{-webkit-mask-image:linear-gradient(180deg,rgba(0,0,0,.25) 0%,transparent 42%,#000 88%);
                   mask-image:linear-gradient(180deg,rgba(0,0,0,.25) 0%,transparent 42%,#000 88%)}
}
.strands.ready{opacity:1}


/* Referral band (Home + Reviews) — brings pink in and surfaces the $50/$50 offer. */
.refer-band{display:flex;align-items:center;gap:16px;flex-wrap:wrap;justify-content:center;
  text-align:center;background:var(--magenta);color:#fff;border-radius:var(--r);
  padding:16px 24px;text-decoration:none;box-shadow:0 14px 30px -16px rgba(206,53,138,.7)}
.refer-band:hover{filter:brightness(1.06)}
.refer-amt{font-family:var(--display);font-weight:800;font-size:27px;line-height:1}
.refer-txt{font-size:15px;color:#fff}
.refer-go{font-weight:800;font-size:14px;white-space:nowrap}
/* Homepage closing CTA: navy->pink gradient so it stands apart from the section above. */
.band.band-cta{background:linear-gradient(100deg,var(--navy) 0%,var(--magenta) 120%)}


/* Smaller nav CTA so it fits the menu band. */
.nav-cta .btn{padding:9px 18px;font-size:14px;font-family:var(--display);letter-spacing:-.01em}


.bbf-empty{min-height:30px}

/* Plans header two-column with a lifestyle image */
.plans-hero-in{display:flex;align-items:center;gap:var(--s5);flex-wrap:wrap}
.plans-hero-txt{flex:1 1 340px;min-width:0}
.plans-hero-img{flex:1 1 320px;max-width:520px;width:100%;height:auto;aspect-ratio:5/4;object-fit:cover;border-radius:var(--r);box-shadow:0 18px 44px -24px rgba(20,30,80,.5)}
@media (max-width:760px){.plans-hero-img{aspect-ratio:16/10}}

.addon h3{color:var(--magenta)}

/* Coverage two-city band with crew photo */
.cov-band{display:flex;align-items:center;justify-content:center;gap:var(--s6);flex-wrap:wrap}
.cov-txt{flex:0 1 440px;min-width:0}
.cov-cities{display:flex;flex-direction:column;gap:var(--s2);align-items:flex-start}
.cov-cities .btn{min-width:240px;justify-content:center}
.cov-img{flex:0 1 380px;max-width:400px;width:100%;height:auto;aspect-ratio:4/5;object-fit:cover;border-radius:var(--r);box-shadow:0 18px 44px -24px rgba(20,30,80,.5)}
@media (max-width:760px){.cov-img{aspect-ratio:4/3}}

/* Solutions: fiber photo strip behind the four cards */
.sol-photoband{background:linear-gradient(160deg,rgba(20,29,66,.74),rgba(27,46,96,.64)),url("/photos/sol-band-smarthome.jpg");background-size:cover;background-position:center}

/* Why fiber: hero text + real fiber-cabinet photo, cards stay full width below */
.why-hero-in{display:flex;gap:var(--s5);align-items:stretch}
.why-hero-txt{flex:1;min-width:0}
.why-hero-cards{margin-top:var(--s4)}
.why-hero-img{flex:none;width:340px;border-radius:16px;background-size:cover;background-position:center;box-shadow:0 18px 40px -22px rgba(20,30,80,.5)}
@media(max-width:820px){.why-hero-in{flex-direction:column;align-items:stretch}.why-hero-img{width:100%;height:240px}}

/* brand wordmark inside the fiber-vs-cable table header */
.vs .wm{font-family:var(--display);text-transform:none;letter-spacing:-.01em;font-size:15px;color:var(--navy)}
.vs .wm span{color:var(--magenta)}

/* How we build: hero jump buttons + anchor offsets under the sticky header */
.wte-jump{display:flex;gap:12px;flex-wrap:wrap;margin-top:var(--s3)}
#construction,#install{scroll-margin-top:96px}

/* How we build: two vertical (reel-ratio) clips side by side */
.wte-reels{display:flex;gap:var(--s4);justify-content:center;flex-wrap:wrap;align-items:flex-start}
.wte-reel-wrap{display:flex;flex-direction:column;gap:10px;align-items:center}
.wte-reel{position:relative;width:min(280px,44vw);aspect-ratio:9/16;border-radius:18px;overflow:hidden;
  background:linear-gradient(160deg,var(--navy),var(--navy-3));display:flex;align-items:center;justify-content:center;
  border:1px solid var(--line);box-shadow:0 18px 40px -22px rgba(20,30,80,.5)}
.wte-reel-vid{background:#000}
.wte-reel iframe{width:100%;height:100%;border:0;display:block}
.wte-reel-play{width:58px;height:58px;border-radius:50%;background:rgba(255,255,255,.14);color:#fff;display:grid;place-items:center;font-size:20px;padding-left:3px}
.wte-reel-cap{font-family:var(--display);font-size:14px;color:var(--ink)}
.wte-reel-cap small{color:var(--muted);font-weight:400;font-family:var(--sans)}
/* placeholder that stands in for a not-yet-supplied install photo */
.ins-ph{aspect-ratio:4/3;display:grid;place-items:center;text-align:center;padding:20px;background:var(--paper);color:var(--muted);font-size:13.5px}

/* Support: ticket form on the left, three checks stacked on the right */
.sup-cols{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,0.95fr);gap:var(--s5);align-items:start}
@media(max-width:920px){.sup-cols{grid-template-columns:1fr}}
.tkt-note{margin:var(--s4) 0 0;font-size:13.5px;color:var(--muted);line-height:1.5}
.sup-right>h2{margin:0}
.sup-right .triage{grid-template-columns:1fr;gap:var(--s3)}
.sup-checks-note{margin:var(--s3) 0 0;font-size:13.5px;color:var(--muted)}

/* Support: equal-size form buttons */
.tkt-go .btn{width:100%;justify-content:center}
/* Support: neon photo band behind the form section (cards pop, right-column text goes light) */
.sup-photoband{background:linear-gradient(160deg,rgba(16,24,54,.85),rgba(27,46,96,.77)),url("/photos/support-band.jpg");background-size:cover;background-position:center;background-attachment:fixed}
.sup-photoband .sup-right h2{color:#fff}
.sup-photoband .sup-right .lede{color:#dce3f5}
.sup-photoband .sup-checks-note{color:#c9d4ee}
@media(max-width:700px){.sup-photoband{background-attachment:scroll}}

/* About: leadership row */
.leaders{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s5)}
@media(max-width:760px){.leaders{grid-template-columns:1fr;gap:var(--s4);max-width:340px;margin-inline:auto}}
.leader{text-align:center}
.leader img{width:100%;max-width:230px;aspect-ratio:4/5;object-fit:cover;border-radius:16px;display:block;margin:0 auto;border:1px solid var(--line)}
.leader b{display:block;font-family:var(--display);font-size:17px;color:var(--ink);margin-top:var(--s2)}
.leader .role{display:block;font-size:12px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--magenta);margin-top:3px}
.leader p{margin:var(--s2) 0 0;font-size:14px;color:var(--body);max-width:34ch;margin-inline:auto}
/* About: Gigz + tagline band */
.gigz-band{background:radial-gradient(120% 120% at 20% 0%, #2f4a97 0%, var(--navy) 45%, var(--navy-3) 100%);color:#fff}
.gigz-in{display:grid;grid-template-columns:1fr 1fr;gap:var(--s5);align-items:center}
@media(max-width:760px){.gigz-in{grid-template-columns:1fr;text-align:center}}
.gigz-stage{display:flex;align-items:flex-end;justify-content:center;min-height:320px}
.gigz{width:clamp(200px,38vw,300px);cursor:pointer;animation:gbob 3.6s ease-in-out infinite;outline:none;position:relative}
.gigz::before{content:"";position:absolute;z-index:-1;left:-6%;right:-6%;top:6%;bottom:-6%;
  background:radial-gradient(52% 46% at 50% 58%,rgba(5,9,26,.52),rgba(5,9,26,0) 72%);pointer-events:none}
.gigz img{width:100%;height:auto;display:block;position:relative;z-index:1;user-select:none;-webkit-user-drag:none}
.gigz:focus-visible{outline:3px solid var(--blue);outline-offset:8px;border-radius:16px}
@keyframes gbob{0%,100%{transform:translateY(0)}50%{transform:translateY(-14px)}}
@media(prefers-reduced-motion:reduce){.gigz{animation:none}}
.tagline{font-family:var(--display);font-size:clamp(32px,4.4vw,52px);line-height:1.08;letter-spacing:-.02em;margin:var(--s2) 0 0;color:#fff;text-wrap:balance}
.tagline .em{font-style:italic;color:var(--magenta);white-space:nowrap}

/* About: animated aurora behind the mission statement */
.mission-band{position:relative;overflow:hidden;color:#fff;
  background:linear-gradient(120deg,var(--navy-3),var(--navy) 55%,#2c4a96)}
.mission-band>.wrap{position:relative;z-index:1}
.mission-band::before,.mission-band::after{content:"";position:absolute;border-radius:50%;
  filter:blur(70px);opacity:.55;pointer-events:none}
.mission-band::before{width:520px;height:520px;top:-170px;left:-120px;
  background:radial-gradient(circle,var(--blue),transparent 70%);animation:missdrift1 22s ease-in-out infinite}
.mission-band::after{width:480px;height:480px;bottom:-190px;right:-110px;
  background:radial-gradient(circle,var(--magenta),transparent 70%);animation:missdrift2 27s ease-in-out infinite}
@keyframes missdrift1{0%,100%{transform:translate(0,0)}50%{transform:translate(130px,70px)}}
@keyframes missdrift2{0%,100%{transform:translate(0,0)}50%{transform:translate(-110px,-60px)}}
@media(prefers-reduced-motion:reduce){.mission-band::before,.mission-band::after{animation:none}}

/* Business: phone-solutions tease card */
.biz-phone{display:flex;flex-wrap:wrap;gap:var(--s3) var(--s5);align-items:center;justify-content:space-between}
.biz-phone>div{max-width:62ch}

/* Business: feature strip over the office photo (white-text feat cards) */
.biz-band{background:linear-gradient(160deg,rgba(16,24,54,.86),rgba(27,46,96,.78)),url("/photos/business-hero.jpg");background-size:cover;background-position:center;color:#fff}
.biz-band .feat{border-left-color:rgba(143,196,244,.5)}
.biz-band .feat h3{color:#fff}
.biz-band .feat p{color:#dce3f5}
.biz-band .feat.on{border-left-color:var(--magenta)}
/* Business: phone + HOA teases sit close together, both button-right */
.biz-teases{display:grid;gap:var(--s3)}

/* Communities: two distinct, equal-height audience boxes */
.comm-cols{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4);align-items:stretch}
@media(max-width:760px){.comm-cols{grid-template-columns:1fr}}
.comm-col{display:flex;flex-direction:column;border-top:4px solid var(--line)}
.comm-col.is-board{border-top-color:var(--blue)}
.comm-col.is-resident{border-top-color:var(--magenta)}
.comm-col h3{margin:var(--s1) 0 0}
.comm-btn{margin-top:auto;width:100%}
/* guarantee a gap above the bottom-aligned CTA even when the column is full */
.comm-col > *:nth-last-child(2){margin-bottom:var(--s4)}
.comm-col{padding-bottom:var(--s4)}
.comm-list{list-style:none;padding:0;margin:var(--s3) 0 0;display:grid;gap:10px}
.comm-list li{padding-left:22px;position:relative;font-size:14.5px;color:var(--body);line-height:1.5}
.comm-list li::before{content:"";position:absolute;left:0;top:7px;width:9px;height:9px;border-radius:50%;background:var(--blue)}
.comm-col.is-resident .comm-list li::before{background:var(--magenta)}
.comm-list li b{color:var(--ink)}
.comm-list.tight{gap:6px;margin-top:var(--s2)}
.comm-list.tight li::before{width:6px;height:6px;top:9px;background:var(--muted)}
.comm-sub{margin:var(--s3) 0 0;font-family:var(--display);font-size:12px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}

/* Communities strip: shorter band, caption vertically centered on the left over the sky */
.comm-strip.media-band{height:clamp(190px,24vw,300px)}
.comm-strip.media::after{background:linear-gradient(90deg,rgba(10,19,48,.75) 0%,rgba(10,19,48,.5) 32%,rgba(10,19,48,.08) 68%)}
.comm-strip .media-cap{top:50%;bottom:auto;transform:translateY(-50%);text-align:left;padding-block:0}

/* News/guide card thumbnails */
.news-thumb{width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:12px;display:block;margin-bottom:var(--s2);border:1px solid var(--line)}

/* News: external press card with a button pinned to the bottom */
.news-ext{display:flex;flex-direction:column}
.news-ext .btn{margin-top:auto;align-self:flex-start}

/* In-article image */
.prose-fig{margin:var(--s4) 0}
.prose-fig img{width:100%;border-radius:14px;display:block;border:1px solid var(--line)}
.prose-fig figcaption{font-size:13px;color:var(--muted);margin-top:8px}


/* Reviews page */
.rev-h{text-align:left;margin:0 0 var(--s4)}
.revcar > .wrap > .rev-h{color:var(--magenta)}
.rev-cust{background:radial-gradient(rgba(255,255,255,.11) 1.4px,transparent 1.5px) 0 0/22px 22px,linear-gradient(158deg,#0b1636 0%,var(--navy-3) 42%,var(--navy) 84%,#2c4a96 100%)}
.rev-cust-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s5);max-width:none}
.rev-cust-grid p{margin:0}
@media(max-width:760px){.rev-cust-grid{grid-template-columns:1fr;gap:var(--s3)}}
.revcar .qbig blockquote{font-size:clamp(18px,2.4vw,26px)}
.qcta{margin:var(--s4) 0 0}
.rev-cust-box{background:rgba(255,255,255,.86);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);border:1px solid rgba(255,255,255,.75);border-radius:20px;padding:clamp(24px,4vw,44px);box-shadow:0 18px 44px -30px rgba(20,32,68,.35)}


/* Contact: office photo beside "Before you call" */
.contact-grid{grid-template-columns:minmax(0,1fr) 380px;align-items:start}
.contact-grid .contact-photo{align-self:center}
@media(max-width:760px){.contact-grid{grid-template-columns:1fr}.contact-grid .contact-photo{align-self:start}}
.contact-photo{margin:0}
.contact-photo img{width:100%;max-width:380px;height:auto;border-radius:16px;display:block;border:1px solid var(--line)}
.contact-photo figcaption{margin-top:var(--s2);font-size:13.5px;color:var(--muted);max-width:380px}
@media(max-width:760px){.contact-photo img,.contact-photo figcaption{margin-inline:auto;text-align:center}}


/* About: faint logo-symbol watermark behind the Our Story section */
.story-scene{position:relative;z-index:0;overflow:hidden}
.story-scene > section{position:relative;z-index:1}
.story-mark{position:absolute;left:-200px;top:-40px;
  width:860px;max-width:92%;opacity:.06;pointer-events:none;z-index:0;user-select:none}
@media(max-width:760px){.story-mark{left:-40%;top:-3%;width:155%;max-width:none;opacity:.05}}


/* Careers: employee benefits band (faded team photo behind white cards) */
.ben-band{background:linear-gradient(rgba(247,248,251,.82),rgba(247,248,251,.82)),url("/photos/careers-team.jpg");background-size:cover;background-position:center 34%}
.ben-card h3{color:var(--magenta)}


/* Coverage: Santa Clarita aerial video hero */
.sc-hero{position:relative;overflow:hidden;color:#fff;display:flex;align-items:center;min-height:clamp(440px,62vh,600px);padding:var(--s6) 0}
.sc-hero-vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
.sc-hero::after{content:"";position:absolute;inset:0;z-index:1;background:linear-gradient(105deg,rgba(8,14,36,.86),rgba(20,32,68,.6) 55%,rgba(206,53,138,.34))}
.sc-hero-in{position:relative;z-index:2;width:100%}
.sc-hero .eyebrow{color:var(--blue)}
.sc-hero h1{color:#fff}
.sc-hero .lede{color:#e6ecfb}
@media(prefers-reduced-motion:reduce){.sc-hero-vid{display:none}.sc-hero{background:#0b1636 url("/photos/sc-aerial-poster.jpg") center/cover}}


/* Santa Clarita: ribbon-cutting feature photo */
.sc-photo{margin:0;max-width:980px;margin-inline:auto}
.sc-photo img{width:100%;border-radius:18px;display:block;border:1px solid var(--line)}
.sc-photo figcaption{margin-top:var(--s2);font-size:13.5px;color:var(--muted);text-align:center}


/* Moorpark: local crew photo */
.mp-crew-photo{margin:0}
.mp-crew-photo img{width:100%;border-radius:16px;display:block;border:1px solid var(--line)}
.mp-crew-photo figcaption{margin-top:var(--s2);font-size:13.5px;color:var(--muted)}


/* Solutions: split cell = two stacked half-height cards (phone + streaming) */
.sol-split{display:flex;flex-direction:column;gap:var(--s3)}
.sol-split .sol-card{flex:1;margin:0}
.sol-card-sm{padding:clamp(18px,2.2vw,24px)}
.sol-card-sm .sol-ic{width:40px;height:40px;border-radius:11px;margin-bottom:var(--s2)}
.sol-card-sm h2{font-size:clamp(18px,2.1vw,21px);margin:0 0 var(--s1)}
.sol-card-sm>p{margin:0 0 var(--s2);font-size:14px}


/* Streaming: photo-background hero band */
.stream-hero{position:relative;overflow:hidden;color:#fff;display:flex;align-items:center;min-height:clamp(420px,56vh,540px);padding:var(--s6) 0;background:linear-gradient(100deg,rgba(8,14,36,.9),rgba(20,32,68,.62) 55%,rgba(206,53,138,.32)),url("/photos/stream-hero.jpg");background-size:cover;background-position:center}
.stream-hero .eyebrow{color:var(--blue)}
.stream-hero h1{color:#fff}
.stream-hero .lede{color:#e6ecfb}


/* Streaming: MyBundle app phone image */
.mybundle-phone{margin:0}
.mybundle-phone img{width:100%;max-width:520px;height:auto;display:block;margin:0 auto;filter:drop-shadow(0 22px 34px rgba(16,24,54,.22))}


/* Streaming: DirecTV channel-guide photo band */
.directv-band{position:relative;color:#fff;background:linear-gradient(100deg,rgba(8,14,36,.92),rgba(20,32,68,.82) 60%,rgba(20,32,68,.8)),url("/photos/directv-band.jpg");background-size:cover;background-position:center}
.directv-band .eyebrow{color:var(--blue)}


/* App store download badges */
.app-badges{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-top:var(--s3)}
.app-badge{display:inline-flex;align-items:center;gap:10px;background:#000;color:#fff;border-radius:11px;padding:9px 18px;text-decoration:none;transition:transform .12s ease,box-shadow .18s ease}
.app-badge:hover{transform:translateY(-1px);box-shadow:0 10px 22px -12px rgba(0,0,0,.55)}
.app-badge svg{width:22px;height:22px;flex:none}
.app-badge .lab{display:flex;flex-direction:column;line-height:1.05}
.app-badge .lab small{font-size:9.5px;letter-spacing:.02em;opacity:.9}
.app-badge .lab b{font-size:15px;font-family:var(--display);letter-spacing:.01em}


/* Events page */
.ev-list{display:grid;gap:var(--s3)}
.ev-card{display:flex;gap:var(--s4);align-items:flex-start;background:var(--card);border:1px solid var(--line);border-radius:16px;padding:var(--s4)}
.ev-date{flex:none;width:66px;text-align:center;border-radius:12px;padding:10px 0;background:linear-gradient(140deg,rgba(61,155,233,.16),rgba(206,53,138,.12))}
.ev-mon{display:block;font-size:12px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--magenta)}
.ev-day{display:block;font-family:var(--display);font-size:26px;font-weight:800;line-height:1;color:var(--navy)}
.ev-body h3{margin:0 0 4px}
.ev-meta{margin:0 0 var(--s2);font-size:14px;color:var(--muted)}
.ev-empty{text-align:center;background:var(--card);border:1px solid var(--line);border-radius:20px;padding:var(--s6) var(--s4)}
.ev-gigz{width:108px;height:auto;display:block;margin:0 auto var(--s3)}
.ev-empty p{max-width:52ch;margin:var(--s2) auto 0}
.ev-social{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-top:var(--s4)}
.ev-link{display:inline-block;margin-top:var(--s2);font-family:var(--display);font-weight:800;font-size:14px;color:var(--magenta);text-decoration:none}
.ev-link:hover{text-decoration:underline}


/* Events: photo hero band */
.events-hero{position:relative;overflow:hidden;color:#fff;display:flex;align-items:center;min-height:clamp(360px,48vh,460px);padding:var(--s6) 0;background:linear-gradient(100deg,rgba(8,14,36,.86),rgba(20,32,68,.6) 55%,rgba(206,53,138,.3)),url("/photos/events-hero.jpg");background-size:cover;background-position:center}
.events-hero .eyebrow{color:var(--blue)}
.events-hero h1{color:#fff}
.events-hero .lede{color:#e6ecfb}


/* Mobile type scale — shrink headings and ledes on phones only (≤640px).
   Desktop is untouched; !important overrides per-page inline clamp() sizes. */
@media (max-width:640px){
  h1{font-size:clamp(23px,6.2vw,29px)!important}
  h2{font-size:clamp(20px,5vw,24px)!important}
  h3{font-size:clamp(16px,4vw,18px)!important}
  .lede{font-size:clamp(15px,4vw,16px)!important}
}


/* ═══════════════════════════════════════════════════════════════════════════════
   MOBILE REVIEW FIXES — phones only. Every rule here is inside a max-width media
   query, so the desktop rendering is untouched.
   ═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width:640px){
  /* Reviews carousel nav: keep the prev/next arrows at a full 44px pinned to each edge,
     and let the nine dots shrink and centre between them. Before this the row overflowed
     — the arrows were crushed to ~21px and the next one sat off the right wall. */
  .qnav{gap:8px}
  .qarrow{flex:0 0 44px}
  .qdots{flex:1 1 0;min-width:0;justify-content:center;overflow:hidden}
  .qdot{width:18px;margin:0}
  .qdot.on::before{width:20px}
}
@media (max-width:520px){
  /* Promo bar: show the "Fall into Fiber" badge on phones too (it was display:none), and
     give the row more vertical room so the price is not jammed against the top edge. */
  .promo-tag{display:inline-flex;align-items:center}
  .promo-in{padding-block:12px;gap:5px 10px}
  /* Availability bar: title + close button share the top row (grid), the address form
     drops to its own row. The close X can no longer overlap the Check availability button. */
  .avail-in{display:grid;grid-template-columns:1fr auto;column-gap:8px;row-gap:10px;align-items:start}
  .avail-txt{grid-column:1;grid-row:1;padding-right:0;flex:none}
  .avail-x{grid-column:2;grid-row:1;position:static;top:auto;right:auto}
  .avail form{grid-column:1 / -1;grid-row:2;width:100%}
  /* Footer legal row: copyright on its own line, then Broadband facts sitting in line with
     Privacy and Terms instead of jammed into a corner. */
  .foot-bot > span:first-child{flex:1 1 100%}
  .foot-bot .sp{margin-left:0}
}
@media (max-width:680px){
  /* Keep the wordmark together instead of breaking "Aspire" and "Fiber" across lines. */
  .tod-key b{white-space:nowrap}
  /* Let the Aspire line read as one natural sentence like the cable line below it. The
     base .tod-key span rule (inline-flex) sits later in the file and was overriding the
     mobile block, which made "Aspire Fiber" render as its own separate box. */
  .tod-key span{display:block;position:relative;padding-left:32px;line-height:1.45}
  .tod-key i{position:absolute;left:0;top:9px}
}
@media (max-width:520px){
  /* Coverage hero: keep the two CTAs on one row (they wrapped and stacked before) by
     letting each take half the width with tighter padding. */
  .hero-cta-row{flex-wrap:nowrap;gap:10px}
  .hero-cta-row .btn-lg{flex:1 1 0;min-width:0;padding:15px 10px;font-size:15px;
    justify-content:center;white-space:nowrap}
}


/* ═══ What-to-expect + closing-box mobile fixes (phones only) ═══  WTE MOBILE FIXES */
.bld-hint{display:none}
@media (max-width:600px){
  /* Construction stepper: shrink the six stages to fit one row instead of scrolling
     sideways (visitors read later stages as missing). Placed after the base/≤900 rail
     rules so these win. */
  .bld-rail{overflow-x:visible;gap:2px;padding:8px 0;margin:0 0 10px}
  .bld-t{min-width:0;flex:1 1 0;gap:5px}
  .bld-t i{width:32px;height:32px;font-size:13px}
  .bld-t span{font-size:10px;line-height:1.15;overflow-wrap:break-word;hyphens:manual}
  .bld-t::before{top:16px}
  /* The "tap each stage" hint, shown only once the stepper is actually active. */
  .bld-rail:not([hidden]) + .bld-hint{display:block;margin:0 0 var(--s4);
    font-size:13px;color:var(--muted);text-align:center}
}
@media (max-width:719px){
  /* Install process becomes an accordion: each picture lives under its own step and opens
     on tap, so you never scroll back up to a photo that changed off-screen. The .acc
     classes and the picture relocation are driven by bld.js. */
  .ins.acc{display:block}
  .ins.acc .ins-stage{display:none}
  .ins-steps.acc{display:block}
  .ins-steps.acc > li{cursor:pointer;position:relative;padding:13px 42px 14px 15px;
    border:1px solid var(--line);border-radius:14px;margin:0 0 10px;background:var(--card)}
  .ins-steps.acc > li .ins-f{grid-column:1 / -1;margin-top:12px}
  /* Collapsed steps show just the title as a tappable header; the description and the
     picture reveal together on tap. */
  .ins-steps.acc > li:not(.open) .ins-c p{display:none}
  .ins-steps.acc > li:not(.open){padding-bottom:14px}
  .ins-steps.acc > li::after{content:"";position:absolute;right:17px;top:19px;
    width:8px;height:8px;border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
    transform:rotate(45deg);transition:transform .18s}
  .ins-steps.acc > li.open{background:var(--bg)}
  .ins-steps.acc > li.open::after{transform:rotate(-135deg);top:22px}
  .ins-steps.acc > li.open .ins-n{color:#fff;border-color:transparent;
    background:linear-gradient(135deg,var(--blue),var(--magenta));
    box-shadow:0 0 0 5px rgba(206,53,138,.13)}
  .ins-steps.acc > li:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
}
@media (max-width:520px){
  /* Closing CTA bands: trim the side padding on phones so the title and body get the full
     width and short words like "yet" stop orphaning. Applies to every band-cta, desktop
     untouched. */
  .band{padding:34px 18px}
}


/* ═══ About: tighten the space above Gigz on phones ═══ */
@media (max-width:760px){
  /* The 320px stage with flex-end left a tall empty gap above the mascot on a phone. */
  .gigz-stage{min-height:0;align-items:center;padding-top:30px}
  .gigz-band.sect{padding-top:34px}
  .gigz-in{gap:24px}
}

/* ═══ Support mobile fixes ═══  SUPPORT MOBILE FIXES */
.sup-jump{display:none}
#checks{scroll-margin-top:84px}
@media (max-width:920px){
  /* On a phone the ticket form sits above the three checks, so a jump button up top sends
     people straight down to try them first. Hidden on desktop, where the checks sit beside
     the form already. */
  .sup-jump{display:inline-flex;margin-top:var(--s3)}
}


/* ═══ Band CTA title prominence + contact photo order (mobile) ═══  BAND TITLE + CONTACT PHOTO */
@media (max-width:520px){
  /* The closing CTA title wrapped into two short centred lines and read as smaller than the
     body beneath it. A touch more size plus balanced wrapping makes it a proper headline. */
  .band h2{font-size:clamp(23px,6.4vw,28px)!important;text-wrap:balance;line-height:1.12}
  .band p{text-wrap:pretty}
}
@media (max-width:760px){
  /* Contact: lift the office photo up to sit right under the three info cards instead of
     being stranded at the very bottom of the page. */
  .contact-grid .contact-photo{order:-1}
}


/* ═══ Streaming hero framing (mobile) ═══  STREAMING HERO FRAMING */
@media (max-width:760px){
  /* Shift the crop toward the TV so the remote-pointing-at-screen scene is visible, instead
     of only the viewer's head on the left. */
  .stream-hero{background-position:72% center}
}

/* Phone-solutions hero: a transparent lifestyle image that floats on the white hero with
   no card frame (owner request). Only this page uses .hero-float; the shared .plans-hero-img
   frame on Plans/About is untouched. */
.plans-hero-img.hero-float{border-radius:0;box-shadow:none;aspect-ratio:auto;height:auto;object-fit:contain;background:transparent;max-width:560px}

/* Plans hero: overhead family-using-devices photo as a full-bleed background with a navy
   scrim so the white heading stays legible (owner request). */
.plans-photo-hero{position:relative;color:#fff;border-bottom:1px solid var(--line);
  background:linear-gradient(100deg,rgba(9,14,34,.93) 0%,rgba(13,20,48,.76) 42%,rgba(18,28,66,.54) 70%,rgba(206,53,138,.42) 100%),url("/photos/plans-hero.jpg");
  background-size:cover;background-position:center 58%;padding:var(--s6) 0}
.plans-photo-hero .eyebrow{color:var(--blue-lo)}
.plans-photo-hero h1{color:#fff}
.plans-photo-hero .lede{color:#e9eefb}
@media (max-width:760px){.plans-photo-hero{padding:var(--s5) 0;background-position:62% center}}

/* ═══ Mobile intro splash — home page only, assets/splash.js ═══ */
.splash{position:fixed;inset:0;z-index:200;display:none;align-items:center;justify-content:center;
  background:linear-gradient(158deg,#141235 0%,var(--navy) 46%,#6c2f7a 100%);color:#fff;
  opacity:1;transition:opacity .5s ease}
.splash.hide{opacity:0;pointer-events:none}
.splash-bg{position:absolute;inset:0;overflow:hidden}
.splash-bg::before{content:"";position:absolute;inset:0;
  background-image:repeating-linear-gradient(0deg,rgba(255,255,255,.05) 0 1px,transparent 1px 46px),
    repeating-linear-gradient(90deg,rgba(255,255,255,.05) 0 1px,transparent 1px 46px);
  -webkit-mask-image:radial-gradient(130% 80% at 50% 38%,#000 42%,transparent 100%);
          mask-image:radial-gradient(130% 80% at 50% 38%,#000 42%,transparent 100%)}
.splash-bg svg{position:absolute;inset:0;width:100%;height:100%}
.splash-in{position:relative;z-index:1;width:100%;max-width:360px;padding:0 30px;text-align:center}
.splash-logo{display:flex;align-items:center;justify-content:center;gap:10px}
.splash-logo img{height:46px;width:auto}
.splash-wm{font-family:var(--display);font-weight:800;font-size:33px;letter-spacing:-.02em;line-height:1;color:var(--magenta)}
.splash-wm span{color:#fff}
.splash-tag{font-family:var(--display);font-weight:800;font-size:21px;margin:16px 0 0;color:#fff}
.splash-form{display:flex;flex-direction:column;gap:10px;margin-top:32px}
.splash-form input{border:1.5px solid rgba(255,255,255,.3);border-radius:12px;padding:14px 16px;
  font-size:16px;font-family:var(--sans);background:#fff;color:var(--ink)}
.splash-form .btn{width:100%;justify-content:center}
.splash-skip{margin-top:18px;background:none;border:0;color:#dbe4fb;font-family:var(--display);
  font-weight:700;font-size:15px;text-decoration:underline;cursor:pointer;min-height:44px}
.splash-skip:hover{color:#fff}
@media (max-width:760px){.splash{display:flex}}
@media (prefers-reduced-motion:reduce){.splash{transition:none}}

/* ═══ Promo banner — centred stacked layout on phones ═══  PROMO RESTRUCTURE */
@media (max-width:520px){
  .promo-in{padding:calc(13px + env(safe-area-inset-top,0px)) 14px 14px;gap:3px;
    flex-direction:column;align-items:center;text-align:center;flex-wrap:nowrap}
  .promo-tag{align-self:center;margin:0 0 2px}
  .promo-txt{font-size:14px;line-height:1.3;margin:0}
  .promo-txt b{font-size:15.5px}
  .promo-cta{margin:3px 0 0;font-size:14px;font-weight:800;width:auto}
  .promo-x{top:8px;right:12px}
}

/* ═══ No sticky availability bar on phones ═══  HIDE AVAIL MOBILE */
@media (max-width:760px){.avail{display:none!important}}
