/* QuantumEtchers /site — dark laser-industrial theme.
   Keeps the splash's identity (near-black violet surfaces, hex grid, chrome
   wordmark) and rebuilds the interface layer around a precision-machining
   language: hairline kerf rules, fiducial corner ticks, cut-edge focus.

   Class names are a compatibility contract — every admin page and the public
   pages reference them directly, so names only ever get added here, never
   renamed.

   NOTE: deliberately no `touch-action` or `overscroll-behavior` on html/body.
   Those globals silently swallow mouse-wheel scroll on Windows precision
   pointers. Canvas layers scope their own `touch-action: pan-y`. */

@import url('https://fonts.googleapis.com/css2?family=Archivo:wdth,wght@75..112,400..800&family=JetBrains+Mono:wght@400;500;700&family=Orbitron:wght@700;900&display=swap');

:root {
  /* ── Surfaces — violet-tinted near-black, lifting in four steps ── */
  --bg:          #070410;
  --bg-card:     #0e0a1c;
  --bg-subtle:   #120d24;
  --bg-raised:   #171030;
  --bg-high:     #1d1540;

  /* ── Ink ramp. Every step clears 4.5:1 on every surface above. ──
     ink-faint used to sit at 2.9:1 and failed small-text contrast
     outright; the ramp is re-spaced rather than merely nudged. */
  --ink:         #eceaf4;
  --ink-dim:     #a9a3bd;
  --ink-faint:   #86809d;

  /* Chrome — brushed-metal fill, reserved for the wordmark lockup */
  --chrome-1:    #d4d8e0;
  --chrome-2:    #8a8f9e;

  /* ── Brand violet. `--violet` fills and borders; `--violet-hot` is
     the only violet that may carry text (it clears 7.5:1 everywhere). */
  --violet:      #8b5cff;
  --violet-hot:  #b794ff;
  --violet-deep: #4a1a99;
  --violet-dim:  rgba(139, 92, 255, 0.35);

  /* Lines — hairlines carry this design; they are never thicker than 1px */
  --line:        rgba(139, 92, 255, 0.18);
  --line-strong: rgba(139, 92, 255, 0.38);
  --line-soft:   rgba(255, 255, 255, 0.06);

  /* Status */
  --danger:      #ff4d6d;
  --ok:          #3ddc97;
  --warn:        #ffb84d;

  /* ── Type ──
     Archivo is variable on both width and weight: display type runs
     condensed + heavy so it reads like an etched nameplate, body runs
     normal width. Mono is restricted to data — measurements, money,
     identifiers, coordinates — never used as a texture for "technical". */
  --font-display: 'Archivo', system-ui, sans-serif;
  --font-body:    'Archivo', system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, monospace;
  --font-brand:   'Orbitron', var(--font-display);

  /* Depth — every shadow carries an offset and a soft blur, with an
     inset top highlight standing in for a machined edge catching light. */
  --shadow-plate: 0 1px 2px rgba(0,0,0,0.5), 0 8px 28px -8px rgba(0,0,0,0.7),
                  inset 0 1px 0 rgba(183,148,255,0.08);
  --shadow-lift:  0 2px 4px rgba(0,0,0,0.5), 0 18px 48px -12px rgba(0,0,0,0.8),
                  inset 0 1px 0 rgba(183,148,255,0.12);
  --shadow-card:  var(--shadow-plate);   /* legacy alias */
  --glow-violet:  0 0 24px rgba(139, 92, 255, 0.25);

  /* Motion — one exponential ease-out, used everywhere */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ── Reset ────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  font-variation-settings: 'wdth' 100;
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  scrollbar-color: var(--violet-dim) var(--bg-subtle);
  scrollbar-width: thin;
}

body {
  color: var(--ink);
  min-height: 100vh;
  position: relative;
  /* The splash's two-lobe violet bloom, toned down so long pages don't
     band. Fixed attachment keeps it anchored while content scrolls. */
  background:
    radial-gradient(ellipse 90% 55% at 50% 0%,   rgba(74, 26, 153, 0.30) 0%, transparent 65%),
    radial-gradient(ellipse 70% 50% at 50% 100%, rgba(139, 92, 255, 0.10) 0%, transparent 70%),
    var(--bg);
  background-attachment: fixed;
}

/* ── Browser surfaces ─────────────────────────────────────────────
   The parts of the page nobody draws still carry the design. Left at
   their defaults these belong to no design system at all. */
::selection { background: rgba(139, 92, 255, 0.42); color: #fff; }
input, textarea, select { caret-color: var(--violet-hot); }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb {
  background: rgba(139, 92, 255, 0.26);
  border: 3px solid var(--bg);
  border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover { background: rgba(139, 92, 255, 0.5); }
::-webkit-scrollbar-corner { background: var(--bg); }

/* Cut-edge focus: a sharp 1px kerf line with a narrow bloom outside it,
   rather than the browser's generic ring. */
:focus-visible {
  outline: 1px solid var(--violet-hot);
  outline-offset: 2px;
  border-radius: 2px;
}
:focus:not(:focus-visible) { outline: none; }

/* ── Typography ───────────────────────────────────────────────── */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--ink);
  line-height: 1.08;
  font-weight: 700;
  font-variation-settings: 'wdth' 86;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 { font-size: clamp(2.25rem, 1.4rem + 3.6vw, 4.25rem); font-weight: 800; }
h2 { font-size: clamp(1.5rem, 1.1rem + 1.5vw, 2.25rem); }
h3 { font-size: 1.125rem; }

p { color: var(--ink-dim); }

/* Prose measure — 65-75ch keeps long copy readable */
.prose, .lead { max-width: 68ch; }

/* Brushed-chrome fill. Reserved for the wordmark: display headings take
   their emphasis from weight and width instead. */
.chrome {
  font-family: var(--font-brand);
  background: linear-gradient(180deg,
    #ffffff 0%, var(--chrome-1) 30%, var(--chrome-2) 58%, #ffffff 82%, var(--chrome-2) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  text-shadow: 0 0 40px rgba(139, 92, 255, 0.22);
}

/* Machine-readable data: measurements, money, identifiers, part numbers. */
.mono, .data-table .num, code, kbd {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* Spec strip — a mono caption line with tick separators, the way a
   nameplate carries its stamped detail. */
.spec {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}
.spec > * + *::before {
  content: '';
  display: inline-block;
  width: 1px; height: 0.6875rem;
  margin-right: 0.75rem;
  vertical-align: -0.1em;
  background: var(--line-strong);
}
/* A tick that belongs to the item it precedes ends up leading a wrapped
   line. Below the width where the strip fits on one line, stack it and
   drop the ticks rather than let them hang in the margin. */
@media (max-width: 720px) {
  .spec { flex-direction: column; align-items: flex-start; gap: 0.4rem; }
  .spec > * + *::before { display: none; }
}

a {
  color: var(--violet-hot);
  text-decoration: none;
  transition: color 0.15s var(--ease);
}
a:hover { color: #fff; }

/* Prose links get a real underline, offset clear of the descenders */
.prose a, p a:not(.btn) {
  text-decoration: underline;
  text-decoration-color: var(--violet-dim);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  transition: color 0.15s var(--ease), text-decoration-color 0.15s var(--ease);
}
.prose a:hover, p a:not(.btn):hover { text-decoration-color: var(--violet-hot); }

/* ── Heading score ────────────────────────────────────────────────
   The signature mark and the one authored motion in the design: a
   hairline is cut under each section title by a bright node that
   travels its length, once, when the section first comes into view. */
.score {
  position: relative;
  display: block;
  height: 1px;
  margin-top: 1rem;
  width: 100%;
  max-width: 420px;
  background: var(--line);
  overflow: visible;
}
/* Scaled rather than widened: `transition: width` relayouts on every frame,
   while a transform stays on the compositor. */
.score::before {
  content: '';
  position: absolute;
  inset: 0 0 0 0;
  background: linear-gradient(90deg, var(--violet-hot), var(--violet));
  box-shadow: 0 0 8px rgba(139, 92, 255, 0.55);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.1s var(--ease);
}
.score::after {
  content: '';
  position: absolute;
  top: -2px; left: 0;
  width: 5px; height: 5px;
  margin-left: -2px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 10px 2px var(--violet-hot), 0 0 20px 5px rgba(139, 92, 255, 0.5);
  opacity: 0;
  transition: left 1.1s var(--ease), opacity 0.4s linear 0.95s;
}
/* `cut` and `cutting` land on the .score element itself, so these are
   compound selectors — a descendant combinator here matches nothing. */
.score.cut::before { transform: scaleX(1); }
.score.cut::after  { left: 100%; opacity: 0; }
.score.cutting::after { opacity: 1; transition: left 1.1s var(--ease), opacity 0.12s linear; }

/* ── Corner trace ─────────────────────────────────────────────────
   The house marker for a selected or highlighted row. A plain bar down
   the left edge is the generic framework tell; this instead traces two
   edges out of the bottom-left corner — up the left side and along the
   bottom — each fading as it travels, so the corner reads as the point
   the light entered. Put `.trace` on a positioned element.

   Both edges share that origin, which is why one is `to top` and the
   other `to right`: the gradients run outward from the same point. */
.trace::before,
.trace::after {
  content: '';
  position: absolute;
  background: var(--violet-hot);
  pointer-events: none;
}
.trace::before {           /* left edge, fading upward */
  left: 0; bottom: 0; top: 0;
  width: var(--trace-w, 2px);
  border-radius: var(--trace-w, 2px);
  background: linear-gradient(to top,
    #e2d2ff 0%, var(--violet-hot) 18%, rgba(183, 148, 255, 0) 94%);
}
.trace::after {            /* bottom edge, fading rightward */
  left: 0; right: 0; bottom: 0;
  height: var(--trace-w, 2px);
  border-radius: var(--trace-w, 2px);
  background: linear-gradient(to right,
    #e2d2ff 0%, var(--violet-hot) 14%, rgba(183, 148, 255, 0) 94%);
}

/* ── Fiducial corners ─────────────────────────────────────────────
   Registration ticks at the corners of a plate, the marks a bed uses to
   square up stock. Applied via `.ticks`; purely decorative. */
.ticks { position: relative; }
.ticks::before, .ticks::after {
  content: '';
  position: absolute;
  width: 11px; height: 11px;
  pointer-events: none;
  border-color: var(--line-strong);
  border-style: solid;
  transition: border-color 0.25s var(--ease);
}
.ticks::before { top: 7px;    left: 7px;    border-width: 1px 0 0 1px; }
.ticks::after  { bottom: 7px; right: 7px;   border-width: 0 1px 1px 0; }
.ticks:hover::before, .ticks:hover::after { border-color: var(--violet-hot); }

/* ── Buttons ──────────────────────────────────────────────────────
   Squared off to 3px: a laser kerf has a corner radius measured in
   fractions of a millimeter, and the hardware reads as precise. */
button, .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  position: relative;
  font-family: var(--font-display);
  font-weight: 700;
  font-variation-settings: 'wdth' 92;
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.8rem 1.4rem;
  border: 1px solid transparent;
  border-radius: 3px;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.18s var(--ease), color 0.18s var(--ease),
              border-color 0.18s var(--ease), box-shadow 0.18s var(--ease),
              transform 0.18s var(--ease);
}
button:active, .btn:active { transform: translateY(1px); }

.btn-primary {
  background: linear-gradient(180deg, rgba(139, 92, 255, 0.92), rgba(74, 26, 153, 0.92));
  color: #fff;
  border-color: var(--violet-hot);
  box-shadow: 0 1px 2px rgba(0,0,0,0.4), 0 0 18px -2px rgba(139, 92, 255, 0.35),
              inset 0 1px 0 rgba(255,255,255,0.18);
}
.btn-primary:hover {
  background: linear-gradient(180deg, rgba(167, 128, 255, 1), rgba(94, 40, 180, 1));
  box-shadow: 0 2px 6px rgba(0,0,0,0.45), 0 0 30px -2px rgba(139, 92, 255, 0.55),
              inset 0 1px 0 rgba(255,255,255,0.26);
  color: #fff;
}

.btn-outline {
  background: rgba(139, 92, 255, 0.05);
  color: var(--violet-hot);
  border-color: var(--line-strong);
}
.btn-outline:hover {
  background: rgba(139, 92, 255, 0.14);
  border-color: var(--violet-hot);
  color: #fff;
}

.btn-ghost {
  background: transparent;
  color: var(--ink-dim);
  border-color: transparent;
}
.btn-ghost:hover { color: var(--ink); background: rgba(139, 92, 255, 0.08); }

.btn-danger {
  background: rgba(255, 77, 109, 0.06);
  color: var(--danger);
  border-color: rgba(255, 77, 109, 0.35);
  font-size: 0.625rem;
  padding: 0.45rem 0.8rem;
}
.btn-danger:hover { background: rgba(255, 77, 109, 0.18); color: #fff; border-color: var(--danger); }

.btn-sm { font-size: 0.625rem; padding: 0.5rem 0.9rem; letter-spacing: 0.16em; }
.btn-lg { font-size: 0.75rem;  padding: 1rem 1.9rem; }

button:disabled, .btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.42;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* Loading: the kerf node tracks across the button face */
.btn[data-loading="true"] { color: transparent; pointer-events: none; }
.btn[data-loading="true"]::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--violet-hot), transparent);
  animation: kerf-pass 1.1s linear infinite;
}
@keyframes kerf-pass {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

/* ── Forms ────────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: 0.4rem; }

.label-text {
  font-family: var(--font-display);
  font-weight: 700;
  font-variation-settings: 'wdth' 92;
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="date"],
input[type="number"],
input[type="file"],
input[type="search"],
textarea,
select {
  width: 100%;
  padding: 0.7rem 0.85rem;
  background: rgba(0, 0, 0, 0.42);
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.9375rem;
  line-height: 1.5;
  outline: none;
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease),
              background 0.18s var(--ease);
}
input[type="number"], input[type="date"] { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
input[type="date"] { color-scheme: dark; }
textarea { resize: vertical; min-height: 6rem; }

input:hover, textarea:hover, select:hover { border-color: var(--violet-dim); }

input:focus, textarea:focus, select:focus {
  border-color: var(--violet-hot);
  background: rgba(0, 0, 0, 0.55);
  box-shadow: 0 0 0 3px rgba(139, 92, 255, 0.16);
  outline: none;
}

input::placeholder, textarea::placeholder { color: var(--ink-faint); }
input:disabled, textarea:disabled, select:disabled {
  opacity: 0.5; cursor: not-allowed; border-color: var(--line);
}
input[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(255, 77, 109, 0.14);
}

input[type="file"] { padding: 0.5rem 0.6rem; font-size: 0.8125rem; color: var(--ink-dim); }
input[type="file"]::file-selector-button {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.4rem 0.8rem;
  margin-right: 0.7rem;
  border-radius: 2px;
  border: 1px solid var(--line-strong);
  background: rgba(139, 92, 255, 0.12);
  color: var(--violet-hot);
  cursor: pointer;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease);
}
input[type="file"]::file-selector-button:hover {
  background: rgba(139, 92, 255, 0.24);
  border-color: var(--violet-hot);
}

input[type="checkbox"], input[type="radio"] { accent-color: var(--violet); }

/* Native dropdown lists render with the OS palette; force dark so the
   options aren't white-on-white in the expanded list. */
select option { background: var(--bg-card); color: var(--ink); }

.field-hint { font-size: 0.75rem; color: var(--ink-faint); }
.req { color: var(--violet-hot); }

/* ── Plates (cards) ───────────────────────────────────────────────
   A card is a machined plate: hairline edge, a lit top bevel, real
   offset shadow. `.ticks` adds the fiducial corners where the plate is
   a focal object rather than a container. */
.card, .plate {
  position: relative;
  background:
    linear-gradient(180deg, rgba(139, 92, 255, 0.055), rgba(74, 26, 153, 0.02)),
    var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1.35rem 1.5rem;
  box-shadow: var(--shadow-plate);
}
.plate-raised { background: linear-gradient(180deg, rgba(139,92,255,0.07), rgba(74,26,153,0.03)), var(--bg-subtle); }

/* Interactive plates lift and light their edge */
.plate-link {
  display: block;
  color: inherit;
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease),
              transform 0.25s var(--ease);
}
.plate-link:hover {
  border-color: var(--violet-dim);
  box-shadow: var(--shadow-lift), 0 0 26px -6px rgba(139, 92, 255, 0.3);
  transform: translateY(-2px);
  color: inherit;
}

/* Plate heading: small, wide-tracked, the stamped label on a panel */
.plate-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-variation-settings: 'wdth' 92;
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ── Badges ───────────────────────────────────────────────────── */
.badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.2rem 0.5rem;
  border-radius: 2px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.badge-new      { background: rgba(139,92,255,0.16); color: var(--violet-hot); border-color: rgba(139,92,255,0.38); }
.badge-read     { background: rgba(169,163,189,0.12); color: var(--ink-dim);   border-color: rgba(169,163,189,0.26); }
.badge-replied  { background: rgba(61,220,151,0.14); color: var(--ok);         border-color: rgba(61,220,151,0.34); }
.badge-draft    { background: rgba(169,163,189,0.12); color: var(--ink-dim);   border-color: rgba(169,163,189,0.26); }
.badge-sent     { background: rgba(139,92,255,0.16); color: var(--violet-hot); border-color: rgba(139,92,255,0.38); }
.badge-partial  { background: rgba(255,184,77,0.14); color: var(--warn);       border-color: rgba(255,184,77,0.34); }
.badge-paid     { background: rgba(61,220,151,0.14); color: var(--ok);         border-color: rgba(61,220,151,0.34); }
.badge-void     { background: rgba(255,77,109,0.12); color: var(--danger);     border-color: rgba(255,77,109,0.32); }

/* Process tag — the operation a material accepts */
.tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--line);
  border-radius: 2px;
  color: var(--ink-dim);
  white-space: nowrap;
}

/* ── Canvas layers (hex grid, beam, vignette) ─────────────────── */
.hex-layer {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100dvh;
  z-index: 0;
  pointer-events: none;
  touch-action: pan-y;
}

.vignette {
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 52%, rgba(0,0,0,0.78) 100%);
}
.scanlines {
  position: fixed; inset: 0; z-index: 2; pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(255,255,255,0.009) 0px,
    rgba(255,255,255,0.009) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: screen;
}

/* ── Viewport HUD ─────────────────────────────────────────────────
   The corner brackets and the pointer readout from the splash page, carried
   onto the site so the two feel like one place. Decorative only: aria-hidden,
   never takes a pointer event, and hidden on small screens where it would
   crowd the content rather than frame it.

   --hud-top is set per page. Pages with the sticky header push the top edge
   clear of it; standalone pages use the same inset on all four sides. */
.hud-frame {
  position: fixed;
  top: var(--hud-top, 24px);
  right: 24px;
  bottom: 24px;
  left: 24px;
  z-index: 200;
  pointer-events: none;
}
.hud-frame::before, .hud-frame::after,
.hud-frame > span::before, .hud-frame > span::after {
  content: '';
  position: absolute;
  width: 28px; height: 28px;
  border-color: var(--violet);
  border-style: solid;
  border-width: 0;
}
.hud-frame::before        { top: 0;    left: 0;  border-top-width: 1px;    border-left-width: 1px; }
.hud-frame::after         { top: 0;    right: 0; border-top-width: 1px;    border-right-width: 1px; }
.hud-frame > span::before { bottom: 0; left: 0;  border-bottom-width: 1px; border-left-width: 1px; }
.hud-frame > span::after  { bottom: 0; right: 0; border-bottom-width: 1px; border-right-width: 1px; }

.hud-bar {
  position: fixed;
  /* Inside the corner brackets: the frame sits 24px in and each bracket arm
     is 28px, so 35px lands the text within the L. */
  left: 35px; right: 35px;
  /* Under the brackets, so the scrim below never dims them. */
  z-index: 199;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
  pointer-events: none;
  text-shadow: 0 0 12px rgba(7, 4, 16, 0.9);
}
.hud-bar.at-top { top: calc(var(--hud-top, 24px) + 9px); }

/* At the bottom the page scrolls underneath, and bare 10px text landing on a
   heading is unreadable both ways. The scrim is the fix: full width so it
   reads as the foot of the viewport rather than a floating box, and faded so
   it dims the content it covers instead of hiding it. */
.hud-bar.at-bottom {
  bottom: 0;
  left: 0; right: 0;
  height: 124px;
  padding: 0 35px 33px;
  align-items: flex-end;
  background: linear-gradient(to top,
    rgba(7, 4, 16, 0.97) 0%,
    rgba(7, 4, 16, 0.97) 70%,
    rgba(7, 4, 16, 0) 100%);
}
.hud-bar .side { display: flex; gap: 18px; align-items: center; opacity: 0.75; }
.hud-bar .hud-coords { font-variant-numeric: tabular-nums; }

.hud-dot {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--violet); box-shadow: 0 0 8px var(--violet);
  margin-right: 7px; vertical-align: 1px;
  animation: hud-pulse 1.6s ease-in-out infinite;
}
@keyframes hud-pulse {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: 0.4; transform: scale(0.85); }
}
@media (prefers-reduced-motion: reduce) {
  .hud-dot { animation: none; }
}

/* Below this the brackets start cutting into the content they should be
   framing, and the readout has nowhere to sit. */
@media (max-width: 1023px) {
  .hud-frame, .hud-bar { display: none; }
}
.qe-gal__item[hidden] { display: none !important; }
.qe-gal__more-wrap { display: flex; justify-content: center; margin-top: 1.75rem; }
.qe-gal__more-wrap[hidden] { display: none; }

/* Anything that should sit above the canvas layers */
.above { position: relative; z-index: 10; }

/* ── Public site chrome ───────────────────────────────────────────
   Shared by every page that includes common/header.php. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  height: 64px;
  padding: 0 2.5rem;
  background: rgba(7, 4, 16, 0.78);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid var(--line);
}

.site-brand { display: flex; align-items: center; gap: 0.65rem; text-decoration: none; }
.site-brand img {
  width: 30px; height: auto;
  filter: drop-shadow(0 0 10px rgba(139, 92, 255, 0.55));
}
.site-brand span {
  font-family: var(--font-brand);
  font-weight: 900;
  font-size: 0.8125rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink);
}

.site-nav { display: flex; align-items: center; gap: 0.35rem; }
.site-nav a {
  position: relative;
  padding: 0.45rem 0.7rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #c4bfd6;
  text-decoration: none;
  border-radius: 3px;
  transition: color 0.18s var(--ease), background 0.18s var(--ease);
}
.site-nav a::after {
  content: '';
  position: absolute;
  left: 0.7rem; right: 0.7rem; bottom: 0.2rem;
  height: 1px;
  background: var(--violet-hot);
  box-shadow: 0 0 8px var(--violet);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s var(--ease);
}
.site-nav a:hover { color: var(--ink); background: rgba(139, 92, 255, 0.07); }
.site-nav a:hover::after { transform: scaleX(1); }
.site-nav .nav-cta {
  color: var(--violet-hot);
  border: 1px solid var(--line-strong);
  margin-left: 0.5rem;
}
.site-nav .nav-cta::after { display: none; }
.site-nav .nav-cta:hover { background: rgba(139, 92, 255, 0.16); border-color: var(--violet-hot); color: #fff; }

.site-footer {
  position: relative;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 1.75rem 2.5rem;
  border-top: 1px solid var(--line);
  font-size: 0.75rem;
  color: var(--ink-faint);
}
/* The readout floats over the foot of the viewport, so the last thing on the
   page needs room underneath it. Without this the footer, and the visitor
   count in it, end up sitting inside the scrim. Only where the HUD shows. */
@media (min-width: 861px) {
  .site-footer { padding-bottom: calc(1.75rem + 124px); }
}
.footer-tag {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
/* Unique visitors and crawlers, the same count the landing page shows.
   Empty when the tally is unavailable, and then it takes no room at all
   rather than leaving a gap in the footer. */
.site-visits {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.2em;
  font-variant-numeric: tabular-nums;
  color: var(--violet-hot);
  text-shadow: 0 0 10px rgba(139, 92, 255, 0.5);
}
.site-visits:empty { display: none; }
.footer-mail {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--violet-hot);
  text-decoration: none;
  transition: color 0.2s var(--ease);
}
.footer-mail:hover { color: #fff; }

/* ── Admin layout (Operate: density and scanability over expression) ── */
.admin-wrap {
  display: grid;
  grid-template-columns: 232px 1fr;
  min-height: 100vh;
  position: relative;
  z-index: 10;
}

.admin-sidebar {
  background: linear-gradient(180deg, rgba(139, 92, 255, 0.07), transparent 38%), var(--bg-subtle);
  border-right: 1px solid var(--line);
  padding: 1.5rem 0;
  display: flex;
  flex-direction: column;
  /* Fills the viewport and stays put while the main column scrolls.
     `align-self: start` alone would shrink it to its content height and
     leave the page background showing below the last nav item. */
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  overflow-y: auto;
}

.admin-sidebar .brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0 1.25rem 1.25rem;
  border-bottom: 1px solid var(--line);
  margin-bottom: 1rem;
  font-family: var(--font-brand);
  font-weight: 900;
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
}

.admin-sidebar .brand img {
  width: 26px; height: auto;
  filter: drop-shadow(0 0 8px rgba(139, 92, 255, 0.5));
}

.admin-nav {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 0 0.75rem;
  flex: 1;
}

.admin-nav a {
  display: block;
  position: relative;
  padding: 0.55rem 0.75rem;
  border-radius: 3px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--ink-dim);
  text-decoration: none;
  transition: background 0.14s var(--ease), color 0.14s var(--ease);
}

.admin-nav a:hover { background: rgba(139, 92, 255, 0.1); color: var(--ink); }
.admin-nav a.active {
  background:
    radial-gradient(circle at 0 100%, rgba(139, 92, 255, 0.3), transparent 62%),
    linear-gradient(90deg, rgba(139, 92, 255, 0.18), rgba(139, 92, 255, 0.03));
  color: #fff;
}

.admin-nav .nav-logout { margin-top: auto; }
.admin-nav .nav-logout a { color: var(--danger); }
.admin-nav .nav-logout a:hover { background: rgba(255, 77, 109, 0.12); color: #fff; }

.admin-main { padding: 2rem 2.25rem 4rem; min-width: 0; }

.admin-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
}

.admin-header h1 {
  font-size: 1.375rem;
  font-weight: 700;
  font-variation-settings: 'wdth' 88;
  letter-spacing: -0.01em;
}

/* ── Tables ───────────────────────────────────────────────────── */
.table-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: 5px; }
.table-scroll .data-table { border: none; }

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.data-table th {
  position: sticky;
  top: 0;
  z-index: 2;
  text-align: left;
  padding: 0.6rem 0.8rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}

.data-table td {
  padding: 0.7rem 0.8rem;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
  color: var(--ink);
}
.data-table tr:last-child td { border-bottom: none; }
.data-table tbody tr { transition: background 0.12s var(--ease); }
.data-table tbody tr:hover td { background: rgba(139, 92, 255, 0.07); }

/* Right-aligned money / measurement column */
.data-table .num { text-align: right; white-space: nowrap; }

/* ── Alerts ───────────────────────────────────────────────────── */
/* Pages place alerts directly above content and rely on this margin;
   dropping it makes an alert collide with whatever follows. */
.alert {
  padding: 0.7rem 1rem;
  margin-bottom: 1rem;
  border-radius: 3px;
  font-size: 0.875rem;
  line-height: 1.55;
}
.alert-error   { background: rgba(255,77,109,0.10);  border: 1px solid rgba(255,77,109,0.38);  color: var(--danger); }
.alert-success { background: rgba(61,220,151,0.10);  border: 1px solid rgba(61,220,151,0.34);  color: var(--ok); }
.alert-info    { background: rgba(139,92,255,0.08);  border: 1px solid var(--line-strong);     color: var(--violet-hot); }

/* ── Empty states ─────────────────────────────────────────────── */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding: 3.5rem 1.5rem;
  text-align: center;
  border: 1px dashed var(--line-strong);
  border-radius: 5px;
  color: var(--ink-faint);
}
.empty svg { opacity: 0.35; }
.empty .empty-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.empty p { font-size: 0.875rem; max-width: 42ch; }

/* ── Responsive ───────────────────────────────────────────────── */
@media (max-width: 860px) {
  .admin-wrap { grid-template-columns: 1fr; }

  .admin-sidebar {
    position: static;
    height: auto;
    overflow: visible;
    border-right: none;
    border-bottom: 1px solid var(--line);
    padding: 0.9rem 0;
  }
  .admin-sidebar .brand { padding-bottom: 0.9rem; margin-bottom: 0.7rem; }

  .admin-nav { flex-direction: row; flex-wrap: wrap; gap: 3px; }
  .admin-nav a { padding: 0.4rem 0.6rem; font-size: 0.75rem; }
  .admin-nav .nav-logout { margin-top: 0; margin-left: auto; }

  .admin-main { padding: 1.25rem 1rem 3rem; }
}

@media (max-width: 720px) {
  .site-header { padding: 0 1rem; height: 56px; }
  .site-nav { gap: 0.1rem; }
  .site-nav a { padding: 0.4rem 0.45rem; font-size: 0.625rem; letter-spacing: 0.1em; }
  .site-nav .nav-cta { margin-left: 0.25rem; }
}

/* Four links plus the wordmark do not share a phone-width bar: the
   wordmark wrapped and pushed the quote button off the edge. The logo
   carries the brand alone here. */
/* Five links at 375px need every pixel the padding and tracking can give. */
@media (max-width: 420px) {
  .site-header { padding: 0 0.75rem; }
  .site-nav { gap: 0; }
  .site-nav a { padding: 0.4rem 0.32rem; letter-spacing: 0.05em; }
  .site-nav .nav-cta { margin-left: 0.15rem; }
}

@media (max-width: 600px) {
  .site-brand span { display: none; }
  .site-brand img { width: 32px; }
  .site-footer { padding: 1.25rem 1rem; justify-content: center; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .hex-layer { display: none; }
  .score::before { transform: scaleX(1) !important; }
}

/* ── Mail reader ───────────────────────────────────────────────────
   The folder rail, the message list and one message. Appended, and
   namespaced under `mail-`, so no existing page's classes change.

   Two things here are load-bearing rather than cosmetic. Every text cell
   carries `min-width: 0` plus a wrap or an ellipsis, because a subject or
   an address from real mail is arbitrary length and would otherwise push
   the whole admin column sideways at phone width. And `.mail-frame` is
   the only place a message's own HTML ever lands. */
.mail-sub { font-size: 0.8125rem; color: var(--ink-dim); margin-top: 0.3rem; }
.mail-switch { display: flex; align-items: flex-end; gap: 0.5rem; }
.mail-switch select { min-width: 15rem; }
.mail-head-acts { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* Visually hidden, still read aloud: the unread and attachment marks are
   color and a glyph, which is not a label. */
.mail-sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.mail-shell {
  display: grid;
  grid-template-columns: 13.5rem minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}

.mail-folders { padding: 1rem 0.9rem; position: sticky; top: 1rem; }
.mail-folders ul { list-style: none; margin-top: 0.7rem; display: flex; flex-direction: column; gap: 1px; }
.mail-folders a {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.45rem 0.6rem;
  border-radius: 3px;
  font-size: 0.8125rem;
  color: var(--ink-dim);
  transition: background 0.14s var(--ease), color 0.14s var(--ease);
}
.mail-folders a:hover { background: rgba(139, 92, 255, 0.1); color: var(--ink); }
.mail-folders a.active { background: rgba(139, 92, 255, 0.14); color: #fff; }
.mail-folder-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-unseen {
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 700;
  padding: 0.1rem 0.4rem;
  border-radius: 2px;
  background: rgba(139, 92, 255, 0.22);
  border: 1px solid rgba(139, 92, 255, 0.4);
  color: var(--violet-hot);
}
.mail-count { flex: none; font-family: var(--font-mono); font-size: 0.625rem; color: var(--ink-faint); }

.mail-pane { min-width: 0; }
.mail-search { display: flex; align-items: flex-end; gap: 0.6rem; flex-wrap: wrap; }
.mail-search .field { flex: 1 1 18rem; min-width: 0; }
.mail-search-acts { display: flex; gap: 0.4rem; }

.mail-list {
  list-style: none;
  margin-top: 0.4rem;
  border: 1px solid var(--line);
  border-radius: 5px;
  overflow: hidden;
}
.mail-list > li + li { border-top: 1px solid var(--line-soft); }

.mail-row {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr) 4.5rem;
  gap: 0.25rem 0.9rem;
  align-items: baseline;
  padding: 0.65rem 0.85rem;
  color: var(--ink-dim);
  background: var(--bg-card);
  transition: background 0.12s var(--ease);
}
.mail-row:hover { background: rgba(139, 92, 255, 0.07); color: var(--ink-dim); }

.mail-row-who {
  display: flex; align-items: baseline; gap: 0.4rem;
  min-width: 0; font-size: 0.8125rem; color: var(--ink-dim);
}
.mail-who-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-dot {
  flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: var(--violet-hot);
  box-shadow: 0 0 7px rgba(183, 148, 255, 0.8);
  align-self: center;
}
.mail-row-body { min-width: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.mail-row-subject {
  display: flex; align-items: center; gap: 0.4rem;
  min-width: 0; font-size: 0.875rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-row-preview {
  font-size: 0.75rem; color: var(--ink-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-clip { flex: none; color: var(--ink-faint); }
.mail-row-when {
  font-family: var(--font-mono); font-size: 0.6875rem;
  color: var(--ink-faint); text-align: right; white-space: nowrap;
}

.mail-row.is-unread .mail-row-subject { font-weight: 700; color: #fff; }
.mail-row.is-unread .mail-row-who { color: var(--ink); font-weight: 600; }

.mail-pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; margin-top: 1rem; flex-wrap: wrap;
}
.mail-pager-at { font-family: var(--font-mono); font-size: 0.6875rem; color: var(--ink-faint); }
.mail-pager [aria-disabled="true"] { pointer-events: none; }

/* ── One message ── */
.mail-msg { max-width: 62rem; }
.mail-subject { font-size: 1.125rem; font-variation-settings: 'wdth' 92; overflow-wrap: anywhere; }

.mail-meta {
  margin-top: 0.9rem; padding: 0.75rem 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 0.25rem;
}
.mail-meta > div { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: 0.75rem; font-size: 0.8125rem; }
.mail-meta dt { color: var(--ink-faint); }
.mail-meta dd { color: var(--ink); overflow-wrap: anywhere; }

.mail-acts { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.9rem; }
.mail-act-form { display: contents; }

.mail-atts { margin-top: 1.1rem; padding-top: 0.9rem; border-top: 1px solid var(--line-soft); }
.mail-atts ul { list-style: none; margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.4rem; }
.mail-atts li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; font-size: 0.8125rem; }
.mail-atts a { overflow-wrap: anywhere; }
.mail-att-meta { font-size: 0.6875rem; color: var(--ink-faint); }

.mail-body {
  margin-top: 1.1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line-soft);
  font-size: 0.9375rem;
  line-height: 1.65;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-width: 76ch;
}

.mail-frame-note { font-size: 0.75rem; color: var(--ink-faint); margin-top: 1.1rem; }
.mail-frame {
  display: block;
  width: 100%;
  height: 32rem;
  margin-top: 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: #fff;
}

/* ── Reply and forward ── */
.mail-compose-card { max-width: 62rem; margin-top: 1.25rem; }
.mail-compose {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.9rem 1.1rem;
  margin-top: 0.7rem;
}
.mail-compose .mail-field-wide, .mail-compose-foot { grid-column: 1 / -1; }
.mail-compose textarea { min-height: 16rem; font-family: var(--font-mono); font-size: 0.8125rem; }
.mail-compose-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; flex-wrap: wrap;
}
.mail-compose-actions { display: flex; gap: 0.5rem; }
.mail-from-note { font-size: 0.75rem; color: var(--ink-faint); line-height: 1.5; max-width: 46ch; }
.mail-from-note strong { color: var(--ink-dim); font-weight: 600; }

@media (max-width: 900px) {
  .mail-shell { grid-template-columns: 1fr; }
  .mail-folders { position: static; }
  .mail-folders ul { flex-direction: row; flex-wrap: wrap; }
  .mail-folders a { gap: 0.4rem; }
}

@media (max-width: 640px) {
  .mail-row { grid-template-columns: minmax(0, 1fr) 4.2rem; }
  .mail-row-who { grid-column: 1; }
  .mail-row-when { grid-column: 2; grid-row: 1; }
  .mail-row-body { grid-column: 1 / -1; }
  .mail-switch select { min-width: 0; width: 100%; }
  .mail-switch { width: 100%; }
  .mail-meta > div { grid-template-columns: 1fr; gap: 0; }
  .mail-compose { grid-template-columns: 1fr; }
  .mail-frame { height: 24rem; }
}

/* ── Portfolio photo editor and batch uploader ─────────────────────
   Appended, and namespaced under `pe-` (editor) and `pu-` (uploader),
   so no existing page's classes change. The faces below are the same
   TTF files api/portfolio_edit.php draws with, declared here so the
   preview canvas and the server cannot diverge on a substituted font;
   they are used only by the editor's canvas, never as page type. */
@font-face { font-family: 'pe-body'; src: url('fonts/Archivo-Regular.ttf') format('truetype');
             font-weight: 400; font-style: normal; font-display: block; }
@font-face { font-family: 'pe-body'; src: url('fonts/Archivo-Bold.ttf') format('truetype');
             font-weight: 700; font-style: normal; font-display: block; }
@font-face { font-family: 'pe-body'; src: url('fonts/Archivo-Italic.ttf') format('truetype');
             font-weight: 400; font-style: italic; font-display: block; }
@font-face { font-family: 'pe-body'; src: url('fonts/Archivo-BoldItalic.ttf') format('truetype');
             font-weight: 700; font-style: italic; font-display: block; }
@font-face { font-family: 'pe-mono'; src: url('fonts/JetBrainsMono-Regular.ttf') format('truetype');
             font-weight: 400; font-style: normal; font-display: block; }
@font-face { font-family: 'pe-mono'; src: url('fonts/JetBrainsMono-Bold.ttf') format('truetype');
             font-weight: 700; font-style: normal; font-display: block; }
@font-face { font-family: 'pe-mono'; src: url('fonts/JetBrainsMono-Italic.ttf') format('truetype');
             font-weight: 400; font-style: italic; font-display: block; }
@font-face { font-family: 'pe-mono'; src: url('fonts/JetBrainsMono-BoldItalic.ttf') format('truetype');
             font-weight: 700; font-style: italic; font-display: block; }
@font-face { font-family: 'pe-display'; src: url('fonts/Orbitron-Regular.ttf') format('truetype');
             font-weight: 400; font-style: normal; font-display: block; }
@font-face { font-family: 'pe-display'; src: url('fonts/Orbitron-Bold.ttf') format('truetype');
             font-weight: 700; font-style: normal; font-display: block; }

.pe-lede { font-size: 0.8125rem; color: var(--ink-dim); max-width: 70ch; line-height: 1.6; margin-bottom: 1rem; }
.pe-lede strong { color: var(--ink); font-weight: 600; }
.pe-flash { margin-bottom: 1rem; }

.pe-shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20.5rem;
  gap: 1.25rem;
  align-items: start;
}
.pe-stage-col { min-width: 0; display: flex; flex-direction: column; gap: 1.25rem; }
.pe-side { min-width: 0; display: flex; flex-direction: column; gap: 1rem; }

.pe-h2 {
  font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.7rem;
}
.pe-note { font-size: 0.75rem; color: var(--ink-faint); line-height: 1.55; margin-top: 0.6rem; }
.pe-note strong { color: var(--ink-dim); font-weight: 600; }
.pe-hint { font-size: 0.75rem; color: var(--ink-faint); margin-top: 0.6rem; }

.pe-stage-card { padding: 0.9rem; }
.pe-toolrow { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.7rem; }
.pe-dims {
  margin-left: auto; font-family: var(--font-mono); font-size: 0.6875rem;
  color: var(--ink-faint); white-space: nowrap;
}
.pe-btnrow { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.pe-btnrow + .pe-btnrow { margin-top: 0.5rem; }
.btn.is-on { border-color: var(--line-strong); background: rgba(139, 92, 255, 0.16); color: #fff; }

/* The picture and its overlays. `touch-action: none` is on this element and
   nowhere wider, so a drag inside it does not scroll the admin column while
   the page itself still scrolls normally with a wheel. */
.pe-stage {
  position: relative;
  display: inline-block;
  max-width: 100%;
  line-height: 0;
  touch-action: none;
  background:
    repeating-conic-gradient(var(--bg-subtle) 0% 25%, var(--bg) 0% 50%) 0 0 / 18px 18px;
  border: 1px solid var(--line);
  border-radius: 4px;
  overflow: hidden;
}
.pe-stage canvas { display: block; max-width: 100%; height: auto; cursor: default; }
.pe-stage[data-tool="crop"] canvas { cursor: crosshair; }
.pe-loading {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 0.75rem; color: var(--ink-faint); line-height: 1.5; text-align: center; padding: 1rem;
}

.pe-cropbox {
  position: absolute;
  border: 1px solid #fff;
  box-shadow: 0 0 0 9999px rgba(7, 4, 16, 0.62);
  cursor: move;
}
.pe-handle {
  position: absolute; width: 14px; height: 14px;
  background: #fff; border: 1px solid var(--violet-deep); border-radius: 2px;
}
.pe-handle[data-corner="nw"] { left: -7px;  top: -7px;    cursor: nwse-resize; }
.pe-handle[data-corner="ne"] { right: -7px; top: -7px;    cursor: nesw-resize; }
.pe-handle[data-corner="se"] { right: -7px; bottom: -7px; cursor: nwse-resize; }
.pe-handle[data-corner="sw"] { left: -7px;  bottom: -7px; cursor: nesw-resize; }

.pe-published { padding: 0.9rem; }
.pe-published img {
  display: block; width: 100%; max-height: 13rem; object-fit: contain;
  border-radius: 4px; background: var(--bg-subtle);
}

.pe-panel { padding: 1rem; }
.pe-slider { display: block; margin-top: 0.8rem; }
.pe-slider .label-text { display: flex; justify-content: space-between; gap: 0.5rem; }
.pe-slider output, .pe-prop output {
  font-family: var(--font-mono); font-size: 0.6875rem; color: var(--ink-dim);
}
.pe-slider input[type="range"], .pe-prop input[type="range"] { width: 100%; }

.pe-link {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--violet-hot); text-decoration: underline; cursor: pointer;
}

.pe-items { list-style: none; margin: 0.8rem 0 0; display: flex; flex-direction: column; gap: 2px; }
.pe-item {
  display: flex; align-items: center; gap: 2px;
  border: 1px solid transparent; border-radius: 4px;
  background: var(--bg-subtle);
}
.pe-item.is-sel { border-color: var(--line-strong); background: rgba(139, 92, 255, 0.12); }
.pe-item-name {
  flex: 1; min-width: 0; text-align: left;
  background: none; border: 0; cursor: pointer;
  padding: 0.45rem 0.55rem; font: inherit; font-size: 0.75rem; color: var(--ink-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pe-item.is-sel .pe-item-name { color: #fff; }
.pe-item-act {
  flex: none; background: none; border: 0; cursor: pointer;
  width: 1.7rem; height: 1.7rem; font-size: 0.75rem; color: var(--ink-faint); border-radius: 3px;
}
.pe-item-act:hover { background: rgba(139, 92, 255, 0.16); color: #fff; }

.pe-props {
  margin-top: 0.9rem; padding-top: 0.9rem; border-top: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 0.7rem;
}
.pe-prop { display: flex; flex-direction: column; gap: 0.3rem; }
.pe-prop-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
.pe-prop textarea { min-height: 3.4rem; font-size: 0.8125rem; }
.pe-prop input[type="color"] { width: 100%; height: 2rem; padding: 2px; }
.pe-check { display: flex; align-items: center; gap: 0.5rem; }
.pe-check .label-text { margin: 0; }

/* ── Batch uploader ── */
.pu-card { margin-bottom: 1.5rem; }
.pu-h2 {
  font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.8rem;
}
.pu-drop {
  display: flex; flex-direction: column; align-items: center; gap: 0.4rem;
  padding: 1.6rem 1rem; text-align: center;
  border: 1px dashed var(--line-strong); border-radius: 6px;
  background: var(--bg-subtle); color: var(--ink-faint);
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease);
}
.pu-drop.is-over { background: rgba(139, 92, 255, 0.12); border-color: var(--violet); }
.pu-drop.has-files { border-style: solid; border-color: var(--line-strong); }
.pu-drop-lead { font-size: 0.875rem; color: var(--ink-dim); }
.pu-note { font-size: 0.75rem; color: var(--ink-faint); line-height: 1.55; max-width: 52ch; }
.pu-link { color: var(--violet-hot); text-decoration: underline; cursor: pointer; }
.pu-link input[type="file"] {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.pu-foot { display: flex; align-items: flex-end; gap: 0.8rem; flex-wrap: wrap; margin-top: 0.9rem; }
.pu-caption { flex: 1 1 16rem; min-width: 0; }
.pu-status {
  margin-top: 0.9rem; font-size: 0.8125rem; color: var(--ink-dim);
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
}
.pu-queue { list-style: none; margin-top: 0.7rem; display: flex; flex-direction: column; gap: 1px; }
.pu-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 7rem minmax(0, 11rem);
  align-items: center; gap: 0.7rem;
  padding: 0.45rem 0.6rem; border-radius: 4px; background: var(--bg-subtle);
  font-size: 0.75rem; color: var(--ink-dim);
}
.pu-row-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pu-row-msg { min-width: 0; text-align: right; overflow-wrap: anywhere; }
.pu-row.is-ok  { color: var(--ok); }
.pu-row.is-bad { color: var(--danger); }
.pu-bar { height: 4px; border-radius: 2px; background: var(--bg-high); overflow: hidden; }
.pu-bar-fill { display: block; height: 100%; width: 0; background: var(--violet); transition: width 0.15s linear; }
.pu-row.is-bad .pu-bar { background: rgba(255, 77, 109, 0.2); }

@media (max-width: 1080px) {
  .pe-shell { grid-template-columns: 1fr; }
  .pe-side { flex-direction: row; flex-wrap: wrap; }
  .pe-side > .pe-panel { flex: 1 1 18rem; }
}

@media (max-width: 640px) {
  .pe-side { flex-direction: column; }
  .pe-side > .pe-panel { flex: 1 1 auto; }
  .pe-stage-card { padding: 0.6rem; }
  .pe-stage { display: block; }
  .pe-dims { margin-left: 0; width: 100%; }
  .pe-published img { max-height: 9rem; }
  .pe-btnrow .btn { flex: 1 1 auto; }
  .pu-drop { padding: 1.1rem 0.7rem; }
  .pu-foot { flex-direction: column; align-items: stretch; }
  .pu-row { grid-template-columns: minmax(0, 1fr) 4rem; }
  .pu-row-msg { grid-column: 1 / -1; text-align: left; }
}

/* ── The work gallery ──────────────────────────────────────────────────
   Shared by the public page and the admin preview, so a piece's size and
   proportions are judged against the same layout a visitor will see.

   Two layouts live here. Without scripting the pieces run as a flexible
   wrapped row whose items are sized from their own aspect ratio, which is
   correct but leaves the last row short. The script then measures the
   gallery, solves the row breaks over the whole sequence and positions
   every piece absolutely; `.qe-gal--js` is what it adds once it has, and
   from that point the container's height is its own.

   `--qe-ar` is each piece's width ÷ height, written inline by the
   renderer. It is the only thing the no-script layout needs to be right. */

.qe-gal {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 1.6rem;
}

.qe-gal__item {
  /* Grow in proportion to width, so pieces on a row end up a common
     height — the no-script approximation of a justified row. */
  flex: var(--qe-ar, 1.333) 1 calc(var(--qe-ar, 1.333) * 13rem);
  position: relative;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg-subtle);
  box-shadow: var(--shadow-plate);
}

/* Absorbs the slack on the final row so the pieces there keep the height
   of the rows above instead of stretching to fill the width. */
.qe-gal__fill {
  flex: 24 1 40%;
  min-width: 0;
}

.qe-gal__link {
  display: block;
  position: relative;
  aspect-ratio: var(--qe-ar, 1.333);
  overflow: hidden;
}

.qe-gal__item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.001);               /* hides the odd half-pixel seam */
  transition: transform 0.7s var(--ease), filter 0.5s var(--ease);
}

/* Once the script has solved the layout it owns every position and size,
   and the aspect ratio has done its job. */
.qe-gal--js { display: block; position: relative; }
.qe-gal--js .qe-gal__item { position: absolute; top: 0; left: 0; }
.qe-gal--js .qe-gal__link { aspect-ratio: auto; width: 100%; height: 100%; }
.qe-gal--js .qe-gal__fill { display: none; }

/* ── The hover read ──
   A single beam crossing the piece, which is the one gesture this brand
   already uses elsewhere, plus a corner trace that fades outward. */
.qe-gal__scan {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background:
    linear-gradient(100deg,
      transparent 42%,
      rgba(183, 148, 255, 0.16) 50%,
      transparent 58%) no-repeat,
    linear-gradient(to top, rgba(139, 92, 255, 0.22), transparent 42%) no-repeat;
  background-size: 260% 100%, 100% 100%;
  background-position: 160% 0, 0 0;
  transition: opacity 0.45s var(--ease), background-position 0.95s var(--ease);
}

.qe-gal__item::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 42%;
  height: 1px;
  background: linear-gradient(to right, var(--violet), transparent);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
}

@media (hover: hover) {
  .qe-gal__item:hover img { transform: scale(1.045); }
  .qe-gal__item:hover .qe-gal__scan,
  .qe-gal__link:focus-visible .qe-gal__scan {
    opacity: 1;
    background-position: -60% 0, 0 0;
  }
  .qe-gal__item:hover::after { opacity: 1; }
}

.qe-gal__link:focus-visible {
  outline: 2px solid var(--violet-hot);
  outline-offset: -2px;
}

/* ── Captions ──
   The script sizes a piece to its picture exactly, so a caption below it
   would overflow. It reads over the foot of the image instead, and stays
   legible on a pale photograph by sitting on its own scrim. */
.qe-gal__cap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 1.4rem 0.7rem 0.5rem;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--ink);
  background: linear-gradient(to top, rgba(7, 4, 16, 0.92), rgba(7, 4, 16, 0.55) 45%, transparent);
  pointer-events: none;
  opacity: 0;
  transform: translateY(0.35rem);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}

@media (hover: hover) {
  .qe-gal__item:hover .qe-gal__cap,
  .qe-gal__link:focus-visible + .qe-gal__cap,
  .qe-gal__item:focus-within .qe-gal__cap { opacity: 1; transform: none; }
}

/* A touch visitor gets no hover, so the caption is simply always there. */
@media (hover: none) {
  .qe-gal__cap { opacity: 1; transform: none; }
}

/* ── The admin preview ──
   The same layout at a smaller base height, with the presentation each
   piece carries labelled on it. Nothing here is clickable. */
.qe-gal--preview { margin-top: 1rem; }
.qe-gal--preview .qe-gal__item { box-shadow: none; }
.qe-gal--preview .qe-gal__cap { opacity: 1; transform: none; font-size: 0.66rem; }
.qe-gal--preview .qe-gal__item img { transition: none; }
.qe-gal--preview .qe-gal__item:hover img { transform: scale(1.001); }

.qe-gal__tags {
  position: absolute;
  top: 0.35rem;
  left: 0.35rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  pointer-events: none;
}

.qe-gal__tag {
  font-family: var(--font-mono);
  font-size: 0.56rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.14rem 0.34rem;
  border-radius: 2px;
  border: 1px solid var(--line-strong);
  background: rgba(7, 4, 16, 0.82);
  color: var(--ink-dim);
}

.qe-gal__tag--featured { color: var(--violet-hot); border-color: var(--violet-dim); }
.qe-gal__tag--large    { color: var(--ink); }
.qe-gal__tag--small    { color: var(--ink-faint); }

/* ── The larger view ───────────────────────────────────────────────────
   A dialog, so the browser handles the focus trap, the Esc key and the
   backdrop for us. */
.qe-lb {
  width: 100vw;
  max-width: none;
  height: 100vh;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  overflow: hidden;
}

.qe-lb::backdrop {
  background: rgba(3, 2, 8, 0.92);
  backdrop-filter: blur(6px);
}

/* The stage is the whole viewport; the picture sits at its center at the
   largest size that shows all of it, with its caption on a slim line
   beneath. Nothing frames it but a hairline and its own shadow. */
.qe-lb__stage {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 3.4rem 4rem 3.6rem;
  box-sizing: border-box;
}

.qe-lb__img {
  display: block;
  max-width: min(100%, 1700px);
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(139, 92, 255, 0.12);
  background: var(--bg-subtle);
}

.qe-lb__close,
.qe-lb__nav {
  position: absolute;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  background: rgba(7, 4, 16, 0.55);
  color: var(--ink-dim);
  cursor: pointer;
  backdrop-filter: blur(4px);
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease),
              background 0.2s var(--ease), transform 0.2s var(--ease);
}

.qe-lb__close:hover,
.qe-lb__nav:hover { color: var(--ink); border-color: var(--violet); background: rgba(139, 92, 255, 0.18); }

.qe-lb__close:focus-visible,
.qe-lb__nav:focus-visible { outline: 2px solid var(--violet-hot); outline-offset: 2px; }

.qe-lb__close {
  top: 1rem;
  right: 1rem;
  width: 2.6rem;
  height: 2.6rem;
  z-index: 2;
}
.qe-lb__close svg { width: 0.95rem; height: 0.95rem; }

.qe-lb__nav {
  top: 50%;
  transform: translateY(-50%);
  width: 2.8rem;
  height: 2.8rem;
}
.qe-lb__nav:hover { transform: translateY(-50%) scale(1.06); }
.qe-lb__nav svg { width: 1.1rem; height: 1.1rem; }
.qe-lb__prev { left: 1rem; }
.qe-lb__next { right: 1rem; }

.qe-lb__foot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.2rem;
  padding: 0 4.5rem;
  background: transparent;
}

.qe-lb__cap {
  margin: 0;
  flex: 0 1 auto;
  min-width: 0;
  font-size: 0.86rem;
  letter-spacing: 0.01em;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.qe-lb__cap[hidden] { display: none; }

.qe-lb__count {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  flex: 0 0 auto;
}

.qe-lb__hint {
  font-size: 0.64rem;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  flex: 0 0 auto;
}

@media (max-width: 720px) {
  .qe-gal { gap: 6px; }
  .qe-gal__item { flex-basis: calc(var(--qe-ar, 1.333) * 9rem); }
  .qe-lb__stage { padding: 3.2rem 0.6rem 3.4rem; }
  .qe-lb__img { max-width: 100%; border-radius: 2px; }
  .qe-lb__hint { display: none; }
  .qe-lb__nav { width: 2.5rem; height: 2.5rem; top: auto; bottom: 0.4rem; transform: none; }
  .qe-lb__nav:hover { transform: none; }
  .qe-lb__prev { left: 0.6rem; }
  .qe-lb__next { right: 0.6rem; }
  .qe-lb__foot { padding: 0 3.4rem; height: 3.3rem; }
  .qe-gal__cap { font-size: 0.68rem; padding: 1.1rem 0.5rem 0.4rem; }
}

@media (prefers-reduced-motion: reduce) {
  .qe-gal__item img,
  .qe-gal__scan,
  .qe-gal__cap,
  .qe-gal__item::after { transition: none; }
  .qe-gal__item:hover img { transform: scale(1.001); }
}

/* ── Presentation choices ──────────────────────────────────────────────
   The owner picks how the gallery is presented from the admin; each choice
   arrives as a class on `.qe-gal` (or a block beside it). Nothing above
   changes: with no choice made, none of these rules apply. */

/* The introduction under the heading. */
.qe-work-intro {
  max-width: 62ch;
  margin: -1.5rem 0 0.25rem;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--ink-dim);
}

/* ── Spacing ── */
.qe-gal--normal { --qe-gap: 10px; }
.qe-gal--tight  { --qe-gap: 4px;  gap: 4px; }
.qe-gal--airy   { --qe-gap: 18px; gap: 18px; }

/* ── Captions ── */
.qe-gal--cap-always .qe-gal__cap { opacity: 1; transform: none; }
.qe-gal--cap-off .qe-gal__cap { display: none; }

/* ── Hover ──
   Lift keeps the piece whole and raises it; still does nothing at all. */
@media (hover: hover) {
  .qe-gal--hover-lift .qe-gal__item:hover img,
  .qe-gal--hover-none .qe-gal__item:hover img { transform: scale(1.001); }
  .qe-gal--hover-lift .qe-gal__item:hover .qe-gal__scan,
  .qe-gal--hover-none .qe-gal__item:hover .qe-gal__scan { opacity: 0; }
  .qe-gal--hover-none .qe-gal__item:hover::after { opacity: 0; }
  .qe-gal--hover-lift .qe-gal__item {
    transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease),
                border-color 0.35s var(--ease);
  }
  .qe-gal--hover-lift .qe-gal__item:hover {
    transform: translateY(-4px);
    border-color: var(--violet-dim);
    box-shadow: var(--shadow-lift), 0 0 26px -6px rgba(139, 92, 255, 0.3);
  }
}

/* ── Uniform grid ──
   Square tiles, cropped to fit; a large piece takes a 2x2 block and the
   dense flow fills the gaps that leaves. The script leaves this alone. */
.qe-gal--grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
  grid-auto-flow: dense;
  gap: var(--qe-gap, 10px);
}
.qe-gal--grid .qe-gal__item { flex: none; aspect-ratio: 1; }
.qe-gal--grid .qe-gal__item[data-size="large"] { grid-column: span 2; grid-row: span 2; }
.qe-gal--grid .qe-gal__link { aspect-ratio: auto; height: 100%; }
.qe-gal--grid .qe-gal__fill { display: none; }

/* ── Filmstrip ──
   One row that scrolls itself, never the page. Pieces share a center line
   and differ in height by their size. */
.qe-strip { position: relative; max-width: 100%; margin-top: 1.6rem; container-type: inline-size; }
.qe-gal--filmstrip {
  --qe-h: clamp(220px, 30cqw, 400px);
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--qe-gap, 10px);
  margin-top: 0;
  padding: 0.4rem 0 0.9rem;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  overscroll-behavior-x: contain;
}
.qe-gal--filmstrip .qe-gal__item {
  flex: 0 0 auto;
  height: calc(var(--qe-h) * 0.82);
  width: calc(var(--qe-ar, 1.333) * var(--qe-h) * 0.82);
  max-width: 100%;
  scroll-snap-align: start;
}
.qe-gal--filmstrip .qe-gal__item[data-size="large"] {
  height: var(--qe-h);
  width: calc(var(--qe-ar, 1.333) * var(--qe-h));
}
.qe-gal--filmstrip .qe-gal__item[data-size="small"] {
  height: calc(var(--qe-h) * 0.6);
  width: calc(var(--qe-ar, 1.333) * var(--qe-h) * 0.6);
}
.qe-gal--filmstrip .qe-gal__link { aspect-ratio: auto; height: 100%; }
.qe-gal--filmstrip .qe-gal__fill { display: none; }

.qe-strip__arrow {
  position: absolute;
  top: calc(50% - 0.25rem);
  transform: translateY(-50%);
  z-index: 2;
  width: 2.4rem;
  height: 3.4rem;
  padding: 0;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  background: rgba(7, 4, 16, 0.78);
  color: var(--ink-dim);
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease),
              background 0.2s var(--ease), opacity 0.2s var(--ease);
}
.qe-strip__arrow svg { width: 1rem; height: 1rem; }
.qe-strip__arrow--prev { left: 0.5rem; }
.qe-strip__arrow--next { right: 0.5rem; }
.qe-strip__arrow:hover { color: var(--ink); border-color: var(--violet); background: var(--bg-high); }
.qe-strip__arrow:disabled { opacity: 0.3; cursor: default; }
.qe-strip__arrow:focus-visible { outline: 2px solid var(--violet-hot); outline-offset: 2px; }
.qe-strip--fits .qe-strip__arrow { display: none; }
.qe-gal--preview.qe-gal--filmstrip { padding-bottom: 0.4rem; }

/* ── Spotlight ──
   A band above the gallery. The picture is shown whole, against a blurred
   copy of itself so a portrait fills the band as well as a landscape, and
   the slides crossfade slowly. The bottom-left trace is the brand gesture. */
.qe-spot {
  position: relative;
  margin-top: 1.6rem;
  border: 1px solid var(--line);
  border-radius: 5px;
  overflow: hidden;
  background: var(--bg-subtle);
  box-shadow: var(--shadow-plate);
}
.qe-spot__stage {
  position: relative;
  aspect-ratio: 2.2;
  min-height: 260px;
  max-height: 600px;
  overflow: hidden;
  background: var(--bg);
}
.qe-spot__stage::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 3;
  width: 34%;
  height: 1px;
  background: linear-gradient(to right, var(--violet-hot), transparent);
  opacity: 0.8;
  pointer-events: none;
}
.qe-spot__slide {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.6s var(--ease);
}
.qe-spot__slide.is-on { opacity: 1; pointer-events: auto; z-index: 1; }
.qe-spot__bg {
  position: absolute;
  inset: -8%;
  width: 116%;
  height: 116%;
  object-fit: cover;
  filter: blur(28px) saturate(1.15) brightness(0.5);
}
.qe-spot__link {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
  color: inherit;
}
.qe-spot__link:focus-visible { outline: 2px solid var(--violet-hot); outline-offset: -4px; }
.qe-spot__img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 3px;
  box-shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.9);
  transition: transform 0.9s var(--ease);
}
@media (hover: hover) {
  .qe-spot__link:hover .qe-spot__img { transform: scale(1.015); }
}
.qe-spot__cap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  margin: 0;
  padding: 2.2rem 1.25rem 0.9rem;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--ink);
  background: linear-gradient(to top, rgba(7, 4, 16, 0.9), rgba(7, 4, 16, 0.5) 50%, transparent);
  pointer-events: none;
}
.qe-spot__bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.6rem;
  border-top: 1px solid var(--line);
  background: var(--bg-card);
}
.qe-spot__btn {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  padding: 0;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  background: rgba(7, 4, 16, 0.6);
  color: var(--ink-dim);
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.qe-spot__btn svg { width: 0.9rem; height: 0.9rem; }
.qe-spot__btn:hover { color: var(--ink); border-color: var(--violet); background: var(--bg-high); }
.qe-spot__btn:focus-visible { outline: 2px solid var(--violet-hot); outline-offset: 2px; }
.qe-spot__btn[hidden] { display: none; }
.qe-spot__ico-play { display: none; }
.qe-spot--paused .qe-spot__ico-play { display: block; }
.qe-spot--paused .qe-spot__ico-pause { display: none; }
.qe-spot__dots {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  margin: 0 auto;
  min-width: 0;
}
.qe-spot__dot {
  width: 1.6rem;
  height: 3px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: var(--line-strong);
  cursor: pointer;
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.qe-spot__dot:hover { background: var(--violet-dim); }
.qe-spot__dot.is-on { background: var(--violet-hot); box-shadow: 0 0 8px var(--violet-dim); }
.qe-spot__dot:focus-visible { outline: 1px solid var(--violet-hot); outline-offset: 3px; }
.qe-spot--preview { margin-top: 1rem; box-shadow: none; }
.qe-spot--preview .qe-spot__img { transition: none; }

@media (max-width: 720px) {
  .qe-gal--normal { --qe-gap: 6px; }
  .qe-gal--tight  { --qe-gap: 3px;  gap: 3px; }
  .qe-gal--airy   { --qe-gap: 12px; gap: 12px; }
  .qe-gal--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .qe-strip__arrow { width: 2rem; height: 2.8rem; }
  .qe-spot__stage { aspect-ratio: 4 / 3; min-height: 0; }
  .qe-spot__link { padding: 0.6rem; }
  .qe-spot__cap { font-size: 0.75rem; padding: 1.6rem 0.8rem 0.6rem; }
  .qe-spot__dot { width: 1.1rem; }
  .qe-work-intro { margin-top: -0.75rem; font-size: 0.9375rem; }
}

@media (prefers-reduced-motion: reduce) {
  .qe-spot__slide, .qe-spot__img, .qe-gal--hover-lift .qe-gal__item { transition: none; }
  .qe-spot__link:hover .qe-spot__img { transform: none; }
  .qe-gal--hover-lift .qe-gal__item:hover { transform: none; }
}

/* The account option beside the quote form: a lit panel so it reads as the
   one place to act, with a violet glow drawing the eye to its edge. */
.quote-acct { border: 1px solid var(--violet); border-radius: 6px;
  padding: 0.9rem 1.05rem; background: rgba(139, 92, 255, 0.12);
  box-shadow: var(--glow-violet), 0 0 26px -4px rgba(139, 92, 255, 0.5);
  margin-bottom: 0.9rem; }
.quote-acct .btn { margin: 0.2rem 0.35rem 0 0; }

/* ── Laser trace and entry transition ── */

/* The account panel draws its own violet outline the first time it is seen,
   then its glow rises. Armed by JS so a no-JS panel keeps its static border. */
.quote-acct { position: relative; }
.quote-acct.qe-arm { border-color: transparent; box-shadow: none; transition: box-shadow .6s ease 1s; }
.quote-acct:not(.qe-arm) .qe-trace { display: none; }
.qe-trace { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.qe-trace rect { fill: none; vector-effect: non-scaling-stroke; }
.qe-trace__line { stroke: var(--violet); stroke-width: 1.3; stroke-dasharray: 100; stroke-dashoffset: 100; }
.qe-trace__beam { stroke: #e7dcff; stroke-width: 2.2; stroke-dasharray: 2.5 97.5; stroke-dashoffset: 100;
  opacity: 0; filter: drop-shadow(0 0 3px var(--violet-hot)); }
.quote-acct.qe-arm.is-lit .qe-trace__line { animation: qe-draw 1.15s ease forwards; }
.quote-acct.qe-arm.is-lit .qe-trace__beam { opacity: 1; animation: qe-draw 1.15s ease forwards, qe-fade-out .3s ease 1.1s forwards; }
.quote-acct.qe-arm.is-lit { box-shadow: var(--glow-violet), 0 0 26px -4px rgba(139, 92, 255, 0.5); }
@keyframes qe-draw { to { stroke-dashoffset: 0; } }
@keyframes qe-fade-out { to { opacity: 0; } }
@keyframes qe-glow-in { to { border-color: var(--violet);
  box-shadow: var(--glow-violet), 0 0 26px -4px rgba(139, 92, 255, 0.5); } }

/* Entering the site: the landing fades out, then the main page is traced
   shape by shape — like a laser following each outline — behind a dark veil
   that lifts once the shapes are drawn. */
body.qe-leaving { animation: qe-leave .6s ease forwards; }
@keyframes qe-leave { to { opacity: 0; filter: brightness(1.35); } }
html.qe-cutting .showcase-wrap,
html.qe-cutting .site-header { opacity: 0; }
#qe-cut { position: fixed; inset: 0; z-index: 9999; pointer-events: none;
  background: radial-gradient(120% 90% at 50% 8%, rgba(22, 11, 44, 0.98), rgba(4, 2, 10, 0.99)); }
#qe-cut svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
#qe-cut rect { fill: none; stroke: var(--violet-hot); stroke-width: 1.6; stroke-linecap: round;
  filter: drop-shadow(0 0 4px var(--violet)); }
#qe-cut.qe-cut-done { opacity: 0; transition: opacity .5s ease; }

/* Jumping to a section from the header: the page blurs under the header,
   glides to the section, and the laser traces the outlines on screen.
   The blur layer fades itself; an ancestor with opacity would cut its
   backdrop off from the page. */
.qe-jump { position: fixed; left: 0; right: 0; bottom: 0; z-index: 90; pointer-events: none; }
.qe-jump__blur {
  position: absolute; inset: 0;
  background: rgba(7, 4, 16, 0.32);
  -webkit-backdrop-filter: blur(12px) saturate(0.8);
          backdrop-filter: blur(12px) saturate(0.8);
  opacity: 0; transition: opacity .16s ease;
}
.qe-jump.is-on .qe-jump__blur { opacity: 1; }
.qe-jump.is-on .qe-jump__blur { transition-duration: .16s; }
.qe-jump .qe-jump__blur { transition: opacity .3s ease; }
/* Outlines are drawn in viewport coordinates, header included. */
.qe-jump svg { position: fixed; inset: 0; width: 100%; height: 100%; overflow: visible; transition: opacity .4s ease; }
.qe-jump svg.is-done { opacity: 0; }
.qe-jump rect { fill: none; stroke: var(--violet-hot); stroke-width: 1.6; stroke-linecap: round;
  filter: drop-shadow(0 0 4px var(--violet)); }

@media (prefers-reduced-motion: reduce) {
  body.qe-leaving { animation: none; opacity: 0; }
  html.qe-cutting .showcase-wrap, html.qe-cutting .site-header { opacity: 1; }
  #qe-cut { display: none; }
  .qe-jump { display: none; }
  .quote-acct.qe-arm { border-color: var(--violet);
    box-shadow: var(--glow-violet), 0 0 26px -4px rgba(139, 92, 255, 0.5); }
  .quote-acct.qe-arm .qe-trace { display: none; }
  .quote-acct.qe-arm.is-lit .qe-trace__line,
  .quote-acct.qe-arm.is-lit .qe-trace__beam,
  .quote-acct.qe-arm.is-lit { animation: none; }
}
