/* ============================================================
   XQUBE GLOBAL — SERVICE 05 · Entity Formation & Structuring
   Page-specific styles only. The design system lives in main.css.
   The page's centrepiece is THE ENTITY DECISION: four branching
   paths that are set aside one by one until one structure is left
   standing, and a compliance calendar that stands up behind it.
   ============================================================ */

/* the cold open belongs to the homepage title sequence — an inner
   page must never blank its own chrome */
.js.cold .nav-inner,
.js.cold .scroll-progress { opacity: 1; }

/* current page marker in the nav */
.nav-links a.is-here { color: var(--text); }
.nav-links a.is-here::after { transform: scaleX(1); opacity: 0.75; }
.mobile-links a.is-here { color: var(--text); }
.mobile-links a.is-here .mono { color: var(--pink); }

/* ============================================================
   1 · THE TITLE SCENE
   ============================================================ */
#ef-title.scene { height: 220vh; }
#ef-title .scene-stage {
  position: sticky;
  height: 100vh; height: 100svh;
  overflow: hidden;
  justify-content: center;
  padding: calc(var(--nav-h) + var(--s6)) var(--pad) var(--s6);
}
.ef-title-stage .title-block { max-width: 1080px; }
.ef-h1 {
  font-size: clamp(34px, 5.4vw, 76px);
  line-height: 1.07;
  letter-spacing: -0.028em;
  font-weight: 600;
  text-wrap: balance;
  max-width: 18ch;
  margin: 0 auto;
}
.ef-sub {
  margin: clamp(30px, 5vh, 54px) auto 0;
  max-width: 58ch;
  opacity: var(--subop, 1);
  transform: translateY(var(--suby, 0px));
}
@media (prefers-reduced-motion: no-preference) {
  .js .ef-title-stage { --subop: 0; --suby: 26px; }
}

/* ============================================================
   2 · THE PROBLEM — a reading rest, set narrow and unhurried
   ============================================================ */
.ef-problem .problem-h2 { max-width: 14ch; }
.ef-problem .problem-body {
  margin-top: clamp(36px, 6vh, 64px);
  display: flex; flex-direction: column;
  gap: var(--s5);
  border-left: 1px solid var(--line);
  padding-left: clamp(22px, 3vw, 44px);
}
.ef-problem .body-lg { max-width: 62ch; }

/* ============================================================
   3 · PAIRED PANELS — what we do / what changes for you
   ============================================================ */
.pair-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(20px, 2.6vw, 34px);
  align-items: stretch;
}
.pair-panel {
  position: relative;
  display: flex; flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09);
  padding: clamp(28px, 3.2vw, 46px);
  overflow: hidden;
}
.pair-panel::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 2px;
  background: linear-gradient(180deg, transparent, var(--purple) 22%, var(--purple) 78%, transparent);
  opacity: 0.7;
}
.pair-panel-alt::before {
  background: linear-gradient(180deg, transparent, var(--pink) 22%, var(--pink) 78%, transparent);
}
.pair-num {
  font-size: 11px; letter-spacing: 0.22em;
  color: var(--purple-soft);
  margin-bottom: var(--s3);
}
.pair-panel-alt .pair-num { color: var(--pink-soft); }
.pair-h2 {
  font-size: clamp(24px, 2.6vw, 33px);
  font-weight: 600; letter-spacing: -0.02em; line-height: 1.15;
  margin-bottom: var(--s3);
}
.pair-body {
  font-size: clamp(15.5px, 1.3vw, 17px);
  line-height: 1.8;
  color: var(--mist);
  max-width: 46ch;
}

/* ============================================================
   4 · THE ENTITY DECISION — the signature device
   ============================================================ */
#ef-decide.scene { height: 460vh; }
#ef-decide .scene-stage {
  position: sticky;
  height: 100vh; height: 100svh;
  overflow: hidden;
  justify-content: center;
  gap: clamp(14px, 2.2vh, 26px);
  padding: calc(var(--nav-h) + var(--s4)) var(--pad) var(--s4);
}
.decide-head {
  position: relative; z-index: 2;
  width: min(1100px, 100%);
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3);
  border-bottom: 1px solid var(--line);
  padding-bottom: var(--s2);
}
.decide-head .decide-label { margin-bottom: 0; }
.decide-count {
  font-size: 10.5px; letter-spacing: 0.2em;
  color: var(--faint);
  white-space: nowrap;
}
.decide-count em {
  font-style: normal;
  color: var(--purple-soft);
  text-shadow: 0 0 14px rgba(91, 76, 255, 0.6);
}

.decide-rig {
  position: relative; z-index: 2;
  width: min(1100px, 100%);
  display: flex; flex-direction: column;
  gap: clamp(12px, 2.2vh, 28px);
}
.rig-label,
.cal-head {
  font-size: 10px; letter-spacing: 0.24em;
  color: var(--faint);
}
.goal-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s1);
}
.goal {
  display: flex; align-items: center; gap: 9px;
  font-size: 10.5px; letter-spacing: 0.14em;
  color: var(--mist);
  border: 1px solid var(--line);
  border-radius: 100px;
  background: var(--surface);
  padding: 9px 14px;
  transition: opacity 0.5s ease, color 0.5s ease, border-color 0.5s ease, box-shadow 0.5s ease;
}
.goal::before {
  content: "";
  flex: none;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--purple-soft);
  box-shadow: 0 0 10px rgba(148, 138, 255, 0.9);
}
/* The scene runs at every width now (js: flowP), so the pre-states are no
   longer gated on 981px — a phone that is handed the resolved picture is
   handed the answer without the question. Still .js + no-preference: with
   no JS or reduced motion nothing writes .on and the base rules stand. */
@media (prefers-reduced-motion: no-preference) {
  .js .goal { opacity: 0.24; }
  .js .goal.on {
    opacity: 1;
    color: var(--text);
    border-color: rgba(148, 138, 255, 0.45);
    box-shadow: 0 0 22px rgba(91, 76, 255, 0.16);
  }
}

/* --- the branch --- */
.branch {
  position: relative;
  height: clamp(78px, 13vh, 132px);
}
.br-origin {
  position: absolute; left: 50%; top: -4px;
  width: 9px; height: 9px;
  transform: translateX(-50%) rotate(45deg);
  background: var(--purple);
  box-shadow: 0 0 18px rgba(91, 76, 255, 0.85);
}
.br-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.br-path {
  fill: none;
  stroke: rgba(148, 138, 255, 0.55);
  stroke-width: 1.2;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 1;
  stroke-dashoffset: calc(1 - var(--draw, 1));
  transition: stroke 0.6s ease, opacity 0.6s ease, stroke-width 0.6s ease;
}
.br-path.out { opacity: 0.16; }
.br-path.won {
  stroke: var(--pink);
  stroke-width: 2;
  filter: drop-shadow(0 0 8px rgba(255, 77, 128, 0.6));
}
@media (prefers-reduced-motion: no-preference) {
  .js .br-path { --draw: 0; }
}

/* --- the four structures --- */
.ent-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(10px, 1.4vw, 20px);
}
.ent {
  display: flex; flex-direction: column; gap: 7px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  padding: clamp(18px, 2.8vh, 34px) clamp(12px, 1.4vw, 20px);
  text-align: center;
  transition: opacity 0.6s ease, border-color 0.6s ease, box-shadow 0.6s ease, transform 0.6s cubic-bezier(0.3, 0.7, 0.3, 1);
}
.ent-name {
  font-size: clamp(19px, 2.1vw, 27px);
  font-weight: 500; letter-spacing: -0.01em;
  color: var(--text);
}
.ent-meta { font-size: 9.5px; letter-spacing: 0.18em; color: var(--faint); }
.ent-state {
  margin-top: 4px;
  font-size: 9.5px; letter-spacing: 0.2em;
  color: var(--faint);
}
/* default (no JS · reduced motion · mobile) = the resolved end state */
.ent { opacity: 0.36; }
.ent-state::after { content: "SET ASIDE"; }
.ent[data-win] {
  opacity: 1;
  border-color: rgba(148, 138, 255, 0.6);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 0 36px rgba(91, 76, 255, 0.22);
}
.ent[data-win] .ent-state { color: var(--pink-soft); }
.ent[data-win] .ent-state::after { content: "THE FIT"; }
/* WITH JS AND MOTION, at any width, the four structures start level and
   the decision is made in front of the reader. This block used to stop at
   981px, which left a phone permanently on the line above it: three cards
   at 36% reading SET ASIDE with nothing to explain why, and the winner
   already glowing. The narrow film re-lights its hold-back states a little
   for daylight — see §THE SAME FILM, ON A PHONE. */
@media (prefers-reduced-motion: no-preference) {
  .js .ent { opacity: 1; }
  .js .ent-state::after { content: "IN PLAY"; }
  .js .ent[data-win] {
    border-color: var(--line);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  }
  .js .ent[data-win] .ent-state { color: var(--faint); }
  .js .ent[data-win] .ent-state::after { content: "IN PLAY"; }
  .js .ent.out { opacity: 0.3; }
  .js .ent.out .ent-state::after { content: "SET ASIDE"; }
  .js .ent.won {
    opacity: 1;
    transform: translateY(-4px);
    border-color: rgba(148, 138, 255, 0.6);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 0 36px rgba(91, 76, 255, 0.22);
  }
  .js .ent.won .ent-state { color: var(--pink-soft); }
  .js .ent.won .ent-state::after { content: "THE FIT"; }
}

.rig-foot {
  font-size: 9.5px; letter-spacing: 0.2em;
  color: var(--faint);
  opacity: 0.75;
}

/* --- the compliance calendar, standing up --- */
.cal-block {
  margin-top: clamp(4px, 1.4vh, 18px);
  border-top: 1px solid var(--line);
  padding-top: clamp(14px, 2.2vh, 26px);
  opacity: var(--calop, 1);
}
.cal-head {
  display: flex; align-items: baseline;
  gap: 12px;
  margin-bottom: 12px;
}
.cal-day { color: var(--purple-soft); }
.cal-day::before { content: "· "; color: var(--faint); }
.cal-row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(5px, 0.7vw, 10px);
  align-items: end;
  height: clamp(84px, 13vh, 142px);
}
.cal-col {
  position: relative;
  display: block;
  height: 100%;
}
.cal-bar {
  position: absolute; left: 0; right: 0; bottom: 20px;
  height: calc(var(--h, 0.5) * (100% - 20px));
  border-radius: 3px 3px 0 0;
  background: linear-gradient(180deg, rgba(148, 138, 255, 0.5), rgba(148, 138, 255, 0.08));
  border-top: 1px solid rgba(148, 138, 255, 0.7);
  transform: scaleY(var(--k, 1));
  transform-origin: 50% 100%;
}
.cal-q .cal-bar {
  background: linear-gradient(180deg, rgba(255, 77, 128, 0.42), rgba(255, 77, 128, 0.06));
  border-top-color: rgba(255, 125, 161, 0.85);
}
.cal-q::after {
  content: "";
  position: absolute; left: 50%;
  bottom: calc(26px + var(--h, 0.5) * (100% - 20px));
  width: 5px; height: 5px; margin-left: -2.5px;
  border-radius: 50%;
  background: var(--pink);
  box-shadow: 0 0 12px rgba(255, 77, 128, 0.9);
  opacity: calc((var(--k, 1) - 0.74) * 3.9);
}
.cal-m {
  position: absolute; left: 0; right: 0; bottom: 0;
  font-size: 9px; letter-spacing: 0.1em;
  text-align: center;
  color: var(--faint);
}
@media (prefers-reduced-motion: no-preference) {
  .js .decide-stage { --calop: 0.25; }
  .js .cal-col { --k: 0; }
}

/* ============================================================
   5 · SCOPE OF WORK — the length is the argument
   ============================================================ */
.scope-h2 {
  font-size: 11px; letter-spacing: 0.24em;
  font-weight: 400;
  color: var(--faint);
  border-bottom: 1px solid var(--line);
  padding-bottom: var(--s2);
}
.scope-list {
  list-style: none;
  margin: 0; padding: 0;
}
.scope-item {
  display: grid;
  grid-template-columns: 78px 1fr;
  gap: var(--s3);
  align-items: baseline;
  border-bottom: 1px solid rgba(255, 255, 255, 0.055);
  padding: clamp(18px, 2.4vh, 28px) 0;
  transition: padding-left 0.4s cubic-bezier(0.3, 0.7, 0.3, 1), border-color 0.4s ease;
}
.scope-item:hover { padding-left: 10px; border-bottom-color: rgba(148, 138, 255, 0.35); }
.scope-num {
  font-size: 12px; letter-spacing: 0.14em;
  color: var(--purple-soft);
  opacity: 0.85;
}
.scope-text {
  font-size: clamp(17px, 1.9vw, 24px);
  line-height: 1.4;
  letter-spacing: -0.012em;
  color: var(--text);
}
.js .scope-list .reveal:nth-child(1) { transition-delay: 0.02s; }
.js .scope-list .reveal:nth-child(2) { transition-delay: 0.06s; }
.js .scope-list .reveal:nth-child(3) { transition-delay: 0.10s; }
.js .scope-list .reveal:nth-child(4) { transition-delay: 0.14s; }
.js .scope-list .reveal:nth-child(5) { transition-delay: 0.18s; }
.js .scope-list .reveal:nth-child(6) { transition-delay: 0.22s; }
.js .scope-list .reveal:nth-child(7) { transition-delay: 0.26s; }
.js .scope-list .reveal:nth-child(8) { transition-delay: 0.30s; }

/* ============================================================
   6 · QUESTIONS WE ASK — THE DECISION TREE
   The page's second device, and deliberately the opposite of the
   first one. #ef-decide fans one origin sideways into four cards
   and resolves in a single move. This one descends: a route that
   forks four times, holds one branch and ghosts the other, and by
   the bottom runs unbroken from top to tail — the structure chosen.
   Vertical, sequential, angular, binary. Nothing about it rhymes
   with the horizontal curved fan above it.

   Capability ladder, richest last:
     any width, no JS / reduced motion / <=980px
        → the base rules below: everything at full opacity, route
          fully drawn, nothing positioned off-stage
     <=980px
        → the tree restacks as a gutter spine + four nodes; the SVG
          rail (decorative) is dropped
     >=981px + JS + motion
        → the scene pins and the route draws on scroll
   ============================================================ */
#ef-ask.scene { height: auto; }
#ef-ask .scene-stage {
  position: static;
  height: auto;
  overflow: visible;
  justify-content: center;
  padding: var(--section-gap) var(--pad) 0;
}
.ask-head {
  position: relative; z-index: 2;
  width: min(1060px, 100%);
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3);
  border-bottom: 1px solid var(--line);
  padding-bottom: var(--s2);
  margin-bottom: clamp(22px, 3.4vh, 46px);
}
.ask-h2 {
  font-size: clamp(11px, 1.15vw, 13px);
  font-weight: 400;
  letter-spacing: 0.26em;
  line-height: 1.9;
  color: var(--faint);
}
.dt-meter {
  font-size: 10.5px; letter-spacing: 0.2em;
  color: var(--faint);
  white-space: nowrap;
}
.dt-meter em {
  font-style: normal;
  color: var(--purple-soft);
  text-shadow: 0 0 14px rgba(91, 76, 255, 0.6);
}

/* --- the rig: a fixed-ratio rail on the left, questions beside it ---
   The SVG keeps its 400:1000 ratio, so the rail's width is always
   0.4 x its height and the questions column can be offset by exactly
   that. Each fork's --y is the node's y in the viewBox, as a percent. */
.dt-rig {
  --rig-h: clamp(380px, 58vh, 600px);
  --rail: calc(var(--rig-h) * 0.4);
  --lead-gap: clamp(14px, 1.6vw, 26px);
  position: relative; z-index: 2;
  width: min(1060px, 100%);
  height: var(--rig-h);
}
.dt-svg {
  position: absolute; left: 0; top: 0;
  width: var(--rail);
  height: var(--rig-h);
}

/* The two dashed paths deliberately do NOT use non-scaling-stroke:
   Chromium measures the dash pattern in screen space while pathLength
   normalises in user space, so the two together make the line draw
   ~1/scale too fast and overshoot every fork. Widths are therefore in
   user units — the rail's scale runs 0.38–0.60, so these land between
   about 1.4px and 2.3px (route) and 0.9px and 1.4px (ghost). */

/* the route that was held — one unbroken line, drawn top to tail */
.dt-route {
  fill: none;
  stroke: var(--purple-soft);
  stroke-width: 3.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 7px rgba(91, 76, 255, 0.5));
  stroke-dasharray: 1;
  stroke-dashoffset: calc(1 - var(--draw, 1));
}
/* the branch not taken */
.dt-ghost {
  fill: none;
  stroke: rgba(255, 255, 255, 0.19);
  stroke-width: 2.4;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 0;
}
.dt-gdot { fill: rgba(255, 255, 255, 0.22); }
/* the hairline that carries the fork out to its question */
.dt-lead {
  fill: none;
  stroke: rgba(148, 138, 255, 0.3);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.dt-node {
  fill: var(--bg);
  stroke: var(--purple-soft);
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
.dt-term {
  fill: none;
  stroke: var(--pink);
  stroke-width: 2.6;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 10px rgba(255, 77, 128, 0.7));
}

.dt-forks {
  list-style: none;
  margin: 0; padding: 0;
  position: absolute;
  left: calc(var(--rail) + var(--lead-gap));
  right: 0; top: 0; bottom: 0;
}
.dt-fork {
  position: absolute;
  left: 0; right: 0;
  top: var(--y, 0%);
  transform: translateY(-50%);
}
/* bridges the gap between the rail's leader and the question */
.dt-fork::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--lead-gap));
  top: 50%;
  width: var(--lead-gap); height: 1px;
  background: rgba(148, 138, 255, 0.3);
}
.dt-tag {
  display: flex; align-items: baseline; gap: 12px;
  font-size: 10px; letter-spacing: 0.22em;
  color: var(--faint);
  margin-bottom: 9px;
}
.dt-i { color: var(--purple-soft); }
.dt-state::before { content: "· "; opacity: 0.5; }
.dt-state::after { content: "FORK"; }
.dt-q {
  font-size: clamp(17px, 1.55vw, 24px);
  line-height: 1.32;
  letter-spacing: -0.018em;
  font-weight: 500;
  color: var(--text);
  /* 46ch keeps even the longest question to two lines, so no fork
     block ever grows tall enough to collide with the one below it */
  max-width: 46ch;
  text-wrap: pretty;
}

.dt-foot {
  position: relative; z-index: 2;
  width: min(1060px, 100%);
  display: flex; justify-content: space-between; gap: var(--s3);
  flex-wrap: wrap;
  border-top: 1px solid var(--line);
  margin-top: clamp(20px, 3vh, 40px);
  padding-top: var(--s2);
  font-size: 9.5px; letter-spacing: 0.2em;
  color: var(--faint);
  opacity: 0.8;
}
.dt-foot-end { color: var(--purple-soft); opacity: 0.85; }

/* ---- >=981px + JS + motion: the scene pins and the route draws ---- */
@media (prefers-reduced-motion: no-preference) and (min-width: 981px) {
  .js #ef-ask.scene { height: 400vh; }
  .js #ef-ask .scene-stage {
    position: sticky;
    height: 100vh; height: 100svh;
    overflow: hidden;
    padding: calc(var(--nav-h) + var(--s3)) var(--pad) var(--s4);
  }

  .js .dt-route { --draw: 0; }

  /* forks arrive with the route; none of this state exists without JS */
  .js .dt-ghost,
  .js .dt-gdot,
  .js .dt-lead { opacity: 0; transition: opacity 0.7s ease; }
  .js .dt-ghost { stroke-dashoffset: 1; transition: opacity 0.7s ease, stroke-dashoffset 0.9s cubic-bezier(0.22, 0.6, 0.2, 1); }
  .js .dt-lvl.on .dt-ghost { opacity: 1; stroke-dashoffset: 0; }
  .js .dt-lvl.on .dt-gdot,
  .js .dt-lvl.on .dt-lead { opacity: 1; }

  .js .dt-node {
    opacity: 0.22;
    transform: scale(0.7);
    transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.3, 0.7, 0.3, 1), stroke 0.5s ease;
  }
  .js .dt-lvl.on .dt-node { opacity: 1; transform: scale(1); }
  .js .dt-lvl.now .dt-node {
    stroke: var(--pink);
    transform: scale(1.35);
    filter: drop-shadow(0 0 8px rgba(255, 77, 128, 0.75));
  }

  .js .dt-term { opacity: 0; transition: opacity 0.6s ease; }
  .js .dt-rig.done .dt-term { opacity: 1; }

  .js .dt-fork {
    opacity: 0.13;
    transform: translateY(-50%) translateY(16px);
    transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 0.6, 0.2, 1);
  }
  .js .dt-fork.on { opacity: 0.52; transform: translateY(-50%); }
  .js .dt-fork.now { opacity: 1; }
  .js .dt-fork.now .dt-i { color: var(--pink-soft); }
  /* the payoff: one lit route, and all four questions standing */
  .js .dt-rig.done .dt-fork.on { opacity: 0.95; }

  .js .dt-state::after { content: "PENDING"; }
  .js .dt-fork.on .dt-state::after { content: "ANSWERED"; }
  .js .dt-fork.now .dt-state::after { content: "ASKED"; }

  .js .dt-foot-end { opacity: 0.35; transition: opacity 0.6s ease; }
  .js .dt-rig.done ~ .dt-foot .dt-foot-end { opacity: 0.85; }
}

/* ============================================================
   7 · THE CLOSE
   ============================================================ */
.ef-close { text-align: center; }
.close-h2 { margin: 0 auto; max-width: 18ch; }
.ef-close .chapter-line-rest { text-align: center; }
.ef-close .cta-row { justify-content: center; margin-top: var(--s5); }
.siblings {
  margin-top: var(--s7);
  border-top: 1px solid var(--line);
  padding-top: var(--s4);
  text-align: left;
}
.sib-head {
  font-size: 10.5px; letter-spacing: 0.24em;
  color: var(--faint);
  margin-bottom: var(--s2);
}
.sib {
  display: grid;
  grid-template-columns: 62px 1fr 24px;
  align-items: center;
  gap: var(--s2);
  border-bottom: 1px solid rgba(255, 255, 255, 0.055);
  padding: var(--s3) 0;
  transition: padding-left 0.35s cubic-bezier(0.3, 0.7, 0.3, 1), border-color 0.35s ease;
}
.sib:hover { padding-left: 10px; border-bottom-color: rgba(148, 138, 255, 0.35); }
.sib-num { font-size: 11.5px; letter-spacing: 0.1em; color: var(--purple-soft); }
.sib-name { font-size: clamp(16px, 1.5vw, 19px); font-weight: 500; letter-spacing: -0.01em; }
.sib-arrow {
  justify-self: end;
  font-size: 15px; color: var(--faint);
  transform: translateX(-4px);
  transition: transform 0.3s cubic-bezier(0.3, 0.7, 0.3, 1), color 0.3s ease;
}
.sib:hover .sib-arrow { transform: translateX(0); color: var(--purple-soft); }

/* ============================================================
   RESPONSIVE — every pinned scene collapses to static flow
   ============================================================ */
@media (max-width: 1100px) {
  .scope-item { grid-template-columns: 62px 1fr; gap: var(--s2); }
}

@media (max-width: 980px) {
  #ef-title.scene { height: auto; }
  #ef-title .scene-stage {
    position: static;
    height: auto;
    min-height: 82svh;
    overflow: visible;
    padding: calc(var(--nav-h) + var(--s6)) var(--pad) var(--s6);
  }
  #ef-title .ghost-center { display: none; }
  #ef-title .scroll-cue { display: none; }
  .ef-h1 { max-width: 100%; font-size: clamp(30px, 8.2vw, 46px); }

  .pair-grid { grid-template-columns: 1fr; }

  #ef-decide.scene { height: auto; }
  #ef-decide .scene-stage {
    position: static;
    height: auto;
    overflow: visible;
    padding: var(--section-gap) var(--pad) 0;
  }
  /* the head stacks rather than hiding half of itself. STRUCTURES IN PLAY
     04 -> 01 is the scene's own scoreboard — it was display:none here, so
     even once the scene runs the phone would be told nothing is being set
     aside. It is one 10.5px line; it stacks. */
  .decide-head {
    flex-direction: column; align-items: flex-start;
    gap: var(--s1);
    border-bottom: 0; padding-bottom: 0;
  }
  .decide-rig { gap: var(--s3); }
  .goal-row { grid-template-columns: 1fr 1fr; }
  .ent-row { grid-template-columns: 1fr 1fr; }
  .cal-row { height: 84px; }

  /* THE BRANCH, RESHAPED FOR TWO COLUMNS.
     The SVG fan lands its four endpoints at 12.5 / 37.5 / 62.5 / 87.5% of
     the row — geometry drawn for the four-across .ent-row, which does not
     exist below 981px. Aimed at a 2x2 grid it points between the cards
     rather than at them, and below 640px it used to be deleted outright,
     which took the origin diamond with it: the single filing at the apex
     is the SUBJECT of the device, not decoration. So the fan is replaced,
     not removed, by the same statement in a shape two columns can hold —
     one stem out of the origin, then a crossbar whose two ends drop onto
     the two column centres. Same claim: one origin, these structures.
     Both halves draw with the scroll off --brstem / --brfan (js BEAT 2)
     and default to 1, so no-JS and reduced motion get them fully drawn. */
  .branch { height: 66px; }
  .br-svg { display: none; }
  .branch::before {
    content: "";
    position: absolute; left: 50%; top: 4px;
    width: 1px; height: 22px; margin-left: -0.5px;
    background: rgba(148, 138, 255, 0.55);
    transform: scaleY(var(--brstem, 1));
    transform-origin: 50% 0;
  }
  /* one box, three borders: the crossbar and both drops in a single
     element, so the ends land exactly on the column centres */
  .branch::after {
    content: "";
    position: absolute; left: 25%; right: 25%; top: 26px;
    height: 20px;
    border: 1px solid rgba(148, 138, 255, 0.55);
    border-bottom: 0;
    transform: scaleX(var(--brfan, 1));
    transform-origin: 50% 0;
  }

  /* the world runs behind the whole page — on a small screen the
     device needs its own ground to stay legible. No blur: see §GLASS,
     COSTED FOR A PHONE at the foot of this file. --glass-flat is the
     opacity that stands in for one. */
  .goal, .ent, .cal-block { background: var(--glass-flat); }
  .cal-block { border: 1px solid var(--line); border-radius: 14px; padding: var(--s3); }

  /* THE DECISION TREE restacks: the SVG rail is decoration, so it goes,
     and the route is redrawn as a gutter spine with a node and a ghost
     stub at each fork. Every question keeps its full size and opacity. */
  .ask-head {
    flex-direction: column; align-items: flex-start;
    gap: var(--s1);
  }
  /* .ask-head is already stacked above, so the fork meter has a line of
     its own and no longer needs hiding — FORK 01/04 advancing is half of
     how the reader knows where the route has got to */
  .dt-svg { display: none; }
  .dt-rig { height: auto; }
  .dt-forks {
    position: relative;
    left: auto; right: auto; top: auto; bottom: auto;
    display: flex; flex-direction: column;
    gap: clamp(30px, 5vh, 52px);
    padding-left: 44px;
  }
  /* the spine */
  .dt-forks::before {
    content: "";
    position: absolute; left: 13px; top: 8px; bottom: 14px;
    width: 1px;
    background: linear-gradient(180deg,
      transparent, rgba(148, 138, 255, 0.55) 7%,
      rgba(148, 138, 255, 0.55) 93%, transparent);
  }
  /* the terminus */
  .dt-forks::after {
    content: "";
    position: absolute; left: 5px; bottom: 8px;
    width: 17px; height: 2px; border-radius: 2px;
    background: var(--pink);
    box-shadow: 0 0 10px rgba(255, 77, 128, 0.7);
  }
  .dt-fork {
    position: relative;
    left: auto; right: auto; top: auto;
    transform: none;
    opacity: 1;
  }
  /* the node */
  .dt-fork::before {
    left: -36px; top: 6px;
    width: 10px; height: 10px;
    border-radius: 50%;
    border: 1.5px solid var(--purple-soft);
    background: var(--bg);
    box-shadow: 0 0 10px rgba(91, 76, 255, 0.45);
  }
  /* the branch not taken, peeling off into the gutter */
  .dt-fork::after {
    content: "";
    position: absolute; left: -30px; top: 14px;
    width: 22px; height: 1px;
    background: rgba(255, 255, 255, 0.2);
    transform-origin: 0 50%;
    transform: rotate(48deg);
  }
  .dt-q { font-size: clamp(19px, 4.4vw, 26px); max-width: 30ch; }
}

/* ============================================================
   THE SAME FILM, ON A PHONE  (<=980px, with JS and motion)
   Both devices on this page used to be switched off below 981px: the
   scene functions returned early and the state blocks above stopped at
   981, so a phone was handed the RESOLVED picture — three structures
   already SET ASIDE, the winner already glowing, the calendar already
   standing, every fork already forked. The conclusion before the
   question, which is the reverse of the argument the copy makes.

   The scenes now run at every width (js: flowP). Nothing is pinned here
   and nothing needs to be — the section's own travel is the timeline.
   These rules give the narrow layout the beats desktop gets, in the
   shape the narrow layout already restacked itself into.

   Scoped .js + no-preference, and placed after the <=980 block so it
   wins on source order. With no JS or reduced motion none of it applies
   and the static, complete end state stands exactly as before.
   ============================================================ */
@media (prefers-reduced-motion: no-preference) and (max-width: 980px) {
  /* A phone is read in daylight, at arm's length, over a ground that is
     18% transparent. Desktop's 0.24 / 0.30 hold-backs are legible on a
     bright monitor in a dim room and nowhere else, so the narrow film
     says the same thing a little louder. Both are transient states on
     the way to the same resolved picture. */
  .js .goal { opacity: 0.34; }
  .js .ent.out { opacity: 0.42; }

  /* the branch's two halves start undrawn, exactly as .br-path does on
     desktop — without this they would render fully drawn until the
     reader comes within range of the scene and then snap back to zero */
  .js .decide-stage { --brstem: 0; --brfan: 0; }

  /* --- THE DECISION TREE ---
     The restacked spine draws with the route, each node lights as its
     question is reached and turns pink while it is the one being asked,
     the ghost stub peels off with it, and the terminus arrives when the
     route runs unbroken. The FORK / PENDING / ASKED / ANSWERED state
     machine moves here from the >=981px block.

     WHAT IS DELIBERATELY NOT PORTED: desktop's `.js .dt-fork {
     opacity: 0.13 }`. On a phone the four questions ARE the section —
     they carry approved copy at full size, and `.dt-fork { opacity: 1 }`
     above is what keeps them readable. "Not yet reached" is expressed on
     the node and the tag word only, never on the question. */
  .js .dt-rig { --spine: 0; }
  .js .dt-forks::before {
    transform: scaleY(var(--spine, 1));
    transform-origin: 50% 0;
  }
  .js .dt-fork::before {
    opacity: 0.3;
    transform: scale(0.72);
    transition: opacity 0.45s ease, transform 0.45s cubic-bezier(0.3, 0.7, 0.3, 1),
                border-color 0.45s ease, box-shadow 0.45s ease;
  }
  .js .dt-fork.on::before { opacity: 1; transform: scale(1); }
  .js .dt-fork.now::before {
    border-color: var(--pink);
    box-shadow: 0 0 12px rgba(255, 77, 128, 0.6);
  }
  .js .dt-fork::after { opacity: 0; transition: opacity 0.5s ease; }
  .js .dt-fork.on::after { opacity: 1; }
  .js .dt-forks::after { opacity: 0; transition: opacity 0.5s ease; }
  .js .dt-rig.done .dt-forks::after { opacity: 1; }

  .js .dt-state::after { content: "PENDING"; }
  .js .dt-fork.on .dt-state::after { content: "ANSWERED"; }
  .js .dt-fork.now .dt-state::after { content: "ASKED"; }
  .js .dt-fork.now .dt-i { color: var(--pink-soft); }

  /* the payoff line, held back until the route has run top to tail */
  .js .dt-foot-end { opacity: 0.4; transition: opacity 0.6s ease; }
  .js .dt-rig.done ~ .dt-foot .dt-foot-end { opacity: 0.85; }
}

@media (max-width: 640px) {
  .goal-row { grid-template-columns: 1fr; }
  .ent-row { grid-template-columns: 1fr 1fr; }
  .cal-row { gap: 4px; height: 72px; }
  .cal-m { font-size: 8px; }
  .scope-item { grid-template-columns: 46px 1fr; }
  .ef-close .cta-row .btn { width: 100%; justify-content: center; }
  .sib { grid-template-columns: 52px 1fr 20px; }
}

/* ============================================================
   SHORT WINDOWS (>=981px) — the pinned film, compacted
   A pinned stage clips: it is 100svh with overflow:hidden, there is no
   inner scroll, and no scroll position will ever reveal what does not
   fit. main.js's fit guard cannot help here — it only runs at vw<=980,
   because on desktop a stage may overflow on purpose. This page shipped
   nothing but viewport clamps and every one of them floors at a value
   sized for a tall window, so at 1024x600 the decide rig wants ~518px
   against 458px of usable stage and the compliance calendar — BEAT 4,
   the payoff — is simply cut off; #ef-ask loses the fourth question the
   same way (380px rig floor + head + foot against 466px).

   The fix is to shrink the DEVICES, not to unpin the scenes. Unpinning
   at short heights would also collapse the engine's own p (s.range =
   s.h - vh goes to ~1 the moment the section is height:auto), so the
   desktop film would flash past on exactly the laptops it was meant to
   rescue. Nothing below applies above 640px of viewport height, which
   keeps every normal laptop on the metrics it has today.
   ============================================================ */
@media (min-width: 981px) and (max-height: 640px) {
  #ef-decide .scene-stage { gap: 12px; }
  .decide-rig { gap: 10px; }
  .branch { height: 56px; }
  .ent { padding: 12px clamp(12px, 1.4vw, 20px); }
  .cal-block { margin-top: 4px; padding-top: 12px; }
  .cal-row { height: 74px; }

  .dt-rig { --rig-h: clamp(320px, 52vh, 600px); }
  .ask-head { margin-bottom: 16px; }
  .dt-foot { margin-top: 16px; }
}

/* ============================================================
   REDUCED MOTION — the whole page, static and complete
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  #ef-title.scene, #ef-decide.scene, #ef-ask.scene { height: auto !important; }
  #ef-title .scene-stage,
  #ef-decide .scene-stage,
  #ef-ask .scene-stage {
    position: static; height: auto; overflow: visible;
    padding-top: var(--section-gap);
  }
  #ef-title .ghost-center { display: none; }
  .ef-sub { opacity: 1; transform: none; }
  /* the tree is simply already drawn, and every question is lit */
  .dt-route { --draw: 1; }
  .dt-fork { opacity: 1; }
  /* and so is the narrow layout's version of it: the spine, and the
     branch's stem and crossbar, are all scrubbed vars whose hold-back
     states live in a no-preference block. Restated here so a phone on
     reduced motion is never left with an undrawn device. */
  .dt-rig { --spine: 1; }
  .decide-stage { --brstem: 1; --brfan: 1; }
}

/* ============================================================
   NO-JS — same static, complete play
   ============================================================ */
.no-js #ef-title .scene-stage,
.no-js #ef-decide .scene-stage,
.no-js #ef-ask .scene-stage {
  position: static;
  height: auto;
  overflow: visible;
  padding-top: var(--section-gap);
}
.no-js #ef-title .ghost-center,
.no-js #ef-title .scroll-cue { display: none; }
.no-js .ef-sub { opacity: 1; transform: none; }
/* the pinned tree and its pre-states are all gated on .js, so the
   no-JS page already gets the drawn route and four lit questions —
   these are here so that stays true if the gating ever slips */
.no-js .dt-route { --draw: 1; }
.no-js .dt-fork { opacity: 1; }
.no-js .dt-ghost { stroke-dashoffset: 0; }
.no-js .dt-rig { --spine: 1; }
.no-js .decide-stage { --brstem: 1; --brfan: 1; }


/* ============================================================
   GLASS, COSTED FOR A PHONE (page-scoped)
   css/main.css flattens its OWN glass components below 980px, but it cannot
   reach the devices this page declares. backdrop-filter re-blurs everything
   behind an element on every frame it moves, and these sit inside a
   scrolling film — it is the most expensive thing a handset does here.

   Blur removed below 980px. Where the surface was too sheer to survive
   without it (var(--surface) is 3.5% white) an opaque base is added as
   background-COLOR, not background, so any ruled gradient painted on top
   of it is left intact.
   ============================================================ */
@media (max-width: 980px) {
  .pair-panel {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: var(--glass-flat);
  }
  .goal, .ent, .cal-block {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
