/* NORC in motion — "Four tiles, one cross".
   Kit v1 (2026-10-21). Spec: docs/brand/README.md §7. Prefix: nm-.
   Three moves only: grow from a pivot (uniform scale), slide along the cross
   (translate on x/y), turn a quarter about a pivot. Never skew, stretch or
   spin the whole mark. Reduced motion: final state at once. */

:root {
  --nm-indigo: #1313BA;
  --nm-indigo-deep: #0D0D8A;
  --nm-night: #0B0B2E;
  --nm-ivory: #F6F6FC;
  --nm-mist: #E4E4FA;
  --nm-signal: #FFB547;

  --nm-ease: cubic-bezier(.2, .8, .2, 1);      /* default: arrive and settle */
  --nm-snap: cubic-bezier(.34, 1.56, .64, 1);  /* the click of the last tile */
  --nm-io: cubic-bezier(.65, 0, .35, 1);       /* loops, there and back */

  --nm-press: 120ms;
  --nm-select: 160ms;
  --nm-panel: 240ms;
  --nm-reveal: 480ms;
  --nm-signature: 760ms;
  --nm-stagger: 70ms;                          /* clockwise, tile to tile */
  --nm-cascade: 50ms;                          /* list item to list item */
}

/* ---------- The mark (inline SVG built from geometry.ts) ---------- */
.nm-mark { display: inline-block; flex: none; overflow: visible; vertical-align: middle; }
.nm-mark .nm-t { transform-box: view-box; transform-origin: var(--ox) var(--oy); }
.nm-icon { display: inline-block; flex: none; vertical-align: middle; overflow: hidden; }

/* I1 — The assembly: tiles grow from their pivots, clockwise; the last one clicks in.
   Fill "backwards" only: once played, the tiles return to normal styles so M1 can move them. */
.nm-assemble .nm-t { animation: nm-grow var(--nm-signature) var(--nm-ease) backwards; animation-delay: calc(var(--i) * var(--nm-stagger)); }
.nm-assemble .nm-bl { animation-name: nm-grow-click; }
@keyframes nm-grow { from { transform: scale(0); } to { transform: scale(1); } }
@keyframes nm-grow-click { 0% { transform: scale(0); } 70% { transform: scale(1.06); } 100% { transform: scale(1); } }

/* I3 — The wordmark: the mark assembles, NORC slides out of the cross. */
/* Sized by --nm-h (the mark height): cap height ½, gap 0.3 — the lockup rule. */
.nm-lockup { --nm-h: 40px; display: inline-flex; align-items: center; gap: calc(var(--nm-h) * .3); line-height: 0; }
.nm-lockup > .nm-mark { height: var(--nm-h); width: auto; }
.nm-lockup .nm-word { overflow: hidden; display: inline-block; }
.nm-lockup .nm-word > * { display: block; }
.nm-lockup .nm-word svg { display: block; height: calc(var(--nm-h) * .5134); width: auto; }
.nm-lockup.nm-assemble .nm-word > * { animation: nm-word 560ms var(--nm-ease) 320ms backwards; }
@keyframes nm-word { from { transform: translateX(-104%); } to { transform: none; } }

/* M1 — The cross opens: on hover/focus the tiles step apart along both axes. */
.nm-spread .nm-t { transition: transform var(--nm-panel) var(--nm-ease); }
:is(a, button):is(:hover, :focus-visible) .nm-spread .nm-t,
.nm-spread.is-open .nm-t { transform: translate(calc(var(--sx) * var(--nm-spread, 18px)), calc(var(--sy) * var(--nm-spread, 18px))); }

/* ---------- Loaders: only while something real is pending ---------- */

/* L1 — The round: each tile takes its turn, clockwise. Any size, currentColor. */
.nm-round .nm-t { animation: nm-round 1.2s var(--nm-io) infinite; animation-delay: calc(var(--i) * 300ms); }
@keyframes nm-round {
  0%, 100% { opacity: .2; transform: scale(.9); }
  12%, 32% { opacity: 1; transform: scale(1); }
  55% { opacity: .2; transform: scale(.9); }
}
/* The signature hands over to the round once it has played. */
.nm-assemble.nm-round .nm-t { animation: nm-grow var(--nm-signature) var(--nm-ease) backwards, nm-round 1.2s var(--nm-io) infinite; animation-delay: calc(var(--i) * var(--nm-stagger)), calc(var(--nm-signature) + var(--i) * 300ms); }

/* L2 — The relay: a packet runs the lane, a second follows one gutter behind.
   The "background loader": thin bar at the top of a region while it loads. */
.nm-relay { position: absolute; inset: 0 0 auto; height: 3px; overflow: hidden; pointer-events: none; z-index: 5;
  animation: nm-appear 200ms linear 180ms both; }
.nm-relay::before, .nm-relay::after { content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 22%; border-radius: 0 3px 3px 0; background: var(--nm-indigo);
  animation: nm-relay 1.5s var(--nm-io) infinite; }
.nm-relay::after { width: 9%; opacity: .45; animation-delay: 140ms; }
@keyframes nm-relay { from { transform: translateX(-110%); } to { transform: translateX(560%); } }
@keyframes nm-appear { from { opacity: 0; } to { opacity: 1; } }
.nm-relay-inline { position: relative; display: block; height: 3px; border-radius: 3px; background: color-mix(in srgb, var(--nm-indigo) 12%, transparent); }

/* L3 — The bloom: squares become the mark, one after another, and back. JS (motion.ts). */

/* L4 — The dispatch: a signal crosses the gutter and wakes the tile it reaches. */
.nm-dispatch .nm-sig { fill: var(--nm-signal); transform-box: view-box; animation: nm-dispatch 2.4s var(--nm-io) infinite; }
/* Arms clockwise from the top; the tile clockwise of an arm wakes as the signal reaches its end. */
.nm-dispatch .nm-t { animation: nm-woken 2.4s var(--nm-ease) infinite; animation-delay: calc(var(--i) * 600ms - 300ms); opacity: .3; }
@keyframes nm-dispatch {
  0%, 100% { transform: translate(0, 0); }
  12.5% { transform: translate(0, var(--up)); }
  25% { transform: translate(0, 0); }
  37.5% { transform: translate(var(--right), 0); }
  50% { transform: translate(0, 0); }
  62.5% { transform: translate(0, var(--down)); }
  75% { transform: translate(0, 0); }
  87.5% { transform: translate(var(--left), 0); }
}
@keyframes nm-woken { 0% { opacity: .3; } 6% { opacity: 1; } 30%, 100% { opacity: .3; } }

/* L5 — The tick: one quarter turning in quarter steps. A "working" status dot. */
.nm-tick { display: inline-block; position: relative; width: 10px; height: 10px; flex: none; color: inherit; }
.nm-tick::before { content: ""; position: absolute; inset: 0; border-radius: 50%; background: currentColor; opacity: .18; }
.nm-tick i { position: absolute; left: 0; top: 0; width: 50%; height: 50%; background: currentColor; border-radius: 100% 0 0 0; transform-origin: 100% 100%; animation: nm-tick 2.4s var(--nm-snap) infinite; }
@keyframes nm-tick {
  0%, 18% { transform: rotate(0); }
  25%, 43% { transform: rotate(90deg); }
  50%, 68% { transform: rotate(180deg); }
  75%, 93% { transform: rotate(270deg); }
  100% { transform: rotate(360deg); }
}

/* Busy button: label stays readable, the round sits beside it. */
.nm-busy { display: inline-flex; align-items: center; gap: 8px; }
.nm-busy .nm-mark { width: 1em; height: 1em; }

/* ---------- Fields (backgrounds). Canvas, drawn by motion.ts ---------- */
.nm-field { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; display: block; }

/* F3 — The big mark: a cropped mark whose cross breathes. */
.nm-giant { position: absolute; pointer-events: none; overflow: visible; }
.nm-giant .nm-t { animation: nm-breathe 9s var(--nm-io) infinite alternate; }
@keyframes nm-breathe { from { transform: none; } to { transform: translate(calc(var(--sx) * 22px), calc(var(--sy) * 22px)); } }

/* F4 — The pattern: tiles on a grid, drifting slowly along one axis. */
.nm-pattern { background-repeat: repeat; animation: nm-drift 60s linear infinite; }
@keyframes nm-drift { from { background-position: 0 0; } to { background-position: var(--nm-pattern-size, 96px) 0; } }

/* ---------- Transitions ---------- */

/* T1 — The shutter: four panels close from the corners, the mark assembles in the cross, they open. */
.nm-shutter { position: fixed; inset: 0; z-index: 10000; pointer-events: auto; display: grid; place-items: center; }
.nm-shutter > i { position: absolute; width: calc(50% - 3px); height: calc(50% - 3px); background: var(--nm-indigo);
  transition: transform 420ms var(--nm-ease); transition-delay: calc(var(--i) * var(--nm-stagger)); }
.nm-shutter > i:nth-child(1) { left: 0; top: 0; transform: translate(-101%, -101%); border-bottom-right-radius: 0; }
.nm-shutter > i:nth-child(2) { right: 0; top: 0; transform: translate(101%, -101%); }
.nm-shutter > i:nth-child(3) { right: 0; bottom: 0; transform: translate(101%, 101%); }
.nm-shutter > i:nth-child(4) { left: 0; bottom: 0; transform: translate(-101%, 101%); }
.nm-shutter.is-closed > i { transform: none; }
.nm-shutter .nm-mark { position: relative; width: 72px; height: auto; color: var(--nm-ivory); opacity: 0; }
.nm-shutter.is-closed .nm-mark { opacity: 1; }

/* T2 — The quarter reveal: content opens from one corner along a quarter arc. */
.nm-ready [data-nm-reveal="quarter"] { clip-path: circle(0 at 0 0); }
.nm-ready [data-nm-reveal="quarter"].is-in { clip-path: circle(142% at 0 0); transition: clip-path 640ms var(--nm-ease); }

/* T3 — The cascade: items rise into place, 50 ms apart. Visible without JS. */
.nm-ready [data-nm-cascade] > * { opacity: 0; transform: translateY(10px); }
.nm-ready [data-nm-cascade].is-in > * { opacity: 1; transform: none; transition: opacity 360ms var(--nm-ease), transform 480ms var(--nm-ease);
  transition-delay: calc(var(--n, 0) * var(--nm-cascade)); }

/* M2 — Press: 97 % for 120 ms. */
.nm-press { transition: transform var(--nm-press) var(--nm-ease); }
.nm-press:not(:disabled):active { transform: scale(.97); }

/* ---------- Reduced motion: the final state, at once ---------- */
@media (prefers-reduced-motion: reduce) {
  .nm-mark .nm-t, .nm-lockup .nm-word > *, .nm-giant .nm-t, .nm-pattern { animation: none !important; transition: none !important; }
  .nm-round .nm-t { animation: nm-round-calm 2.4s linear infinite !important; animation-delay: calc(var(--i) * 600ms) !important; transform: none; }
  .nm-relay::before { animation: nm-relay-calm 1.6s linear infinite !important; width: 100%; transform: none; border-radius: 0; }
  .nm-relay::after { display: none; }
  .nm-dispatch .nm-sig { animation: none; opacity: 0; }
  .nm-dispatch .nm-t { animation: nm-round-calm 2.4s linear infinite !important; }
  .nm-tick i { animation: none; transform: none; }
  .nm-shutter { display: none; }
  .nm-ready [data-nm-reveal="quarter"], .nm-ready [data-nm-cascade] > * { clip-path: none; opacity: 1; transform: none; transition: none; }
  .nm-press:not(:disabled):active { transform: none; }
}
html.nm-reduced .nm-mark .nm-t, html.nm-reduced .nm-lockup .nm-word > *, html.nm-reduced .nm-giant .nm-t, html.nm-reduced .nm-pattern { animation: none !important; transition: none !important; }
html.nm-reduced .nm-round .nm-t { animation: nm-round-calm 2.4s linear infinite !important; animation-delay: calc(var(--i) * 600ms) !important; transform: none; }
html.nm-reduced .nm-relay::before { animation: nm-relay-calm 1.6s linear infinite !important; width: 100%; transform: none; border-radius: 0; }
html.nm-reduced .nm-relay::after { display: none; }
html.nm-reduced .nm-dispatch .nm-sig { animation: none; opacity: 0; }
html.nm-reduced .nm-dispatch .nm-t { animation: nm-round-calm 2.4s linear infinite !important; }
html.nm-reduced .nm-tick i { animation: none; transform: none; }
html.nm-reduced .nm-shutter { display: none; }
html.nm-reduced [data-nm-reveal="quarter"], html.nm-reduced [data-nm-cascade] > * { clip-path: none !important; opacity: 1 !important; transform: none !important; }
/* Still says "working", without travelling: a slow fade, no movement. */
@keyframes nm-round-calm { 0%, 100% { opacity: .35; } 25% { opacity: 1; } 50% { opacity: .35; } }
@keyframes nm-relay-calm { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* ---------- App placements ---------- */
.nm-splash { position: relative; overflow: hidden; isolation: isolate; }
.nm-splash > .nm-field { z-index: -1; opacity: .5; -webkit-mask-image: radial-gradient(farthest-side, transparent 18%, #000 55%, transparent 100%); mask-image: radial-gradient(farthest-side, transparent 18%, #000 55%, transparent 100%); }
.nm-splash-label { color: var(--text-secondary, #566174); font-size: 14px; animation: nm-appear 320ms var(--nm-ease) 420ms both; }
.nm-page-wait { position: relative; flex: 1; }

/* L1 beside the existing "Loading…" texts. */
.nm-waiting { display: inline-flex; align-items: center; gap: 8px; }
.nm-waiting .nm-mark { color: var(--nm-indigo); }

/* T3 in the app: lists rise in as they arrive (and a new item rises when it appears).
   Plays on insertion only; "backwards" fill releases the item for hover/press. */
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card) { animation: nm-rise 420ms var(--nm-ease) backwards; animation-delay: calc(var(--n, 0) * var(--nm-cascade)); }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(2) { --n: 1; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(3) { --n: 2; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(4) { --n: 3; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(5) { --n: 4; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(6) { --n: 5; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(7) { --n: 6; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(8) { --n: 7; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(9) { --n: 8; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(10) { --n: 9; }
:is(.nm-cascade > *, .nm-rise, .project-card, .integration-card):nth-child(n + 11) { --n: 10; }
@keyframes nm-rise { from { opacity: 0; transform: translateY(10px); } }

/* T2 in the app: a report card opens from its corner along a quarter arc. */
.rc { animation: nm-quarter 640ms var(--nm-ease) backwards; }
@keyframes nm-quarter { from { clip-path: circle(0 at 0 0); } to { clip-path: circle(142% at 0 0); } }

@media (prefers-reduced-motion: reduce) { :is(.nm-cascade > *, .nm-rise, .project-card, .integration-card), .rc { animation: none !important; } }
