/* ============================================================
   NEXO — ART DIRECTION LAYER  ("Instrument")
   Loaded LAST on every page, after styles.css, tokens.css and
   the page stylesheet, so it can re-art shared primitives
   without touching the markup that depends on them.

   WHY THIS LAYER EXISTS
   The audit found one problem repeated eight times: every band
   was centred eyebrow -> centred H2 with a blue gradient span ->
   centred lead -> grid of soft rounded cards on blue-grey. One
   typeface (Sora) carried display, body and data. That sameness
   — not any single element — is what read as template work.

   The three moves here:
     1. a real type pairing, incl. a mono for data/labels
     2. section headers become left-aligned editorial blocks
        with a running index number instead of centred stacks
     3. cards lose the soft-SaaS shell: hairline borders,
        tight radii, no heavy drop shadows

   Nothing below changes copy, structure or behaviour.
   ============================================================ */

/* ------------------------------------------------------------
   1. TYPE PAIRING
   Space Grotesk sets display — it has real character at large
   sizes where Sora goes generic. Inter carries body copy, which
   Sora handles poorly below 16px. JetBrains Mono takes labels,
   counters, timecodes and camera IDs: this is a monitoring
   product, so its data should look like data.
   ------------------------------------------------------------ */
:root {
  --font-head:    'Space Grotesk', 'Sora', system-ui, sans-serif;
  --font-body:    'Inter', system-ui, -apple-system, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, 'SFMono-Regular', monospace;

  /* ---- surfaces ----
     #E9E9E9 blue-grey read as cold default SaaS. A warm paper
     white sits under the blue far better and looks printed. */
  --bg:        #F6F5F2;
  --bg-2:      #FFFFFF;
  --surface:   #FFFFFF;
  --surface-2: #F1EFEA;
  --paper-deep:#EDEAE3;

  /* ---- ink ---- */
  --text:      #0B0D12;
  --text-soft: #4A4F5C;
  --text-dim:  #878C99;

  /* hairlines, not shadows — these carry all the structure now,
     so they need to actually be visible on white */
  --line:      rgba(11, 13, 18, 0.16);
  --line-2:    rgba(11, 13, 18, 0.26);

  /* ---- geometry ----
     18–30px radii made every element a soft capsule. Tight
     radii + hairlines read precise, which is the brand. */
  --radius:    6px;
  --radius-lg: 10px;
  --r-xs: 4px; --r-sm: 5px; --r-md: 6px; --r-lg: 8px; --r-xl: 12px;

  /* elevation is nearly gone; borders do the work now */
  --shadow: 0 18px 44px -28px rgba(11, 13, 18, 0.35);
  --sh-1: 0 1px 2px rgba(11,13,18,0.04);
  --sh-2: 0 2px 8px -4px rgba(11,13,18,0.10);
  --sh-3: 0 18px 44px -30px rgba(11,13,18,0.30);
  --sh-4: 0 30px 70px -44px rgba(11,13,18,0.38);
  --sh-blue:   0 10px 30px -18px rgba(0,70,255,0.40);
  --sh-blue-l: 0 18px 44px -24px rgba(0,70,255,0.45);

  /* ---- editorial type scale ----
     Wider range than before, so display type actually leads. */
  --t-h1:   clamp(38px, 6.2vw, 82px);
  --t-h2:   clamp(30px, 4.4vw, 56px);
  --t-h3:   clamp(18px, 1.5vw, 21px);
  --t-lead: clamp(15px, 1.1vw, 17px);
  --t-body: 15.5px;
  --lh-tight: 1.02;
  --lh-body:  1.62;
  --ls-head: -0.035em;

  --measure: 1240px;
  --sec-y: clamp(72px, 8.5vw, 132px);   /* more air between bands */
}

body { background: var(--bg); font-family: var(--font-body); letter-spacing: -0.005em; }

h1, h2, h3, h4,
.section-head h2, .hero h1, .page-hero h1, .cta-strip h2 {
  font-family: var(--font-head);
  font-weight: 600;
  letter-spacing: var(--ls-head);
}
main h1, .hero h1 { font-size: var(--t-h1); font-weight: 600; line-height: var(--lh-tight); }
main h2 { font-size: var(--t-h2); font-weight: 600; line-height: 1.05; }

/* ------------------------------------------------------------
   2. THE GRADIENT-TEXT TIC
   Every H2 on the site had a blue gradient span. Used that
   often it stops being emphasis and becomes wallpaper. The
   phrase now stays ink and is marked with a rule underneath —
   emphasis you notice because it is rationed.
   ------------------------------------------------------------ */
/* The rule is painted as a background, not a positioned ::after, and
   the box decoration is cloned onto every line box. An abspos ::after
   only covers the first fragment of a wrapped inline, and pinning the
   phrase with `white-space: nowrap` to dodge that pushed industries.html
   16px past a 390px viewport. This wraps cleanly and underlines each
   line. */
.gradient-text,
.gradient-text.violet {
  color: var(--text);
  -webkit-text-fill-color: currentColor;
  background-image: linear-gradient(var(--blue), var(--blue));
  background-clip: border-box;
  -webkit-background-clip: border-box;
  background-repeat: no-repeat;
  background-size: 100% 2px;
  background-position: 0 100%;
  padding-bottom: 0.06em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
/* on dark bands the ink flips to white; keep the rule blue */
.hero .gradient-text, .cta-strip .gradient-text,
.sec-dark .gradient-text, .live-section .gradient-text,
.prob-section .gradient-text { color: #fff; }

/* one exception, and only one: the hero rotator keeps the
   brand gradient. It is the single most important word on the
   site and nothing else competes with it any more. */
.hero .rotator, .hero .rot-word {
  background: var(--grad-primary);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

/* ------------------------------------------------------------
   3. SECTION HEADERS — centred stack -> editorial block
   Heading left, supporting copy in a narrow right column, a
   mono label and hairline above. Sections read as chapters
   instead of eight identical announcements.
   ------------------------------------------------------------ */
/* NOTE: .contact-head is deliberately NOT in this list. It is a
   hero headline, not a section header — routing it through the
   two-column grid stranded the H1 in the right column and
   stretched its label into a full-width bar. It is art-directed
   in section 11 instead. */
.section-head,
.section-title,
.ind-head, .svx-head, .svc-hub-head {
  max-width: var(--measure);
  margin-left: auto; margin-right: auto;
  text-align: left;
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  column-gap: clamp(32px, 5vw, 90px);
  row-gap: 18px;
  align-items: end;
  padding-top: 26px;
  border-top: 1px solid var(--line);
  margin-bottom: clamp(38px, 4.4vw, 64px);
}
.section-head > .eyebrow,
.section-title > .mini-title,
.ind-head > .ind-eyebrow, .svx-head > .eyebrow {
  grid-column: 1 / -1;
  margin-bottom: 4px;
}
.section-head h2, .section-title h2, .ind-head h2, .svx-head h2 {
  grid-column: 1; margin: 0; text-wrap: balance;
}
.section-head p, .section-title p, .ind-head p, .svx-head p {
  grid-column: 2; margin: 0 0 6px 0;
  font-size: var(--t-lead); line-height: 1.6; color: var(--text-soft);
  max-width: 46ch;
}

/* ---- the same treatment for the other page families ----
   The section header markup is not shared: index uses
   .section-head, the 55 city pages use .city-head + .city-kicker,
   services uses .cvs-head, industries .ind-head. Styling only the
   index names left every other page on the old centred stack, so
   each family is named explicitly here.
   The grid only engages where there is a heading AND supporting
   copy to sit beside it; a lone heading just goes left-aligned. */
.city-head, .cvs-head, .vs-head, .svx-vs-head, .proc2-head, .ind-why-head {
  max-width: var(--measure);
  margin-left: auto; margin-right: auto;
  text-align: left;
  padding-top: 26px;
  border-top: 1px solid var(--line);
  margin-bottom: clamp(34px, 4vw, 56px);
}
.city-head:has(h2):has(p),
.cvs-head:has(h2):has(p),
.vs-head:has(h2):has(p) {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  column-gap: clamp(32px, 5vw, 90px);
  row-gap: 16px;
  align-items: end;
}
.city-head:has(h2):has(p) > .city-kicker,
.cvs-head:has(h2):has(p) > .city-kicker { grid-column: 1 / -1; }
.city-head:has(h2):has(p) > h2,
.cvs-head:has(h2):has(p) > h2 { grid-column: 1; margin: 0; text-wrap: balance; }
.city-head:has(h2):has(p) > p,
.cvs-head:has(h2):has(p) > p {
  grid-column: 2; margin: 0 0 6px 0; max-width: 46ch;
  font-size: var(--t-lead); color: var(--text-soft);
}

/* ---- emphasis inside headings ----
   Only index used .gradient-text. City pages mark the key phrase
   with <em>, services with a bare <span>. Same rationing applies:
   ink plus a rule, not a second colour. */
.city-head h2 em, .cvs-head h2 em, .city-sec h2 em,
main h2 > em, .frow h2 > span, .abx-title em,
.about-lead h2 span, .ind-head h2 em, .svx-h2 em,
.svc-det-body h2 em, .stk-body h3 em, .section-head h2 > span {
  font-style: normal;
  color: inherit;
  -webkit-text-fill-color: currentColor;
  background-image: linear-gradient(var(--blue), var(--blue));
  background-clip: border-box; -webkit-background-clip: border-box;
  background-repeat: no-repeat;
  background-size: 100% 2px;
  background-position: 0 100%;
  padding-bottom: 0.06em;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}

/* mono label with a running rule — replaces the pill/eyebrow */
.eyebrow, .ind-eyebrow, .mini-title, .svx-eyebrow,
.city-kicker, .city-eyebrow, .svx-eyebrow-pill, .abx-badge {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
  display: inline-flex; align-items: center; gap: 12px;
  background: none; border: 0; padding: 0; border-radius: 0;
}
.eyebrow::before, .ind-eyebrow::before,
.city-kicker::before, .city-eyebrow::before {
  content: ""; width: 28px; height: 1px; flex: none;
  background: var(--blue); border-radius: 0;
}
/* these markups carry an empty <i> as the pill's dot — the rule
   above replaces it, so hide it rather than leave a stray gap */
.eyebrow > i:empty, .ind-eyebrow > i:empty,
.city-kicker > i:empty, .city-eyebrow > i:empty,
.abx-badge > i:empty, .mini-title > .mt-dot { display: none; }
.abx-badge::before {
  content: ""; width: 28px; height: 1px; flex: none; background: var(--blue);
}

/* Dark bands keep a solid tinted emphasis — a hairline rule
   disappears on navy, and one colour on a dark panel never read
   as the gradient tic in the first place. Listed so the rule
   above does not reach in and flatten them. */
.cta-strip h2 span, .impact-left h2 span, .tb-copy h2 span,
.city-why-panel h2 em, .info h2 span,
.hero h1 em, .page-hero h1 span, .svx-hero-inner h1 em, .ind-hero h1 em {
  background-image: none;
  padding-bottom: 0;
}
/* .center was how these were centred; the layout is left now.
   industry-v2.css styles the pill on `.ind-eyebrow.center` (two
   classes), which outranks a bare `.ind-eyebrow`, so the pill has
   to be unset at matching specificity or it survives. */
.eyebrow.center, .ind-eyebrow.center,
.ind .ind-sec .ind-eyebrow.center {
  justify-content: flex-start;
  background: none;
  border: 0;
  padding: 0;
  border-radius: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--text-dim);
}
/* As grid items these labels stretch the full column and the pill
   background smeared across it. Shrink-wrap them. */
.section-head > .eyebrow, .section-title > .mini-title,
.ind-head > .ind-eyebrow, .svx-head > .eyebrow,
.city-head > .city-kicker, .cvs-head > .city-kicker {
  justify-self: start;
}

/* ------------------------------------------------------------
   4. CARDS — lose the soft-SaaS shell
   ------------------------------------------------------------ */
.card, .ind-chal-card, .ind-why-card, .ci-card, .ind-stat, .acc-item {
  border-radius: var(--radius);
  box-shadow: none;
  border: 1px solid var(--line);
  background: var(--surface);
  transition: border-color .3s var(--ease), transform .3s var(--ease), background .3s ease;
}
.card:hover, .ind-chal-card:hover, .ind-why-card:hover, .ci-card:hover {
  transform: none;
  border-color: var(--blue);
  box-shadow: none;
}
.ind-ph, .ind-area-card, .ind-serv-row.panel, .c-item,
.split-img, .impact-img, .bc-image img { border-radius: var(--radius-lg); }
.ind-final-card, .cta-strip, .svx-final { border-radius: var(--radius-lg); }

/* ------------------------------------------------------------
   5. BUTTONS — pill -> squared, typeset like UI not marketing
   ------------------------------------------------------------ */
.btn, .ind-btn, .svx-btn, .nav-cta, .bc-viewall, .foot-cta, .ci-cta, .ind-sticky-cta {
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-weight: 550;
  letter-spacing: -0.005em;
  font-size: 14.5px;
}
.btn:hover, .ind-btn:hover, .svx-btn:hover, .bc-viewall:hover, .foot-cta:hover {
  transform: translateY(-1px);
}

/* ------------------------------------------------------------
   6. DATA IS SET IN MONO
   Counters, stat numbers, step indices, timecodes and camera
   IDs. This is the detail that makes the brand feel like an
   operations product rather than a landing page.
   ------------------------------------------------------------ */
.hero-mini b, .stat .num, .prob-num, .proc-num, .xp-tag, .step-num,
.lstep-time, .ldash-clock, .cam-time, .ldash-cam-label, .ldash-status,
.ldash-live, .cc-n, .coverage-badge h3, .tb-cred b, .city-hero-stat b {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.stat .num, .coverage-badge h3 {
  background: none; -webkit-text-fill-color: currentColor; color: var(--text);
}
.step-num { -webkit-text-stroke: 1px var(--blue); }

/* ------------------------------------------------------------
   7. BAND RHYTHM
   Alternating bands so the eye gets a beat instead of one
   uninterrupted wash of blue-grey.
   ------------------------------------------------------------ */
.section { padding-left: clamp(20px, 5vw, 56px); padding-right: clamp(20px, 5vw, 56px); }
.logo-wall { background: var(--paper-deep) !important; }
/* white cards need a non-white band under them or the hairlines vanish */
.bento-premium { background: var(--surface-2); border-block: 1px solid var(--line); }

/* ------------------------------------------------------------
   7b. BENTO COMPOSITION
   The featured card floated its product shot in the middle with
   the copy pinned to the bottom, leaving a dead band across the
   card. Anchor the shot to the top so heading -> image -> copy
   reads as one column. The alarm card ran copy straight over a
   busy photo; it gets a scrim.
   ------------------------------------------------------------ */
.feat-cam .feat-art { top: 78px; bottom: 132px; }
.feat-cam .feat-art img { object-position: center 40%; }

.feat-alarm.feat-photo::after,
.feat-wide.feat-photo::after {
  content: "";
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(100deg,
              rgba(255,255,255,0.97) 0%,
              rgba(255,255,255,0.90) 38%,
              rgba(255,255,255,0.20) 66%,
              rgba(255,255,255,0) 100%);
}
.feat-alarm.feat-photo h3, .feat-alarm.feat-photo p,
.feat-wide.feat-photo h3, .feat-wide.feat-photo p { position: relative; z-index: 2; }
.feat-alarm.feat-photo h3, .feat-alarm.feat-photo p { max-width: 78%; }

/* the decorative blur orbs / mesh were doing the "AI gradient"
   look more than any single element. Dim them hard. */
.bento-fx, .prob-bg .mesh, .bmesh, .bc-fx { opacity: .34; }
.orb, .orb-a, .orb-b, .orb-c, .svx-bg .o1, .svx-bg .o2 { opacity: .3; }

/* ------------------------------------------------------------
   8. RESPONSIVE
   The two-column header collapses to a stack; everything else
   inherits the existing breakpoints.
   ------------------------------------------------------------ */
@media (max-width: 900px) {
  .section-head, .section-title, .ind-head, .svx-head,
  .svc-hub-head {
    grid-template-columns: 1fr;
    row-gap: 14px;
    align-items: start;
  }
  .section-head h2, .section-title h2, .ind-head h2, .svx-head h2,
  .section-head p, .section-title p, .ind-head p, .svx-head p { grid-column: 1; }
  .section-head p, .section-title p, .ind-head p, .svx-head p { max-width: 60ch; }
  :root { --sec-y: clamp(56px, 9vw, 84px); }
}
@media (max-width: 640px) {
  :root { --t-h1: clamp(34px, 10vw, 46px); --t-h2: clamp(27px, 7.4vw, 34px); }
  .section-head, .section-title, .ind-head, .svx-head { padding-top: 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .btn:hover, .ind-btn:hover, .svx-btn:hover { transform: none; }
}

/* ============================================================
   8b. HERO CLEARANCE UNDER THE FIXED NAV
   The nav is position:fixed and 132px tall (168px on mobile,
   where the top contact bar wraps), but every hero starts at
   top:0 underneath it. Measured clearance from the nav's bottom
   edge to the hero H1:

     contact.html  desktop   -6px   <- H1 sat behind the nav
     index.html    mobile     0px   <- touching
     city-*        mobile    38px
     industries    mobile    49px

   Heroes reserve the nav's height as padding. Because
   box-sizing is border-box globally, a 100vh hero stays 100vh
   and its centred content simply centres in what is left.
   ============================================================ */
:root { --nav-h: 132px; --hero-gap: clamp(28px, 4vw, 64px); }
@media (max-width: 900px) { :root { --nav-h: 168px; } }

.hero, .page-hero, .contact, .svx-hero-wrap,
.ind-hero, .city-hero, .svx-hero {
  padding-top: calc(var(--nav-h) + var(--hero-gap));
}
/* these two set their own generous top padding already; adding
   the nav height on top would push the headline off the fold */
.page-hero, .ind-hero { padding-top: calc(var(--nav-h) + clamp(16px, 2vw, 34px)); }

/* give the display type room to breathe now that it is bigger */
.hero h1, .city-hero h1, .ind-hero h1, .svx-hero-inner h1 {
  text-wrap: balance;
  max-width: 20ch;
}
.hero p.sub, .city-hero .lead, .ind-hero .ind-lead {
  max-width: 54ch;
  color: var(--text-soft);
}
/* the home hero sits on a light gradient, so the dark-scope --text-soft
   (white) is unreadable there — use the dark body ink instead */
.hero p.sub { color: #0A1220; }

/* ============================================================
   9. MOBILE LAYOUT REPAIR
   Several sections were laid out with fixed pixel heights that
   only ever fit the desktop composition. On a phone the content
   is two to three times taller than the box holding it, and the
   box has overflow:hidden — so headings and copy were being cut
   off mid-word rather than wrapping.

   The rule applied throughout: on small screens a container may
   set a MINIMUM height, never a fixed one.
   ============================================================ */
@media (max-width: 900px) {

  /* ---- 9a. BENTO — the visible clipping ----
     .bento sets grid-auto-rows to a fixed 190-212px and each card
     is overflow:hidden. "AI Security Cameras" and the fencing copy
     were being sliced. One column, rows sized to their content. */
  .bento,
  .bento-premium .bento {
    grid-template-columns: 1fr;
    grid-auto-rows: auto;
    gap: 14px;
  }
  .bento > *,
  .bento-premium .bento > * {
    grid-column: 1 / -1 !important;
    grid-row: auto !important;
  }
  .bento-premium .card {
    justify-content: flex-start;
    padding: 22px;
    min-height: 0;
  }
  /* the product shot is absolutely positioned between two offsets,
     so the card needs room for it before the copy starts */
  .bento-premium .card.feat-cam { min-height: 340px; padding-top: 210px; }
  .feat-cam .feat-art { top: 56px; bottom: auto; height: 150px; }

  /* Photo cards put the image in a right-hand 55% column, which
     leaves no readable width on a phone. Overlaying copy on a
     scrim does not work either — these are busy photographs.
     Make the image a banner across the top and set the copy
     underneath it, which is legible at any width. */
  .bento-premium .card.feat-photo {
    padding-top: 0;
    display: flex; flex-direction: column;
  }
  .feat-photo .feat-art {
    position: relative !important;
    inset: auto !important;
    width: 100% !important;
    height: auto !important;
    aspect-ratio: 16 / 9;
    margin: 0 0 16px !important;
    filter: none !important;
    transform: none !important;
    order: -1;
  }
  .feat-photo .feat-art img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
  }
  /* the scrim exists to lift copy off the photo; with the copy
     no longer over the photo it would just grey the card out */
  .feat-wide.feat-photo::after,
  .feat-alarm.feat-photo::after { display: none; }
  .feat-photo h3, .feat-photo p { max-width: 100% !important; padding-inline: 22px; }
  .feat-photo p { padding-bottom: 22px; }
  .bento-premium .card.feat-wide, .bento-premium .card.feat-sm { min-height: 0; }
  .bento-premium .card.feat-cta { min-height: 0; }

  /* ---- 9b. "WHAT WE DELIVER" (xpanel) ----
     A 3-up accordion where the open panel takes flex:3 and the
     other two take flex:1. At 350px that leaves the two closed
     panels ~70px wide — unreadable slivers, so only one service
     of three was legible. Stack them; every panel stays open. */
  .xpanel {
    flex-direction: column;
    height: auto;
    gap: 12px;
  }
  .xpanel .xp-item,
  .xpanel .xp-item.on {
    flex: 1 1 auto;
    min-height: 300px;
  }
  /* .xp-desc / .xp-mini animate in only for the .on panel, so the
     stacked panels showed a bare title over a photo. With no
     accordion on mobile there is nothing to reveal — show all. */
  .xpanel .xp-item .xp-body { opacity: 1; visibility: visible; transform: none; }
  .xpanel .xp-item .xp-desc,
  .xpanel .xp-item .xp-mini { opacity: 1; transform: none; }
  .xpanel .xp-item .xp-arrow { opacity: 1; transform: none; }
  .xpanel .xp-item:not(.on) .xp-title {
    position: static; left: auto; bottom: auto; right: auto; transform: none;
  }
  /* the photo needs a scrim or white copy over a bright frame is
     unreadable — the accordion relied on the closed state hiding it */
  .xpanel .xp-item::after {
    background: linear-gradient(180deg,
                rgba(6,12,26,0.10) 0%,
                rgba(6,12,26,0.62) 46%,
                rgba(6,12,26,0.88) 100%) !important;
  }
  .xp-body { padding: 20px; }
  .xp-mini { flex-wrap: wrap; gap: 8px; margin-top: 14px; }
  .xp-mini .m { flex: 1 1 100%; max-width: 100%; padding: 11px 13px; }

  /* ---- 9c. LIVE RESPONSE dashboard ----
     The status bar (LIVE / camera ID / state / clock) is the part
     that makes this read as an operations console. It was being
     dropped on mobile, leaving an unlabelled video. Keep it, and
     let it wrap onto two lines rather than squeeze. */
  .ldash-top {
    display: flex !important;
    flex-wrap: wrap;
    row-gap: 6px;
    column-gap: 10px;
    padding: 10px 14px;
  }
  .ldash-cam-label {
    order: 3; flex: 1 1 100%;
    font-size: 10.5px; letter-spacing: .3px;
  }
  .ldash-live, .ldash-status, .ldash-clock { font-size: 10.5px; }
  .ldash-clock { margin-left: auto; }
  .ldash-cam { min-height: 210px; }
  .ldash-steps { padding: 6px; }

  /* ---- 9d. THE IMPACT before/after ----
     The two column headers were display:none below 560px, so the
     rows became an unlabelled alternating list — there was no way
     to tell a "before" row from an "after" row. Restore the
     distinction per cell instead of per column. */
  .impact-left { min-height: 0; height: auto; }
  .impact-left > div { min-width: 0; }
  .impact-cols { display: none; }
  .impact-row {
    grid-template-columns: 1fr;
    gap: 10px;
    padding: 14px 0;
  }
  .ic-cell {
    position: relative;
    padding: 14px 14px 14px 15px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
  }
  .ic-cell.before { background: var(--surface-2); border-style: dashed; }
  .ic-cell.after  { border-color: rgba(0,70,255,0.34); }
  .ic-cell::before {
    content: "BEFORE";
    display: block; margin-bottom: 8px;
    font-family: var(--font-mono);
    font-size: 9.5px; font-weight: 600; letter-spacing: .16em;
    color: var(--text-dim);
  }
  .ic-cell.after::before { content: "WITH NEXO"; color: var(--blue); }
}

/* ------------------------------------------------------------
   10. MOBILE TYPE SCALE
   Display sizes tuned for a 1440px canvas were carrying straight
   down to a 390px one. Everything steps down, and the long-form
   body copy stops competing with the headings.
   ------------------------------------------------------------ */
@media (max-width: 640px) {
  :root {
    --t-h1:   clamp(28px, 8vw, 36px);
    --t-h2:   clamp(22px, 6.2vw, 28px);
    --t-h3:   16.5px;
    --t-lead: 14.5px;
    --t-body: 14.5px;
    --lh-tight: 1.08;
  }
  main h1, .hero h1 { font-size: var(--t-h1); letter-spacing: -0.02em; }
  main h2 { font-size: var(--t-h2); letter-spacing: -0.02em; }
  main h3 { font-size: var(--t-h3); }
  main p, .lead, .section-head p, .ind-head p, .city-head p { font-size: var(--t-lead); }

  .hero p.sub { font-size: 15px; line-height: 1.58; }
  .hero-mini b { font-size: 21px; }
  .hero-mini small { font-size: 10.5px; }

  .bento-premium .card h3 { font-size: 16.5px; }
  .bento-premium .card p  { font-size: 13.5px; line-height: 1.55; }
  .xp-title { font-size: 19px; }
  .xp-desc  { font-size: 13.5px; }
  .xp-mini .m b    { font-size: 13px; }
  .xp-mini .m span { font-size: 12px; }
  .lstep-body b { font-size: 13.5px; }
  .lstep-body p { font-size: 12.5px; }
  .ic-cell b { font-size: 13.5px; }
  .ic-cell p { font-size: 12.5px; }
  .card h3 { font-size: 16.5px; }
  .card p  { font-size: 13.5px; }
  .eyebrow, .ind-eyebrow, .city-kicker, .city-eyebrow, .mini-title { font-size: 10px; }
}

/* ============================================================
   11. CONTACT PAGE
   Rebuilt composition. The old page centred a headline over two
   equal glass slabs floating on a dark video, so the eye had no
   entry point and the form — the only thing on the page that
   matters — carried the same weight as the address block.

   New structure:
     - hero headline left-aligned against the dark band, with the
       trust proof set as mono data beside it
     - the form is the hero object: white, wide, and the first
       thing in the reading order on the right
     - contact details drop to a quieter sidebar that scrolls
       with the form rather than competing with it
   ============================================================ */
.contact-head {
  position: relative; z-index: 2;
  max-width: var(--measure);
  margin: 0 auto clamp(34px, 4vw, 56px);
  text-align: left;
  color: #fff;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  column-gap: clamp(32px, 5vw, 80px);
  row-gap: 14px;
  align-items: end;
}
.contact-head > .eyebrow {
  grid-column: 1 / -1;
  justify-self: start;
  background: none; border: 0; padding: 0;
  color: rgba(255,255,255,0.60);
}
.contact-head > .eyebrow::before { background: #5A8CFF; }
.contact-head h1 {
  grid-column: 1; margin: 0;
  font-size: clamp(34px, 4.8vw, 64px);
  line-height: 1.03;
  letter-spacing: -0.035em;
  max-width: 15ch;
  text-wrap: balance;
}
.contact-head h1 span { color: #7FA8FF; }
.contact-head p {
  grid-column: 2; margin: 0 0 8px 0;
  max-width: 44ch;
  font-size: var(--t-lead);
  line-height: 1.65;
  color: rgba(255,255,255,0.74);
}

/* ---- the two panels ---- */
.contact-grid {
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: clamp(18px, 2.4vw, 30px);
  align-items: start;
}
.contact-grid > .info,
.contact-grid > .form-card {
  border-radius: var(--radius-lg);
  border: 1px solid rgba(255,255,255,0.14);
}
/* details panel: quieter, so it reads as reference not as a
   second call to action */
.contact-grid > .info {
  padding: clamp(26px, 2.6vw, 38px);
  background: rgba(255,255,255,0.055);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  color: #fff;
  position: sticky;
  top: calc(var(--nav-h) + 18px);
}
.info-item .label {
  font-family: var(--font-mono);
  font-size: 10px; letter-spacing: .16em;
  color: rgba(255,255,255,0.52);
}
.info-item .value { color: #fff; font-size: 16px; }
.info-item a.value:hover { color: #7FA8FF; }
.info-item { border-bottom-color: rgba(255,255,255,0.10); }
.info-ic {
  width: 42px; height: 42px; border-radius: var(--radius);
  background: rgba(0,70,255,0.20);
  border: 1px solid rgba(120,160,255,0.34);
  box-shadow: none;
}
.info-ic svg { width: 19px; height: 19px; }
.info-item:hover .info-ic { transform: none; box-shadow: none; border-color: #5A8CFF; }
.contact-trust span,
.cb-guarantees li { color: rgba(255,255,255,0.76); }
.contact-trust span {
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.13);
  border-radius: var(--radius);
}
.contact-brand { border-top: 1px solid rgba(255,255,255,0.10); }
.cb-name, .contact-brand p { color: rgba(255,255,255,0.80); }
.socials a {
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: var(--radius);
  color: #fff;
}
.socials a:hover { background: var(--blue); border-color: var(--blue); }

/* form: the page's primary object — solid white, full contrast.
   .contact is a dark-context scope in styles.css, which re-points
   --text/--text-soft to white for everything inside it. Turning
   this card white therefore made its heading, labels and help
   text white-on-white, so the ink has to be re-pointed back. */
.contact-grid > .form-card {
  padding: clamp(28px, 3vw, 44px);
  background: #fff;
  border-color: rgba(11,13,18,0.10);
  box-shadow: 0 40px 90px -50px rgba(0,0,0,0.7);
  --text:      #0B0D12;
  --text-soft: #4A4F5C;
  --text-dim:  #878C99;
  color: var(--text);
}
.contact-grid > .form-card h3,
.contact-grid > .form-card .sub,
.contact-grid > .form-card label,
.contact-grid > .form-card p { color: inherit; }
.contact-grid > .form-card h3 { color: var(--text); }
.contact-grid > .form-card .sub,
.contact-grid > .form-card .form-note { color: var(--text-soft); }
.form-card h3 {
  font-size: clamp(22px, 2vw, 28px);
  letter-spacing: -0.03em;
  color: var(--text);
}
.form-card .sub { color: var(--text-soft); font-size: 14.5px; }
.form-card label,
.contact-form label {
  font-family: var(--font-mono);
  font-size: 10px; letter-spacing: .15em;
  color: var(--text-dim);
}
.contact-form input,
.contact-form select,
.contact-form textarea {
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-2);
  font-family: var(--font-body);
  font-size: 14.5px;
  color: var(--text);
  transition: border-color .2s ease, background .2s ease;
}
.contact-form input:focus,
.contact-form select:focus,
.contact-form textarea:focus {
  border-color: var(--blue);
  background: #fff;
  outline: none;
  box-shadow: 0 0 0 3px rgba(0,70,255,0.12);
}
.contact-form ::placeholder { color: var(--text-dim); }
.contact-form [type="submit"], .form-card .btn {
  border-radius: var(--radius);
  min-height: 52px;
  font-size: 15px;
}

@media (max-width: 900px) {
  .contact-head {
    grid-template-columns: 1fr;
    row-gap: 12px;
    align-items: start;
  }
  .contact-head h1, .contact-head p { grid-column: 1; max-width: 100%; }
  .contact-grid { grid-template-columns: 1fr; }
  /* sticky in a single column just pins the details above the
     form and pushes it off screen */
  .contact-grid > .info { position: static; }
  /* form first: it is why anyone opens this page */
  .contact-grid > .form-card { order: -1; }
}
@media (max-width: 640px) {
  .contact-head h1 { font-size: clamp(28px, 8.4vw, 36px); }
  .contact-head p  { font-size: 14.5px; }
  .info-item .value { font-size: 15px; }
}

/* ------------------------------------------------------------
   12. TESTIMONIAL STAGE HEIGHT
   .tstage is a fixed 250px and its slides are absolutely
   positioned inside it, so the stage cannot size to content.
   Inter runs taller than Sora at the same px, so swapping the
   body face pushed the quote 34px past the card and the last
   line was being cut off. Give the stage room and let the card
   grow into it.
   ------------------------------------------------------------ */
.tstage { height: clamp(300px, 27vw, 330px); }
.tslide .card { min-height: 0; height: auto; }
@media (max-width: 820px) { .tstage { height: 330px; } }
@media (max-width: 560px) { .tstage { height: 390px; } }
@media (max-width: 380px) { .tstage { height: 430px; } }
