/* ==========================================================================
   News Optix — Theme foundation
   The single source of truth for the dark News Optix UI: design tokens, base
   reset, shared components, and animations. Every page links this first, then
   its own page stylesheet. Tokens are lifted from the Trade Optix design system
   and the homepage design handoff.
   ========================================================================== */

:root {
  color-scheme: dark;

  /* ---------- Surfaces & borders ---------- */
  --nx-bg: #08111C;
  --nx-surface: #0C1724;
  --nx-card: #101D2B;
  --nx-card-2: #122234;
  --nx-border: rgba(100, 160, 215, 0.16);
  --nx-border-strong: rgba(100, 160, 215, 0.30);

  /* ---------- Brand & text ---------- */
  --nx-blue: #2389E8;
  --nx-accent: #45A9FF;
  --nx-accent-hover: #7CC4FF;
  --nx-text: #F5F8FC;
  --nx-muted: #8795A6;
  --nx-faint: #5C6B7C;
  --nx-footer-brand: #B6C2CE;

  /* ---------- Market signal colors (glyph-paired, never decorative) ---------- */
  --nx-up: #22C77A;
  --nx-down: #EF5A67;
  --nx-warn: #E8A73C;

  /* ---------- Type families ---------- */
  --nx-disp: 'Plus Jakarta Sans', ui-sans-serif, system-ui, sans-serif;
  --nx-body: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --nx-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ---------- Motion ---------- */
  --nx-ease: cubic-bezier(0.2, 0, 0.1, 1);
  --nx-dur-fast: 120ms;
  --nx-dur-base: 200ms;
  --nx-dur-slow: 360ms;

  /* ---------- Async-state surfaces ----------
     Semantic tints shared by every loading / error / empty / offline affordance.
     Each page previously re-invented these as inline rgba(); they are tokens so
     an error panel on the homepage and one on the article page cannot drift. */
  --nx-skel-1: rgba(100, 160, 215, 0.06);
  --nx-skel-2: rgba(100, 160, 215, 0.17);
  --nx-danger-bg: rgba(239, 90, 103, 0.06);
  --nx-danger-bd: rgba(239, 90, 103, 0.30);
  --nx-warn-bg: rgba(232, 167, 60, 0.10);
  --nx-warn-bd: rgba(232, 167, 60, 0.32);
  --nx-info-bg: rgba(69, 169, 255, 0.06);
  --nx-info-bd: rgba(69, 169, 255, 0.26);

  /* ---------- Added for Event Detail ----------
     The four the event-detail design needs that had no equivalent here. The
     rest of its palette maps onto the tokens above, as the previous handoffs
     did — this page should look like the site, not like a second product.

     --nx-info is the solid "developing / primary document" blue (the existing
     --nx-info-bg/-bd were derived from --nx-accent and there was no solid);
     --nx-conflict is the disputed-evidence orange, deliberately distinct from
     --nx-down so "sources disagree" never reads as "bearish";
     --nx-surface-3 is a third elevation, used as the track under progress bars;
     --nx-accent-2 is the muted accent for system/neutral marks. */
  --nx-info: #38BDF8;
  --nx-conflict: #F6893B;
  --nx-surface-3: #17253F;
  --nx-accent-2: #94B2E1;

  /* ---------- Measured chrome ----------
     Height of the sticky banner+header stack, written by theme.js from the live
     element. Pages use it for sticky offsets and scroll-spy rootMargin. The
     fallback matches the header's design height so layout is sane pre-script. */
  --nx-stack: 64px;
}

/* ==========================================================================
   Base reset
   ========================================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The [hidden] attribute must actually hide.
   The UA stylesheet's `[hidden] { display: none }` loses to any author rule
   that sets `display`, so an element styled `.thing { display: flex }` stays
   visible even while carrying `hidden`. Every state container on the site is
   toggled by that attribute, so without this they are all permanently on
   screen — which is exactly what was happening to the load-more area. */
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--nx-bg);
}

body {
  min-height: 100dvh;
  font-family: var(--nx-body);
  color: var(--nx-text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a {
  color: var(--nx-accent);
  text-decoration: none;
  transition: color var(--nx-dur-fast) var(--nx-ease);
}

a:hover {
  color: var(--nx-accent-hover);
}

::selection {
  background: rgba(35, 137, 232, 0.32);
}

/* ==========================================================================
   Accessibility helpers
   ========================================================================== */
:focus-visible {
  outline: 2px solid var(--nx-accent);
  outline-offset: 2px;
  border-radius: 6px;
}

button:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--nx-accent);
  outline-offset: 2px;
  border-radius: 6px;
}

/* Visually hidden but available to assistive tech. */
/* ==========================================================================
   Asset logos
   ==========================================================================
   One atom, sized in `em`, so it scales itself across every chip's existing
   font-size (10.5px on an events card through 13px in the article rails) from
   a single rule. Only the two avatar slots — which have no text to size from —
   need pixel modifiers.

   `nx-logo__sym` and `nx-logo__fb` are deliberately unstyled: the slot's own
   class keeps styling its text, which is what makes the no-logo case render
   pixel-identically to how it did before logos existed. Roughly half the labels
   in the fixtures have no logo file, so that path is the common one.
   ========================================================================== */
.nx-logo {
  width: 1.35em;
  height: 1.35em;
  /* Never upscale past the intrinsic 32px — the feed-card ticker sits at 22px
     and would otherwise blur. */
  max-width: 32px;
  max-height: 32px;
  flex: none;
  border-radius: 50%;
  object-fit: contain;
  vertical-align: -0.25em;
}

.nx-logo--md { width: 22px; height: 22px; }   /* inside the 36x36 .nx-asset__tile */
.nx-logo--lg { width: 26px; height: 26px; }   /* inside the 42x42 .evd-assetcard__chip */

/* Containers that hold a logo beside its ticker text. Declared here rather than
   per-page so a chip gains the gap by adopting the atom, not by remembering to
   add a rule. */
.ev-asset,
.lt-asset,
.nx-assetchip,
.nx-strip__ticker,
.nx-logo-row {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.nx-logo-row { flex-wrap: wrap; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   Layout
   ========================================================================== */
.nx-container {
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 40px;
}

@media (max-width: 900px) {
  .nx-container {
    padding: 0 20px;
  }
}

/* ==========================================================================
   Shared components
   ========================================================================== */

/* Mono / tabular numerics — tickers, prices, counts, timestamps, code. */
.nx-mono {
  font-family: var(--nx-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" on, "zero" on;
}

/* Wordmark: "News" white / "Optix" accent. */
.nx-wordmark {
  font-family: var(--nx-disp);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--nx-text);
}

.nx-wordmark .nx-optix {
  color: var(--nx-accent);
}

/* Primary CTA — gradient fill with inset highlight, hover-brighten. */
.nx-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  padding: 0 20px;
  border-radius: 10px;
  font-family: var(--nx-body);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: filter var(--nx-dur-fast) var(--nx-ease),
    background var(--nx-dur-base) var(--nx-ease),
    border-color var(--nx-dur-base) var(--nx-ease);
}

.nx-btn--primary {
  color: #fff;
  background: linear-gradient(180deg, #2A93F2 0%, #1F7AD4 100%);
  border: 1px solid rgba(69, 169, 255, 0.4);
  box-shadow: 0 6px 18px -8px rgba(35, 137, 232, 0.7),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.nx-btn--primary:hover {
  filter: brightness(1.08);
}

.nx-btn--primary:active {
  filter: brightness(0.94);
}

.nx-btn--ghost {
  color: var(--nx-text);
  border: 1px solid var(--nx-border-strong);
  background: rgba(100, 160, 215, 0.05);
}

.nx-btn--ghost:hover {
  background: rgba(100, 160, 215, 0.1);
}

/* Pill / badge — the mono-uppercase status chip. */
.nx-pill {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--nx-border-strong);
  background: rgba(69, 169, 255, 0.07);
  font-family: var(--nx-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--nx-accent);
}

/* Card surface. */
.nx-card {
  border: 1px solid var(--nx-border);
  background: var(--nx-card);
  border-radius: 16px;
  transition: border-color var(--nx-dur-base) var(--nx-ease);
}

.nx-card:hover {
  border-color: var(--nx-border-strong);
}

/* Live status dot — pulsing signal indicator. */
.nx-live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--nx-up);
  box-shadow: 0 0 8px rgba(34, 199, 122, 0.9);
  animation: nxPulse 2.4s ease-in-out infinite;
}

/* Development-preview banner — amber, sticky above the header. */
.nx-dev-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 9px;
  padding: 6px 20px;
  background: rgba(232, 167, 60, 0.12);
  border-bottom: 1px solid rgba(232, 167, 60, 0.28);
}

.nx-dev-banner__dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--nx-warn);
  flex: 0 0 auto;
}

.nx-dev-banner__text {
  font-family: var(--nx-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--nx-warn);
  text-align: center;
}

.nx-dev-banner__aside {
  font-family: var(--nx-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: rgba(232, 167, 60, 0.65);
}

/* Thin custom scrollbars for horizontally scrolling rows. */
.nx-scroll::-webkit-scrollbar {
  height: 6px;
  width: 6px;
}

.nx-scroll::-webkit-scrollbar-thumb {
  background: rgba(100, 160, 215, 0.22);
  border-radius: 99px;
}

/* ==========================================================================
   Animations
   ========================================================================== */
@keyframes nxPulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.4;
    transform: scale(0.8);
  }
}

@keyframes nxShimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@keyframes nxSpin {
  to { transform: rotate(360deg); }
}

/* Indeterminate progress: a bar that traverses its track without ever
   claiming a percentage, for waits whose duration is genuinely unknown. */
@keyframes nxIndet {
  0%   { left: -45%; width: 45%; }
  60%  { width: 55%; }
  100% { left: 100%; width: 35%; }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* ==========================================================================
   Async states — skeletons, panels, progress
   The single vocabulary for "we are fetching / it failed / there is nothing".
   Every page uses these; no page defines its own shimmer or error panel.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Skeleton
   The background-color is load-bearing, not decoration. The blanket
   prefers-reduced-motion rule above kills the animation entirely, and a
   skeleton whose only visible fill came from the moving gradient would render
   as an invisible block for those users. The flat tint underneath is what they
   see; the gradient is the enhancement on top.
   -------------------------------------------------------------------------- */
.nx-skel {
  display: block;
  border-radius: 7px;
  background-color: var(--nx-skel-1);
  background-image: linear-gradient(
    90deg,
    var(--nx-skel-1) 20%,
    var(--nx-skel-2) 50%,
    var(--nx-skel-1) 80%
  );
  background-size: 200% 100%;
  animation: nxShimmer 1.6s linear infinite;
}

.nx-skel--strong { --nx-skel-1: rgba(100, 160, 215, 0.10); --nx-skel-2: rgba(100, 160, 215, 0.23); }
.nx-skel--text   { height: 13px; border-radius: 5px; }
.nx-skel--title  { height: 26px; border-radius: 6px; }
.nx-skel--pill   { height: 22px; width: 92px; border-radius: 99px; }
.nx-skel--card   { height: 110px; border-radius: 12px; }

/* Skeleton groups carry aria-busy; hide the scaffolding from assistive tech
   since the visible shapes convey nothing on their own. */
.nx-skelgroup { display: grid; gap: 10px; }
.nx-skelrow { display: flex; gap: 10px; align-items: center; }

/* --------------------------------------------------------------------------
   State panel
   One component for error / empty / filtered-empty / offline, distinguished by
   modifier. Layout and rhythm are shared so an error on the homepage and an
   error on the article page cannot drift apart.
   -------------------------------------------------------------------------- */
.nx-state {
  text-align: center;
  padding: 64px 24px;
  border: 1px solid var(--nx-border);
  border-radius: 16px;
  background: var(--nx-card);
  margin-top: 16px;
}

.nx-state--error   { border-color: var(--nx-danger-bd); background: var(--nx-danger-bg); }
.nx-state--offline { border-color: var(--nx-warn-bd);   background: var(--nx-warn-bg); }
.nx-state--empty   { border-color: var(--nx-info-bd);   background: var(--nx-info-bg); }

/* Filtered-empty reads as "your query returned nothing", not as a failure —
   dashed and untinted, so it is visibly a lesser event than an outage. */
.nx-state--filtered {
  border-style: dashed;
  border-color: var(--nx-border-strong);
  background: rgba(100, 160, 215, 0.03);
  padding: 56px 24px;
}

.nx-state__icon {
  width: 52px;
  height: 52px;
  border-radius: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  background: rgba(100, 160, 215, 0.08);
  color: var(--nx-accent);
}

.nx-state--error .nx-state__icon   { background: rgba(239, 90, 103, 0.12); color: var(--nx-down); }
.nx-state--offline .nx-state__icon { background: rgba(232, 167, 60, 0.14); color: var(--nx-warn); }

.nx-state__h { font-family: var(--nx-disp); font-size: 19px; color: var(--nx-text); margin: 0 0 8px; }
.nx-state__p { font-size: 14px; color: var(--nx-faint); margin: 0 auto 20px; max-width: 420px; line-height: 1.55; }
.nx-state__actions { display: inline-flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

/* Machine-readable failure reference. Monospaced because it exists to be read
   aloud to support or pasted into a ticket. */
.nx-state__ref {
  font-family: var(--nx-mono);
  font-size: 11.5px;
  color: var(--nx-faint);
  margin-top: 18px;
  letter-spacing: 0.02em;
}

.nx-state__reasons { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin: 0 0 18px; padding: 0; list-style: none; }
.nx-state__reason {
  font-size: 12px;
  color: var(--nx-muted);
  border: 1px solid var(--nx-border);
  border-radius: 99px;
  padding: 5px 11px;
  background: var(--nx-card-2);
}

/* --------------------------------------------------------------------------
   Offline banner
   Distinct from .nx-state--offline: this sits above content that is still on
   screen and usable, so it is a strip rather than a panel. Cached data with a
   warning beats a blank page.
   -------------------------------------------------------------------------- */
.nx-offline {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border: 1px solid var(--nx-warn-bd);
  background: var(--nx-warn-bg);
  border-radius: 12px;
  margin-bottom: 16px;
  font-size: 13.5px;
  color: var(--nx-text);
}

.nx-offline__text { flex: 1; min-width: 0; }
.nx-offline__text strong { font-weight: 600; }

/* --------------------------------------------------------------------------
   Progress affordances
   -------------------------------------------------------------------------- */
.nx-spinner {
  width: 15px;
  height: 15px;
  border: 2px solid rgba(69, 169, 255, 0.3);
  border-top-color: var(--nx-accent);
  border-radius: 50%;
  display: inline-block;
  flex: none;
  animation: nxSpin 1s linear infinite;
}

.nx-spinner--sm { width: 12px; height: 12px; border-width: 1.5px; }

/* Shown while refreshing content that is already on screen. Deliberately
   understated — the feed below it stays readable and must not feel disabled. */
.nx-updating {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--nx-mono);
  font-size: 12px;
  color: var(--nx-faint);
}

.nx-indet {
  position: relative;
  height: 3px;
  width: 100%;
  overflow: hidden;
  border-radius: 99px;
  background: rgba(100, 160, 215, 0.10);
}

.nx-indet__bar {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -45%;
  width: 45%;
  background: var(--nx-accent);
  border-radius: 99px;
  animation: nxIndet 1.1s var(--nx-ease) infinite;
}

/* Reduced motion removes the traversal, leaving a static bar that would read as
   "stalled at 0%". A filled track is the honest still-frame of "working". */
@media (prefers-reduced-motion: reduce) {
  .nx-indet__bar { left: 0; width: 100%; opacity: 0.55; }
}

/* Section-scoped refresh: dim slightly so it reads as provisional, but stay
   legible and interactive. This is the stale half of stale-while-revalidate. */
.nx-stale { opacity: 0.62; transition: opacity var(--nx-dur-base) var(--nx-ease); }

/* Utility: hide on narrow viewports (≤900px). */
@media (max-width: 900px) {
  .nx-hide-sm {
    display: none !important;
  }
}

/* ==========================================================================
   Sentiment & signal vocabulary
   Market direction is NEVER conveyed by colour alone — always paired with a
   glyph (▲ ▼ ●) and text in the markup. These classes only carry the colour.
   ========================================================================== */
.nx-up { color: var(--nx-up); }
.nx-down { color: var(--nx-down); }
.nx-neutral { color: var(--nx-warn); }

/* Signal glyph chip — a small tinted badge showing only the ▲/▼/● glyph. */
.nx-sig-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--nx-mono);
  font-size: 10px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 5px;
}

.nx-sig-chip--up { color: var(--nx-up); background: rgba(34, 199, 122, 0.14); }
.nx-sig-chip--down { color: var(--nx-down); background: rgba(239, 90, 103, 0.14); }
.nx-sig-chip--neutral { color: var(--nx-warn); background: rgba(232, 167, 60, 0.14); }

/* Tooltip affordance — pair with native title="" + tabindex="0" for keyboard reach. */
.nx-help { cursor: help; }

/* ==========================================================================
   Shared text roles
   ========================================================================== */

/* Eyebrow / overline — the section marker above a heading. */
.nx-eyebrow {
  font-family: var(--nx-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--nx-accent);
}

.nx-eyebrow--wide { letter-spacing: 0.2em; font-size: 12px; }
.nx-eyebrow--faint { color: var(--nx-faint); letter-spacing: 0.14em; }

/* Section heading. */
.nx-h2 {
  font-family: var(--nx-disp);
  font-weight: 800;
  font-size: 30px;
  letter-spacing: -0.025em;
  color: var(--nx-text);
  margin: 0;
}

/* "Feature Preview" / "Coming Later" status tag. */
.nx-tag {
  display: inline-flex;
  align-items: center;
  font-family: var(--nx-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid rgba(232, 167, 60, 0.4);
  background: rgba(232, 167, 60, 0.1);
  color: var(--nx-warn);
}

/* ==========================================================================
   Button size modifier + link-style CTA
   ========================================================================== */
.nx-btn--sm { height: 40px; padding: 0 18px; }
.nx-btn--wide-radius { border-radius: 12px; }

/* Inline "View all … →" text link used under section lists. */
.nx-link-accent {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--nx-accent);
}

/* ==========================================================================
   Site header (reusable chrome)
   ========================================================================== */
.nx-sticky-stack {
  position: sticky;
  top: 0;
  z-index: 50;
}

.nx-header {
  position: relative;
  z-index: 2;
  border-bottom: 1px solid var(--nx-border);
  background: rgba(8, 17, 28, 0.55);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transition: background var(--nx-dur-base) var(--nx-ease),
    border-color var(--nx-dur-base) var(--nx-ease),
    box-shadow var(--nx-dur-base) var(--nx-ease);
}

/* Scroll-solidified state, toggled by theme.js. */
.nxHeader--solid {
  background: rgba(8, 17, 28, 0.94);
  border-bottom-color: var(--nx-border-strong);
  box-shadow: 0 8px 30px -12px rgba(0, 0, 0, 0.6);
}

.nx-header__inner {
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.nx-header__lead {
  display: flex;
  align-items: center;
  flex: 0 1 auto;
  min-width: 0;
}

.nx-header__brand {
  display: flex;
  align-items: center;
  gap: 11px;
  flex: 0 1 auto;
  min-width: 0;
}

.nx-header__brand:hover { color: var(--nx-text); }

/* "Back to top" control — collapsed to zero footprint until the page is
   scrolled (.nxHeader--scrolled is toggled by theme.js), then it expands in
   beside the brand. Collapsing width + margin (rather than hiding) means there
   is no empty gap next to the logo at the top of the page. */
.nx-header__top {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  height: 30px;
  max-width: 0;
  margin-left: 0;
  padding: 0;
  border-radius: 8px;
  border: 0 solid var(--nx-border-strong);
  background: rgba(69, 169, 255, 0.06);
  color: var(--nx-muted);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: max-width var(--nx-dur-base) var(--nx-ease),
              margin-left var(--nx-dur-base) var(--nx-ease),
              padding var(--nx-dur-base) var(--nx-ease),
              opacity var(--nx-dur-fast) var(--nx-ease),
              color var(--nx-dur-fast) var(--nx-ease),
              border-color var(--nx-dur-fast) var(--nx-ease);
}

.nx-header__top svg { flex: 0 0 auto; }

.nx-header__top:hover {
  color: var(--nx-accent);
  border-color: color-mix(in srgb, var(--nx-accent) 45%, transparent);
  background: rgba(69, 169, 255, 0.12);
}

.nxHeader--scrolled .nx-header__top {
  max-width: 120px;
  margin-left: 10px;
  padding: 0 10px;
  border-width: 1px;
  opacity: 1;
  pointer-events: auto;
}

@media (max-width: 720px) {
  .nx-header__top-label { display: none; }
  .nxHeader--scrolled .nx-header__top { padding: 0 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .nx-header__top { transition: opacity var(--nx-dur-fast) var(--nx-ease); }
}

.nx-header__logo {
  height: 32px;
  width: auto;
  display: block;
  filter: drop-shadow(0 3px 10px rgba(35, 137, 232, 0.35));
}

.nx-header__wordmark { font-size: 19px; }

/* Primary desktop navigation. */
.nx-nav {
  display: flex;
  align-items: center;
  gap: 26px;
}

.nx-nav__link {
  font-size: 14px;
  font-weight: 500;
  color: var(--nx-muted);
  white-space: nowrap;
  transition: color var(--nx-dur-fast) var(--nx-ease);
}

.nx-nav__link:hover { color: var(--nx-text); }

.nx-nav__link.is-active {
  color: var(--nx-text);
  background: rgba(69, 169, 255, 0.16);
  border-radius: 8px;
  padding: 6px 12px;
}

.nx-menu__link.is-active { color: var(--nx-text); background: rgba(69, 169, 255, 0.12); }

.nx-header__actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}

/* Icon button (search). */
.nx-iconbtn {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  border: 1px solid var(--nx-border);
  background: transparent;
  color: var(--nx-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color var(--nx-dur-fast) var(--nx-ease),
    border-color var(--nx-dur-fast) var(--nx-ease),
    background var(--nx-dur-fast) var(--nx-ease);
}

.nx-iconbtn:hover {
  color: var(--nx-text);
  border-color: var(--nx-border-strong);
  background: rgba(100, 160, 215, 0.06);
}

.nx-header__signin {
  font-size: 14px;
  font-weight: 500;
  color: var(--nx-muted);
  padding: 0 4px;
}

.nx-header__signin:hover { color: var(--nx-text); }

/* Hamburger — shown ≤900px only. */
.nx-burger {
  display: none;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  border: 1px solid var(--nx-border);
  background: transparent;
  color: var(--nx-text);
  cursor: pointer;
  align-items: center;
  justify-content: center;
}

/* Mobile dropdown menu — toggled by theme.js (.is-open). */
.nx-menu {
  display: none;
  border-top: 1px solid var(--nx-border);
  background: rgba(8, 17, 28, 0.97);
  padding: 12px 20px 18px;
}

.nx-menu.is-open { display: block; }

.nx-menu__inner {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nx-menu__link {
  padding: 11px 8px;
  font-size: 15px;
  font-weight: 500;
  color: var(--nx-text);
  border-radius: 8px;
}

.nx-menu__link:hover { background: rgba(100, 160, 215, 0.08); }

/* Shown only at the narrow tier, where its header counterpart is hidden. */
.nx-show-xs { display: none; }

.nx-menu__cta { width: 100%; margin-top: 8px; }

@media (max-width: 900px) {
  .nx-nav { display: none; }
  .nx-burger { display: inline-flex; }
}

/* ==========================================================================
   Phone header
   The header is the whole document's widest fixed obligation, so it is what
   decides the minimum page width. Below this tier the CTA moves into the menu:
   at 153px it is the single widest item in the bar and the one with the most
   comfortable home elsewhere.
   ========================================================================== */
@media (max-width: 700px) {
  .nx-header__cta { display: none; }
}

/*
   Last-resort tier: search moves into the menu too, leaving brand + burger.

   Set at 300px rather than at a phone width because the icon genuinely fits
   down to 320: brand (~137) + search (48) + burger (40) leaves headroom in the
   280px of content width a 320px viewport provides. Hiding it earlier would
   bury a control that had somewhere to be.
*/
@media (max-width: 300px) {
  .nx-hide-xs { display: none !important; }
  .nx-show-xs { display: block; }
}

/*
   Let the header's three parts share the width instead of forcing it wider.

   The brand may shrink and ellipsise its wordmark; the actions never shrink,
   because a burger that has been squeezed out of reach defeats the point of
   having one. The logo image keeps its full size — a shrunk-to-illegible mark
   is worse than a truncated word.
*/
.nx-header__inner { min-width: 0; }
.nx-header__brand { min-width: 0; flex: 0 1 auto; overflow: hidden; }
.nx-header__logo { flex: 0 0 auto; }
.nx-header__wordmark { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nx-header__actions { min-width: 0; flex: 0 0 auto; }

@media (max-width: 700px) {
  .nx-header__inner { gap: 12px; }
  .nx-header__actions { gap: 8px; }
}

/* ==========================================================================
   Horizontal-scroll row (mobile ticker / filter rows)
   ========================================================================== */
.nx-scrollrow {
  display: flex;
  align-items: center;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.nx-scrollrow > * { flex: 0 0 auto; }

/* ==========================================================================
   Market status strip (reusable chrome — used by home + article)
   ========================================================================== */
.nx-strip {
  position: relative;
  z-index: 10;
  border-bottom: 1px solid var(--nx-border);
  background: rgba(12, 23, 36, 0.6);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.nx-strip__inner {
  height: 44px;
  display: flex;
  align-items: center;
  gap: 16px;
}

/* ---- Scrolling ticker (marquee) ------------------------------------------
   The track holds two identical groups and translates -50%, so the loop is
   seamless. Fixed height + overflow:hidden means the motion never shifts the
   page. Slow (60s) and paused on hover / keyboard focus. Under reduced motion
   the blanket rule (above) freezes the animation; the fallback at the foot of
   this block hides the clone and makes the row user-scrollable instead. */
.nx-strip__marquee {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 24px, #000 calc(100% - 24px), transparent);
          mask-image: linear-gradient(90deg, transparent, #000 24px, #000 calc(100% - 24px), transparent);
}

.nx-strip__track {
  display: flex;
  width: max-content;
  animation: nxMarquee 60s linear infinite;
  will-change: transform;
}
.nx-strip__marquee:hover .nx-strip__track,
.nx-strip__marquee:focus-within .nx-strip__track { animation-play-state: paused; }

/* The gap between a group and its repeat, so the seam reads as a fresh ticker
   rather than one run-on sentence. */
.nx-strip__group { display: flex; padding-right: 22px; }

@keyframes nxMarquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.nx-strip__ticker {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  font-size: 12.5px;
  padding: 0 18px;
  border-right: 1px solid var(--nx-border);
}

.nx-strip__sym { color: var(--nx-muted); font-weight: 600; display: inline-flex; align-items: center; gap: 5px; }
.nx-strip__price { color: var(--nx-text); }
.nx-strip__chg { font-weight: 600; }

/* ---- Fixed status cell (does not scroll) --------------------------------- */
.nx-strip__status {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  white-space: nowrap;
  padding-left: 16px;
  border-left: 1px solid var(--nx-border);
}

.nx-strip__dot { width: 7px; height: 7px; border-radius: 99px; flex: 0 0 auto; }
.nx-strip__dot.is-open { background: var(--nx-up); animation: nxPulse 2.4s infinite; }
.nx-strip__dot.is-closed { background: var(--nx-faint); }

.nx-strip__market { font-size: 11px; letter-spacing: 0.04em; color: var(--nx-muted); text-transform: uppercase; }
.nx-strip__updated { font-size: 11.5px; color: var(--nx-faint); }
.nx-strip__clock { font-size: 11px; letter-spacing: 0.05em; color: var(--nx-faint); }

.nx-strip__divider {
  width: 1px;
  height: 16px;
  background: var(--nx-border);
  flex: 0 0 auto;
}

.nx-strip__skelitem { width: 90px; height: 12px; }

/* Reduced motion: the blanket rule already stops the animation; give it a
   clean static resting state — a user-scrollable single list, clone hidden. */
@media (prefers-reduced-motion: reduce) {
  .nx-strip__marquee { overflow-x: auto; }
  .nx-strip__track { width: auto; }
  .nx-strip__group[aria-hidden="true"] { display: none; }
}

/* The fixed status cell (flex: 0 0 auto, nowrap) cannot shrink, so its full
   content sets a minimum document width that overflows a phone (invariant 8).
   Drop the least-critical parts progressively — freshness, then the clock — but
   always keep the dot beside its market label, so the open/closed state is
   never reduced to colour alone. */
@media (max-width: 680px) { .nx-strip__updated { display: none; } }
@media (max-width: 460px) { .nx-strip__clock { display: none; } }

/* ==========================================================================
   Site footer (reusable chrome)
   ========================================================================== */
.nx-foot {
  position: relative;
  z-index: 1;
  margin-top: 48px;
  border-top: 1px solid var(--nx-border);
  background: rgba(6, 12, 20, 0.6);
}

.nx-foot__inner { padding: 48px 40px 24px; }

.nx-foot__cols {
  display: grid;
  grid-template-columns: 1.4fr repeat(4, 1fr);
  gap: 32px;
}

.nx-foot__brandcopy {
  margin: 16px 0 0;
  color: var(--nx-muted);
  font-size: 14px;
  line-height: 1.6;
  max-width: 34ch;
}

.nx-foot__coltitle {
  font-family: var(--nx-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--nx-faint);
  margin-bottom: 16px;
}

.nx-foot__links {
  display: flex;
  flex-direction: column;
  gap: 11px;
}

.nx-foot__links a {
  font-size: 13.5px;
  color: var(--nx-muted);
  transition: color var(--nx-dur-fast) var(--nx-ease);
}

.nx-foot__links a:hover { color: var(--nx-text); }

.nx-foot__disclaimer {
  margin-top: 36px;
  padding: 16px 18px;
  border-radius: 10px;
  border: 1px solid var(--nx-border);
  background: rgba(100, 160, 215, 0.04);
  color: var(--nx-faint);
  font-size: 12.5px;
  line-height: 1.55;
}

.nx-foot__bottom {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--nx-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 12.5px;
  color: var(--nx-faint);
}

@media (max-width: 900px) {
  .nx-foot__inner { padding: 40px 20px 24px; }
  .nx-foot__cols { grid-template-columns: 1fr 1fr; gap: 28px; }
}

@media (max-width: 640px) {
  .nx-foot__cols { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   Segmented control + filter chips (reusable, aria-pressed driven)
   ========================================================================== */
.nx-seg {
  display: inline-flex;
  border: 1px solid var(--nx-border);
  border-radius: 9px;
  overflow: hidden;
}

.nx-seg__btn {
  padding: 8px 13px;
  font-family: var(--nx-body);
  font-size: 12.5px;
  font-weight: 600;
  background: transparent;
  color: var(--nx-muted);
  border: none;
  cursor: pointer;
  transition: background var(--nx-dur-fast) var(--nx-ease),
    color var(--nx-dur-fast) var(--nx-ease);
}

.nx-seg__btn + .nx-seg__btn { border-left: 1px solid var(--nx-border); }

.nx-seg__btn[aria-pressed="true"] {
  background: rgba(35, 137, 232, 0.16);
  color: var(--nx-accent);
}

.nx-seg--mono .nx-seg__btn {
  font-family: var(--nx-mono);
  font-size: 12px;
  padding: 8px 14px;
}

/* Filter chips row. */
.nx-chip {
  flex: 0 0 auto;
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 600;
  border-radius: 999px;
  border: 1px solid var(--nx-border);
  background: transparent;
  color: var(--nx-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: all var(--nx-dur-fast) var(--nx-ease);
}

.nx-chip[aria-pressed="true"] {
  border-color: var(--nx-border-strong);
  background: rgba(35, 137, 232, 0.16);
  color: var(--nx-accent);
}

/* Skeleton bar widths. Utility classes rather than inline styles, so the
   placeholder shapes stay in the stylesheet with everything else. */
.nx-w5 { width: 5%; }
.nx-w10 { width: 10%; }
.nx-w15 { width: 15%; }
.nx-w20 { width: 20%; }
.nx-w25 { width: 25%; }
.nx-w30 { width: 30%; }
.nx-w35 { width: 35%; }
.nx-w40 { width: 40%; }
.nx-w45 { width: 45%; }
.nx-w50 { width: 50%; }
.nx-w55 { width: 55%; }
.nx-w60 { width: 60%; }
.nx-w65 { width: 65%; }
.nx-w70 { width: 70%; }
.nx-w75 { width: 75%; }
.nx-w80 { width: 80%; }
.nx-w85 { width: 85%; }
.nx-w90 { width: 90%; }
.nx-w95 { width: 95%; }
.nx-w100 { width: 100%; }
