/* =====================================================================
   style.css — blueprint scroll-build section

   Single committed visual world (a drafting sheet under tungsten light),
   so this deliberately does not ship a light theme.
   ===================================================================== */

:root {
  --navy-deep:  #061527;
  --navy:       #0B1F3B;
  --line:       #F2EDE4;
  --gold:       #B8925A;

  --grid-fine:  rgba(242, 237, 228, 0.045);
  --grid-major: rgba(242, 237, 228, 0.085);

  --mono: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas, monospace;
  --sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

html {
  /* Scroll-scrubbing must read raw scroll position; smooth scrolling
     fights the lerp and makes the build feel rubbery. */
  scroll-behavior: auto;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--navy-deep);
  color: var(--line);
  font-family: var(--sans);
  overflow-x: hidden;
}

/* =====================================================================
   BUILD SECTION — tall track, sticky pin
   ===================================================================== */

.build-section {
  position: relative;
  /* Scroll distance for the build: site survey, the tower rising while
     the camera turns between viewpoints, then the pitch over to plan.

     1241vh rather than 1050vh: the stretch from the old 45% mark to the
     end was given 33% more scroll distance so the camera moves, tag
     holds and plan sequence all read more slowly. The timeline constants
     in main.js were remapped to match, so the OPENING still occupies the
     same number of pixels as before — only the tail is slower. */
  height: 1775vh;
}

.pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background:
    radial-gradient(125% 92% at 50% 38%,
      #123258 0%, var(--navy) 44%, var(--navy-deep) 100%);
}

/* Blueprint grid — pure CSS, no image requests, so it costs nothing
   to serve from CloudFront. */
.blueprint-grid {
  position: absolute;
  inset: -2px;
  pointer-events: none;
  background-image:
    linear-gradient(var(--grid-fine) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-fine) 1px, transparent 1px),
    linear-gradient(var(--grid-major) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-major) 1px, transparent 1px);
  background-size: 12px 12px, 12px 12px, 96px 96px, 96px 96px;
}

/* Vignette, applied to the pin itself so it sits above the grid but
   below the canvas linework. */
.pin::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(80% 66% at 50% 46%,
    transparent 34%, rgba(4, 12, 24, 0.72) 100%);
}

#tower-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Above the vignette so linework stays crisp */
  z-index: 2;
}

/* =====================================================================
   HUD — drafting readouts
   ===================================================================== */

.hud {
  position: absolute;
  z-index: 3;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 2.05;
  color: rgba(242, 237, 228, 0.5);
}

.hud--left  { left: 30px; bottom: 28px; }
.hud--right { right: 30px; bottom: 28px; text-align: right; }

.hud-row { display: flex; gap: 14px; align-items: baseline; }

.hud-key { min-width: 62px; }

.hud-val {
  color: var(--line);
  font-variant-numeric: tabular-nums;
}

/* =====================================================================
   ANNOTATION TAGS

   Drafting-style callouts: a dot on the structure, a leader line with a
   single elbow, and a label block. Positions are computed in JS by
   projecting each tag's 3D anchor, so the leader keeps pointing at the
   right part of the building. Tags only appear while the camera is
   holding at a settled viewpoint, so nothing drifts.
   ===================================================================== */

.tag-layer {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.tag-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.tag-leader,
.tag-dot {
  opacity: 0;
  transition: opacity 0.5s ease;
}

.tag-leader.is-visible,
.tag-dot.is-visible { opacity: 1; }

.tag-leader {
  fill: none;
  stroke: var(--line);
  stroke-width: 1;
  stroke-linecap: square;
  stroke-linejoin: miter;
  vector-effect: non-scaling-stroke;
}

.tag-dot {
  fill: none;
  stroke: var(--line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.tag {
  position: absolute;
  width: min(19rem, 34vw);
  opacity: 0;
  /* Slight rise as it appears — enough to register, not enough to
     read as decoration. */
  transform: translateY(6px);
  transition: opacity 0.55s ease, transform 0.55s ease;
  /* Sit on the leader's horizontal run */
  margin-top: -0.55rem;
}

.tag.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Labels extend away from the anchor, so a left-side tag is right-aligned
   and shifted fully off its own anchor point. */
.tag--right { padding-left: 0.7rem; }
.tag--left  { padding-right: 0.7rem; text-align: right; transform: translate(-100%, 6px); }
.tag--left.is-visible { transform: translate(-100%, 0); }

.tag-label {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 0.5rem;
}

.tag-body {
  display: block;
  font-size: 0.82rem;
  line-height: 1.6;
  color: rgba(242, 237, 228, 0.82);
  text-wrap: pretty;
}

/* A soft scrim keeps the text legible where a leader runs it over dense
   wireframe. Sized to the text, so it reads as paper rather than a box. */
.tag-label,
.tag-body {
  text-shadow:
     0 0 8px rgba(6, 21, 39, 0.95),
     0 0 16px rgba(6, 21, 39, 0.85);
}

/* Stacked layout (narrow screens): labels sit in a column at the foot of
   the screen rather than beside their anchors, which do not fit. */
.tag.is-stacked {
  width: calc(100vw - 40px);
  max-width: none;
  padding: 0;
  text-align: left;
  transform: translateY(6px);
}

.tag.is-stacked.is-visible { transform: translateY(0); }

.tag.is-stacked .tag-body {
  font-size: 0.78rem;
  line-height: 1.5;
}

/* Tags depend on camera moves that reduced motion suppresses. */
@media (prefers-reduced-motion: reduce) {
  .tag, .tag-leader, .tag-dot { display: none; }
}

.scroll-hint {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  margin: 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(242, 237, 228, 0.42);
  transition: opacity 0.5s ease;
}

/* =====================================================================
   PANEL SEQUENCE

   The left column runs as a set of pages, cross-fading as the build
   progresses. All panels share one grid cell so they occupy exactly the
   same space; only one carries .is-visible at a time.
   ===================================================================== */

.panels {
  position: absolute;
  z-index: 3;
  left: 7vw;
  top: 50%;
  transform: translateY(-50%);
  width: min(30rem, 38vw);
  pointer-events: none;
  /* Single-cell grid: panels stack rather than flow, so switching one
     for another does not move anything. */
  display: grid;
}

.panel {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.7s ease, transform 0.7s ease;
  /* Non-visible panels must not catch clicks or be read out. */
  visibility: hidden;
}

.panel.is-visible {
  opacity: 1;
  transform: translateY(0);
  visibility: visible;
}

/* The supplied logo is dark artwork on a solid cream ground, which would
   sit as a bright rectangle on the navy. Inverting flips it to light
   artwork on near-black, and multiply then drops the black — leaving the
   mark and wordmark reading light against the blueprint. The hue rotation
   restores the gold, which inversion would otherwise turn blue.

   A pre-cut transparent PNG or SVG would be cleaner; drop one in at the
   same path and remove the filter/blend below. */
/* ---- Logo ----
   The cream ground has been keyed out of the asset itself. The artwork
   splits at 48.3% of its height: gold mark above, navy wordmark below.

   The mark is drawn as the image (its gold is already correct); the
   wordmark is drawn as a MASK filled with the line colour, because navy
   artwork would vanish against a navy ground. Masking recolours it
   solidly, where filters only outlined it. */

.panel-logo {
  display: block;
  width: min(14.5rem, 62%);
  margin: 0 0 1.7rem -0.3rem;
  /* Aspect of the two bands, preserved so nothing is squashed */
  line-height: 0;
}

.logo-mark,
.logo-word {
  display: block;
  width: 100%;
  background-repeat: no-repeat;
  background-size: 100% auto;
}

/* Mark: 59..240 of 542 — shown as-is so the gold survives. */
.logo-mark {
  aspect-ratio: 1024 / 262;
  background-image: url("../Meridian-logo.png");
  background-position: 0 0;
}

/* Wordmark: 262..542 of 542 — masked and filled in the line colour. */
.logo-word {
  aspect-ratio: 1024 / 280;
  background: none;
  background-color: var(--line);
  -webkit-mask-image: url("../Meridian-logo.png");
  mask-image: url("../Meridian-logo.png");
  -webkit-mask-size: 100% auto;
  mask-size: 100% auto;
  -webkit-mask-position: 0 100%;
  mask-position: 0 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.panel-eyebrow {
  margin: 0 0 0.6rem;
  font-family: var(--sans);
  font-size: clamp(1rem, 1.6vw, 1.25rem);
  font-weight: 300;
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--gold);
}

.panel-h1 {
  margin: 0 0 1rem;
  font-size: clamp(1.8rem, 3.3vw, 2.7rem);
  font-weight: 300;
  line-height: 1.14;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.panel-h2 {
  margin: 0 0 1.2rem;
  font-size: clamp(1.4rem, 2.5vw, 2rem);
  font-weight: 300;
  line-height: 1.2;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.panel-sub {
  margin: 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(242, 237, 228, 0.5);
}

.panel-body {
  margin: 0 0 2rem;
  font-size: 0.95rem;
  line-height: 1.7;
  color: rgba(242, 237, 228, 0.74);
  max-width: 36ch;
  text-wrap: pretty;
}

.panel-stats {
  display: flex;
  gap: 2.2rem;
  flex-wrap: wrap;
}

.stat { display: flex; flex-direction: column; gap: 0.4rem; }

.stat-num {
  font-size: 1.5rem;
  font-weight: 300;
  font-variant-numeric: tabular-nums;
  color: var(--gold);
}

.stat-unit { font-size: 0.9rem; }

.stat-label {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(242, 237, 228, 0.44);
}

/* ---- Pricing tiers ---- */

.tiers {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1.35rem;
}

.tier {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.3rem 1rem;
  padding-top: 1.05rem;
  border-top: 1px solid rgba(242, 237, 228, 0.14);
}

.tier-name {
  font-size: 0.95rem;
  color: var(--line);
}

.tier-price {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  text-align: right;
  align-self: baseline;
}

.tier-note {
  grid-column: 1 / -1;
  font-size: 0.8rem;
  line-height: 1.55;
  color: rgba(242, 237, 228, 0.56);
  text-wrap: pretty;
}

/* =====================================================================
   WEBGL FALLBACK
   ===================================================================== */

.webgl-fallback {
  position: absolute;
  z-index: 2;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
}

/* display:flex overrides the [hidden] attribute's default display:none,
   so the fallback must be suppressed explicitly. */
.webgl-fallback[hidden] { display: none; }

.webgl-fallback svg { height: 62vh; width: auto; }

.webgl-fallback p {
  margin: 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(242, 237, 228, 0.45);
}

/* With no WebGL there is nothing to scrub, so collapse the tall track
   to a single screen and let the page read as a normal document. */
.no-webgl .build-section { height: 100vh; }
.no-webgl .scroll-hint   { display: none; }
.no-webgl .panels {
  position: static;
  transform: none;
  width: auto;
  max-width: 40rem;
  margin: 0 auto;
  padding: 3rem 7vw;
}

/* =====================================================================
   RESPONSIVE
   ===================================================================== */

@media (max-width: 860px) {
  .panels {
    left: 0;
    right: 0;
    top: auto;
    bottom: 0;
    transform: none;
    width: auto;
    /* The panel sits over the drawing on small screens, so it needs a
       ground of its own. A gradient rather than a flat block, so the
       tower stays visible above it. */
    padding: 3.25rem 6vw 5rem;
    background: linear-gradient(to bottom,
      rgba(6, 21, 39, 0) 0%,
      rgba(6, 21, 39, 0.88) 22%,
      rgba(6, 21, 39, 0.97) 48%);
  }
  .panel.is-visible { transform: translateY(0); }

  .panel-h1 { font-size: clamp(1.4rem, 5.6vw, 2rem); }
  .panel-h2 { font-size: clamp(1.2rem, 4.8vw, 1.6rem); }

  .panel-logo { width: min(11rem, 52%); margin-bottom: 1.1rem; }
  .panel-body { font-size: 0.86rem; margin-bottom: 1.4rem; }

  /* Price beside the name rather than wrapping under it */
  .tiers { gap: 0.95rem; }
  .tier { padding-top: 0.8rem; gap: 0.15rem 0.8rem; }
  .tier-name  { font-size: 0.88rem; }
  .tier-price { font-size: 0.7rem; white-space: nowrap; }
  .tier-note  { font-size: 0.74rem; line-height: 1.45; }

  .panel-stats { gap: 1.4rem; }
  .stat-num { font-size: 1.2rem; }
  .lede { font-size: 0.92rem; margin-bottom: 1.5rem; max-width: none; }

  /* Two-up grid instead of a wrapping flex row, so labels never
     collide with each other mid-line. */
  .stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1.1rem 1rem;
  }
  .stat-num { font-size: 1.2rem; }

  .hud--right { display: none; }
  /* Stacked tags occupy the lower-left, so the HUD moves to the top. */
  .hud--left  { left: 20px; top: 20px; bottom: auto; }
}

@media (max-width: 520px) {
  .lede { display: none; }
  .build-section { height: 790vh; }
  /* HUD would sit under the scrim once copy appears; keep it above. */
  .hud--left { z-index: 4; }
}

/* =====================================================================
   CALL TO ACTION — closing panel

   A stamped drafting annotation rather than UI chrome: mono, uppercase,
   letterspaced, hairline rule. Its gold fill is the first beat of the
   datum wipe below, so the two are one gesture.
   ===================================================================== */

.cta {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  margin-top: 2rem;
  padding: 0.95rem 1.6rem;
  border: 1px solid rgba(242, 237, 228, 0.3);
  color: var(--line);
  text-decoration: none;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  /* .panels sets pointer-events: none so the column never intercepts
     scroll over the canvas. The button has to claim them back or it
     renders but never receives a click. */
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
  transition: border-color 0.3s ease;
}

.cta:hover { border-color: rgba(242, 237, 228, 0.55); }

/* Label sits above the fill, and inverts as the gold passes under it. */
.cta-label {
  position: relative;
  z-index: 1;
  white-space: nowrap;
}

.cta-fill {
  position: absolute;
  inset: 0;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left center;
}

/* Fill and label inversion are driven by .is-leaving on <body>, so the
   whole transition runs from a single class. */
.is-leaving .cta-fill {
  animation: cta-fill 0.34s cubic-bezier(0.65, 0, 0.35, 1) forwards;
}

@keyframes cta-fill { to { transform: scaleX(1); } }

.is-leaving .cta-label {
  color: var(--navy-deep);
  transition: color 0.16s linear 0.14s;
}

/* =====================================================================
   DATUM WIPE

   A gold sheet grows from the button's line to fill the screen, holds
   briefly carrying the Meridian mark, then travels up and off. The
   destination loads behind it, so the new page is painted before the
   sheet clears.

   Height/bottom rather than transform: the sheet has to grow from the
   bottom edge and then leave past the top, which is two different
   origins — cheaper to express as position than to chain transforms.
   ===================================================================== */

.datum-wipe {
  position: fixed;
  z-index: 100;
  inset: 0;
  pointer-events: none;
  /* Inert until the transition starts, so it never blocks the page. */
  visibility: hidden;
}

.is-leaving .datum-wipe { visibility: visible; }

.datum-sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0;
  background: var(--gold);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* A cream hairline rides the leading edge, so the sheet reads as a
   drawn datum rather than a block of colour. */
.datum-sheet::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  background: var(--line);
}

.is-leaving .datum-sheet {
  animation: datum-sweep 1.5s cubic-bezier(0.76, 0, 0.24, 1) 0.32s forwards;
}

@keyframes datum-sweep {
  0%   { height: 0;    bottom: 0;    }
  34%  { height: 100%; bottom: 0;    }
  62%  { height: 100%; bottom: 0;    }
  100% { height: 100%; bottom: 100%; }
}

/* ---- Mark on the sheet ----
   Same split-logo treatment as the panels, but inverted for the gold
   ground: the wordmark is masked and filled in NAVY, since the cream
   used on the blueprint would disappear against gold. The gold mark is
   likewise masked to navy rather than drawn as artwork — gold on gold
   would vanish. */

.datum-logo {
  position: relative;
  width: min(17rem, 46vw);
  margin: 0;
  line-height: 0;
  opacity: 0;
  transform: translateY(12px);
}

.is-leaving .datum-logo {
  animation: datum-logo-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.52s forwards;
}

@keyframes datum-logo-in {
  to { opacity: 1; transform: translateY(0); }
}

.datum-mark,
.datum-word {
  display: block;
  width: 100%;
  background-color: var(--navy-deep);
  -webkit-mask-image: url("../Meridian-logo.png");
  mask-image: url("../Meridian-logo.png");
  -webkit-mask-size: 100% auto;
  mask-size: 100% auto;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* Mark: top band of the artwork. */
.datum-mark {
  aspect-ratio: 1024 / 262;
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
}

/* Wordmark: bottom band. */
.datum-word {
  aspect-ratio: 1024 / 280;
  -webkit-mask-position: 0 100%;
  mask-position: 0 100%;
}

@media (max-width: 860px) {
  .cta { margin-top: 1.5rem; padding: 0.85rem 1.25rem; letter-spacing: 0.2em; }
  .datum-logo { width: min(13rem, 62vw); }
}

/* Keyboard focus must stay visible against the dark ground. */
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .panel, .scroll-hint { transition: none; }

  /* No travel: the sheet covers the screen in a short fade and the
     navigation still reads. Matches how main.js drops its camera work. */
  .cta-fill, .datum-logo { animation: none !important; }
  .cta-label { transition: none !important; }

  .datum-sheet {
    height: 100%;
    opacity: 0;
    transition: opacity 0.25s linear;
  }
  .is-leaving .datum-sheet { animation: none !important; opacity: 1; }
  .is-leaving .datum-logo { opacity: 1; transform: none; }
}
