/* ==========================================================================
   G7 Technology — site.css
   Everything Tailwind utilities can't express: the blueprint field, the
   detection brackets, and the scroll-reveal timing.
   ========================================================================== */

:root {
  --navy-950: #01060f;
  --navy-900: #030b1c;
  --navy-800: #0a1730;   /* lifted from #04122e — 900 and 800 were too close to read as separate surfaces */
  --navy-700: #052160;
  --navy-600: #0a2e7a;
  --signal:   #d7181f;
  --cyan:     #22d3ee;   /* AI surfaces only — see header.php colour note */
  --ice:      #e8edf7;
  --steel:    #6e7c9a;

  --radius:   10px;
}

/* Base type scale — bumped from the 16px default so body copy reads larger.
   Tailwind sizing is rem-based, so this lifts text and spacing together. */
html { font-size: 112.5%; scroll-behavior: smooth; }

body {
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* --------------------------------------------------------------------------
   Header — transparent over the hero, solid once the page scrolls
   -------------------------------------------------------------------------- */
#siteHeader {
  background-color: transparent;
  border-bottom-color: transparent;
}
#siteHeader.is-scrolled {
  background-color: rgba(3, 11, 28, 0.94);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
  border-bottom-color: rgba(255, 255, 255, 0.1);
}

/* --------------------------------------------------------------------------
   Type
   -------------------------------------------------------------------------- */
.font-display { font-family: 'Space Grotesk', 'Inter', sans-serif; }
.font-mono    { font-family: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', monospace; }

.eyebrow {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--signal);
  margin-bottom: 0.9rem;
}
.eyebrow::before {
  content: '';
  display: inline-block;
  width: 1.6rem;
  height: 1px;
  background: currentColor;
  vertical-align: middle;
  margin-right: 0.7rem;
}

/* Sentence case. All-caps was on headings, nav, buttons, labels, tags and
   product names at once — stacked, that reads as equipment placards. Caps now
   earn their place on the eyebrows, mono labels and part numbers, which are
   the things that genuinely are placards. */
.h-section {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
  /* Stepped down from 3.4rem: Space Grotesk is a normal-width grotesque, so
     the old condensed-face sizes no longer fit the columns they were set for. */
  font-size: clamp(1.9rem, 4vw, 2.9rem);
}

/* --------------------------------------------------------------------------
   Blueprint field — the drafting grid the fabrication drawings sit on
   -------------------------------------------------------------------------- */
.blueprint {
  background-image:
    linear-gradient(rgba(110, 124, 154, 0.13) 1px, transparent 1px),
    linear-gradient(90deg, rgba(110, 124, 154, 0.13) 1px, transparent 1px);
  background-size: 44px 44px;
}
.blueprint-dark {
  background-image:
    linear-gradient(rgba(120, 160, 255, 0.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120, 160, 255, 0.07) 1px, transparent 1px);
  background-size: 44px 44px;
}
.blueprint-fade {
  -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 35%, transparent 100%);
          mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 35%, transparent 100%);
}

/* --------------------------------------------------------------------------
   Page fields — every page draws its own construction behind the content, so
   moving between sections of the site feels like moving between instruments
   rather than scrolling one long page.

   The pattern lives on a ::before instead of the .blueprint-dark div itself:
   a transform on a child composites on the GPU, where animating
   background-position on a full-viewport element would repaint every frame.
   Each page only overrides the custom properties; the machinery is shared.

   --field-shift must equal exactly one tile of --field-size, or the loop
   visibly jumps when the animation restarts.
   -------------------------------------------------------------------------- */
.blueprint-dark {
  --field-image:
    linear-gradient(rgba(120, 160, 255, 0.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120, 160, 255, 0.07) 1px, transparent 1px);
  --field-size: 44px 44px;
  --field-shift-x: 44px;
  --field-shift-y: 44px;
  --field-time: 70s;

  /* The pattern moved to the child; the div is now just the clipping box. */
  background-image: none;
  overflow: hidden;
}

.blueprint-dark::before {
  content: '';
  position: absolute;
  /* Oversized so the drift never exposes an edge. */
  inset: -60px;
  background-image: var(--field-image);
  background-size: var(--field-size);
  animation: fieldDrift var(--field-time) linear infinite;
  will-change: transform;
}

@keyframes fieldDrift {
  to { transform: translate3d(calc(var(--field-shift-x) * -1), calc(var(--field-shift-y) * -1), 0); }
}

/* Home — neural mesh. Nodes on a matrix with faint links running between
   them: the site's own argument that the products are one connected system. */
.page-index .blueprint-dark {
  --field-image:
    radial-gradient(rgba(34, 211, 238, 0.16) 1px, transparent 1.5px),
    linear-gradient(60deg, transparent 48%, rgba(120, 160, 255, 0.05) 49.5%, transparent 51%),
    linear-gradient(-60deg, transparent 48%, rgba(120, 160, 255, 0.05) 49.5%, transparent 51%);
  --field-size: 46px 46px, 92px 92px, 92px 92px;
  --field-shift-x: 92px;
  --field-shift-y: 92px;
  --field-time: 80s;
}

/* Capabilities — the drafting grid, kept, because fabrication drawings are
   literally what this page is about. A finer sub-grid adds the dimension
   ticks a real drawing carries. */
.page-capabilities .blueprint-dark {
  --field-image:
    linear-gradient(rgba(120, 160, 255, 0.075) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120, 160, 255, 0.075) 1px, transparent 1px),
    linear-gradient(rgba(120, 160, 255, 0.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120, 160, 255, 0.028) 1px, transparent 1px);
  --field-size: 88px 88px, 88px 88px, 22px 22px, 22px 22px;
  --field-shift-x: 88px;
  --field-shift-y: 88px;
  --field-time: 110s;
}

/* Products — circuit traces. Three line sets at 0/60/120 degrees read as
   etched silicon; cyan because this is the software and AI arm. */
.page-products .blueprint-dark {
  --field-image:
    linear-gradient(60deg, transparent 47%, rgba(34, 211, 238, 0.07) 48.5%, transparent 50%),
    linear-gradient(-60deg, transparent 47%, rgba(34, 211, 238, 0.07) 48.5%, transparent 50%),
    linear-gradient(90deg, transparent 47%, rgba(120, 160, 255, 0.05) 48.5%, transparent 50%),
    radial-gradient(rgba(34, 211, 238, 0.2) 1px, transparent 1.5px);
  --field-size: 60px 104px, 60px 104px, 60px 104px, 60px 104px;
  --field-shift-x: 60px;
  --field-shift-y: 104px;
  --field-time: 90s;
}

/* Partners — orbital arcs. Concentric rings imply things in formation around
   a centre, which is what a partner roster is. */
.page-partners .blueprint-dark {
  --field-image:
    repeating-radial-gradient(circle at 18% 12%,
      transparent 0 78px, rgba(120, 160, 255, 0.07) 78px 79px, transparent 79px 158px),
    repeating-radial-gradient(circle at 82% 88%,
      transparent 0 96px, rgba(120, 160, 255, 0.05) 96px 97px, transparent 97px 194px);
  --field-size: 100% 100%;
  --field-shift-x: 0px;
  --field-shift-y: 0px;
  --field-time: 120s;
}

/* About — topographic contours. The company page gets terrain: Melaka, the
   facility, ground rather than signal. */
.page-about .blueprint-dark {
  --field-image:
    repeating-radial-gradient(ellipse 220px 150px at 30% 40%,
      transparent 0 26px, rgba(120, 160, 255, 0.055) 26px 27px, transparent 27px 54px),
    repeating-radial-gradient(ellipse 260px 190px at 74% 68%,
      transparent 0 32px, rgba(110, 124, 154, 0.05) 32px 33px, transparent 33px 66px);
  --field-size: 520px 400px;
  --field-shift-x: 520px;
  --field-shift-y: 0px;
  --field-time: 150s;
}

/* Contact — radar. Concentric sweep rings plus a crosshair: the page where
   someone is trying to reach a fixed position. */
.page-contact .blueprint-dark {
  --field-image:
    repeating-radial-gradient(circle at 50% 50%,
      transparent 0 54px, rgba(215, 24, 31, 0.075) 54px 55px, transparent 55px 110px),
    linear-gradient(rgba(120, 160, 255, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120, 160, 255, 0.035) 1px, transparent 1px);
  --field-size: 440px 440px, 110px 110px, 110px 110px;
  --field-shift-x: 0px;
  --field-shift-y: 110px;
  --field-time: 100s;
}

/* VR Training — the VR floor. A flat grid tipped away from the viewer
   converges to a horizon, the one thing on this site that should read as a
   headset view. Scoped to the section rather than a page because vcbt.php is
   the standalone Unity player and never loads header.php. rotateX has to live
   in the keyframes too, or the animation replaces it and the grid snaps flat. */
#vr-training .blueprint-dark {
  --field-image:
    linear-gradient(rgba(34, 211, 238, 0.12) 1px, transparent 1px),
    linear-gradient(90deg, rgba(34, 211, 238, 0.12) 1px, transparent 1px);
  --field-size: 60px 60px;
  --field-shift-y: 60px;
  --field-time: 6s;
  perspective: 320px;
}
#vr-training .blueprint-dark::before {
  inset: -20% -60% -60% -60%;
  transform-origin: 50% 0%;
  animation-name: fieldFloor;
  animation-timing-function: linear;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 45%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 45%);
}
@keyframes fieldFloor {
  from { transform: rotateX(66deg) translate3d(0, 0, 0); }
  to   { transform: rotateX(66deg) translate3d(0, var(--field-shift-y), 0); }
}

@media (prefers-reduced-motion: reduce) {
  .blueprint-dark::before { animation: none; }
  #vr-training .blueprint-dark::before { transform: rotateX(66deg); }
}

/* --------------------------------------------------------------------------
   Mesh field — the AI counterpart to the blueprint grid. A drafting grid says
   fabrication drawing; a dot matrix says data. Blueprint stays on the
   fabrication and capability sections where it's literally true; this goes
   behind the software.
   -------------------------------------------------------------------------- */
.mesh-dark {
  background-image: radial-gradient(rgba(34, 211, 238, 0.15) 1px, transparent 1px);
  background-size: 22px 22px;
}

/* --------------------------------------------------------------------------
   AI surfaces — cyan accent and emission. Used only on Astrum, EyeDrone and
   detection UI, never on the aerospace/fabrication side of the site.
   -------------------------------------------------------------------------- */
.ai-surface { border-color: rgba(34, 211, 238, 0.22) !important; }
.ai-surface:hover { border-color: rgba(34, 211, 238, 0.5) !important; }
.ai-surface .text-signal,
.ai-surface .spec-tag { color: var(--cyan); }
/* Window-chrome dots and carousel markers inside an AI section follow the
   accent too, otherwise one red pip sits in an otherwise cyan panel. */
.ai-surface .bg-signal { background-color: var(--cyan); }
.ai-surface .spec-tag { border-color: rgba(34, 211, 238, 0.45); }
.ai-surface.bracket::before,
.ai-surface.bracket::after,
.ai-surface .cnr { border-color: var(--cyan); }

.glow-cyan {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 0 0 1px rgba(34, 211, 238, 0.2),
    0 0 48px -10px rgba(34, 211, 238, 0.35);
}

/* --------------------------------------------------------------------------
   EyeDrone detection panel — the home page hero demo.

   Boxes are absolutely positioned in percentages of the frame, so they track
   the image at every width without a single line of layout maths. The whole
   interaction is one class on the panel: .is-on draws the boxes, .is-on
   removed leaves the raw frame, which is the entire point of the control.
   -------------------------------------------------------------------------- */
.detect-box {
  border: 1.5px solid rgba(34, 211, 238, 0.9);
  box-shadow:
    0 0 0 1px rgba(1, 6, 15, 0.45),
    0 0 20px -4px rgba(34, 211, 238, 0.55);
  opacity: 0;
  transform: scale(0.965);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.detect.is-on .detect-box {
  opacity: 1;
  transform: none;
  /* Staggered per box via the inline --d, so the boxes land one after another
     the way a detector reports them, rather than all at once. */
  transition-delay: var(--d, 0ms);
}

.detect-label {
  position: absolute;
  bottom: 100%;
  left: -1.5px;
  margin-bottom: 3px;
  display: inline-flex;
  gap: 0.4em;
  white-space: nowrap;
  background: rgba(34, 211, 238, 0.92);
  color: #01060f;
  font-size: 0.56rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.15rem 0.4rem;
}
/* Labels are a fixed size while the frame scales, so a box in the last third
   of a narrow frame needs its label hung off the right edge instead. */
.detect-label.is-right {
  left: auto;
  right: -1.5px;
}

.detect-read { font-weight: 600; }
.detect-conf { color: rgba(1, 6, 15, 0.6); }

/* Readout bar: the LED and the state text both read the panel's state. */
.detect-led {
  box-shadow: 0 0 10px 1px rgba(34, 211, 238, 0.8);
  transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.detect:not(.is-on) .detect-led {
  background-color: var(--steel);
  box-shadow: none;
}
.detect:not(.is-on) .detect-count { opacity: 0.45; }
.detect-count { transition: opacity 0.3s ease; }

/* Seven labels in a 167px-tall frame is noise, not a readout. Phones keep the
   boxes — which is the part that reads at a glance — and drop the text. */
@media (max-width: 639px) {
  .detect-label { display: none; }
  .detect-box { border-width: 1px; }
}

@media (prefers-reduced-motion: reduce) {
  .detect.is-on .detect-box { transition-delay: 0ms; }
}

/* --------------------------------------------------------------------------
   Detection brackets — the signature. Corner marks in the shape EyeDrone
   draws around a recognised object.
   -------------------------------------------------------------------------- */
.bracket { position: relative; }
.bracket::before,
.bracket::after {
  content: '';
  position: absolute;
  width: 16px;
  height: 16px;
  border-color: var(--signal);
  border-style: solid;
  transition: width 0.35s ease, height 0.35s ease;
  pointer-events: none;
}
.bracket::before {
  top: -1px; left: -1px;
  border-width: 2px 0 0 2px;
}
.bracket::after {
  bottom: -1px; right: -1px;
  border-width: 0 2px 2px 0;
}
.bracket:hover::before,
.bracket:hover::after { width: 26px; height: 26px; }

/* Four-corner variant for the hero frame */
.bracket-4 { position: relative; }
.bracket-4 > .cnr {
  position: absolute;
  width: 22px; height: 22px;
  border: 2px solid var(--signal);
  pointer-events: none;
}
.bracket-4 > .cnr-tl { top: 0; left: 0;  border-width: 2px 0 0 2px; }
.bracket-4 > .cnr-tr { top: 0; right: 0; border-width: 2px 2px 0 0; }
.bracket-4 > .cnr-bl { bottom: 0; left: 0;  border-width: 0 0 2px 2px; }
.bracket-4 > .cnr-br { bottom: 0; right: 0; border-width: 0 2px 2px 0; }

/* --------------------------------------------------------------------------
   Media — real photography and drawings dropped into placeholder slots.
   Grayscale-by-default keeps mixed source imagery (photos, renders, scans)
   reading as one system; color on hover rewards interaction.
   -------------------------------------------------------------------------- */
.portrait-frame img {
  filter: grayscale(65%) contrast(1.02);
  transition: filter 0.4s ease, transform 0.5s ease;
}
.portrait-frame:hover img,
.card:hover .portrait-frame img {
  filter: grayscale(0%) contrast(1.02);
  transform: scale(1.03);
}
.portrait-frame::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 45%;
  background: linear-gradient(to top, rgba(3, 11, 28, 0.85), transparent);
  pointer-events: none;
}

.media-frame img {
  filter: saturate(0.9);
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1), filter 0.5s ease;
}
.media-frame:hover img {
  transform: scale(1.045);
  filter: saturate(1.05);
}

/* Product screenshots are the proof the software exists — they get full
   colour. The house desaturation is for mixed photo/render/scan sources. */
.media-frame.is-product img,
.media-frame.is-product:hover img { filter: none; }

/* --------------------------------------------------------------------------
   Carousel HUD — the screenshot prev/next controls read as instrument
   targeting reticles rather than generic round chevrons. Accent follows the
   section: red everywhere, cyan inside .ai-surface, same rule as the rest of
   the site.
   -------------------------------------------------------------------------- */
.slide-nav {
  --nav-accent: var(--signal);
  position: absolute;
  top: 50%;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  transform: translateY(-50%);
  color: rgba(232, 237, 247, 0.75);
  background: rgba(3, 11, 28, 0.55);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  transition: color 0.25s ease, background-color 0.25s ease, box-shadow 0.35s ease;
}
.ai-surface .slide-nav { --nav-accent: var(--cyan); }

/* Targeting box. Brackets sit wide at rest and snap inward on hover — a lens
   pulling focus, not a button growing. */
.slide-nav > .cnr {
  position: absolute;
  width: 13px; height: 13px;
  border: 1px solid var(--nav-accent);
  opacity: 0.45;
  pointer-events: none;
  transition: width 0.22s cubic-bezier(0.2, 0.9, 0.2, 1),
              height 0.22s cubic-bezier(0.2, 0.9, 0.2, 1),
              inset 0.22s cubic-bezier(0.2, 0.9, 0.2, 1),
              opacity 0.22s ease;
}
.slide-nav > .cnr-tl { top: 0; left: 0;     border-width: 1px 0 0 1px; }
.slide-nav > .cnr-tr { top: 0; right: 0;    border-width: 1px 1px 0 0; }
.slide-nav > .cnr-bl { bottom: 0; left: 0;  border-width: 0 0 1px 1px; }
.slide-nav > .cnr-br { bottom: 0; right: 0; border-width: 0 1px 1px 0; }

.slide-nav:hover > .cnr,
.slide-nav:focus-visible > .cnr { width: 9px; height: 9px; opacity: 1; }
.slide-nav:hover > .cnr-tl,
.slide-nav:focus-visible > .cnr-tl { top: 4px; left: 4px; }
.slide-nav:hover > .cnr-tr,
.slide-nav:focus-visible > .cnr-tr { top: 4px; right: 4px; }
.slide-nav:hover > .cnr-bl,
.slide-nav:focus-visible > .cnr-bl { bottom: 4px; left: 4px; }
.slide-nav:hover > .cnr-br,
.slide-nav:focus-visible > .cnr-br { bottom: 4px; right: 4px; }

/* Radar dish — a conic wedge masked to a thin annulus, turning slowly at rest
   and spinning up on hover. mask is what makes it a ring instead of a pie. */
@keyframes radarSweep { to { transform: rotate(360deg); } }

.slide-nav::before {
  content: '';
  position: absolute;
  inset: 6px;
  border-radius: 50%;
  /* Plain two-stop wedge first, for browsers without color-mix (Safari < 16.2);
     the softer three-stop tail below overrides it where color-mix is supported. */
  background: conic-gradient(
    from 0deg,
    transparent 0deg,
    transparent 262deg,
    var(--nav-accent) 352deg,
    transparent 360deg
  );
  background: conic-gradient(
    from 0deg,
    transparent 0deg,
    transparent 232deg,
    color-mix(in srgb, var(--nav-accent) 35%, transparent) 300deg,
    var(--nav-accent) 352deg,
    transparent 360deg
  );
  -webkit-mask: radial-gradient(closest-side, transparent 70%, #000 72%);
          mask: radial-gradient(closest-side, transparent 70%, #000 72%);
  opacity: 0.4;
  animation: radarSweep 12s linear infinite;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.slide-nav:hover::before,
.slide-nav:focus-visible::before {
  opacity: 1;
  animation-duration: 2.4s;
}

/* Lock ring — the closed circle the sweep is searching for. Sits scaled out
   and invisible until the control is engaged. */
.slide-nav::after {
  content: '';
  position: absolute;
  inset: 6px;
  border: 1px solid var(--nav-accent);
  border-radius: 50%;
  opacity: 0;
  transform: scale(1.35);
  transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.2, 0.9, 0.2, 1);
  pointer-events: none;
}
.slide-nav:hover::after,
.slide-nav:focus-visible::after { opacity: 0.85; transform: scale(1); }

.slide-nav:hover,
.slide-nav:focus-visible {
  color: #fff;
  background: rgba(3, 11, 28, 0.8);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06),
              0 0 24px -4px var(--nav-accent);
  outline: none;
}

/* Lock confirmed — one flash of the ring on an actual slide change. */
@keyframes navLock {
  0%   { opacity: 1; transform: scale(0.72); }
  55%  { opacity: 1; transform: scale(1.18); }
  100% { opacity: 0; transform: scale(1.34); }
}
.slide-nav.is-locked::after {
  animation: navLock 0.5s cubic-bezier(0.3, 0, 0.2, 1) 1;
}

.slide-nav svg { position: relative; z-index: 1; }

/* Sensor sweep on slide change — one pass of the accent line across the
   frame, the same gesture as the hero .scanline but shorter and horizontal. */
@keyframes slideSweep {
  0%   { transform: translateX(-100%); opacity: 0; }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translateX(100%); opacity: 0; }
}
.slide-sweep {
  --nav-accent: var(--signal);
  position: absolute;
  inset: 0;
  z-index: 15;
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.04) 42%,
    var(--nav-accent) 50%,
    rgba(255, 255, 255, 0.04) 58%,
    transparent 100%
  );
  mix-blend-mode: screen;
}
.ai-surface .slide-sweep { --nav-accent: var(--cyan); }
.slide-sweep.is-sweeping {
  animation: slideSweep 0.65s cubic-bezier(0.4, 0, 0.2, 1) 1;
}

@media (prefers-reduced-motion: reduce) {
  .slide-sweep.is-sweeping { animation: none; }
  .slide-nav > .cnr { transition: none; }
  .slide-nav::before { animation: none; }
  .slide-nav.is-locked::after { animation: none; }
}

/* Logo chip — a light panel so third-party logo files sit consistently on the
   dark navy cards. Eight of the sixteen partner PNGs have opaque white or
   coloured backgrounds, so the usual mask-to-white treatment isn't available:
   it would render those as solid blocks. Muted instead, colour on hover. */
.logo-chip {
  background: rgba(232, 237, 247, 0.88);
  filter: grayscale(1);
  opacity: 0.72;
  transition: opacity 0.3s ease, filter 0.3s ease;
}
.logo-chip:hover {
  opacity: 1;
  filter: grayscale(0);
}

/* --------------------------------------------------------------------------
   Cards
   -------------------------------------------------------------------------- */
/* The inset top hairline is the light source. Dark surfaces read as flat paper
   without it — one pixel of caught light is what separates a panel from a hole. */
.card {
  border-radius: var(--radius);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 1px 2px rgba(0, 0, 0, 0.4);
  transition: transform 0.3s ease, border-color 0.3s ease,
              background-color 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
  transform: translateY(-4px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    0 12px 32px -12px rgba(0, 0, 0, 0.7);
}

/* Detection frames keep square corners — the brackets are the concept, and a
   rounded corner mark isn't a corner mark. Everything else rounds. */
.bracket,
.bracket-4,
.card.bracket,
.card.bracket-4 { border-radius: 0; }

/* Buttons. Targets the Tailwind classes already on the anchors rather than
   editing six files. */
a.bg-signal,
button.bg-signal,
button[type="submit"] { border-radius: 8px; }

/* Part-number tag, set in mono because these are real MAFCA listing codes */
.spec-tag {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  padding: 0.15rem 0.5rem;
  border: 1px solid rgba(215, 24, 31, 0.45);
  color: var(--signal);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Scroll reveal
   -------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
[data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* Hologram hero — floating product panel above the laptop line-art */
@keyframes hologramFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}
.hologram-float {
  animation: hologramFloat 6.5s ease-in-out infinite;
}
@keyframes hologramScan {
  0%   { transform: translateY(0); }
  100% { transform: translateY(148px); }
}
.hologram-scan {
  animation: hologramScan 2.6s ease-in-out infinite alternate;
}
@keyframes hologramPulse {
  0%, 100% { opacity: 0.68; }
  50%      { opacity: 1; }
}
.hologram-pulse {
  animation: hologramPulse 3.8s ease-in-out infinite;
  transform-origin: center;
}

/* Hero scan line — one sweep on load, echoing a sensor pass. It was looping
   forever, which is the difference between an instrument and a screensaver. */
@keyframes scan {
  0%   { transform: translateY(-10%); opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translateY(700%); opacity: 0; }
}
.scanline {
  animation: scan 4.5s ease-in-out 0.6s 1 both;
}

/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */
.field {
  width: 100%;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(110, 124, 154, 0.35);
  color: #fff;
  padding: 0.8rem 1rem;
  font-size: 0.95rem;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}
.field::placeholder { color: rgba(232, 237, 247, 0.35); }
.field:focus {
  outline: none;
  border-color: var(--signal);
  background: rgba(255, 255, 255, 0.06);
}
.field-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel);
  display: block;
  margin-bottom: 0.45rem;
}

/* --------------------------------------------------------------------------
   Accessibility
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--signal);
  color: #fff;
  padding: 0.75rem 1.25rem;
}
.skip-link:focus { left: 0; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
}
