/* ============================================================
   FEAT-014 — "Three new ways to reach us" launch band
   docs/plans/FEAT-014-BUILD-SPEC.md is the binding contract for
   every rule below. Loaded LAST on every page (after proposal-a.css,
   and after respect-letters.css on respect-approach.html). Touches
   nothing in tokens.css / base.css / components.css / partners.css —
   every declaration below is additive, scoped to the §2 class list.

   Contents
     0. Glyph hook contract — READ BEFORE editing any glyph rule
     1. Launch band layout (.launch, head, grid)
     2. Card mechanics — stretched link, hover/focus, --soon modifier
     3. Status pill (.launch-card__status, __dot)
     4. Glyph animation system (rail / node / active) + keyframes
     5. Card content typography (__tag/__title/__cta/__body/__more)
     6. Solutions hub — detail panels (.launch-detail*)
     7. Nav "New" pip (.nav__new)
     8. Responsive
     9. Reduced motion — explicit glyph safety net (hard gate, §7 G4)
   ============================================================ */

/* ============================================================
   0 · GLYPH HOOK CONTRACT — binding for the markup agents

   Every inline glyph <svg> is authored inside .launch-card__glyph
   (home) or .launch-detail__aside .launch-card__glyph (solutions
   hub, larger). Three data-attributes only — do not invent a fourth:

     [data-glyph-rail]    ONE stroke element (path/line/rect) that
                          draws left-to-right (or around, for the
                          ticket outline) via stroke-dasharray. Author
                          it as a normal static shape — no dasharray
                          attribute needed, this file supplies one via
                          a local custom property. Give it whatever
                          `opacity` the spec's "structural stroke,
                          ~55%" calls for (this file never touches
                          SVG-element opacity, only `transform`, so
                          whatever opacity is authored is the opacity
                          that ships, in every state, always).

     [data-glyph-node]    any element (or wrapping <g>) that "pops
                          in" — the ticks, the likert circles/arc, the
                          QR corner-brackets and modules, the
                          perforation dash. Set a 0-based `--i` in a
                          `style` attribute for stagger order:
                          style="--i:0", style="--i:1", etc. `--i` is
                          optional — omitted, it staggers as index 0
                          (no visible break, just no stagger).

     [data-glyph-active]  the ONE final-accent element per glyph: the
                          booking capsule, the assessments dot, or the
                          <g> wrapping the events QR corner-frame. Give
                          it whichever data-glyph-node/--i treatment
                          above fits (the QR frame's individual
                          modules/brackets carry their own
                          data-glyph-node + --i as children of one
                          data-glyph-active wrapper — see solutions.
                          html's #events panel for the pattern this
                          file expects).

   WHY NO DASHARRAY/PATHLENGTH ATTRIBUTE IS ASKED OF THE MARKUP: two
   sibling agents already drafted glyph markup ahead of this contract
   (index.html and solutions.html — same three glyphs, inconsistent
   in whether the arc/QR children carry data-glyph-node at all). Since
   this file cannot touch either HTML file, every rule below is
   written to degrade safely when a hook is missing or a shape is
   simpler than expected: no `--i` = no stagger (still pops, just
   together); no data-glyph-node on a decorative child = it just
   stays static-visible (opacity/position as authored — never hidden).
   ⚠️ Flagged in this agent's return report as a real cross-file gap:
   index.html's capsule/dot/QR-frame elements do NOT carry
   data-glyph-node the way solutions.html's do, so those specific
   home-page instances render static (safe) rather than staggered.

   THE CORE SAFETY INVARIANT (hard requirement, ties to BUG-003/
   BUG-007): every rule in §4 treats the UNANIMATED, no-class default
   state as the FINAL, fully-drawn look. `.is-visible` only ever adds
   a *replay* of the draw-in on top via `@keyframes` + `animation-
   fill-mode: forwards` (never a `transition` from a hidden base
   state) — so with JS entirely off (`.is-visible` never applied) or
   under reduced motion (animation collapses near-instantly, see §9),
   the glyph was ALREADY showing its finished state the whole time.
   ============================================================ */

/* ============================================================
   1 · Launch band layout — REMOVED 2026-08-31 (FEAT-016)

   .launch__inner / __head / __eyebrow / __title / __lede / __grid /
   __more styled the #launch band's own wrapper and head. The client
   asked for that head deleted and the three cards folded into the
   closing call-to-action, so every one of those selectors is now
   unused across all nine pages (verified: 0 matches). Deleted rather
   than left dangling.

   The amber accent the __eyebrow carried is NOT gone from the system
   — .launch-card__status--soon and .launch-card--soon still own it,
   and that is still the live/in-build cue. See the ".closing" block
   at the foot of this file for what replaced the band.

   .launch-card* and .launch-detail* below are untouched: index.html
   still uses the cards inside the closing band, and solutions.html
   still uses the detail panels.
   ============================================================ */
/* ============================================================
   2 · Card mechanics
   ============================================================ */

.launch-card {
  position: relative; /* anchors the stretched-link ::after, spec §3 */
  display: flex;
  flex-direction: column;
  background: var(--surface-raised);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  transition: transform var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out),
              background var(--dur-mid) var(--ease-out),
              box-shadow var(--dur-mid) var(--ease-out);
}

/* The ONLY <a> in a card, stretched to fill it (spec §3, non-
   negotiable). .launch-card__more below is aria-hidden and purely
   visual — it must never become a second link. */
.launch-card__cta {
  color: var(--text-100);
  text-decoration: none;
}
.launch-card__cta::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}

/* :hover / :focus-within twin, every time (hard requirement #5).
   :focus-within also matches the .launch-card__cta <a> being focused
   directly — it is not only for a focused descendant — so this one
   rule covers both mouse and keyboard without a separate :focus rule. */
.launch-card:hover,
.launch-card:focus-within {
  transform: translateY(-4px);
  border-color: var(--jade-400);
  background: var(--ink-750);
  box-shadow: var(--shadow-glow);
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}
/* Mouse-only hover shouldn't wear a keyboard focus ring. */
.launch-card:hover:not(:focus-within) { outline: none; }

.launch-card:hover .launch-card__cta,
.launch-card:focus-within .launch-card__cta {
  text-decoration: underline;
  text-decoration-color: var(--jade-400);
  text-underline-offset: 0.15em;
}

.launch-card:hover .launch-card__more svg,
.launch-card:focus-within .launch-card__more svg {
  transform: translateX(3px);
}

/* Amber swap — card 3 only. Jade = live, amber = new/in-build; this
   is the one modifier allowed to repaint the accent (spec §1, §7). */
.launch-card--soon:hover,
.launch-card--soon:focus-within {
  border-color: var(--amber-400);
}

@media (prefers-reduced-motion: reduce) {
  .launch-card:hover,
  .launch-card:focus-within { transform: none; }
}

/* ============================================================
   3 · Status pill
   ============================================================ */

.launch-card__status {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  padding: 0.35em 0.85em;
  border-radius: var(--radius-pill);
  margin-bottom: var(--space-md);
  width: fit-content;
}
.launch-card__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  flex: none;
}

/* Jade / live — 8.1:1 text-vs-fill, see this agent's return report
   for the full contrast workings. */
.launch-card__status--live {
  color: var(--jade-300);
  background: rgba(95, 201, 172, 0.14); /* == --border-soft's tint, reused */
  border: 1px solid rgba(95, 201, 172, 0.3);
}
.launch-card__status--live .launch-card__dot {
  background: var(--jade-400);
  box-shadow: 0 0 8px var(--jade-400);
}

/* Amber / in-build — 8.3:1 text-vs-fill. */
.launch-card__status--soon {
  color: var(--amber-300);
  background: rgba(233, 184, 114, 0.14);
  border: 1px solid rgba(233, 184, 114, 0.3);
}
.launch-card__status--soon .launch-card__dot {
  background: var(--amber-400);
  box-shadow: 0 0 8px var(--amber-400);
}

/* ============================================================
   4 · Glyph animation system

   Colour: the SVG root sets stroke="currentColor"; this file only
   ever sets the `color` (jade default, amber under --soon) so every
   stroke/fill using currentColor — structural or active — inherits
   it. Structural strokes' ~55% dimming is authored INLINE by the
   markup (opacity="0.55") and is never touched here; see §0.
   ============================================================ */

.launch-card__glyph { color: var(--jade-400); }
.launch-card__glyph svg { display: block; overflow: visible; width: 100%; max-width: 176px; height: auto; }

/* Covers both placements seen in the wild: --soon on an ancestor
   (index.html's <article class="launch-card launch-card--soon">) or
   on the glyph wrapper itself (solutions.html's events panel). */
.launch-card--soon .launch-card__glyph,
.launch-card__glyph.launch-card--soon { color: var(--amber-400); }

/* ---------- 4a · Rail — draws once, L→R (or around), on reveal ----------
   --fl-dash is a LOCAL custom property (not :root — hard requirement
   #1 only forbids new :root tokens). Default 72 covers both glyph 1
   and 2's rail, which are geometrically identical horizontal runs
   (x=12→84 in a 96×96 viewBox) in every markup variant seen. The
   events ticket outline is a different, longer, closed shape; 232 is
   a deliberate over-estimate of its ~222-unit perimeter — dasharray
   only needs to be >= the real path length, a slightly-long dash
   just wraps invisibly, it never leaves a gap. */
[data-glyph-rail] {
  --fl-dash: 72;
  stroke-dasharray: var(--fl-dash);
  stroke-dashoffset: 0; /* fully drawn — the safe, always-visible default */
}
.launch-card--soon [data-glyph-rail] { --fl-dash: 232; }

@keyframes fl-rail-draw {
  from { stroke-dashoffset: var(--fl-dash); }
  to   { stroke-dashoffset: 0; }
}
[data-reveal].is-visible [data-glyph-rail] {
  animation: fl-rail-draw var(--dur-mid) var(--ease-out) forwards;
}

/* ---------- 4b · Nodes — pop in, staggered by --i ----------
   transform-box: fill-box is load-bearing, not decorative: SVG
   shapes default to transform-box: view-box, so a bare `transform:
   scale()` would scale from the centre of the *whole 96×96 glyph*,
   not the node's own centre — every tick/circle/module would visibly
   drift toward the middle of the icon instead of growing in place.
   fill-box repoints transform-origin at the element's own bbox. */
[data-glyph-node] {
  transform-box: fill-box;
  transform-origin: center;
  transform: none; /* final/settled — safe default, see §0 */
}

@keyframes fl-node-pop {
  from { transform: scale(0.35); }
  to   { transform: scale(1); }
}
[data-reveal].is-visible [data-glyph-node] {
  animation: fl-node-pop var(--dur-fast) var(--ease-soft) forwards;
  animation-delay: calc(var(--i, 0) * 60ms);
}
/* Events glyph asks for a tighter 50ms step (spec §4 row 3). Same
   specificity as the rule above (attribute + class + attribute, on
   both branches below) — it wins on source order, placed after. */
[data-reveal].is-visible.launch-card--soon [data-glyph-node],
[data-reveal].is-visible .launch-card--soon [data-glyph-node] {
  animation-delay: calc(var(--i, 0) * 50ms);
}

/* Delay-phase safety: with animation-fill-mode: forwards (never
   backwards/both), the element renders its plain, non-animated CSS
   right up until its own animation-delay elapses — i.e. `transform:
   none` above, already the fully "arrived" pose. A node due to pop at
   +240ms is sitting fully in place for the first 240ms, not hidden or
   mid-flight. This is what makes the stagger safe without a bespoke
   reduced-motion rule for the delay itself. */

/* ---------- 4c · Active — the one full-accent element per glyph ----------
   [data-glyph-active] always also matches the generic node-pop rule
   above when it carries data-glyph-node (the booking capsule and the
   assessments dot both do, per §0) — this section LAYERS a second,
   more specific animation on top rather than replacing the pop, so
   both fire together via comma-separated animations. The events QR
   frame is excluded via :not(.launch-card--soon, .launch-card--soon *)
   below: its modules/brackets are already correctly handled as plain
   staggered nodes (§4b) and must not additionally get the capsule/dot
   glow-settle treatment meant for a single element. */
[data-reveal].is-visible [data-glyph-active]:not(.launch-card--soon, .launch-card--soon *) {
  animation:
    fl-node-pop var(--dur-fast) var(--ease-soft) forwards,
    fl-active-settle var(--dur-fast) var(--ease-out) forwards;
  animation-delay: calc(var(--i, 0) * 60ms), calc(var(--i, 0) * 60ms + var(--dur-fast));
}
@keyframes fl-active-settle {
  from { filter: drop-shadow(0 0 0 transparent); }
  to   { filter: drop-shadow(0 0 6px var(--jade-400)); }
}

/* The assessments dot is a <circle> (or wraps one) rather than the
   capsule's <rect> — the one place this file reads SVG shape rather
   than a class, because no class/id reliably tells the two apart in
   either markup variant (see §0). It needs an extra ~51 user-unit
   rightward travel (position 1 → 4 on the five-stop scale, x=16→67)
   layered on top of the settle-glow above; the more specific selector
   (one extra :is()) wins over 4c's base rule without relying on
   source order. */
[data-reveal].is-visible [data-glyph-active]:not(.launch-card--soon, .launch-card--soon *):is(circle, :has(circle)) {
  animation:
    fl-dot-travel var(--dur-slow) var(--ease-soft) forwards,
    fl-active-settle var(--dur-fast) var(--ease-out) forwards;
  animation-delay: 0s, var(--dur-slow);
}
@keyframes fl-dot-travel {
  from { transform: translateX(-51px); }
  to   { transform: translateX(0); }
}

/* ============================================================
   5 · Card content typography
   ============================================================ */

.launch-card__tag {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-500); /* 6.57:1 on --surface-raised, the system's AA floor */
  margin: var(--space-md) 0 var(--space-2xs);
}

.launch-card__title {
  font-size: var(--step-1);
  margin-bottom: var(--space-sm);
}

.launch-card__body {
  color: var(--text-300);
  font-size: var(--step-0);
  margin: 0 0 var(--space-md);
  flex: 1; /* keeps the CTA row aligned across uneven copy lengths */
}

.launch-card__more {
  display: block;
  margin-top: auto;
  padding-top: var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--jade-400);
}
.launch-card--soon .launch-card__more { color: var(--amber-400); }
.launch-card__more svg {
  width: 14px; height: 14px; flex: none;
  display: inline-block; vertical-align: -0.15em; margin-left: 0.5em;
  transition: transform var(--dur-fast) var(--ease-out);
}

/* The card's visible action button (owner instruction 2026-09-02: every
   home door reads "In build" and offers "Tell us you're interested").
   It carries .launch-card__cta, so it is still the ONE stretched link in
   the card — .launch-card__cta::after in §2 fills the whole card, and the
   z-index there keeps the button clickable. margin-top:auto pins the row
   to the bottom across uneven copy lengths, the same job .launch-card__more
   used to do. */
.launch-card__action {
  margin: auto 0 0;
  padding-top: var(--space-sm);
}
/* §2's :hover/:focus-within rule underlines a bare text CTA. On a real
   button that reads as a defect, so opt the button out of it. */
.launch-card__cta.btn,
.launch-card:hover .launch-card__cta.btn,
.launch-card:focus-within .launch-card__cta.btn { text-decoration: none; }
/* Amber stays load-bearing: an in-build card's button follows its status. */
.launch-card--soon .launch-card__cta.btn--ghost:hover { border-color: var(--amber-400); }

/* ============================================================
   6 · Solutions hub — detail panels (solutions.html only)
   ============================================================ */

.launch-detail {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  gap: var(--space-2xl);
  align-items: center;
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(1.15rem, 5vw, 2.5rem);
  padding-block: var(--space-2xl);
  border-top: 1px solid var(--border-soft);
}

.launch-detail:first-of-type { border-top: 0; }

/* .page-cta (subpages.css) was authored for exactly one button, so the
   assessments panel's second action stacked flush against the first with no
   gap at all and the pair read as one block. Scoped to the hub panels, so
   every other page's single-button CTA keeps its original layout. Wraps
   rather than shrinking, so both keep their 44px touch target on a phone.
   NOTE: this must stay OUTSIDE the 860px media query above — it belongs at
   every width, and it was briefly authored inside it, where it silently did
   nothing on desktop. */
.launch-detail .page-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

.launch-detail__head .launch-card__status { margin-bottom: var(--space-md); }
.launch-detail__head h2 {
  font-size: var(--step-2);
  color: var(--text-100);
  margin-bottom: var(--space-sm);
}
.launch-detail__head > p:not([class]) {
  color: var(--text-300);
  max-width: 52ch;
}

.launch-detail__points {
  list-style: none;
  padding: 0;
  margin: var(--space-md) 0;
  display: grid;
  gap: var(--space-xs);
}
.launch-detail__points li {
  position: relative;
  padding-left: 1.3em;
  color: var(--text-300);
  margin-bottom: 0;
}
.launch-detail__points li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.55em;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--jade-400);
}

/* Same visual idiom as proposal-a.css's .pending-note — a dashed top
   rule marks a caveat/status line, already established in this
   bundle rather than invented fresh here. */
.launch-detail__note {
  color: var(--text-500);
  font-size: var(--step--1);
  padding-top: var(--space-sm);
  border-top: 1px dashed var(--border-soft);
  margin-bottom: var(--space-lg);
}

.launch-detail__aside {
  display: flex;
  align-items: center;
  justify-content: center;
}
.launch-detail__aside .launch-card__glyph svg {
  width: min(280px, 100%);
  height: auto;
}

/* ============================================================
   7 · Nav "New" pip — all 9 pages
   ============================================================ */

.nav__new {
  display: inline-flex;
  align-items: center;
  line-height: 1; /* must not grow the nav row — spec §6 */
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--amber-300);
  background: rgba(233, 184, 114, 0.16);
  border: 1px solid rgba(233, 184, 114, 0.35);
  border-radius: var(--radius-pill);
  padding: 0.2em 0.55em;
  margin-left: 0.5em;
  vertical-align: 0.05em; /* optical centring against the mono baseline */
}
.nav__links a:hover .nav__new,
.nav__links a:focus-within .nav__new {
  border-color: var(--amber-400);
}

/* ============================================================
   8 · Responsive — bundle's existing breakpoints only (720/860/640)
   ============================================================ */

@media (max-width: 860px) {
  .launch-detail {
    grid-template-columns: 1fr;
    gap: var(--space-xl);
  }
  .launch-detail__aside {
    order: -1;
    max-width: 220px;
    margin-inline: auto;
  }
}

@media (max-width: 640px) {
  .launch-card { padding: var(--space-lg); }
}

/* ============================================================
   9 · Reduced motion — explicit glyph safety net (hard gate G4)

   base.css's global kill-switch (line ~162) already forces every
   animation/transition duration to ~0 and iteration-count to 1, which
   collapses all @keyframes above to their end state almost instantly
   — but it does not zero animation-delay. This block is the explicit,
   redundant-on-purpose guarantee the brief demands: nothing in this
   feature may EVER be caught mid-draw, mid-pop, or delayed under
   reduced motion. Opacity is deliberately untouched here — this file
   never animates opacity (see §4), so the inline 0.55/1 authored on
   each element already IS the correct final state; forcing opacity:1
   here would wrongly brighten the structural strokes past their
   intended ~55% dimming.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  [data-glyph-rail] {
    animation: none !important;
    stroke-dashoffset: 0 !important;
  }
  [data-glyph-node],
  [data-glyph-active] {
    animation: none !important;
    transform: none !important;
    filter: none !important;
  }
  .launch-card__more svg,
  .nav__new { transition: none !important; }
}

/* ============================================================
   Closing band (FEAT-016) — the products merged into the call to action
   #launch (eyebrow/h2/lede) is deleted per the client instruction;
   its three product cards resolve the client's own "Discover the
   Right Solution for You" line, so that string survives as a caption
   over the doors instead of as a button. Design C of three; the chooser that
   compared them is archived at docs/archives/2026-08-31-232451-home-section-choosers/.
   Scoped under .closing, the section at the foot of index.html.
   ============================================================ */

/* ---------- 2 · The doors grid ----------
   Fixed 3-up (the retired .launch__grid used auto-fit) — these three ARE the
   discovery step, so they hold the row deliberately, only breaking
   to 1-up once there's no room to read a door as a door. */
.closing .closing__doors {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
  align-items: stretch;
  margin-bottom: var(--space-xl);
}
@media (max-width: 720px) {
  .closing .closing__doors { grid-template-columns: 1fr; }
}

/* Reuses .launch-card wholesale — background, stretched-link ::after
   on .launch-card__cta, the :hover/:focus-within twin and its
   reduced-motion guard all come from featured.css for free. Only the
   scale changes: bigger surface, bigger type, so the door carries the
   weight the removed button used to. */
.closing .closing__doors .launch-card {
  padding: var(--space-xl) var(--space-xl) var(--space-lg);
  min-height: 300px;
}
.closing .closing__doors .launch-card__tag { font-size: var(--step-0); }
.closing .closing__doors .launch-card__title { font-size: var(--step-2); }
.closing .closing__doors .launch-card__body { font-size: var(--step-0); }

/* ------------------------------------------------------------------
   components.css has `.cta-band p { color: var(--text-300);
   margin-inline: auto; margin-bottom: var(--space-xl) }` at specificity
   0,1,1 — which outranks every .launch-card__* rule in featured.css
   (0,1,0). Putting the cards inside .cta-band therefore flattened the
   jade and amber status pills and the tag to one grey, and gave every
   card <p> the band's centred, extra-tall spacing. These restore
   featured.css's own values at matching specificity. Verified against
   the live #launch band: pills rgb(143,224,201) / rgb(242,206,150),
   tag rgb(140,163,154), body rgb(196,212,205).
   ------------------------------------------------------------------ */
.closing .cta-band .launch-card p {
  margin-inline: 0;
}
.closing .cta-band .launch-card__status {
  margin-bottom: var(--space-md);
}
.closing .cta-band .launch-card__status--live { color: var(--jade-300); }
.closing .cta-band .launch-card__status--soon { color: var(--amber-300); }
.closing .cta-band .launch-card__tag {
  color: var(--text-500);
  margin: var(--space-md) 0 var(--space-2xs);
}
.closing .cta-band .launch-card__body {
  color: var(--text-300);
  margin: 0 0 var(--space-md);
}

/* .cta-band sets text-align:center on the whole band, which the doors
   inherited — the live #launch cards are text-align:start. Variant A
   already resets this; C did not, so its cards were centred and the
   status pill, tag and arrow row drifted off the left edge the card
   idiom is built on. Match the live band. */
.closing .cta-band .launch-card {
  text-align: left;
}
