/* ============ Design tokens ============ */
:root{
  --red-900:#7a0e0e;
  --red-700:#a91b1b;
  --red-600:#c81e1e;
  --red-500:#e2402c;
  --amber-500:#f5a524;
  --ink-900:#14161a;
  --ink-800:#1e2126;
  --ink-600:#4b5158;
  --ink-400:#7a828c;
  --line:#e6e4e1;
  --paper:#ffffff;
  --tint:#f7f5f2;
  --dark:#101215;
  --radius:14px;
  --shadow-sm:0 2px 8px rgba(20,18,16,.06);
  --shadow-md:0 12px 32px rgba(20,18,16,.10);
  --ff-display:'Cormorant Garamond',serif;
  --ff-body:'Inter',system-ui,sans-serif;
}

/* ============ Self-hosted fonts (static/font/) ============
   Replaces the Google Fonts CDN (Poppins + Inter) previously linked in
   every page <head> — Cormorant Garamond is a self-hosted display serif
   used for headings/company name/etc via --ff-display; Inter (18pt optical
   size, tuned for small/body text rather than the 24pt/28pt display-size
   instances also present in static/font/) replaces the CDN-loaded Inter via
   --ff-body. Only the weights actually referenced in this stylesheet are
   loaded (400/500/600/700 display, 400/500/600/700/800 body) — no italics,
   since none are used anywhere in the site. */
@font-face{
  font-family:'Cormorant Garamond';
  src:url(../font/CormorantGaramond-Regular.ttf) format('truetype');
  font-weight:400;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Cormorant Garamond';
  src:url(../font/CormorantGaramond-Medium.ttf) format('truetype');
  font-weight:500;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Cormorant Garamond';
  src:url(../font/CormorantGaramond-SemiBold.ttf) format('truetype');
  font-weight:600;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Cormorant Garamond';
  src:url(../font/CormorantGaramond-Bold.ttf) format('truetype');
  font-weight:700;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url(../font/Inter_18pt-Regular.ttf) format('truetype');
  font-weight:400;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url(../font/Inter_18pt-Medium.ttf) format('truetype');
  font-weight:500;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url(../font/Inter_18pt-SemiBold.ttf) format('truetype');
  font-weight:600;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url(../font/Inter_18pt-Bold.ttf) format('truetype');
  font-weight:700;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url(../font/Inter_18pt-ExtraBold.ttf) format('truetype');
  font-weight:800;font-style:normal;font-display:swap;
}

/* ============ Page transitions ============
   Cross-page fade, done in plain JS/CSS rather than the native
   cross-document View Transitions API — that API is Chromium-only (no
   Firefox/Safari) and, even in Chromium, didn't reliably fire in testing.
   Scoped to <main> only (not the whole body) — the topbar/header/footer
   are chrome that should stay put through a navigation, only the content
   in between fades. `.js` is flipped onto <html> by a tiny blocking inline
   script in <head> (see base.html / adminhub/base.html / adminhub/login.html)
   before first paint, so JS-disabled visitors never see a hidden <main>.
   static/js/page-transitions.js (loaded on every page, public + Admin Hub)
   fades <main> in on load and fades it out before any same-origin link
   navigation. */
.js main{opacity:0;}
main{transition:opacity .25s ease;}
.js main.content-visible{opacity:1;}
.js main.content-leaving{opacity:0;}
@media (prefers-reduced-motion:reduce){
  main{transition:none;}
}

*,*::before,*::after{box-sizing:border-box;}
html{scroll-behavior:smooth;overflow-x:hidden;}
body{
  margin:0;
  font-family:var(--ff-body);
  color:var(--ink-800);
  background:var(--paper);
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  min-height:100vh;
  display:flex;
  flex-direction:column;
}
main{flex:1 0 auto;}
img{max-width:100%;display:block;}
a{color:inherit;text-decoration:none;}
ul{list-style:none;margin:0;padding:0;}
h1,h2,h3,h4{font-family:var(--ff-display);line-height:1.2;margin:0 0 .5em;color:var(--ink-900);}
p{margin:0 0 1em;}
.container{width:100%;max-width:1180px;margin:0 auto;padding:0 24px;}
.icon{width:18px;height:18px;fill:currentColor;flex-shrink:0;}

/* ============ Buttons ============ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:12px 22px;border-radius:999px;font-weight:600;font-size:.95rem;
  border:2px solid transparent;cursor:pointer;transition:transform .15s ease, box-shadow .15s ease, background .15s ease, color .15s ease;
  white-space:nowrap;
}
.btn--primary{background:var(--red-600);color:#fff;}
.btn--primary:hover{background:var(--red-700);transform:translateY(-2px);box-shadow:var(--shadow-md);}
.btn--outline{border-color:var(--ink-800);color:var(--ink-800);}
.btn--outline:hover{background:var(--ink-800);color:#fff;}
/* White fill, red border+text — used by "Forgot Password?" on the Admin Hub
   login card, sized/shaped like the primary Log In button above it rather
   than reading as a plain inline text link. */
.btn--outline-red{background:#fff;border-color:var(--red-600);color:var(--red-600);}
.btn--outline-red:hover{background:var(--red-600);color:#fff;}
.btn--outline-red:disabled{background:#fff;border-color:var(--ink-400);color:var(--ink-400);cursor:not-allowed;}
.btn--ghost{border-color:rgba(255,255,255,.6);color:#fff;}
.btn--ghost:hover{background:rgba(255,255,255,.12);}
.btn--lg{padding:15px 30px;font-size:1rem;}
.btn--block{width:100%;}
.btn--icon{width:44px;height:44px;padding:0;border-radius:10px;}
.btn--icon .icon{width:17px;height:17px;fill:none;}

/* ============ Site header (topbar + header, both always on screen) ============ */
/* position:fixed, not sticky — html/body's overflow-x:hidden (load-bearing, see below)
   makes browsers compute overflow-y:auto on both, which breaks position:sticky for descendants. */
.site-header{position:fixed;top:0;left:0;width:100%;z-index:100;}

/* ============ Top bar ============ */
.topbar{
  background:var(--ink-900);color:#cfd3d8;font-size:.82rem;
  max-height:60px;overflow:hidden;
  transition:max-height .25s ease, opacity .2s ease;
}
.topbar__inner{display:flex;justify-content:space-between;align-items:center;padding:8px 24px;flex-wrap:wrap;gap:6px;}
.topbar__contact{display:flex;gap:20px;flex-wrap:wrap;}
.topbar__contact a{display:flex;align-items:center;gap:6px;opacity:.9;}
.topbar__contact a:hover{opacity:1;color:var(--amber-500);}
.topbar__contact .icon{width:14px;height:14px;}
.topbar__social{display:flex;align-items:center;gap:20px;}
/* padding+negative-margin grows the tappable area to ~44x44 without
   changing the icon's visual size/position or affecting sibling layout
   (the margin box — what governs flex sizing/spacing — nets back to the
   original 24x24 footprint since the negative margin exactly cancels the
   added padding). */
.topbar__social a{display:flex;opacity:.9;transition:opacity .15s ease;padding:10px;margin:-10px;}
.topbar__social a:hover{opacity:1;}
.topbar__social .icon{width:24px;height:24px;}
a.instagram-icon{color:var(--red-600);}
a.instagram-icon:hover{color:var(--red-700);}
a.whatsapp-icon{color:#25d366;}
a.whatsapp-icon:hover{color:#1ebe5b;}
.site-header.is-scrolled .topbar{max-height:0;opacity:0;}

/* ============ Header ============ */
.header{
  background:rgba(255,255,255,.92);
  backdrop-filter:blur(8px);
  border-bottom:1px solid var(--line);
}
.header__inner{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;gap:20px;transition:padding .25s ease;}
.site-header.is-scrolled .header__inner{padding:8px 16px;}
.logo{display:flex;align-items:center;gap:10px;}
.logo__img{height:42px;width:auto;display:block;transition:height .25s ease;}
.logo--footer .logo__img{height:44px;}
.site-header.is-scrolled .logo__img{height:32px;}

.nav{display:flex;gap:20px;margin-left:32px;}
.nav a{font-weight:500;font-size:.95rem;color:var(--ink-600);position:relative;padding:4px 0;}
.nav a:hover{color:var(--red-600);}
.nav a::after{content:'';position:absolute;left:0;bottom:-2px;width:0;height:2px;background:var(--red-600);transition:width .2s ease;}
.nav a:hover::after{width:100%;}
.nav a.active{color:var(--red-600);text-shadow:0 0 10px rgba(200,30,30,.55);}
.nav a.active::after{width:100%;}

.header__actions{display:flex;align-items:center;gap:12px;}
/* Drawer-only "Get Brochure", below the link list — hidden here (desktop,
   where .nav is just an inline row of links); shown at the drawer
   breakpoint below. */
.nav__brochure-link{display:none;}
/* Logo shown above the link list inside the mobile drawer — a normal-flow
   .nav child, so it's positioned automatically without fighting the fixed
   header's own stacking (see the drawer media query below for why it's
   hidden on desktop, where .nav is just an inline row of links). */
.nav__brand{display:none;}
/* Box grown to a ~44x44 touch target (from an effective ~34x28px);
   flex-centering keeps the 3 visual bars (22x2px, unchanged) centered in
   the larger box instead of just padding out unevenly. */
.nav__toggle{display:none;flex-direction:column;align-items:center;justify-content:center;gap:5px;background:none;border:none;cursor:pointer;width:44px;height:44px;padding:0;}
.nav__toggle span{width:22px;height:2px;background:var(--ink-900);border-radius:2px;transition:transform .25s ease, opacity .2s ease;}
/* Hamburger-to-X: bars 1/3 rotate into an X, middle bar fades out — the
   visible "cancel mark" confirming the drawer is open and this same
   button closes it. .open is toggled by static/js/site.js alongside
   .nav/.nav-overlay's own .open class. 7px = span height (2px) + gap
   (5px), so the top/bottom bars land exactly on the middle bar's line. */
.nav__toggle.open span:nth-child(1){transform:translateY(7px) rotate(45deg);}
.nav__toggle.open span:nth-child(2){opacity:0;}
.nav__toggle.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg);}

/* ============ Hero (brand strip + intro share one bg image) ============ */
.hero{position:relative;color:#fff;padding-top:10px;overflow:hidden;}
.hero__bg{position:absolute;inset:0;}
/* Background slideshow: stacked layers crossfaded by site.js on a 5s
   interval (see the hero background slideshow block there). Each layer's
   image is DB-driven (HeroSlide), set as an inline background-image style
   by includes/hero-bg-slideshow.html rather than a fixed --N rule here. */
.hero__bg-slide{
  position:absolute;inset:0;background-position:center;background-size:cover;
  background-repeat:no-repeat;opacity:0;transition:opacity 1.2s ease;
}
.hero__bg-slide.is-active{opacity:1;}
.hero__overlay{
  position:absolute;inset:0;
  background:linear-gradient(120deg, rgba(16,18,21,.92) 30%, rgba(122,14,14,.75));
}
/* Brand strip is the hero's own first flow child now (not a separate
   section with its own white background) — sits directly on the shared bg
   image/overlay, so it needs the same relative+z-index treatment as
   .hero__content to render above them, and light-colored logo/text. */
.hero-brand{position:relative;z-index:2;padding:36px 0 16px;}
.hero-brand__inner{display:flex;align-items:center;justify-content:center;gap:22px;}
.hero-brand__logo .logo__img{height:76px;width:auto;display:block;}
.hero-brand__text{display:flex;flex-direction:column;gap:6px;}
.hero-brand__name{font-family:var(--ff-display);font-weight:700;font-size:2rem;color:#fff;line-height:1.1;}
.hero-brand__tagline{
  text-transform:uppercase;letter-spacing:.1em;font-size:.9rem;font-weight:700;color:var(--amber-500);
}
.hero__content{position:relative;z-index:2;padding:24px 24px 120px;max-width:950px;}
.eyebrow{
  display:inline-block;text-transform:uppercase;letter-spacing:.12em;font-size:.78rem;font-weight:700;
  color:var(--red-600);margin-bottom:14px;
}
.eyebrow--light{color:var(--amber-500);}
.hero h1{font-size:clamp(2.1rem,5vw,3.4rem);color:#fff;text-align:center;}
.hero__lead{
  text-transform:uppercase;letter-spacing:.1em;font-size:.9rem;font-weight:700;color:var(--amber-500);
  max-width:720px;margin-left:auto;margin-right:auto;text-align:center;
}
.hero__cta{display:flex;gap:16px;flex-wrap:wrap;margin-top:28px;justify-content:center;}
/* Hidden on desktop (its own Free Consultation button already lives in the
   header) — shown only at the 600px breakpoint below, the same one that
   turns .hero__cta into a column, so this slots in as a natural third
   stacked button rather than needing its own separate placement logic. */
.hero__cta-mobile-only{display:none;}

.stats{position:relative;z-index:2;background:var(--ink-900);border-top:1px solid rgba(255,255,255,.08);}
.stats__grid{display:grid;grid-template-columns:repeat(4,1fr);padding:26px 24px;}
.stats__grid--2{grid-template-columns:repeat(2,1fr);max-width:420px;margin:0 auto;}
.stats__grid--3{grid-template-columns:repeat(3,1fr);max-width:620px;margin:0 auto;}
.stat{text-align:center;color:#fff;border-right:1px solid rgba(255,255,255,.1);}
.stat:last-child{border-right:none;}
.stat__num,.stat__suffix{font-family:var(--ff-body);font-size:2rem;font-weight:700;color:var(--amber-500);}
.stat__label{display:block;font-size:.82rem;color:#c7cad0;margin-top:4px;}

/* ============ Sections ============ */
.section{padding:90px 0;}
.section--tint{background:var(--tint);}
.section--dark{background:var(--dark);}
.section--flush-bottom{padding-bottom:0;}
.text-light{color:#fff;}
.section__sub--light{color:#c7cad0;}
.section__head{max-width:640px;margin:0 auto 50px;text-align:center;}
.section__head .eyebrow{display:block;}
.section__sub{color:var(--ink-600);}

/* ============ About intro (team photo backdrop) ============ */
.about-intro{position:relative;overflow:hidden;}
/* Temporary stand-in background until the client provides a real team photo */
.about-intro__bg{position:absolute;inset:0;z-index:0;background:url(../image/brochure-equipment-lineup.jpg) center/cover no-repeat;}
.about-intro__overlay{position:absolute;inset:0;z-index:0;background:linear-gradient(180deg, rgba(16,18,21,.6) 0%, rgba(16,18,21,.78) 55%, rgba(16,18,21,.94) 100%);}
.about-intro__content{position:relative;z-index:1;margin-bottom:140px;}
.about-intro__content .eyebrow{color:var(--amber-500);}
.about-intro__content h1{color:#fff;}
.about-intro__content .section__sub{color:#e7e5e2;}

/* ============ Consultation intro (photo backdrop) ============ */
.consultation-intro{position:relative;overflow:hidden;min-height:382px;display:flex;align-items:center;}
.consultation-intro__bg{position:absolute;inset:0;z-index:0;background:url(../image/consultation-intro-bg.jpg) center/cover no-repeat;}
.consultation-intro__overlay{position:absolute;inset:0;z-index:0;background:linear-gradient(180deg, rgba(16,18,21,.6) 0%, rgba(16,18,21,.78) 55%, rgba(16,18,21,.94) 100%);}
.consultation-intro__content{position:relative;z-index:1;margin:0 auto;}
.consultation-intro__content h1{color:#fff;}
.consultation-intro__content .section__sub{color:#e7e5e2;}

/* ============ About ============ */
.about{display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:center;}
.about__media{position:relative;}
.about__img{aspect-ratio:1200/551;width:100%;object-fit:cover;border-radius:var(--radius);display:block;box-shadow:var(--shadow-md);}
.about__badge{
  position:absolute;top:-24px;left:-24px;background:var(--ink-900);color:var(--amber-500);
  border-radius:var(--radius);padding:20px 40px;box-shadow:var(--shadow-md);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  line-height:1.1;min-width:160px;
  animation:badge-float-x 2.8s ease-in-out infinite;
}
@keyframes badge-float-x{
  0%,100%{transform:translateX(0);}
  50%{transform:translateX(35px);}
}
.about__badge strong{font-family:var(--ff-body);font-size:2.2rem;color:var(--amber-500);}
.about__badge span{font-size:.85rem;opacity:.9;}
.about__pic{
  position:absolute;width:200px;aspect-ratio:4/3;object-fit:cover;
  border-radius:var(--radius);border:4px solid #fff;box-shadow:var(--shadow-md);
}
.about__pic--tl{top:-24px;left:-24px;}
.about__pic--br{bottom:-24px;right:-24px;}
.about__media .map{margin-top:40px;}
.check-list li{position:relative;padding-left:28px;margin-bottom:10px;color:var(--ink-600);}
.section--dark .check-list li,
.fire-risk .check-list li{color:#c7cad0;}
.check-list li::before{
  content:'';position:absolute;left:0;top:6px;width:16px;height:16px;border-radius:50%;
  background:var(--red-600);
}
.check-list li::after{
  content:'';position:absolute;left:5px;top:8px;width:6px;height:3px;border-left:2px solid #fff;border-bottom:2px solid #fff;transform:rotate(-45deg);
}
.check-list--columns{columns:2;column-gap:40px;}
/* Prevents a single <li> splitting across the two columns at tablet
   widths (~861-1024px, where the 2-column layout is active); scoped to
   the --columns variant only, not the base .check-list used elsewhere
   in a single column where this can't happen anyway. */
.check-list--columns li{break-inside:avoid;-webkit-column-break-inside:avoid;}

/* ============ Mission & Vision ============ */
/* Reuses includes/hero-bg-slideshow.html verbatim (same .hero__bg/
   .hero__bg-slide classes, same DB-driven HeroSlide photos as the Home
   hero) — this section just needs position:relative to anchor it and its
   own dark overlay +
   z-index:2 content wrapper, mirroring .hero's own working stacking order
   (bg, then overlay, both z-index:auto painted in DOM order; content
   explicitly raised above both) rather than inventing a new pattern. */
.mission-vision{position:relative;overflow:hidden;}
.mission-vision__overlay{
  position:absolute;inset:0;
  background:linear-gradient(180deg, rgba(16,18,21,.6) 0%, rgba(16,18,21,.78) 55%, rgba(16,18,21,.94) 100%);
}
.mission-vision__content{position:relative;z-index:2;}

/* Same pattern as .mission-vision — bg slideshow behind the Services
   page's 8 cards. border-top divides it from the Brands section above. */
.services-cards{position:relative;overflow:hidden;border-top:4px solid #000;}
.services-cards__overlay{
  position:absolute;inset:0;
  background:linear-gradient(180deg, rgba(16,18,21,.6) 0%, rgba(16,18,21,.78) 55%, rgba(16,18,21,.94) 100%);
}
.services-cards__content{position:relative;z-index:2;}

/* Same pattern as .mission-vision/.services-cards — bg slideshow behind the
   Services page's Fire Risk Assessment section. */
.fire-risk{position:relative;overflow:hidden;}
.fire-risk__overlay{
  position:absolute;inset:0;
  /* Darker than .mission-vision__overlay/.services-cards__overlay's shared
     .6/.78/.94 gradient — this section's slide photos are busier (people,
     vehicles) right behind the paragraph text, which wasn't legible at the
     lighter opacity. */
  background:linear-gradient(180deg, rgba(16,18,21,.82) 0%, rgba(16,18,21,.9) 55%, rgba(16,18,21,.96) 100%);
}
.fire-risk__content{position:relative;z-index:2;}
.fire-risk__content .section__sub{color:#fff;text-shadow:0 1px 4px rgba(0,0,0,.7);}

/* ============ Services grid ============ */
.grid{display:grid;gap:26px;}
.grid--services{grid-template-columns:repeat(3,1fr);gap:32px;}
.grid--mission{grid-template-columns:repeat(2,1fr);}
.card{
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:30px 24px;
  transition:transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
/* Public Services cards are rendered as <a> so the whole card is clickable
   (Admin Hub's version of .card stays a <div>, unaffected) — resets anchor
   defaults so it still looks/behaves exactly like the div-based .card. */
.card-link{display:block;color:inherit;text-decoration:none;}
.card:hover{transform:translateY(-6px);box-shadow:var(--shadow-md);border-color:transparent;}
/* Black cards matching .grid--services .card exactly — same gradient-wipe
   hover, just with a check-list (not present on Services cards) also
   getting the white/black text-color flip for consistency. */
.grid--mission .card{
  background:#000;border-color:#000;position:relative;overflow:hidden;z-index:0;
}
.grid--mission .card::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to bottom, var(--red-600), var(--amber-500));
  transform:scaleY(0);transform-origin:bottom;
  transition:transform .25s ease;
}
.grid--mission .card:hover::before{transform:scaleY(1);}
.grid--mission .card h3{color:var(--amber-500);transition:color .25s ease;}
.grid--mission .card p{color:#fff;transition:color .25s ease;}
.grid--mission .card .check-list li{color:#fff;transition:color .25s ease;}
.grid--mission .card:hover h3,
.grid--mission .card:hover p,
.grid--mission .card:hover .check-list li{color:#000;}
.card__icon{
  width:52px;height:52px;border-radius:12px;margin-bottom:18px;
  background:linear-gradient(145deg,var(--red-600),var(--red-500));
  display:flex;align-items:center;justify-content:center;
}
.card__icon svg{width:26px;height:26px;fill:none;stroke:#fff;stroke-width:1.6;}
.card__icon img{width:26px;height:26px;object-fit:contain;}
.card .logo-card__admin-actions{margin-top:16px;}
.card h3{font-size:1.05rem;}
.card p{color:var(--ink-600);font-size:.92rem;margin:0;}
/* Services grid only (not Mission & Vision, which also reuses .card) —
   black card body with white text; .card__icon is left untouched so the
   red-gradient icon boxes stay exactly as they were. */
.grid--services .card{background:#000;border-color:#000;position:relative;overflow:hidden;z-index:0;padding:24px 20px;}
/* Same bottom-to-top gradient wipe as .why's hover effect — pseudo-element
   grown via scaleY from the bottom edge, sitting behind the card's own
   content via negative z-index inside the card's stacking context. */
.grid--services .card::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to bottom, var(--red-600), var(--amber-500));
  transform:scaleY(0);transform-origin:bottom;
  transition:transform .25s ease;
}
.grid--services .card:hover::before{transform:scaleY(1);}
.grid--services .card p{color:#fff;transition:color .25s ease;}
.grid--services .card:hover p{color:#000;}
/* Bleeds past .grid--services .card's own 24px/20px padding to sit flush
   with the card edges — .grid--services .card already has overflow:hidden
   (above), so the negative margin gets clipped to the card's own
   border-radius for free. Only rendered when the service has an icon image;
   cards without one keep the old text-only layout. */
.card__thumb{position:relative;margin:-24px -20px 18px;aspect-ratio:16/10;}
.card__thumb img{width:100%;height:100%;object-fit:cover;display:block;}
/* Ribbon tag between the photo and the card's own heading. Bleeds past
   .card's left padding with a negative margin (same bleed trick as
   .card__thumb above) so the left edge sits flush against .card's true
   border and reads as tucked behind/emerging from the card rather than a
   floating rectangle — .card's own overflow:hidden clips it clean. The
   right edge is cut into a V notch (two points) instead of a flat edge via
   clip-path, giving it a physical ribbon-tail look. */
.card__phase{
  display:inline-block;margin:-4px 0 16px -20px;
  background:var(--red-600);color:#fff;font-weight:800;font-size:.78rem;
  padding:7px 26px 7px 16px;letter-spacing:.03em;
  clip-path:polygon(0 0, 100% 0, calc(100% - 13px) 50%, 100% 100%, 0 100%);
  box-shadow:var(--shadow-md);
}

/* ============ Process ============ */
.process{display:grid;grid-template-columns:repeat(4,1fr);gap:26px;position:relative;}
.process__step{
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:28px 22px;position:relative;
}
.process__num{
  font-family:var(--ff-body);font-weight:800;font-size:2.2rem;color:transparent;
  -webkit-text-stroke:1.5px var(--red-600);margin-bottom:10px;
}
.process__step h3{font-size:1.05rem;}
.process__step p{color:var(--ink-600);font-size:.9rem;margin:0;}

/* ============ Process phases (timeline) ============ */
.phase-list{position:relative;display:flex;flex-direction:column;gap:48px;}
.phase-list::before{content:'';position:absolute;left:35px;top:8px;bottom:8px;width:3px;background:var(--line);}
.phase{display:grid;grid-template-columns:72px 1fr;gap:28px;position:relative;}
.phase__marker{
  position:relative;z-index:1;width:72px;height:72px;border-radius:50%;flex-shrink:0;
  background:linear-gradient(145deg,var(--red-600),var(--red-500));display:flex;align-items:center;justify-content:center;
  box-shadow:var(--shadow-md);
}
.phase__marker svg{width:30px;height:30px;fill:none;stroke:#fff;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;}
.phase__marker-num{
  position:absolute;bottom:-4px;right:-4px;width:26px;height:26px;border-radius:50%;
  background:var(--ink-900);color:#fff;font-family:var(--ff-body);font-weight:800;font-size:.68rem;
  display:flex;align-items:center;justify-content:center;border:3px solid var(--paper);
}
.phase__label{color:var(--red-600);font-weight:700;font-size:.8rem;text-transform:uppercase;letter-spacing:.06em;margin:0 0 4px;}
.phase__content h3{font-size:1.2rem;margin-bottom:4px;}
.phase__tagline{color:var(--ink-600);font-style:italic;font-size:.92rem;margin-bottom:18px;}
.phase__body{display:grid;grid-template-columns:1.2fr 1fr;gap:20px;margin-top:18px;}
/* width:100% + aspect-ratio gives the image a self-determined, definite
   height (based on its own width) instead of height:100% — percentage
   heights on a replaced element inside a grid row whose height is itself
   driven by the sibling .phase__cards column is a circular/indefinite-size
   case browsers resolve inconsistently, which was leaving the image
   shorter/narrower than its column and a visible gap before the cards.
   The cards column then stretches (grid's default align-items:stretch) to
   match the image's height, and .phase__card's flex:1 makes both cards
   grow to fill that height evenly, so there's no dead space either side. */
.phase__image{border-radius:var(--radius);width:100%;aspect-ratio:16/10;}
/* Not-yet-supplied phases use a plain <div> placeholder; real photos are
   <img> — tag-specific rules so both share sizing/radius but the
   placeholder keeps its dashed hint box while the photo fills/covers. */
div.phase__image{
  border:2px dashed var(--line);background:var(--tint);
  display:flex;align-items:center;justify-content:center;text-align:center;
  color:var(--ink-600);font-size:.85rem;padding:20px;
}
img.phase__image{object-fit:cover;display:block;box-shadow:var(--shadow-md);}
.phase__cards{display:flex;flex-direction:column;gap:16px;}
/* Same bottom-to-top gradient wipe as .why / .grid--services .card. */
.phase__card{
  flex:1;position:relative;background:#000;border-radius:var(--radius);padding:20px 22px;
  overflow:hidden;z-index:0;
  transition:transform .2s ease, box-shadow .2s ease;
}
.phase__card::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to bottom, var(--red-600), var(--amber-500));
  transform:scaleY(0);transform-origin:bottom;
  transition:transform .25s ease;
}
.phase__card:hover::before{transform:scaleY(1);}
.phase__card h4{color:var(--amber-500);font-size:.95rem;margin:0 0 6px;transition:color .25s ease;}
.phase__card p{color:#fff;font-size:.86rem;margin:0;transition:color .25s ease;}
.phase__card:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);}
.phase__card:hover h4{color:#000;}
.phase__card:hover p{color:#000;}

/* ============ Our Products (Services page) ============ */
.product-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:20px;}
.product-card{
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
  transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.product-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:var(--ink-400);}
.product-card__img{aspect-ratio:1/1;padding:14px;display:flex;align-items:center;justify-content:center;background:var(--tint);}
.product-card__img img{max-width:100%;max-height:100%;object-fit:contain;}
.product-card__caption{padding:12px 10px;text-align:center;font-size:.82rem;font-weight:600;color:var(--ink-800);border-top:1px solid var(--line);}

/* ============ Why choose us ============ */
.grid--why{grid-template-columns:repeat(3,1fr);}
.why{
  position:relative;padding:26px;border-radius:var(--radius);background:rgba(255,255,255,.04);
  border:1px solid rgba(255,255,255,.08);overflow:hidden;z-index:0;
  transition:border-color .35s ease, transform .2s ease, box-shadow .2s ease;
}
/* Hover wipe: a red-to-amber gradient grows in from the bottom edge up to
   the top, sitting behind the card's own content (negative z-index inside
   the card's own stacking context — see .why's position+z-index above) so
   text stays readable throughout the reveal. */
.why::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to bottom, var(--red-600), var(--amber-500));
  transform:scaleY(0);transform-origin:bottom;
  transition:transform .25s ease;
}
.why:hover::before{transform:scaleY(1);}
.why:hover{border-color:rgba(245,165,36,.4);transform:translateY(-4px);box-shadow:var(--shadow-md);}
.why__icon{
  font-family:var(--ff-body);font-weight:800;font-size:1.1rem;color:var(--amber-500);
  width:44px;height:44px;border-radius:50%;border:2px solid var(--amber-500);
  display:flex;align-items:center;justify-content:center;margin-bottom:16px;
  transition:color .25s ease, border-color .25s ease;
}
.why h3{color:#fff;font-size:1.05rem;transition:color .25s ease;}
.why p{color:#aab0b8;font-size:.9rem;margin:0;transition:color .25s ease;}
.why:hover .why__icon{color:#000;border-color:#000;}
.why:hover h3{color:#000;}
.why:hover p{color:#000;}
.why__cta{text-align:center;margin-top:40px;}

/* ============ Clientele intro backdrop ============ */
.clientele-intro{position:relative;overflow:hidden;min-height:382px;display:flex;align-items:center;}
.clientele-intro__bg{position:absolute;inset:0;z-index:0;background:url(../image/clientele-intro-bg.jpg) center/90% no-repeat;}
.clientele-intro__overlay{position:absolute;inset:0;z-index:0;background:linear-gradient(180deg, rgba(16,18,21,.6) 0%, rgba(16,18,21,.78) 55%, rgba(16,18,21,.94) 100%);}
.clientele-intro__content{position:relative;z-index:1;}
.clientele-intro__content .eyebrow{color:var(--amber-500);}
.clientele-intro__content h1,.clientele-intro__content h2{color:#fff;}
.clientele-intro__content .section__sub{color:#e7e5e2;}

.clientele-intro--no-bg{min-height:0;background:var(--tint);}
.clientele-intro--no-bg .clientele-intro__content .eyebrow{color:var(--red-600);}
.clientele-intro--no-bg .clientele-intro__content h2{color:var(--ink-900);}
.clientele-intro--no-bg .clientele-intro__content .section__sub{color:var(--ink-600);}

/* ============ Services intro backdrop ============ */
.services-intro{position:relative;overflow:hidden;min-height:382px;display:flex;align-items:center;}
.services-intro__bg{position:absolute;inset:0;z-index:0;background:url(../image/services-intro-bg.jpg) center/cover no-repeat;}
/* Same .services-intro banner as the main Services page; the placeholder
   gradient below is a fallback for any service page without its own photo
   class yet. */
.services-intro__bg--placeholder{background:linear-gradient(135deg, var(--dark), var(--ink-800));}
.services-intro__bg--fire-alarm-system{background:url(../image/service-fire-detection-system-bg.png) center/cover no-repeat;}
.services-intro__bg--gas-detection-system{background:url(../image/service-gas-detection-system-bg.png) center/cover no-repeat;}
.services-intro__bg--gas-suppression-system{background:url(../image/service-co2-gas-flooding-bg.png) center/cover no-repeat;}
.services-intro__bg--hvws-mvws-system{background:url(../image/service-hvws-mvws-system-bg.png) center/cover no-repeat;}
.services-intro__bg--fire-extinguishers{background:url(../image/service-fire-extinguishers-bg.png) center/cover no-repeat;}
.services-intro__bg--safety-equipment{background:url(../image/service-safety-equipment-bg.png) center/cover no-repeat;}
.services-intro__bg--fire-pump-house{background:url(../image/service-fire-pump-house-bg.png) center/cover no-repeat;}
.services-intro__bg--pava-system{background:url(../image/service-electrical-works-bg.png) center/cover no-repeat;}
.services-intro__bg--fire-hydrant-system{background:url(../image/service-fire-hydrant-system-bg.png) center/cover no-repeat;}
.services-intro__overlay{position:absolute;inset:0;z-index:0;background:linear-gradient(180deg, rgba(16,18,21,.6) 0%, rgba(16,18,21,.78) 55%, rgba(16,18,21,.94) 100%);}
.services-intro__content{position:relative;z-index:1;}
.services-intro__content .eyebrow{color:var(--amber-500);}
.services-intro__content h1{color:#fff;}
.services-intro__content .section__sub{color:#e7e5e2;}

/* ============ Process intro backdrop ============ */
.process-intro{position:relative;overflow:hidden;min-height:382px;display:flex;align-items:center;}
.process-intro__bg{position:absolute;inset:0;z-index:0;background:url(../image/process-intro-bg.jpg) center/cover no-repeat;}
.process-intro__overlay{position:absolute;inset:0;z-index:0;background:linear-gradient(180deg, rgba(16,18,21,.6) 0%, rgba(16,18,21,.78) 55%, rgba(16,18,21,.94) 100%);}
.process-intro__content{position:relative;z-index:1;}
.process-intro__content .eyebrow{color:var(--amber-500);}
.process-intro__content h1{color:#fff;}
.process-intro__content .section__sub{color:#e7e5e2;}

/* ============ Clientele ============ */
.logo-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:18px;margin-bottom:50px;}
.logo-slot{aspect-ratio:2/1;min-height:70px;}
.logo-slot::before{font-size:.68rem;}
.logo-card{
  aspect-ratio:2/1;min-height:70px;padding:18px;
  display:flex;align-items:center;justify-content:center;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);
  transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.logo-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:var(--ink-400);}
.logo-card img{max-width:100%;max-height:100%;width:auto;object-fit:contain;}
.testimonial{
  max-width:720px;margin:0 auto;text-align:center;padding:40px;border-radius:var(--radius);
  background:var(--tint);border:1px solid var(--line);
}
.testimonial blockquote{font-family:var(--ff-display);font-size:1.2rem;color:var(--ink-900);margin:0 0 16px;font-weight:500;}
.testimonial cite{color:var(--ink-600);font-style:normal;font-size:.9rem;}

/* ============ Certifications ============ */
/* auto-fit + minmax(250px, 1fr) so the *number of tracks* stays correct —
   the browser calculates how many auto-fit tracks to create using each
   track's MAX sizing function when that max is a fixed length, but falls
   back to the MIN when the max is flexible (1fr). A fixed px max (e.g.
   minmax(250px,380px)) was tried first and broke the "4 per row" math: it
   made auto-fit create only 2 tracks (floor(1154/402)), wrapping 4 real
   certificates into a 2x2 block instead of one row. Keeping the max as 1fr
   restores the min-based calculation (4 tracks — see below) while
   .cert-card's own max-width (not the track's) is what actually stops a
   lone certificate from stretching into an absurdly tall ~1600px portrait
   card; justify-items:center centers that narrower-than-track card inside
   whatever leftover track width auto-fit gave it.
   250px is tuned against .container's max content width
   (1180 - 2*24 padding = 1132px) with a 22px gap so auto-fit creates
   exactly 4 tracks (4*250 + 3*22 = 1066 <= 1132, a 5th would need
   5*250 + 4*22 = 1338 > 1132) — a 5th+ certificate wraps to a new row
   instead of a 5th column. With exactly 4 real cards the natural
   even-split track width (~266px) is already under .cert-card's 380px cap,
   so the cap never engages and the row still ends up edge-to-edge. */
.cert-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));justify-items:center;gap:22px;}
.cert{aspect-ratio:3/4;}
.cert-card{
  display:flex;flex-direction:column;background:#fff;border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;
  width:100%;max-width:380px;
}
.cert-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:var(--amber-500);}
.cert-card__img{position:relative;aspect-ratio:210/297;background:var(--tint);display:flex;align-items:center;justify-content:center;padding:10px;cursor:zoom-in;}
.cert-card__img img{width:100%;height:100%;object-fit:contain;}
/* Same bottom-to-top red-to-amber gradient wipe as .why / .grid--services
   .card, scoped to just the details section (not the certificate image,
   which already has its own zoom-in "View Full Certificate" hover cue) —
   the outer card border also picks up the accent color on hover above.
   flex:1 — .cert-card is a grid item stretched to match the tallest card in
   its row (e.g. ISO 45001's two-line description), but .cert-card__body's
   own height is otherwise just its content; without flex:1 that leftover
   stretched space sits below the body as a blank gap the gradient (inset:0
   on ::before, sized to .cert-card__body's own box) never reaches. */
.cert-card__body{padding:16px 18px;border-top:1px solid var(--line);position:relative;overflow:hidden;z-index:0;flex:1;}
.cert-card__body::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to bottom, var(--red-600), var(--amber-500));
  transform:scaleY(0);transform-origin:bottom;
  transition:transform .25s ease;
}
.cert-card:hover .cert-card__body::before{transform:scaleY(1);}
.cert-card__body h3{font-size:1rem;margin:0 0 4px;transition:color .25s ease;}
.cert-card__body p{font-size:.85rem;color:var(--ink-600);margin:0 0 6px;transition:color .25s ease;}
.cert-card__meta{font-size:.78rem;color:var(--ink-400);font-weight:600;transition:color .25s ease;}
.cert-card:hover .cert-card__body h3,
.cert-card:hover .cert-card__body p,
.cert-card:hover .cert-card__meta{color:#000;}

/* ============ Blog ============ */
.blog-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;}
/* Black card with the same bottom-to-top red-to-amber gradient wipe on
   hover as .grid--services .card/.phase__card/.why — matches this site's
   established "black card in a light section" language rather than the
   plain white .cert-card/.product-card style this used before. Reused
   as-is by the Admin Hub's blog list (see admin-blog/BlogPostCard.tsx),
   same as .grid--services .card is already shared between the public
   Services page and its own Admin Hub management cards. */
.blog-card{
  display:flex;flex-direction:column;background:#000;border:1px solid #000;border-radius:var(--radius);
  position:relative;overflow:hidden;z-index:0;
  transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.blog-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:var(--amber-500);}
.blog-card::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to bottom, var(--red-600), var(--amber-500));
  transform:scaleY(0);transform-origin:bottom;transition:transform .3s ease;
}
.blog-card:hover::before{transform:scaleY(1);}
/* Same aspect-ratio + object-fit:contain box as .cert-card__img — a fixed
   ratio container with the real photo letterboxed inside it, rather than
   .phase__image's object-fit:cover crop (which relies on a sibling-driven
   CSS Grid height and caused a real visual regression on the Process page
   earlier in this project — see the CSS Grid percentage-height gotcha in
   CLAUDE.md). Blog post photos are arbitrary admin uploads with no
   guaranteed aspect ratio, so contain (never cropping) is the safe default.
   Letterbox background is a faint white wash (matching .why's own
   rgba(255,255,255,.04) dark-card convention) rather than the light
   var(--tint) this used before — var(--tint) reads as a mismatched pale
   patch against the card's own black background. */
.blog-card__img{position:relative;aspect-ratio:16/9;background:rgba(255,255,255,.05);}
.blog-card__img img{width:100%;height:100%;object-fit:contain;display:block;}
.blog-card__body{padding:20px 22px;flex:1;display:flex;flex-direction:column;}
.blog-card__body h3{font-size:1.15rem;margin:0 0 8px;color:#fff;transition:color .25s ease;}
.blog-card__body h3 a{color:inherit;}
.blog-card__body p{color:#fff;font-size:.92rem;flex:1;transition:color .25s ease;}
.blog-card:hover .blog-card__body h3{color:#000;}
.blog-card:hover .blog-card__body p{color:#000;}
/* .inline-link's own color:var(--red-600) (red text) would sit against the
   card's own red-to-amber gradient wash during hover otherwise — poor
   contrast, unlike its normal red-on-black default. Flips to black here for
   the same reason h3/p do above. */
.blog-card:hover .blog-card__body .inline-link{color:#000;}
.blog-empty{text-align:center;}

.pagination{display:flex;align-items:center;justify-content:center;gap:20px;margin-top:50px;}
.pagination__status{color:var(--ink-600);font-size:.9rem;}

.blog-post{max-width:760px;}
.blog-post__head{max-width:none;margin:0 0 30px;text-align:left;}
.blog-post__meta{color:var(--ink-400);font-size:.85rem;font-weight:600;}
/* No forced CSS aspect-ratio here, unlike .blog-card__img above — the
   native width/height attributes on this <img> (see blog-detail.html)
   already give the browser its intrinsic aspect ratio for CLS purposes,
   and width:100%;height:auto scales it proportionally with nothing else
   competing to override that ratio. */
.blog-post__image{width:100%;height:auto;border-radius:var(--radius);box-shadow:var(--shadow-md);margin-bottom:30px;}
.blog-body{color:var(--ink-800);font-size:1rem;line-height:1.75;}
.blog-body p{margin:0 0 1.3em;}

/* ============ Service detail (9 fixed service pages) ============ */
.service-detail{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:start;}
.service-detail__media{border-radius:var(--radius);}
/* Not-yet-supplied pages use a plain <div> placeholder — needs its own fixed
   aspect-ratio since there's no image to size it. */
div.service-detail__media{
  aspect-ratio:4/3;border:2px dashed var(--line);background:var(--tint);
  display:flex;align-items:center;justify-content:center;text-align:center;
  color:var(--ink-600);font-size:.85rem;padding:20px;
}
/* Real photos size to their own natural aspect ratio instead of a fixed
   box — these are arbitrary uploaded photos (e.g. a wide collage) and a
   forced 4:3 box either cropped them (object-fit:cover) or left visible
   letterbox bands around them (object-fit:contain). Full width, natural
   height shows the whole image with no leftover space either way. */
img.service-detail__media{width:100%;height:auto;display:block;}
/* Frames a real detail photo in the same black-card language as the
   /services/ cards (.grid--services .card) — bg #000, var(--radius),
   var(--shadow-md) — minus the link/hover-wipe behavior, since this is a
   static content photo, not a clickable card. */
.service-detail__media-card{
  position:relative;background:#000;border-radius:var(--radius);padding:6px;box-shadow:var(--shadow-md);
}
.service-detail__media-card .service-detail__media{border-radius:calc(var(--radius) - 3px);}
/* Small corner inset photo, bottom-right this time — same shadow/rounded
   language as the main card, no animation (unlike the earlier top-left
   floating badge on Fire Alarm System, which was removed). */
.service-detail__media-badge{
  position:absolute;bottom:-24px;right:-24px;
  border:2px solid #fff;border-radius:var(--radius);box-shadow:var(--shadow-md);
  width:170px;aspect-ratio:4/3;overflow:hidden;
}
.service-detail__media-badge img{width:100%;height:100%;object-fit:cover;border-radius:var(--radius);display:block;}
@media (max-width:480px){
  .service-detail__media-badge{bottom:-16px;right:-12px;width:120px;}
}
.service-detail__included{margin-top:28px;}
.service-detail__included h2{margin-bottom:16px;}
.service-detail__body{color:var(--ink-800);font-size:1rem;line-height:1.75;font-weight:600;}
.service-detail__body p{margin:0 0 1.3em;}
.service-detail__body p:last-child{margin-bottom:0;}
.service-detail__body p:first-of-type::first-letter{
  font-family:var(--ff-display);font-weight:700;color:var(--ink-900);
  font-size:3.8rem;line-height:.78;float:left;padding-right:10px;
}
.service-detail__included .check-list li{font-weight:600;}
@media (max-width:860px){
  .service-detail{grid-template-columns:1fr;gap:0;}
  .service-detail__media{margin-bottom:24px;}
}

.lead-tabs{display:flex;flex-wrap:wrap;gap:10px;margin-top:24px;}
.lead-tab{
  padding:10px 20px;border-radius:999px;border:2px solid var(--ink-800);
  background:transparent;color:var(--ink-800);font-weight:600;font-size:.9rem;
  cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease;
}
.lead-tab:hover{background:var(--ink-800);color:#fff;}
.lead-tab.is-active{background:var(--red-600);border-color:var(--red-600);color:#fff;}
/* .section's default 90px top/bottom padding, stacked between the tabs'
   section and each panel's own section, left a huge dead gap before the
   table (each panel also repeated its own now-removed heading — the tab
   button already names it) — tightened just for the Leads page instead of
   touching .section globally. */
.leads-header{padding-bottom:20px;}
[data-lead-panel]{padding-top:20px;padding-bottom:60px;}

.cert-table-wrap{overflow-x:auto;border:1px solid var(--ink-400);border-radius:var(--radius);background:#fff;}
.cert-table{width:100%;border-collapse:collapse;}
.cert-table th,.cert-table td{padding:16px 20px;text-align:left;vertical-align:middle;border-bottom:1px solid var(--ink-400);overflow-wrap:anywhere;}
.cert-table tr:last-child td{border-bottom:none;}
.cert-table tr:target{background:var(--tint);}
.cert-table th{background:var(--ink-900);font-size:.78rem;text-transform:uppercase;letter-spacing:.04em;color:#fff;font-weight:700;}
.cert-table__name{font-weight:700;color:var(--ink-900);}
.cert-table__desc{color:var(--ink-600);font-size:.9rem;}
.cert-table__meta{color:var(--ink-400);font-weight:600;font-size:.85rem;white-space:nowrap;}
.cert-table__preview{width:1%;}
.cert-table__preview a{
  position:relative;display:block;width:64px;aspect-ratio:210/297;background:var(--tint);
  border-radius:8px;overflow:hidden;cursor:zoom-in;
}
.cert-table__preview img{width:100%;height:100%;object-fit:contain;}
.cert-table__preview a::after{
  content:"View";
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  background:rgba(20,18,16,.55);color:#fff;font-size:.7rem;font-weight:600;
  opacity:0;transition:opacity .2s ease;
}
.cert-table__preview a:hover::after{opacity:1;}
/* Same touch-visibility treatment as .cert-card__img::after above. */
@media (hover:none){
  .cert-table__preview a::after{opacity:1;}
}
.cert-table__action{width:1%;white-space:nowrap;}
/* Prev/Next month buttons below the Home dashboard's daily-breakdown table.
   justify-content:space-between pins Previous to the table's left edge and
   Next to its right edge, mirroring the table's own corners rather than
   sitting side by side. Left/Previous always renders (no bound on how far
   back an admin can look); when Right/Next isn't in the DOM at all (current
   month — see AdminHubHomeView), space-between still leaves Previous
   correctly pinned left with a lone flex item. */
.breakdown-nav{display:flex;justify-content:space-between;margin-top:20px;}
/* Leads-page table pagination (Survey/Contact/Consultation/Resolved) —
   centered Prev/status/Next, unlike .breakdown-nav's corner-pinned arrows,
   since both ends are always meaningful here (Prev/Next disable at the
   bounds rather than one of them disappearing). */
.lead-pagination{display:flex;align-items:center;justify-content:center;gap:16px;margin-top:20px;}
.lead-pagination__status{font-size:.9rem;font-weight:600;color:var(--ink-600);}
.lead-pagination__prev:disabled,.lead-pagination__next:disabled{opacity:.35;cursor:not-allowed;}
.lead-pagination__prev:disabled:hover,.lead-pagination__next:disabled:hover{background:transparent;color:var(--ink-800);}
/* #breakdownTable is swapped out wholesale (not diffed) whenever a
   Previous/Next click fetch()es a new month, so this transition targets the
   element itself rather than any child — see breakdown-table.html's script. */
#breakdownTable{opacity:1;transition:opacity .18s ease;}
#breakdownTable.is-loading{opacity:0;}
/* Same fade treatment on the Leads-page tables' Prev/Next (leads.html's
   script), even though that pagination is pure client-side (rows already in
   the DOM, just re-hidden) rather than a fetch() swap like #breakdownTable
   above — the fade is what actually reads as "smooth" to the user, not the
   network round-trip. [data-lead-panel] gets the same treatment for the
   Survey/Contact/Consultation/Resolved tab buttons themselves, not just
   pagination within one tab. */
.cert-table-wrap,[data-lead-panel]{opacity:1;transition:opacity .18s ease;}
.cert-table-wrap.is-loading,[data-lead-panel].is-loading{opacity:0;}
@media (prefers-reduced-motion:reduce){
  #breakdownTable,.cert-table-wrap,[data-lead-panel]{transition:none;}
}

/* ============ Brochure ============ */
.brochure-icon{display:block;width:180px;margin:0 auto;color:var(--red-600);transition:transform .2s ease;}
.brochure-icon:hover{transform:translateY(-6px);}
.brochure-icon svg{width:100%;height:auto;fill:currentColor;filter:drop-shadow(var(--shadow-md));}
.brochure-download{text-align:center;margin-top:32px;}

/* ============ Contact ============ */
.contact{display:grid;grid-template-columns:1fr 1.1fr;gap:60px;align-items:start;}
.contact__item{display:flex;align-items:center;gap:12px;margin-bottom:16px;color:var(--ink-600);}
.contact__item .icon{color:var(--red-600);width:20px;height:20px;}
.map{margin-top:24px;width:100%;aspect-ratio:16/10;border:1px solid var(--ink-400);border-radius:var(--radius);}
.map-open-link{display:inline-block;margin-top:10px;font-size:.85rem;font-weight:600;color:var(--red-600);text-decoration:none;}
.map-open-link:hover{text-decoration:underline;}

/* A same-size text link embedded inside body copy (e.g. a sentence in a
   <p>) — distinct from .map-open-link/.btn--link, which are both smaller
   standalone UI links, not inline with surrounding paragraph text. */
.inline-link{color:var(--red-600);text-decoration:underline;}
.inline-link:hover{color:var(--red-700);}

.contact__form{background:#fff;border:1px solid var(--ink-400);border-radius:var(--radius);padding:32px;box-shadow:var(--shadow-md);}
.contact__form h3{margin-bottom:20px;}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.field{margin-bottom:16px;}
.field label{display:block;font-size:.85rem;font-weight:600;margin-bottom:6px;color:var(--ink-800);}
.field input,.field select,.field textarea{
  width:100%;padding:11px 14px;border:1px solid var(--ink-400);border-radius:8px;font-family:inherit;font-size:.95rem;
  background:var(--tint);transition:border-color .15s ease, box-shadow .15s ease;
}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;border-color:var(--red-600);box-shadow:0 0 0 3px rgba(200,30,30,.12);background:#fff;
}
.form-note{margin-top:12px;font-size:.85rem;color:var(--red-600);min-height:1.2em;}
.field__hint{margin:6px 0 0;font-size:.78rem;color:var(--ink-600);}
.form-note--success{color:var(--ink-600);}

/* A button styled to read as an inline text link — used where a "link" needs
   real onClick behavior (opening a modal) rather than an href, e.g. "Forgot
   Password?" and "Resend Code" in the Admin Hub Forgot Password flow. */
.btn--link{background:none;border:none;padding:0;font:inherit;font-size:.85rem;color:var(--red-600);text-decoration:underline;cursor:pointer;}
.btn--link:hover{color:var(--red-700);}
.btn--link:disabled{color:var(--ink-400);cursor:not-allowed;text-decoration:none;}
.contact__item a{display:flex;align-items:center;gap:12px;color:inherit;text-decoration:none;}
.contact__item a:hover{color:var(--red-600);}
/* The email address is one unbreakable string with no spaces — flex items
   default to min-width:auto, which honors that full unbroken width and
   bubbles up through .contact__item's flex row into .contact__info's grid-
   item sizing, pushing the whole contact block past its container's right
   padding on narrow phones (~320-346px). overflow-wrap lets it break like
   any other text so it never forces a wider minimum than it has room for. */
.contact__item a span{overflow-wrap:anywhere;}

/* ============ Footer ============ */
.footer{background:var(--ink-900);color:#c7cad0;padding-top:60px;}
.footer .container{max-width:1360px;}
.footer__grid{display:grid;grid-template-columns:1.3fr .9fr 1.7fr 1.1fr;gap:40px;padding-bottom:40px;}
.footer__col h4{color:#fff;font-size:.95rem;margin-bottom:16px;}
.footer__col a,.footer__col p{display:block;color:#a5abb3;font-size:.9rem;margin-bottom:10px;}
.footer__col a:hover{color:var(--amber-500);}
.footer__social{display:flex;align-items:center;gap:20px;margin-top:6px;}
/* Same padding+negative-margin hit-area technique as .topbar__social a
   above — grows the tappable area to ~44x44. margin-bottom stays 0 (not
   negated) to preserve its original job of cancelling the 10px inherited
   from .footer__col a, since this is the last element in the column; the
   other three sides net back to the original footprint. */
.footer__social a{display:inline-flex;padding:11px;margin:-11px -11px 0 -11px;}
.footer__social .icon{width:22px;height:22px;}
.footer__social a.instagram-icon{color:var(--red-600);}
.footer__social a.instagram-icon:hover{color:var(--red-700);}
.footer__social a.whatsapp-icon{color:#25d366;}
.footer__social a.whatsapp-icon:hover{color:#1ebe5b;}
.footer__services-grid{display:grid;grid-template-columns:1fr 1fr;column-gap:16px;}
.footer__bottom{border-top:1px solid rgba(255,255,255,.08);padding:18px 0;}
.footer__bottom-inner{display:flex;justify-content:center;flex-wrap:wrap;gap:8px;font-size:.8rem;color:#8b909a;}

/* ============ Back to top ============ */
.back-to-top{
  position:fixed;bottom:24px;right:24px;width:46px;height:46px;border-radius:50%;
  background:var(--red-600);color:#fff;border:none;font-size:1.2rem;cursor:pointer;
  box-shadow:var(--shadow-md);opacity:0;pointer-events:none;transition:opacity .2s ease, transform .2s ease;z-index:90;
}
.back-to-top.show{opacity:1;pointer-events:auto;}
.back-to-top:hover{transform:translateY(-3px);}

/* ============ Reveal animation ============ */
.reveal{opacity:0;transform:translateY(24px);transition:opacity .6s ease, transform .6s ease;}
.reveal.in{opacity:1;transform:translateY(0);}
.reveal--left{transform:translateX(-40px);transition-delay:.5s;}
.reveal--left.in{transform:translateX(0);}

/* ============ Loading skeletons ============
   Shared shimmer block (frontend/src/lib/Skeleton.tsx renders a plain
   `<span class="skeleton">`) — each Admin Hub island composes these into
   its own real grid/card/table markup while its data is still loading, so
   the skeleton matches the eventual layout instead of a generic spinner. */
@keyframes skeleton-shimmer{
  0%{background-position:200% 0;}
  100%{background-position:-200% 0;}
}
.skeleton{
  display:block;background:linear-gradient(90deg,var(--tint) 25%,var(--line) 37%,var(--tint) 63%);
  background-size:400% 100%;animation:skeleton-shimmer 1.4s ease infinite;border-radius:6px;
}
.skeleton-text{height:14px;margin-bottom:8px;border-radius:4px;}
.skeleton-text:last-child{margin-bottom:0;}
@media (prefers-reduced-motion:reduce){
  .skeleton{animation:none;}
  .about__badge{animation:none;}
}

/* ============ Admin Hub / Auth ============ */
.auth-page{background:var(--tint);}
.auth-page main{display:flex;align-items:center;justify-content:center;padding:24px;}
.auth-card{width:100%;max-width:420px;}
.auth-card__logo{display:block;text-align:center;margin-bottom:28px;}
.auth-card__logo .logo__img{height:44px;margin:0 auto;}
.auth-card__forgot{text-align:center;margin-top:16px;}

/* position:fixed, not sticky — same reason as .site-header above: the
   load-bearing overflow-x:hidden on html/body breaks position:sticky. */
.adminhub-header{position:fixed;top:0;left:0;width:100%;z-index:100;background:#fff;border-bottom:1px solid var(--line);}
.adminhub-header__inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;padding:16px 16px;gap:12px;transition:padding .25s ease;}
/* Same shrink-on-scroll treatment as .site-header.is-scrolled (public site)
   — .adminhub-header never had this until now (adminhub.js had no scroll
   listener at all), just the static/js/header-offset.js height-sync every
   fixed header needs regardless of whether it shrinks. No topbar to collapse
   here (Admin Hub's header is a single row, unlike the public site's
   topbar+header pair), so only padding/logo shrink, not a topbar toggle. */
.adminhub-header.is-scrolled .adminhub-header__inner{padding:8px 16px;}
.adminhub-header.is-scrolled .logo__img{height:32px;}
.adminhub-header__brand{display:flex;align-items:center;flex-wrap:wrap;gap:16px 40px;}
/* .nav is the same shared class the public header uses, where it needs its
   own margin-left for logo-to-nav spacing (see .nav's base rule) — here
   .adminhub-header__brand's own gap:16px 40px already provides that
   spacing, so the public header's margin-left would just stack on top of
   it unnecessarily, eating into the room this row needs to fit all 7 nav
   links + Blog without wrapping onto a second line. */
.adminhub-header__brand .nav{margin-left:0;}
.adminhub-header__actions{display:flex;align-items:center;flex-wrap:wrap;gap:12px;}
.adminhub-header__user{font-weight:600;font-size:.9rem;color:var(--ink-600);}
.adminhub-header__actions form{margin:0;}
/* Above the same 1150px breakpoint where the public nav goes off-canvas,
   Admin Hub's nav stays a plain inline row (this used to be unconditional —
   Admin Hub now gets the same off-canvas drawer as the public site below
   1150px, reusing the plain .nav/.nav-overlay/.nav__toggle rules further
   down verbatim, so it only needs gating here, not a parallel set of rules). */
@media (min-width:1151px){
  .adminhub-header .nav{position:static;height:auto;width:auto;background:none;flex-direction:row;flex-wrap:wrap;padding:0;box-shadow:none;transform:none;row-gap:10px;}
}
/* Username + Log Out shown inline in the header above 1150px; the drawer
   gets its own copy at the bottom (.nav__footer, below) instead of this one
   once the nav goes off-canvas — see the max-width:1150px block below. */
.nav__footer{display:none;}

.admin-mv-card .btn{padding:9px 18px;font-size:.85rem;}
/* .btn--icon needs its own padding:0 to win back over the rule above —
   without this, the Edit/Delete icon buttons' box shrinks (box-sizing:
   border-box) far below the fixed 17x17 icon inside, clipping it. */
.admin-mv-card .btn.btn--icon{padding:0;}
.admin-mv-actions{display:flex;align-items:center;gap:10px;margin-top:16px;}
.admin-mv-confirm-text{font-weight:600;font-size:.9rem;color:var(--ink-800);}
.admin-mv-point{display:flex;align-items:center;gap:10px;margin-bottom:10px;}
.admin-mv-point input{flex:1;}
.admin-mv-point .btn{flex-shrink:0;}
.admin-mv-point__text{flex:1;color:var(--ink-600);}

.client-logos__admin-actions{display:flex;justify-content:flex-end;gap:12px;margin-bottom:32px;}
.client-logos__admin-actions .btn{padding:10px 20px;font-size:.85rem;}

.logo-card--admin{aspect-ratio:auto;flex-direction:column;gap:12px;padding:16px;}
.logo-card--admin img{max-height:50px;}
.logo-card__admin-actions{display:flex;gap:8px;}
.logo-card__admin-actions .btn{padding:6px 14px;font-size:.75rem;}
/* Same fix as .admin-mv-card .btn.btn--icon above — restore .btn--icon's
   padding:0 so it wins over the padding:6px 14px rule immediately above,
   which otherwise squishes the icon buttons' box below the icon's size. */
.logo-card__admin-actions .btn.btn--icon{padding:0;}

.logo-add-form{width:100%;}

/* ============ Admin Hub — Process Phases (row-per-phase layout) ============
   Reuses the public Process page's .phase__body/.phase__image/.phase__cards/
   .phase__card (black gradient-hover cards) for visual consistency between
   what the admin edits and what the public page renders — only the
   surrounding row chrome (border, header with the phase number + actions) is
   admin-specific. */
.phase-admin-list{display:flex;flex-direction:column;gap:28px;margin-bottom:50px;}
.phase-admin-row{border:1px solid var(--line);border-radius:var(--radius);padding:20px;}
.phase-admin-row__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;}
.phase-admin-row__label{font-weight:700;font-size:.95rem;color:var(--ink-800);margin:0;}
.phase-admin-row .phase__body{margin-top:0;}

/* ============ Admin Hub — Blog ============
   Reuses the public Blog page's .blog-card/.blog-card__img/.blog-card__body
   (same convention as Admin Hub — Process Phases above, which reuses the
   public Process page's classes) — only the status badge and the
   Edit/Delete actions layered into the card body are admin-specific. */
.blog-status{
  display:inline-block;padding:4px 12px;border-radius:999px;font-size:.72rem;font-weight:700;
  text-transform:uppercase;letter-spacing:.03em;margin-bottom:10px;
}
/* No existing green/success color token in this project (even
   .form-note--success above uses ink-600, not green) — Published reuses the
   site's own amber accent instead of introducing a new color. */
.blog-status--published{background:rgba(245,165,36,.15);color:var(--amber-500);}
.blog-status--draft{background:var(--tint);color:var(--ink-400);}
.blog-card__body .logo-card__admin-actions{margin-top:16px;}
/* Same flex/gap treatment as .account-email-section .field--checkbox
   (Account Settings) — scoped to .modal instead, since the Blog Add and Edit
   forms' checkbox lives inside two different modals (Add Blog Post, Edit
   Blog Post), not one shared wrapper class. */
.modal .field--checkbox{display:flex;align-items:center;gap:8px;cursor:pointer;margin-bottom:20px;}

/* ============ Admin Home dashboard (command center) ============
   Scoped dark theme + accent tokens — deliberately local to .command-center,
   not merged into :root, since the rest of Admin Hub (login, other admin
   pages) stays on the site's light brand theme. See conversation history for
   why: the user asked for a dark "command center" look on this page only. */
.command-center{
  --cc-bg:#0b1120;--cc-panel:#111a2e;--cc-line:#22314f;
  --cc-text:#eef2ff;--cc-muted:#8590ad;
  --cc-cyan:#22d3ee;--cc-cyan-track:rgba(34,211,238,.16);--cc-cyan-glow:rgba(34,211,238,.35);
  width:100%;max-width:1180px;margin:32px auto;min-height:calc(100vh - 139px);box-sizing:border-box;
  background:
    radial-gradient(circle at 1px 1px, rgba(255,255,255,.05) 1px, transparent 0) 0 0/24px 24px,
    var(--cc-bg);
  border-radius:var(--radius);padding:36px 40px;color:var(--cc-text);
  font-family:var(--ff-body);
}
.command-center__header{
  display:flex;align-items:center;justify-content:space-between;
  gap:24px;margin-bottom:32px;padding-bottom:20px;border-bottom:1px solid var(--cc-line);
}
.command-center__brand{display:flex;align-items:center;gap:14px;}
.command-center__brand .logo__img{height:38px;}
.command-center__brand-text{display:flex;flex-direction:column;}
.command-center__title{font-family:var(--ff-display);font-weight:700;font-size:1.3rem;color:var(--cc-text);}
.command-center__subtitle{font-size:.78rem;letter-spacing:.08em;text-transform:uppercase;color:var(--cc-cyan);}
/* flex-wrap:nowrap (not wrap) + min-width:0 on both this row and each stat
   item — a flex item's default min-width:auto keeps it at its content's
   natural width, which is what was letting this row overflow its share of
   .command-center__header and wrap its last item (Team Members) onto a
   second line instead of everything shrinking to fit one row. */
.cc-header-stats{display:flex;flex-wrap:nowrap;align-items:center;gap:10px 14px;min-width:0;}
.cc-header-stats [data-stat-editor]{padding-right:14px;border-right:1px solid var(--cc-line);min-width:0;}
.cc-header-stats [data-stat-editor]:last-child{padding-right:0;border-right:none;}
.cc-years-exp{display:flex;flex-direction:column;align-items:flex-end;gap:2px;}
.cc-years-exp__value{
  font-family:var(--ff-body);font-weight:700;font-size:1.8rem;color:var(--cc-text);line-height:1;
  background:none;border:none;padding:4px;cursor:pointer;transition:color .15s ease;
}
.cc-years-exp__value:hover{color:var(--cc-cyan);}
.cc-years-exp__suffix{color:var(--cc-cyan);}
.cc-years-exp__label{font-size:.78rem;color:var(--cc-muted);margin:0;}

/* Dark-theme button variant — .btn--outline's ink-800 border/text reads as
   near-invisible against the command-center's navy background, so header
   controls placed inside it (e.g. the Hero Slideshow button) use this
   instead, matching the cyan accent used throughout this section. */
.cc-btn{
  display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;
  box-sizing:border-box;width:160px;margin:0;padding:9px 18px;
  border-radius:999px;border:1px solid var(--cc-line);
  background:transparent;color:var(--cc-text);font-family:var(--ff-body);font-size:.82rem;font-weight:600;line-height:normal;
  text-align:center;cursor:pointer;appearance:none;-webkit-appearance:none;
  transition:border-color .15s ease,color .15s ease;
}
/* Firefox draws an extra inner border/padding on <button> that box-sizing
   alone doesn't remove — Hero Slideshow (a <button>) would otherwise render
   fractionally narrower than Brochure (a plain <a>) despite identical CSS. */
.cc-btn::-moz-focus-inner{border:0;padding:0;}
.cc-btn:hover{border-color:var(--cc-cyan);color:var(--cc-cyan);}

/* Stacks the Hero Slideshow button and the Brochure button (and whatever
   else lands in this header slot next) in a column between the brand and
   the stats row, instead of them fighting for the same row. */
.cc-header-actions{display:flex;flex-direction:column;align-items:flex-start;gap:8px;}

/* ---- Meters ---- */
.command-center__meters{display:flex;flex-wrap:wrap;gap:20px;margin-bottom:28px;}
.cc-meter-card{
  flex:1 1 180px;background:var(--cc-panel);border:1px solid var(--cc-line);border-radius:var(--radius);
  padding:22px;display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;
}
.cc-meter{
  --pct:0;position:relative;width:104px;height:104px;border-radius:50%;
  background:conic-gradient(var(--cc-cyan) calc(var(--pct)*1%), var(--cc-cyan-track) 0);
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 0 24px var(--cc-cyan-glow);
}
.cc-meter::before{content:'';position:absolute;inset:9px;border-radius:50%;background:var(--cc-panel);}
.cc-meter__value{position:relative;z-index:1;font-family:var(--ff-body);font-weight:700;font-size:1.5rem;color:var(--cc-text);}
.cc-meter__unit{font-size:.85rem;color:var(--cc-muted);}
.cc-meter-card__label{font-weight:600;font-size:.92rem;color:var(--cc-text);margin:0;}
.cc-meter-card__sub{font-size:.78rem;color:var(--cc-muted);margin:0;}

/* ---- Main grid: trend chart / activity feed / hero counter ---- */
.command-center__main{display:grid;grid-template-columns:2fr 1fr 1fr;gap:20px;align-items:stretch;}
.cc-panel{background:var(--cc-panel);border:1px solid var(--cc-line);border-radius:var(--radius);padding:22px;}
.cc-panel__head h3{margin:0 0 16px;font-size:.95rem;font-weight:600;color:var(--cc-text);}

/* flex column + margin-top:auto on the wrap keeps the heading pinned to the
   top and pushes the (fixed-height) chart itself down flush with the
   panel's bottom edge, instead of floating with empty space beneath it
   whenever the panel stretches taller than the chart's own content. */
.cc-panel--chart{position:relative;display:flex;flex-direction:column;}
.cc-linechart-wrap{position:relative;margin-top:auto;}
.cc-linechart{width:100%;height:160px;display:block;overflow:visible;}
.cc-linechart__peak-label{
  position:absolute;transform:translate(-50%, calc(-100% - 10px));
  background:var(--cc-panel);border:1px solid var(--cc-line);border-radius:6px;
  padding:2px 8px;font-size:.72rem;font-weight:600;color:var(--cc-cyan);
  white-space:nowrap;pointer-events:none;
}
.cc-linechart__grid{stroke:var(--cc-line);stroke-width:1;}
.cc-linechart__area{fill:var(--cc-cyan);fill-opacity:.1;stroke:none;}
.cc-linechart__line{fill:none;stroke:var(--cc-cyan);stroke-width:2;stroke-linejoin:round;stroke-linecap:round;}
.cc-linechart__dot{fill:var(--cc-cyan);r:3;}
.cc-linechart__hit{fill:transparent;cursor:pointer;}
.cc-tooltip{
  position:absolute;pointer-events:none;background:#050a16;border:1px solid var(--cc-line);
  border-radius:8px;padding:8px 12px;font-size:.8rem;color:var(--cc-text);line-height:1.5;
  box-shadow:var(--shadow-md);white-space:nowrap;z-index:5;
}
.cc-tooltip strong{color:var(--cc-cyan);}

.cc-activity{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px;}
.cc-activity__link{
  display:flex;flex-direction:column;gap:2px;text-decoration:none;color:inherit;
  padding:10px 12px;border-radius:10px;transition:background .15s ease;
}
.cc-activity__link:hover{background:rgba(255,255,255,.04);}
.cc-activity__type{font-size:.72rem;letter-spacing:.04em;text-transform:uppercase;color:var(--cc-cyan);}
.cc-activity__name{font-weight:600;color:var(--cc-text);}
.cc-activity__time{font-size:.78rem;color:var(--cc-muted);}
.cc-activity__empty{color:var(--cc-muted);font-size:.85rem;padding:10px 12px;}

.cc-panel--counter{display:flex;flex-direction:column;justify-content:center;text-align:center;}
.cc-hero{font-family:var(--ff-body);font-weight:800;font-size:3rem;color:var(--cc-text);line-height:1;}
.cc-hero__sub{margin:8px 0 0;font-size:.82rem;color:var(--cc-muted);}

/* ============ Modal ============ */
.modal-overlay{
  position:fixed;inset:0;z-index:1000;
  background:rgba(16,18,21,.6);
  display:flex;align-items:center;justify-content:center;
  padding:24px;
}
.modal-overlay[hidden]{display:none;}
.modal{
  position:relative;
  background:#fff;border-radius:var(--radius);
  padding:28px;width:100%;max-width:420px;max-height:90vh;overflow-y:auto;
  box-shadow:0 20px 60px rgba(0,0,0,.35);
  /* Scroll is kept (content still overflows-and-scrolls on short viewports,
     e.g. the multi-section Account Settings modal) — only the visible
     scrollbar track/thumb is hidden, cross-browser. */
  scrollbar-width:none;
  -ms-overflow-style:none;
}
.modal::-webkit-scrollbar{display:none;}
.modal__title{margin:0 0 20px;font-size:1.1rem;color:var(--ink-900);}
.modal__close{
  /* Box grown 28x28 -> 44x44 for a real touch target; top/right pulled in
     from 14px to 6px so the glyph's visual center stays roughly where it
     was (14+14=28 before, 6+22=28 now), rather than the corner icon
     visibly drifting inward as the box grows. */
  position:absolute;top:6px;right:6px;
  width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  background:none;border:none;border-radius:6px;cursor:pointer;
  font-size:1.3rem;line-height:1;color:var(--ink-600);
}
.modal__close:hover{background:var(--tint);}
.modal__body{color:var(--ink-600);margin-bottom:20px;}
.admin-modal-divider{border:none;border-top:1px solid var(--line);margin:24px 0;}

/* ---- Account Settings "Change Email" checkbox + OTP-confirmed tick ---- */
.account-email-section .field--checkbox{
  display:flex;align-items:center;gap:8px;margin-bottom:20px;cursor:pointer;
}
.field--checkbox input[type="checkbox"]{width:auto;margin:0;accent-color:var(--red-600);cursor:pointer;}
.field--checkbox span{font-size:.85rem;font-weight:600;color:var(--ink-800);}
.email-confirmed-tick{
  display:inline-flex;align-items:center;justify-content:center;
  width:20px;height:20px;border-radius:50%;
  background:#1f9d55;color:#fff;font-size:.7rem;font-weight:700;line-height:1;
}

/* Account Settings modal's Cancel/Done footer — replaces the modal's usual
   top-right "×" (see Modal's hideCloseButton prop) since this modal applies
   each section's change immediately but only *finalizes* it once Done is
   clicked; Cancel (or Escape/overlay-click, treated the same) reverts
   whatever was applied this session instead. */
.account-settings-footer{
  display:flex;justify-content:flex-end;gap:12px;
  margin-top:28px;padding-top:20px;border-top:1px solid var(--line);
}

/* ---- Hero Slideshow admin modal ---- */
.hero-slideshow-preview{
  position:relative;height:160px;border-radius:var(--radius);overflow:hidden;
  margin-bottom:20px;background:var(--tint);
}
.hero-slideshow-preview__slide{
  position:absolute;inset:0;background-position:center;background-size:cover;
  background-repeat:no-repeat;opacity:0;transition:opacity 1s ease;
}
.hero-slideshow-preview__slide.is-active{opacity:1;}
.hero-slideshow-preview__empty{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:var(--ink-600);font-size:.85rem;margin:0;
}
.hero-slide-list{display:flex;flex-direction:column;gap:10px;margin:16px 0;}
.hero-slide-row{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  border:1px solid var(--line);border-radius:var(--radius);padding:8px 10px;
}
.hero-slide-row img{width:72px;height:44px;object-fit:cover;border-radius:6px;flex-shrink:0;}

/* ---- Brochure admin modal ---- */
.brochure-preview{
  max-height:320px;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius);
  margin-bottom:20px;background:var(--tint);display:flex;justify-content:center;
}
.brochure-preview img{width:100%;height:auto;object-fit:contain;display:block;}

/* ---- Lead detail modal — deliberately larger than the standard confirm-dialog
   .modal, per the request for a "giant" pop-up presenting the full message ---- */
.modal--lead{max-width:680px;padding:36px;}
.lead-modal__type{margin:0 0 6px;font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--red-600);}
.lead-modal__fields{display:flex;flex-direction:column;gap:18px;margin-bottom:28px;}
.lead-modal__field{border-bottom:1px solid var(--line);padding-bottom:16px;}
.lead-modal__label{display:block;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-400);margin-bottom:6px;}
.lead-modal__value{margin:0;color:var(--ink-800);white-space:pre-wrap;line-height:1.65;}
/* flex-wrap so the two buttons (neither of which shrinks/wraps its own
   text) stack instead of overflowing the modal box on narrow phones. */
.lead-modal__footer{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:12px;}
/* .btn{display:inline-flex} (an author rule) otherwise wins over the plain
   hidden attribute's UA-stylesheet default — same fix as .modal-overlay[hidden]
   above, needed here so the already-resolved-lead case actually hides the
   button instead of just leaving it inert. */
#leadModalResolveBtn[hidden]{display:none;}

/* ============ Responsive ============ */
/* The off-canvas nav drawer used to only activate at <=860px, but the
   full desktop row (logo + 7 nav links + 2 CTA buttons, none of it
   wrapping) only actually fits again above ~1150px — leaving 861-1150px
   as a dead zone where nav items/buttons were clipped by the load-bearing
   overflow-x:hidden on html/body. Widening the drawer's trigger width to
   1150px closes that gap using the same working pattern already in place
   below 860px, rather than inventing a second, different responsive
   mechanism for the gap. Above 1150px and at/below 600px, appearance is
   unchanged from before. */
@media (max-width:1150px){
  .nav{
    position:fixed;top:0;right:0;height:100vh;width:260px;background:#fff;flex-direction:column;
    padding:20px 28px;gap:16px;margin-left:0;box-shadow:-8px 0 24px rgba(0,0,0,.12);
    /* overflow-y:auto is a safety net, not the fix itself — the
       padding/gap values above are sized to fit all 8 links + logo row +
       Get Brochure button within real mobile viewport heights without
       needing to scroll (body.nav-open locks page scroll while this is
       open, so unlike before, there was no way to reach content that
       overflowed height:100vh — it was genuinely unreachable, not just
       hidden behind a scroll). This only activates on unusually short
       viewports (e.g. landscape phones) as a fallback. */
    overflow-y:auto;
    transform:translateX(100%);transition:transform .25s ease;z-index:99;
  }
  .nav.open{transform:translateX(0);}
  .nav__toggle{display:flex;}
  /* .nav is a DOM descendant of .site-header (nested inside .header__inner,
     needed so it stays in-flow between the logo and header__actions at
     desktop widths). Because .site-header itself establishes the stacking
     context, .nav's z-index:99 is compared against ITS OWN stacking
     siblings inside .header__inner — not against .site-header as a whole.
     .header__actions has no explicit position/z-index, so it paints in
     normal document-flow order (before any positioned+z-indexed sibling),
     meaning the drawer was rendering on top of the toggle button and
     hiding it entirely once open. Giving .header__actions its own explicit
     stacking (position+z-index above .nav's 99) fixes that without moving
     .nav in the DOM. */
  .header__actions{position:relative;z-index:101;}
  .header__actions .btn--outline,
  .header__actions .btn--primary{display:none;}
  /* .nav a's ancestor-class+element selector ((0,1,1) specificity) would
     otherwise win over a plain .btn/.btn--outline pairing ((0,1,0) each) —
     same cascade gotcha as elsewhere in this file — so this is styled as
     its own explicit button look scoped under .nav rather than composed
     from the shared .btn classes. */
  .nav .nav__brochure-link{
    display:flex;align-items:center;justify-content:center;gap:8px;
    margin-top:auto;padding:10px 22px;border-radius:999px;
    border:2px solid var(--red-600);color:var(--red-600);
    font-weight:600;font-size:.95rem;
  }
  .nav .nav__brochure-link:hover{background:var(--red-600);color:#fff;}
  .nav .nav__brochure-link::after{display:none;}
  .nav .nav__brochure-link .icon{width:18px;height:18px;flex-shrink:0;}
  /* Logo row pinned to the top of the drawer, roughly level with the fixed
     close (X) button that floats above via .header__actions — .nav's own
     padding-top was shrunk from 100px to 24px (above) so this sits right at
     the top instead of appearing further down near the first link. */
  .nav__brand{
    display:flex;align-items:center;padding-bottom:14px;margin-bottom:4px;
    border-bottom:1px solid var(--line);
  }
  .nav__brand .logo__img{height:32px;}
  /* Backdrop scrim behind the drawer — see static/js/site.js for the
     open/close logic that toggles .nav-overlay's .open class alongside
     .nav's. Only relevant while the drawer can open (<=1150px). */
  .nav-overlay{
    position:fixed;inset:0;background:rgba(16,18,21,.5);z-index:98;
    opacity:0;pointer-events:none;transition:opacity .25s ease;
  }
  .nav-overlay.open{opacity:1;pointer-events:auto;}
  /* Body-scroll lock while the drawer is open — toggled by static/js/site.js
     alongside .nav/.nav-overlay's own .open class. Scoped inside this same
     breakpoint since the drawer (and so this class) can only be toggled
     open at widths where the toggle button is actually visible. */
  body.nav-open{overflow:hidden;}
  /* Admin Hub's own copy of the header__actions stacking fix above — same
     reasoning, same z-index, just a differently-named class since Admin Hub
     uses .adminhub-header__actions instead of .header__actions. The inline
     username/Log Out this container normally shows are hidden here in favor
     of the .nav__footer copy at the bottom of the drawer (below); only the
     toggle button stays visible in the header itself. */
  .adminhub-header__actions{position:relative;z-index:101;}
  .adminhub-header__actions .adminhub-header__user,
  .adminhub-header__actions [data-change-password-root],
  .adminhub-header__actions form{display:none;}
  /* Username + Log Out, pinned to the bottom of the drawer via margin-top:
     auto (.nav is flex-direction:column here) — same footer-of-sidebar
     pattern requested for Admin Hub's drawer. */
  .nav__footer{
    display:flex;flex-direction:column;gap:14px;margin-top:auto;padding-top:20px;
    border-top:1px solid var(--line);
  }
  .nav__footer form{margin:0;}
  .nav__footer .btn{width:100%;}
  /* Username reads as a plain trailing label under Account Settings/Log Out
     otherwise — centering it and giving it its own thin top divider (on top
     of .nav__footer's own gap:14px) marks it as a separate "who's logged in"
     line rather than one more stacked action. */
  .nav__footer .adminhub-header__user{
    text-align:center;
    border-top:1px solid var(--line);
    padding-top:14px;
  }
}
@media (max-width:1024px){
  .grid--services{grid-template-columns:repeat(2,1fr);}
  .process{grid-template-columns:repeat(2,1fr);}
  .grid--why{grid-template-columns:repeat(2,1fr);}
  .logo-grid{grid-template-columns:repeat(3,1fr);}
  .cert-grid{grid-template-columns:repeat(2,1fr);}
  .product-grid{grid-template-columns:repeat(4,1fr);}
  .footer__grid{grid-template-columns:1fr 1fr;}
  .blog-grid{grid-template-columns:repeat(2,1fr);}
}
@media (max-width:860px){
  .about,.contact{grid-template-columns:1fr;}
  .about__media{margin-bottom:40px;}
  .stats__grid{grid-template-columns:repeat(2,1fr);gap:20px;}
  .stats__grid--3{grid-template-columns:repeat(3,1fr);}
  .stat{border-right:none;border-bottom:1px solid rgba(255,255,255,.1);padding-bottom:14px;}
  .phase__body{grid-template-columns:1fr;}
  .product-grid{grid-template-columns:repeat(3,1fr);}
  .check-list--columns{columns:1;}
  .command-center{padding:28px 20px;margin:20px auto;max-width:calc(100% - 32px);}
  .command-center__header{flex-direction:column;align-items:flex-start;gap:14px;}
  .command-center__main{grid-template-columns:1fr;}
  /* .cc-header-stats' flex-wrap (desktop) leaves each wrapped row's
     rightmost item keeping its border-right divider with nothing beside it
     on that row — the stray floating lines seen on mobile. A 2-column grid
     gives all 5 stats a consistent home instead of fighting the wrap. */
  .cc-header-stats{display:grid;grid-template-columns:repeat(2,1fr);gap:20px 16px;width:100%;}
  .cc-header-stats [data-stat-editor]{padding-right:0;border-right:none;}
  .cc-years-exp{align-items:flex-start;}
}
@media (max-width:600px){
  .about__pic{width:150px;}
  .about__pic--tl{top:-16px;left:-16px;}
  .about__pic--br{bottom:-16px;right:-16px;}
  /* top pulled up further than the -16px used elsewhere for this same
     corner-badge positioning (.about__pic--tl above) — the mobile photo is
     much shorter than its desktop crop, so -16px let the badge's bottom
     edge sink down over the people's faces instead of just clipping the
     photo's corner. */
  .about__badge{top:-48px;left:-16px;padding:18px 22px;}
  .about__badge strong{font-size:1.8rem;}
  .grid--services,.grid--why,.grid--mission,.cert-grid,.blog-grid{grid-template-columns:1fr;}
  .product-grid{grid-template-columns:repeat(2,1fr);}
  .phase{grid-template-columns:52px 1fr;gap:16px;}
  .phase__marker{width:52px;height:52px;}
  .phase__marker svg{width:22px;height:22px;}
  .phase__marker-num{width:20px;height:20px;font-size:.6rem;}
  .phase-list::before{left:25px;}
  .logo-grid{grid-template-columns:repeat(2,1fr);}
  /* .logo-card's aspect-ratio:2/1 + min-height:70px implies a 140px min-width
     (grid items default to min-width:auto, which factors that in) — wider
     than the ~127-147px a 2-column track actually has on narrow phones,
     pushing the right card past the viewport edge instead of centering.
     min-width:0 alone isn't enough: once min-height:70 wins over the
     aspect-ratio-derived height, browsers transfer that height back into a
     140px preferred width that still overrides the grid's stretch sizing.
     An explicit width:100% pins the card to its actual track width so
     aspect-ratio can only affect height, not push back out into width. */
  .logo-card{min-width:0;width:100%;}
  .form-row{grid-template-columns:1fr;}
  /* Mobile phone/email/Instagram row is dropped entirely below 600px —
     it stays desktop/tablet-only; syncHeaderOffset() in site.js re-measures
     .site-header's height on resize so body padding-top shrinks to match. */
  .topbar{display:none;}
  .hero-brand{padding:24px 0;}
  .hero-brand__inner{gap:14px;}
  .hero-brand__logo .logo__img{height:50px;}
  .hero-brand__name{font-size:1.4rem;}
  .hero-brand__tagline{font-size:.75rem;}
  .hero__cta{flex-direction:column;}
  .hero__cta-mobile-only{display:inline-flex;}
  .hero h1{text-align:left;}
  .footer__grid{grid-template-columns:1fr;}
  .footer__services-grid{grid-template-columns:1fr;}
  .cc-meter{width:84px;height:84px;}
  .cc-meter__value{font-size:1.2rem;}
  /* Same 2-col grid as the 860px breakpoint, just tighter — the 5 header
     stats (value+suffix+label each) are cramped in a 2-column grid at very
     narrow phone widths, so shrink the numbers down a step. */
  .cc-header-stats{gap:16px 12px;}
  .cc-years-exp__value{font-size:1.4rem;}
  .cc-years-exp__label{font-size:.72rem;}
  /* Extra safety margin for .lead-modal__footer's flex-wrap fix above —
     less padding leaves more room for the wrapped buttons on very narrow
     phones (~360px), on top of the modal-overlay's own 24px padding. */
  .modal--lead{padding:24px;}
  /* .cert-table (Leads, the Home dashboard's daily-breakdown table, Admin
     Hub Certifications) is wider than any phone viewport once every column
     is shown at a readable size. A prior pass handled this by hiding
     columns and shrinking padding so everything crammed into the viewport
     with no scrolling — but overflow-wrap:anywhere (base rule above) then
     let the browser break header/cell text mid-word to fit the squeezed
     columns, reading as vertically-stacked letters instead of words.
     Reverted per explicit request: keep every column and let the table
     scroll horizontally instead of compressing or hiding content.
     .cert-table-wrap keeps its base overflow-x:auto (no override needed),
     and every cell is forced to a single line (white-space:nowrap,
     overriding overflow-wrap:anywhere) so content determines the table's
     real width rather than being squeezed into the viewport.
     Mixing white-space:normal on .cert-table__desc (an earlier attempt)
     with nowrap siblings in the same row confused the browser's auto
     table-layout column-width calculation — the Address/Problem columns
     both collapsed to near-zero assigned width while their text still
     rendered at its natural, unclipped size (table cells don't clip
     overflow by default), so the two columns' text visually overlapped
     each other instead of sitting side by side. Keeping every column
     nowrap, with no per-column exception, is what makes auto table-layout
     assign each column its own non-overlapping natural width. */
  .cert-table th,.cert-table td{padding:12px 16px;white-space:nowrap;}
  /* "Leads — Daily Breakdown — <month>" (#breakdownTable's h2) uses the
     browser-default h2 size — no site-wide h2 font-size rule exists — which
     reads oversized and wraps awkwardly at phone widths. */
  #breakdownTable h2{font-size:1.2rem;}
}
