/* ==========================================================================
   XTROWS DXP — Design System
   Premium enterprise: restrained palette, generous space, motion on state
   change only. See Master Architecture Section 12.
   ========================================================================== */

/* Self-hosted Inter (variable). Paths are relative to this CSS file, so they
   resolve from any page depth. No Google Fonts dependency — works on
   restricted government/education networks. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/inter-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* ---- Brand ---- */
  --accent: 21 128 61;        /* green-700 — matches XTROWS logo mark */
  --accent-dark: 22 101 52;   /* green-800 */
  --accent-light: 220 252 231;/* green-100 */
  --cta: 245 158 11;          /* amber-500 */
  --cta-dark: 217 119 6;      /* amber-600 */
  --success: 22 163 74;
  --danger: 220 38 38;

  /* ---- Light mode surfaces ---- */
  --bg: 247 248 250;
  --surface: 255 255 255;
  --surface-2: 241 245 249;
  --ink: 15 23 42;
  --ink-soft: 71 85 105;
  --ink-faint: 100 116 139;
  --border: 226 232 240;
  --hero-bg: 7 20 13;
  --hero-ink: 255 255 255;

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --shadow-sm: 0 1px 2px rgba(15,23,42,.06), 0 1px 1px rgba(15,23,42,.04);
  --shadow-md: 0 8px 24px rgba(15,23,42,.08), 0 2px 6px rgba(15,23,42,.04);
  --shadow-lg: 0 20px 48px rgba(15,23,42,.14), 0 4px 12px rgba(15,23,42,.06);
  --maxw: 1240px;
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
}

html[data-theme="dark"] {
  --bg: 10 14 23;
  --surface: 17 24 39;
  --surface-2: 24 33 51;
  --ink: 241 245 249;
  --ink-soft: 203 213 225;
  --ink-faint: 148 163 184;
  --border: 39 49 68;
  --hero-bg: 5 8 15;
  --hero-ink: 255 255 255;
  --shadow-md: 0 8px 24px rgba(0,0,0,.35);
  --shadow-lg: 0 20px 48px rgba(0,0,0,.5);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font);
  background: rgb(var(--bg));
  color: rgb(var(--ink));
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background .25s ease, color .25s ease;
}
img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
h1,h2,h3,h4 { font-weight: 700; line-height: 1.15; margin: 0 0 .5em; letter-spacing: -.01em; }
h1 { font-size: clamp(2rem, 1.4rem + 2.4vw, 3.4rem); }
h2 { font-size: clamp(1.5rem, 1.2rem + 1.3vw, 2.3rem); }
h3 { font-size: 1.3rem; }
p { margin: 0 0 1em; color: rgb(var(--ink-soft)); }
ul, ol { color: rgb(var(--ink-soft)); }
.container { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
/* One vertical rhythm sitewide. 60 of 62 sections already opted into 40px via
   .section-tight, so that is the default; .section-tight is kept as a no-op alias
   so the existing markup keeps working. */
section { padding: clamp(36px, 3.2vw, 52px) 0; }
.section-tight { padding: clamp(36px, 3.2vw, 52px) 0; }
.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: .78rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: rgb(var(--accent)); margin-bottom: 14px;
}
.eyebrow::before { content: ""; width: 18px; height: 2px; background: rgb(var(--cta)); border-radius: 2px; }
.lede { font-size: 1.15rem; color: rgb(var(--ink-soft)); max-width: 62ch; }
.center { text-align: center; margin-left: auto; margin-right: auto; }

/* ---- Buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 24px; border-radius: 999px; font-weight: 600; font-size: .95rem;
  border: 1px solid transparent; cursor: pointer; transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary { background: rgb(var(--cta)); color: #1a1300; box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: rgb(var(--cta-dark)); box-shadow: var(--shadow-md); }
.btn-dark { background: rgb(var(--ink)); color: rgb(var(--bg)); }
.btn-outline { background: transparent; border-color: rgb(var(--border)); color: rgb(var(--ink)); }
.btn-outline:hover { border-color: rgb(var(--accent)); color: rgb(var(--accent)); }
.btn-ghost { background: rgba(var(--accent),.08); color: rgb(var(--accent)); }
.btn-sm { padding: 9px 16px; font-size: .85rem; }
.btn-block { width: 100%; }

/* ---- Header / Nav ---- */
.utility-bar {
  background: rgb(var(--surface-2)); border-bottom: 1px solid rgb(var(--border));
  font-size: .8rem;
}
.utility-bar .container { display: flex; justify-content: flex-end; align-items: center; gap: 20px; height: 36px; }
.utility-coverage { color: rgb(var(--ink-faint)); margin-right: auto; }
/* The utility bar is a desktop convenience strip: five items, no wrapping, and
   justify-content:flex-end. Below ~720px the row is wider than the screen, so the
   overflow spilled off the LEFT edge (the phone number was sliced to "0-…333")
   while "Book a Demo" was clipped by the fixed 36px height on the right.
   Rather than shrink five items into 390px, drop the three that are duplicated
   elsewhere — email is in the footer, and both Portal Login and Book a Demo are
   already reachable from the header button and the sticky bottom bar. */
@media (max-width: 720px) {
  .utility-coverage { display: none; }
  .util-email { display: none; }
  .utility-bar .container {
    justify-content: center; gap: 18px;
    height: auto; min-height: 36px; padding-top: 7px; padding-bottom: 7px;
    flex-wrap: wrap;
  }
  .utility-bar { font-size: .78rem; }
}
.utility-bar a, .utility-bar button { color: rgb(var(--ink-faint)); background: none; border: none; cursor: pointer; font: inherit; display: flex; align-items: center; gap: 5px; }
.utility-bar a:hover, .utility-bar button:hover { color: rgb(var(--accent)); }

.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(var(--surface), .88); backdrop-filter: blur(14px) saturate(160%);
  border-bottom: 1px solid rgb(var(--border));
}
.nav-row { display: flex; align-items: center; justify-content: space-between; height: 74px; gap: 24px; }
.logo { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 1.15rem; letter-spacing: -.02em; }
.logo .mark {
  width: 34px; height: 34px; border-radius: 9px;
  background: linear-gradient(135deg, rgb(var(--accent)), rgb(var(--cta)));
  display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 800; font-size: .95rem;
}
.primary-nav { display: flex; align-items: center; gap: 4px; }
.primary-nav > li { position: relative; list-style: none; }
.primary-nav > li > a, .primary-nav > li > button {
  display: flex; align-items: center; gap: 6px; padding: 10px 14px; border-radius: 8px;
  font-weight: 600; font-size: .92rem; background: none; border: none; color: rgb(var(--ink)); cursor: pointer; font-family: inherit;
}
.primary-nav > li > a:hover, .primary-nav > li > button:hover { background: rgb(var(--surface-2)); }
.primary-nav ul { list-style: none; margin: 0; padding: 0; }
.nav-list { display: flex; align-items: center; gap: 4px; margin: 0; padding: 0; }

.mega {
  position: absolute; top: calc(100% + 10px); left: 0; min-width: 680px;
  background: rgb(var(--surface)); border: 1px solid rgb(var(--border)); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); padding: 26px; display: none; grid-template-columns: repeat(3, 1fr); gap: 22px 28px;
}
.mega::before { content: ""; position: absolute; left: 0; right: 0; top: -10px; height: 10px; } /* ponytail: hover bridge across the 10px gap so the menu survives the trip */
.mega.mega-narrow { min-width: 340px; grid-template-columns: 1fr; }
.mega.mega-2 { min-width: 480px; grid-template-columns: repeat(2, 1fr); }
.mega.mega-3 { min-width: 720px; grid-template-columns: repeat(3, 1fr); }
li.has-mega:hover .mega, li.has-mega:focus-within .mega { display: grid; }
.mega h4 { font-size: .72rem; text-transform: uppercase; letter-spacing: .07em; color: rgb(var(--ink-faint)); margin-bottom: 10px; }
.mega a.mega-link { display: block; padding: 6px 0; font-size: .9rem; font-weight: 600; color: rgb(var(--ink)); border-radius: 6px; }
.mega a.mega-link:hover { color: rgb(var(--accent)); }
/* display:block — the description was running on the same line as the link
   label, which read as one long sentence. */
.mega .mega-desc { display: block; margin-top: 2px; font-size: .78rem; line-height: 1.35; color: rgb(var(--ink-faint)); font-weight: 400; }

.header-actions { display: flex; align-items: center; gap: 10px; }
.icon-btn {
  width: 38px; height: 38px; border-radius: 999px; border: 1px solid rgb(var(--border));
  background: rgb(var(--surface)); display: flex; align-items: center; justify-content: center; cursor: pointer; color: rgb(var(--ink));
}
.icon-btn:hover { border-color: rgb(var(--accent)); color: rgb(var(--accent)); }
.nav-toggle { display: none; }

/* ---- Breadcrumb ---- */
.breadcrumb { display: flex; flex-wrap: wrap; gap: 6px; font-size: .82rem; color: rgb(var(--ink-faint)); padding: 18px 0; }
.breadcrumb a { color: rgb(var(--ink-faint)); }
.breadcrumb a:hover { color: rgb(var(--accent)); }
.breadcrumb .sep { opacity: .5; }
.breadcrumb .current { color: rgb(var(--ink)); font-weight: 600; }

/* ---- Hero ---- */
.hero { position: relative; isolation: isolate; overflow: hidden; background: radial-gradient(1200px 500px at 80% -10%, rgba(76,175,80,.30), transparent), rgb(var(--hero-bg)); color: rgb(var(--hero-ink)); padding: 68px 0 60px; }
.hero > .container { position: relative; z-index: 2; }
.hero p, .hero .lede { color: rgba(255,255,255,.78); }
.hero-grid { display: grid; grid-template-columns: 1.08fr .92fr; gap: 64px; align-items: center; }
/* Hero headings run long (the default is 20+ words), so the global h1 scale
   ran to six lines here. One notch smaller keeps it to three or four. */
.hero h1 { font-size: clamp(1.9rem, 1.25rem + 1.9vw, 2.85rem); line-height: 1.2; max-width: 24ch; margin-bottom: .4em; text-wrap: balance; }
.hero .lede { max-width: 54ch; }
.badge-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }

/* ---- Hero background slideshow ----
   Cross-fade + slow Ken Burns push so the hero reads as alive. Rotation is 10
   lines of JS in main.js (any number of photos); everything visual is CSS, so
   with JS off the first photo simply stays put.
   ponytail: no dots/pause control — it is decorative background, not content,
   so WCAG 2.2.2 does not bite; prefers-reduced-motion kills the motion. */
.hero-bg { position: absolute; inset: 0; z-index: 0; }
.hero-bg img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  /* Resting transform equals the zoom end state, so an outgoing photo keeps
     drifting while it fades instead of snapping back mid-crossfade. */
  opacity: 0; transform: scale(1.14); transition: opacity 1.4s ease, transform 1.4s ease;
  will-change: opacity, transform;
}
.hero-bg img.is-on { opacity: 1; animation: hero-kenburns 8s ease-out forwards; }
@keyframes hero-kenburns { from { transform: scale(1.02); } to { transform: scale(1.14); } }
/* Legibility scrim: darkest behind the copy column, lighter over the photo. */
.hero.has-bg::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(100deg, rgb(var(--hero-bg) / .94) 0%, rgb(var(--hero-bg) / .82) 42%, rgb(var(--hero-bg) / .55) 100%);
}
@media (max-width: 980px) {
  .hero.has-bg::before { background: linear-gradient(180deg, rgb(var(--hero-bg) / .88) 0%, rgb(var(--hero-bg) / .93) 100%); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-bg img { transition: none; }
  .hero-bg img.is-on { animation: none; transform: none; }
}
.badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 7px 13px; border-radius: 999px;
  font-size: .75rem; font-weight: 700; letter-spacing: .02em; background: rgba(255,255,255,.08); color: #fff; border: 1px solid rgba(255,255,255,.16);
}
.hero .cta-row { display: flex; gap: 14px; margin-top: 32px; flex-wrap: wrap; }

/* ---- AI advisor prompt card ---- */
.ai-card {
  background: rgb(var(--surface)); border-radius: var(--radius-lg); padding: 28px; box-shadow: var(--shadow-lg);
  color: rgb(var(--ink)); border: 1px solid rgb(var(--border));
}
/* On the hero the opaque white card fought the photo behind it — glass instead,
   so it reads as part of the hero rather than a sheet dropped on top. */
.hero .ai-card {
  background: rgb(255 255 255 / .07); backdrop-filter: blur(18px) saturate(140%);
  border-color: rgb(255 255 255 / .16); color: #fff; padding: 26px 24px;
  box-shadow: 0 24px 60px rgb(0 0 0 / .35);
}
.hero .ai-card .ai-tag { color: rgb(var(--cta)); letter-spacing: .01em; }
.hero .ai-card .chip { background: rgb(255 255 255 / .1); border-color: rgb(255 255 255 / .2); color: #fff; }
.hero .ai-card .chip:hover { background: rgb(255 255 255 / .2); border-color: rgb(var(--cta)); color: #fff; }
.hero .ai-card .ai-input-row input { background: rgb(255 255 255 / .1); border-color: rgb(255 255 255 / .2); color: #fff; }
.hero .ai-card .ai-input-row input::placeholder { color: rgb(255 255 255 / .6); }
.ai-card .ai-tag { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: .95rem; margin-bottom: 10px; color: rgb(var(--accent)); }
.ai-card-prompt { margin: 0; font-size: 1.02rem; font-weight: 600; line-height: 1.4; color: inherit; }
.hero .ai-card .ai-card-prompt { color: #fff; }
.ai-thread { max-height: 180px; overflow: auto; }
.ai-thread:not(:empty) { margin-top: 16px; }
.ai-chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 0; }
.chip {
  padding: 8px 14px; border-radius: 999px; border: 1px solid rgb(var(--border)); font-size: .82rem; font-weight: 600;
  background: rgb(var(--surface-2)); cursor: pointer; color: rgb(var(--ink));
}
.chip:hover { border-color: rgb(var(--accent)); color: rgb(var(--accent)); }
.ai-input-row { display: flex; gap: 8px; margin-top: 16px; }
.ai-input-row input {
  flex: 1; padding: 13px 16px; border-radius: 12px; border: 1px solid rgb(var(--border)); background: rgb(var(--surface-2)); color: rgb(var(--ink)); font: inherit;
}

/* ---- Grids / Cards ---- */
.grid { display: grid; gap: 22px; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
/* Single-item grids (one published case study, say). Capping the track keeps the
   lone card the size of a card instead of a full-bleed banner. */
.grid-1 { grid-template-columns: minmax(0, 520px); }
.card {
  background: rgb(var(--surface)); border: 1px solid rgb(var(--border)); border-radius: var(--radius-md);
  padding: 24px; transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: rgba(var(--accent),.35); }
.card h3 { margin-bottom: 8px; font-size: 1.08rem; }
.card p { font-size: .9rem; margin-bottom: 12px; }
.card .card-link { font-size: .85rem; font-weight: 700; color: rgb(var(--accent)); }

/* Bottom-align the "Explore ..." link across a row of cards.
   Grid already stretches every card box to the row height, but the link sits
   straight after a paragraph of variable length, so the links landed at
   different heights and the row read as ragged. Only cards that actually have
   a link become flex containers; inline children are opted out of the stretch,
   since a column flex container blockifies them (see .case-card-body above). */
.card:has(> .card-link) { display: flex; flex-direction: column; }
.card:has(> .card-link) > .card-link { margin-top: auto; }
.card:has(> .card-link) > .card-link,
.card:has(> .card-link) > .pill,
.card:has(> .card-link) > .btn,
.card:has(> .card-link) > .sample-tag { align-self: flex-start; }

/* The "Your sector" tile on /industries/ — a prompt, not a vertical. Dashed so it
   reads as the odd one out rather than as a twelfth page we forgot to link. */
.card-invite { background: rgb(var(--surface-2)); border-style: dashed; }

/* Two-line allowance on card headings inside a multi-column grid, so a
   one-word title ("Retail") and a wrapping one ("Government & Public Sector")
   put their body copy on the same line. Released at one column, where there is
   no neighbour to align to. */
@media (min-width: 861px) {
  .grid-3 > .card > h3,
  .grid-4 > .card > h3 { line-height: 1.3; min-height: 2.6em; }
}

.icon-tile {
  width: 46px; height: 46px; border-radius: 12px; display: flex; align-items: center; justify-content: center;
  background: rgba(var(--accent), .1); color: rgb(var(--accent)); margin-bottom: 16px;
}

/* ---- Media placeholder (no real photography yet) ---- */
.media-ph {
  border-radius: var(--radius-md); overflow: hidden; position: relative; aspect-ratio: 4/3;
  background: linear-gradient(135deg, rgba(var(--accent),.16), rgba(var(--cta),.14));
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  border: 1px dashed rgba(var(--accent),.4); color: rgb(var(--accent)); text-align: center; padding: 20px;
}
.media-ph.wide { aspect-ratio: 16/9; }
/* Real photo/video uploaded — drop the dashed placeholder chrome. */
.media-ph.has-media { padding: 0; border: 0; background: none; }
.media-ph.has-media > img,
.media-ph.has-media > iframe { width: 100%; height: 100%; display: block; border: 0; }
.media-ph.has-media > img { object-fit: cover; }
/* Carousel cards: keep every card the same height and letterbox the upload inside
   it, so a portrait product shot is not cropped down to a white sliver. */
.media-ph.has-media.fit-contain { aspect-ratio: 4/3; background: rgba(var(--ink), .05); }
.media-ph.has-media.fit-contain > img { object-fit: contain; }
.media-ph .ph-icon { width: 54px; height: 54px; opacity: .85; }
.media-ph .ph-label { font-size: .78rem; font-weight: 700; color: rgb(var(--ink-faint)); max-width: 220px; }

/* --------------------------------------------------------------------------
   Products catalog — two-up card grid (page-products.php)
   Image on top with the SKU/name caption overlaid on its lower edge, copy
   below, and a stretched "Know more" link that makes the whole card clickable.
   -------------------------------------------------------------------------- */
.product-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 32px; }

.product-card {
  position: relative; display: flex; flex-direction: column;
  background: rgb(var(--surface)); border: 1px solid rgb(var(--border));
  border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-sm);
  transition: box-shadow .3s ease, border-color .3s ease;
}
.product-card:hover { box-shadow: var(--shadow-lg); border-color: rgba(var(--accent),.45); }

/* The cards carry data-reveal, so the sitewide scroll-reveal (main.js + the
   .js-reveal rules further down) already slides the left column in from the left
   and the right column from the right. Restate its transition here only to keep
   the hover shadow animating too — that rule replaces this element's transition
   wholesale, and it wins on specificity from further down the file. */
.js-reveal .product-card[data-reveal] {
  transition: opacity .62s cubic-bezier(.22,.61,.36,1),
              transform .62s cubic-bezier(.22,.61,.36,1),
              box-shadow .3s ease, border-color .3s ease;
}

.product-card-media {
  position: relative; aspect-ratio: 4/3; overflow: hidden;
  background: linear-gradient(135deg, rgba(var(--accent),.16), rgba(var(--cta),.14));
  display: flex; align-items: center; justify-content: center;
  color: rgb(var(--accent));
}
.product-card-media.has-media { background: rgba(var(--ink), .05); }
/* Portrait product shots letterbox inside a 4/3 card (see object-fit below). Rather
   than leave flat bars down each side, fill them with a blurred, over-scaled copy of
   the same photograph — one network request, since it is the identical URL. The blur
   is heavy enough that no detail reads as a second image. */
.product-card-media.has-media::before {
  content: ""; position: absolute; inset: -8%;
  background-image: var(--card-img); background-size: cover; background-position: center;
  filter: blur(26px) saturate(1.25) brightness(.72); transform: scale(1.06);
}
/* Layer order inside the media box: blurred backdrop (::before) < photo < caption.
   The caption must sit above the photo or a full-bleed landscape shot hides it. */
.product-card-media > img { position: relative; z-index: 1; }
.product-card-cap { z-index: 2; }
.product-card-media > img {
  width: 100%; height: 100%; display: block;
  /* Letterbox, never crop — most product renders are portrait. */
  object-fit: contain;
  transition: transform .5s cubic-bezier(.22,.8,.3,1);
}
.product-card:hover .product-card-media > img { transform: scale(1.05); }
.product-card-media .ph-icon { width: 54px; height: 54px; opacity: .85; }

/* Caption sits on the image's lower edge, over a scrim so it stays legible on
   any upload. Pointer-events off so the stretched link underneath still wins. */
.product-card-cap {
  position: absolute; inset: auto 0 0 0; pointer-events: none;
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  padding: 40px 20px 16px;
  background: linear-gradient(to top, rgba(2,6,12,.86) 0%, rgba(2,6,12,.62) 45%, rgba(2,6,12,0) 100%);
}
.product-card-cap .pill { background: rgba(255,255,255,.16); color: #fff; }
.product-card-name {
  margin: 0; color: #fff; font-size: clamp(1.05rem, 1.5vw, 1.3rem);
  line-height: 1.25; font-weight: 700; text-shadow: 0 1px 3px rgba(0,0,0,.4);
  /* The cap is align-items:flex-start, which sizes children to max-content — a long
     name ("Interactive Flat Panel Display") then overflows and is clipped by the
     media box's overflow:hidden. Stretch puts it back on the container's width so it
     wraps instead. */
  align-self: stretch;
}

.product-card-body { padding: 20px 22px 22px; display: flex; flex-direction: column; flex: 1; gap: 14px; }
.product-card-body > p { margin: 0; color: rgb(var(--ink-soft)); font-size: .93rem; }
.product-card-tags { display: flex; flex-wrap: wrap; gap: 8px; }
/* Push "Know more" to the card's foot so every card's link lines up. */
.know-more {
  margin-top: auto; align-self: flex-start;
  font-weight: 700; font-size: .9rem; color: rgb(var(--accent));
  display: inline-flex; align-items: center; gap: 7px;
}
.know-more span { transition: transform .25s ease; }
.product-card:hover .know-more span { transform: translateX(4px); }
/* Stretched link — the whole card is the hit target, but there is only one link. */
.know-more::after { content: ""; position: absolute; inset: 0; z-index: 2; }

@media (max-width: 860px) {
  .product-grid { grid-template-columns: 1fr; gap: 24px; }
  /* One column: a horizontal slide reads as a layout glitch, so rise instead. */
  .js-reveal .product-card[data-reveal="left"],
  .js-reveal .product-card[data-reveal="right"] { transform: translate3d(0, 28px, 0); }
  .js-reveal .product-card[data-reveal].is-revealed { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .product-card, .product-card-media > img, .know-more span { transition: none; }
  .product-card:hover .product-card-media > img { transform: none; }
}

/* --------------------------------------------------------------------------
   Live scene (front-page.php) — a still installation photo whose panel screen
   plays a loop. The photo keeps its natural ratio so the percentage-positioned
   overlay lands on the screen at every viewport with no JS measuring involved.
   -------------------------------------------------------------------------- */
.live-frame { position: relative; margin: 0; border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-lg); }
.live-frame-base { display: block; width: 100%; height: auto; }

.live-screen { position: absolute; overflow: hidden; background: #05080f; }
.live-screen > img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; opacity: 0;
}
/* Slide 1 is a permanent opaque base; later slides fade in on top of it. Fading
   each slide out to transparent in turn would let the black screen show through
   mid-transition, which reads as the panel flickering off — the whole point is
   that it looks like a screen that is simply on. */
.live-screen > img:first-child { opacity: 1; animation: none; }
/* Screens are emissive: lift the content slightly so it reads as backlit rather
   than as a photo pasted into a photo. */
.live-screen > img { filter: saturate(1.12) brightness(1.06); }
/* A faint diagonal sheen sells the glass. Never blocks the image underneath. */
.live-glare {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(112deg, rgba(255,255,255,.14) 0%, rgba(255,255,255,.04) 26%, rgba(255,255,255,0) 52%);
}

/* Each later slide takes its turn on top, then they all drop together at the wrap
   to reveal the base again. Keyframe percentages cannot be derived from a custom
   property, so there is one set per slide position. */
@keyframes live-in-2  { 0%,40%   { opacity: 0 } 50%,92%   { opacity: 1 } 100% { opacity: 0 } }
@keyframes live-in-3a { 0%,23.3% { opacity: 0 } 33.3%,92% { opacity: 1 } 100% { opacity: 0 } }
@keyframes live-in-3b { 0%,56.7% { opacity: 0 } 66.7%,92% { opacity: 1 } 100% { opacity: 0 } }
@keyframes live-in-4a { 0%,15%   { opacity: 0 } 25%,92%   { opacity: 1 } 100% { opacity: 0 } }
@keyframes live-in-4b { 0%,40%   { opacity: 0 } 50%,92%   { opacity: 1 } 100% { opacity: 0 } }
@keyframes live-in-4c { 0%,65%   { opacity: 0 } 75%,92%   { opacity: 1 } 100% { opacity: 0 } }
.live-screen > img { animation-timing-function: ease-in-out; animation-iteration-count: infinite; }
.live-frame[data-slides="2"] .live-screen > img:nth-child(2) { animation-name: live-in-2;  animation-duration: 8s; }
.live-frame[data-slides="3"] .live-screen > img:nth-child(2) { animation-name: live-in-3a; animation-duration: 12s; }
.live-frame[data-slides="3"] .live-screen > img:nth-child(3) { animation-name: live-in-3b; animation-duration: 12s; }
.live-frame[data-slides="4"] .live-screen > img:nth-child(2) { animation-name: live-in-4a; animation-duration: 16s; }
.live-frame[data-slides="4"] .live-screen > img:nth-child(3) { animation-name: live-in-4b; animation-duration: 16s; }
.live-frame[data-slides="4"] .live-screen > img:nth-child(4) { animation-name: live-in-4c; animation-duration: 16s; }

@media (prefers-reduced-motion: reduce) {
  /* Hold the first slide instead of cycling. */
  .live-screen > img { animation: none !important; opacity: 0; }
  .live-screen > img:first-child { opacity: 1; }
}

/* --------------------------------------------------------------------------
   Industry vertical pages (inc/blocks.php)
   -------------------------------------------------------------------------- */
.vertical-hero .two-col { align-items: center; }
.vertical-hero-media {
  margin: 0; border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: var(--shadow-lg); background: rgba(255, 255, 255, .06);
  /* Portrait kiosk shots would run taller than the copy beside them, so cap the
     box and letterbox instead of cropping the product out of frame. */
  aspect-ratio: 4/3; display: flex; align-items: center; justify-content: center;
}
.vertical-hero-media img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* Application cards carry a photo above the copy. */
.app-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
/* Fixed height, not aspect-ratio: in a column flex container flex-basis:auto
   resolves the height from content, so the intrinsic (portrait) image height wins
   and the ratio is ignored. A set height keeps every card's photo band equal. */
.app-card-media { height: 230px; flex: 0 0 230px; background: rgba(var(--ink), .06); }
.app-card-media img { width: 100%; height: 100%; object-fit: contain; display: block; }
.app-card > h3 { margin: 16px 20px 0; }
.app-card > p { margin: 8px 20px 20px; }

@media (max-width: 860px) {
  .vertical-hero .two-col { grid-template-columns: 1fr; gap: 28px; }
}

/* --------------------------------------------------------------------------
   Case-study cards (inc/blocks.php) — used on vertical landing pages and product
   pages. Renders only when a case study has actually been supplied.
   -------------------------------------------------------------------------- */
.case-card {
  display: flex; flex-direction: column; overflow: hidden;
  background: rgb(var(--surface)); border: 1px solid rgb(var(--border));
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.case-card-media { aspect-ratio: 16/10; background: rgba(var(--ink), .06); }
.case-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* align-items must be pinned: this is a column flex container, so a bare
   inline-block child like .pill is blockified and stretches to the full card
   width, rendering the sector chip as a full-bleed bar. */
.case-card-body { padding: 18px 20px 20px; display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.case-card-body > h3, .case-card-body > p { align-self: stretch; }
.case-card-body h3 { margin: 0; font-size: 1.05rem; }
.case-card-body p { margin: 0; color: rgb(var(--ink-soft)); font-size: .92rem; }
.case-card .case-result {
  margin-top: 4px; padding-top: 10px; border-top: 1px solid rgb(var(--border));
  color: rgb(var(--accent)); font-weight: 700; font-size: .9rem;
}

/* Single product body (single-product.php). Marketing writes this in Gutenberg,
   so the shapes that arrive are core blocks — mostly a Gallery of product shots. */
.product-body img { max-width: 100%; height: auto; border-radius: var(--radius-md); display: block; }
.product-body .wp-block-image { margin: 0 0 18px; }
.product-body h2 { margin-top: 28px; }
/* Core's gallery lays its figures out with flex-grow and calc() widths, which
   leaves ragged rows of odd-sized product shots. Override to an even grid of
   fixed-height tiles, letterboxed so the portrait renders are never cropped. */
.product-body .wp-block-gallery.has-nested-images {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 14px; list-style: none; padding: 0; margin: 0;
}
/* Core sizes these with `figure.wp-block-image:not(#individual-image)`, borrowing
   ID specificity on purpose, so !important is the only way a theme can take the
   layout back. Scoped to .product-body, it touches nothing else. */
.product-body .wp-block-gallery.has-nested-images > figure.wp-block-image {
  width: 100% !important; max-width: none !important; flex: none !important; margin: 0 !important;
}
.product-body .wp-block-gallery.has-nested-images > figure.wp-block-image { position: relative; }
.product-body .wp-block-gallery.has-nested-images > figure.wp-block-image > img {
  width: 100% !important; height: 230px !important; object-fit: contain;
  background: rgba(var(--ink), .06); border-radius: var(--radius-md);
}
/* Numbered captions so a buyer can say "we want Design 04" in an enquiry. Core
   positions these statically; pin them to the tile instead. */
.product-body .wp-block-gallery.has-nested-images > figure.wp-block-image > figcaption {
  position: absolute; left: 10px; bottom: 10px; right: auto; top: auto;
  width: auto; max-height: none; margin: 0; padding: 4px 10px; overflow: visible;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: #fff; text-align: left; text-shadow: none;
  background: rgba(2, 6, 12, .72); border-radius: 999px;
}
/* Core paints a dark gradient behind any gallery caption; the pill is enough. */
.product-body .wp-block-gallery.has-nested-images > figure.wp-block-image:before { content: none; }

/* ---- Section header ---- */
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 34px; flex-wrap: wrap; }
.section-head .lede { margin: 0; }

/* ---- Trust / badge strip ---- */
.trust-strip { background: rgb(var(--surface-2)); border-top: 1px solid rgb(var(--border)); border-bottom: 1px solid rgb(var(--border)); }
.trust-strip .container { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; justify-content: space-between; padding: 22px 24px; }
.trust-badges { display: flex; flex-wrap: wrap; gap: 10px; }
.trust-badges span {
  font-size: .76rem; font-weight: 700; padding: 7px 12px; border-radius: 999px; background: rgb(var(--surface)); border: 1px solid rgb(var(--border)); color: rgb(var(--ink-soft));
}

/* ---- Stats ---- */
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; text-align: center; }
.stat-grid strong { display: block; font-size: 2.1rem; font-weight: 800; color: rgb(var(--accent)); }
.stat-grid span { font-size: .84rem; color: rgb(var(--ink-faint)); }

/* ---- Tables ---- */
table { width: 100%; border-collapse: collapse; background: rgb(var(--surface)); border-radius: var(--radius-md); overflow: hidden; }
table.spec-table th, table.spec-table td { border-bottom: 1px solid rgb(var(--border)); padding: 13px 16px; text-align: left; font-size: .9rem; }
table.spec-table th { width: 34%; color: rgb(var(--ink-faint)); font-weight: 600; background: rgb(var(--surface-2)); }
table.compare-table { border: 1px solid rgb(var(--border)); }
table.compare-table th, table.compare-table td { border: 1px solid rgb(var(--border)); padding: 14px 16px; text-align: left; font-size: .88rem; vertical-align: top; }
table.compare-table thead th { background: rgb(var(--surface-2)); font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; color: rgb(var(--ink-faint)); }
table.compare-table td.yes { color: rgb(var(--success)); font-weight: 700; }
table.compare-table td.no { color: rgb(var(--ink-faint)); }

/* ---- Tabs ---- */
.tabs { display: flex; gap: 6px; border-bottom: 1px solid rgb(var(--border)); margin-bottom: 28px; overflow-x: auto; }
.tab-btn { padding: 12px 18px; font-weight: 600; font-size: .88rem; background: none; border: none; cursor: pointer; color: rgb(var(--ink-faint)); border-bottom: 2px solid transparent; white-space: nowrap; font-family: inherit; }
.tab-btn.active { color: rgb(var(--accent)); border-color: rgb(var(--accent)); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* ---- Accordion (FAQ) ---- */
.accordion-item { border-bottom: 1px solid rgb(var(--border)); }
.accordion-trigger {
  width: 100%; text-align: left; padding: 18px 4px; background: none; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; font-weight: 700; font-size: .98rem; color: rgb(var(--ink)); font-family: inherit;
}
.accordion-trigger .plus { transition: transform .2s ease; color: rgb(var(--accent)); font-size: 1.3rem; }
.accordion-item.open .plus { transform: rotate(45deg); }
.accordion-panel { max-height: 0; overflow: hidden; transition: max-height .25s ease; }
.accordion-panel p { padding: 0 4px 18px; }

/* ---- Badges / pills ---- */
.pill { display: inline-block; padding: 5px 12px; border-radius: 999px; font-size: .74rem; font-weight: 700; background: rgba(var(--accent),.1); color: rgb(var(--accent)); }
.pill-cta { background: rgba(var(--cta),.14); color: rgb(var(--cta-dark)); }
.pill-outline { border: 1px solid rgb(var(--border)); color: rgb(var(--ink-faint)); background: none; }
/* Size options on a model page. A model is one product sold in several screen sizes,
   so the sizes read as a property of it rather than as links to separate pages. */
.size-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.size-row-label { font-size: .74rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: rgb(var(--ink-faint)); margin-right: 4px; }
.pill-size { background: rgba(var(--ink), .06); color: rgb(var(--ink)); font-variant-numeric: tabular-nums; }


/* ---- Filter sidebar (download centre) ---- */
.filter-layout { display: grid; grid-template-columns: 260px 1fr; gap: 34px; align-items: start; }
.filter-box { background: rgb(var(--surface)); border: 1px solid rgb(var(--border)); border-radius: var(--radius-md); padding: 22px; position: sticky; top: 96px; }
.filter-box h4 { font-size: .82rem; text-transform: uppercase; letter-spacing: .05em; color: rgb(var(--ink-faint)); margin: 18px 0 10px; }
.filter-box h4:first-child { margin-top: 0; }
.filter-box label { display: flex; align-items: center; gap: 8px; font-size: .87rem; padding: 5px 0; color: rgb(var(--ink-soft)); }
.download-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 18px;
  border: 1px solid rgb(var(--border)); border-radius: var(--radius-sm); margin-bottom: 10px; background: rgb(var(--surface));
}
.download-row .dl-meta { display: flex; align-items: center; gap: 14px; }
.download-row .dl-icon { width: 38px; height: 38px; border-radius: 8px; background: rgba(var(--accent),.1); color: rgb(var(--accent)); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.download-row strong { font-size: .92rem; }
.download-row small { color: rgb(var(--ink-faint)); font-size: .78rem; }
/* Product pages deep-link to a row (/downloads/#xtrows-...). The header is sticky
   at ~96px, so without scroll-margin the row lands underneath it; :target then
   makes it obvious which of eight rows the visitor was sent to. */
.download-row { scroll-margin-top: 110px; }
.download-row:target {
  border-color: rgb(var(--accent));
  box-shadow: 0 0 0 3px rgba(var(--accent), .16);
  background: rgba(var(--accent), .04);
}

/* Section headings inside the downloads list. The list is long enough now that a
   buyer scans for their product family rather than reading top to bottom; the first
   heading loses its top margin so it does not float away from the rows above it. */
.dl-group-heading {
  font-size: .82rem; text-transform: uppercase; letter-spacing: .05em;
  color: rgb(var(--ink-faint)); font-weight: 600;
  margin: 30px 0 12px; padding-bottom: 8px;
  border-bottom: 1px solid rgb(var(--border));
}
.dl-group-heading:first-child { margin-top: 0; }

/* Inline form error — the download gate now has a failure state worth showing, since
   an unsent email is the whole transaction failing rather than a best-effort ping. */
.form-error {
  margin: 0 0 12px; padding: 10px 12px; border-radius: var(--radius-sm);
  background: rgba(220, 38, 38, .08); border: 1px solid rgba(220, 38, 38, .28);
  color: #b91c1c; font-size: .85rem; line-height: 1.45;
}

/* ---- Portal role cards ---- */
.role-card { border: 1px solid rgb(var(--border)); border-radius: var(--radius-md); padding: 26px; background: rgb(var(--surface)); }
.role-card ul { padding-left: 18px; font-size: .87rem; margin: 12px 0 16px; }
.role-card li { margin-bottom: 4px; }

/* ---- Forms ---- */
.form-field { margin-bottom: 18px; }
.form-field label { display: block; font-size: .85rem; font-weight: 700; margin-bottom: 7px; }
.form-field input, .form-field select, .form-field textarea {
  width: 100%; padding: 12px 14px; border-radius: 10px; border: 1px solid rgb(var(--border)); background: rgb(var(--surface)); color: rgb(var(--ink)); font: inherit;
}
.form-field textarea { min-height: 110px; resize: vertical; }

/* ---- CTA banner ---- */
.cta-banner {
  background: linear-gradient(120deg, rgb(var(--accent)), rgb(var(--accent-dark)));
  border-radius: var(--radius-lg); padding: 56px; color: #fff; display: flex; align-items: center; justify-content: space-between; gap: 30px; flex-wrap: wrap;
}
.cta-banner h2 { margin-bottom: 8px; }
.cta-banner p { color: rgba(255,255,255,.82); margin: 0; }

/* ---- Footer ---- */
.site-footer { background: rgb(var(--hero-bg)); color: rgba(255,255,255,.7); padding: 64px 0 28px; margin-top: 40px; }
.footer-grid { display: grid; grid-template-columns: 1.4fr repeat(5, 1fr); gap: 30px; }
.footer-grid h5 { color: #fff; font-size: .82rem; text-transform: uppercase; letter-spacing: .06em; margin-bottom: 14px; }
.footer-grid a { display: block; padding: 5px 0; font-size: .87rem; color: rgba(255,255,255,.62); }
.footer-grid a:hover { color: #fff; }
.footer-bottom { border-top: 1px solid rgba(255,255,255,.12); margin-top: 46px; padding-top: 24px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 14px; font-size: .8rem; color: rgba(255,255,255,.5); }
.footer-badges { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
/* Badges and socials sit in the bottom strip rather than stacked under the address.
   The brand column used to run twice the height of the five link columns, which left
   a large empty block beneath them; moving these two rows down evens the columns out
   and fills a bar that was carrying nothing but the copyright line. */
.footer-bottom .footer-badges,
.footer-bottom .social-row { margin-top: 0; }
/* The legal line gets a row to itself — sharing with the badges and socials squeezed
   it until the sentence wrapped and the CIN broke across two lines. `cin` is nowrap so
   the registration number is never split, whatever the viewport. */
.footer-bottom > .footer-legal { flex: 1 1 100%; }
.footer-bottom .cin { white-space: nowrap; }
.footer-bottom .footer-badges { flex: 0 1 auto; }
.footer-bottom .social-row { flex: 0 0 auto; }
@media (max-width: 780px) {
  .footer-bottom { justify-content: flex-start; }
}
.footer-badges span { font-size: .72rem; font-weight: 700; padding: 6px 11px; border-radius: 999px; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14); }

/* ---- Modal (AI Advisor) ---- */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(5,8,15,.6); backdrop-filter: blur(4px);
  display: none; align-items: center; justify-content: center; z-index: 300; padding: 20px;
}
.modal-overlay.open { display: flex; }
.modal-box { background: rgb(var(--surface)); border-radius: var(--radius-lg); max-width: 560px; width: 100%; padding: 30px; box-shadow: var(--shadow-lg); max-height: 86vh; overflow-y: auto; }
.modal-box .modal-close { float: right; background: none; border: none; font-size: 1.3rem; cursor: pointer; color: rgb(var(--ink-faint)); }
.ai-msg { padding: 12px 16px; border-radius: 12px; background: rgb(var(--surface-2)); margin-bottom: 12px; font-size: .9rem; }
.ai-msg.from-user { background: rgba(var(--accent),.12); margin-left: 20%; }

/* ---- Timeline (roadmap-style) ---- */
.timeline { position: relative; padding-left: 30px; border-left: 2px solid rgb(var(--border)); }
.timeline-item { position: relative; padding-bottom: 34px; }
.timeline-item::before { content: ""; position: absolute; left: -37px; top: 4px; width: 12px; height: 12px; border-radius: 50%; background: rgb(var(--accent)); border: 3px solid rgb(var(--bg)); }

/* ---- Utilities ---- */
.mt-0 { margin-top: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }
.text-faint { color: rgb(var(--ink-faint)); }
.flex { display: flex; }
.gap-3 { gap: 12px; }
.items-center { align-items: center; }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: start; }
/* ponytail: the form column is short and the contact column is long — stick it instead of leaving a screen of dead space */
@media (min-width: 861px) { .two-col > :first-child { position: sticky; top: 96px; } }

/* ---- Responsive ---- */
@media (max-width: 980px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2,1fr); }
  .hero-grid { grid-template-columns: 1fr; gap: 36px; }
  .hero h1 { max-width: none; }
  .footer-grid { grid-template-columns: repeat(3,1fr); }
  .filter-layout { grid-template-columns: 1fr; }
  .two-col { grid-template-columns: 1fr; }
  .stat-grid { grid-template-columns: repeat(2,1fr); }
}
@media (max-width: 900px) {
  /* Below this the 5-item bar starts crowding the logo + Book Demo button. */
  .primary-nav { display: none; }
  .nav-toggle { display: flex; }
  /* The toggle already flipped this class; it just had no styles, so the
     mobile menu never opened. Full-width drop-down panel under the header. */
  .primary-nav.mobile-open {
    display: block; position: absolute; left: 0; right: 0; top: 100%;
    background: rgb(var(--surface)); border-bottom: 1px solid rgb(var(--border));
    box-shadow: var(--shadow-lg); padding: 10px 24px 18px; max-height: 78vh; overflow-y: auto;
  }
  .primary-nav.mobile-open > li > a,
  .primary-nav.mobile-open > li > button { width: 100%; padding: 14px 4px; font-size: 1rem; }
  /* Sub-menus stack inline on mobile — hover has no meaning on touch. */
  .primary-nav.mobile-open .mega {
    position: static; display: grid; grid-template-columns: 1fr; min-width: 0;
    border: 0; box-shadow: none; background: none; padding: 0 0 10px 4px; gap: 14px;
  }
  .primary-nav.mobile-open .mega h4 { margin-bottom: 6px; }
}
@media (max-width: 720px) {
  .grid-1, .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: repeat(2,1fr); }
  .hero { padding: 60px 0; }
  .cta-banner { padding: 34px; flex-direction: column; align-items: flex-start; }
}

/* ==========================================================================
   Round 2 additions: social, FAB, sticky mobile CTA, OTP gate, gallery,
   testimonials, map, FAQ-everywhere
   ========================================================================== */

/* ---- Footer social row ---- */
.social-row { display: flex; gap: 10px; margin-top: 16px; }
.social-row a {
  width: 36px; height: 36px; border-radius: 999px; background: rgba(255,255,255,.08);
  border: 1px solid rgba(255,255,255,.14); display: flex; align-items: center; justify-content: center; color: #fff;
}
.social-row a:hover { background: rgb(var(--accent)); border-color: rgb(var(--accent)); }

/* ---- Floating action buttons ---- */
.fab-stack { position: fixed; right: 22px; bottom: 96px; z-index: 250; display: flex; flex-direction: column; gap: 12px; align-items: flex-end; }
.fab {
  width: 56px; height: 56px; border-radius: 999px; display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-lg); cursor: pointer; border: none; color: #fff; position: relative;
}
.fab-whatsapp { background: #25D366; }
.fab-chat { background: rgb(var(--accent)); }
.fab-tooltip {
  position: absolute; right: 66px; top: 50%; transform: translateY(-50%); background: rgb(var(--ink)); color: rgb(var(--bg));
  padding: 6px 12px; border-radius: 8px; font-size: .78rem; font-weight: 700; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.fab:hover .fab-tooltip { opacity: 1; }

/* ---- Sticky mobile CTA bar ---- */
.mobile-cta-bar { display: none; }
@media (max-width: 720px) {
  .mobile-cta-bar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 240;
    background: rgb(var(--surface)); border-top: 1px solid rgb(var(--border)); box-shadow: 0 -6px 20px rgba(0,0,0,.08);
  }
  .mobile-cta-bar a { flex: 1; text-align: center; padding: 14px 6px; font-weight: 700; font-size: .82rem; border-right: 1px solid rgb(var(--border)); display: flex; flex-direction: column; align-items: center; gap: 3px; }
  .mobile-cta-bar a:last-child { border-right: none; }
  /* iPhones reserve space at the bottom for the home indicator. Without this the
     last row of the bar sits under it and the tap targets are partly unreachable. */
  .mobile-cta-bar { padding-bottom: env(safe-area-inset-bottom, 0px); }
  /* The bar measures ~66px (14px padding x2 + icon + label + border), not 62 —
     the old value left the foot of every page tucked underneath it. */
  body { padding-bottom: calc(70px + env(safe-area-inset-bottom, 0px)); }
  /* Clear the bar, and keep clearing it on handsets with a home indicator. */
  .fab-stack { bottom: calc(84px + env(safe-area-inset-bottom, 0px)); right: 16px; }
  /* WhatsApp is already the middle button of the sticky bar. Two of them stacked
     one above the other cost a second floating circle over the body copy for no
     extra reach, so on mobile only the chat FAB survives. */
  .fab-whatsapp { display: none; }
}

/* ---- OTP / verification modal ---- */
.verify-step { display: none; }
.verify-step.active { display: block; }
.otp-row { display: flex; gap: 10px; margin: 18px 0; }
.otp-row input {
  width: 46px; height: 54px; text-align: center; font-size: 1.3rem; font-weight: 700; border-radius: 10px;
  border: 1px solid rgb(var(--border)); background: rgb(var(--surface-2)); color: rgb(var(--ink));
}
.verify-channel { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-radius: 10px; background: rgb(var(--surface-2)); margin-bottom: 10px; font-size: .87rem; }
.verify-channel svg { color: rgb(var(--success)); flex-shrink: 0; }
.progress-dots { display: flex; gap: 6px; margin-bottom: 20px; }
.progress-dots span { height: 4px; flex: 1; border-radius: 4px; background: rgb(var(--border)); }
.progress-dots span.done { background: rgb(var(--accent)); }

/* ---- Installation gallery ---- */
.gallery-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.gallery-grid .media-ph { aspect-ratio: 1; }
@media (max-width: 720px) { .gallery-grid { grid-template-columns: repeat(2,1fr); } }

/* ---- Testimonials ---- */
.testimonial-card { background: rgb(var(--surface)); border: 1px solid rgb(var(--border)); border-radius: var(--radius-md); padding: 26px; }
.testimonial-card .stars { color: rgb(var(--cta)); font-size: .9rem; margin-bottom: 10px; letter-spacing: 2px; }
.testimonial-card p { font-style: italic; }
.testimonial-card .who { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.testimonial-card .who .avatar { width: 40px; height: 40px; border-radius: 999px; background: rgba(var(--accent),.14); color: rgb(var(--accent)); display: flex; align-items: center; justify-content: center; font-weight: 800; }
.sample-tag { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: rgb(var(--ink-faint)); font-weight: 700; }

/* ---- Map ---- */
.map-wrap { border-radius: var(--radius-md); overflow: hidden; border: 1px solid rgb(var(--border)); }
.map-wrap iframe { width: 100%; height: 340px; border: 0; display: block; }

/* ---- Content roadmap callout ---- */
.roadmap-note { border: 1px dashed rgba(var(--accent),.4); border-radius: var(--radius-md); padding: 20px 22px; background: rgba(var(--accent),.05); font-size: .87rem; }


/* ---- Logo image (replacing placeholder mark) ---- */
/* The badge logo is portrait (186×240), not the old wide wordmark, so it needs
   more height than 38px for the XTROWS lettering inside it to read. */
.logo-img { height: 54px; width: auto; display: block; }
.logo-img-footer { height: 62px; filter: brightness(1.35) saturate(1.1); }

/* ---- Home slideshow ------------------------------------------------------
   Native CSS scroll-snap: swipe on touch, scroll/keyboard on desktop, works
   with JS off. No autoplay by design — an auto-advancing carousel needs a
   pause control to meet WCAG 2.2.2, and nobody reads slide 4 anyway.
   -------------------------------------------------------------------------- */
.carousel {
  display: flex; gap: 24px; overflow-x: auto; overscroll-behavior-x: contain;
  /* `proximity`, not `mandatory`: mandatory snaps the track back whenever a
     programmatic scroll lands off a snap point, which makes the arrows look dead. */
  scroll-snap-type: x proximity; scroll-behavior: smooth;
  padding: 4px 4px 12px; margin: 0 -4px; scrollbar-width: thin;
}
.carousel:focus-visible { outline: 2px solid rgb(var(--accent)); outline-offset: 4px; border-radius: var(--radius-md); }
.carousel-slide {
  flex: 0 0 calc((100% - 48px) / 3); scroll-snap-align: start; min-width: 0;
}
.carousel-slide .media-ph { margin-bottom: 16px; }
.carousel-slide h3 { margin: 0 0 8px; }
.carousel-slide p { margin: 0 0 12px; }
.slide-link { font-weight: 700; color: rgb(var(--accent)); text-decoration: none; }
.slide-link:hover { text-decoration: underline; }

.carousel-nav { display: flex; gap: 8px; }
.carousel-btn {
  width: 40px; height: 40px; border-radius: 50%; cursor: pointer; font-size: 1rem; line-height: 1;
  border: 1px solid rgba(var(--accent), .4); background: rgb(var(--surface)); color: rgb(var(--accent));
}
.carousel-btn:hover:not([disabled]) { background: rgba(var(--accent), .1); }
.carousel-btn[disabled] { opacity: .35; cursor: default; }

.carousel-dots { display: flex; gap: 8px; justify-content: center; margin-top: 18px; }
.carousel-dot {
  width: 9px; height: 9px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 0; background: rgba(var(--accent), .28);
}
.carousel-dot[aria-current="true"] { background: rgb(var(--accent)); transform: scale(1.35); }

@media (max-width: 900px) { .carousel-slide { flex-basis: calc((100% - 24px) / 2); } }
@media (max-width: 620px) { .carousel-slide { flex-basis: 100%; } }
@media (prefers-reduced-motion: reduce) { .carousel { scroll-behavior: auto; } }

/* Video lightbox — play badge over a still, 16:9 frame in the shared modal shell. */
.media-ph.has-video { position: relative; }
.video-play {
  position: absolute; inset: 0; margin: auto;
  width: 62px; height: 62px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: none; cursor: pointer; color: #fff;
  background: rgba(var(--cta), .92); box-shadow: var(--shadow-lg);
  transition: transform .18s ease, background .18s ease;
}
.video-play svg { margin-left: 3px; }
.video-play:hover, .video-play:focus-visible { transform: scale(1.08); background: rgb(var(--cta)); }
.video-modal .video-box { max-width: 900px; padding: 14px; }
.video-frame { position: relative; padding-top: 56.25%; margin-top: 8px; }
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; border-radius: var(--radius); }
.media-ph.is-video iframe { width: 100%; aspect-ratio: 16 / 9; border: 0; border-radius: var(--radius); }
@media (prefers-reduced-motion: reduce) { .video-play { transition: none; } }

/* Scroll reveal — only active once main.js has set .js-reveal, so content is never
   left invisible if the script fails to load. */
.js-reveal [data-reveal] {
  opacity: 0;
  transition: opacity .62s cubic-bezier(.22,.61,.36,1), transform .62s cubic-bezier(.22,.61,.36,1);
  will-change: opacity, transform;
}
.js-reveal [data-reveal="left"]  { transform: translate3d(-34px, 0, 0); }
.js-reveal [data-reveal="right"] { transform: translate3d(34px, 0, 0); }
.js-reveal [data-reveal="up"]    { transform: translate3d(0, 22px, 0); }
.js-reveal [data-reveal].is-revealed { opacity: 1; transform: none; will-change: auto; }
@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* Template-rendered photo gallery (xtrows_gallery_for). Deliberately not styled off
   .wp-block-gallery: that selector is in a specificity fight with core's block CSS,
   and this markup never passes through the block renderer. */
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; margin-top: 22px; }
.photo-grid-item { margin: 0; border-radius: 12px; overflow: hidden; background: rgba(var(--ink), .05); aspect-ratio: 3 / 4; }
.photo-grid-item img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .45s cubic-bezier(.22,.61,.36,1); }
.photo-grid-item:hover img { transform: scale(1.045); }
@media (max-width: 600px) { .photo-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; } }

/* Site footage tiles. Same rounded frame as the photo gallery; letterboxed because the
   clips are portrait phone footage and the grid cell is landscape. */
.install-video {
  width: 100%; aspect-ratio: 4 / 3; border-radius: var(--radius-lg);
  background: #0a0e17; object-fit: contain; display: block;
}

/* ---- Installation gallery ----
   Uniform 4:3 tiles cropped to a common shape, so a mix of portrait phone shots and
   landscape site photos reads as one grid instead of a ragged wall. Tiles past the
   twelfth are hidden until the visitor asks for them. */
/* Five across on desktop so the first fifteen form a clean 5x3 block. Steps down in
   whole columns rather than auto-filling, which is what kept the last row ragged. */
.install-gallery { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; }
@media (max-width: 1180px) { .install-gallery { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 900px)  { .install-gallery { grid-template-columns: repeat(3, 1fr); } }
.install-tile {
  padding: 0; border: 0; margin: 0; background: rgb(var(--surface-2));
  border-radius: var(--radius-md); overflow: hidden; aspect-ratio: 4 / 3;
  cursor: zoom-in; position: relative; display: block;
}
.install-tile img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .45s ease; }
.install-tile:hover img, .install-tile:focus-visible img { transform: scale(1.06); }
.install-tile:focus-visible { outline: 2px solid rgb(var(--cta)); outline-offset: 2px; }
.install-tile.is-extra { display: none; }
.install-gallery.is-open .install-tile.is-extra { display: block; }
.gallery-more { text-align: center; margin-top: 22px; }
@media (max-width: 560px) { .install-gallery { grid-template-columns: repeat(2, 1fr); gap: 8px; } }

/* Lightbox. Built once on first open and reused; the image is swapped rather than
   the node rebuilt, so paging through thirty photographs allocates nothing extra. */
.gallery-lightbox {
  position: fixed; inset: 0; background: rgba(5, 8, 15, .93); z-index: 400;
  display: none; align-items: center; justify-content: center; padding: 28px;
}
.gallery-lightbox.open { display: flex; }
.gallery-lightbox img { max-width: 92vw; max-height: 84vh; border-radius: var(--radius-md); display: block; }
.gallery-lightbox .lb-close, .gallery-lightbox .lb-prev, .gallery-lightbox .lb-next {
  position: absolute; background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.22);
  color: #fff; width: 44px; height: 44px; border-radius: 999px; cursor: pointer;
  font-size: 1.2rem; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.gallery-lightbox .lb-close:hover, .gallery-lightbox .lb-prev:hover, .gallery-lightbox .lb-next:hover { background: rgba(255,255,255,.24); }
.gallery-lightbox .lb-close { top: 20px; right: 20px; }
.gallery-lightbox .lb-prev { left: 18px; top: 50%; transform: translateY(-50%); }
.gallery-lightbox .lb-next { right: 18px; top: 50%; transform: translateY(-50%); }
.gallery-lightbox .lb-count { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); color: rgba(255,255,255,.75); font-size: .82rem; }
@media (max-width: 560px) {
  .gallery-lightbox .lb-prev { left: 8px; } .gallery-lightbox .lb-next { right: 8px; }
}
