/* ============================================================
   SPEED 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/velocityforge.js only replays how it got there.
   ============================================================ */

.sp-grid { display: grid; grid-template-columns: 1.34fr .74fr; gap: clamp(26px, 4vw, 56px); align-items: center; }
/* ============================================================
   SPEED — «The rails»: the belt, the verdicts, the meter
   ============================================================ */
.sp-belt {
  /* ONE gutter number for the whole belt. The row ticks and the spine used to
     be measured from different boxes — the tick `left: 0` on the row (x 149)
     and the spine `left: 8px` on the belt's padding box (x 137) — so the belt
     showed two vertical bars eleven pixels apart. That is what «полоски стоят
     криво» was: two rails, not one. Both now read --sp-tick off this element,
     so they cannot drift apart again. */
  --sp-gutter: 20px;   /* horizontal padding: also the row's left border edge */
  --sp-padv: 18px;     /* vertical padding: also where the last row ends */
  --sp-tick: 7px;      /* tick x, measured from the row's left border edge */
  --sp-row-bd: 1px;    /* a row's own border: the last px between tick and spine */
  --sp-tick-h: 16px;   /* tick height, centred in the row */
  --sp-text: 20px;     /* where a row's text starts: past the tick, with air */
  position: relative; padding: var(--sp-padv) var(--sp-gutter); 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);
  font-family: "JetBrains Mono", ui-monospace, monospace;
}
.sp-belt__head {
  display: flex; justify-content: space-between;
  /* Same inset as a row's own text, so the column headings sit over their
     columns instead of over the tick gutter. */
  padding: 0 12px 8px var(--sp-text);
  font-size: .68rem; text-transform: uppercase; letter-spacing: .12em;
  color: var(--fdry-ink-faint, #6B7299); border-bottom: 1px solid rgba(255,255,255,.1);
}
.sp-req {
  position: relative;
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: baseline;
  /* Left padding clears the tick gutter; the tick lives inside it. */
  padding: 8px 12px 8px var(--sp-text);
  border-radius: 9px; font-size: .84rem;
  border: 1px solid transparent; will-change: transform, opacity;
}
.sp-req__label  { color: var(--fdry-ink, #ECEEF7); }
.sp-req__handle { color: var(--fdry-ink-faint, #6B7299); font-size: .74rem; }
.sp-req__verdict { font-size: .72rem; letter-spacing: .06em; white-space: nowrap; }
/* Deferred: got out of the way of the first paint. */
.sp-req--free.is-deferred {
  border-color: color-mix(in srgb, var(--accent) 34%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.sp-req--free .sp-req__verdict { color: var(--accent-light, #A9CF00); }
/* Railed: the plugin refused, and that refusal is the feature. */
.sp-req--railed.is-held {
  border-color: rgba(255,255,255,.14);
  background: rgba(255,255,255,.035);
}
.sp-req--railed .sp-req__verdict { color: var(--fdry-ink-soft, #A7AEC8); }
/* One tick per row, centred, all at the same x — a bead on the spine below.
   `top: 50%` with a negative margin rather than a translate: the row itself
   carries a GSAP transform during the scrub, and the two must not be confused
   for one another. Before, only the railed rows had a tick and it was pinned
   to `top: 0; left: 0` — sitting on the row's 9px border radius, clipped flat
   at the top, and absent from four of the ten rows. */
.sp-req::after {
  content: ""; position: absolute; border-radius: 2px;
  left: var(--sp-tick); width: 3px;
  height: var(--sp-tick-h); top: 50%; margin-top: calc(var(--sp-tick-h) / -2);
  background: var(--fdry-ink-soft, #A7AEC8); opacity: .5;
}
/* The tick carries the verdict too: lime where the rule landed, grey where the
   rail held it off. Same column, same length — only the colour differs. */
.sp-req--free::after { background: var(--accent, #A2C700); opacity: 1; }
/* The rows' own box: the spine measures itself against this, not against a
   constant. */
.sp-belt__rows { position: relative; display: grid; gap: 6px; }
.sp-rail-bar {
  /* Threaded exactly through the ticks, top and bottom and sideways, with no
     number that can go stale. Vertically: `top: 0; bottom: 0` inside the rows'
     box is the first row's top edge to the last row's bottom, gaps included —
     it used to be `top: 46px` («padding plus the head plus a gap»), which
     measured 5px above the first bead because the head's height is a font
     metric. Sideways: the tick is positioned inside the row's own 1px border,
     so the spine has to add that border too; leaving it out is what left the
     two columns 1px apart, and 1px apart is what «crooked» measured as. */
  position: absolute; top: 0; bottom: 0;
  left: calc(var(--sp-row-bd) + var(--sp-tick)); width: 3px;
  border-radius: 2px;
  background: linear-gradient(180deg, transparent, var(--accent), transparent);
  /* Visible at rest. It used to be `opacity: 0`, waiting for the scrub to
     raise it — so with no JS, with ?fg-static=1 and under reduced motion the
     rail that gives this scene its name simply was not there, and the ticks
     were beads on nothing. The armed path hides it itself (gsap.set before the
     timeline), which is the pattern every other scene in this theme uses. */
  opacity: 1; will-change: transform, opacity; transform-origin: 50% 0%;
}
.sp-side { display: grid; gap: 16px; justify-items: start; }
.sp-meter { display: grid; justify-items: center; gap: 4px; color: var(--accent, #A2C700); }
.sp-meter svg { width: 104px; height: 104px; }
.sp-meter__num {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 1.9rem;
  color: var(--fdry-ink, #ECEEF7); font-variant-numeric: tabular-nums;
}
.sp-meter__cap { font-size: .74rem; color: var(--fdry-ink-faint, #6B7299); text-align: center; max-width: 210px; }
.sp-side__math { font-family: "JetBrains Mono", ui-monospace, monospace; color: var(--fdry-ink-soft, #A7AEC8); font-size: .9rem; margin: 0; }
.sp-side__math span { color: var(--accent-light, #A9CF00); font-weight: 600; }
.sp-side__note { color: var(--fdry-ink-soft, #A7AEC8); font-size: .93rem; line-height: 1.6; margin: 0; }
/* Recovery doors */

/* ============================================================
   HERO ARTEFACT — the rails (hero-velocityforge.php)
   ============================================================ */
.sph { max-width: 430px; margin-inline: auto; }
.sph-list { position: relative; list-style: none; margin: 0; padding: 16px 18px; display: grid; gap: 7px; }
.sph-row {
  display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 9px 12px; border-radius: 9px;
  background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.08);
}
.sph-row code { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .78rem; color: var(--fdry-ink, #ECEEF7); overflow: hidden; text-overflow: ellipsis; }
.sph-row__lock { display: grid; place-items: center; color: var(--fdry-ink-faint, #6B7299); }
.sph-row__lock svg { width: 17px; height: 17px; }
.sph-row__verdict { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .68rem; white-space: nowrap; }
/* A rail is not a failure state, so it is not red: it is the product working. */
.sph-row.is-rail { border-color: rgba(255,255,255,.16); }
.sph-row.is-rail .sph-row__verdict { color: var(--fdry-ink-faint, #6B7299); }
.sph-row.is-rail .sph-row__lock { color: var(--fdry-ink-soft, #A7AEC8); }
.sph-row.is-free { border-color: color-mix(in srgb, var(--accent) 40%, transparent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.sph-row.is-free .sph-row__verdict { color: var(--accent-light, #A9CF00); }
/* The rule looking for somewhere to land. */
.sph-chip {
  position: absolute; left: 18px; top: 16px; z-index: 2; opacity: 0;
  padding: 4px 10px; border-radius: 999px;
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .68rem;
  color: #12160A; background: var(--accent, #A2C700);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--accent) 35%, transparent);
}
.sph-foot { padding: 2px 18px 14px; }

/* ============================================================
   SCENE 1 — the belt, and the tally of what the rails refused
   ============================================================ */
.sp-tally { display: grid; gap: 10px; }
.sp-tally p { display: flex; align-items: baseline; gap: 10px; margin: 0; font-size: .86rem; color: var(--fdry-ink-soft, #A7AEC8); }
.sp-tally b { font-size: 1.7rem; color: var(--accent-light, #A9CF00); min-width: 2ch; }
.sp-tally__rail { color: var(--fdry-ink, #ECEEF7) !important; }
.sp-side { align-content: center; }
.sp-side__note { max-width: 46ch; }

/* ============================================================
   SCENE 2 — the dial, the bytes and the four keep-rules
   ============================================================ */
.sp-crit__grid { grid-template-columns: .8fr 1.2fr; }
.sp-crit .sp-meter svg { width: clamp(120px, 18vh, 168px); height: clamp(120px, 18vh, 168px); }
.sp-crit .sp-meter__num { font-size: clamp(1.6rem, 3vw, 2.2rem); }
.sp-crit__side { display: grid; gap: 18px; align-content: center; }
.sp-bytes { display: grid; gap: 14px; }
.sp-byte { display: grid; grid-template-columns: 1fr auto; gap: 6px 14px; align-items: baseline; }
.sp-byte__k { font-size: .84rem; color: var(--fdry-ink-soft, #A7AEC8); }
.sp-byte__t { grid-column: 1 / -1; display: block; height: 12px; border-radius: 4px; background: rgba(255,255,255,.07); overflow: hidden; }
.sp-byte__t em { display: block; height: 100%; width: var(--w, 100%); transform-origin: 0 50%; background: rgba(255,255,255,.22); }
.sp-byte--after .sp-byte__t em { background: var(--accent, #A2C700); }
.sp-byte--after b { color: var(--accent-light, #A9CF00); }
.sp-rules { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.sp-rules li { display: grid; gap: 2px; font-size: .84rem; line-height: 1.55; color: var(--fdry-ink-soft, #A7AEC8); }
.sp-rules b { color: var(--fdry-ink, #ECEEF7); font-weight: 500; }

/* ============================================================
   SCENE 3 — the crash, and the three ways out of it
   ============================================================ */
/* ------------------------------------------------------------------
   The owner of this landing read the old version of this block and said
   «не понимаю что происходит» — and was right. Five unlabelled grey bars
   read as a loading skeleton; the site's state was .7rem uppercase mono in
   the panel head; the three doors were equal-weight paragraphs that never
   said they do nothing until something is broken; and the requirement that
   is the whole argument was grey italic fine print. Four fixes, in order:
   a browser frame, a state sentence, a spoken instruction, a chip.
   ------------------------------------------------------------------ */
.sp-crash { max-width: 1000px; }
.sp-crash__body { display: grid; grid-template-columns: .86fr 1.14fr; gap: clamp(20px, 3vw, 40px); padding: clamp(18px, 2.4vw, 26px); }

/* ── the screen: this is your site, and you can tell before it breaks ── */
/* The window takes whatever height the doors column needs, so the left half is
   a browser rather than a small card above a lot of nothing — and the white,
   when it comes, is the size of a screenful. Which is what the comment always
   said, and what `grid-template-rows: auto auto; align-content: center` did
   not do: the screen sized to its own content and floated in the middle of a
   625px column with 123px of nothing above and below it, using 61% of the
   space it was given. The screen takes the free row; the state line keeps its
   own. */
.sp-site { display: grid; grid-template-rows: minmax(220px, 1fr) auto; gap: 14px; align-content: stretch; }
.sp-screen {
  position: relative; border-radius: 12px; overflow: hidden;
  border: 1px solid rgba(255,255,255,.14); background: rgba(255,255,255,.03);
  display: grid; grid-template-rows: auto minmax(0, 1fr);
}
/* Browser chrome. Nothing else in the block says «site» before the white
   arrives, and by then it is too late to establish whose site it was. */
.sp-screen__chrome {
  display: flex; align-items: center; gap: 6px; padding: 8px 11px;
  background: rgba(255,255,255,.05); border-bottom: 1px solid rgba(255,255,255,.1);
}
.sp-screen__chrome i { width: 7px; height: 7px; border-radius: 50%; background: rgba(255,255,255,.2); flex: none; }
.sp-screen__url {
  margin-left: 6px; flex: 1; padding: 3px 9px; border-radius: 999px;
  background: rgba(0,0,0,.28); border: 1px solid rgba(255,255,255,.08);
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .68rem;
  color: var(--fdry-ink-soft, #A7AEC8); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The white covers the CONTENT area, not the chrome: the browser is fine, the
   site is what broke, and showing that distinction is half the point. */
.sp-screen__view { position: relative; display: grid; align-content: start; min-height: 216px; }
.sp-screen__page { display: grid; gap: 9px; padding: 18px 12%; }
.sp-screen__nav { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; }
.sp-screen__nav b { width: 16px; height: 16px; border-radius: 4px; background: var(--accent, #A2C700); opacity: .8; flex: none; }
.sp-screen__nav i { height: 5px; border-radius: 3px; background: rgba(255,255,255,.14); flex: 1; max-width: 26px; }
.sp-screen__nav i:first-of-type { margin-left: auto; }
.sp-screen__h1 { height: 14px; border-radius: 4px; background: rgba(255,255,255,.34); width: 82%; }
.sp-screen__bar { height: 7px; border-radius: 4px; background: rgba(255,255,255,.15); }
.sp-screen__bar--short { width: 62%; }
.sp-screen__block { height: 56px; border-radius: 8px; margin-top: 4px; background: color-mix(in srgb, var(--accent) 22%, transparent); }
.sp-screen__cta { height: 18px; width: 92px; border-radius: 999px; margin-top: 4px; background: var(--accent, #A2C700); opacity: .85; }
/* A hero and a card strip: enough page for the frame to read as a real one at
   real proportions, and enough of it that the white has something to erase. */
.sp-screen__cards { display: flex; gap: 8px; margin-top: 9px; }
.sp-screen__cards i { flex: 1; height: 36px; border-radius: 6px; background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.08); }
.sp-screen__white {
  position: absolute; inset: 0; margin: 0; display: grid; place-items: center;
  background: #fff; color: #1A1A1A; font-weight: 600; font-size: .95rem; opacity: 0;
}

/* ── the state: the one thing the block is about, at reading size ── */
.sp-state {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 9px; margin: 0;
  font-size: .92rem; color: var(--fdry-ink-soft, #A7AEC8);
}
.sp-state__dot {
  width: 9px; height: 9px; border-radius: 50%; flex: none; align-self: center;
  background: #7ED9A6; box-shadow: 0 0 10px rgba(126,217,166,.7);
}
.sp-state__v { font-size: 1.08rem; font-weight: 600; color: #7ED9A6; }
.sp-state__db {
  flex-basis: 100%;
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .74rem;
  letter-spacing: .04em; color: var(--fdry-ink-faint, #858DB3);
}
/* Broken, and the state says so in the colour of the problem. */
.sp-crash.is-broken .sp-state__dot { background: #E88C82; box-shadow: 0 0 10px rgba(232,140,130,.7); }
.sp-crash.is-broken .sp-state__v { color: #E88C82; }
.sp-crash.is-dbdown .sp-state__db { color: #E88C82; }

/* ── the doors: three, and one of them is not like the other two ── */
.sp-ways { display: grid; gap: 12px; align-content: start; min-width: 0; }
.sp-ways__head { display: grid; gap: 5px; margin: 0; }
/* The instruction the block never gave. At rest it says there is nothing to
   undo yet; the script rewrites it the moment there is. */
.sp-ways__head b { font-size: .92rem; font-weight: 500; line-height: 1.5; color: var(--fdry-ink, #ECEEF7); }
.sp-doors { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; align-content: start; }
.sp-door__btn {
  appearance: none; width: 100%; cursor: pointer; font: inherit; text-align: left;
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px;
  padding: 13px 15px; border-radius: 11px;
  background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.09);
  color: var(--fdry-ink-soft, #A7AEC8);
  transition: border-color 120ms cubic-bezier(.2,.6,.2,1), opacity 120ms cubic-bezier(.2,.6,.2,1);
}
.sp-door__btn:hover { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.sp-door__btn:focus-visible { outline: 3px solid var(--accent-light, #A9CF00); outline-offset: 2px; }
.sp-door__step {
  width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .76rem;
  border: 1.5px solid rgba(255,255,255,.22); color: var(--fdry-ink-soft, #A7AEC8);
}
.sp-door__main { display: grid; gap: 6px; min-width: 0; }
.sp-door__top { display: flex; align-items: baseline; flex-wrap: wrap; gap: 5px 10px; }
.sp-door__top b { color: var(--fdry-ink, #ECEEF7); font-size: .95rem; }
/* THE punchline, promoted out of italic fine print. Neutral where the door
   needs the database; accent where it needs nothing — because «works with no
   database» is the claim this whole scene exists to prove. */
.sp-door__needs {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .68rem;
  letter-spacing: .05em; padding: 3px 8px; border-radius: 999px; white-space: nowrap;
  border: 1px solid rgba(255,255,255,.16); color: var(--fdry-ink-soft, #A7AEC8);
  background: rgba(255,255,255,.04);
}
.sp-door__body { font-size: .84rem; line-height: 1.55; }
.sp-door__main code {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .74rem;
  color: var(--accent-light, #A9CF00); background: rgba(255,255,255,.05);
  border-radius: 6px; padding: 4px 8px; justify-self: start; overflow-wrap: anywhere;
}
/* «Press it» — faint while there is nothing to undo, lit once there is. */
.sp-door__go {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: .7rem;
  letter-spacing: .05em; color: var(--fdry-ink-faint, #858DB3);
}
.sp-door__go::after { content: " →"; }
.sp-crash.is-broken .sp-door__go { color: var(--accent-light, #A9CF00); }
/* The third door, marked before anybody presses anything. */
.sp-door--always .sp-door__btn { border-color: color-mix(in srgb, var(--accent) 42%, transparent); background: color-mix(in srgb, var(--accent) 7%, transparent); }
.sp-door--always .sp-door__step { border-color: color-mix(in srgb, var(--accent) 60%, transparent); color: var(--accent-light, #A9CF00); }
.sp-door--always .sp-door__needs { border-color: color-mix(in srgb, var(--accent) 55%, transparent); color: var(--accent-light, #A9CF00); background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* A door that needs the database is not hidden when the database goes — it is
   shown as unavailable, because that is the fact being demonstrated. Dimmed by
   recolouring, NOT by `opacity` on the button: an opacity of .6 pulled the red
   requirement note down to 3.3:1 against the panel, and the note is the one
   line on the door that has to be read in that state. */
.sp-door.is-out .sp-door__btn { background: rgba(255,255,255,.015); border-color: rgba(255,255,255,.07); }
.sp-door.is-out .sp-door__top b { color: var(--fdry-ink-soft, #A7AEC8); font-weight: 500; }
.sp-door.is-out .sp-door__body { color: var(--fdry-ink-faint, #858DB3); }
.sp-door.is-out .sp-door__main code { color: var(--fdry-ink-faint, #858DB3); text-decoration: line-through; text-decoration-color: rgba(232,140,130,.85); }
.sp-door.is-out .sp-door__needs { border-color: rgba(232,140,130,.6); color: #E88C82; background: rgba(232,140,130,.1); }
/* No arrow on a door that is shut: an invitation recoloured red still reads as
   an invitation. The line says what is wrong instead (swapped by the script),
   and it loses the «→» that means «go». */
.sp-door.is-out .sp-door__go { color: #E88C82; }
.sp-door.is-out .sp-door__go::after { content: ""; }
.sp-door.is-out .sp-door__step { border-color: rgba(255,255,255,.2); color: var(--fdry-ink-faint, #858DB3); }
.sp-door.is-used .sp-door__btn { border-color: #7ED9A6; background: rgba(126,217,166,.09); }
.sp-door.is-used .sp-door__go { color: #7ED9A6; }
.sp-doors__foot { margin: clamp(18px, 2.4vw, 28px) auto 0; max-width: 88ch; text-align: center; font-size: .86rem; line-height: 1.62; color: var(--fdry-ink-faint, #6B7299); }
@media (max-width: 899px) {
  .sp-crash__body { grid-template-columns: 1fr; }
  .sp-screen__page { padding: 14px 10%; }
}

/* Phone: one column (see the note in clearwayforge.css). `justify-items` goes
   back to stretch too — content-sized children inside a 108px column is how
   the tally ran off the side of the screen. */
@media (max-width: 899px) {
  .sp-grid { grid-template-columns: 1fr; }
  .sp-doors { grid-template-columns: 1fr; }
  .sp-side { justify-items: stretch; }
  .sp-side__note { max-width: none; }
}
