/* ==========================================================================
   Brixzly Blueprint — front-end design system
   ========================================================================== */

:root {
  --build-progress: 0;
  --blueprint: #0a1930;
  --blueprint-deep: #061224;
  --blueprint-line: #5f8fc9;
  --ink: #0b0d10;
  --paper: #f6f7fa;
  --white: #ffffff;
  --signal: #df8f3c;
}

html {
  scroll-behavior: smooth;
  /* Belt-and-suspenders alongside body's overflow-x: clip below — html
     is the box that actually determines the mobile layout viewport, so if
     anything ever forces content wider than the screen again, this keeps
     it from widening/zooming the whole page instead of just clipping it.
     Deliberately `clip`, not `hidden` — `hidden` turns html/body into a
     "scroll container", which breaks `position: sticky` for every sticky
     element on the page (Greenshift's sticky left panels included) since
     browsers then anchor sticky positioning to that container instead of
     the viewport. `clip` still prevents the horizontal swipe/overflow but
     isn't a scroll container, so sticky keeps working normally. */
  overflow-x: clip;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  /* See the overflow-x note on html above — same reasoning applies here. */
  overflow-x: clip;
  background: var(--paper);
}

body.admin-bar .brixzly-header {
  top: calc(1rem + 32px);
}

/* --------------------------------------------------------------------------
   Header — floating frosted pill, sits above everything on every template
   -------------------------------------------------------------------------- */

/* WP wraps the header template part in a plain <header>, which is
   position:static by default — since .brixzly-header itself is the thing
   that's position:fixed, that static wrapper never establishes its own
   stacking context. In that state, the page's main content (itself several
   layers of position:relative wrappers for the sticky/scrollnav sections)
   can end up painted — and hit-tested — ABOVE the fixed header despite its
   z-index:100, silently swallowing clicks/taps on the logo and mobile menu
   toggle. Forcing a stacking context here, one level up, fixes it reliably;
   z-index just needs to clear every other value in this file (currently
   nothing above 100). */
header.wp-block-template-part {
  position: relative;
  z-index: 999;
}

.brixzly-header {
  position: fixed;
  z-index: 100;
  top: 1rem;
  left: clamp(1rem, 3vw, 2.5rem);
  right: clamp(1rem, 3vw, 2.5rem);
  width: auto;
  max-width: none !important;
  align-items: center;
}

.brixzly-brand {
  display: inline-flex;
  flex: 0 0 auto;
}

.brixzly-brand img {
  width: 132px;
  height: auto;
  display: block;
}

/* On the homepage the header loads logo-full-home.svg (orange squares, white
   wordmark baked in — see functions.php's {{BRIXZLY_LOGO_URI}} placeholder),
   since it sits bare over the fixed scene. Just add a shadow for contrast.
   Inner pages keep the default dark mark on their paper background. */
body.home .brixzly-brand img {
  filter: drop-shadow(0 1px 8px rgba(6, 18, 36, 0.4));
}

.brixzly-brand--footer img {
  width: 150px;
}

/* The nav links + CTA live in their own floating frosted pill — separate
   from the bare brand mark — like futureoffinance.peachweb.io's header. */
.brixzly-nav-shell {
  flex: 0 0 auto;
  align-items: center;
  padding: 0.45rem 0.45rem 0.45rem 0.9rem;
  border: 1px solid rgba(10, 25, 48, 0.1);
  border-radius: 10px;
  background: rgba(246, 247, 250, 0.10);
  box-shadow: 0 18px 45px rgba(6, 18, 36, 0.18);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  transition: background 0.3s ease, box-shadow 0.3s ease, backdrop-filter 0.3s ease, -webkit-backdrop-filter 0.3s ease;
}

/**
 * Reusable frosted-glass look (same recipe as .brixzly-nav-shell above).
 * Add "frosted-panel" in a block's Advanced panel > "Additional CSS
 * Class(es)" field to apply this to any container/group/section.
 */
.frosted-panel {
  border: 1px solid rgba(10, 25, 48, 0.1);
  border-radius: 20px;
  background: rgba(246, 247, 250, 0.05);
  box-shadow: 0 18px 45px rgba(6, 18, 36, 0.18);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  transition: background 0.3s ease, box-shadow 0.3s ease, backdrop-filter 0.3s ease, -webkit-backdrop-filter 0.3s ease;
}

.brixzly-nav .wp-block-navigation-item__content,
.brixzly-nav-shell .wp-block-loginout a {
  padding: 0.7rem 0.85rem;
  color: var(--ink);
}

/* Core sets `.wp-block-navigation-item { background-color: inherit; }`
   globally, so every <li> (top-level pill items AND submenu rows alike)
   otherwise paints a flat, square-cornered copy of its container's
   background — which then pokes out past the pill's/panel's rounded
   corners at the first and last item. Kill it (excluding .nav-cta, which
   needs its own solid pill background below) and round the <li> itself to
   match .wp-block-navigation-item__content's radius as a backstop. */
.brixzly-nav .wp-block-navigation-item:not(.nav-cta) {
  background-color: transparent !important;
  border-radius: 10px;
}

/* Orange text fade + a underline that slides in from the left on hover.
   Scoped to skip .nav-cta ("Start for free"), which has its own pill
   background swap below. The Login/out link (`.wp-block-loginout a`) is a
   bare <a>, not a `.wp-block-navigation-item`, so it never matches the
   `:not(.nav-cta) > .wp-block-navigation-item__content` selector above and
   needs to be added to each rule explicitly to get the same treatment. */
.brixzly-nav .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content,
.brixzly-nav-shell .wp-block-loginout a {
  position: relative;
  transition: color 0.25s ease;
}

.brixzly-nav .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content::after,
.brixzly-nav-shell .wp-block-loginout a::after {
  content: "";
  position: absolute;
  left: 0.85rem;
  right: 0.85rem;
  bottom: 0.55rem;
  height: 2px;
  background: var(--signal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
}

.brixzly-nav .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content:hover,
.brixzly-nav-shell .wp-block-loginout a:hover {
  color: var(--signal) !important;
}

.brixzly-nav .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content:hover::after,
.brixzly-nav-shell .wp-block-loginout a:hover::after {
  transform: scaleX(1);
}

/* Sub-menus (dropdowns) — add a "Submenu" block under any header nav item in
   the block editor and it'll pick this up automatically, no per-item setup
   needed. WP core handles the show/hide-on-hover mechanics; this just
   restyles the panel to match the header's frosted-glass look and swaps the
   parent underline hover for a rounded highlight (reads better on rows).
   Core ships its own fallback-background rule in wp-includes:
   `.wp-block-navigation:not(.has-background) .wp-block-navigation__submenu-container
   { background-color: #b9b095 !important }` (shown when the Navigation
   block itself has no explicit background set) — 3 classes deep, !important.
   Selectors below add ".brixzly-nav.wp-block-navigation" (both classes live
   on the same <nav> element) to reach 4 classes, so they win outright on
   specificity alone regardless of stylesheet load order — no reliance on
   loading after core's CSS. */
.brixzly-nav-shell .brixzly-nav.wp-block-navigation .wp-block-navigation__submenu-container {
  border: 1px solid rgba(10, 25, 48, 0.1);
  border-radius: 16px;
  background-color: rgba(246, 247, 250, 0.92) !important;
  box-shadow: 0 18px 45px rgba(6, 18, 36, 0.18);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  padding: 0.4rem;
  min-width: 200px;
  /* Backstop: clips any square-cornered background core (or a future WP
     update) paints on a descendant, so it can never poke past this panel's
     own rounded corners regardless of what's fixed above. */
  overflow: hidden;
}

.brixzly-nav-shell .brixzly-nav.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
  border-radius: 10px;
  padding: 0.65rem 0.9rem;
  white-space: nowrap;
  color: var(--ink) !important;
  transition: background 0.2s ease, color 0.2s ease;
}

.brixzly-nav-shell .brixzly-nav.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content::after {
  content: none;
}

.brixzly-nav-shell .brixzly-nav.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content:hover {
  background-color: rgba(223, 143, 60, 0.14) !important;
  color: var(--signal) !important;
}

/* The chevron toggle that appears on items with a sub-menu. */
.brixzly-nav .wp-block-navigation-submenu__toggle {
  color: var(--ink);
}

.brixzly-nav .nav-cta {
  border-radius: 10px;
  background: var(--ink);
  margin-left: 0.25rem;
}

.brixzly-nav .nav-cta a {
  color: #fff !important;
  padding-inline: 1.2rem;
}

.brixzly-nav .nav-cta:hover {
  background: var(--signal);
}

.brixzly-nav .nav-cta:hover a {
  color: var(--ink) !important;
}

/* --------------------------------------------------------------------------
   Mobile menu — independent off-canvas panel (assets/js/mobile-menu.js)
   -------------------------------------------------------------------------- */

/* Hidden outside the mobile breakpoint by default so no !important fights
   are needed against the desktop nav rules above — see the media query
   further down for where these actually become visible. */
.brixzly-mm-open,
.brixzly-mm {
  display: none;
}

.brixzly-mm-open {
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: #fff;
}

/* The header nav is tuned for the front page, where it floats bare over
   the dark, fixed 3D scene — hence a near-invisible frosted pill and
   white nav text/icons (set via a Site Editor customization directly on
   the shared header template part, so it applies on every page). Most
   other pages have the plain, light --paper background behind the fixed
   header instead, so both need inverting there: a pill with real
   contrast against light content, and dark ink text/icons instead of
   white. !important is needed to beat that same-specificity
   has-white-color utility class coming from the block markup.

   Scoped to `body:not(.home):not(.dark-header)` — core adds the "home"
   body class precisely when is_front_page() is true (matching the same
   check functions.php's {{BRIXZLY_LOGO_URI}} swap already uses for the
   logo variant), and "dark-header" is a small opt-in functions.php adds
   for any other page that also sits on a dark background right under
   the header — see brixzly_dark_header_page_slugs() in functions.php to
   flag one. */
body:not(.home):not(.dark-header) .brixzly-nav-shell {
  background: rgba(11, 13, 16, 0.04);
  border-color: rgba(10, 25, 48, 0.12);
  box-shadow: 0 10px 30px rgba(10, 25, 48, 0.08);
}

body:not(.home):not(.dark-header) .brixzly-nav,
body:not(.home):not(.dark-header) .brixzly-nav .wp-block-navigation-item__content,
body:not(.home):not(.dark-header) .brixzly-nav .wp-block-navigation-submenu__toggle {
  color: var(--ink) !important;
}

/* Mirrors the rule above for the home/dark-header side of the split: makes
   the white nav text an explicit, guaranteed CSS rule instead of relying on
   a `has-white-color` class the block editor happens to have attached to
   the block markup. Any nav item added later (which may not pick up that
   class automatically) still gets the correct color without per-block
   editor fiddling.

   Scoped to `.wp-block-navigation__container > .wp-block-navigation-item`
   (a *direct*-child combinator) so this only reaches top-level pill items
   like "Home"/"Blog"/"My Project" — NOT the items inside a dropdown's
   `.wp-block-navigation__submenu-container`, which sits on its own light
   frosted panel (see the submenu rules above) and needs dark ink text
   regardless of page. Without this scoping, this rule's `body.home`/
   `body.dark-header` prefix out-specifies the submenu's ink-color rule
   above and force white-on-white text inside the dropdown. */
body.home .brixzly-nav .wp-block-navigation__container > .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content,
body.dark-header .brixzly-nav .wp-block-navigation__container > .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content,
body.home .brixzly-nav .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation-submenu__toggle,
body.dark-header .brixzly-nav .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation-submenu__toggle {
  color: #fff !important;
}

/* The rule above (with its extra `body.home`/`body.dark-header` type
   selector) is very slightly more specific than the plain, unscoped
   `:hover { color: var(--signal) !important; }` rule further up — which
   would otherwise silently cancel the hover color change on exactly these
   pages. Re-declared here, scoped the same way, so hover still wins. Also
   restricted to top-level items only, same reasoning as above. */
body.home .brixzly-nav .wp-block-navigation__container > .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content:hover,
body.dark-header .brixzly-nav .wp-block-navigation__container > .wp-block-navigation-item:not(.nav-cta) > .wp-block-navigation-item__content:hover {
  color: var(--signal) !important;
}

/* A Login/out block dropped into the header nav renders as a bare
   `.wp-block-loginout > a` sitting *outside* the `<ul>` that carries
   `has-white-color` — it's a sibling, not a `.wp-block-navigation-item`,
   so none of the nav-item rules above ever touch it. Worse, theme.json's
   sitewide `styles.elements.link` color (ink) applies to plain `<a>` tags
   everywhere except nav items, which core explicitly excludes from that
   rule — but a loginout link isn't excluded, so it falls through to that
   global ink color instead of inheriting white from its `<nav>` ancestor.
   On non-home/non-dark-header pages that global ink default already
   matches what we want, so only the home/dark-header case needs a
   dedicated color override here — the shared padding/underline-hover
   structure it needs everywhere is already handled above, threaded into
   the same rules as the regular nav items. */
body.home .brixzly-nav-shell .wp-block-loginout a,
body.dark-header .brixzly-nav-shell .wp-block-loginout a {
  color: #fff !important;
  text-decoration: none;
}

body.home .brixzly-nav-shell .wp-block-loginout a:hover,
body.dark-header .brixzly-nav-shell .wp-block-loginout a:hover {
  color: var(--signal) !important;
}

/* The "Start for free" CTA is its own solid --ink pill regardless of
   page (see .nav-cta below), so it needs white text on every page —
   including light-background ones, where the ink-color override right
   above would otherwise win here too (it's more specific than the
   existing `.brixzly-nav .nav-cta a` rule further down) and turn it
   ink-on-ink. */
body:not(.home):not(.dark-header) .brixzly-nav .nav-cta .wp-block-navigation-item__content {
  color: #fff !important;
}

/* Our own hamburger button (assets/js/mobile-menu.js) paints its icon via
   `stroke: currentColor`, which would otherwise pick up the forced white
   above through inheritance since SVG stroke doesn't count as an explicit
   color on the icon itself. The panel it opens is always the same dark
   scheme regardless of page (see .brixzly-mm further down), so only the
   *closed* header's button needs to flip color per page. */
body:not(.home):not(.dark-header) .brixzly-nav-shell .brixzly-mm-open {
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   Fixed blueprint scene — the background that "builds itself" on scroll
   -------------------------------------------------------------------------- */

.build-canvas {
  position: fixed;
  z-index: -2;
  inset: 0;
  overflow: hidden;
  /* Doubles as the "sky" showing through/above the transparent WebGL canvas
     — the ground plane + fog inside house-scene.js are colored to blend
     into the lower part of this gradient at the horizon. */
  background: linear-gradient(160deg, var(--blueprint-deep) 0%, var(--blueprint) 55%, #1c1330 100%);
}

/* The real, textured 3D house model (assets/models/house/house.glb) is the
   scene's entire fixed background now — rendered live via WebGL, full
   viewport, "under construction" on its own lawn. house-scene.js frames the
   camera so the house sits right-of-center, clearing the hero copy (which
   sits over the darker, left-hand side of the wash). */
.build-house {
  position: absolute;
  inset: 0;
}

.build-house__canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.build-canvas__wash {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg, rgba(6, 18, 36, 0.82) 0%, rgba(6, 18, 36, 0.42) 45%, rgba(6, 18, 36, 0.12) 75%),
    linear-gradient(0deg, rgba(6, 18, 36, 0.55) 0%, rgba(6, 18, 36, 0) 30%);
}

.build-progress {
  position: absolute;
  z-index: 2;
  right: clamp(1rem, 3vw, 2.5rem);
  bottom: 1.5rem;
  display: grid;
  grid-template-columns: auto minmax(70px, 12vw) 2.3rem auto;
  align-items: center;
  gap: 0.65rem;
  color: var(--white);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.build-progress__bar {
  height: 2px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.25);
}

.build-progress__bar::before {
  content: "";
  display: block;
  width: calc(var(--build-progress) * 100%);
  height: 100%;
  background: var(--signal);
}

.build-progress__value {
  font-variant-numeric: tabular-nums;
}

.build-progress__stage {
  font-style: normal;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.75);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Page dots — right-edge bullet nav for long single-page layouts. Built
   entirely by assets/js/page-dots.js from any ".dot-section[id]" elements
   on the page; nothing to hand-maintain here beyond the look.
   -------------------------------------------------------------------------- */

.page-dots {
  position: fixed;
  z-index: 90;
  right: clamp(1rem, 2.5vw, 2rem);
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
}

.page-dot {
  position: relative;
  display: block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.55);
  transition: background 0.3s ease, border-color 0.3s ease, transform 0.3s ease;
}

/* The visible dot stays a deliberately small 10px, but that's well under
   the ~44px touch target guideline — pad the actual tappable area out
   with an invisible pseudo-element instead of enlarging the dot itself,
   so it's easy to hit on tablet/mobile without changing the look. */
.page-dot::before {
  content: "";
  position: absolute;
  inset: -14px;
}

.page-dot:hover,
.page-dot:focus-visible {
  border-color: var(--signal);
}

.page-dot.is-active {
  background: var(--signal);
  border-color: var(--signal);
  transform: scale(1.4);
}

.page-dot__tip {
  position: absolute;
  right: calc(100% + 0.85rem);
  top: 50%;
  transform: translateY(-50%) translateX(4px);
  padding: 0.32rem 0.75rem;
  border-radius: 999px;
  background: rgba(6, 18, 36, 0.92);
  border: 1px solid rgba(95, 143, 201, 0.35);
  color: #fff;
  font-family: "Space Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: capitalize;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.page-dot:hover .page-dot__tip,
.page-dot:focus-visible .page-dot__tip {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

@media (max-width: 782px) {
  /* Shown on tablet/mobile too now (was hidden entirely) — just tightened
     up a bit for narrower screens: less gap between dots so a longer
     stack of sections still fits comfortably within the viewport height,
     and pulled a touch closer to the edge since there's less spare margin
     to spend on either side. */
  .page-dots {
    right: 0.6rem;
    gap: 0.65rem;
  }

  .page-dot {
    width: 8px;
    height: 8px;
  }

  /* The hover tooltip has nothing to trigger it on a touch device (no
     mouse to hover with), so it'd otherwise just sit permanently
     invisible dead weight — hide it and rely on each dot's aria-label
     for accessibility instead, same as the tooltip already does visually
     on desktop. */
  .page-dot__tip {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   Story scroll sections (front page only)
   -------------------------------------------------------------------------- */

.brixzly-story {
  position: relative;
  padding: 0 !important;
}

.story-scene {
  /* min-height: 110vh; */
  display: flex;
  align-items: center;
  /* padding-top: 7rem !important; */
  /* padding-bottom: 5rem !important; */
}

/* A section built around .scrollnav is usually much taller than the
   viewport (it has to fit 3+ stacked panels) — vertically centering that
   inside a shorter box pushes its top edge above the visible section
   bounds, so jumping to an anchor marker at that top edge lands scrolled
   past the first panel already. Anchor content like this to the top
   instead of centering it. */
.story-scene:has(.scrollnav) {
  align-items: flex-start;
}

.story-hero {
  min-height: 100svh;
  color: var(--white);
}

.scene-grid {
  width: 100%;
  align-items: center;
  gap: clamp(2rem, 8vw, 8rem);
}

.story-copy {
  max-width: 650px;
  margin-left: 0 !important;
}

.hero-heading,
.final-card h2 {
  margin-block: 0.25em;
}

.hero-heading em,
.final-card h2 em,
.story-card h2 em {
  font-family: "Space Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-weight: 400;
  color: var(--signal);
  font-style: normal;
}

.hero-lede {
  max-width: 560px;
  color: rgba(255, 255, 255, 0.85);
}

.story-hero .is-style-outline .wp-block-button__link {
  border-color: rgba(255, 255, 255, 0.6) !important;
  color: #fff !important;
}

.story-hero .is-style-outline .wp-block-button__link:hover {
  border-color: var(--signal) !important;
  color: var(--signal) !important;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1rem;
  font-family: "Space Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.eyebrow::before {
  content: "";
  width: 1.7rem;
  height: 0.55rem;
  border-radius: 99px;
  background: var(--signal);
}

.eyebrow--light {
  color: rgba(255, 255, 255, 0.85);
}

.badge-soon {
  display: inline-block;
  margin-left: 0.6rem;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: rgba(223, 143, 60, 0.16);
  color: var(--signal);
  font-size: 0.65rem;
  vertical-align: middle;
}

.hero-note {
  height: 60vh;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
}

.vertical-label {
  margin-left: auto;
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  writing-mode: vertical-rl;
}

/* "Scroll to build" arrow: a keyframe animation (rather than a plain
   :hover transform) is used deliberately, because the arrow's rotation is
   set via an inline style="transform: rotate(90deg)" attribute on the
   <img> — a plain CSS transform rule can't override that without
   !important, but an animation's per-frame transform value does win over
   an inline (non-!important) style while it's running. translateY comes
   *before* rotate() in each frame so the bounce moves vertically on
   screen — since the arrow is already rotated 90deg, a translateY placed
   *after* rotate() in the transform value would get carried along by that
   rotation and bounce sideways instead. */
.scroll-to-build:hover img {
  animation: brixzly-arrow-bounce 0.6s ease-in-out infinite;
}

@keyframes brixzly-arrow-bounce {
  0%, 100% {
    transform: translateY(0) rotate(90deg);
  }
  50% {
    transform: translateY(6px) rotate(90deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-to-build:hover img {
    animation: none;
  }
}

.story-card,
.final-card {
  position: relative;
  padding: clamp(2rem, 5vw, 5rem) !important;
  border-radius: clamp(1.25rem, 3vw, 2rem);
  box-shadow: 0 30px 90px rgba(6, 18, 36, 0.35);
  transform: translateY(3rem);
  opacity: 0;
  transition: transform 700ms cubic-bezier(.2,.8,.2,1), opacity 700ms ease;
}

.is-current .story-card,
.is-current .final-card {
  transform: translateY(0);
  opacity: 1;
}

/* Generic, reusable version of the exact same fade/slide-up reveal, for
   Greenshift-built sections. Unlike .story-card, this carries no padding,
   shadow, or font styling of its own — just the reveal. Two ways to
   trigger it (see scroll-engine.js), and you only need one:
   - Just add .reveal-in to a block — it's observed directly and reveals
     itself right as it individually scrolls into view. Use this for
     several stacked reveal-in blocks in one big section (scrollnav
     panels, etc.) so each reveals on its own timing.
   - Or wrap it in a .reveal-scene ancestor to have the *ancestor's*
     visibility (~35% on screen) gate the reveal instead — useful when the
     card is meant to hold off until a bigger surrounding scene is mostly
     in view, e.g. a single centered card in a tall section.
   To adjust how snappy the reveal feels: the transition durations below
   control the fade/slide animation itself once triggered; the
   IntersectionObserver threshold/rootMargin in scroll-engine.js control
   how early, relative to scroll position, it triggers in the first
   place — that's usually the one to change if reveal feels "delayed". */
.reveal-in {
  transform: translateY(3rem);
  opacity: 0;
  transition: transform 700ms cubic-bezier(.2, .8, .2, 1), opacity 700ms ease;
}

.reveal-scene.is-current .reveal-in,
.reveal-in.is-current {
  transform: translateY(0);
  opacity: 1;
}

.story-card h2 {
  margin: 0.25em 0;
  font-size: clamp(2.4rem, 5vw, 4.8rem);
}

.story-card--dark {
  grid-column: 1;
  color: #fff;
  background: rgba(6, 18, 36, 0.86);
  border: 1px solid rgba(95, 143, 201, 0.35);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
}

.story-card--paper {
  grid-column: 2;
  color: var(--ink);
  background: rgba(246, 247, 250, 0.92);
  border: 1px solid rgba(246, 247, 250, 0.6);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
}

.story-card--paper .eyebrow,
.story-card--paper .badge-soon {
  color: var(--ink);
}

.story-card--paper .eyebrow::before {
  background: var(--signal);
}

.mini-stats {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
}

.story-card--paper .mini-stats {
  border-top-color: rgba(11, 13, 16, 0.14);
}

.mini-stats p {
  margin: 0;
  font-family: "Space Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* --------------------------------------------------------------------------
   Scrollspy sections — a sticky column of buttons on the left, each linked
   by anchor (href="#some-id") to a matching panel on the right; the button
   whose panel is currently passing the "trigger band" gets an accent
   border. Reusable via classes (see assets/js/scrollnav.js) so it can be
   dropped into any numbered section (01, 02, 03…) without extra JS/CSS.
   -------------------------------------------------------------------------- */

/* Greenshift's Row block wraps its columns in an extra .gspb_row__content
   div — we don't touch its display/columns at all (Greenshift's own
   gspb_row__col--N classes already handle the width split correctly).
   The only thing we force is align-items: stretch, so the (usually
   shorter) buttons column is stretched to the full row height — that's
   what gives .scrollnav-buttons room to actually travel/stick within it
   instead of being stuck flush against its own (short) content. */
.scrollnav > .gspb_row__content {
  align-items: stretch !important;
}

.scrollnav-buttons {
  position: sticky;
  top: 6.5rem; /* clears the floating header pill (fixed, top: 1rem) */
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

body.admin-bar .scrollnav-buttons {
  top: calc(6.5rem + 32px);
}

/* A row of several scrollnav-btn buttons (e.g. the "Meeting Minutes /
   Request for Information / Change Orders / Draw Requests" row) is often
   built as its own nested Greenshift row/column inside .scrollnav-buttons,
   which defaults to flex-wrap: nowrap. On narrow screens the buttons don't
   fit on one line and don't shrink below their padded content, so the
   column (and with it the whole page) gets pushed wider than the screen —
   the classic "page can be swiped sideways a little" mobile bug. Letting it
   wrap is harmless wherever it already fits on one line. */
@media (max-width: 782px) {
  .scrollnav-buttons .wp-block-greenshift-blocks-row-column {
    flex-wrap: wrap;
    row-gap: 0.5rem;
  }
}

/* Your buttons already have their own full styling from the Greenshift
   Button block (padding, background, border-style, radius, etc.), often
   applied via that block's own per-instance scoped CSS — which can
   out-specificity a plain class rule. We only touch border-width/color,
   background and text color for the active state (never border-style,
   padding, or radius), with !important so those specific properties
   reliably win regardless. Adjust to taste; everything else about the
   button (shape, padding, font) is untouched. */
.scrollnav-btn {
  border-width: 2px !important;
  transition: border-color 0.3s ease, background-color 0.3s ease, color 0.3s ease !important;
}

.scrollnav-btn.is-active {
  border-width: 3px !important;
  border-color: var(--signal) !important;
  background-color: rgba(223, 143, 60, 0.18) !important;
  color: var(--signal) !important;
}

/* Greenshift renders the button's visible text in nested spans that often
   have their own explicit color, which wouldn't otherwise inherit the
   `color` change above. */
.scrollnav-btn.is-active .gspb-buttonbox-title,
.scrollnav-btn.is-active .gspb-buttonbox-text {
  color: var(--signal) !important;
}

.scrollnav-panels {
  display: flex;
  flex-direction: column;
  /* Deliberately no `gap` here — Greenshift inserts an empty, id-only
     anchor-marker div (see the global "[id]:not([class])" rule further
     down) right before each panel as a sibling; `gap` would add spacing
     around those too and double up the visual space between panels.
     Margin on the panel itself only adds space above real panels. */
}

.scrollnav-panel {
  margin-top: clamp(3rem, 8vw, 6rem);
  scroll-margin-top: 6.5rem; /* keeps a clicked/keyboard-focused anchor clear of the header */
}

/* Greenshift's anchor-marker convention: a bare `<div id="...">` element
   with no class, used as the actual link/scroll target instead of putting
   the id on a styled container — used for scrollnav panels, page-dots
   sections, and plain in-page nav links alike. Applied globally (not
   scoped to a specific ancestor like .scrollnav-panels/.story-scene/
   .reveal-scene/.dot-section) because scoping it turned out fragile: it
   only takes one section using a different combination of wrapper classes
   to reintroduce the header-overlap bug (see the "ignite" section, which
   used .reveal-scene.dot-section and so missed an earlier .story-scene-
   only version of this rule). This only affects where the viewport lands
   if/when something actually jumps to the id, so it's safe everywhere. */
[id]:not([class]) {
  scroll-margin-top: 6.5rem;
}

body.admin-bar [id]:not([class]) {
  scroll-margin-top: calc(6.5rem + 32px);
}

@media (max-width: 782px) {
  /* Just drop the sticky behavior on mobile — leave everything else
     (intro copy stacked above the button row) exactly as on desktop. */
  .scrollnav-buttons {
    position: static;
  }
}

.scene-final {
  /* min-height: 125vh; */
  align-items: flex-end;
}

.final-card {
  width: min(100%, 1000px);
  margin: 0 auto 8vh;
  text-align: center;
  /* Was var(--ink) back when this card had its own opaque light
     background (see the commented-out rule below). It now sits directly
     over the fixed 3D scene's dark wash/fog, which never lightens, so the
     card needs light text instead — dark ink on a dark backdrop was
     invisible (most obviously on .eyebrow, which has no color of its own
     and has its accent dash hidden below, leaving nothing to see). */
  color: rgba(255, 255, 255, 0.92);
  /* background: rgba(246, 247, 250, 0.96); */
}

.final-card .wp-block-buttons {
  justify-content: center;
}

/* --------------------------------------------------------------------------
   Buttons & links (global)
   -------------------------------------------------------------------------- */

.wp-block-button__link {
  transition: background-color 200ms ease, color 200ms ease, border-color 200ms ease;
}

.is-style-outline .wp-block-button__link {
  border: 2px solid var(--ink);
  background: transparent !important;
  color: var(--ink) !important;
}

.is-style-outline .wp-block-button__link:hover {
  border-color: var(--signal);
  color: var(--signal) !important;
}

/* --------------------------------------------------------------------------
   Standard pages (blog index, single, page, 404, search)
   -------------------------------------------------------------------------- */

.brixzly-page {
  position: relative;
  z-index: 1;
  background: var(--paper);
  min-height: 60vh;
}

.post-grid.wp-block-post-template {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
  list-style: none;
  padding: 0;
}

.post-card {
  padding: 1.25rem !important;
  border-radius: 1.25rem;
  background: #fff;
  border: 1px solid rgba(10, 25, 48, 0.08);
  box-shadow: 0 20px 50px rgba(10, 25, 48, 0.06);
}

.post-card .eyebrow {
  margin-top: 1rem;
}

.brixzly-pagination {
  margin-top: 3rem;
  justify-content: center;
}

.brixzly-search input[type="search"] {
  border-radius: 999px;
  border: 2px solid var(--ink);
  padding: 0.8rem 1.4rem;
}

.brixzly-search .wp-block-search__button {
  border-radius: 999px !important;
}

.brixzly-404 {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.brixzly-footer {
  position: relative;
  z-index: 5;
  padding-top: 4rem !important;
  padding-bottom: 2.5rem !important;
  background: var(--blueprint-deep);
  color: rgba(255, 255, 255, 0.75);
}

/* The footer's background (--blueprint-deep) is always dark, on every
   page, so it always loads logo-full-home.svg (orange squares + white
   wordmark baked in — same file the homepage header uses) instead of the
   default dark-ink mark. No color-fixing filter needed since that file's
   colors are already correct for a dark background. */

.footer-tag {
  max-width: 360px;
  margin-top: 0.75rem;
  color: rgba(255, 255, 255, 0.6);
}

.footer-nav .wp-block-navigation-item__content {
  padding: 0.4rem 0.8rem;
  color: rgba(255, 255, 255, 0.75) !important;
}

.footer-nav a:hover {
  color: var(--signal) !important;
}

.footer-rule {
  margin: 2.5rem 0;
  border-color: rgba(255, 255, 255, 0.14) !important;
}

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 782px) {
  body.admin-bar .brixzly-header {
    top: calc(0.65rem + 46px);
  }

  .brixzly-header {
    top: 0.65rem;
    left: 0.65rem;
    right: 0.65rem;
  }

  .brixzly-brand img {
    width: 108px;
  }

  /* Core's own mobile hamburger/close buttons and overlay are replaced
     entirely by assets/js/mobile-menu.js's independent ".brixzly-mm"
     panel (see below) — hide core's toggle so it can never be triggered. */
  .brixzly-nav .wp-block-navigation__responsive-container-open {
    display: none !important;
  }

  .brixzly-mm-open {
    display: inline-flex;
  }

  /* ── Panel root — fixed to the viewport and appended straight to
     <body> by mobile-menu.js, so it's never affected by
     .brixzly-nav-shell's own backdrop-filter/transform (which would
     otherwise become the containing block for this fixed element and
     shrink it down to the pill instead of the full viewport). */
  .brixzly-mm {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 9999;
    visibility: hidden;
    pointer-events: none;
  }

  .brixzly-mm.is-open {
    visibility: visible;
    pointer-events: auto;
  }

  .brixzly-mm-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(6, 10, 20, 0.55);
    opacity: 0;
    transition: opacity 0.3s ease;
  }

  .brixzly-mm.is-open .brixzly-mm-backdrop {
    opacity: 1;
  }

  /* Same dark "blueprint navy" family used elsewhere in this file
     (.story-card--dark, .page-dot__tip) so it reads as part of the
     theme rather than a stock overlay — and, being an independent panel
     appended to <body>, it needs no !important fights against the
     light-page header rules above: nothing here is a descendant of
     .brixzly-nav, so none of those selectors can reach it. */
  .brixzly-mm-panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(88vw, 380px);
    background: linear-gradient(160deg, #06122f 0%, #0a1930 55%, #1c1330 100%);
    display: flex;
    flex-direction: column;
    box-shadow: -8px 0 44px rgba(0, 0, 0, 0.35);
    transform: translateX(100%);
    transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .brixzly-mm.is-open .brixzly-mm-panel {
    transform: translateX(0);
  }

  /* ── Head: logo + close ── */
  .brixzly-mm-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1.1rem 1.4rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
    flex-shrink: 0;
  }

  .brixzly-mm-logo {
    display: inline-flex;
    align-items: center;
  }

  .brixzly-mm-logo img {
    width: 110px;
    height: auto;
    display: block;
  }

  .brixzly-mm-close {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.35rem;
    color: #fff;
    line-height: 1;
    flex-shrink: 0;
    transition: color 0.2s ease;
  }

  .brixzly-mm-close:hover {
    color: var(--signal);
  }

  /* ── Nav list ── */
  .brixzly-mm-nav {
    flex: 1;
    padding: 0.5rem 1.4rem 1.5rem;
  }

  .brixzly-mm-list,
  .brixzly-mm-sublist {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .brixzly-mm-item {
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  }

  .brixzly-mm-item:last-child {
    border-bottom: none;
  }

  .brixzly-mm-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .brixzly-mm-link {
    flex: 1;
    display: block;
    color: #fff;
    text-decoration: none;
    font-size: 1.1rem;
    font-weight: 600;
    padding: 1rem 0;
    transition: color 0.2s ease;
  }

  .brixzly-mm-link:hover {
    color: var(--signal);
  }

  .brixzly-mm-item.is-current > .brixzly-mm-link,
  .brixzly-mm-item.is-current > .brixzly-mm-row > .brixzly-mm-link {
    color: var(--signal);
  }

  .brixzly-mm-toggle {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.5rem;
    margin-left: 0.5rem;
    color: rgba(255, 255, 255, 0.6);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    transition: transform 0.25s ease, color 0.2s ease;
  }

  .brixzly-mm-item.is-open > .brixzly-mm-row > .brixzly-mm-toggle {
    color: var(--signal);
    transform: rotate(180deg);
  }

  .brixzly-mm-sublist {
    display: none;
    border-left: 2px solid rgba(223, 143, 60, 0.35);
    margin: 0 0 0.6rem 0.1rem;
    padding: 0.15rem 0 0.4rem 1rem;
  }

  .brixzly-mm-item.is-open > .brixzly-mm-sublist {
    display: block;
  }

  .brixzly-mm-sublist .brixzly-mm-item {
    border-bottom: none;
  }

  .brixzly-mm-sublist .brixzly-mm-link {
    font-size: 0.95rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.65);
    padding: 0.55rem 0.25rem;
  }

  .brixzly-mm-sublist .brixzly-mm-link:hover {
    color: var(--signal);
  }

  /* ── Footer: CTA + Sign In placeholder + social share ── */
  .brixzly-mm-foot {
    margin-top: auto;
    padding: 1.25rem 1.4rem calc(1.25rem + env(safe-area-inset-bottom));
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }

  .brixzly-mm-cta {
    display: block;
    text-align: center;
    background: var(--signal);
    color: var(--ink);
    font-size: 1.05rem;
    font-weight: 700;
    text-decoration: none;
    border-radius: 10px;
    padding: 0.95rem 1.5rem;
  }

  /* Placeholder until Brixzly has real account sign-in — see the "Sign
     In" comment in assets/js/mobile-menu.js. */
  .brixzly-mm-signin {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    align-self: flex-start;
    color: var(--signal);
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-decoration: none;
    border: 1px solid rgba(223, 143, 60, 0.4);
    border-radius: 8px;
    padding: 0.55rem 1rem;
    transition: border-color 0.2s ease, background 0.2s ease;
  }

  .brixzly-mm-signin:hover {
    border-color: var(--signal);
    background: rgba(223, 143, 60, 0.08);
  }

  .brixzly-mm-share-label {
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.45);
    margin-bottom: -0.4rem;
  }

  .brixzly-mm-share {
    display: flex;
    align-items: center;
    gap: 0.6rem;
  }

  .brixzly-mm-share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 50%;
    background: none;
    color: rgba(255, 255, 255, 0.7);
    cursor: pointer;
    text-decoration: none;
    transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
  }

  .brixzly-mm-share-btn:hover {
    color: var(--signal);
    border-color: var(--signal);
    background: rgba(223, 143, 60, 0.08);
  }

  .brixzly-mm-share-copy.is-copied {
    color: var(--signal);
    border-color: var(--signal);
    background: rgba(223, 143, 60, 0.14);
  }

  .scene-grid {
    grid-template-columns: 1fr !important;
  }

  .story-scene {
    /* min-height: 100svh; */
    align-items: flex-end;
    padding-bottom: 5rem !important;
  }

  .story-hero {
    align-items: center;
  }

  .story-copy {
    margin-top: 6rem;
    padding: 1.5rem;
    border-radius: 1.5rem;
    background: rgba(6, 18, 36, 0.55);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
  }

  .hero-heading {
    font-size: clamp(3rem, 15vw, 4.6rem) !important;
  }

  .hero-note,
  .scene-grid > .wp-block-spacer {
    display: none;
  }

  .story-card--dark,
  .story-card--paper {
    grid-column: 1;
  }

  .story-card,
  .final-card {
    padding: 1.75rem !important;
    border-radius: 1.25rem;
  }

  .story-card h2 {
    font-size: clamp(2.4rem, 11vw, 3.4rem);
  }

  .build-progress {
    left: 1rem;
    right: 1rem;
    grid-template-columns: auto 1fr 2.3rem;
  }

  .build-progress__stage {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .story-card,
  .final-card {
    transform: none;
    transition: none;
  }

  .story-card,
  .final-card {
    opacity: 1;
  }

  .page-dot,
  .page-dot__tip {
    transition: none;
  }
}
