/* ============================================================
   Home page (/) — everything below the hero
   ------------------------------------------------------------
   Loaded after globals.css and scoped to <body class="page-home">,
   so the shared .section-head / .product-card / .final-cta rules
   keep behaving exactly as they do on every other page.

   The hero sets the grammar this file extends down the page
   (see "Home hero" at the end of globals.css):

     type      left-aligned, display face, tight tracking
     structure hairlines, not card chrome and alternating slabs
     colour    the canvas ramp, carrying meaning not decoration
                 slate    the visitor's own systems
                 indigo   our agent / work in flight
                 emerald  the finished result
     motion    one 7s page tempo; things arrive and settle,
               nothing jumps, no border changes colour on its own

   Two cascade traps to know about before editing:

    1. These rules sit after globals.css's responsive blocks, so a
       plain rule here outranks an earlier @media one at equal
       specificity. Any padding set here has to be restated in the
       media queries at the bottom of this file.
    2. globals.css styles .product-card / .pc-* directly (0,1,0);
       the overrides here lead with .page-home to clear that
       without touching the shared component.
   ============================================================ */

/* ---- Section rhythm ----
   The hero has no slab behind it — it sits on the page background in an
   atmosphere. The sections below now do the same, so the page reads as one
   continuous surface with hairlines carrying the structure. The first rule also
   closes the hero's stat rail, which only draws its own top border. */
.page-home .mission,
.page-home .products,
.page-home .stories,
.page-home .final-cta {
  background: var(--bg);
  border-top: 1px solid var(--border);
  padding: 92px 0;
  position: relative;
}

/* ---- Section head: asymmetric, left-aligned, claim beside qualifier ----
   A separate class from the shared .section-head, which is centred and would
   have to be unwound rule by rule. The hero's ruled eyebrow is deliberately not
   repeated here: it stays the one eyebrow on the page. */
/* Baseline alignment, not top or bottom: the qualifier's first line sits on the
   claim's first baseline whatever length either runs to, which holds the head
   together when the paragraph is two lines and when it is six. */
.page-home .sec-head {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  gap: 26px 56px;
  align-items: baseline;
  margin-bottom: 56px;
}
.page-home .sec-head h2 {
  margin: 0;
  font-size: clamp(1.9rem, 3.4vw, 2.75rem);
  letter-spacing: -0.038em;
  line-height: 1.03;
}
/* The qualifier hangs off the right margin in every head, so the three read as
   one system rather than three separate placements. At 44ch it was wider than
   its own column, which left it starting at the column edge and stopping 18px
   short of the page margin — an alignment to nothing at either end. 38ch is the
   narrowest measure that still holds both the products and use-case lines to
   two lines. */
.page-home .sec-head p {
  justify-self: end;
  margin: 0;
  max-width: 38ch;
  color: var(--muted);
  font-size: clamp(1rem, 1.3vw, 1.08rem);
}

/* ============================================================
   Mission — the claim, then the claim drawn
   ============================================================ */
.page-home .mission { isolation: isolate; }

/* Aurora under the diagram, same treatment as the hero's, anchored low so the
   graphic sits in atmosphere rather than on a flat field. */
.page-home .mission::before {
  content: ""; position: absolute; inset: auto 0 0; height: 76%;
  z-index: -1; pointer-events: none;
  background:
    radial-gradient(40% 68% at 20% 84%, var(--glow-1), transparent 70%),
    radial-gradient(38% 64% at 80% 72%, var(--glow-2), transparent 72%);
  filter: blur(58px);
}

/* The graphic below already carries a band of empty space inside its own
   viewBox, so this section closes tighter than the others rather than stacking
   that air on top of a full 92px. */
.page-home .mission { padding-bottom: 74px; }

/* This head is the one that drops the shared baseline alignment, and the only
   one set as a diagonal: the claim at the top left, the qualifier low and hard
   right, with the space between them left open.

   Both halves of that offset are layout rather than nudges, so they hold at
   every width:

     across  a shorter measure than the column, right-aligned. 48ch computed to
             542px against a 498px column, so it never bit and the paragraph
             simply filled the column; pulling it in lets justify-self: end pin
             it to the page margin and put its left edge where it belongs.
             Shifting the full-width block instead would have hung it past the
             container into the page margin, and off the viewport entirely once
             the container stops growing at its max-width.
     measure 40ch is the widest setting that still holds this to six lines —
             one line fewer and 34px wider than the 37ch it started at, which is
             most of the height back without giving up the offset. 44ch would
             match the column exactly and collapse the whole diagonal.
     down    a margin tracking the heading's own scale, so the paragraph starts
             just below it. The heading is clamp(2.05rem, 4vw, 3.05rem) over two
             lines, so this clamp follows the same curve rather than pinning one
             pixel value that only reads correctly at one viewport. */
.page-home .mission .sec-head { margin-bottom: 28px; align-items: start; }
.page-home .mission .sec-head h2 { font-size: clamp(2.05rem, 4vw, 3.05rem); }
.page-home .mission .sec-head p {
  margin-top: clamp(70px, 7.1vw, 94px);
  max-width: 40ch;
  /* Tighter than the 1.6 body default: six lines of a narrow measure stack up
     fast, and this is a lede rather than running text. */
  line-height: 1.5;
  font-size: clamp(1.02rem, 1.4vw, 1.12rem);
}

/* ============================================================
   The diagram (components/mission-flow.php)
   ------------------------------------------------------------
   Four zones on one row: the chores, the fan that gathers them
   and spreads them across the agents, the agents themselves, and
   the merge into one rail. Only the two fans are SVG — curves are
   the one thing CSS cannot draw. Everything else is a box, so the
   labels stay real text at every width.

   The junction inside the first SVG is where the line style
   changes, and the change is the meaning: dashed before it,
   because that is stop-start work a person is doing; solid after
   it, because it is running.

   Three constants are the contract with the component, which
   mirrors them as MF_ROW / MF_AGENT / MF_AGENT_GAP:

     five rows of --mf-row make the list exactly as tall as both
     viewBoxes, so the gather curves land on the row centres; and
     three --mf-agent boxes with --mf-agent-gap between them,
     centred in that height, land the agents on the rows the fan
     curves aim at.

   Change one here and you must change its twin there. A test
   asserts the two copies still agree.
   ============================================================ */
.mf {
  --mf-row: 38px;
  --mf-agent: 44px;
  --mf-agent-gap: 15px;
  display: grid;
  grid-template-columns: minmax(0, auto) 190px minmax(0, 1fr);
  align-items: center;
  width: 100%;
}

/* ---- The chores ----
   A subgrid, so the label column sizes to the longest chore and every feed
   line ends up the same length — which is what lets one shared keyframe drive
   all five packets at one speed. Without subgrid support the rows simply size
   themselves and the feeds go ragged; nothing breaks. */
.mf-steps {
  display: grid;
  grid-template-columns: auto auto minmax(72px, 1fr);
  grid-auto-rows: var(--mf-row);
  gap: 0 12px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.mf-step {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  align-items: center;
}

/* The per-chore icons carry the chore on a phone only — see the stacked block
   near the bottom of this file. Wide enough for five sentences, five sentences
   are the better telling. */
.mf-icon { display: none; }

/* Each chore starts in a system of the visitor's own, so its marker is slate. */
.mf-tick {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--fx-slate);
}

.mf-chore {
  font-family: var(--font-display), sans-serif;
  font-size: 0.98rem; font-weight: 500; letter-spacing: -0.01em;
  color: var(--fx-label);
  white-space: nowrap;
}
/* The five openings align into a quiet column; the specific half carries the
   weight. --muted rather than the slate of the markers, which is a marker
   colour and too faint to set real text in. */
.mf-every { color: var(--muted); }

/* The feed. A gradient rather than `border-top: dashed`, whose dash length is
   the browser's to choose — these have to match the fan's 4/5 stroke dashes
   exactly, since the two meet end to end. */
.mf-feed {
  height: 1.5px;
  background: repeating-linear-gradient(to right,
    var(--fx-wire) 0 4px, transparent 4px 9px);
}

/* ---- The two fans ---- */
.mf-join { display: flex; align-items: center; }
.mf-gather, .mf-merge { display: block; height: auto; overflow: visible; }
.mf-gather { width: 190px; }
.mf-merge { width: 96px; }

/* Before the junction: dashed, because a person is still doing this. */
.mf-wire {
  fill: none; stroke: var(--fx-wire); stroke-width: 1.5;
  stroke-dasharray: 4 5; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
/* The junction: the single point you hand work to, and the last moment the
   work is one thing. */
.mf-junction { fill: var(--fx-packet); }

/* After it: solid, because it is running. Indigo into the agents, emerald out
   of them — the work is only finished on the far side. */
.mf-link {
  fill: none; stroke: var(--indigo); stroke-opacity: 0.42; stroke-width: 1.5;
  stroke-linecap: round; vector-effect: non-scaling-stroke;
}
/* The fade sits on the group, not the strokes: the three curves share their
   last point, so per-path stroke-opacity compounded there into a segment about
   twice as bright as the rail it runs into. */
.mf-done-fan { opacity: 0.38; }
/* 2, matching .mf-rail's 2px, rather than the 1.5 the dashed half uses. The
   curve and the rail are one line with a change of medium halfway down it, and
   at 1.5 the join read as a step. */
/* Butt caps, not the round ones the dashed half wants: at stroke-width 2 a
   round cap overhangs its endpoint by 1px, and all three of them overhung onto
   the rail's first pixel — a bright hairline exactly at the join. Butt ends the
   curve on the rail's top edge instead. */
.mf-done {
  fill: none; stroke: var(--fx-emerald); stroke-width: 2;
  stroke-linecap: butt; vector-effect: non-scaling-stroke;
}

/* Packets keep their scaled stroke: non-scaling-stroke moves dash lengths into
   device space, which would desynchronise them from pathLength="100". */
.mf-packet {
  fill: none; stroke: var(--fx-packet); stroke-width: 3.5; stroke-linecap: round;
  stroke-dasharray: 14 100;
  stroke-dashoffset: -100;   /* parked past the end until its window opens */
  opacity: 0;
  filter: drop-shadow(0 0 5px var(--fx-packet));
}
.mf-packet-done { stroke: var(--fx-emerald); filter: drop-shadow(0 0 5px var(--fx-emerald)); }

/* ---- The agents, and the run that leaves them ---- */
.mf-run-out { display: flex; align-items: center; min-width: 0; }

/* A flex column rather than absolute positions: three boxes with a gap,
   centred, resolve to exactly the centre lines the fan curves are drawn to. */
.mf-agents {
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--mf-agent-gap);
  height: calc(5 * var(--mf-row));
  flex: none;
  list-style: none; margin: 0; padding: 0;
}
.mf-agent {
  position: relative;
  width: var(--mf-agent); height: var(--mf-agent);
  border-radius: 12px;
  background: var(--fx-frame-bg);
  border: 1px solid color-mix(in srgb, var(--indigo) 55%, transparent);
  display: grid; place-items: center;
}
/* The mascot, at the size where its two lenses still read as a face. The
   drop-shadow is what separates a light grey robot from a near-white panel in
   light mode; on dark it just seats it in the box. */
.mf-agent .wb-svg {
  position: relative;
  display: block;
  width: 28px; height: auto;
  filter: drop-shadow(0 3px 6px rgba(20, 18, 50, 0.30));
}
/* Three mascots blinking on one clock would read as one animation played three
   times. Offset, they read as three separate things thinking. */
.mf-agent:nth-child(2) .wb-lens { animation-delay: 3.4s; }
.mf-agent:nth-child(3) .wb-lens { animation-delay: 4.9s; }

/* One halo element per pass the agent takes part in, rather than a single
   pseudo-element: participation is a subset of the passes, not an offset, so it
   cannot be expressed as one animation with a delay.

   A ring rather than the filled disc the outcome marker uses: there is a robot
   underneath this one, and a disc scaling out of its centre would wipe across
   its face on every pass. */
.mf-agent-halo {
  position: absolute; top: 50%; left: 50%;
  width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border-radius: 50%;
  border: 1.5px solid var(--fx-packet);
  opacity: 0;
}

/* The single continuous run: solid and unbroken, the visual opposite of the
   five dashed feeds. ::before is the line, ::after the packet on it — kept
   separate so brightening the line cannot fade the packet with it. */
.mf-exec { display: flex; align-items: center; flex: 1; min-width: 0; }
.mf-rail { position: relative; flex: 1; min-width: 32px; height: 2px; }
.mf-rail::before {
  /* Square ends, not the 2px radius this had. The rail is 2px thick, so a 2px
     radius made it a stadium — and the merge curve butts against one of those
     ends, where a rounded cap shows up as a 1px dim notch at the join. */
  content: ""; position: absolute; inset: 0;
  background: var(--fx-emerald); opacity: 0.38;
}
.mf-rail::after {
  content: ""; position: absolute; top: -1px; left: -46px;
  width: 46px; height: 4px; border-radius: 4px;
  background: linear-gradient(90deg, transparent, var(--fx-emerald));
  box-shadow: 0 0 8px var(--fx-emerald);
  opacity: 0;
}

.mf-out {
  display: flex; align-items: center; gap: 12px;
  flex: none; margin: 0;
  font-family: var(--font-display), sans-serif;
  font-size: 0.98rem; font-weight: 500; letter-spacing: -0.02em;
  color: var(--mf-out-text);
}
/* Emerald is the ramp's finished-result colour, but --fx-emerald is tuned for
   strokes; real text needs the darker step in light mode to clear 4.5:1. */
:root { --mf-out-text: #047857; }
[data-theme="dark"] { --mf-out-text: #6ee7b7; }

.mf-out-dot {
  position: relative; flex: none;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--fx-emerald);
}
/* The ring around the outcome marker — the one point on the page that means
   "finished", so it is the only marker that gets one. */
.mf-out-dot::before {
  content: ""; position: absolute; inset: -7px; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--fx-emerald) 45%, transparent);
}

/* ---- Motion: one 7s pass, the hero's tempo ----
   0.1–2.5s  the five chores arrive at the junction, one at a time
   2.6s      the junction pings, once the last one lands
   2.8–3.5s  the work fans out across the agents, all at once
   3.6s      all three agents ping together
   4.0–4.7s  their output merges, all at once
   4.9–6.2s  one run crosses to the outcome
   6.3s      the outcome pings; the rest of the cycle is still

   The stagger is the argument: chores arrive one by one because that is what
   doing them by hand is like, and the agents fire together because that is
   what they are for. */
.mf-packet { animation: mf-travel 7s linear infinite; }
.mf-p-2 { animation-delay: 0.26s; }
.mf-p-3 { animation-delay: 0.52s; }
.mf-p-4 { animation-delay: 0.18s; }
.mf-p-5 { animation-delay: 7.10s; }
@keyframes mf-travel {
  0%          { stroke-dashoffset: 14; opacity: 0; }
  2%          { stroke-dashoffset: 14; opacity: 1; }
  21%         { stroke-dashoffset: -100; opacity: 1; }
  22%, 100%   { stroke-dashoffset: -100; opacity: 0; }
}

/* ---- The passes ----
   Three turns of the 7s master cycle make a 21s super-cycle, and each turn
   routes to a different set of agents — three, then one, then two. The set
   lives in the markup (mission-flow.php's $PASSES); all that lives here is the
   arithmetic that turns a pass index into a delay.

   Within a pass the legs either side of the agents have no stagger between
   them: whichever agents this pass uses fire together, because running at once
   is the point of splitting the work at all. */
.mf-pass-1 { --mf-pass: 0; }
.mf-pass-2 { --mf-pass: 1; }
.mf-pass-3 { --mf-pass: 2; }

.mf-packet-fan, .mf-packet-done {
  animation-name: mf-hop;
  animation-duration: 21s;
}
.mf-packet-fan  { animation-delay: calc(2.8s + var(--mf-pass) * 7s); }
.mf-packet-done { animation-delay: calc(4s + var(--mf-pass) * 7s); }
/* A 0.7s hop, which is 3.33% of the super-cycle rather than 10% of one pass. */
@keyframes mf-hop {
  0%          { stroke-dashoffset: 14; opacity: 0; }
  0.35%       { stroke-dashoffset: 14; opacity: 1; }
  3.33%       { stroke-dashoffset: -100; opacity: 1; }
  3.7%, 100%  { stroke-dashoffset: -100; opacity: 0; }
}

/* Arrival is marked only by a halo scaling out of the marker itself. The
   outcome pings once per pass on the 7s cycle — every job ends up done — while
   the agents ping on the 21s one, only for the passes that reach them. */
.mf-junction, .mf-out-dot { position: relative; }
.mf-out-dot::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: var(--fx-emerald); opacity: 0;
  animation: mf-ping 7s ease-out 6.3s infinite;
}
@keyframes mf-ping {
  0%       { opacity: 0.8; transform: scale(1); }
  9%, 100% { opacity: 0; transform: scale(3.6); }
}
.mf-agent-halo {
  animation: mf-ping-pass 21s ease-out infinite;
  animation-delay: calc(3.6s + var(--mf-pass) * 7s);
}
@keyframes mf-ping-pass {
  0%       { opacity: 0.75; transform: scale(0.75); }
  3%, 100% { opacity: 0; transform: scale(2.1); }
}
/* The junction is an SVG circle, so its halo scales from its own box. */
.mf-junction {
  transform-box: fill-box; transform-origin: center;
  animation: mf-junction-ping 7s ease-out 2.6s infinite;
}
@keyframes mf-junction-ping {
  0%       { r: 4; opacity: 1; }
  6%       { r: 7; opacity: 1; }
  14%,100% { r: 4; opacity: 1; }
}

/* Each half of the emerald run lifts as the work reaches it, and both settle
   together once the outcome has fired. Two windows rather than one, because one
   made the whole run flare at 4.6s — after the merge packets had already left
   the agents, and before the rail packet had started. Read in order that is
   backwards twice over: the fan announced itself as its work was leaving, and
   the rail announced itself before any work was on it.
   Staggered, the light travels the line the same way the work does. */
.mf-done-fan { animation: mf-merge-lit 7s ease-out infinite; }
@keyframes mf-merge-lit {
  0%, 55%   { opacity: 0.38; }   /* dim until 3.85s */
  61%, 90%  { opacity: 0.78; }   /* lit 4.27s, as its packets run 4.0-4.7s */
  96%, 100% { opacity: 0.38; }   /* settles with the rail, after the outcome */
}
.mf-rail::before { animation: mf-rail-lit 7s ease-out infinite; }
@keyframes mf-rail-lit {
  0%, 66%   { opacity: 0.38; }   /* dim until 4.62s */
  71%, 90%  { opacity: 0.78; }   /* lit 4.97s, as its packet runs 4.9-6.2s */
  96%, 100% { opacity: 0.38; }   /* both settle by 6.72s, after the 6.3s ping */
}

/* The packet on the rail. `left` rather than a transform because the distance
   is the rail's width, which only the layout knows.
   It stops with its head on the outcome dot's centre — 100% is the rail's far
   end and the dot's near edge, so 6px past that is the middle of the 12px dot,
   and the packet's own 46px length is what the -40px takes back off. Running
   `left` to a flat 100% instead, as this did, put the head 46px past the rail:
   the bar drove straight through the marker and out the other side of its ring,
   a bright green stub pointing at "Just gets done". The dot paints over the
   packet, so ending underneath it hides the tail cleanly. */
.mf-rail::after { animation: mf-rail-run 7s linear 4.9s infinite; }
@keyframes mf-rail-run {
  0%           { left: -46px; opacity: 0; }
  2%           { opacity: 1; }
  17%          { left: calc(100% - 40px); opacity: 1; }
  18.5%        { left: calc(100% - 40px); opacity: 0; }
  18.51%, 100% { left: calc(100% - 40px); opacity: 0; }
}
/* Same window and the same rule as mf-rail-run, turned a quarter: the head
   finishes 6px past the rail's end, which is the outcome dot's centre, and
   dissipates there. The rail is 46px here — exactly the packet's length — so
   an unchecked overshoot was the whole rail again and swept the head down
   across "Just gets done". */
@keyframes mf-rail-run-down {
  0%           { top: -30px; opacity: 1; }
  2%           { opacity: 1; }
  17%          { top: 6px; opacity: 1; }
  18.5%        { top: 6px; opacity: 0; }
  18.51%, 100% { top: 6px; opacity: 0; }
}

/* ============================================================
   Products — three nodes you can pick
   ------------------------------------------------------------
   Each card is built like a node on the hero canvas: a translucent
   rounded panel with a slate hairline and a status dot, no fill and
   no shadow until you reach for it. The ramp assigns one product
   one colour — indigo, sky, emerald — the same three the hero uses
   for work in flight, reasoning, and finished results.

   --pc-accent is the contrast-safe text/marker colour and is
   retuned per theme; --pc-tint is the brand hue, only ever used at
   low opacity for plates, borders and glow.
   ============================================================ */
.page-home .product-grid { gap: 24px; }

.page-home .product-card {
  --pc-accent: var(--accent);
  --pc-tint: var(--indigo);
  position: relative;
  padding: 28px 26px 26px;
  border-radius: 20px;
  background: var(--fx-node-bg);
  border: 1px solid color-mix(in srgb, var(--fx-slate) 38%, transparent);
  box-shadow: none;
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.page-home .pc-indigo  { --pc-accent: #4f46e5; --pc-tint: #6366f1; }
.page-home .pc-sky     { --pc-accent: #0369a1; --pc-tint: #0ea5e9; }
.page-home .pc-emerald { --pc-accent: #047857; --pc-tint: #10b981; }
[data-theme="dark"] .page-home .pc-indigo  { --pc-accent: #a5b4fc; }
[data-theme="dark"] .page-home .pc-sky     { --pc-accent: #7dd3fc; }
[data-theme="dark"] .page-home .pc-emerald { --pc-accent: #6ee7b7; }

.page-home .product-card:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--pc-tint) 55%, transparent);
  box-shadow: 0 22px 46px -24px color-mix(in srgb, var(--pc-tint) 65%, transparent);
}

/* The node's status dot, top right — and a halo that pings out of it once when
   you reach for the card, the same arrival marker the canvas uses. */
.page-home .product-card::before,
.page-home .product-card::after {
  content: ""; position: absolute; top: 32px; right: 26px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--pc-accent);
}
.page-home .product-card::before { opacity: 0.9; }
.page-home .product-card::after { opacity: 0; }
.page-home .product-card:hover::after { animation: pc-ping 1.3s ease-out; }
@keyframes pc-ping {
  from { opacity: 0.75; transform: scale(1); }
  to   { opacity: 0; transform: scale(3.6); }
}

/* Brand plate. All three marks are dark artwork on transparent — pink, black,
   red — so the plate stays light in both themes instead of following --surface;
   a dark plate would erase the black Hermes mark. Same idea as the hero's
   destination nodes, which also hold real brand marks in a neutral frame. */
.page-home .pc-thumb {
  width: 60px; height: 60px; border-radius: 15px; margin-bottom: 22px;
  background: #fbfbfd;
  border: 1px solid color-mix(in srgb, var(--pc-tint) 34%, transparent);
  box-shadow: 0 8px 18px -10px color-mix(in srgb, var(--pc-tint) 75%, transparent);
}
.page-home .pc-thumb .gthumb-logo { width: 64%; height: 64%; filter: none; }

.page-home .product-card h3 {
  margin-bottom: 9px; padding-right: 24px;
  font-size: 1.28rem; letter-spacing: -0.028em;
}
.page-home .pc-tagline {
  margin-bottom: 20px;
  color: var(--text-soft); font-size: 0.96rem;
}

/* Bullet markers are wire stubs in the product's own colour, not check glyphs —
   each line is one hop the product does for you. */
.page-home .pc-bullets { margin: 0 0 22px; }
.page-home .pc-bullets li {
  padding-left: 22px; margin-bottom: 11px;
  color: var(--text-soft); font-size: 0.92rem; line-height: 1.5;
}
.page-home .pc-bullets li:last-child { margin-bottom: 0; }
.page-home .pc-bullets li::before {
  content: ""; position: absolute; left: 0; top: 0.68em;
  width: 11px; height: 1.5px; border-radius: 2px;
  background: var(--pc-accent); opacity: 0.85;
}

.page-home .pc-cta {
  display: flex; align-items: center; gap: 8px;
  margin-top: auto; padding-top: 18px;
  border-top: 1px solid var(--border);
  color: var(--pc-accent); font-size: 0.94rem; font-weight: 600;
}
.page-home .pc-cta span { transition: transform 0.2s ease; }
.page-home .product-card:hover .pc-cta span { transform: translateX(4px); }

/* ============================================================
   Use cases — photo, outcome, detail
   ------------------------------------------------------------
   The card chrome is gone: each one is a framed portrait with the
   team named on the photo and the outcome set below it as the
   loudest line in the column. The team label is the only coloured
   thing, in the ramp's emerald, because what it introduces is a
   finished result.
   ============================================================ */
.page-home .stories .grid-3 { gap: 32px; }

.page-home .hcase { display: flex; flex-direction: column; }

.page-home .hcase-media {
  position: relative; margin: 0 0 20px;
  aspect-ratio: 4 / 5;
  border-radius: 18px; overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
}
/* Stock photography reads as filler at full saturation. Held back, it becomes
   texture the type sits on, and comes back when you reach for the card. */
.page-home .hcase-media img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(0.62) contrast(1.06);
  transform: scale(1.01);
  transition: filter 0.5s ease, transform 0.6s ease;
}
.page-home .hcase:hover .hcase-media img {
  filter: grayscale(0); transform: scale(1.05);
}
.page-home .hcase-media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top,
    rgba(10, 9, 22, 0.84) 0%, rgba(10, 9, 22, 0.30) 34%, rgba(10, 9, 22, 0) 62%);
}

/* The scrim under this label is dark in both themes, so the emerald is pinned
   to the light end of the ramp rather than following --fx-emerald. */
.page-home .hcase-tag {
  position: absolute; left: 18px; right: 18px; bottom: 16px; z-index: 1;
  display: flex; align-items: center; gap: 9px;
  color: #6ee7b7;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase;
}
.page-home .hcase-tag .ck-icon { flex: none; }

.page-home .hcase-result {
  margin: 0 0 12px;
  font-size: clamp(1.28rem, 1.9vw, 1.55rem);
  letter-spacing: -0.03em; line-height: 1.14;
}
/* Grows to absorb the slack in a stretched column, which lands the run strips
   below on one line across all three cards however long each paragraph runs. */
.page-home .hcase p { margin: 0; flex: 1; color: var(--text-soft); font-size: 0.96rem; }

/* ---- The run: what fires, what reasons, where it lands ----
   The paragraph above already says this, but says it in prose. Pulled out as
   three beats it can be read at a glance, and the ramp does the explaining:
   slate for the visitor's own system, sky for the reasoning step, emerald for
   the finished work — the same three colours, in the same order, as the hero
   canvas. The last beat carries the tool's own mark, which is the one spot of
   full colour in a card whose photograph is deliberately held back. */
.page-home .hcase-run {
  display: grid; gap: 13px;
  margin: 20px 0 0; padding: 18px 0 0;
  border-top: 1px solid var(--border);
  list-style: none;
}
.page-home .hcase-beat {
  display: flex; align-items: center; gap: 10px;
  font-size: 0.86rem; line-height: 1.35;
  color: var(--text-soft);
}
.page-home .hcase-beat-dot {
  position: relative; flex: none;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--hb-tone);
}
/* The spine, drawn upward out of each dot but the first, so it spans exactly
   the row gap. Same 4/5 dash as every other wire on the page. */
.page-home .hcase-beat + .hcase-beat .hcase-beat-dot::before {
  content: ""; position: absolute; left: 50%; bottom: 100%;
  transform: translateX(-50%);
  width: 1.5px; height: 13px;
  background: repeating-linear-gradient(to bottom,
    var(--fx-wire) 0 4px, transparent 4px 9px);
}
.page-home .hb-in  { --hb-tone: var(--fx-slate); }
.page-home .hb-ai  { --hb-tone: var(--fx-sky); }
.page-home .hb-out { --hb-tone: var(--fx-emerald); }
/* The landing beat is the payoff, so it is the only one set in the ink colour. */
.page-home .hb-out .hcase-beat-label { color: var(--fx-label); font-weight: 500; }

.page-home .hcase-beat-mark { flex: none; display: block; }

/* ============================================================
   Closing CTA — the page's last wire
   ------------------------------------------------------------
   The indigo slab is gone. This section bookends the hero with the
   same atmosphere, and its top hairline is a dashed wire with one
   packet crossing it into the buttons on the right: the last thing
   the page shows is work arriving where you asked for it.
   ============================================================ */
.page-home .final-cta {
  border-top: 1px dashed color-mix(in srgb, var(--fx-slate) 46%, transparent);
  color: var(--ink); text-align: left;
  isolation: isolate;
}
.page-home .final-cta::before {
  content: ""; position: absolute; inset: -18% -10% auto -10%; height: 132%;
  z-index: -1; pointer-events: none;
  background:
    radial-gradient(34% 62% at 26% 46%, var(--glow-1), transparent 70%),
    radial-gradient(34% 58% at 76% 40%, var(--glow-2), transparent 72%);
  filter: blur(56px);
}

/* The packet. It rides the section's own top border across the full viewport;
   anything past the right edge is clipped by html/body's overflow-x: clip. */
.page-home .final-cta::after {
  content: ""; position: absolute; top: -2px; left: 0; z-index: 1;
  width: 96px; height: 3px; border-radius: 3px;
  background: linear-gradient(90deg, transparent, #34d399);
  box-shadow: 0 0 10px rgba(52, 211, 153, 0.5);
  pointer-events: none;
  animation: fc-dispatch 7s linear infinite;
}
@keyframes fc-dispatch {
  0%           { transform: translateX(-100px); opacity: 0; }
  4%           { opacity: 1; }
  52%          { opacity: 1; }
  58%          { transform: translateX(calc(100vw + 20px)); opacity: 0; }
  58.01%, 100% { opacity: 0; }
}

.page-home .final-cta .fc-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 30px 56px; align-items: center;
}
.page-home .final-cta h2 {
  margin: 0 0 12px; color: var(--ink);
  font-size: clamp(1.9rem, 3.5vw, 2.8rem);
  letter-spacing: -0.038em; line-height: 1.04;
}
.page-home .final-cta p {
  margin: 0; max-width: 50ch; opacity: 1;
  color: var(--muted); font-size: clamp(1.02rem, 1.4vw, 1.12rem);
}
/* The buttons return to the site-wide pair; globals only turns them white
   because the shared section is a solid indigo slab, which this one is not. */
.page-home .final-cta .cta-row { justify-content: flex-end; }
.page-home .final-cta .btn-primary {
  background: var(--indigo-dark); color: #fff;
  box-shadow: 0 8px 20px rgba(79, 70, 229, 0.35);
}
.page-home .final-cta .btn-primary:hover { box-shadow: 0 12px 28px rgba(79, 70, 229, 0.45); }
.page-home .final-cta .btn-ghost {
  background: var(--surface); color: var(--accent); border-color: var(--indigo);
}
.page-home .final-cta .btn-ghost:hover { background: var(--soft); }

/* ============================================================
   Responsive
   ------------------------------------------------------------
   The section padding set at the top of this file outranks
   globals.css's earlier @media rules, so every step down has to
   restate it here.
   ============================================================ */
@media (max-width: 980px) {
  /* Stacked, so there is no cross-column baseline left to align to — and the
     mission's hand-tuned drop becomes a hole between the two blocks. */
  .page-home .sec-head { grid-template-columns: 1fr; align-items: start; gap: 18px; }
  .page-home .sec-head p { justify-self: start; }
  .page-home .mission .sec-head p { margin-top: 0; }
  .page-home .final-cta .fc-grid { grid-template-columns: 1fr; }
  .page-home .final-cta .cta-row { justify-content: flex-start; }

  /* ---- The diagram, turned a quarter-turn ----
     Stacked, the chores become a plain list that wraps and the graph runs down
     the page instead of across it. Everything below is a rotation, not a second
     design: both fans are text-free, so rotate(90deg) reuses the desktop curves,
     the packets on them and the whole 21s schedule untouched.

     The arithmetic that makes it line up: rotating maps the gather's three
     outputs, and the merge's three inputs, onto x = 36 / 95 / 154 of a 190px
     box. Three 38px agents with 21px between them, centred in 190px, land on
     exactly those three centres — which is what --mf-agent-gap-narrow is for.

     Rotation also reverses their order, so the row is laid out row-reverse to
     put agent 0 back under its own curve. Without that the packet travels one
     branch while the robot on the opposite side pings. */
  .mf { grid-template-columns: 1fr; justify-items: center; }
  .mf-feed { display: none; }

  /* ---- The chores, as five marks on the five mouths ----
     Rotating the gather opens its five curves at x = 19 / 57 / 95 / 133 / 171
     of a 190px box. Five 38px cells across that same 190px put their centres on
     exactly those five numbers, so each icon sits on the mouth of its own
     curve — which is the correspondence the stacked list never had, five
     sentences down the left with the wires starting somewhere off to the side.

     Row-reverse for the same reason .mf-agents needs it: a quarter turn
     reverses the order, so laying them out backwards puts chore 0 back on
     chore 0's curve. Get this wrong and the packet for the CRM form leaves the
     mouth under the megaphone.

     The sentences stay in the markup and are only hidden from the eye — they
     are the section's informational payload, and a phone visitor using a screen
     reader should still get them. */
  .mf-steps {
    display: flex; flex-direction: row-reverse; justify-content: center;
    width: 190px; gap: 0; margin-bottom: 0;
  }
  .mf-step {
    display: flex; align-items: center; justify-content: center;
    position: relative;
    width: 38px; flex: none;
  }
  .mf-tick { display: none; }
  .mf-icon { display: block; color: var(--fx-slate); }
  .mf-icon svg { display: block; }
  .mf-chore {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }

  /* 10px, not the 20 the sentence list needed: each mouth now opens directly
     under its own mark, so the gap is a join to keep short, not a hazard to
     clear. */
  .mf-join { width: 190px; height: 190px; display: grid; place-items: center; margin-top: 10px; }
  /* The merge only needs the 96px band its three curves converge into, so its
     box is squat where the gather's is square. But rotate() is a paint-time
     transform: the SVG still *lays out* 96 wide x 190 tall, so the grid row
     sizes to 190px inside a 96px box and — with the default `align-content:
     normal` — starts at the box's top edge. place-items centres the SVG in that
     190px row, not in the box, so the rotated art landed 47px low: a hole under
     the agents and its tails crossing the rail and the "Just gets done" label
     below. align-content centres the oversized row on the box instead. */
  /* No margin here, unlike the gather: the merge's three curves leave the
     agents' own edges, so any gap opens a break in a line that is meant to be
     continuous. Dropping it lifts .mf-exec by the same 20px, which is where
     the rail wants to be anyway — it starts where the curves converge. */
  .mf-join-out { width: 190px; height: 96px; align-content: center; margin-top: 0; }
  .mf-gather, .mf-merge { transform: rotate(90deg); }

  .mf-run-out { flex-direction: column; align-items: center; margin-top: 0; }
  .mf-agents {
    flex-direction: row-reverse;
    height: auto;
    gap: 21px;
    margin: 0;
  }
  .mf-agent { width: 38px; height: 38px; border-radius: 12px; }
  .mf-agent .wb-svg { width: 26px; }

  /* The rail stands up with everything else, so its packet has to travel down
     it rather than along it.
     The explicit 190px is an alignment contract, not decoration. Left to shrink
     to fit, .mf-exec took the label's fractional text width, so the rail was
     centred in a fractional box that was itself centred in .mf-run-out — two
     roundings, where the merge SVG above it gets one (it is centred in a 190px
     .mf-join-out). At whole device-pixel ratios the two happened to agree; at
     1.25 / 1.5 / 2.5 — which is what browser zoom and scaled displays produce —
     they landed half a device pixel apart and the join looked off. Same width,
     same parent, same rounding. */
  .mf-exec { flex-direction: column; flex: none; width: 190px; }
  .mf-rail { width: 2px; height: 46px; flex: none; min-width: 0; }
  /* The painted line starts 1px above its box, tucking under the SVG's last
     pixel. The stem is a stroke inside a rotated SVG layer and the rail is a
     plain box, and the two rasterise about half a device pixel apart at
     fractional device-pixel ratios — browser zoom at 125%/150%, scaled
     displays. Nothing in layout fixes that; the two go through different
     rasterisers. Overlapping them by a pixel means there is no junction line
     left for the difference to show up on. The box itself is untouched, so the
     packet keyframes still measure from the same 46px. */
  .mf-rail::before { top: -1px; }
  .mf-rail::after {
    top: -46px; left: -1px;
    width: 4px; height: 46px;
    background: linear-gradient(180deg, transparent, var(--fx-emerald));
    animation-name: mf-rail-run-down;
  }
  /* A column, so the order down the page is rail -> dot -> label, which is the
     same order the desktop row reads left to right. As a row the dot sat 60px
     off to the side and the rail ended in mid-air, with nothing for the packet
     running down it to arrive at. No margin, for the same reason .mf-exec has
     no gap on desktop: the rail meets the marker. */
  .mf-out { flex-direction: column; gap: 12px; margin-top: 0; }
}

@media (max-width: 768px) {
  .page-home .mission,
  .page-home .products,
  .page-home .stories,
  .page-home .final-cta { padding: 68px 0; }
  .page-home .sec-head { margin-bottom: 40px; }
  .page-home .mission .sec-head { margin-bottom: 44px; }
  /* One column of full-width photos: portrait crops would run past a screen
     height each, so the frames go landscape. */
  .page-home .hcase-media { aspect-ratio: 16 / 10; }
}

@media (max-width: 600px) {
  .page-home .mission,
  .page-home .products,
  .page-home .stories,
  .page-home .final-cta { padding: 56px 0; }
  .page-home .product-card { padding: 24px 22px 22px; }
  .page-home .product-card::before,
  .page-home .product-card::after { top: 28px; right: 22px; }
  .page-home .final-cta .cta-row { justify-content: stretch; }
}

/* ---- Reduced motion: every loop settles on its resting frame ----
   Listed one by one rather than with a `.mf *` sweep, because the motion now
   lives largely on pseudo-elements, which a descendant selector never reaches. */
@media (prefers-reduced-motion: reduce) {
  .mf-packet, .mf-junction, .mf-agent-halo,
  .mf-rail::before, .mf-rail::after,
  .mf-out-dot::after,
  .page-home .product-card::after,
  .page-home .final-cta::after { animation: none !important; }
  /* The travelling marks go entirely; the lines they ran on stay, lit. */
  .mf-packet, .mf-agent-halo,
  .mf-rail::after,
  .page-home .final-cta::after { display: none; }
  .mf-done-fan { animation: none !important; }
  .mf-rail::before, .mf-done-fan { opacity: 0.6; }
  .page-home .hcase-media img { transition: none; }
}
