/* ============================================================
   FLOW FORGE — landing scenes.
   The product's own hero artefact and idea scenes; the shared
   grammar (hold/stage, ritual, panels) lives in foundry-scene.css.
   Loaded only on this site's front page (inc/enqueue.php).
   Fail-safe: static markup IS the end state — the scrub in
   assets/js/scenes/flowforge.js only replays how it got there.
   ============================================================ */

/* Scene 1: the rules on the left, the chain they decide on the right. */
.ff-grid { display: grid; grid-template-columns: 1.38fr .7fr; gap: clamp(26px, 4vw, 56px); align-items: center; }
/* ============================================================
   FLOW — «The chain»: conditions, verdicts, the linkage
   ============================================================ */
.ff-rules {
  position: relative; padding: 18px 20px; display: grid; gap: 6px;
  background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.028));
  border: 1px solid rgba(255,255,255,.1);
}
.ff-rules__head {
  display: flex; justify-content: space-between; padding-bottom: 8px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: .68rem; text-transform: uppercase; letter-spacing: .12em;
  color: var(--fdry-ink-faint, #6B7299); border-bottom: 1px solid rgba(255,255,255,.1);
}
/* Narrow: the expression sits under its label, so there is no column to head. */
.ff-rules__head-expr { display: none; }
.ff-rule {
  display: grid; grid-template-columns: 1fr auto; grid-template-areas: "label verdict" "expr verdict";
  gap: 2px 14px; padding: 9px 12px; border-radius: 10px;
  border: 1px solid transparent; will-change: transform, opacity;
}
.ff-rule__label   { grid-area: label; color: var(--fdry-ink, #ECEEF7); font-size: .92rem; }
.ff-rule__expr    { grid-area: expr; font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .74rem; color: var(--fdry-ink-faint, #6B7299); }
.ff-rule__verdict { grid-area: verdict; align-self: center; font-size: .73rem; letter-spacing: .05em; white-space: nowrap; }
.ff-rule--match.is-match {
  border-color: color-mix(in srgb, var(--accent) 34%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.ff-rule--match .ff-rule__verdict { color: var(--accent-light, #EB88CC); }
.ff-rule--skip.is-skip { border-color: rgba(255,255,255,.12); background: rgba(255,255,255,.03); }
.ff-rule--skip .ff-rule__verdict { color: var(--fdry-ink-faint, #6B7299); }
/* Refused is deliberately loud: it is the security claim of the product. */
.ff-rule--refused.is-refused {
  border-color: color-mix(in srgb, #D96A5E 40%, transparent);
  background: color-mix(in srgb, #D96A5E 10%, transparent);
}
.ff-rule--refused .ff-rule__verdict { color: #E58275; }
.ff-rule--refused .ff-rule__expr { text-decoration: line-through; text-decoration-color: color-mix(in srgb, #D96A5E 55%, transparent); }

/* ============================================================
   The rules are a TABLE where the hold applies (>= 1024px, the
   sceneHold breakpoint) — one line per rule, three columns.

   Not a style preference: stacked (label over expression) each row is
   67 px, and seven of them plus the payload made this stage 1031 px
   against a 960 px viewport. sceneHold measures that, decides the stage
   would hide its own tail for the whole scrub, and downgrades the scene
   to a one-shot — so the one scene on this landing that most needs the
   scrub was the only one on the seven that never got it. One line per
   rule is 41 px, the panel drops from 680 to ~500, and the stage holds
   with 120 px to spare. It also reads better: a condition, the
   expression it compiles to and the verdict, which is what the product
   does to it.

   Every row is its OWN grid, so the columns can only line up across
   rows if they are proportional (fr) or fixed — never `max-content`.
   The verdict column is a fixed width for the same reason: `auto` made
   its left edge ragged, one x per verdict word. */
@media (min-width: 1024px) {
  /* The split is measured, not chosen. At 1024px — the narrowest width this
     layout runs at, since below it the hold gives way and the rows stack
     again — a row has 549px inside the panel's padding and its own, so
     463px to share once the verdict column and the two gaps are out.
     Longest label 185px, longest expression 256px; .88/1.12 gives them 204
     and 259. Every number above is measured off this fixture, and the point
     of them is a table whose rows are all one line tall: at .95/1.05 the
     expression column came to 250px, `customer.orders_count greater_than 1`
     wrapped, and that one row stood 15px taller than its neighbours.
     The verdict column is 62px because the longest verdict is «no match» at
     58px, and it is fixed rather than `auto` so its left edge does not move
     from row to row. */
  .ff-rules { --ff-verdict-w: 62px; --ff-cols: minmax(0, .88fr) minmax(0, 1.12fr) var(--ff-verdict-w); }
  .ff-rule {
    grid-template-columns: var(--ff-cols);
    grid-template-areas: "label expr verdict";
    align-items: center; gap: 4px 14px; padding-inline: 10px;
  }
  .ff-rules__head {
    display: grid; grid-template-columns: var(--ff-cols);
    gap: 4px 14px; padding-inline: 10px;
  }
  .ff-rules__head-expr { display: block; }
  .ff-rules__head > :last-child,
  .ff-rule__verdict { text-align: right; }
}
.ff-side { display: grid; gap: 16px; justify-items: start; }
.ff-chain { display: grid; justify-items: center; gap: 4px; color: var(--accent, #DA76BB); }
.ff-chain svg { width: 100%; max-width: 240px; height: auto; }
.ff-chain__num {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 1.9rem;
  color: var(--fdry-ink, #ECEEF7); font-variant-numeric: tabular-nums;
}
.ff-chain__cap { font-size: .74rem; color: var(--fdry-ink-faint, #6B7299); text-align: center; max-width: 200px; }
.ff-side__math { font-family: "JetBrains Mono", ui-monospace, monospace; color: var(--fdry-ink-soft, #A7AEC8); font-size: .9rem; margin: 0; }
.ff-side__math span { color: var(--accent-light, #EB88CC); font-weight: 600; }
.ff-side__note { color: var(--fdry-ink-soft, #A7AEC8); font-size: .93rem; line-height: 1.6; margin: 0; }

/* ============================================================
   HERO ARTEFACT — the canvas (hero-flowforge.php)
   ============================================================ */
/* The canvas is a graph, so its lanes are declared once here and every edge is
   measured off them. The old wire carried its own hand-picked `top`/`height`
   per row: four floating stubs that sat in the gaps, missed the dots by 6px,
   and drifted further the moment a step wrapped to two lines. Nothing below is
   a standalone pixel — it is one of these tokens or arithmetic on them. */
.ffh {
  --ffh-canvas-gap: 12px;   /* trigger → steps: the rail's first segment spans it */
  --ffh-row-gap: 9px;       /* step → step: each segment spans the gap above it */
  --ffh-gutter: 26px;       /* the rail's lane, left of the step cards */
  --ffh-rail-w: 2px;
  --ffh-step-bd: 1px;
  --ffh-step-pad-x: 13px;
  --ffh-dot: 7px;
  --ffh-rail-ink: color-mix(in srgb, var(--accent) 58%, transparent);
  --ffh-tie-draw: 1;        /* the tie is drawn; the scrub winds it back to 0 */
  /* Offsets inside a step resolve against its PADDING box, so its border has
     to be paid for explicitly — leave it out and the rail lands 1px off centre
     and every row boundary shows a 2px nick in the chain. */
  --ffh-rail-left: calc(-1 * (var(--ffh-gutter) / 2 + var(--ffh-rail-w) / 2 + var(--ffh-step-bd)));
  /* The dot's left edge back to the rail's left edge: the step's border and
     inline padding, plus the half-gutter the rail sits in. */
  --ffh-tie-w: calc(var(--ffh-gutter) / 2 + var(--ffh-rail-w) / 2
                    + var(--ffh-step-bd) + var(--ffh-step-pad-x));
  max-width: 440px; margin-inline: auto;
}
.ffh-canvas { padding: 18px 18px 6px; display: grid; gap: var(--ffh-canvas-gap); }
.ffh-trigger {
  position: relative; display: grid; gap: 6px; padding: 13px 15px; border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.ffh-trigger b { color: var(--fdry-ink, #ECEEF7); font-size: .95rem; }
.ffh-trigger code {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .7rem;
  color: var(--accent-light, #EB88CC); overflow-wrap: anywhere;
}
.ffh-pulse {
  position: absolute; inset: -1px; border-radius: 12px; pointer-events: none; opacity: 0;
  border: 1.5px solid var(--accent-light, #EB88CC);
}
.ffh-steps { list-style: none; margin: 0; padding: 0 0 0 var(--ffh-gutter); display: grid; gap: var(--ffh-row-gap); }
.ffh-step {
  position: relative; display: grid; grid-template-columns: auto 1fr auto; gap: 10px;
  align-items: baseline; padding: 10px var(--ffh-step-pad-x); border-radius: 10px;
  background: rgba(255,255,255,.035); border: var(--ffh-step-bd) solid rgba(255,255,255,.08);
  font-size: .84rem; color: var(--fdry-ink-soft, #A7AEC8);
}
.ffh-step b { color: var(--fdry-ink, #ECEEF7); font-weight: 500; }
.ffh-step em { font-style: normal; font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .66rem; color: var(--fdry-ink-faint, #6B7299); }
/* The dot is the node the rail lands on, so it rides the row's centre line
   instead of the first text line's baseline: on the phone «Hand off to Lang
   Forge» wraps, and a baseline dot pulls up and away from its own edge. */
.ffh-step__dot {
  position: relative; align-self: center;   /* relative: the tie below anchors to it */
  width: var(--ffh-dot); height: var(--ffh-dot); border-radius: 50%;
  background: rgba(255,255,255,.22);
}
.ffh-step.is-on { border-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.ffh-step.is-on .ffh-step__dot { background: var(--accent-light, #EB88CC); box-shadow: 0 0 9px currentColor; color: var(--accent-light, #EB88CC); }
/* ── THE RAIL: one chain, drawn as segments that abut ─────────────
   Each step owns the piece from the gap above it down to its own bottom edge,
   so a row that grows takes its own piece with it and the next piece still
   starts exactly where this one stopped — no row height is ever guessed. The
   first piece starts on the trigger's bottom edge; the last stops on its dot's
   centre, because that is where the chain ends. */
.ffh-step__wire {
  position: absolute; left: var(--ffh-rail-left); width: var(--ffh-rail-w);
  top: calc(-1 * (var(--ffh-row-gap) + var(--ffh-step-bd)));
  bottom: calc(-1 * var(--ffh-step-bd));
  /* height/transform are declared, not merely omitted, and that is on purpose.
     This landing inlines a GENERATED first-screen bundle —
     assets/css/critical/front-flowforge.css, which is byte-for-byte what the
     page's `<style id="forge-critical">` carries (front-product.css is only
     the fallback for a site with no bundle of its own) — ahead of this file,
     which arrives deferred. While a stale bundle was inlined it still held the
     old stub's `height: 30px`, and top+height+bottom over-constrains a box:
     `bottom` lost, the segments rendered 30px long, and the geometry measured
     correct in the source and wrong in the browser. Stating both makes the
     rail immune to whatever that snapshot happens to hold. Re-extract with
     `npm run critical` after touching these rules. */
  height: auto; transform: none;
  background: var(--ffh-rail-ink);
}
.ffh-step:first-child .ffh-step__wire {
  top: calc(-1 * (var(--ffh-canvas-gap) + var(--ffh-step-bd))); height: auto;
}
.ffh-step:last-child .ffh-step__wire { bottom: 50%; }
/* The short edge off the rail into the node — the dot's own ::before, and a
   pseudo-element on purpose. It has to land on the dot's centre line, and the
   dot is the one box whose 50% IS that line however tall the row grows; an
   element of its own would have to be told where the dot is. It also keeps the
   step's grid at the three items it declares columns for, which matters
   because the scene sheet is deferred (inc/enqueue.php) and a fourth in-flow
   child would reshuffle the row for the whole of the first paint.
   `--ffh-tie-draw` defaults to 1 — static is the closed chain; the scrub tweens
   the custom property, so `transform` here stays CSS's alone. */
.ffh-step__dot::before {
  content: ""; position: absolute; right: 100%; top: 50%;
  width: var(--ffh-tie-w); height: var(--ffh-rail-w);
  margin-top: calc(-1 * var(--ffh-rail-w) / 2);
  background: var(--ffh-rail-ink);
  transform: scaleX(var(--ffh-tie-draw, 1)); transform-origin: 0 50%;
}
.ffh-foot { padding: 6px 18px 14px; }

/* ============================================================
   SCENE 1 — the event, and the rules it is compared against
   ============================================================ */
.ff-payload {
  display: grid; gap: 5px; padding: 11px 14px; margin: 0 0 10px;
  border-radius: 10px; background: rgba(255,255,255,.03);
  border: 1px dashed rgba(255,255,255,.12);
}
.ff-payload code {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .7rem;
  color: var(--fdry-ink, #ECEEF7); overflow-wrap: anywhere; line-height: 1.7;
}

/* ============================================================
   SCENE 2 — the catalogue wall, and the edge you draw across it
   ============================================================ */
.ff-cat__wall { display: grid; grid-template-columns: 1fr 120px 1fr; gap: clamp(10px, 1.6vw, 22px); align-items: center; }
.ff-col { display: grid; gap: 12px; align-content: start; }
.ff-col__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.ff-col__head span { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; color: var(--fdry-ink-faint, #6B7299); }
.ff-col__head b { font-size: 1.5rem; }
.ff-chip--core  { border-color: rgba(255,255,255,.16); }
.ff-chip--woo   { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.ff-chip--suite { border-color: color-mix(in srgb, var(--accent) 60%, transparent); color: var(--accent-light, #EB88CC); }
.ff-chip--out   { border-color: rgba(255,255,255,.22); }
.ff-chip--more  { border-style: dashed; color: var(--fdry-ink-faint, #6B7299); }
.ff-chip.is-wired {
  border-color: var(--accent-light, #EB88CC); color: var(--accent-light, #EB88CC);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.ff-wire { position: relative; display: grid; place-items: center; }
.ff-wire svg { width: 100%; height: clamp(120px, 22vh, 220px); }
.ff-wire__cap { position: absolute; bottom: -4px; }
.ff-cat__note { margin: clamp(14px, 2vh, 24px) auto 0; max-width: 84ch; text-align: center; font-size: .86rem; line-height: 1.6; color: var(--fdry-ink-soft, #A7AEC8); }
@media (max-width: 899px) {
  .ff-cat__wall { grid-template-columns: 1fr; }
  .ff-wire { display: none; }
}

/* ============================================================
   SCENE 3 — the run at three in the morning
   ============================================================ */
.ff-run { max-width: 940px; }
.ff-run__body { display: grid; grid-template-columns: .85fr 1.15fr; gap: clamp(18px, 2.6vw, 34px); padding: clamp(18px, 2.4vw, 26px); }
.ff-run__steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; align-content: start; }
.ff-run__step {
  display: grid; grid-template-columns: auto 1fr; gap: 6px 10px; align-items: baseline;
  padding: 11px 13px; border-radius: 10px;
  background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.08);
}
.ff-run__step b { color: var(--fdry-ink, #ECEEF7); font-size: .92rem; font-weight: 500; }
.ff-run__step em { grid-column: 2; font-style: normal; font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .7rem; color: var(--fdry-ink-faint, #6B7299); }
.ff-run__dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,.2); align-self: center; }
.ff-run__step.is-ok .ff-run__dot { background: #7ED9A6; }
.ff-run__step.is-ok em { color: #7ED9A6; }
.ff-run__step.is-dlq { border-color: rgba(224,106,94,.4); background: rgba(224,106,94,.07); }
.ff-run__step.is-dlq .ff-run__dot { background: #E88C82; }
.ff-run__step.is-dlq em { color: #E88C82; }
.ff-log { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; align-content: start; }
.ff-log__line {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: baseline;
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .74rem;
  color: var(--fdry-ink-soft, #A7AEC8); padding: 5px 9px; border-radius: 7px;
  background: rgba(255,255,255,.03);
}
.ff-log__line code { color: var(--fdry-ink-faint, #6B7299); }
.ff-log__line.is-fail { color: #E88C82; }
.ff-log__line.is-wait { color: var(--fdry-ink-faint, #6B7299); }
.ff-log__line.is-dlq { background: rgba(224,106,94,.1); color: #E88C82; }
.ff-run__dedupe {
  grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px;
  margin: 0; padding-top: 14px; border-top: 1px dashed rgba(255,255,255,.11);
  font-size: .86rem; line-height: 1.6; color: var(--fdry-ink-soft, #A7AEC8);
}
@media (max-width: 899px) { .ff-run__body { grid-template-columns: 1fr; } }

/* Phone: one column (see the note in clearwayforge.css). */
@media (max-width: 899px) {
  .ff-grid { grid-template-columns: 1fr; }
}
