  /* Canonical DOMTEC brand palette — single source of truth (2026-07-28).
     Root cause of the "teal in one browser, green in another" report: this
     :root block was duplicated across all 29 page CSS files instead of
     living here once, and 7 of them (404, bazen, blog-senzor-prisutnosti,
     klima, kontakt, legal, rasvjeta) had drifted to a duller, more
     green-shifted teal (#45c7ac) while the other 22 used the intended
     brighter cyan-teal (#4ee2c4) — a real content/data inconsistency
     between pages, not a browser rendering difference. No oklch()/lab()/
     lch()/color-mix()/display-p3 found anywhere in the codebase, so exotic
     color-space handling was ruled out as a cause. Values below are the
     majority (verified-consistent-elsewhere) ones; the 7 outlier files'
     duplicate :root blocks are removed as part of this same fix so there is
     exactly one definition left, here, and no cascade order can re-diverge
     it again. Plain sRGB hex everywhere — renders identically across
     Chromium/WebKit/Firefox. */
  :root{
    color-scheme:dark;
    forced-color-adjust:none;
    --navy-950:#060f1c;
    --navy-900:#0a1826;
    --navy-800:#0e2338;
    --navy-700:#163a5f;
    --navy-600:#1b4b78;
    --teal-500:#2fbfa0;
    --teal-400:#4ee2c4;
    --teal-300:#8ff2dc;
    --ice-100:#eaf2f8;
    --slate-400:#7c93a8;
    --slate-500:#5a7288;
    --line:rgba(234,242,248,0.09);
    --glow-teal: 0 0 40px rgba(79,226,196,.25);
    --amber:#ffb347;
    --alert:#ff6b4a;
    --heat-cold:#4ee2c4;
    --heat-warm:#ffb347;
    --heat-hot:#ff6b4a;
    --font-display:'Fraunces', serif;
    --font-body:'Inter', sans-serif;
    --font-mono:'JetBrains Mono', monospace;
  }
  *{margin:0;padding:0;box-sizing:border-box;}
  /* -webkit-tap-highlight-color was never set anywhere in the codebase, so
     every link/button/menu item shows the browser's DEFAULT translucent
     grey tap-flash on touch (reported as "a background appears on
     selection" on menus/nav items on mobile). The site already has
     purpose-built :hover/:focus-visible/.mega-open states everywhere, so
     removing the generic native flash doesn't remove feedback — it removes
     a second, uncustomized, browser-default one layered on top of it. */
  *{-webkit-tap-highlight-color:transparent;}
  /* header is position:fixed (~90-122px tall, measured across pages) with no
     scroll-margin handling anywhere, so any #anchor jump (incl. every
     "Zatraži ponudu" -> /kontakt/#ponuda CTA site-wide) or scrollIntoView()
     call lands the target flush with the viewport top — directly BEHIND the
     fixed header. The form's first field(s) end up visually covered and,
     since the header sits in a higher stacking context, taps meant for the
     field hit the header instead: this is the reported "can't enter data on
     kontakt" bug. scroll-padding-top on the scrolling root fixes every
     anchor/scrollIntoView target at once, site-wide, in one place. */
  html{scroll-behavior:smooth; scroll-padding-top:140px;}
  body{background:var(--navy-950); color:var(--ice-100); font-family:var(--font-body); line-height:1.6; overflow-x:hidden; -webkit-font-smoothing:antialiased;}
  img{max-width:100%;display:block;}
  a{color:inherit;text-decoration:none;}
  .context-link{color:var(--teal-300); text-decoration:underline; text-underline-offset:.18em; text-decoration-thickness:1px;}
  .context-link:hover{color:var(--teal-400);}
  ul{list-style:none;}
  button{font-family:inherit;}
  @media (prefers-reduced-motion: reduce){
    *{animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; scroll-behavior:auto !important;}
    /* The near-zero-duration rule above is a safety net for any animation not
       listed below, but a near-zero duration still technically "runs" the
       animation once — not a genuine removal. For every animation confirmed
       to be purely decorative and infinite-looping (no animation-fill-mode
       dependency — the element is fully visible/usable with or without it
       running: gridDrift, heading-shimmer, aurora blobs, the pulse/live dots,
       the hero ring, the drag-hint icon wiggle, the logo marquee, the KNX
       lighting-scene glow), genuinely remove it instead. Deliberately NOT
       included here: .drag-hint, .knx-log-list li, .lock-log-list li — these
       start from opacity:0 with animation-fill-mode:forwards, so animation:none
       would strand them permanently invisible; they rely on the near-zero
       safety net above to reach their final visible state instead. */
    body::after, h1 .accent, .aurora-1, .aurora-2, .aurora-3, .aurora-blob,
    .eyebrow::before, .live-dot, .hero-art .ring, .drag-hint svg,
    .marquee-track, .lighting-glow-strip{
      animation:none !important;
    }
  }

  /* Mobile/touch flicker fix (HITNO, 2026-07-28): body::after (gridDrift) and
     h1 .accent (heading-shimmer) animate background-position, which forces
     continuous full/partial repaint every frame instead of cheap GPU
     compositing — the confirmed source of a production flicker bug on real
     mobile hardware. Desktop-only pointer-follow effects (custom cursor,
     spotlight glow, card tilt) have no purpose on touch devices either; this
     is a CSS-level backstop alongside the existing JS matchMedia guards in
     common.js/page JS. !important is required: common.css loads before
     page-specific CSS in base.njk, which would otherwise win the cascade.
     Condition covers BOTH coarse-pointer devices (phones/most tablets) AND
     narrow viewports regardless of pointer type (desktop DevTools responsive
     mode, split-screen/foldables, a touchscreen laptop in wide mode reporting
     hover:hover) — a fine-pointer device at <=1024px width still gets the
     safe-mode treatment, per explicit request. */
  @media (hover: none) and (pointer: coarse), (max-width: 1024px){
    body::after, h1 .accent{ animation:none !important; }
    /* Real-device follow-up (2026-07-29): h1/h2's OWN shimmer (a SEPARATE
       heading-shimmer animation from h1 .accent's, sweeping a wider
       ice-100/teal-500/ice-100 gradient across the ENTIRE heading, not just
       the accent span — missed in the pass above) was still running on
       mobile. User-reported "headings are green not turquoise" and
       "headings too dark, only readable when the color effect passes over
       them" on Samsung Internet is exactly what a frozen/inconsistently
       time gradient-clip-text sweep looks like: text sits in a low-contrast
       part of the gradient most of the time, briefly passing through the
       readable peak. background-clip:text is also independently confirmed
       fragile across WebKit-derived mobile browsers (this codebase never
       sets -webkit-text-fill-color, which some engines need alongside
       color:transparent for the fallback to behave). Rather than only
       stopping the animation (freezing at an arbitrary, possibly
       low-contrast, gradient position), remove the gradient-text technique
       entirely on mobile and use the plain solid colors it was sweeping
       between — .accent's gradient stops (teal-400/teal-300) are both
       "tirkizna", so plain teal-400 matches what was intended; h1/h2's own
       gradient rests on ice-100 most of the time, so plain ice-100 matches
       its dominant appearance. Guarantees identical, correct, readable
       color on every mobile browser instead of depending on gradient-clip
       cross-browser support. */
    h1, h2{
      background:none !important; -webkit-background-clip:initial !important; background-clip:initial !important;
      color:var(--ice-100) !important; animation:none !important;
    }
    h1 .accent{
      background:none !important; -webkit-background-clip:initial !important; background-clip:initial !important;
      color:var(--teal-400) !important;
    }
    /* pametno-grijanje.css alone has a second, wider "EXPERIMENTAL: shimmer
       applied to all white (ice-100) text, for preview" rule (its own
       animation, named "shimmer" not "heading-shimmer" — a different
       keyframe the two fixes above never touched) covering h3, h4, and
       several unrelated text elements (FAQ questions, package list items,
       the interactive temperature-dial readout, room names) — exactly the
       "small headings" and "energy consumption chart" the user still saw
       affected after the h1/h2 fix above. Same treatment: remove the
       gradient-clip-text, keep the plain resting color. */
    h3, h4, .pkg li, .faq-q, .dial-temp, .room-item .rname{
      background:none !important; -webkit-background-clip:initial !important; background-clip:initial !important;
      color:var(--ice-100) !important; animation:none !important;
    }
    #customCursor{ display:none !important; }
    .card, .cat-card, .room-map-card, .benefit, .extra-card, .knx-compare-card{ transform:none !important; }
    /* backdrop-filter:blur() is used on ~30+ component classes site-wide
       (.badge, .card, .benefit, .mega-menu, .nav-links, .modal-overlay, every
       product-page "panel"/"tag" card, etc.) — this pass had only disabled it
       on <header>. Real-device video evidence (2026-07-29, Samsung Galaxy
       Note 24 Ultra, staging) showed washed-out/near-invisible "frosted
       glass" boxes and flicker that the desktop-GPU Playwright/Chromium and
       WebKit testing in this audit could not reproduce — real mobile GPUs
       handle backdrop-filter far worse than desktop, and it forces a
       re-blur of everything behind the element on every scroll frame across
       every one of these elements simultaneously. Disabling it universally
       (not per-class) so no component using this pattern is missed again;
       every one of these elements already has its own background-color, so
       removing the blur just flattens the "frosted glass" look, no layout
       or content is affected. */
    *{ backdrop-filter:none !important; -webkit-backdrop-filter:none !important; }
    /* .aurora-blob combines filter:blur(90px) with an animated transform:scale()
       (auroraDrift keyframes) — unlike a plain translate, animating scale on a
       blurred element forces the browser to re-rasterize the blur every frame
       instead of just compositing, continuously, forever, on every page. This
       was missed in the first mobile-flicker pass; disabling it here too. */
    .aurora-blob{ animation:none !important; }
    /* .grain is a position:fixed, inset:-100px full-viewport SVG-noise layer
       with mix-blend-mode:overlay on every page — static (not animated), but
       a full-viewport blend layer still forces the compositor to re-blend it
       against everything underneath on every repaint (e.g. during scroll or
       any other animation on the page), which is needless cost on mobile for
       a purely decorative texture. */
    .grain{ display:none !important; }
  }
  .wrap{max-width:1240px;margin:0 auto;padding:0 32px;}
  @media(max-width:640px){.wrap{padding:0 20px;}}
  .legal-updated{font-size:.85rem; color:var(--slate-500); margin-bottom:22px;}
  .modal-full-link{font-family:var(--font-mono); font-size:.85rem; color:var(--teal-400); align-self:center; margin-right:auto;}
  .modal-full-link:hover{text-decoration:underline;}

  /* Honeypot field for the quote forms — visually and from-tab-order hidden
     for real visitors, but not display:none (some bots skip display:none
     fields when filling forms, defeating the point). */
  .hp-field{position:absolute; left:-5000px; width:1px; height:1px; overflow:hidden;}

  .form-status{margin-top:14px; font-size:.88rem;}
  .form-status.is-error{color:var(--danger-400, #f87171);}
  .form-status.is-success{color:var(--teal-400);}
