html { scroll-behavior: smooth; }

/* ───── Lenis smooth-scroll baseline ───── */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }

body {
  font-family: 'DM Sans', sans-serif;
  background: #050505;
  color: #fafafa;
  cursor: none;
}

/* ───── Custom cursor ───── */
.cursor-dot, .cursor-ring {
  position: fixed; top: 0; left: 0;
  pointer-events: none;
  z-index: 100;
  transform: translate(-50%, -50%);
  will-change: transform;
}
.cursor-dot {
  width: 5px; height: 5px;
  background: #fff;
  border-radius: 50%;
  transition: width 0.25s, height 0.25s, background 0.25s, opacity 0.15s;
}
.cursor-ring {
  width: 32px; height: 32px;
  border: 1px solid rgba(255,255,255,0.35);
  border-radius: 50%;
  transition: width 0.35s cubic-bezier(0.16,1,0.3,1),
              height 0.35s cubic-bezier(0.16,1,0.3,1),
              border-color 0.3s, background 0.3s, opacity 0.15s;
}
/* Hide the custom cursor while the user is over the Turnstile iframe:
   parent-page mouse events stop inside the iframe, so the ring would
   otherwise freeze at the boundary. */
body.cursor-over-turnstile .cursor-dot,
body.cursor-over-turnstile .cursor-ring { opacity: 0; }
body.cursor-hover .cursor-ring {
  width: 60px; height: 60px;
  border-color: rgba(255,255,255,0.7);
  /* No fill: the ring is 60px on hover and sits directly over whatever
     you're hovering. A translucent white fill here was being composited
     on top of navbar/submenu text, reading as "the text gets blurry on
     hover". The brighter border is the only visual cue we need. */
}
body.cursor-hover .cursor-dot {
  width: 8px; height: 8px;
}
@media (max-width: 768px) {
  body { cursor: auto; }
  .cursor-dot, .cursor-ring { display: none; }
}
/* Restore the OS cursor for users who prefer reduced motion — the custom
   cursor hides `cursor: none`, which users with motor needs depend on. */
@media (prefers-reduced-motion: reduce) {
  body { cursor: auto; }
  .cursor-dot, .cursor-ring { display: none; }
}

/* ───── Grain ───── */
.grain {
  position: fixed; inset: 0;
  pointer-events: none; z-index: 60;
  opacity: 0.05;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

/* ───── Backgrounds ───── */
.dot-grid {
  background-image: radial-gradient(rgba(255,255,255,0.05) 1px, transparent 1px);
  background-size: 28px 28px;
}
.line-grid {
  background-image:
    linear-gradient(to right, rgba(255,255,255,0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,0.04) 1px, transparent 1px);
  background-size: 80px 80px;
}

/* ───── Animations ───── */
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} }
.animate-blink { animation: blink 1.05s step-end infinite; }

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(28px) }
  to { opacity: 1; transform: translateY(0) }
}
.animate-fade-up {
  opacity: 0;
  animation: fadeUp 1.1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes pulse-dot {
  0%,100% { transform: scale(1); opacity: 1 }
  50%     { transform: scale(2.4); opacity: 0 }
}
.pulse-dot::after {
  content:''; position:absolute; inset:0;
  border-radius:50%; background:inherit;
  animation: pulse-dot 2.2s ease-out infinite;
}

.marquee-scroll {
  overflow: hidden;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  contain: paint;
  touch-action: none;
  max-width: 100%;
}
.marquee-scroll.is-dragging { cursor: grabbing; }
.marquee-track,
.marquee-track-fast {
  will-change: transform;
  transform: translate3d(0, 0, 0);
}

@keyframes node-blink {
  0%,100% { opacity: 0.4 }
  50%     { opacity: 1 }
}
.node-blink { animation: node-blink 2.6s ease-in-out infinite; }

/* Toggled by the offscreen-animation IO below to halt always-on infinite
   animations while their host element is outside the viewport — saves
   compositor work on the dozens of node-blink/pulse-dot elements. */
.anim-paused,
.anim-paused::after,
.anim-paused::before { animation-play-state: paused !important; }

@keyframes line-trace {
  0%   { stroke-dashoffset: 200 }
  100% { stroke-dashoffset: 0 }
}
.line-trace { animation: line-trace 4s ease-in-out infinite; }

@keyframes float-y {
  0%,100% { transform: translateY(0) }
  50%     { transform: translateY(-8px) }
}
.float-y { animation: float-y 6s ease-in-out infinite; }

/* ───── FX boxes (three-pillar visual cards) ─────
   One organising motion per surface. Modulok: a slow antidiagonal
   wave of light traverses the grid. Waveform: paths flow softly,
   centre node breathes. Orbits: the rings revolve around the core,
   a single pulse expands outward. Everything tagged .fx-anim is
   paused offscreen by the IO below. */

/* Modulok: every cell shares the same animation, staggered by
   antidiagonal index (i + j) so peaks sweep NW→SE. */
@keyframes fx-cell-dim {
  0%, 72%, 100% { opacity: 0.08; }
  36%           { opacity: 0.42; }
}
@keyframes fx-cell-glow {
  0%, 72%, 100% {
    opacity: 0.55;
    transform: scale(1);
    box-shadow: 0 0 0 currentColor;
  }
  36% {
    opacity: 1;
    transform: scale(1.32);
    box-shadow: 0 0 14px currentColor, 0 0 4px currentColor;
  }
}
.fx-cell-dim,
.fx-cell-glow {
  animation-duration: 4.8s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-delay: calc((var(--i, 0) + var(--j, 0)) * 0.14s);
}
.fx-cell-dim  { animation-name: fx-cell-dim;  }
.fx-cell-glow { animation-name: fx-cell-glow; will-change: transform; }

/* Waveform: slow dash flow, like quiet signal travelling */
@keyframes fx-wave-flow {
  to { stroke-dashoffset: -160; }
}
.fx-wave-flow {
  stroke-dasharray: 4 10;
  animation: fx-wave-flow 7.5s linear infinite;
}

/* Centre node breathing (single soft pulse) */
@keyframes fx-core-breathe {
  0%, 100% { transform: scale(1);    filter: drop-shadow(0 0 4px rgba(255,255,255,0.35)); }
  50%      { transform: scale(1.45); filter: drop-shadow(0 0 12px rgba(236,72,153,0.65)); }
}
.fx-core-breathe {
  transform-box: fill-box;
  transform-origin: center;
  animation: fx-core-breathe 4.2s ease-in-out infinite;
}

/* Single expanding ring outward from the orbits' core */
@keyframes fx-ring-out {
  0%   { transform: scale(0.35); opacity: 0.7; }
  100% { transform: scale(2.5);  opacity: 0; }
}
.fx-ring-out {
  transform-box: fill-box;
  transform-origin: center;
  animation: fx-ring-out 5s ease-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .fx-cell-dim, .fx-cell-glow, .fx-wave-flow,
  .fx-core-breathe, .fx-ring-out,
  .animate-blink, .pulse-dot, .pulse-dot::after,
  .node-blink, .line-trace, .float-y, .animate-fade-up {
    animation: none !important;
  }
  .animate-fade-up { opacity: 1; transform: none; }
}

/* ───── Hero intro reveal (one-shot on first WebGL frame) ─────
   The canvas stays at opacity 0 until JS adds .hero-ready right after
   the first gl.drawArrays() — this prevents the visible "flash to a
   blurred form" you'd otherwise get during WebGL boot, because the
   animation only begins from a coherent painted state.

   Once ready, the field blooms outward from the wordmark center via a
   clip-path circle, while the rings keep animating beneath. Scale and
   color settle in lockstep so the whole reveal feels like one motion
   rather than several effects stacked. */
@keyframes hero-canvas-bloom {
  0% {
    opacity: 1;
    transform: scale(1.06);
    clip-path: circle(0% at 50% 62%);
    -webkit-clip-path: circle(0% at 50% 62%);
    filter: saturate(0.55) brightness(0.82);
  }
  100% {
    opacity: 1;
    transform: scale(1);
    clip-path: circle(150% at 50% 62%);
    -webkit-clip-path: circle(150% at 50% 62%);
    filter: saturate(1) brightness(1);
  }
}
@keyframes hero-vignette-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.hero-fx-canvas {
  opacity: 0;
  clip-path: circle(0% at 50% 62%);
  -webkit-clip-path: circle(0% at 50% 62%);
  will-change: transform, clip-path, filter;
}
.hero-fx-canvas.hero-ready {
  animation: hero-canvas-bloom 2.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.hero-fx-vignette {
  opacity: 0;
}
.hero-fx-canvas.hero-ready ~ .hero-fx-vignette {
  animation: hero-vignette-in 2s cubic-bezier(0.16, 1, 0.3, 1) 0.35s forwards;
}
@media (prefers-reduced-motion: reduce) {
  .hero-fx-canvas, .hero-fx-vignette {
    animation: none !important;
    opacity: 1; transform: none; filter: none;
    clip-path: none; -webkit-clip-path: none;
  }
}

/* ───── Page bloom (every page load) ─────
   Mirrors the hero's clip-path bloom literally: duration 2.5s, easing
   cubic-bezier(0.22, 1, 0.36, 1), radius 0 → 150% of viewport — same
   values as .hero-fx-canvas.hero-ready above. A fixed-position overlay
   carries the radial mask so the cursor (z-100) stays responsive while
   everything below z-80 (page content, nav, grain) is revealed in one
   continuous radial sweep from viewport center.

   --bloom-r needs @property so the browser interpolates it as a length,
   not a string — without this the mask wouldn't animate. */
@property --bloom-r {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}
.bloom-overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  pointer-events: none;
  background: #050505;
  --bloom-r: 0px;
  /* Hairline soft band (24px) takes the hard stencil edge off without
     softening the bloom so much that it stops reading as the hero's
     clip-path. */
  -webkit-mask-image: radial-gradient(circle at 50% 50%,
    transparent calc(var(--bloom-r) - 24px),
    black var(--bloom-r));
  mask-image: radial-gradient(circle at 50% 50%,
    transparent calc(var(--bloom-r) - 24px),
    black var(--bloom-r));
  will-change: --bloom-r;
}
.bloom-overlay.bloomed {
  /* Match .hero-fx-canvas.hero-ready exactly: 2.5s, same bezier, 150%. */
  --bloom-r: 150vmax;
  transition: --bloom-r 2.5s cubic-bezier(0.22, 1, 0.36, 1);
}

/* ───── Reveal on scroll (below-fold only) ─────
   The page bloom owns above-fold elements via [data-cascade] — they
   appear in their final state while the bloom reveals them. Below-fold
   elements still fade in on scroll via IntersectionObserver. */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  filter: blur(6px);
  transition: opacity 1s cubic-bezier(0.16,1,0.3,1),
              transform 1s cubic-bezier(0.16,1,0.3,1),
              filter   1s cubic-bezier(0.16,1,0.3,1);
}
.reveal:not(.in-view):not([data-cascade]) {
  will-change: opacity, transform, filter;
}
.reveal.in-view { opacity: 1; transform: none; filter: blur(0); }
/* Cascade-owned (above-fold): permanently in their final state. */
.reveal[data-cascade] {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition: none;
}
.reveal[data-d="1"] { transition-delay: .08s }
.reveal[data-d="2"] { transition-delay: .16s }
.reveal[data-d="3"] { transition-delay: .24s }
.reveal[data-d="4"] { transition-delay: .32s }
.reveal[data-d="5"] { transition-delay: .40s }
.reveal[data-d="6"] { transition-delay: .48s }
.reveal[data-d="7"] { transition-delay: .56s }
.reveal[data-d="8"] { transition-delay: .64s }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; filter: none; transition: none; }
  .bloom-overlay { display: none; }
}

/* ───── Blur-reveal (per-word) ───── */
[data-anim-blur] .br-word {
  display: inline-block;
  opacity: 0;
  filter: blur(12px);
  transform: translateY(10px);
  transition:
    opacity .9s cubic-bezier(0.16, 1, 0.3, 1),
    filter  .9s cubic-bezier(0.16, 1, 0.3, 1),
    transform .9s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 35ms);
  will-change: opacity, filter, transform;
}
[data-anim-blur].br-in .br-word {
  opacity: 1;
  filter: blur(0);
  transform: none;
}
/* Cascade-owned blur targets are revealed by the page bloom, so any words
   that were ever split short-circuit straight to their final state. */
[data-anim-blur][data-cascade] .br-word {
  opacity: 1;
  filter: none;
  transform: none;
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-anim-blur] .br-word {
    opacity: 1; filter: none; transform: none; transition: none;
  }
}

/* ───── Type styles ───── */
.display-mega {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 400;
  letter-spacing: -0.045em;
  line-height: 0.9;
  font-variation-settings: "opsz" 96, "wght" 420;
}
.display-h {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 400;
  letter-spacing: -0.035em;
  line-height: 0.96;
  font-variation-settings: "opsz" 72, "wght" 440;
}
.italic-serif {
  font-family: 'Instrument Serif', serif;
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.015em;
}

/* ───── Hero position lock: only the vertical rhythm is viewport-
   relative, so content sits at the same proportional spot on every
   desktop viewport / zoom level. Typography, icons, paddings stay
   rem-based so they scale visually with browser zoom (like the navbar).
   The WebGL rings re-anchor automatically to the wordmark's CSS height
   via updateAnchor() (RING_RADIUS_VS_WM in JS). ───── */
@media (min-width: 1280px) {
  #hero h2 { margin-top: -0.3125vw; }                                       /* -8px  @ 2560 */
  #hero > .relative.z-10 > p { margin-top: 0.9375vw; }                       /* 24px  @ 2560 */
  #hero > .relative.z-10 > .flex.flex-col.sm\:flex-row { margin-top: 1.5625vw; } /* 40px @ 2560 */
  #hero > .relative.z-10 { padding-bottom: 9.375vw; }                        /* 240px @ 2560 */
}

/* ───── 4K-TV trim: at the same browser zoom, a 4K panel with
   compounded Windows DPI scaling shows the same CSS larger than a
   1440p panel does (DPR ≈ 2.25 vs 1.5 at viewport 1707). Shrink the
   hero content by ~12% on very high-DPR desktops so the visual feel
   matches 1440p @ 150% instead of 1440p @ 170%. The WebGL rings
   auto-follow because updateAnchor() reads the scaled wordmark
   dimensions from getBoundingClientRect. ───── */
@media (min-resolution: 2dppx) and (min-width: 1280px) {
  #hero > .relative.z-10 {
    transform: scale(0.88);
    transform-origin: center bottom;
  }
}

/* ───── 1080p-TV trim: a 1080p TV running the browser at the OS's
   default UI scaling lands at ~1270 CSS px wide with DPR ≈ 1. From a
   10-foot viewing distance the lg: hero typography reads as far too
   zoomed in, so on low-DPR viewports in that range pull the hero
   down by ~25% to match the visual weight a desktop monitor delivers
   at the same width. ───── */
@media (max-resolution: 1.5dppx) and (min-width: 1200px) and (max-width: 1500px) {
  #hero > .relative.z-10 {
    transform: scale(0.75);
    transform-origin: center bottom;
  }
}

.label-mono {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgba(255,255,255,0.45);
}

/* ───── Misc ───── */
::selection { background: rgba(168,85,247,0.55); color: #fff; }
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #050505; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.18); }

.module-card {
  transition: transform .55s cubic-bezier(0.16,1,0.3,1),
              background .4s, border-color .4s;
}
.module-card:hover {
  transform: translateY(-4px);
  background: rgba(255,255,255,0.025);
  border-color: rgba(255,255,255,0.16);
}

.link-underline { position: relative; display: inline-block; }
.link-underline::after {
  content:''; position:absolute;
  left:0; bottom:-2px; width:100%; height:1px;
  background: currentColor;
  transform: scaleX(0); transform-origin: right;
  transition: transform .55s cubic-bezier(0.16,1,0.3,1);
}
.link-underline:hover::after {
  transform: scaleX(1); transform-origin: left;
}

/* radial mask for sections */
.mask-radial {
  mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
}

/* nav islands — base + scrolled shadow.
   Asymmetric timing: faster + bouncy when growing (Dynamic-Island pop),
   slower + smooth when shrinking (settle). Transition rules are read from
   the *target* state, so base = grow timing, .scrolled = shrink timing. */
.nav-island {
  /* Grow direction: 480ms with a fluid overshoot. */
  transition:
    transform 0.48s cubic-bezier(0.34, 1.45, 0.64, 1),
    box-shadow 0.5s ease,
    background-color 0.3s ease;
  will-change: transform;
}
#nav.scrolled .nav-island {
  /* Shrink direction: 720ms with Apple's standard fluid ease (no overshoot). */
  transition:
    transform 0.72s cubic-bezier(0.32, 0.72, 0, 1),
    box-shadow 0.5s ease,
    background-color 0.3s ease;
  box-shadow: 0 8px 32px rgba(0,0,0,0.35);
}

/* Dynamic-Island-style expanded state at top of page.
   Subtle ~8% scale-up with a springy bezier + staggered delays
   so the three pills settle sequentially instead of in lockstep. */
#nav:not(.scrolled) a.nav-island,
#nav:not(.scrolled) button.nav-island {
  transform: scale(1.08);
}
/* Center pill already carries translate(-50%, -50%) — combine. */
nav.nav-island {
  transform: translate(-50%, -50%) scale(1);
}
#nav:not(.scrolled) nav.nav-island {
  transform: translate(-50%, -50%) scale(1.08);
}

/* Stagger: logo first, center next, CTA last — gives a lively, ordered morph. */
a.nav-island      { transition-delay: 0ms; }
nav.nav-island    { transition-delay: 60ms; }
button.nav-island { transition-delay: 120ms; }

/* ───── Nav submenu — iOS/macOS "liquid glass" ─────
   Lives under the Szolgáltatások pill. Hover the trigger (or the panel)
   to reveal a translucent, blurred card with the three service routes
   plus a footer link to the overview page. The visual is built from
   four stacked refractive layers (blur, saturated tint, top specular
   highlight, soft drop shadow) so it reads like a real glass slab
   rather than a flat dropdown. */
.nav-has-submenu {
  display: inline-flex;
  align-items: center;
  /* position:relative so the ::after hover-extension below can absolutely
     position itself against this wrapper. The submenu itself no longer
     needs to "fall through" to a positioned ancestor — it's a sibling of
     <nav>, position:fixed, with width/left/top written by JS. */
  position: relative;
}

/* Invisible hover-extension that fills the dead strip between the
   trigger anchor's bottom and the submenu's bridge (the ~6px of pill
   bottom-padding + border). Without this, the cursor crosses non-
   hoverable space → open rule unmatches → pointer-events snaps to none
   → submenu can't catch the cursor when it arrives at the bridge. With
   it, hovering the extension keeps :has(.nav-has-submenu:hover) true,
   so the open state persists until cursor lands inside .nav-submenu. */
.nav-has-submenu::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 18px;
}

/* Promote the trigger anchor to its own compositor layer so its
   hover:bg-white/5 + hover:text-white transitions don't repaint the
   navbar's backing surface. */
.nav-has-submenu > a {
  transform: translateZ(0);
}

.nav-submenu {
  /* Position: fixed (header is also fixed). The .nav-submenu lives
     outside nav.nav-island in the DOM — if it were inside, the pill's
     backdrop-filter / will-change / transform would establish a
     backdrop root, and the submenu's own backdrop-filter would sample
     from the empty area inside the pill instead of from the page
     content behind. With it outside, the submenu's blur samples the
     page directly and matches the navbar's "smoked glass".
     width / left / top are written by initSubmenuSync() in scripts.js
     so the panel stays exactly under the pill and matches its width
     even as the pill scales between scrolled / unscrolled states.
     padding-top is the transparent bridge for cursor traversal —
     paired with .nav-has-submenu::after which covers the dead strip
     above the bridge (inside the pill's bottom padding). Together they
     form an unbroken hover region from trigger to panel. */
  position: fixed;
  padding-top: 10px;
  transform: translateY(-6px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 0.32s cubic-bezier(0.32, 0.72, 0, 1),
    transform 0.42s cubic-bezier(0.32, 0.72, 0, 1),
    visibility 0s linear 0.32s;
  z-index: 50;
}

/* Open state: trigger hover (detected through #nav via :has) or cursor
   on the submenu's own area (panel + bridge). Both branches needed so
   the panel stays open as the cursor traverses from trigger to panel. */
#nav:has(.nav-has-submenu:hover) .nav-submenu,
#nav:has(.nav-has-submenu:focus-within) .nav-submenu,
.nav-submenu:hover,
.nav-submenu:focus-within {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
  /* Opacity snaps to 1 immediately on open so the glass renders at full
     smoked-blur strength from frame 1. The close transition (in the base
     .nav-submenu rule above) still fades opacity 0→1, so dismissals stay
     smooth. Only transform animates on entry. */
  transition:
    opacity 0s,
    transform 0.42s cubic-bezier(0.32, 0.72, 0, 1),
    visibility 0s linear 0s;
}

/* Tiny chevron next to the Szolgáltatások trigger — signals that the
   pill expands. Subtle at rest, brightens and flips up when the submenu
   is open so the user gets a quiet "this opens something" cue. */
.nav-submenu-chevron {
  /* Reserve box BEFORE iconify-icon hydrates — otherwise the chevron
     pops in at width 11px after the SVG fetch resolves, widening the
     Szolgáltatások pill and shifting Termékeink/Rólunk/Kapcsolat to
     the right on every page load. */
  display: inline-block;
  width: 11px;
  height: 11px;
  flex: none;
  opacity: 0.5;
  transition:
    transform 0.32s cubic-bezier(0.32, 0.72, 0, 1),
    opacity 0.22s ease;
  /* Same reason as .nav-has-submenu > a — keep the rotate/opacity
     transition off the navbar's backing surface. */
  will-change: transform, opacity;
}
.nav-has-submenu:hover .nav-submenu-chevron,
.nav-has-submenu:focus-within .nav-submenu-chevron {
  opacity: 1;
  transform: rotate(180deg);
}

/* Same material as the center navbar pill (bg-white/[0.04] +
   border-white/10 + backdrop-blur-md). Width is inherited from the
   parent .nav-submenu wrapper, which stretches to nav.nav-island's
   width — so the glass is exactly as wide as the navbar pill above it. */
.nav-submenu-glass {
  padding: 8px;
  border-radius: 24px;
  background: rgba(255, 255, 255, 0.04);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.10);
}

.nav-submenu-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 10px;
  border-radius: 14px;
  color: rgba(255, 255, 255, 0.92);
  transition:
    background-color 0.22s ease,
    transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
}

.nav-submenu-item:hover {
  background: rgba(255, 255, 255, 0.07);
}

.nav-submenu-icon {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
}

.nav-submenu-icon--blue   { color: #93c5fd; }
.nav-submenu-icon--pink   { color: #f9a8d4; }
.nav-submenu-icon--purple { color: #c4b5fd; }

.nav-submenu-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.nav-submenu-title {
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: #fff;
  line-height: 1.2;
}

.nav-submenu-desc {
  font-size: 11.5px;
  color: rgba(255, 255, 255, 0.5);
  line-height: 1.35;
  margin-top: 2px;
}

.nav-submenu-divider {
  height: 1px;
  margin: 6px 8px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255, 255, 255, 0.10) 20%,
    rgba(255, 255, 255, 0.10) 80%,
    transparent 100%
  );
}

.nav-submenu-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 12px;
  font-size: 12.5px;
  color: rgba(255, 255, 255, 0.7);
  font-family: 'JetBrains Mono', monospace;
  letter-spacing: 0.02em;
  transition: background-color 0.22s ease, color 0.22s ease;
}

.nav-submenu-foot:hover {
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
}

.nav-submenu-foot-arrow {
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
}

.nav-submenu-foot:hover .nav-submenu-foot-arrow {
  transform: translateX(2px);
}

/* Teal variant used by the kapcsolat custom-select "konzultáció" option. */
.nav-submenu-icon--teal { color: #5eead4; }

/* ───── Custom <select> (kapcsolat form) ─────
   Reuses .nav-submenu-* item visuals (icon square, title, desc) so the
   dropdown reads as a sibling of the navbar submenu. The native <select>
   is replaced; value is mirrored into a hidden input named projectType
   so the existing mailto form submits unchanged. */
.custom-select {
  position: relative;
}

.custom-select-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  /* Padding + min-height tuned so the trigger stays the same ~48px tall as
     the sibling inputs whether or not the 32×32 icon square is shown. */
  padding: 0.5rem 1rem;
  min-height: 48px;
  border-radius: 0.75rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.10);
  color: #fff;
  text-align: left;
  transition:
    border-color 0.2s ease,
    background 0.2s ease,
    box-shadow 0.2s ease;
}
.custom-select-trigger:focus,
.custom-select-trigger:focus-visible {
  outline: none;
  border-color: rgba(96, 165, 250, 0.5);
  box-shadow: 0 0 0 1px rgba(96, 165, 250, 0.3);
}
.custom-select.is-open .custom-select-trigger {
  border-color: rgba(96, 165, 250, 0.5);
  box-shadow: 0 0 0 1px rgba(96, 165, 250, 0.3);
}
.custom-select.is-error .custom-select-trigger {
  border-color: rgba(248, 113, 113, 0.55);
  box-shadow: 0 0 0 1px rgba(248, 113, 113, 0.3);
}

.custom-select-trigger-value {
  flex: 1;
  min-width: 0;
  font-size: 0.95rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.custom-select-trigger-value[data-placeholder="true"] {
  color: #52525b;
}

/* Accent square in the trigger after selection — matches the dropdown
   item's .nav-submenu-icon (32×32, 18px glyph) so the closed trigger
   reads as the same component as an open dropdown row. Hidden until an
   accent is assigned. */
.custom-select-trigger-icon {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  display: none;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
}
.custom-select-trigger-icon iconify-icon { width: 18px; height: 18px; }
.custom-select-trigger-icon[data-accent] { display: inline-flex; }
.custom-select-trigger-icon[data-accent="blue"]   { color: #93c5fd; }
.custom-select-trigger-icon[data-accent="pink"]   { color: #f9a8d4; }
.custom-select-trigger-icon[data-accent="purple"] { color: #c4b5fd; }
.custom-select-trigger-icon[data-accent="teal"]   { color: #5eead4; }

.custom-select-chevron {
  color: rgba(255, 255, 255, 0.55);
  flex-shrink: 0;
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
}
.custom-select.is-open .custom-select-chevron {
  transform: rotate(180deg);
}

.custom-select-panel {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  z-index: 30;
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  transform-origin: top center;
  pointer-events: none;
  transition:
    opacity 0.18s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}
.custom-select.is-open .custom-select-panel {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/* Panel surface. Slightly more opaque than the navbar variant because it
   sits over the form card's dot-grid, not the page background. */
.custom-select-panel-inner {
  padding: 8px;
  border-radius: 20px;
  background: rgba(17, 17, 17, 0.92);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(255, 255, 255, 0.10);
  box-shadow:
    0 18px 40px -12px rgba(0, 0, 0, 0.6),
    0 1px 0 rgba(255, 255, 255, 0.04) inset;
}

.custom-select-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 10px;
  border-radius: 14px;
  color: rgba(255, 255, 255, 0.92);
  text-align: left;
  background: transparent;
  border: 0;
  transition:
    background-color 0.18s ease,
    transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
}
.custom-select-item:hover,
.custom-select-item.is-active {
  background: rgba(255, 255, 255, 0.06);
}
.custom-select-item[aria-selected="true"] {
  background: rgba(255, 255, 255, 0.04);
}
.custom-select-item:focus-visible {
  outline: 1px solid rgba(96, 165, 250, 0.5);
  outline-offset: -1px;
}

.custom-select-item-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.custom-select-item-title {
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: #fff;
  line-height: 1.2;
}
.custom-select-item-desc {
  font-size: 11.5px;
  color: rgba(255, 255, 255, 0.5);
  line-height: 1.35;
  margin-top: 2px;
}

/* ───── Subpage hero (no WebGL) ─────
   Lighter hero used on /szolgaltatasok/, /termekeink/, /rolunk/, etc.
   Uses the same dot-grid + radial vignette + label-mono kicker as the
   main hero, but without the WebGL canvas or giant wordmark, so subpages
   feel cohesive without burning GPU on every page. */
.subhero {
  position: relative;
  background: #050505;
  overflow: hidden;
}
.subhero-glow {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 60% 50% at 50% 30%, rgba(59,130,246,0.10), transparent 70%);
}
/* ───── FAQ accordion (kapcsolat page) ─────
   Open/close is driven by JS (initFAQ) via inline-style transitions.
   All transitions on this surface share one out-quart curve at 280ms
   (chevron, border, panel open, panel close) so swapping items reads
   as a single coordinated motion instead of the close lingering past
   the open. */
.faq-item {
  transition:
    border-color .28s cubic-bezier(0.22,1,0.36,1),
    background-color .28s cubic-bezier(0.22,1,0.36,1);
}
.faq-item:hover {
  border-color: rgba(255,255,255,0.16);
  background-color: rgba(255,255,255,0.018);
}
.faq-item[open] {
  border-color: rgba(147,197,253,0.22);
  background-color: rgba(255,255,255,0.025);
}
.faq-item summary {
  list-style: none;
  cursor: pointer;
  transition: color .22s cubic-bezier(0.22,1,0.36,1);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item[open] .faq-chevron {
  transform: rotate(180deg);
  color: rgba(147,197,253,0.85);
}
.faq-chevron {
  transition:
    transform .28s cubic-bezier(0.22,1,0.36,1),
    color .22s cubic-bezier(0.22,1,0.36,1);
}
@media (prefers-reduced-motion: reduce) {
  .faq-item, .faq-chevron, .faq-item summary { transition: none; }
}

/* ───── Subpage section nav (sticky in-page links) ───── */
.subnav-link {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgba(255,255,255,0.45);
  padding: 0.5rem 0.85rem;
  border-radius: 999px;
  transition: color .3s, background .3s;
  white-space: nowrap;
}
.subnav-link:hover { color: #fff; background: rgba(255,255,255,0.04); }

/* ───── Mobile menu (hamburger + glass sheet) ─────
   Only renders below md (768px). Desktop nav is completely untouched.
   Material matches .nav-submenu so the sheet reads like the desktop
   submenu, just larger and full-width. Elevated Ajánlat at the bottom
   sits on a brand-colored gradient halo (blue → purple → pink — the
   same hues as the Nexora logo). */

.nav-mobile-toggle {
  width: 42px;
  height: 42px;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 0;
}
@media (max-width: 768px) {
  .nav-mobile-toggle { display: inline-flex; }
}
.nav-burger {
  position: relative;
  width: 18px;
  height: 14px;
  display: block;
}
.nav-burger-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 1.5px;
  background: #fff;
  border-radius: 1px;
  transition:
    transform 0.42s cubic-bezier(0.34, 1.45, 0.64, 1),
    opacity 0.22s ease,
    top 0.34s cubic-bezier(0.32, 0.72, 0, 1);
}
.nav-burger-line:nth-child(1) { top: 2px; }
.nav-burger-line:nth-child(2) { top: 6.5px; }
.nav-burger-line:nth-child(3) { top: 11px; }
[aria-expanded="true"] .nav-burger-line:nth-child(1) { top: 6.5px; transform: rotate(45deg); }
[aria-expanded="true"] .nav-burger-line:nth-child(2) { opacity: 0; transform: scaleX(0.4); }
[aria-expanded="true"] .nav-burger-line:nth-child(3) { top: 6.5px; transform: rotate(-45deg); }

.nav-mobile-sheet {
  position: fixed;
  top: 70px;
  left: 12px;
  right: 12px;
  z-index: 35;
  display: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(-12px) scale(0.97);
  transform-origin: top right;
  /* Close transition: opacity + transform durations are aligned so the
     sheet doesn't go invisible mid-flight. Visibility delay covers the
     full row cascade (up to ~0.22s stagger + 0.46s row transform). */
  transition:
    opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1),
    transform 0.56s cubic-bezier(0.32, 0.72, 0, 1),
    visibility 0s linear 0.68s;
}
.nav-mobile-sheet.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0) scale(1);
  /* Opacity snaps to 1 immediately on open so the glass renders at full
     smoked-blur strength from frame 1 (same pattern as .nav-submenu).
     Close transition (in the base .nav-mobile-sheet rule above) still
     fades opacity 1→0, so dismissals remain smooth. Only transform
     animates on entry. */
  transition:
    opacity 0s,
    transform 0.42s cubic-bezier(0.32, 0.72, 0, 1),
    visibility 0s linear 0s;
}
@media (max-width: 768px) {
  .nav-mobile-sheet { display: block; }
}

.nav-mobile-glass {
  padding: 10px;
  border-radius: 26px;
  background: rgba(10, 10, 10, 0.72);
  backdrop-filter: blur(18px) saturate(1.2);
  -webkit-backdrop-filter: blur(18px) saturate(1.2);
  border: 1px solid rgba(255, 255, 255, 0.10);
  box-shadow:
    0 18px 48px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-mobile-section-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: rgba(255, 255, 255, 0.42);
  padding: 8px 12px 4px;
}

.nav-mobile-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 13px 14px;
  border-radius: 14px;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: rgba(255, 255, 255, 0.92);
  transition: background-color 0.22s ease, color 0.22s ease;
}
.nav-mobile-link:active { background: rgba(255, 255, 255, 0.07); }

/* Row cascade: each direct child of .nav-mobile-glass fades + lifts
   between opacity 0/translateY(-6px) (hidden) and opacity 1/none (open).
   Base rule timing matches the sheet's close transition so rows fade
   out at the same rate as the container — without that, the rows snap
   away in 0.32s while the card lingers for 0.44s and the close reads
   as "content vanished, empty card slid up". Reverse stagger via
   :nth-last-child runs on close (bottom row leaves first); the open
   state overrides with forward :nth-child delays below. */
.nav-mobile-glass > * {
  opacity: 0;
  transform: translateY(-6px);
  transition:
    opacity 0.42s cubic-bezier(0.32, 0.72, 0, 1),
    transform 0.46s cubic-bezier(0.32, 0.72, 0, 1);
}
.nav-mobile-glass > *:nth-last-child(1)  { transition-delay: 0s; }
.nav-mobile-glass > *:nth-last-child(2)  { transition-delay: 0.02s; }
.nav-mobile-glass > *:nth-last-child(3)  { transition-delay: 0.04s; }
.nav-mobile-glass > *:nth-last-child(4)  { transition-delay: 0.06s; }
.nav-mobile-glass > *:nth-last-child(5)  { transition-delay: 0.08s; }
.nav-mobile-glass > *:nth-last-child(6)  { transition-delay: 0.10s; }
.nav-mobile-glass > *:nth-last-child(7)  { transition-delay: 0.12s; }
.nav-mobile-glass > *:nth-last-child(8)  { transition-delay: 0.14s; }
.nav-mobile-glass > *:nth-last-child(9)  { transition-delay: 0.16s; }
.nav-mobile-glass > *:nth-last-child(10) { transition-delay: 0.18s; }
.nav-mobile-glass > *:nth-last-child(11) { transition-delay: 0.20s; }
.nav-mobile-glass > *:nth-last-child(12) { transition-delay: 0.22s; }

.nav-mobile-sheet.is-open .nav-mobile-glass > * {
  opacity: 1;
  transform: none;
}
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(1)  { transition-delay: 0.04s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(2)  { transition-delay: 0.08s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(3)  { transition-delay: 0.12s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(4)  { transition-delay: 0.16s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(5)  { transition-delay: 0.20s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(6)  { transition-delay: 0.24s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(7)  { transition-delay: 0.28s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(8)  { transition-delay: 0.32s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(9)  { transition-delay: 0.36s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(10) { transition-delay: 0.40s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(11) { transition-delay: 0.44s; }
.nav-mobile-sheet.is-open .nav-mobile-glass > *:nth-child(12) { transition-delay: 0.48s; }

/* Elevated CTA — full-width white pill on a soft brand-color halo.
   The halo is a blurred conic gradient using the same hues as the
   Nexora logo (blue #60a5fa, purple #c084fc, pink #ec4899). isolate
   keeps the halo behind .nav-mobile-cta-inner without bleeding out
   of the pill's stacking context. */
.nav-mobile-cta {
  position: relative;
  display: block;
  margin-top: 10px;
  isolation: isolate;
  border-radius: 999px;
  transition: transform 0.22s cubic-bezier(0.34, 1.45, 0.64, 1);
}
.nav-mobile-cta:active { transform: scale(0.97); }

.nav-mobile-cta-halo {
  position: absolute;
  inset: -8px -10px;
  border-radius: 999px;
  background: linear-gradient(90deg, #60a5fa 0%, #c084fc 100%);
  filter: blur(18px);
  opacity: 0.55;
  z-index: -1;
}

.nav-mobile-cta-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 15px 22px;
  background: #fff;
  color: #000;
  border-radius: 999px;
  font-size: 16px;
  font-weight: 500;
  letter-spacing: -0.01em;
  box-shadow:
    0 10px 30px rgba(0, 0, 0, 0.35),
    inset 0 -1px 0 rgba(0, 0, 0, 0.06);
}

