/* ============================================================================
   The Sentinel — a small serpent-dragon coiled into the ouroboros, because
   that is what the audit trail is: entries hash-chained nose-to-tail.
   Pure SVG + CSS; no images, honours prefers-reduced-motion.
   ============================================================================ */

.sentinel {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 64px;
  height: 64px;
  z-index: 900;
  cursor: pointer;
  --spin: 12s;
  --ring: var(--teal, #5cf2ff);
  --glow: color-mix(in srgb, var(--ring) 50%, transparent);
  transition: opacity .5s ease, transform .3s cubic-bezier(.34,1.56,.64,1);
  filter: drop-shadow(0 4px 14px rgba(0,0,0,.55));
}
.sentinel.mini { width: 36px; height: 36px; right: 16px; bottom: 16px; }
.sentinel.asleep { opacity: .26; }
.sentinel:hover { transform: scale(1.07); }
.sentinel:focus-visible { outline: 2px solid var(--ring); outline-offset: 5px; border-radius: 50%; }
.sentinel svg { width: 100%; height: 100%; display: block; overflow: visible; }

/* The whole coil turns — the dragon forever a snout behind its own tail.
   Counter-clockwise, because that is the way the head faces: an animal
   swims head-first, and one that circles backwards reads as broken. */
.sn-spin { transform-origin: 50% 50%; animation: sn-turn var(--spin) linear infinite; }
@keyframes sn-turn { to { transform: rotate(-360deg); } }

/* ---- the body ------------------------------------------------------------
   Each scale breathes slightly out of phase with its neighbour, so a slow
   ripple travels down the body: the slither. */
.sn-seg {
  fill: url(#sn-grad);
  opacity: .88;
  transform-box: fill-box;
  transform-origin: center;
  animation: sn-slither 3s ease-in-out infinite;
  animation-delay: calc(var(--i) * -0.12s);
  transition: opacity .3s ease, fill .4s ease;
}
@keyframes sn-slither {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.13); }
}
.sn-spine, .sn-tail { fill: url(#sn-grad); opacity: .68; transition: fill .4s ease; }

/* ---- the face -------------------------------------------------------------
   Drawn to read at 62 pixels: skull, brow, one bright eye, tiny horns. */
.sn-skull { fill: url(#sn-grad); transition: fill .4s ease; }
.sn-horn { fill: url(#sn-grad); opacity: .8; }
.sn-horn2 { opacity: .5; }
.sn-mouth { fill: none; stroke: var(--bg-0, #030407); stroke-width: .55;
  stroke-linecap: round; opacity: .55; }
.sn-nostril { fill: var(--bg-0, #030407); opacity: .55; }
.sn-eyeball { fill: #eef6ff; }
.sn-pupil { fill: var(--bg-0, #030407); transition: rx .25s ease, ry .25s ease; }
/* The lid: skull-coloured, closed = scaleY 1. Blinks now and then. */
.sn-lid {
  fill: #6d59d6;
  transform-box: fill-box;
  transform-origin: center top;
  transform: scaleY(0);
  animation: sn-blink 6.4s ease-in-out infinite;
}
@keyframes sn-blink {
  0%, 93.5%, 100% { transform: scaleY(0); }
  95.5%, 97%      { transform: scaleY(1); }
}
/* The tongue flicks across the gap toward the tail, then thinks better. */
.sn-tongue {
  fill: none; stroke: #ff7a9b; stroke-width: .6; stroke-linecap: round;
  transform-box: fill-box; transform-origin: left center;
  transform: scaleX(0);
  animation: sn-flick 8s ease-out infinite;
}
@keyframes sn-flick {
  0%, 88%, 100% { transform: scaleX(0); }
  91%, 94%      { transform: scaleX(1); }
}

/* Asleep: the eye closes and the body settles. */
.sentinel.asleep .sn-lid { animation: none; transform: scaleY(1); }
.sentinel.asleep .sn-seg { animation-play-state: paused; }
.sentinel.asleep .sn-tongue { animation: none; }

/* ---- a new entry: a ripple runs down the back ---------------------------- */
.sentinel.running .sn-seg {
  animation: sn-slither 3s ease-in-out infinite,
             sn-relay 1.5s ease-out 1;
  animation-delay: calc(var(--i) * -0.12s), calc(var(--i) * 0.05s);
}
@keyframes sn-relay {
  0%   { opacity: .88; }
  22%  { opacity: 1; filter: drop-shadow(0 0 4px var(--glow)); }
  100% { opacity: .88; }
}

/* ---- verify: light runs scale to scale, the whole body checked ----------- */
.sentinel.verifying .sn-spin { animation-duration: 4s; }
.sentinel.verifying .sn-seg {
  animation: sn-verify 1.6s ease-out 1;
  animation-delay: calc(var(--i) * 0.055s);
}
@keyframes sn-verify {
  0%   { opacity: .5; }
  35%  { opacity: 1; fill: #fff; filter: drop-shadow(0 0 6px var(--glow)); }
  100% { opacity: .88; }
}
.sentinel.sealed { animation: sn-seal 1.8s ease-out 1; }
.sentinel.sealed .sn-seg { opacity: 1; }
/* Content: the eye closes softly while the glow settles. */
.sentinel.sealed .sn-lid { animation: none; transform: scaleY(.85); }
@keyframes sn-seal {
  0%   { filter: drop-shadow(0 0 0 transparent); }
  35%  { filter: drop-shadow(0 0 18px var(--glow)); }
  100% { filter: drop-shadow(0 4px 14px rgba(0,0,0,.55)); }
}

/* ---- attention ------------------------------------------------------------
   Amber: alert — wide eye, raised spines. Red: alarm, and it pulses. */
.sentinel.warn { --ring: var(--abort, #ffb44c); }
.sentinel.crit { --ring: var(--block, #ff3b5c); }
.sentinel.warn .sn-seg, .sentinel.crit .sn-seg,
.sentinel.warn .sn-skull, .sentinel.crit .sn-skull,
.sentinel.warn .sn-horn, .sentinel.crit .sn-horn,
.sentinel.warn .sn-spine, .sentinel.crit .sn-spine,
.sentinel.warn .sn-tail, .sentinel.crit .sn-tail { fill: var(--ring); }
.sentinel.warn .sn-lid, .sentinel.crit .sn-lid { fill: var(--ring); animation: none; transform: scaleY(0); }
.sentinel.warn .sn-pupil, .sentinel.crit .sn-pupil { rx: .55; ry: .8; }
.sentinel.warn .sn-spine, .sentinel.crit .sn-spine { opacity: 1; }
.sentinel.crit { animation: sn-alarm 1.2s ease-in-out infinite; }
@keyframes sn-alarm {
  0%, 100% { filter: drop-shadow(0 4px 14px rgba(0,0,0,.55)); }
  50% { filter: drop-shadow(0 0 14px var(--glow)) drop-shadow(0 4px 14px rgba(0,0,0,.55)); }
}

/* ---- broken ---------------------------------------------------------------
   The coil stops, reddens, and the body visibly parts mid-back — a gap
   you can see across the room. That gap IS the finding. */
.sentinel.broken .sn-spin { animation: none; }
.sentinel.broken .sn-seg { animation: none; fill: var(--block, #ff3b5c); opacity: .5; }
.sentinel.broken .sn-skull, .sentinel.broken .sn-horn,
.sentinel.broken .sn-spine, .sentinel.broken .sn-tail { fill: var(--block, #ff3b5c); }
.sentinel.broken .sn-seg[data-i="18"],
.sentinel.broken .sn-seg[data-i="19"],
.sentinel.broken .sn-seg[data-i="20"] { opacity: 0; }
.sentinel.broken .sn-seg[data-i="17"],
.sentinel.broken .sn-seg[data-i="21"] { opacity: 1; }
.sentinel.broken .sn-lid { animation: none; transform: scaleY(0); }
.sentinel.broken .sn-tongue { animation: none; }

/* ---- the line it speaks --------------------------------------------------- */
.sn-say {
  position: fixed;
  right: 94px;
  bottom: 26px;
  z-index: 900;
  max-width: 300px;
  padding: 10px 13px;
  border-radius: 12px 12px 4px 12px;
  background: var(--bg-2, #0e1a24);
  color: var(--ink, #eaf1fb);
  border: 1px solid color-mix(in srgb, var(--ring) 40%, var(--line, #22303c));
  box-shadow: 0 12px 34px -10px rgba(0,0,0,.65);
  font: 13px/1.45 var(--font-sans, system-ui, sans-serif);
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  transition: opacity .3s ease, transform .3s ease;
}
.sn-say.show { opacity: 1; transform: translateY(0); pointer-events: auto; }
.sn-say b { color: var(--ring); font-weight: 600; }
.sn-say .sn-act {
  display: inline-block;
  margin-top: 7px;
  font-size: .78rem;
  color: var(--ring);
  background: color-mix(in srgb, var(--ring) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--ring) 35%, transparent);
  border-radius: 7px;
  padding: 3px 9px;
  cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
  .sn-spin, .sn-seg, .sn-lid, .sn-tongue,
  .sentinel.running .sn-seg, .sentinel.verifying .sn-seg,
  .sentinel.crit, .sentinel.sealed { animation: none; }
  .sn-say { transition: opacity .2s ease; transform: none; }
}
@media (max-width: 560px) {
  .sentinel { width: 48px; height: 48px; right: 12px; bottom: 12px; }
  .sn-say { right: 68px; bottom: 18px; max-width: 60vw; }
}
