/**
 * HERO CITY - V2 EXPERIMENT ONLY (design/vigiworks-v2 branch).
 * Styles for the V2 hero - the left text column on a plain solid-black
 * canvas, with the pre-rendered VigiWorks city video (`.hero-city-video`
 * - see index.html's `<video>`) occupying its own area on the right,
 * fading into that black background rather than acting as a full-bleed
 * background itself. The rules further down for the earlier LIVE
 * PROCEDURAL SVG CITY (buildings, windows, streets, pedestrians,
 * network links - everything from `.cb-face` down to `.city-node`) are
 * left in place but currently unused, since js/marketing/hero-city.js
 * has nothing to render them into any more - kept for an easy revert,
 * not dead weight to clean up casually.
 *
 * THE TOGGLE: index.html's `.hero` carries `data-hero-variant="city"`.
 * While that attribute is present, this file hides the original robot
 * scene (`.hero-robot-scene`) and shows this one (`.hero-city-scene`),
 * AND disables the robot hero's own lamp-glow spill (`.hero::before`)
 * and the sitewide `.grid-bg` texture for this view specifically -
 * this hero is meant to read as a clean black canvas, not the
 * textured/glowing look the robot hero uses. To go back to the robot
 * hero, delete that one attribute from the `<section class="hero">`
 * tag in index.html - nothing else needs to change, and nothing from
 * the robot hero (including its glow/grid) was removed.
 *
 * Requires base.css (tokens) and hero.css (the shared .hero/.lamp/
 * .hero-badge/.hero-btns rules this reuses) to be linked first.
 */

/* Both scenes exist in the DOM at once; only one is ever visible. */
.hero:not([data-hero-variant="city"]) .hero-city-scene { display: none; }
.hero[data-hero-variant="city"] .hero-robot-scene { display: none; }
/* The robot hero's lamp (graphic + glow + spotlight), its green glow
   wash (hero.css's `.hero::before`), and the sitewide graph-paper
   `.grid-bg` texture all conflict with this hero's "one seamless solid
   black canvas" - switched off only while this variant is active.
   `:has()` (not a later-sibling selector) is needed for `.grid-bg`
   specifically because it sits BEFORE `.hero` in the DOM (a direct
   child of <body>, next to <nav>), so a plain sibling combinator can't
   reach it from here.
   NOTE: the lamp was also this site's only light/dark theme toggle -
   its replacement is the small sun/moon button in the nav
   (`.theme-toggle` - see nav.css), not removed/reduced here, just
   moved. */
.hero[data-hero-variant="city"] .lamp { display: none; }
.hero[data-hero-variant="city"]::before { display: none; }
body:has(.hero[data-hero-variant="city"]) .grid-bg { display: none; }
/* The real source of the "two separate black panels" look: `.hero`
   itself still used the page's ordinary --bg token (#0a0a0a) in its
   own padding-top strip (where the nav sits, above `.hero-city-scene`'s
   own box) - one pixel value apart from this hero's #000, but enough
   to read as a seam exactly where the video used to reach up into that
   strip. Forcing `.hero` itself to #000 here removes any possible
   mismatch, on every edge, not just where the video happens to be.
   padding-top is raised from hero.css's shared 72px to 110px: the
   full-bleed `.hero-city`/`.hero-city-video` below is `inset:0` on
   `.hero-city-scene`, so it starts exactly where THIS padding-top
   ends, not where `.hero-city-scene`'s own padding ends - at 72px the
   video was visible behind the bottom of the (unscrolled, transparent-
   background) nav, making the logo look cramped against the brighter
   video right behind it. The nav is a fixed 88px tall now (same
   unscrolled and scrolled - see nav.css), so 110px clears it with a
   deliberate ~22px margin, not the much larger gap an earlier pass
   here (152px, sized for the nav's old, taller 98px unscrolled state)
   left behind once the nav itself got shorter. */
.hero[data-hero-variant="city"] { background: #000; min-height: auto; padding-top: 110px; }

/* NAV: pinned to its dark-mode appearance on this page, regardless of
   `data-theme`. This hero is intentionally solid black in BOTH themes
   (see the rule just above), and the nav sits directly on top of it
   (transparent until scrolled, then a translucent dark blur) - the
   *light*-mode text/logo/stuck-background values are tuned for a
   near-white page background and go unreadable against this black
   hero. Full light-mode support for the rest of this page is separate,
   larger work; this specifically only fixes nav legibility. Hardcoded
   to the exact dark-mode token values (not `var(--text)` etc, which
   would just flip back to the light-mode values here) - see base.css's
   `:root` block for where these came from. Higher specificity than
   nav.css's own `[data-theme="light"]` rules (and loaded after them),
   so this wins in light mode without touching nav.css itself. */
body:has(.hero[data-hero-variant="city"]) .nav-logo-word { color: #f0ede8; }
body:has(.hero[data-hero-variant="city"]) .nav-links a { color: #6b6560; }
body:has(.hero[data-hero-variant="city"]) .nav-links a:hover { color: #00e882; }
body:has(.hero[data-hero-variant="city"]) .nav.stuck { background: rgba(10,10,10,.88); }
body:has(.hero[data-hero-variant="city"]) .nav-logo-mark-dark { display: block; }
body:has(.hero[data-hero-variant="city"]) .nav-logo-mark-light { display: none; }
body:has(.hero[data-hero-variant="city"]) .theme-toggle { color: #6b6560; }
body:has(.hero[data-hero-variant="city"]) .theme-toggle:hover { color: #00e882; }

.hero-city-scene {
  position: relative; z-index: 1;
  display: flex; align-items: flex-start;
  background: #000;
  /* Compact, not min-height:100vh - that forced a nearly-full-viewport
     box and, combined with align-items:center, left a few hundred
     pixels of empty black above the headline. align-items:flex-start
     + a modest padding-top here (on top of `.hero`'s own 72px nav-
     clearance padding) gives a normal gap under the nav instead. */
  min-height: auto; padding: 28px 0 70px;
  text-align: left;
}
.hero-city-scene .wrap { width: 100%; }
.hero-city-scene .hero-row { display: flex; align-items: center; gap: 40px; }

/* ── LEFT: TEXT COLUMN ────────────────────────────────
   Deliberately narrower than the old centered composition, and left-
   aligned, so the city has real room to breathe on the right without
   ever needing to shrink under the text. */
.hero-city-text { position: relative; z-index: 2; max-width: 480px; flex: 0 0 auto; }
.hero-city-h1 {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.6rem, 5.2vw, 4.4rem);
  line-height: 1.04; letter-spacing: -.035em;
  color: var(--text); margin: 0 0 18px;
}
/* "We build" pinned to its dark-mode white, same reasoning as the nav
   fix above - this hero is solid black in both themes, and the light-
   mode --text value (near-black) goes unreadable against it. Doesn't
   need :has() scoping like the nav did: .hero-city-h1 only ever exists
   on this hero, so a plain [data-theme="light"] override is already
   correctly scoped. The green flip-words aren't affected - they get
   their color from .hero-city-flip-item's --accent, which is already
   theme-independent (see base.css's un-prefixed :root block). */
[data-theme="light"] .hero-city-h1 { color: #f0ede8; }
.hero-city-sub { font-size: clamp(.95rem, 1.6vw, 1.1rem); color: var(--text-3); line-height: 1.7; max-width: 440px; margin: 0 0 30px; }
/* The CTA row reuses hero.css's shared `.hero-btns` (centered there,
   for the robot hero's centered composition) - scoped back to left-
   aligned here so its left edge lines up with the headline/paragraph
   above it, without touching the shared rule. */
.hero-city-text .hero-btns { justify-content: flex-start; }
/* Same "pinned to dark-mode, this hero is solid black in both themes"
   fix as the headline above, extended to the paragraph and the ghost
   "Get a Quote" button - both were using light-mode token values
   (--text-3, --text-2, --border-mid) tuned for a near-white page, so
   they were going low-contrast/near-invisible against this hero's
   black background. .btn-ghost is shared sitewide (base.css), so this
   is scoped to `.hero-city-text .btn-ghost` specifically - it doesn't
   touch ghost buttons anywhere else on the (correctly light-mode-
   styled) rest of the page. */
[data-theme="light"] .hero-city-sub { color: #6b6560; }
[data-theme="light"] .hero-city-text .btn-ghost { color: #b8b2ab; border-color: rgba(255,255,255,.11); }
[data-theme="light"] .hero-city-text .btn-ghost:hover { color: #f0ede8; border-color: rgba(255,255,255,.2); background: rgba(255,255,255,.04); }

/* ── HEADLINE WORD FLIP ("visions." / "websites.") ────
   "We build" (plain text before the <br>) never moves. Only the second
   line flips - two full words, stacked in the same CSS Grid cell
   (grid-area:1/1 on both), so the track auto-sizes to the WIDER word
   ("websites.") and neither word ever causes a layout shift, in either
   direction, when the other is showing (the brief: "reserve enough
   width/height for both words"). One keyframe animation is shared by
   both items; the second one gets a negative animation-delay of
   exactly half the duration, so it's permanently in anti-phase with
   the first - only one is ever actually visible at a time. */
.hero-city-flip {
  display: inline-grid;
  perspective: 700px;
  vertical-align: top;
}
.hero-city-flip-item {
  grid-area: 1 / 1;
  color: var(--accent);
  white-space: nowrap;
  backface-visibility: hidden;
  animation: heroCityFlipWord 6.5s cubic-bezier(.65,0,.35,1) infinite;
}
.hero-city-flip-item--b { animation-delay: -3.25s; }
.hero-city-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* Hold ~2.75s visible, ~0.5s to flip out, hold hidden through the other
   word's turn, ~0.5s to flip back in - opacity reaches 0 slightly
   BEFORE the rotation hits a literal edge-on 90deg (and back to 1
   slightly AFTER), so the mirrored/edge-on glyph shapes this would
   otherwise briefly show are never actually visible - it reads as a
   smooth flip+fade, not a jarring mirror-flash. The instantaneous
   rotateX(-90deg)->rotateX(90deg) "reset" at 50%/50.01% happens while
   opacity is 0 on both sides of it, so it's invisible too. */
@keyframes heroCityFlipWord {
  0%      { transform: rotateX(0deg);    opacity: 1; }
  42.31%  { transform: rotateX(0deg);    opacity: 1; }
  46.15%  {                              opacity: 0; }
  50%     { transform: rotateX(-90deg);  opacity: 0; }
  50.01%  { transform: rotateX(90deg);   opacity: 0; }
  92.31%  { transform: rotateX(90deg);   opacity: 0; }
  96.15%  {                              opacity: 1; }
  100%    { transform: rotateX(0deg);    opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-city-flip-item { animation: none; }
  .hero-city-flip-item--a { opacity: 1; transform: none; }
  .hero-city-flip-item--b { display: none; }
}

/* ── TEMP EXPERIMENT (design/vigiworks-v2-hero-fullbg branch only) ──
   CITY VIDEO AS FULL-HERO BACKGROUND, revisiting the very first video
   pass before it was pulled back into its own right-side box - see
   this branch's sibling `design/vigiworks-v2` for that current
   (smaller, masked, right-aligned) version. This fills the ENTIRE
   `.hero-city-scene` box again (object-fit:cover, no aspect-ratio
   container, no edge fades) and layers a flat black scrim on top
   (`.hero-city-fade` below) so the video reads as "slightly less
   clear"/dimmer behind the text, per this test's brief - a flat
   overlay rather than the earlier per-edge fade, since the whole
   point here is the video behind everything, not a bounded shape. */
.hero-city { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.hero-city-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
[data-theme="light"] .hero-city-video { opacity: .9; }
/* A flat darkening scrim over the whole video - "slightly less clear",
   not heavily obscured: the city/green activation should still read
   clearly, this just knocks the brightness down a notch so the white/
   green text sitting directly on top of it stays comfortably legible
   everywhere, not just over the video's own naturally-darker areas. A
   touch stronger on the left (behind the text column) than the rest,
   same reasoning as the per-edge fade this replaced. */
.hero-city-fade {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to right, rgba(0,0,0,.72) 0%, rgba(0,0,0,.4) 45%, rgba(0,0,0,.4) 100%);
}

/* Buildings: near-black, subtle per-building shade variation (set via
   inline fill per face in hero-city.js), very thin roof-edge highlight
   only - most of the city stays quiet/dark until a building is
   featured. */
.cb-face { stroke: rgba(255,255,255,.045); stroke-width: .5; }
.cb-roof { stroke: rgba(255,255,255,.07); }

/* A featured building currently mid-story (representative inside,
   transforming, or networking) - a restrained green edge, not a bright
   outline, plus a soft "green life appearing inside a dark building"
   glow. Green here always means "VigiWorks is here" or "this business
   now has an active digital presence", never decoration - everything
   else in the city stays near-black/monochrome. */
.city-building.is-active .cb-roof {
  stroke: rgba(0,255,140,.85); stroke-width: .75;
  filter: drop-shadow(0 0 4px rgba(0,255,140,.5));
  transition: stroke .4s ease, filter .4s ease;
}
.city-building.is-active .cb-left,
.city-building.is-active .cb-right { stroke: rgba(0,255,140,.25); transition: stroke .4s ease; }
.city-building:not(.is-active) .cb-roof { transition: stroke .6s ease, filter .6s ease; }

/* A soft, low green pool of light right at an activated business's
   entrance - "dark building + green life appearing inside it", not a
   neon building. Added/removed in JS alongside .is-active. */
.cb-glow { fill: #00ff8c; opacity: 0; filter: blur(3px); pointer-events: none; transition: opacity .6s ease; }
.cb-glow.is-on { opacity: .22; }

/* Streets: a real rectilinear grid (pedestrians patrol them at runtime
   by reading getPointAtLength() every frame - see hero-city.js's
   People/stepPerson), rendered as a slightly lighter near-black band so
   the camera can actually read BUILDING / STREET / BUILDING, the way a
   miniature city model would - never a literal asphalt texture. */
.city-street { stroke: #1a1d1d; stroke-width: 15; fill: none; pointer-events: none; opacity: .85; }

/* ── BUILDING DETAIL (windows/door) ────────────────────
   Deliberately restrained: near-facade-color by default, so the city
   still reads as mostly black from a distance even with this detail.
   A few windows get a faint neutral highlight (never green); a GREEN
   window only appears via .cb-window--lit, added in JS only while a
   building is actively mid-story and removed again once it resets. */
.cb-window { fill: rgba(255,255,255,.035); stroke: rgba(255,255,255,.05); stroke-width: .35; }
.cb-window--faint { fill: rgba(255,255,255,.09); }
.cb-window--lit {
  fill: rgba(0,255,140,.55); stroke: rgba(0,255,140,.65);
  filter: drop-shadow(0 0 1.5px rgba(0,255,140,.6));
  transition: fill .5s ease, stroke .5s ease;
}
.cb-door { fill: rgba(0,0,0,.4); stroke: rgba(255,255,255,.08); stroke-width: .35; }
.city-building.is-active .cb-door { stroke: rgba(0,255,140,.4); transition: stroke .4s ease; }

/* ── PEOPLE ───────────────────────────────────────────
   Tiny, deliberately plain stick figures (head + torso + two arms +
   two legs) - closer to an architectural-diagram pictogram than a
   character. Ordinary pedestrians stay muted/low-contrast; the one
   VigiWorks representative per story gets a slightly brighter body and
   a small green accent dot so it's recognizable on inspection without
   reading as a glowing mascot. */
.city-ped { pointer-events: none; }
.ped-head, .ped-body, .ped-arm, .ped-leg { fill: none; }
.city-ped .ped-head { fill: rgba(220,222,225,.4); }
.city-ped .ped-body,
.city-ped .ped-arm,
.city-ped .ped-leg { stroke: rgba(220,222,225,.3); stroke-width: .5; }
.city-ped--rep .ped-head { fill: rgba(236,240,238,.75); }
.city-ped--rep .ped-body,
.city-ped--rep .ped-arm,
.city-ped--rep .ped-leg { stroke: rgba(236,240,238,.62); stroke-width: .55; }
.city-ped--rep .ped-accent { fill: #00ff8c; filter: drop-shadow(0 0 2.5px rgba(0,255,140,.8)); }
.city-ped--rep { opacity: 0; transition: opacity .3s ease; }

.city-link {
  stroke: rgba(0,255,140,.5); stroke-width: .75; stroke-linecap: round;
  filter: drop-shadow(0 0 2px rgba(0,255,140,.3));
}
/* A link whose far end is a live, moving person rather than a fixed
   point - its `d` attribute is rewritten every frame in JS, so it gets
   a plain opacity fade instead of the dash-offset "draw-in" the fixed
   .city-link paths use (a changing path length would make that dash
   trick look broken). */
.city-link--tracked { stroke-dasharray: none; }
.city-node { filter: drop-shadow(0 0 4px rgba(0,255,140,.5)); }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 960px) {
  .hero-city-scene .hero-row { gap: 0; }
}
@media (max-width: 640px) {
  .hero-city-scene { min-height: 86vh; padding: 118px 0 24px; align-items: flex-start; }
  .hero-city-scene .hero-row { flex-direction: column; }
  .hero-city-text { max-width: 100%; }
  /* No override needed here for this temp experiment - the base rule's
     inset:0/object-fit:cover already fills whatever height/width this
     breakpoint's `.hero-city-scene` ends up being, full-bleed, same as
     desktop. */
}
