/*
  Homepage hero, Variant B: the Operational Thread.

  Loaded only when the homepage renders Variant B. See the conditional enqueue
  in functions.php. Variant A visitors never download this file.

  Everything here is scoped under .si-thread-hero. Nothing overrides a shared
  token or a shared class, so this file cannot change any other page, and
  deleting it removes the variant cleanly.

  Colours come from the tokens in si-theme.css. The two that carry meaning are
  --si-emerald for the thread and --si-orange for the approval seam, and the
  orange appears nowhere else in this hero, which is what makes it readable as a
  legend rather than as decoration.

  No @font-face. Every face used here is already declared and served by
  si-theme.css, so this variant adds no font request.
*/

.si-thread-hero {
  position: relative;
  background: linear-gradient(168deg, var(--si-night-sky) 0%, var(--si-prussian) 62%, #0d1b2b 100%);
  color: #fff;
  padding: 96px 24px 72px;
  overflow: hidden;
}

/* A single, cheap radial wash. It was written as the alternative to the ambient
   particle canvas the other hero ran; that hero was retired on 2026-08-21 and
   there is no longer anything to be an alternative to, which does not change
   what this is. Costs nothing to paint and nothing to keep painting, which is
   the difference that matters on a laptop with the fan already up.
   pointer-events: none because it is inset across the top of the hero and a
   decorative wash should never be the target of a gesture. */
.si-thread-hero::before {
  content: '';
  position: absolute;
  inset: -20% -10% auto -10%;
  height: 70%;
  background: radial-gradient(60% 60% at 50% 0%, rgba(70, 180, 145, .14) 0%, rgba(70, 180, 145, 0) 70%);
  pointer-events: none;
}

.si-thread-hero-inner {
  position: relative;
  max-width: 1120px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* ---------- copy ---------- */

.si-thread-eyebrow {
  font-family: var(--si-font-mono), ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--si-emerald);
  margin: 0 0 18px;
}

.si-thread-h1 {
  font-family: var(--si-font-display), system-ui, sans-serif;
  font-weight: 600;
  font-size: clamp(34px, 5.4vw, 62px);
  line-height: 1.06;
  letter-spacing: -.015em;
  margin: 0 0 20px;
}

.si-thread-accent { color: var(--si-emerald); }

.si-thread-sub {
  font-family: var(--si-font-sans), system-ui, sans-serif;
  font-size: clamp(16px, 1.35vw, 19px);
  line-height: 1.62;
  color: rgba(255, 255, 255, .82);
  max-width: 640px;
  margin: 0 0 30px;
}

.si-thread-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
  margin-bottom: 16px;
}

/* Derived from the registry at render time, so it is deliberately quiet: it is
   a fact a visitor can act on, not a headline. */
.si-thread-availability {
  font-family: var(--si-font-sans), system-ui, sans-serif;
  font-size: 14px;
  line-height: 1.55;
  color: rgba(255, 255, 255, .62);
  margin: 0;
  max-width: 560px;
}

/* ---------- the thread ---------- */

.si-thread-figure {
  width: 100%;
  max-width: 880px;
  margin: 46px 0 0;
}

/* Read aloud, never drawn. The diagram's structure (four stages, then a person,
   then three more) is obvious by eye and invisible to a screen reader that is
   handed eight list items with no relationship between them. Given a local name
   rather than a generic .si-visually-hidden, because a utility class introduced
   by a variant stylesheet outlives the variant and turns into a dependency the
   next person cannot see. */
.si-thread-caption {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
  Pinned to the viewBox ratio. With a free height the SVG letterboxes inside the
  box, the spine drifts away from the middle, and every percentage-positioned
  label floats some distance from the node it names. Matching the ratio makes a
  label at left:35.3% sit exactly over the node at x=275, at any width.
*/
.si-thread-visual {
  position: relative;
  width: 100%;
  aspect-ratio: 780 / 430;
  container-type: inline-size;
}

/*
  The diagram is decorative and takes no input.

  pointer-events: none is not defensive styling here. The SVG is absolutely
  positioned across the whole figure, so on a phone it is most of what a thumb
  lands on when somebody starts a swipe over the hero. With the default
  pointer-events: auto, every touch and pointer event in that area targets the
  svg or one of its circles first. Nothing here listens, so nothing was
  cancelled, but the target of a gesture is what decides which element's
  handlers and which element's touch-action apply to it, and this element
  should never be that answer. Setting it none makes the gesture address the
  section underneath, which is ordinary scrollable page.

  It also matches what the markup already says. The svg is aria-hidden with
  focusable="false", so it is out of the accessibility tree and out of tab
  order. This is the same statement for pointer input, and it is what keeps
  the hero honest against the rule that no decorative layer captures touch.

  The labels beside it are a real <ol> outside this rule and stay selectable.
  Held by tests/neural-storm-retirement-test.php and the mobile Playwright
  spec, which starts a swipe over this element and asserts the page scrolls.
*/
.si-thread-visual svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.thread-path-muted {
  fill: none;
  stroke: rgba(255, 255, 255, .10);
  stroke-width: 3;
  stroke-linecap: round;
}

/*
  760 is a little over the path's own length, so the reveal finishes with the
  thread complete rather than the last stretch snapping into place.

  One iteration, forwards. The thread draws and stops. There is no loop here and
  no script that could restart it.
*/
.thread-path-main {
  fill: none;
  stroke: var(--si-emerald);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 760;
  stroke-dashoffset: 760;
  animation: thread-draw 1.7s cubic-bezier(.33, .9, .35, 1) .25s 1 forwards;
}

.thread-node, .thread-end, .thread-human, .thread-human-ring {
  opacity: 0;
  animation: thread-node-in .45s ease-out 1 forwards;
}

.thread-node { fill: var(--si-emerald); }
.thread-end  { fill: #fff; stroke: var(--si-emerald); stroke-width: 3; }

/* The seam. Orange, larger, and ringed, because it is the one place on the
   diagram where the answer to "who is in control" is written down. */
.thread-human      { fill: var(--si-orange); }
.thread-human-ring { fill: none; stroke: var(--si-orange); stroke-width: 2; opacity: 0; }

/* Nodes arrive along the thread rather than all at once, so the eye follows the
   left-to-right order the stages are actually in. */
.thread-n1 { animation-delay: .30s }
.thread-n2 { animation-delay: .55s }
.thread-n3 { animation-delay: .80s }
.thread-n4 { animation-delay: 1.05s }
.thread-n5 { animation-delay: 1.30s }
.thread-n6 { animation-delay: 1.55s }
.thread-n7 { animation-delay: 1.72s }
.thread-n8 { animation-delay: 1.90s }

@keyframes thread-draw    { to { stroke-dashoffset: 0 } }
@keyframes thread-node-in { to { opacity: 1 } }

/* ---------- stage labels ---------- */

.thread-labels {
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  inset: 0;
}

/*
  Widths are container percentages, not pixels, and that is load-bearing.

  The label positions are already percentages of the SVG box, so they scale with
  it. Fixed-pixel widths do not. Narrow the figure and the boxes stay their
  original size while the gaps between them close, so a pair that clears at
  880px overlaps at 590px, which is exactly the width the two-column desktop
  layout below hands this diagram. In cqw both the positions and the boxes are
  fractions of the same container, so geometry that reads correctly at one width
  reads correctly at every width.

  11.8cqw and 13.4cqw are the original 104px and 118px expressed against the
  880px figure they were tuned on, so the wide layout is unchanged.

  Font size is the one thing that cannot scale freely: below about 10px a label
  stops being readable, so it is clamped rather than left proportional.
*/
.thread-label {
  position: absolute;
  transform: translateX(-50%);
  width: 11.8cqw;
  font-family: var(--si-font-sans), system-ui, sans-serif;
  font-size: clamp(10.5px, 1.42cqw, 13px);
  line-height: 1.35;
  color: rgba(255, 255, 255, .74);
  text-align: center;
}

.thread-label.is-approval {
  color: #ffc98d;
  font-weight: 600;
}

/* Lower lane, y=250: the work before approval. */
.thread-s1 { left: 7.1%;  top: 63% }
.thread-s2 { left: 21.2%; top: 42% }
.thread-s3 { left: 35.3%; top: 63% }
.thread-s4 { left: 49.4%; top: 42% }
.thread-s5 { left: 60.3%; top: 63% }
/* Upper lane, y=150: what only approved work reaches. */
.thread-s6 { left: 71.2%; top: 19%; width: 13.4cqw }
.thread-s7 { left: 83.3%; top: 40%; width: 13.4cqw }
.thread-s8 { left: 96%;   top: 19%; width: 13.4cqw; text-align: right; transform: translateX(-100%) }

.si-thread-legend {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--si-font-sans), system-ui, sans-serif;
  font-size: 12px;
  color: rgba(255, 255, 255, .55);
  margin: 22px 0 0;
}

.si-thread-legend-dot {
  width: 9px;
  height: 9px;
  border-radius: 100px;
  background: var(--si-orange);
  flex: 0 0 auto;
}

/* ---------- trust row ---------- */

.si-thread-trust {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 28px;
  margin: 44px 0 0;
  padding: 0;
  font-family: var(--si-font-mono), ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .48);
}

/* ---------- wide desktop: two columns ---------- */

/*
  Stacked, this hero is taller than a laptop viewport, and the casualty is the
  seam. A 1440x900 capture of the stacked layout put the orange node on the last
  visible row of pixels: the one element the whole direction exists to show was
  the one element a visitor had to scroll to find. A hero whose argument is below
  the fold is not making the argument.

  Two columns fix that by spending horizontal room instead of vertical. The
  headline drops from 62px to about 49px, which is what lets the copy hold a
  ~560px column without the H1 wrapping mid-phrase, and the diagram sits beside
  the copy rather than under it.

  1080px is the breakpoint because below it the copy column gets too narrow for
  the headline to stay on two lines, and a three-line headline beside a squeezed
  diagram is worse than the stacked layout.
*/
@media (min-width: 1080px) {
  .si-thread-hero { padding: 76px 40px 68px }

  .si-thread-hero-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
    align-items: center;
    column-gap: 48px;
    max-width: 1240px;
    text-align: left;
  }

  .si-thread-h1 { font-size: clamp(40px, 3.5vw, 54px) }

  .si-thread-sub,
  .si-thread-availability { max-width: 34em }

  .si-thread-ctas { justify-content: flex-start }

  .si-thread-figure {
    margin: 0;
    max-width: none;
  }

  .si-thread-legend { justify-content: flex-start }

  /* Spans both columns: these four words describe the platform, not the
     diagram, so they should not read as a caption on it. */
  .si-thread-trust {
    grid-column: 1 / -1;
    justify-content: flex-start;
    margin-top: 54px;
  }
}

/* ---------- phone ---------- */

/*
  Below 760 the diagram is not shrunk, it is replaced. A 780-wide eight-label
  layout at 390 CSS pixels produces 8px type and overlapping labels, which is a
  worse answer than the same eight stages read down the page. The vertical rule
  keeps the sequence and the seam keeps its size and colour, so the one thing
  the diagram exists to say survives the smaller screen.
*/
@media (max-width: 760px) {
  .si-thread-hero { padding: 64px 20px 56px }
  .si-thread-figure { margin-top: 34px }

  .si-thread-visual { aspect-ratio: auto }
  .si-thread-visual svg { display: none }

  .thread-labels { position: static; inset: auto }

  .thread-label {
    position: relative;
    left: auto;
    top: auto;
    transform: none;
    width: auto;
    text-align: left;
    font-size: 14px;
    color: rgba(255, 255, 255, .8);
    padding: 0 0 18px 26px;
    margin-left: 5px;
    border-left: 2px solid rgba(255, 255, 255, .14);
  }

  .thread-label:last-child { padding-bottom: 0; border-left-color: transparent }

  .thread-label::before {
    content: '';
    position: absolute;
    left: -6px;
    top: 3px;
    width: 11px;
    height: 11px;
    border-radius: 100px;
    background: var(--si-emerald);
  }

  .thread-label.is-approval::before {
    left: -8px;
    width: 15px;
    height: 15px;
    background: var(--si-orange);
    box-shadow: 0 0 0 4px rgba(220, 116, 14, .22);
  }

  .thread-s8 { text-align: left; transform: none; width: auto }
  .si-thread-trust { gap: 8px 18px; margin-top: 32px }
}

/* ---------- reduced motion ---------- */

/*
  The finished diagram, immediately. Not a degraded one: same thread, same
  nodes, same seam, no drawing. A visitor who has asked their operating system
  to stop animation gets the whole picture and none of the movement.

  Scoped to this hero rather than declared globally with `*`, because a blanket
  reset in a variant stylesheet would reach into every other component on the
  page and quietly become the site's reduced-motion policy.
*/
@media (prefers-reduced-motion: reduce) {
  .si-thread-hero *,
  .si-thread-hero *::before,
  .si-thread-hero *::after {
    animation: none !important;
    transition: none !important;
  }

  .si-thread-hero .thread-path-main { stroke-dashoffset: 0 !important }

  .si-thread-hero .thread-node,
  .si-thread-hero .thread-end,
  .si-thread-hero .thread-human,
  .si-thread-hero .thread-human-ring { opacity: 1 !important }
}

/* ---------- breadth band ---------- */

/*
  Sits directly under the dark hero and reads as the first light section of the
  page, so it uses the same surface as .si-light-section rather than inventing a
  third background. It carries the argument the hero cannot: that the thread is
  the pattern the whole portfolio is built on, not a description of one product.

  Scoped under .si-breadth, and .si-breadth only renders from
  template-parts/homepage-breadth.php, so like the rest of this stylesheet it is
  inert anywhere the thread hero is not.
*/
.si-breadth {
  background: #fff;
  padding: 64px 24px 56px;
  border-bottom: 1px solid rgba(24, 44, 67, .08);
}

.si-breadth-inner {
  max-width: 1240px;
  margin: 0 auto;
}

.si-breadth-lead {
  font-family: var(--si-font-display), var(--si-font-sans), system-ui, sans-serif;
  font-size: clamp(20px, 2.1vw, 27px);
  line-height: 1.35;
  letter-spacing: -.01em;
  color: var(--si-prussian);
  margin: 0 0 26px;
  max-width: 30em;
}

.si-breadth-cols {
  display: grid;
  gap: 12px 48px;
  margin: 0 0 38px;
}

.si-breadth-col p {
  font-family: var(--si-font-sans), system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.62;
  color: #46586c;
  margin: 0 0 12px;
  max-width: 42em;
}

.si-breadth-col p:last-child { margin-bottom: 0 }

.si-breadth-label {
  font-family: var(--si-font-mono), ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #7b8a9a;
  margin: 0 0 14px;
}

.si-breadth-list {
  list-style: none;
  margin: 0 0 26px;
  padding: 0;
  display: grid;
  gap: 10px;
}

.si-breadth-item-link {
  display: grid;
  gap: 3px;
  padding: 14px 16px;
  border: 1px solid rgba(24, 44, 67, .12);
  border-radius: 9px;
  text-decoration: none;
  background: #fbfcfd;
  transition: border-color .16s ease, background .16s ease;
}

.si-breadth-item-link:hover,
.si-breadth-item-link:focus-visible {
  border-color: var(--si-emerald);
  background: #fff;
}

.si-breadth-item-name {
  font-family: var(--si-font-display), var(--si-font-sans), system-ui, sans-serif;
  font-size: 16px;
  color: var(--si-prussian);
}

.si-breadth-item-desc {
  font-family: var(--si-font-sans), system-ui, sans-serif;
  font-size: 13.5px;
  line-height: 1.45;
  color: #5d6e80;
}

/*
  The access label is derived, never typed, and it is styled quietly on purpose.
  A loud status chip on a homepage invites the eye to read it as a badge of
  progress. It is not one. It is the answer to "can I get this today", and today
  that answer is usually no.
*/
.si-breadth-item-state {
  font-family: var(--si-font-mono), ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #8593a2;
  margin-top: 2px;
}

.si-breadth-more { margin: 0 }

/* The only route from this band to the rest of the portfolio, so it has to be
   hittable with a thumb. As a plain inline link at 15px its box was 21px tall,
   under the 24px minimum target size. The extra height is taken above the text
   rather than around it so the underline does not move. */
.si-breadth-more-link {
  font-family: var(--si-font-sans), system-ui, sans-serif;
  font-size: 15px;
  color: var(--si-azure);
  text-decoration: none;
  border-bottom: 1px solid rgba(0, 120, 212, .35);
  display: inline-block;
  padding-top: 5px;
  padding-bottom: 1px;
}

.si-breadth-more-link:hover,
.si-breadth-more-link:focus-visible {
  border-bottom-color: var(--si-azure);
}

@media (min-width: 760px) {
  .si-breadth-cols { grid-template-columns: repeat(2, minmax(0, 1fr)) }
  .si-breadth-list { grid-template-columns: repeat(2, minmax(0, 1fr)) }
}

@media (min-width: 1080px) {
  .si-breadth { padding: 76px 40px 66px }
  .si-breadth-list { grid-template-columns: repeat(3, minmax(0, 1fr)) }
}
