/* NOD brand primitives shared by the public site and the signed-in app:
   the local sans face, the original mark and its motion. No page layout lives here. */
/* Geist is NOD's one typeface on every page (revision 4.2). */
/* Declared 100-600: any request for 700-950 from an older layer renders at 600, NOD's heaviest weight. */
/* Part 10: every font is self-hosted (licences beside the files); no Google Fonts requests.
   Older style.css rules name Inter, Bebas Neue and JetBrains Mono directly. */
@font-face { font-family: 'JetBrains Mono'; src: url('assets/fonts/jetbrains-mono-latin.woff2') format('woff2'); font-weight: 100 800; font-style: normal; font-display: swap; }
@font-face { font-family: 'Inter'; src: url('assets/fonts/inter-latin.woff2') format('woff2'); font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: 'Bebas Neue'; src: url('assets/fonts/geist-latin.woff2') format('woff2'); font-weight: 100 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'NOD Geist'; src: url('assets/fonts/geist-latin.woff2') format('woff2'); font-weight: 100 600; font-style: normal; font-display: swap; }
/* Arial scaled to Geist's measured width and metrics, so a late font swap barely reflows the page. */
@font-face { font-family: 'NOD Geist Fallback'; src: local('Arial'), local('ArialMT'), local('Liberation Sans'); size-adjust: 102%; ascent-override: 98.5%; descent-override: 29%; line-gap-override: 0%; }
@font-face { font-family: 'NOD Sans'; src: url('assets/fonts/instrument-sans-latin.woff2') format('woff2'); font-weight: 400 700; font-style: normal; font-display: swap; }
/* Arial scaled to Instrument Sans metrics, so a late font swap does not reflow the page. */
@font-face { font-family: 'NOD Sans Fallback'; src: local('Arial'), local('ArialMT'), local('Liberation Sans'); size-adjust: 101.8%; ascent-override: 95.3%; descent-override: 24.6%; line-gap-override: 0%; }

/* The NOD loading mark: original artwork, a soft settle, then light moving across the mark. */
.nod-loader { position: relative; display: inline-block; width: var(--loader-size, 20px); height: var(--loader-size, 20px); flex: none; }
.nod-loader img { display: block; width: 100%; height: 100%; object-fit: contain; }
.nod-loader::after { content: ""; position: absolute; inset: 0; opacity: 0; pointer-events: none; background: linear-gradient(100deg, transparent 32%, rgba(255, 246, 222, .95) 50%, transparent 68%) no-repeat; background-size: 300% 100%; background-position: 120% 0; -webkit-mask: url('assets/nod-mark-display.webp') center / contain no-repeat; mask: url('assets/nod-mark-display.webp') center / contain no-repeat; mix-blend-mode: screen; }
.is-loading .nod-loader { animation: nod-loader-in 420ms var(--ease-out, cubic-bezier(.16, 1, .3, 1)) 150ms both, nod-loader-breathe 1.8s ease-in-out 600ms infinite; }
.is-loading .nod-loader::after { animation: nod-loader-sweep 1.8s ease-in-out 600ms infinite; }
@keyframes nod-loader-in { from { opacity: 0; transform: scale(.86); } to { opacity: 1; transform: none; } }
@keyframes nod-loader-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .72; } }
@keyframes nod-loader-sweep { 0% { opacity: 0; background-position: 120% 0; } 12% { opacity: 1; } 68% { opacity: 1; background-position: -20% 0; } 100% { opacity: 0; background-position: -20% 0; } }

/* Signed-in app logo. The same mark stays visible at all times:
   one short entrance per page load, still when idle, and the highlight only while
   genuine foreground work is pending. script.js sets data-foreground-loading;
   the 150 ms animation delay keeps fast requests from flashing. No minimum duration. */
@keyframes nod-mark-enter { from { opacity: 0; transform: scale(.9); } to { opacity: 1; transform: none; } }
.app-mark { --loader-size: 30px; }
.app-mark:not(.has-entered) img { animation: nod-mark-enter 460ms var(--ease-out, cubic-bezier(.16, 1, .3, 1)) both; }
.app-mark:not(.has-entered)::after { animation: nod-loader-sweep 1.1s ease-in-out 220ms 1; }
html[data-foreground-loading] .app-mark { animation: nod-loader-breathe 1.8s ease-in-out 150ms infinite; }
html[data-foreground-loading] .app-mark::after { animation: nod-loader-sweep 1.8s ease-in-out 150ms infinite; }
/* Local indicator beside status text, for panels, answers and the shared request pill. */
.nod-local-loader { --loader-size: 18px; }
.nod-local-loader.nod-loader, .is-pending > .nod-local-loader { animation: nod-loader-breathe 1.8s ease-in-out 150ms infinite; }
.nod-local-loader.nod-loader::after, .is-pending > .nod-local-loader::after { animation: nod-loader-sweep 1.8s ease-in-out 150ms infinite; }
/* Continuous motion stops while the page is hidden. */
html[data-page-hidden] .nod-loader { animation-play-state: paused !important; }
html[data-page-hidden] .nod-loader::after { animation-play-state: paused !important; }
@media (prefers-reduced-motion: reduce) {
  :is(.app-mark, .nod-local-loader), :is(.app-mark, .nod-local-loader) img, :is(.app-mark, .nod-local-loader)::after { animation: none !important; }
}
.pref-reduced-motion :is(.app-mark, .nod-local-loader), .pref-reduced-motion :is(.app-mark, .nod-local-loader) img, .pref-reduced-motion :is(.app-mark, .nod-local-loader)::after { animation: none !important; }
