/* ============================================================
   Rylexx Studio — machine layer.
   The instrument reading, kept quiet: two signal colours, a sensor
   eye, soft corner ticks, and page-to-page transitions. Nothing
   follows the cursor. Loaded after site.css, so it overrides on
   purpose. Everything degrades to a plain static page when motion
   is switched off.
   ============================================================ */

:root {
  --data:        #3A8492;
  --data-wash:   rgba(58,132,146,0.08);
  --hud:         rgba(71,97,125,0.24);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground:      #0B0E13;
    --surface:     #131822;
    --surface-2:   #1A202B;
    --data:        #7EC4CE;
    --data-wash:   rgba(126,196,206,0.09);
    --hud:         rgba(143,174,201,0.22);
  }
}
:root[data-theme="dark"] {
  --ground:      #0B0E13;
  --surface:     #131822;
  --surface-2:   #1A202B;
  --data:        #7EC4CE;
  --data-wash:   rgba(126,196,206,0.09);
  --hud:         rgba(143,174,201,0.22);
}

/* ---------- the scene belongs to the masthead area, not the whole page ----------
   Anchored to the top of the document and scrolled away with it, so no
   moving line ever crosses running text further down. */
body { position: relative; }
#fx-field {
  /* a canvas is a replaced element: it keeps its intrinsic 300x150
     unless width and height are stated outright */
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 760px;
  z-index: 0;
  pointer-events: none;
  opacity: 0.3;
  /* dissolve downward and at the sides, so it reads as atmosphere */
  -webkit-mask-image: linear-gradient(to bottom, #000 0 30%, transparent 88%),
                      radial-gradient(70% 60% at 50% 42%, transparent 30%, #000 78%);
          mask-image: linear-gradient(to bottom, #000 0 30%, transparent 88%),
                      radial-gradient(70% 60% at 50% 42%, transparent 30%, #000 78%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}
@media (min-width: 1100px) { #fx-field { height: 880px; } }
body > header, body > main, body > footer { position: relative; z-index: 1; }

/* a single soft key light behind the hero, still and warm */
.hero { position: relative; }
.hero::before {
  content: "";
  position: absolute;
  inset: -22% -12%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(52% 58% at 66% 40%, var(--amber-wash), transparent 72%);
}

/* ============================================================
   The sensor eye — the mark. It looks around on its own and
   blinks now and then. It does not follow you.
   ============================================================ */
.reclamp {
  position: relative;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--surface);
  border: 1.5px solid var(--hud);
  flex: none;
  animation: none;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.reclamp::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 32%, var(--amber-bright), var(--amber) 64%, #000 135%);
  box-shadow: 0 0 7px -2px var(--amber-bright);
  transform: translate(var(--eye-x, 0px), var(--eye-y, 0px));
}
.reclamp::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--surface);
  transform: scaleY(0);
  transform-origin: 50% 0;
  animation: blink 9s ease-in-out infinite;
}
@keyframes blink {
  0%, 94%, 100% { transform: scaleY(0); }
  96.5%         { transform: scaleY(1); }
}

/* ============================================================
   Quiet instrument details
   ============================================================ */

/* corner ticks only where a card is genuinely a unit of its own */
.pack, .work-state, .panel { position: relative; }
.pack::before, .work-state::before, .panel::before {
  content: "";
  position: absolute;
  top: -1px; left: -1px;
  width: 14px; height: 14px;
  border: 1px solid var(--amber);
  border-right: none; border-bottom: none;
  border-radius: 11px 0 0 0;
  opacity: 0.4;
  pointer-events: none;
}

.rule-plat { color: var(--data); }
table.spec th { color: var(--data); }
.tile-id { color: var(--data); }

/* seams read as light rather than as rules */
section { position: relative; border-top: none; }
section::before {
  content: "";
  position: absolute;
  inset-inline: 0; top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--hud) 20%, var(--hud) 80%, transparent);
}
section:first-of-type::before { display: none; }

header.masthead { border-bottom: 1px solid var(--hud); }
nav.sitenav a[aria-current="page"] { box-shadow: inset 0 -2px 0 var(--data); }
.btn-ghost { border-color: var(--hud); }
.btn-ghost:hover { border-color: var(--data); }

/* the running variant, lit but not shouting */
.tile.is-live { box-shadow: var(--shadow-lift), 0 0 22px -8px var(--amber-bright); }

/* ============================================================
   Arrival — one gentle motion, used everywhere
   ============================================================ */
.fx-anim {
  transition: opacity 0.55s cubic-bezier(0.22, 0.61, 0.36, 1),
              transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.fx-hidden { opacity: 0; transform: translate3d(0, 14px, 0); }

/* the top of a page arrives at once, on load — never on a scroll observer,
   so the first thing a visitor sees is never a blank column */
@keyframes fxRise {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: none; }
}
.fx-rise { animation: fxRise 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) both; }

/* ============================================================
   Page transitions — the visual link between the pages.
   Content crosses over, the masthead and the scene stay put.
   ============================================================ */
@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: fxOut 0.32s cubic-bezier(0.4, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: fxIn 0.46s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
@keyframes fxOut {
  to { opacity: 0; transform: translateY(-10px) scale(0.994); }
}
@keyframes fxIn {
  from { opacity: 0; transform: translateY(14px) scale(0.994); }
}

/* the header and the scene keep their own identity across the swap,
   so only the content appears to change */
header.masthead { view-transition-name: masthead; }
#fx-field { view-transition-name: field; }
::view-transition-old(masthead), ::view-transition-new(masthead),
::view-transition-old(field), ::view-transition-new(field) {
  animation: none;
  mix-blend-mode: normal;
}

/* same fade for browsers without the View Transition API */
body { transition: opacity 0.26s ease, transform 0.26s ease; }
body.fx-leaving { opacity: 0; transform: translateY(-8px); }

@media (prefers-reduced-motion: reduce) {
  #fx-field, .hero::before { display: none; }
  .fx-hidden { opacity: 1 !important; transform: none !important; }
  .fx-rise { animation: none; opacity: 1; }
  .reclamp::after { animation: none; }
  section::before { background: var(--line-soft); }
  body, body.fx-leaving { transition: none; opacity: 1; transform: none; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* ---------- vertical rhythm ----------
   A page head already carries its own bottom padding; the first section
   below it was adding a second helping and left a visible gap. */
.page-head + section,
.page-head ~ section:first-of-type { padding-top: clamp(0.5rem, 1.5vw, 1.5rem); }

/* the closing call to action should not float in an empty field */
main > .wrap > section:last-of-type { padding-bottom: clamp(3rem, 6vw, 5rem); }
