/* ============================================================================
   PRINTORIAN OS — navigation overlay  ("NAV.MODULE")
   ----------------------------------------------------------------------------
   A full-screen command console rather than a dropdown. Opening it should feel
   like a subsystem coming online: the frame draws itself, a scan passes over
   the field, and the entries resolve out of noise one after another.

   The motion budget is deliberate. Every animation here runs once, on open,
   and finishes inside 700ms — a console that keeps moving after it has settled
   reads as decoration. Nothing loops except the cursor.

   Depends on tokens.css + harvester.css. Behaviour lives in js/menu.js.
   ========================================================================= */

/* ------------------------------------------------------------------ shell */

.hv-menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  /* Pin the single column to the container. Left implicit, an `auto` track
     grows to the widest item's min-content, so one unshrinkable child would
     widen every row of the overlay at once. */
  grid-template-columns: minmax(0, 1fr);
  background: var(--hv-void);
  overflow: hidden;
}

.hv-menu[hidden] { display: none; }

/* The backdrop is not transparent — this is a mode change, not a layer on top
   of the page. A blurred glimpse of the screen behind would undercut that. */
.hv-menu__bg {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(var(--hv-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--hv-grid) 1px, transparent 1px),
    radial-gradient(120% 90% at 50% 0%, rgba(76, 215, 232, 0.05), transparent 60%);
  background-size: 32px 32px, 32px 32px, 100% 100%;
  animation: hv-menu-bg 520ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
}

@keyframes hv-menu-bg {
  from { opacity: 0; transform: scale(1.06); }
  to   { opacity: 1; transform: none; }
}

/* Corner brackets that draw outward from nothing. Same eight-gradient trick as
   .hv-frame, but with the arm length animated instead of fixed. */
.hv-menu__frame {
  position: absolute;
  inset: 10px;
  pointer-events: none;
  --arm: 0px;
  --ink: var(--hv-text-faint);
  background-repeat: no-repeat;
  background-image:
    linear-gradient(var(--ink), var(--ink)), linear-gradient(var(--ink), var(--ink)),
    linear-gradient(var(--ink), var(--ink)), linear-gradient(var(--ink), var(--ink)),
    linear-gradient(var(--ink), var(--ink)), linear-gradient(var(--ink), var(--ink)),
    linear-gradient(var(--ink), var(--ink)), linear-gradient(var(--ink), var(--ink));
  background-size:
    var(--arm) 1px, 1px var(--arm), var(--arm) 1px, 1px var(--arm),
    var(--arm) 1px, 1px var(--arm), var(--arm) 1px, 1px var(--arm);
  background-position:
    left top, left top, right top, right top,
    left bottom, left bottom, right bottom, right bottom;
  animation: hv-menu-frame 460ms cubic-bezier(0.2, 0.7, 0.3, 1) 60ms both;
}

@keyframes hv-menu-frame {
  from { --arm: 0px;  opacity: 0; }
  to   { --arm: 64px; opacity: 1; }
}

/* `--arm` only animates smoothly where custom properties are registered. */
@property --arm {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

/* A single scan pass. Once — it is a power-on self-test, not a CRT effect. */
.hv-menu__scan {
  position: absolute;
  left: 0;
  right: 0;
  height: 140px;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    transparent,
    rgba(76, 215, 232, 0.05) 45%,
    rgba(76, 215, 232, 0.14) 50%,
    rgba(76, 215, 232, 0.05) 55%,
    transparent
  );
  animation: hv-menu-scan 680ms cubic-bezier(0.35, 0, 0.2, 1) 80ms both;
}

@keyframes hv-menu-scan {
  from { transform: translateY(-140px); opacity: 1; }
  to   { transform: translateY(100vh);  opacity: 0; }
}

/* ------------------------------------------------------------ menu chrome */

.hv-menu .hv-chrome { position: relative; z-index: 2; background: transparent; }

.hv-menu__query {
  flex: 1 1 auto;
  /* An input carries an intrinsic width from its `size` attribute, and a flex
     item will not shrink below that without this. `width: 0` makes the flex
     basis do the sizing instead. */
  min-width: 0;
  width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--hv-text);
  font-family: var(--hv-font-mono);
  font-size: var(--hv-size-tiny);
  letter-spacing: var(--hv-track-label);
  text-transform: uppercase;
}
.hv-menu__query::placeholder { color: var(--hv-text-faint); }
.hv-menu__query:focus { outline: none; }

/* Blinking block cursor — the one looping animation in the file, because a
   command line without a cursor does not look like it is waiting for you. */
.hv-menu__caret {
  width: 7px;
  height: 12px;
  flex: none;
  background: var(--hv-live);
  animation: hv-caret 1.05s steps(1) infinite;
}

@keyframes hv-caret { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }

/* --------------------------------------------------------------- the field */

.hv-menu__field {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 26rem);
  gap: var(--hv-6);
  align-content: start;
  padding: var(--hv-6) var(--hv-6) var(--hv-4);
  overflow-y: auto;
}

.hv-menu__list { display: flex; flex-direction: column; }

/* ------------------------------------------------------------------ entry
   Each row is a numbered plate. The number is the address, the label is the
   destination, the trailing note is what you will find there. */

.hv-menu__item {
  position: relative;
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--hv-4);
  padding: var(--hv-3) var(--hv-3) var(--hv-3) 0;
  border: 0;
  border-top: 1px solid var(--hv-line-dim);
  background: none;
  color: var(--hv-text-faint);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  animation: hv-menu-item 280ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
  animation-delay: calc(var(--i, 0) * 34ms + 140ms);
}

.hv-menu__list .hv-menu__item:last-child { border-bottom: 1px solid var(--hv-line-dim); }

@keyframes hv-menu-item {
  from { opacity: 0; transform: translateX(-18px); clip-path: inset(0 100% 0 0); }
  to   { opacity: 1; transform: none;              clip-path: inset(0 0 0 0); }
}

/* The active marker slides in as a solid bar on the left edge. */
.hv-menu__item::before {
  content: '';
  position: absolute;
  left: 0; top: -1px; bottom: -1px;
  width: 3px;
  background: var(--hv-text);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform 140ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.hv-menu__item[data-active='true'] { color: var(--hv-text); }
.hv-menu__item[data-active='true']::before { transform: scaleY(1); }

.hv-menu__n {
  font-family: var(--hv-font-mono);
  font-size: var(--hv-size-tiny);
  letter-spacing: var(--hv-track-micro);
  padding-left: var(--hv-4);
  color: var(--hv-text-faint);
}
.hv-menu__item[data-active='true'] .hv-menu__n { color: var(--hv-live); }

.hv-menu__label {
  font-family: var(--hv-font-display);
  font-size: clamp(1.1rem, 2.6vw, 1.9rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transform: scaleX(var(--hv-stretch-brand));
  transform-origin: left center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
  transition: color 120ms linear;
}

.hv-menu__note {
  font-family: var(--hv-font-mono);
  font-size: var(--hv-size-micro);
  letter-spacing: var(--hv-track-micro);
  text-transform: uppercase;
  text-align: right;
  color: var(--hv-text-faint);
}

/* The chevron travels in only for the active row. */
.hv-menu__go {
  display: inline-block;
  margin-left: var(--hv-3);
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity 140ms linear, transform 140ms cubic-bezier(0.2, 0.7, 0.3, 1);
}
.hv-menu__item[data-active='true'] .hv-menu__go { opacity: 1; transform: none; }

.hv-menu__item[hidden] { display: none; }

/* ---------------------------------------------------------------- preview
   What the highlighted destination actually contains. Reading it should make
   the choice, so it carries live figures rather than a description. */

.hv-menu__preview {
  position: sticky;
  top: 0;
  align-self: start;
  animation: hv-menu-item 320ms cubic-bezier(0.2, 0.7, 0.3, 1) 220ms backwards;
}

/* Content swap: a short flicker rather than a cross-fade, so it reads as a
   channel change on an instrument instead of a slide transition. */
.hv-menu__preview[data-swapping='true'] .hv-menu__pv { animation: hv-flicker 180ms steps(2) both; }

@keyframes hv-flicker {
  0%   { opacity: 0.25; transform: translateX(3px); }
  50%  { opacity: 0.6;  transform: translateX(-1px); }
  100% { opacity: 1;    transform: none; }
}

.hv-menu__pv-mark {
  font-family: var(--hv-font-display);
  font-size: clamp(1.6rem, 4vw, 2.4rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  width: calc(100% / var(--hv-stretch-brand));
  transform: scaleX(var(--hv-stretch-brand));
  transform-origin: left center;
}

/* Route diagram — strokes draw themselves in when the preview changes. */
.hv-menu__pv-svg path,
.hv-menu__pv-svg circle,
.hv-menu__pv-svg rect {
  stroke-dasharray: 260;
  stroke-dashoffset: 260;
  animation: hv-draw 620ms cubic-bezier(0.35, 0, 0.2, 1) both;
}
.hv-menu__pv-svg > *:nth-child(2) { animation-delay: 70ms; }
.hv-menu__pv-svg > *:nth-child(3) { animation-delay: 140ms; }
.hv-menu__pv-svg > *:nth-child(4) { animation-delay: 210ms; }

@keyframes hv-draw { to { stroke-dashoffset: 0; } }

/* ------------------------------------------------------------------ footer */

.hv-menu__foot {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--hv-4);
  flex-wrap: wrap;
  padding: var(--hv-2) var(--hv-6);
  border-top: 1px solid var(--hv-line);
  font-family: var(--hv-font-mono);
  font-size: var(--hv-size-micro);
  letter-spacing: var(--hv-track-micro);
  text-transform: uppercase;
  color: var(--hv-text-faint);
  animation: hv-menu-item 260ms cubic-bezier(0.2, 0.7, 0.3, 1) 320ms backwards;
}

.hv-key {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 16px;
  padding: 0 5px;
  margin-right: 5px;
  border: 1px solid var(--hv-line);
  background: var(--hv-bg-inset);
  color: var(--hv-text-dim);
}

/* ------------------------------------------------------------- the trigger
   Sits in the app bar. The three rules shorten to a caret on hover — a small
   promise that the thing it opens is a command surface, not a drawer. */

.hv-menu-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--hv-2);
  padding: var(--hv-1) var(--hv-3);
  border: 1px solid var(--hv-line);
  background: var(--hv-bg-inset);
  color: var(--hv-text);
  font-size: var(--hv-size-tiny);
  font-weight: 700;
  letter-spacing: var(--hv-track-chrome);
  text-transform: uppercase;
  cursor: pointer;
}
.hv-menu-trigger:hover { border-color: var(--hv-text); }

.hv-menu-trigger__bars {
  display: grid;
  gap: 3px;
  width: 14px;
}
.hv-menu-trigger__bars i {
  display: block;
  height: 2px;
  background: currentColor;
  transition: transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1), width 180ms cubic-bezier(0.2, 0.7, 0.3, 1);
  width: 100%;
}
.hv-menu-trigger:hover .hv-menu-trigger__bars i:nth-child(1) { width: 60%; transform: translateX(2px); }
.hv-menu-trigger:hover .hv-menu-trigger__bars i:nth-child(3) { width: 60%; transform: translateX(2px); }

.hv-menu-trigger__hint {
  font-family: var(--hv-font-mono);
  font-weight: 400;
  letter-spacing: var(--hv-track-label);
  color: var(--hv-text-faint);
}

/* -------------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .hv-menu__field { grid-template-columns: minmax(0, 1fr); padding: var(--hv-4); gap: var(--hv-4); }
  .hv-menu__preview { position: static; }
  .hv-menu__item { grid-template-columns: 3rem minmax(0, 1fr); }
  .hv-menu__note { display: none; }
  .hv-menu-trigger__hint { display: none; }
}

/* ------------------------------------------------------- reduced motion
   The whole point of the sequence is theatre, so it is the first thing to go.
   Structure, stagger and the active marker survive; movement does not. */

@media (prefers-reduced-motion: reduce) {
  .hv-menu__bg,
  .hv-menu__frame,
  .hv-menu__item,
  .hv-menu__preview,
  .hv-menu__foot { animation-duration: 1ms; animation-delay: 0ms; }
  .hv-menu__scan { display: none; }
  .hv-menu__caret { animation: none; }
  .hv-menu__pv-svg path,
  .hv-menu__pv-svg circle,
  .hv-menu__pv-svg rect { animation: none; stroke-dashoffset: 0; }
  .hv-menu__frame { --arm: 64px; }
}
