/* Machined Noir — the homepage's one visual language for diagrams.
   Photoreal space-black aluminium objects (rendered once, see noir/SOURCES.md),
   official Apple bezels showing Axis, dark glass chips and teal light paths.
   Every section visual built from these parts: .nx root on the figure.
   Motion: .nx gets .is-in once (reveal) and .is-live while on screen (loops).
   All loops share --nx-cycle so a section's beats stay in sync. */

.nx{--nx-teal:#37cdbc;--nx-teal-soft:#84e4d8;--nx-ink:#e8ecea;--nx-mute:#9aa3a0;--nx-cycle:6.4s;position:relative;margin:0;width:100%;color:var(--nx-ink)}
.nx *,.nx *::before,.nx *::after{box-sizing:border-box}

/* ---------- reveal ---------- */
.nx [data-r]{opacity:1}
.nx.nx-armed [data-r]{opacity:0;translate:0 22px;scale:.975;transition:opacity .9s cubic-bezier(.2,.7,.2,1),translate 1.1s cubic-bezier(.2,.7,.2,1),scale 1.1s cubic-bezier(.2,.7,.2,1);transition-delay:calc(var(--r,0) * 110ms)}
.nx.nx-armed.is-in [data-r]{opacity:1;translate:none;scale:none}

/* ---------- photoreal objects ---------- */
.nx-obj{position:relative;display:block;margin:0 auto}
.nx-obj>img{position:relative;z-index:1;display:block;width:100%;height:auto;-webkit-mask-image:radial-gradient(closest-side,#000 80%,transparent 100%);mask-image:radial-gradient(closest-side,#000 80%,transparent 100%);user-select:none;-webkit-user-drag:none;pointer-events:none}
/* teal light pooled under the object */
.nx-obj::after{content:"";position:absolute;z-index:0;left:18%;right:18%;bottom:6%;height:22%;border-radius:50%;background:radial-gradient(closest-side,rgba(55,205,188,.5),rgba(55,205,188,0));filter:blur(22px);opacity:var(--nx-pool,.28);pointer-events:none}
/* light that sits on top of an object (lit mark, LED) */
.nx-lume{position:absolute;z-index:2;border-radius:50%;background:radial-gradient(closest-side,rgba(132,228,216,.55),rgba(55,205,188,.18) 55%,rgba(55,205,188,0));mix-blend-mode:screen;opacity:0;pointer-events:none}

/* ---------- glass ---------- */
.nx-glass{position:relative;background:linear-gradient(180deg,rgba(255,255,255,.075),rgba(255,255,255,.02));border:1px solid rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.09),0 18px 40px -18px rgba(0,0,0,.9),0 2px 6px rgba(0,0,0,.4);-webkit-backdrop-filter:blur(16px) saturate(140%);backdrop-filter:blur(16px) saturate(140%)}
.nx-chip{display:inline-flex;align-items:center;gap:8px;padding:7px 13px 7px 9px;border-radius:999px;font:500 12.5px/1.2 'DM Sans',ui-sans-serif,system-ui,sans-serif;letter-spacing:-.005em;color:#e3e8e6;white-space:nowrap}
.nx-chip svg{flex:none;width:15px;height:15px}
.nx-chip .nx-tick{display:grid;place-items:center;width:18px;height:18px;border-radius:50%;background:var(--nx-teal);color:#04201c}
.nx-chip .nx-tick svg{width:11px;height:11px}
.nx-chip.is-dim{color:#8d9592}
.nx-dot{width:7px;height:7px;border-radius:50%;background:var(--nx-teal);box-shadow:0 0 0 3px rgba(55,205,188,.16),0 0 10px rgba(55,205,188,.8)}

/* app tile: same dark glass tile the model field uses */
.nx-tile{display:grid;place-items:center;width:var(--s,58px);height:var(--s,58px);border-radius:calc(var(--s,58px) * .27);background:linear-gradient(160deg,#20262a,#0b0e10 70%);border:1px solid rgba(255,255,255,.12);box-shadow:inset 0 1px 0 rgba(255,255,255,.14),inset 0 -8px 16px rgba(0,0,0,.45),0 16px 30px -14px rgba(0,0,0,.95)}
.nx-tile img{width:46%;height:46%;object-fit:contain;filter:brightness(0) invert(1);opacity:.94}

/* steel ring for people */
.nx-person{position:relative;display:block;width:var(--s,54px);height:var(--s,54px);padding:2px;border-radius:50%;background:conic-gradient(from 200deg,#4c5357,#f1f5f6 11%,#30353a 27%,#a5adb2 48%,#24282b 66%,#dfe4e6 84%,#4c5357);box-shadow:0 12px 26px -12px rgba(0,0,0,.95)}
.nx-person img{display:block;width:100%;height:100%;border-radius:50%;object-fit:cover;border:2px solid #07090a}

/* ---------- captions ---------- */
.nx-cap{margin:0;font:600 10.5px/1.2 'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.17em;text-transform:uppercase;color:var(--nx-teal)}
.nx-lab{margin:7px 0 0;font:500 15px/1.35 'DM Sans',ui-sans-serif,system-ui,sans-serif;letter-spacing:-.01em;color:#c9cfcc}

/* ---------- light paths ---------- */
/* .nx-link is a straight fibre between two stations; its <i> is the travelling pulse.
   Horizontal by default, .is-v for vertical. --t0/--t1 set when in the cycle (0..1) the pulse runs. */
.nx-link{position:relative;flex:1 1 0;min-width:28px;height:2px;align-self:center}
.nx-link::before{content:"";position:absolute;inset:0;border-radius:2px;background:linear-gradient(90deg,rgba(132,228,216,0),rgba(132,228,216,.34) 14%,rgba(132,228,216,.34) 86%,rgba(132,228,216,0))}
.nx-link i{position:absolute;top:50%;left:0;width:96px;max-width:60%;height:2px;margin-top:-1px;border-radius:2px;background:linear-gradient(90deg,rgba(55,205,188,0),#37cdbc 68%,#e9fffb);box-shadow:0 0 10px 1px rgba(55,205,188,.75),0 0 26px 4px rgba(55,205,188,.28);opacity:0;transform:translateX(-100%)}
.nx-link.is-v{flex:none;width:2px;height:var(--h,64px);min-width:0;align-self:center}
.nx-link.is-v::before{background:linear-gradient(180deg,rgba(132,228,216,0),rgba(132,228,216,.26) 14%,rgba(132,228,216,.26) 86%,rgba(132,228,216,0))}
.nx-link.is-v i{top:0;left:50%;width:2px;height:64px;max-height:60%;margin:0 0 0 -1px;background:linear-gradient(180deg,rgba(55,205,188,0),#37cdbc 68%,#e9fffb);transform:translateY(-100%)}

/* SVG fibres for curved paths: pathLength=1 on .nx-fibre-run. Stroke widths are in viewBox
   units (stages keep a fixed aspect ratio, so scaling is uniform); never use
   non-scaling-stroke here, Chrome then ignores pathLength for the dash. */
.nx-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}
.nx-fibre{fill:none;stroke:rgba(132,228,216,.26);stroke-width:var(--nx-fw,.26)}
.nx-fibre.is-dim{stroke:rgba(255,255,255,.1)}
.nx-fibre-run{fill:none;stroke:#7ae8da;stroke-width:var(--nx-rw,.42);stroke-linecap:round;stroke-dasharray:.16 1;stroke-dashoffset:.16;opacity:0;filter:drop-shadow(0 0 3px rgba(55,205,188,.95)) drop-shadow(0 0 8px rgba(55,205,188,.45))}

/* ---------- shared keyframes (percentages = position in --nx-cycle) ---------- */
/* Three fixed beats per cycle. A pulse picks its beat with data-w="1|2|3";
   window 1: 8%→28%, window 2: 34%→54%, window 3: 60%→78%. */
.nx.is-live .nx-link[data-w] i,.nx.is-live .nx-fibre-run[data-w]{animation-duration:var(--nx-cycle);animation-iteration-count:infinite;animation-timing-function:cubic-bezier(.45,.05,.35,1)}
.nx.is-live .nx-link[data-w="1"] i{animation-name:nx-w1}
.nx.is-live .nx-link[data-w="2"] i{animation-name:nx-w2}
.nx.is-live .nx-link[data-w="3"] i{animation-name:nx-w3}
.nx.is-live .nx-link.is-v[data-w="1"] i{animation-name:nx-w1v}
.nx.is-live .nx-link.is-v[data-w="2"] i{animation-name:nx-w2v}
.nx.is-live .nx-link.is-v[data-w="3"] i{animation-name:nx-w3v}
@keyframes nx-w1{0%,8%{left:0;transform:translateX(-100%);opacity:0}11%{opacity:1}25%{opacity:1}28%,100%{left:100%;transform:translateX(0);opacity:0}}
@keyframes nx-w2{0%,34%{left:0;transform:translateX(-100%);opacity:0}37%{opacity:1}51%{opacity:1}54%,100%{left:100%;transform:translateX(0);opacity:0}}
@keyframes nx-w3{0%,60%{left:0;transform:translateX(-100%);opacity:0}63%{opacity:1}75%{opacity:1}78%,100%{left:100%;transform:translateX(0);opacity:0}}
@keyframes nx-w1v{0%,8%{top:0;transform:translateY(-100%);opacity:0}11%{opacity:1}25%{opacity:1}28%,100%{top:100%;transform:translateY(0);opacity:0}}
@keyframes nx-w2v{0%,34%{top:0;transform:translateY(-100%);opacity:0}37%{opacity:1}51%{opacity:1}54%,100%{top:100%;transform:translateY(0);opacity:0}}
@keyframes nx-w3v{0%,60%{top:0;transform:translateY(-100%);opacity:0}63%{opacity:1}75%{opacity:1}78%,100%{top:100%;transform:translateY(0);opacity:0}}
/* svg runs: dashoffset 0.16 (hidden before start) → -1 (gone past the end) */
.nx.is-live .nx-fibre-run[data-w="1"]{animation-name:nx-r1}
.nx.is-live .nx-fibre-run[data-w="2"]{animation-name:nx-r2}
.nx.is-live .nx-fibre-run[data-w="3"]{animation-name:nx-r3}
@keyframes nx-r1{0%,8%{stroke-dashoffset:.16;opacity:0}10%{opacity:1}26%{opacity:1}28%,100%{stroke-dashoffset:-1;opacity:0}}
@keyframes nx-r2{0%,34%{stroke-dashoffset:.16;opacity:0}36%{opacity:1}52%{opacity:1}54%,100%{stroke-dashoffset:-1;opacity:0}}
@keyframes nx-r3{0%,60%{stroke-dashoffset:.16;opacity:0}62%{opacity:1}76%{opacity:1}78%,100%{stroke-dashoffset:-1;opacity:0}}
/* arrivals: something lights up right after a window ends */
@keyframes nx-arrive1{0%,26%{opacity:var(--o0,0)}31%{opacity:1}52%{opacity:var(--o1,.35)}100%{opacity:var(--o1,.35)}}
@keyframes nx-arrive2{0%,52%{opacity:var(--o0,0)}57%{opacity:1}80%{opacity:var(--o1,.35)}100%{opacity:var(--o1,.35)}}
@keyframes nx-arrive3{0%,76%{opacity:var(--o0,0)}81%{opacity:1}96%{opacity:var(--o1,.35)}100%{opacity:var(--o0,0)}}
@keyframes nx-boot{0%{opacity:.25}6%{opacity:1}20%{opacity:.45}100%{opacity:.45}}
/* a chip that pops in after a window and leaves before the loop restarts */
@keyframes nx-pop2{0%,53%{opacity:0;transform:translateY(8px) scale(.96)}58%,94%{opacity:1;transform:none}99%,100%{opacity:0;transform:translateY(-4px) scale(.98)}}
@keyframes nx-pop3{0%,77%{opacity:0;transform:translateY(8px) scale(.96)}82%,95%{opacity:1;transform:none}99%,100%{opacity:0;transform:translateY(-4px) scale(.98)}}

.nx:not(.is-live) *{animation-play-state:paused!important}

@media (prefers-reduced-motion:reduce){
  .nx *{animation:none!important;transition:none!important}
  .nx [data-r]{opacity:1!important;translate:none!important;scale:none!important}
  .nx .nx-link i{display:none}
  .nx .nx-fibre-run{display:none}
}
