/* ox features page (/features). Loaded after styles.css, only on this page.
   Every illustration is inline SVG. Each animated element's resting style is
   its final state, so with reduced motion (or before a loop starts) the
   picture still says the whole thing. Motion uses transform and opacity only,
   and a loop runs only while its picture is on screen (main.js, .is-live). */

/* ---------- Layout ---------- */
.fx-hero { padding-top: clamp(64px, 9vw, 120px); }
.fx-display { font-size: clamp(50px, 6.2vw, 88px); }
.fx-lede { margin-top: 28px; max-width: 520px; font-size: 19px; line-height: 1.55; color: var(--text); text-wrap: pretty; }
.fx-index { margin-top: 40px; }
.fx-index ul { display: flex; flex-wrap: wrap; gap: 10px; }
.fx-index a { display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--card); font-size: 14.5px; font-weight: 500; color: #555552; text-decoration: none; white-space: nowrap; transition: border-color 150ms ease, color 150ms ease; }
.fx-index a:hover { border-color: #C9C9C5; color: var(--ink); }
.fx-index .ic { width: 16px; height: 16px; color: #72726E; }

.fx-list { display: grid; gap: clamp(96px, 11vw, 160px); padding-top: clamp(96px, 11vw, 150px); }
.fx { display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr); gap: clamp(32px, 6vw, 96px); align-items: center; scroll-margin-top: 96px; }
.fx:nth-child(even) { grid-template-columns: minmax(0, 1.18fr) minmax(0, .82fr); }
.fx:nth-child(even) .fx-copy { order: 2; }
.fx-n { font: 400 12.5px/1 var(--mono); color: var(--subtle); margin-bottom: 18px; }
.fx-title { font: 600 clamp(30px, 3.2vw, 42px)/1.08 var(--display); letter-spacing: -0.024em; color: var(--ink); text-wrap: balance; }
.fx-text { margin-top: 16px; max-width: 30em; font-size: 18px; line-height: 1.6; color: var(--subtle); text-wrap: pretty; }
.fx-text code { white-space: nowrap; }
.fx-art { margin: 0; padding: clamp(10px, 3vw, 36px); border-radius: 20px; background: var(--stage); }
.fx-art svg { width: 100%; height: auto; overflow: hidden; }
.js .fx-art.reveal { transition-delay: 90ms; }

/* ---------- Illustration parts ---------- */
.fx-art text { font: 400 13px var(--mono); fill: var(--text); }
.fx-art .fx-sm, .fx-art .fx-sm text { font-size: 12px; }
.fx-art .fx-xs, .fx-art .fx-xs text { font-size: 11px; }
.fx-art .fx-h { font: 600 14px var(--sans); fill: var(--ink); }
.fx-art .fx-h13 { font-size: 13px; }
.fx-art .fx-k, .fx-art .fx-k text { fill: var(--ink); }
.fx-art .fx-s, .fx-art .fx-s text { fill: var(--subtle); }
.fx-art .fx-on-ink { fill: #F5F5F3; }
.fx-art .fx-dim { fill: var(--night-subtle); }
.fx-art .fx-term text { fill: var(--night-text); }
.fx-art .fx-prompt { fill: var(--ok-soft); }
.fx-art .fx-okt { fill: var(--ok-ink); }
.fx-art .fx-mask { letter-spacing: 1px; }
.fx-box { fill: var(--card); stroke: var(--line); stroke-width: 1; }
.fx-lift { stroke: #DCDCD8; }
.fx-wire { fill: none; stroke: #CFCFCA; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.fx-dash { stroke-dasharray: 4 5; }
.fx-hair { fill: none; stroke: #EEEEEB; stroke-width: 1; }
.fx-limit { fill: none; stroke: #CFCFCA; stroke-width: 1; stroke-dasharray: 3 4; }
.fx-node { fill: var(--card); stroke: #CFCFCA; stroke-width: 1.5; }
.fx-inkf { fill: var(--ink); }
.fx-nightf { fill: var(--night); }
.fx-stagef { fill: var(--stage); }
.fx-barf, .fx-barf rect { fill: #E6E6E2; }
.fx-okf { fill: var(--ok); }
.fx-warnf { fill: var(--warn); }
.fx-bluef { fill: var(--blue); }
.fx-halo { fill: var(--blue); opacity: .22; }
.fx-okfill { fill: rgba(47, 180, 99, .1); }
.fx-okline { fill: none; stroke: var(--ok); stroke-width: 1.5; }
.fx-inkline { fill: none; stroke: var(--ink); stroke-width: 1.5; }
.fx-inkstroke { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.fx-blueline { fill: none; stroke: var(--blue); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.fx-chk { fill: none; stroke: #fff; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.fx-file { fill: none; stroke: var(--subtle); stroke-width: 1.3; stroke-linejoin: round; }
.fx-lock { fill: none; stroke: var(--ink); stroke-width: 1.3; stroke-linejoin: round; }
.fx-shape { fill: var(--card); stroke: var(--ink); stroke-width: 1.5; stroke-linejoin: round; }
.fx-shape-line { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linejoin: round; }
.fx-tick { fill: none; stroke: #BDBDB8; stroke-width: 2; stroke-linecap: round; }
.fx-scan { fill: var(--blue); opacity: .12; }
.fx-mark { color: #F5F5F3; }
.fx-light { fill: #3A3A37; }
.fx-nightline { fill: none; stroke: var(--night-line); stroke-width: 1; }
.fx-caret { fill: #F5F5F3; }

/* Scaled parts grow from their own center. */
.fx-ping, .fx4-new, .fx5-done, .fx7-undo, .fx8-peak, .fx8-note, .fx8-ok { transform-box: fill-box; transform-origin: center; }
.fx-ping { opacity: 0; }
.fx-art .fx-ping { animation: fx-ping 2.4s var(--ease) infinite; }
@keyframes fx-ping { 0% { transform: scale(1); opacity: .55; } 70%, 100% { transform: scale(3); opacity: 0; } }

/* ---------- 1. Push to deploy (7s): the dot runs the pipeline, then the live marker moves. ---------- */
.fx1-dot { opacity: 0; animation: fx1-dot 7s linear infinite; }
.fx1-n1 { animation: fx1-n1 7s infinite; } .fx1-n2 { animation: fx1-n2 7s infinite; }
.fx1-n3 { animation: fx1-n3 7s infinite; } .fx1-n4 { animation: fx1-n4 7s infinite; }
.fx1-ptr { animation: fx1-ptr 7s infinite; }
.fx1-glow { animation: fx1-glow 7s infinite; }
@keyframes fx1-dot { 0% { transform: translateX(-340px); opacity: 0; } 4% { transform: translateX(-340px); opacity: 1; } 46% { transform: none; opacity: 1; } 52%, 100% { transform: none; opacity: 0; } }
@keyframes fx1-n1 { 0%, 3% { opacity: 0; } 5%, 90% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx1-n2 { 0%, 17% { opacity: 0; } 19%, 90% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx1-n3 { 0%, 31% { opacity: 0; } 33%, 90% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx1-n4 { 0%, 45% { opacity: 0; } 47%, 90% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx1-ptr {
  0%, 48% { transform: translateX(-178px); opacity: 1; animation-timing-function: var(--ease); }
  56%, 88% { transform: none; opacity: 1; }
  93% { transform: none; opacity: 0; }
  94% { transform: translateX(-178px); opacity: 0; }
  100% { transform: translateX(-178px); opacity: 1; }
}
@keyframes fx1-glow { 0%, 52% { opacity: 0; } 58%, 90% { opacity: 1; } 95%, 100% { opacity: 0; } }

/* ---------- 2. Any stack (10s): four files read in turn, the plan follows each. ---------- */
.fx2-p2, .fx2-p3, .fx2-p4 { opacity: 0; }
.fx2-p1 { animation: fx2-p1 10s infinite; } .fx2-p2 { animation: fx2-p2 10s infinite; }
.fx2-p3 { animation: fx2-p3 10s infinite; } .fx2-p4 { animation: fx2-p4 10s infinite; }
@keyframes fx2-p1 { 0%, 21% { opacity: 1; } 23%, 97.5% { opacity: 0; } 100% { opacity: 1; } }
@keyframes fx2-p2 { 0%, 24% { opacity: 0; } 26%, 46% { opacity: 1; } 48%, 100% { opacity: 0; } }
@keyframes fx2-p3 { 0%, 49% { opacity: 0; } 51%, 71% { opacity: 1; } 73%, 100% { opacity: 0; } }
@keyframes fx2-p4 { 0%, 74% { opacity: 0; } 76%, 95% { opacity: 1; } 97%, 100% { opacity: 0; } }

/* ---------- 3. Your server: the agent dials out and the line carries both ways. ---------- */
.fx3-out, .fx3-back { opacity: 0; }
.fx3-out { animation: fx3-out 3s linear infinite; }
.fx3-back { animation: fx3-back 3s linear 1.5s infinite; }
.fx3-led { animation: fx-blink 1.8s ease-in-out infinite; }
.fx3-led2 { animation-duration: 2.6s; animation-delay: .7s; }
@keyframes fx3-out { 0% { transform: none; opacity: 0; } 12%, 82% { opacity: 1; } 100% { transform: translateX(-128px); opacity: 0; } }
@keyframes fx3-back { 0% { transform: none; opacity: 0; } 12%, 82% { opacity: 1; } 100% { transform: translateX(128px); opacity: 0; } }
@keyframes fx-blink { 50% { opacity: .25; } }

/* ---------- 4. Staging and previews (8s): a branch's preview arrives, then staging is promoted. ---------- */
.fx4-amber, .fx4-flash { opacity: 0; }
.fx4-new { animation: fx4-new 8s var(--ease) infinite; }
.fx4-newl { animation: fx4-newl 8s infinite; }
.fx4-amber { animation: fx4-amber 8s infinite; }
.fx4-green { animation: fx4-green 8s infinite; }
.fx4-pro { animation: fx4-pro 8s var(--ease) infinite; }
.fx4-flash { animation: fx4-flash 8s infinite; }
@keyframes fx4-new { 0%, 4% { opacity: 0; transform: scale(.94); } 12%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: scale(.94); } }
@keyframes fx4-newl { 0%, 4% { opacity: 0; } 10%, 90% { opacity: 1; } 96%, 100% { opacity: 0; } }
@keyframes fx4-amber { 0%, 8% { opacity: 0; } 12%, 30% { opacity: 1; } 34%, 100% { opacity: 0; } }
@keyframes fx4-green { 0%, 30% { opacity: 0; } 34%, 90% { opacity: 1; } 96%, 100% { opacity: 0; } }
@keyframes fx4-pro { 0%, 44% { opacity: 0; transform: translateY(8px); } 52%, 82% { opacity: 1; transform: none; } 90%, 100% { opacity: 0; transform: none; } }
@keyframes fx4-flash { 0%, 54% { opacity: 0; } 60%, 80% { opacity: 1; } 88%, 100% { opacity: 0; } }

/* ---------- 5. Services (8s): shapes snap in, the backup clock turns, the backup checks off. ---------- */
.fx5-s { animation: fx5-in 8s var(--ease) var(--d, 0s) infinite both; }
.fx5-s1 { --d: .2s; } .fx5-s2 { --d: .5s; } .fx5-s3 { --d: .8s; }
.fx5-wires { animation: fx5-wires 8s infinite; }
.fx5-hand { transform-origin: 378px 124px; animation: fx-turn 4s linear infinite; }
.fx5-done { animation: fx5-done 8s var(--ease) infinite; }
@keyframes fx5-in { 0% { opacity: 0; transform: translateY(-14px); } 6% { opacity: 1; transform: translateY(2px); } 9%, 86% { opacity: 1; transform: none; } 92%, 100% { opacity: 0; transform: translateY(-14px); } }
@keyframes fx5-wires { 0%, 12% { opacity: 0; } 20%, 88% { opacity: 1; } 94%, 100% { opacity: 0; } }
@keyframes fx-turn { to { transform: rotate(360deg); } }
@keyframes fx5-done { 0%, 70% { opacity: 0; transform: scale(.6); } 76% { opacity: 1; transform: scale(1.1); } 80%, 90% { opacity: 1; transform: none; } 95%, 100% { opacity: 0; transform: none; } }

/* ---------- 6. Explore (7s): a highlight reads down the rows. ---------- */
.fx6-scan { animation: fx6-scan 7s var(--ease) infinite; }
@keyframes fx6-scan {
  0% { transform: none; opacity: 0; } 4%, 14% { transform: none; opacity: 1; }
  20%, 30% { transform: translateY(24px); } 36%, 46% { transform: translateY(48px); }
  52%, 62% { transform: translateY(72px); } 68%, 78% { transform: translateY(96px); }
  84%, 92% { transform: translateY(120px); opacity: 1; } 97%, 100% { transform: translateY(120px); opacity: 0; }
}

/* ---------- 7. Variables (9s): a new value, the change to save, then undo. ---------- */
.fx7-b { opacity: 0; }
.fx7-new { animation: fx7-new 9s var(--ease) infinite; }
.fx7-a { animation: fx7-a 9s infinite; }
.fx7-b { animation: fx7-b 9s infinite; }
.fx7-undo { animation: fx7-undo 9s var(--ease) infinite; }
@keyframes fx7-new { 0%, 12% { opacity: 0; transform: translateY(6px); } 18%, 58% { opacity: 1; transform: none; } 64%, 100% { opacity: 0; transform: none; } }
@keyframes fx7-a { 0%, 20% { opacity: 0; } 25%, 52% { opacity: 1; } 56%, 100% { opacity: 0; } }
@keyframes fx7-b { 0%, 54% { opacity: 0; } 58%, 90% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx7-undo { 0%, 50% { transform: none; } 58%, 100% { transform: rotate(-360deg); } }

/* ---------- 8. Alerts (8s): the chart draws, the spike becomes an alert and a signed event. ---------- */
.fx8-cover { transform: translateX(282px); animation: fx8-cover 8s linear infinite; }
.fx8-fade { animation: fx8-fade 8s infinite; }
.fx8-peak { animation: fx8-peak 8s var(--ease) infinite; }
.fx8-note { animation: fx8-note 8s var(--ease) infinite; }
.fx8-pkt { opacity: 0; animation: fx8-pkt 8s var(--ease) infinite; }
.fx8-ok { animation: fx8-ok 8s var(--ease) infinite; }
@keyframes fx8-cover { 0% { transform: none; } 50%, 92% { transform: translateX(282px); } 93%, 100% { transform: none; } }
@keyframes fx8-fade { 0%, 88% { opacity: 1; } 91%, 99% { opacity: 0; } 100% { opacity: 1; } }
@keyframes fx8-peak { 0%, 31% { opacity: 0; transform: scale(.4); } 35% { opacity: 1; transform: scale(1.3); } 38%, 100% { opacity: 1; transform: none; } }
@keyframes fx8-note { 0%, 35% { opacity: 0; transform: translateY(6px) scale(.97); } 40%, 88% { opacity: 1; transform: none; } 92%, 100% { opacity: 0; transform: none; } }
@keyframes fx8-pkt { 0%, 44% { opacity: 0; transform: none; } 48% { opacity: 1; transform: none; } 60% { opacity: 1; transform: translateY(40px); } 64%, 100% { opacity: 0; transform: translateY(40px); } }
@keyframes fx8-ok { 0%, 62% { opacity: 0; transform: scale(.5); } 66% { opacity: 1; transform: scale(1.15); } 70%, 88% { opacity: 1; transform: none; } 92%, 100% { opacity: 0; transform: none; } }

/* ---------- 9. CLI (9s): the command types itself, the result streams in. ---------- */
.fx9-type { transform: translateX(181px); animation: fx9-type 9s infinite; }
.fx9-caret { animation: fx9-caret 1s steps(1) infinite; }
.fx9-screen { animation: fx9-screen 9s infinite; }
.fx9-l1 { animation: fx9-l1 9s infinite; } .fx9-l2 { animation: fx9-l2 9s infinite; }
.fx9-l3 { animation: fx9-l3 9s infinite; } .fx9-l4 { animation: fx9-l4 9s infinite; }
.fx9-l5 { animation: fx9-l5 9s infinite; }
@keyframes fx9-type { 0%, 6% { transform: none; animation-timing-function: steps(23, end); } 30%, 94% { transform: translateX(181px); } 95%, 100% { transform: none; } }
@keyframes fx9-caret { 50% { opacity: 0; } }
@keyframes fx9-screen { 0%, 88% { opacity: 1; } 93%, 95% { opacity: 0; } 100% { opacity: 1; } }
@keyframes fx9-l1 { 0%, 36% { opacity: 0; } 38%, 94% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx9-l2 { 0%, 46% { opacity: 0; } 48%, 94% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx9-l3 { 0%, 56% { opacity: 0; } 58%, 94% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx9-l4 { 0%, 66% { opacity: 0; } 69%, 94% { opacity: 1; } 95%, 100% { opacity: 0; } }
@keyframes fx9-l5 { 0%, 76% { opacity: 0; } 79%, 94% { opacity: 1; } 95%, 100% { opacity: 0; } }

/* A loop waits off screen. Without script every loop just runs. */
.js .fx-art:not(.is-live) * { animation-play-state: paused; }

/* ---------- Responsive ---------- */
@media (max-width: 880px) {
  .fx, .fx:nth-child(even) { grid-template-columns: 1fr; gap: 28px; }
  .fx:nth-child(even) .fx-copy { order: 0; }
  .fx-list { gap: 88px; }
}
@media (max-width: 560px) {
  .fx-art { border-radius: 16px; }
  .fx-text { font-size: 17px; }
  .fx-index a { height: 34px; padding: 0 12px; font-size: 14px; }
}

/* Reduced motion: no loops at all. Each picture shows its final state. */
@media (prefers-reduced-motion: reduce) {
  .fx-art *, .fx-art *::before, .fx-art *::after { animation: none !important; }
}
