/* =============================================================================
   core.css — Testani Consulting
   Brand, layout, and component styles.
   Load order: normalize.css → core.css
   ============================================================================= */

/* === Font === */

@font-face {
  font-family: 'Inter';
  src: url('../fonts/Inter-VariableFont_slntwght.woff2?cb=2081346956cb') format('woff2');
  font-weight: 100 900;
  font-style: oblique 0deg 10deg;
  font-display: swap;
}

/* === Variables === */

/* Tokens cover the brand palette and any colour used in two or more rules.
   Deliberate one-off accents (tab-state ramp, placeholder, intro copy) stay inline. */

:root {
  --gold: #e7c987;
  --gold-soft: #e7c98780;
  --bg: #14110a;
  --surface-white: #fff;
  --surface-light: #eee;
  --text-light: #fff;
  --text-dark: #14110a;
  --text-body: #333;
  --text-muted: #bebebe;
  --contact-success-bg: #dddddd;
  --contact-error-bg: #ffdede;
  --drawer-rail-width: 3rem;
  --drawer-close-size: 2.5rem;
  --drawer-close-inset: calc((var(--drawer-rail-width) - var(--drawer-close-size)) / 2);
  --drawer-rail-zone: 2.75rem;
  --pane-padding: 1.5rem;
  --divider-color: #ccc;
  --radius-pill: 9999px;
  --transition-fast: .2s;
  --drawer-transition: 0.5s cubic-bezier(0.645, 0.045, 0.355, 1);
}

/* Reduced motion: the drawer's full-panel slide is the one functional
   transition large enough to act as a vestibular trigger; collapse it to an
   instant state change. Decorative motion (hero resolve, circuit field, ASK)
   is separately opt-in via prefers-reduced-motion: no-preference blocks. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --drawer-transition: 0s;
  }
}

/* === Reset === */

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* === Root Typography === */

html {
  height: 100%;
  font-size: clamp(15px, calc(13px + 0.3vw), 21px);
}

/* === Base Reset & Body === */

body,
h1, h2, h3, h4, h5, h6,
p, div, span, a {
  font-family: 'Inter', sans-serif;
}

body {
  color: var(--text-light);
  background-color: var(--bg);
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
  min-height: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* === Headings === */

h1 {
  font-variation-settings: "wght" 200;
  font-weight: 200;
  letter-spacing: -.05em;
  margin-top: 0;
  margin-bottom: 0;
  font-size: 4rem;
  line-height: 1;
}

h2 {
  font-variation-settings: "wght" 252;
  font-weight: 252;
  letter-spacing: -.05em;
  border-bottom: 2px solid var(--divider-color);
  margin-top: 0;
  margin-bottom: 2rem;
  padding-bottom: 1rem;
  font-size: 3rem;
  line-height: 1.2;
}

h3 {
  font-variation-settings: "wght" 200;
  font-weight: 200;
  margin-top: 0;
  margin-bottom: 0;
  font-size: 2rem;
  line-height: 1.2;
}

p {
  margin-bottom: 0;
}

/* Global media default — responsive width, middle alignment. Logo, icon, and
   client-logo layout depends on it. */
img {
  max-width: 100%;
  vertical-align: middle;
  display: inline-block;
}

label {
  margin-bottom: .25rem;
  font-weight: 500;
  display: block;
}

/* === Focus / Accessibility === */

*[tabindex]:focus-visible {
  outline: 0.125rem solid var(--gold);
  outline-offset: 0.125rem;
}

.tab-button:focus-visible,
.vertical-tab-link:focus-visible,
.close-button:focus-visible {
  outline: 0.125rem solid var(--gold);
  outline-offset: 0.125rem;
}

/* === Utility Pages (404 etc.) === */

.error-page {
  width: 100%;
  min-height: 100vh;           /* fallback for browsers without svh support */
  min-height: 100svh;          /* stable small-viewport floor: never re-resolves
                                   when mobile browser UI or overlays expand and
                                   retract, so the page never repaints beneath
                                   them; height stays auto */
  display: grid;
  place-items: center;
  padding: max(1.25rem, env(safe-area-inset-top))
           max(1.25rem, env(safe-area-inset-right))
           max(1.25rem, env(safe-area-inset-bottom))
           max(1.25rem, env(safe-area-inset-left));
  color: var(--text-light);
  background-color: var(--bg);
}

/* No-JavaScript notice: without scripts the drawer (and the contact form
   inside it) is unreachable, so say so plainly instead of failing silently. */
.noscript-notice {
  margin: 0;
  padding: 0.75rem 1.25rem;
  background: #2a2417;
  color: #f5f0e6;
  border-bottom: 1px solid #c9a44e;
  font-size: 0.95rem;
  text-align: center;
}

/* The 404 page's only heading is its document h1; render it at the site's
   h3 scale so the semantic fix changes no pixels. */
.error-wrapper h1 {
  letter-spacing: normal;
  font-size: 2rem;
  line-height: 1.2;
}

.error-wrapper {
  width: min(100%, 24rem);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(1rem, 3vh, 1.5rem);
  text-align: center;
}

.error-image {
  width: clamp(5rem, 18vh, 10rem);
  height: clamp(5rem, 18vh, 10rem);
  object-fit: contain;
}

@media screen and (min-width: 768px) {
  .error-wrapper {
    transform: translateY(-4vh);
  }
}

.error-wrapper .button {
  background-color: var(--surface-white);
  color: var(--text-dark);
}

.error-wrapper .button:hover {
  background-color: var(--gold);
  color: var(--text-dark);
}

/* === Button === */

.button {
  color: var(--text-light);
  text-align: center;
  letter-spacing: .1em;
  text-transform: uppercase;
  background-color: transparent;
  border: 0;
  border-radius: var(--radius-pill);
  padding: .75rem 1.9rem;
  font-weight: 600;
  transition: color var(--transition-fast), background-color var(--transition-fast);
  display: inline-block;
  text-decoration: none;
  cursor: pointer;
}

.button:hover {
  color: var(--text-dark);
  background-color: var(--gold);
}


#contact-form .button {
  background-color: #000;
  color: #fff;
  border-radius: var(--radius-pill);
  padding: .75rem 1.9rem;
  min-width: 11rem;
}

#contact-form .button:hover {
  background-color: var(--gold);
  color: var(--text-dark);
}

.hero {
  color: var(--text-light);
  min-height: 100svh;
  padding: 3rem;
  display: flex;
}

.hero-content {
  flex-direction: column;
  justify-content: space-between;
  align-self: stretch;
  align-items: stretch;
  width: 100%;
  display: flex;
}


/* === Layout === */


.flex-justify {
  justify-content: space-between;
  display: flex;
  position: relative;
}

.flex-justify.hero-lead {
  z-index: 1;
  align-items: flex-end;
}

/* === Hero Text === */

.hero-text {
  gap: 1rem;
  text-shadow: 0 0 .25rem #00000080, 0 0 1rem #00000080;
  flex-direction: column;
  align-items: flex-start;
  width: 50%;
  display: flex;
}

/* === Navigation === */

.nav-wrapper {
  background-image: linear-gradient(#0000, #fff3);
  border-radius: .5rem;
  list-style-type: none;
  overflow: hidden;
  box-shadow: inset 0 0 5px #ffffff80, 0 0 0 1px var(--gold);
}

.tab-button,
.vertical-tab-link,
.close-button {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.tab-button {
  color: var(--text-light);
  background-color: transparent;
  letter-spacing: .1em;
  padding: .6rem 4rem .6rem 1rem;
  text-decoration: none;
  transition: color var(--transition-fast), background-color var(--transition-fast);
  display: block;
  width: 100%;
  box-shadow: 0 -2px var(--gold-soft);
}

.tab-button:hover {
  color: var(--text-dark);
  background-color: #ffffffbf;
}

/* === Eyebrow === */

.eyebrow {
  text-transform: uppercase;
  border-radius: var(--radius-pill);
  padding: .4rem .7rem;
  font-size: .85rem;
  font-weight: 600;
  line-height: 1;
  box-shadow: 0 0 0 2px var(--gold);
}

/* === Body === */

.page-shell {
  background-color: var(--bg);
  background-image: radial-gradient(circle at 100% 0, #0006, #0e0e0e00);
  height: 100svh;
  overflow: hidden;
}

.site-shell {
  position: relative;
  overflow: hidden;
  overflow: clip;
}

/* === Drawer === */

.drawer {
  z-index: 8;
  background-color: var(--surface-white);
  width: 48%;
  max-width: 60ch;
  height: 100dvh;
  display: flex;
  position: absolute;
  inset: 0 0 auto auto;
  overflow: hidden;
  transform: translateX(100%);
  transition: transform var(--drawer-transition);
}

.drawer.is-open {
  transform: translateX(0%);
}

.tab-rail {
  writing-mode: vertical-rl;
  order: 1;
  justify-content: center;
  align-self: flex-end;
  width: var(--drawer-rail-width);
  height: calc(100dvh - var(--drawer-rail-zone));
  display: flex;
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.tab-rail::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

.pane-area {
  background-color: var(--surface-white);
  flex: 1;
  overflow: hidden;
  box-shadow: 1px 0 var(--divider-color);
}

.vertical-tab-link {
  color: var(--text-muted);
  text-align: left;
  letter-spacing: .1em;
  white-space: nowrap;
  background-color: var(--surface-white);
  padding: 1rem .5rem;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--transition-fast), background-color var(--transition-fast);
  box-shadow: 0 -1px var(--divider-color);
}

/* Tab-state text ramp (muted -> hover -> active) is a deliberate three-step scale. */
.vertical-tab-link:hover {
  color: #8a8a8a;
  background-color: var(--surface-light);
}

.vertical-tab-link.is-active {
  color: #2b2b2b;
  background-color: var(--surface-white);
  /* Panel-facing edge: completes the active tab's box, flush with the
     top/bottom dividers. */
  border-left: 1px solid var(--divider-color);
}

.vertical-tab-link:last-child {
  box-shadow: 0 1px var(--divider-color), 0 -1px var(--divider-color);
}

.pane-content {
  color: var(--text-body);
  height: 100dvh;
  padding: var(--pane-padding);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.pane-content::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

.tab-pane {
  display: none;
}

.tab-pane.is-active {
  display: block;
}

.close-button {
  z-index: 8;
  color: var(--text-body);
  justify-content: center;
  align-items: center;
  width: var(--drawer-close-size);
  height: var(--drawer-close-size);
  padding: .75rem;
  text-decoration: none;
  transition: padding-top var(--transition-fast), padding-right var(--transition-fast), padding-left var(--transition-fast), padding-bottom var(--transition-fast);
  display: flex;
  position: absolute;
  inset: 0 var(--drawer-close-inset) auto auto;
}

.drawer-inner {
  background-color: var(--surface-white);
  width: 100%;
  height: 100%;
  display: flex;
  position: absolute;
  inset: 0 0 auto auto;
}


/* === Social Links === */

.social-links {
  gap: 2rem;
  display: flex;
}

.social-link {
  color: var(--text-light);
  text-decoration: none;
  transition: transform var(--transition-fast);
  display: inline-block;
  max-width: 100%;
}

.social-link:hover {
  transform: scale(1.25);
}

/* === Contact Fields === */

.contact-field {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 3rem;
  margin-top: 0;
  margin-bottom: 1.5rem;
  padding: .5rem .75rem;
  font-size: .875rem;
  line-height: 3rem;
  color: var(--text-body);
  background-color: var(--surface-white);
  border: 1px solid var(--divider-color);
  border-radius: 0;
}

.contact-field::placeholder {
  color: #999;
}

.contact-field:focus {
  border-color: var(--gold);
  outline: 0;
}

.contact-field.multi {
  height: 20rem;
  padding: .75rem;
  line-height: 1.5;
  resize: none;
  white-space: pre-wrap;
  word-break: break-word;
}

.contact-label {
  margin-bottom: .4rem;
  display: block;
  font-size: .85rem;
  font-weight: 500;
}

/* === Contact Form & Feedback === */

.contact-form-shell {
  margin: 0 0 1rem;
}

.contact-success {
  display: none;
  padding: 1.25rem;
  text-align: center;
  background-color: var(--contact-success-bg);
}

.contact-error {
  display: none;
  margin-top: .75rem;
  padding: .75rem;
  background-color: var(--contact-error-bg);
}

/* === Honeypot (anti-spam) === */

.contact-pot {
  position: absolute;
  left: -9999px;
  top: -9999px;
  width: 0;
  height: 0;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

/* === Steps === */

.step {
  gap: 2rem;
  display: flex;
}

.step:last-child {
  align-items: flex-start;
}

.step-body {
  flex: 1;
}

.step-number {
  position: relative;
  z-index: 1;
  text-align: center;
  background-color: var(--surface-white);
  border: 1px solid #000;
  border-radius: var(--radius-pill);
  width: 4rem;
  height: 4rem;
  font-size: 2rem;
  font-weight: 200;
  line-height: 4rem;
}

.step-track {
  position: relative;
  width: 4rem;
}

.step-track::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  background-color: #000;
  transform: translateX(-50%);
  pointer-events: none;
}

.step:last-child .step-track::before {
  display: none;
}

.step-title {
  font-size: 1.5rem;
  font-weight: 300;
  line-height: 4rem;
}

.step-text,
.about-body {
  margin-bottom: 2rem;
}

.about-body h3 {
  margin-bottom: .5rem;
  font-size: 1.5rem;
  font-weight: 400;
}

.about-body p {
  margin-bottom: 2rem;
  padding-left: 1rem;
  line-height: 1.75;
}

/* === Client Logos === */

.logo-grid {
  flex-wrap: wrap;
  display: flex;
  /* Top + left live on the container; right + bottom live on each cell,
     so every internal grid line is drawn exactly once (no doubling). */
  border-top: 1px solid #ccc;
  border-left: 1px solid #ccc;
}

.logo-block {
  box-sizing: border-box;
  border-right: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  justify-content: center;
  align-items: center;
  width: 50%;
  padding: 2rem;
  display: flex;
}

.client-logo {
  object-fit: contain;
  height: 3rem;
}

/* === Miscellaneous === */

.close-icon {
  display: block;
  width: 100%;
  height: 100%;
  fill: currentColor;
  pointer-events: none;
}

.logo {
  height: 6rem;
}

.social-logo {
  width: 1.5rem;
  height: 1.5rem;
}


/* === Intro Copy === */

.intro-copy {
  color: #d0cfce;
  max-width: 36em;
}

/* === Hero Heading === */

.hero-heading {
  max-width: 11em;
  text-wrap: balance;
}

/* === Section Title === */

.section-title {
  font-variation-settings: "wght" 400;
  font-weight: 400;
  text-transform: uppercase;
  margin-bottom: .5rem;
  font-size: 1.5rem;
}

/* === Hero circuit-trace field === */

.hero-circuit {
  pointer-events: none;
  /* Fixed, large-viewport-sized background layer. Fixing the canvas to the
     viewport at 100lvh (the largest viewport height) guarantees full coverage
     in every mobile toolbar state with no dark band at the bottom, and a fixed
     element never adds to scroll height. Explicit width/height also override
     the replaced-element intrinsic size, so the JS buffer attributes
     (clientW x DPR) drive resolution only, not layout. */
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100lvh;
}

/* =============================================================================
   Responsive — Tablet (max 1024px)
   ============================================================================= */

@media screen and (max-width: 1024px) {

  h1 {
    margin-bottom: 1rem;
    font-size: 3rem;
  }

  h2 {
    font-size: 2.25rem;
  }


  .site-shell {
    height: 100svh;
    overflow: hidden;
  }


  .hero-text {
    width: 60%;
  }

  .page-shell {
    height: 100svh;
  }

  .drawer {
    width: 60%;
    position: fixed;
  }

  .vertical-tab-link {
    padding: 1.25rem .75rem;
  }

  .about-body h3 {
    font-size: 1.25rem;
  }

  .about-body p {
    font-size: .875rem;
  }
}

/* =============================================================================
   Responsive — Mobile / Narrow Screens (max 768px)
   ============================================================================= */

@media screen and (max-width: 768px) {

  h1 { font-size: 2.5rem; }
  h2 { font-size: 2rem; }
  h3 { font-size: 1.5rem; }
  .error-wrapper h1 { font-size: 1.5rem; }


  .site-main {
    padding: 1.3rem;
  }


  .hero {
    padding: 0;
  }

  .hero-content {
    border-radius: 0;
  }

  .flex-justify.hero-lead {
    gap: 2rem;
    flex-direction: column;
    align-items: flex-start;
  }

  .hero-text,
  .drawer {
    width: 100%;
  }

  .hero-heading {
    /* Optical centering between the eyebrow and the intro copy on mobile:
       the copy's line-height adds half-leading above its first line, leaving
       the heading sitting visually high. A quarter-rem top margin equalizes
       the gaps. Desktop keeps its reference proportions untouched. */
    margin-top: .25rem;
    margin-bottom: 0;
  }
}

/* =============================================================================
   Responsive — Mobile Portrait (max 480px)
   ============================================================================= */

@media screen and (max-width: 480px) {


  .site-main {
    height: 100%;
  }

  .hero {
    height: 100%;
    min-height: 0;
  }

  .hero-content {
    justify-content: flex-start;
  }

  .flex-justify.masthead {
    order: 1;
    gap: 1.25rem 1rem;
    flex-direction: row;
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .hero-content::before {
    content: '';
    display: block;
    order: 2;
    flex-grow: 85;
    flex-shrink: 0;
    flex-basis: 0;
  }

  .flex-justify.hero-lead {
    order: 3;
  }

  .hero-content::after {
    content: '';
    display: block;
    order: 4;
    flex-grow: 15;
    flex-shrink: 0;
    flex-basis: 0;
  }

  .nav-wrapper {
    background-image: linear-gradient(45deg, #0000, #fff3);
  }

  .tab-button {
    padding: .6rem 4rem .6rem 1rem;
    font-size: 1rem;
  }

  .eyebrow {
    font-size: .675rem;
  }

  .vertical-tab-link {
    padding-top: .5rem;
    padding-bottom: .5rem;
    font-size: .875rem;
  }

  .about-body p {
    padding-left: 0;
  }

  .logo {
    height: 3.25rem;
  }

  .hero-heading {
    font-size: 1.75rem;
  }

  .intro-copy {
    font-size: .875rem;
    max-width: 26em;
  }

  .hero-text {
    padding-bottom: 1.15rem;
  }
}

/* =============================================================================
   Responsive — Short Narrow Screens (max 480px wide, max 700px tall)
   ============================================================================= */

@media screen and (max-width: 480px) and (max-height: 700px) {

  .flex-justify.masthead {
    row-gap: 1rem;
  }

  .intro-copy {
    padding-bottom: 1rem;
  }

}

/* =============================================================================
   Responsive - Small / Short Portrait. Use an intentional stacked masthead
   (logo above, full-size nav below) instead of letting the row flex-wrap in two
   cases: (1) any screen 371px wide or narrower, where the full-size nav cannot
   sit beside the logo at all (it wraps at 368px and below, fits from 372px), and
   (2) short SE-style screens up to 389px wide and 700px tall, where it could fit
   beside but feels cramped. Taller narrow phones (e.g. 375x812) and modern
   widths (390px and up) keep the mirrored logo / nav layout. The nav keeps its
   full landscape proportions; only the masthead axis and gap change.
   ============================================================================= */

@media screen and (max-width: 371px),
       screen and (max-width: 389px) and (max-height: 700px) {

  .flex-justify.masthead {
    flex-direction: column;
    align-items: flex-start;
    gap: 2rem;
  }

}

/* =============================================================================
   Responsive — Mobile Landscape orientation
   Source-ordered last so these rules win the cascade naturally.
   Scoped to orientation:landscape + max-height:430px (phones only, not tablets)
   ============================================================================= */

@media screen and (orientation: landscape) and (max-height: 430px) {

  /* Landscape phones are very short (≤430px tall). The full-size hero — 6rem
     logo, 3rem padding, 3rem heading — overflows and pushes the intro copy and
     social icons off-screen. Compact the composition so the whole hero fits the
     viewport the way portrait and desktop do. height:auto + overflow:auto stay
     as a graceful fallback for the very shortest devices, but at typical
     landscape heights nothing is cut off and nothing needs scrolling. */

  html,
  body,
  .page-shell {
    height: auto;
    overflow: auto;
  }

  .site-shell {
    height: auto;
    min-height: 100svh;
    overflow: auto;
  }

  .site-main {
    height: auto;
    overflow: visible;
    padding: 0;
  }

  .hero {
    height: auto;
    min-height: 100svh;
    /* Bottom padding and the hero-text rhythm are the two largest avoidable
       consumers of height here. Measured budget at 855x330: logo 50.6 + gaps
       19.5 + eyebrow 22.9 + heading 58.4 + copy 61.3 + links 24.4 = 283.8, so
       the frame only fits once padding and gaps are compacted. These values
       bring the required height down far enough that the hero fits every
       common landscape viewport from 320px tall upward; below that the
       height:auto + overflow:auto fallback above still scrolls gracefully. */
    padding: 1rem 1.75rem 1.5rem;
  }

  .hero-content {
    gap: 1.25rem;
  }

  .flex-justify.hero-lead {
    gap: .6rem;
  }

  .hero-text {
    gap: .75rem;
    width: 60%;
  }

  .logo {
    height: 3.25rem;
  }

  .eyebrow {
    font-size: .675rem;
  }

  .hero-heading {
    /* Match the portrait heading exactly: same size and the same .25rem optical
       top margin that centers it between the eyebrow and the intro copy. */
    font-size: 1.75rem;
    margin-top: .25rem;
    margin-bottom: 0;
  }

  .intro-copy {
    font-size: .875rem;
    max-width: 26em;
  }

  /* Match the portrait (max-480) nav-button size exactly. Landscape phones fall
     in the 1024 tablet band, which pads buttons to 1.25rem; portrait overrides
     that back to .6rem, so without this the buttons render larger in landscape
     than in portrait. Restore the portrait dimensions for a consistent target. */
  .tab-button {
    padding: .6rem 4rem .6rem 1rem;
    font-size: 1rem;
  }

  /* Take the nav out of flow to the top-right. With portrait-size buttons the
     four-item stack is tall; left in flow it pushes the hero copy down and
     overflows short landscape viewports once the browser's bars reduce usable
     height. Out of flow it sits top-right while the copy sits bottom-left, so
     the hero fits with comfortable margin and no scroll. The z-index lifts it
     above .hero-lead (z-index:1): the full-width hero-lead box overlaps the
     bottom nav button on short viewports, and without this it would swallow
     taps meant for GET IN TOUCH. */
  .masthead nav {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 2;
  }

  .tab-rail {
    justify-content: flex-start;
    border-top: 1px solid var(--divider-color);
  }

  .vertical-tab-link:last-child {
    margin-bottom: var(--drawer-rail-zone);
  }
}


/* =============================================================================
   Drawer open — lock page scroll behind the overlay
   Overrides the landscape rules that re-enable document scrolling, so only
   .pane-content scrolls while the drawer is open. Removed when it closes.
   ============================================================================= */

html.drawer-open,
html.drawer-open body,
html.drawer-open .site-shell {
  height: 100dvh;
  overflow: hidden;
}

/* =============================================================================
   Hero activation — one-time "connection fade" on initial page load
   The circuit field comes up first (its own activation, unchanged); the hero UI
   then resolves into place like a signal locking in — a smooth ease-out fade
   with a subtle brightness lift (dim -> full luminance, "signal acquired").
   No dips, no blinking: once an element is readable it stays readable. Elements
   carry small, heavily-overlapping delays so it reads as one interface
   connecting rather than a top-to-bottom cascade. Opacity / filter / transform
   only, with fill-mode backwards — each element is hidden before its turn and
   reverts to the base style once settled, so the resting state is byte-identical
   to the default (no lingering filter or transform). Lives inside
   prefers-reduced-motion:no-preference, so reduced motion plays nothing and
   shows the settled hero immediately. ASK is intentionally NOT in these
   selectors — it comes online last, on its own (see the ASK launcher block).
   ============================================================================= */

@media (prefers-reduced-motion: no-preference) {

  /* Three-stop resolve: the element fades up to full opacity by ~55%, then
     visibly brightens the rest of the way to full luminance — so the
     stabilization ("the signal locking to full strength") registers as its own
     clearly-visible beat after the element is already readable, rather than
     reading as a quick fade. The mid-point sits well below full, so the
     brighten-to-full is a real change, not a hairline. Brightness only ever
     rises, so nothing dips or blinks. */
  @keyframes heroResolve {
    0%   { opacity: 0; filter: brightness(.42); }
    55%  { opacity: 1; filter: brightness(.58); }
    100% { opacity: 1; filter: brightness(1); }
  }
  /* Same resolve, plus a faint gold "signal clarity" halo for the readable
     hero text (.hero-text descendants). The halo is layered ON TOP of the
     existing dark legibility shadow and is strongest while the text is still
     resolving, then tightens to nothing — so the type reads as sharpening into
     focus. It always ends on the element's exact base shadow (the two dark
     layers), so the settled state is unchanged and there is no snap. Restrained
     and warm (tied to the circuit field), not a neon glow. */
  @keyframes heroResolveText {
    0%   { opacity: 0; filter: brightness(.42); text-shadow: 0 0 10px rgba(231,201,135,.2), 0 0 .25rem #00000080, 0 0 1rem #00000080; }
    55%  { opacity: 1; filter: brightness(.58); text-shadow: 0 0 7px  rgba(231,201,135,.2), 0 0 .25rem #00000080, 0 0 1rem #00000080; }
    100% { opacity: 1; filter: brightness(1);   text-shadow: 0 0 0    rgba(231,201,135,0),  0 0 .25rem #00000080, 0 0 1rem #00000080; }
  }
  /* Headline also carries a tiny 3px settle as it locks in (movement only). */
  @keyframes heroResolveLock {
    0%   { opacity: 0; filter: brightness(.42); transform: translateY(3px); text-shadow: 0 0 10px rgba(231,201,135,.2), 0 0 .25rem #00000080, 0 0 1rem #00000080; }
    55%  { opacity: 1; filter: brightness(.58); transform: translateY(0);   text-shadow: 0 0 7px  rgba(231,201,135,.2), 0 0 .25rem #00000080, 0 0 1rem #00000080; }
    100% { opacity: 1; filter: brightness(1);   transform: translateY(0);   text-shadow: 0 0 0    rgba(231,201,135,0),  0 0 .25rem #00000080, 0 0 1rem #00000080; }
  }

  /* Masthead resolves first and nearly together (logo + nav read as one bar);
     eyebrow + headline next; intro + social together last. Longer, heavily
     overlapping durations: readable by ~0.6s, fully resolved (last element
     locked to full luminance) ~1.14s — within the 1.25s cap, not a cascade.
     The clarity halo rides only the three readable text elements. */
  .hero .logo         { animation: heroResolve     .78s ease-out .12s backwards; }
  .hero .masthead nav { animation: heroResolve     .78s ease-out .16s backwards; }
  .hero .eyebrow      { animation: heroResolveText .80s ease-out .24s backwards; }
  .hero .hero-heading { animation: heroResolveLock .82s ease-out .26s backwards; }
  .hero .intro-copy   { animation: heroResolveText .76s ease-out .38s backwards; }
  .hero .social-links { animation: heroResolve     .76s ease-out .38s backwards; }
}

/* Hide Chatbase's default injected launcher bubble. This targets Chatbase's
   OUTER launcher element (id #chatbase-bubble-button), which the embed injects
   as a top-level element in the parent document — not inside the cross-origin
   iframe. A single stable ID selector, so it's clean rather than brittle; the
   custom ASK launcher below becomes the sole trigger and opens the panel via
   window.chatbase.open(). Chatbase's own × closes it. */
#chatbase-bubble-button { display: none !important; }

/* =============================================================================
   Custom ASK launcher
   A local, branded chat trigger that replaces the default Chatbase bubble:
   gold ring, opaque dark center (var(--bg), matching the site background — no
   transparency/blur), white "ASK". Opens the Chatbase panel; Chatbase's own ×
   closes it (open-only, so no tracked state can desync). Sits above the drawer
   (z-index 8) as a persistent site utility. Fixed 60px so it matches the stock
   bubble on every viewport. It fades up after the hero settles, then a
   two-node signal (a bright pulse and a softer echo opposite it) orbits the
   ring, matching the hero circuit field's own pulse language; drawn on
   .ask-signal-canvas (engine.js), which checks prefers-reduced-motion itself
   and stays blank when motion is reduced.
   ============================================================================= */

.ask-launcher {
  position: fixed;
  right: 16px;                       /* symmetric with bottom: an even 16px gap
                                        from both edges, so it reads as a
                                        deliberately placed corner control */
  bottom: 16px;                      /* constant gap (matches the stock bubble);
                                        not rem, which would balloon on wide
                                        screens and lift the button too high */
  z-index: 10;                       /* above the drawer (z-index: 8) */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 60px;                       /* fixed px: matches the stock bubble and
                                        stays consistent regardless of the
                                        viewport-scaled root font-size */
  height: 60px;
  padding: 0;
  border: 2px solid var(--gold);     /* gold ring */
  border-radius: 50%;
  background-color: var(--bg);       /* opaque dark center, matches site bg */
  color: var(--text-light);          /* white mark */
  font-family: inherit;
  cursor: pointer;
  isolation: isolate;                /* contain the trace's screen blend to the
                                        button so it never lightens the page */
  -webkit-tap-highlight-color: transparent;
}

/* The base halo lives on a pseudo behind the button, so only the gold ring's
   halo glows, never the opaque center or the text. This constant soft halo keeps
   the ring reading as an illuminated, interactive control at all times,
   including reduced motion. The orbiting signal node is drawn separately on
   .ask-signal-canvas, layered over the ring; this ::before stays static. */
.ask-launcher::before {
  content: "";
  position: absolute;
  inset: -2px;
  z-index: -1;
  border-radius: 50%;
  box-shadow: 0 0 8px 1px rgba(231, 201, 135, .22);
  pointer-events: none;
}

/* Signal node orbits the ring on canvas (engine.js draws it), matching the
   hero circuit field's own round pulse language rather than a CSS wedge. The
   canvas sits over the ring; the opaque centre and "ASK" label are unaffected
   since the orbit path stays close to the ring's radius. */
.ask-signal-canvas {
  position: absolute;
  left: -8px;
  top: -8px;
  width: calc(100% + 16px);           /* explicit size: canvas is a replaced
                                          element with its own intrinsic size
                                          (300x150 by default), so inset alone
                                          does not stretch it the way it would
                                          a plain div */
  height: calc(100% + 16px);
  display: block;
  z-index: 1;
  pointer-events: none;
}

.ask-launcher__label {
  position: absolute;
  z-index: 2;                        /* guaranteed above the signal canvas */
  font-size: 15px;
  font-weight: 600;                  /* semi-bold, matching the site's uppercase
                                        controls (.button / eyebrow) — not the
                                        generic heavy widget bold */
  letter-spacing: .1em;              /* same controlled tracking as the site's
                                        uppercase interface labels */
  line-height: 1;
  text-transform: uppercase;
}

/* Collapse mark shown while the Chatbase panel is open. The launcher keeps its
   pill, ring, halo, position, size and hover/focus behaviour; only the interior
   presentation swaps, so the control reads as one continuous element changing
   state rather than two different buttons. Drawn as an inline stroke path in the
   launcher's own visual language (same --text-light mark colour, same rounded
   geometry as the drawer's close icon) rather than an imported graphic.
   The chevron points down: activating it collapses the panel away. */
.ask-launcher__collapse {
  position: absolute;
  z-index: 2;                        /* same layer as the label it replaces */
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transform: scale(.72);
  pointer-events: none;
}

/* Cross-fade between the two interior presentations. The label and the mark are
   both absolutely centred, so neither reflows the button as they swap. */
.ask-launcher__label,
.ask-launcher__collapse {
  transition: opacity .18s ease, transform .18s ease;
}

.ask-launcher.is-chat-open .ask-launcher__label {
  opacity: 0;
  transform: scale(.72);
}

.ask-launcher.is-chat-open .ask-launcher__collapse {
  opacity: 1;
  transform: scale(1);
}

/* While the panel is open the launcher is a collapse control, not an invitation,
   so the orbiting signal stands down and the ring is left still. The canvas is
   faded out on the same curve as the label/mark swap, so the three move as one
   change of state rather than three separate events. The signal resumes on its
   own when the class clears — whether that came from the chevron, a drawer
   taking over, or Chatbase's own close control. */
.ask-signal-canvas {
  transition: opacity .18s ease;
}

.ask-launcher.is-chat-open .ask-signal-canvas {
  opacity: 0;
}

/* Reduced motion: the state still changes, it just does not animate. */
@media (prefers-reduced-motion: reduce) {
  .ask-launcher__label,
  .ask-launcher__collapse,
  .ask-signal-canvas {
    transition: none;
  }
  .ask-launcher__label,
  .ask-launcher__collapse {
    transform: none;
  }
  .ask-launcher.is-chat-open .ask-launcher__label { transform: none; }
  .ask-launcher.is-chat-open .ask-launcher__collapse { transform: none; }
}

.ask-launcher:focus-visible {
  outline: 0.125rem solid var(--gold);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: no-preference) {
  /* ASK is the last channel to come online: it fades in on its own, after the
     hero UI has resolved (~1.12s), with a slightly slower, more deliberate
     three-stop fade (opacity up, then the ring illuminates the rest of the way
     to full): a site utility becoming available, separate from the hero
     entrance. fill-mode backwards keeps it hidden until its turn, then reverts
     to base. The signal canvas (engine.js) checks this same media query itself
     and only animates when motion is not reduced. */
  @keyframes askResolve {
    0%   { opacity: 0; filter: brightness(.38); }
    55%  { opacity: 1; filter: brightness(.55); }
    100% { opacity: 1; filter: brightness(1); }
  }
  .ask-launcher {
    animation: askResolve .6s ease-out 1.18s backwards;
  }
}

@media screen and (orientation: landscape) and (max-height: 430px) {
  .ask-launcher {
    right: .75rem;
    bottom: .75rem;
  }
}
