/* ============================================================================
   ZOLTRAK — zoltrak.css
   Module CSS. Loaded AFTER knull.css / knull-effects.css / shell.css.

   HOUSE RULES for anything added to this file:
     · Never redefine a .kn-* class from knull.css. Reuse it.
     · Neutrals follow the live gray ramp (--kn-white/var(--z-muted) … --kn-faint).
       Gold and the neon green/red are the only bright things on screen.
     · Every section is wrapped in its own "=== NAME ===" comment banner.
       APPEND your section — never edit or overwrite someone else's.
   ============================================================================ */


/* ══════════════════════════════════════════════════════════════════════════
   === LAYOUT ENGINE ===
   Owned by modules/layout.js. Draggable / resizable 12-column widget board.

   Geometry note: widgets are position:absolute and moved with transform, so
   the "grid" is maths, not CSS Grid — that is what keeps a drag on the
   compositor instead of in layout. JS writes --z-cell-x / --z-cell-y onto the
   container so the edit-mode guide can draw the real cell size.

   Stacking inside .z-grid:   guide ::before(0) · ghost(1) · widgets(2) ·
                              the widget being dragged(30)
   ══════════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────── the board ──────────────────────────────── */
.z-grid {
  position: relative;
  width: 100%;
  min-height: 2px;
}

/* edit-mode grid guide — hairlines on the real cell pitch */
/* Alpha is deliberately tiny: the .kn-box face is translucent, so anything
   stronger than this bleeds THROUGH the cards instead of only filling the
   negative space. It reads clearly on bare marble and vanishes under a card. */
.z-grid::before {
  content: "";
  position: absolute; inset: 0 0 auto; z-index: 0;
  height: calc(100% + var(--z-cell-y, 98px));   /* one spare row = "drop here" */
  pointer-events: none; border-radius: 14px;
  background-image:
    repeating-linear-gradient(90deg,
      rgba(232, 189, 120, .085) 0 1px, transparent 1px var(--z-cell-x, 80px)),
    repeating-linear-gradient(180deg,
      rgba(232, 189, 120, .05) 0 1px, transparent 1px var(--z-cell-y, 98px));
  -webkit-mask-image: linear-gradient(180deg, #000 0 92%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0 92%, transparent 100%);
  opacity: 0;
  transition: opacity .3s ease;
}
.z-grid.is-edit:not(.is-narrow)::before { opacity: 1; }

/* ───────────────────────────── the widget ─────────────────────────────── */
.z-grid .z-w {
  /* TOP carries the vertical nudge; the transform carries grid placement and
     nothing else. Both the hover lift and the staggered entrance move top.

     This replaced a --z-rise custom property that layout.js composed into the
     inline transform as a trailing translateY. Hovering a tile animated that
     property, which animated the transform, which promoted the tile to its own
     compositing layer and cost its text subpixel antialiasing until the lift
     settled — i.e. every board tile visibly blurred on hover. It is the same
     trap the "No will-change here" note below describes, arriving by a
     different route.

     The property is gone rather than repointed: measured in the browser, a
     transition on a registered custom property does NOT re-resolve the var()
     that consumes it, so "top: var(--z-rise)" simply never moved. Plain top
     transitions correctly and cannot build a layer. */
  position: absolute; left: 0; top: 0; z-index: 2;
  display: flex; flex-direction: column;
  /* No will-change here. It pinned every panel to its own compositing layer
     for the whole session, not just while it moved, and a panel that never
     leaves its layer never gets subpixel-antialiased text back. The drag loop
     sets it on the one panel being dragged and clears it on drop. */
  transition:
    transform .3s cubic-bezier(.2, .85, .3, 1),
    width     .3s cubic-bezier(.2, .85, .3, 1),
    height    .3s cubic-bezier(.2, .85, .3, 1),
    box-shadow .25s ease;
}

/* header — the drag handle */
.z-w-head {
  flex: none; display: flex; align-items: center; gap:8px;
  height: 44px; padding: 0 14px;
  border-bottom: 1px solid var(--kn-line);
  -webkit-user-select: none; user-select: none;
}
.z-w-heading { min-width: 0; display: flex; flex-direction: column; gap:2px; }
.z-w-title {
  margin: 0; font-size:13px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--kn-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color .18s ease;
}
.z-w:hover .z-w-title { color: var(--kn-text); }
.z-w-sub { margin: 0; line-height: 1.1; }
.z-w-right { margin-left: auto; flex: none; display: flex; align-items: center; gap:4px; }

/* six-dot grip: costs no width until edit mode opens */
.z-w-grip {
  flex: none; width: 0; opacity: 0; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  color:var(--kn-dim);
  transition: width .22s ease, opacity .22s ease, color .18s ease;
}
.z-grid.is-edit:not(.is-narrow) .z-w-grip { width: 13px; opacity: .8; }
.z-grid.is-edit:not(.is-narrow) .z-w:hover .z-w-grip { color:var(--kn-muted); opacity: 1; }

/* body
   overscroll-behavior stays AUTO on purpose. With `contain`, putting the
   cursor over any widget swallowed the wheel — the page underneath refused to
   move, which made the whole dashboard feel stuck. Auto lets a widget scroll
   its own overflow first and then hand the rest of the gesture to the page. */
.z-w-body {
  flex: 1; min-height: 0;
  padding:12px 12px 12px;
  overflow: auto; overscroll-behavior: auto;
}
.z-w-body.flush { padding: 0; }
.z-w-body.noscroll { overflow: hidden; }
.z-w-err { padding: 4px 0; }
.z-w-err p { margin: 0 0 4px; font-size:14px; color: var(--kn-red); }
.z-w-err p + p { color: var(--kn-faint); }

/* ─────────────────────────── edit mode chrome ─────────────────────────── */
.z-grid.is-edit:not(.is-narrow) .z-w-head { cursor: grab; touch-action: none; }
.z-grid.is-edit:not(.is-narrow) .z-w:hover { --ring: var(--kn-ring-active); }
.z-grid.is-edit:not(.is-narrow) .z-w-head:focus-visible {
  outline: none; box-shadow: inset 0 0 0 1px rgba(232, 189, 120, .55);
}

/* gold corner chevron — only exists while the board is unlocked */
.z-w-resize {
  position: absolute; right: 0; bottom: 0; z-index: 4;
  width: 27px; height: 27px; padding: 0 3px 3px 0;
  display: flex; align-items: flex-end; justify-content: flex-end;
  border: 0; background: none; color:var(--kn-dim);
  cursor: nwse-resize; touch-action: none;
  opacity: 0; pointer-events: none;
  transform: translate(4px, 4px) scale(.8); transform-origin: 100% 100%;
  transition: opacity .2s ease, transform .22s cubic-bezier(.2, .85, .3, 1),
              color .18s ease, filter .18s ease;
}
.z-grid.is-edit:not(.is-narrow) .z-w-resize {
  opacity: .62; pointer-events: auto; transform: none;
}
.z-grid.is-edit:not(.is-narrow) .z-w:hover .z-w-resize,
.z-grid.is-edit:not(.is-narrow) .z-w-resize:focus-visible {
  opacity: 1; outline: none;
  color:var(--kn-muted);
  filter: drop-shadow(0 0 6px rgba(232, 189, 120, .55));
}

/* ───────────────────────── live gesture states ────────────────────────── */
.z-w.is-dragging,
.z-w.is-resizing {
  transition: none !important;            /* the pointer is the animation */
  z-index: 30;
  --ring: var(--kn-ring-active);
  box-shadow: 0 26px 62px -22px #000, 0 0 42px -10px rgba(232, 189, 120, .45);
}
.z-w.is-dragging { cursor: grabbing; }
.z-w.is-dragging .z-w-head { cursor: grabbing; }
.z-grid.is-active .z-w-body { pointer-events: none; }  /* no hover noise mid-drag */

/* the drop target: dashed gold outline + faint wash */
.z-ghost {
  position: absolute; left: 0; top: 0; z-index: 1;
  width: 0; height: 0; pointer-events: none;
  border-radius: var(--kn-r-card);
  border: 1px dashed rgba(232, 189, 120, .45);
  background: linear-gradient(180deg, rgba(232, 189, 120, .06), rgba(232, 189, 120, .018));
  box-shadow: inset 0 0 36px -14px rgba(232, 189, 120, .65);
  opacity: 0;
  transition: transform .18s cubic-bezier(.2, .85, .3, 1),
              width .18s cubic-bezier(.2, .85, .3, 1),
              height .18s cubic-bezier(.2, .85, .3, 1),
              opacity .16s ease;
}
.z-ghost.on { opacity: 1; }

/* one soft gold flare where the card lands */
.z-w.z-drop { animation: z-drop .6s ease both; }
@keyframes z-drop {
  from { box-shadow: 0 0 36px -6px rgba(232, 189, 120, .55), 0 12px 30px -24px #000; }
  to   { box-shadow: var(--kn-glow-card); }
}

/* kill text selection everywhere while a gesture is running */
html.z-no-select, html.z-no-select * {
  -webkit-user-select: none !important; user-select: none !important;
}

/* ───────────────────────── entrance (one shot) ────────────────────────── */
/* widgets rise in 55ms apart. Transitions are off until the last one lands so
   the animation and the transition never fight over transform.              */
.z-grid.is-booting .z-w {
  transition: none !important;
  /* backwards, NOT both: "both" pins the end state forever, and a filled
     animation outranks a plain declaration — so the hover lift below would
     have been silently overridden for the life of the page. */
  animation: z-w-in .5s cubic-bezier(.2, .8, .2, 1) backwards;
  animation-delay: calc(var(--i, 0) * 55ms);
}
@keyframes z-w-in {
  from { opacity: 0; top: 16px; }
  to   { opacity: 1; top: 0; }
}

/* ─────────────────────── the Edit layout / Reset pair ─────────────────── */
.z-lay-ctl { display: inline-flex; align-items: center; gap:4px; }
/* .kn-btn sets display:inline-flex, which out-specifies the UA [hidden] rule —
   without this the "Reset" button shows while the board is locked */
.z-lay-ctl [hidden] { display: none !important; }
.z-lay-btn.on {
  --ring: var(--kn-ring-active);
  color:var(--kn-muted);
  box-shadow: var(--kn-glow-active);
}
.z-lay-btn.on svg { color:var(--kn-muted); }
.z-lay-empty { margin-top: 4px; }

/* ───────────────────────────── responsive ─────────────────────────────── */
/* Under 900px the JS stacks every widget into one column and refuses to drag;
   these rules just make sure no edit affordance is left showing.            */
.z-grid.is-narrow .z-w-resize,
.z-grid.is-narrow .z-w-grip { display: none; }
.z-grid.is-narrow .z-w-head { cursor: default; }

@media (prefers-reduced-motion: reduce) {
  .z-grid .z-w, .z-ghost, .z-grid::before {
    transition: none !important; animation: none !important;
  }
  .z-w.z-drop { animation: none !important; }
}

/* a bare widget shows its header only while the board is unlocked — the header
   is the drag handle, but an empty one above real content is just a gap */
.z-grid .z-w.is-bare > .z-w-head { display: none; }
/* ── THE DRAG STRIP FLOATS; IT DOES NOT PUSH ──────────────────────────────
   A bare widget has no header until you enter edit mode, and the header this
   rule switched on was 44px of ordinary flow -- so every bare tile LOST 44px
   of body the moment you pressed Edit layout. On the profile card, which is
   94px tall, that is half the tile: the identity block was shoved out of view
   and the top of the user's own name was cut through the middle. It read as
   "edit layout breaks the profile", and it was never about the profile -- any
   bare tile short enough would do the same.

   Overlaid instead. The tile keeps every pixel of its body in both modes, and
   the strip you grab sits on top of it under a short fade so the content
   beneath stays legible. Shorter too: it holds a grip and a title, not a row
   of controls. */
.z-grid.is-edit:not(.is-narrow) .z-w.is-bare > .z-w-head {
  display: flex; position: absolute; left: 0; right: 0; top: 0; z-index: 4;
  height: 30px; padding: 0 10px; border-bottom: 0;
  background: linear-gradient(180deg, rgba(3,2,8,.86) 0%, rgba(3,2,8,.55) 62%,
              rgba(3,2,8,0) 100%);
  border-radius: inherit; border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}
