/* css/17-lifecycle.css — lifecycle chip, SLA countdown, ESP beat.
   Split from the index.html <style> block (REFACTOR-PLAN Phase 2). Load order = cascade order; do not reorder. */
/* =====================================================================
   LIFECYCLE (wave20) — the status chip, the live SLA countdown and the
   'Assigning to ESP...' beat all live on the ONE card's 44px foot row.
   ===================================================================== */
.tabs{gap:4px}
/* four chips in a 448px panel: the gap tightens rather than the type, which
   stays at the 14px body floor, and "In progress" — the widest label — takes a
   proportionally wider share so nothing is clipped. */
.tab.prog{flex:1.4}
.tab.draft,.tab.done{flex:.85}
/* the ESP variant's blue left edge has to beat .card.inc-card's severity edge */
.card.inc-card.esp{border-left-color:#2E90FA}
/* the lifecycle colour is carried by the card's own status LINE (dot + text),
   so the 44 px foot row is free to say the one thing that is actionable. */
.card.lc .status{color:var(--lc,#2E90FA)}
.card.lc .status>i{background:var(--lc,#2E90FA)}
.card.lc .statusbar{color:var(--lc,#2E90FA);
  border-color:color-mix(in srgb,var(--lc,#2E90FA) 36%,transparent);
  background:color-mix(in srgb,var(--lc,#2E90FA) 12%,transparent)}
.card .statusbar .lcchip{flex:0 0 auto;height:22px;display:inline-flex;align-items:center;padding:0 8px;
  border-radius:999px;font-size:var(--fz-cap);font-weight:800;letter-spacing:.04em;text-transform:uppercase;
  background:var(--lc,#2E90FA);color:#06120A;max-width:50%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.card .statusbar .slachip{margin-left:auto;flex:0 0 auto;font-family:var(--mono);font-size:var(--fz-meta);
  font-weight:700;padding:2px 8px;border-radius:999px;border:1px solid var(--hair2);color:var(--tx2)}
.card .statusbar .slachip.late{color:#FF8A8E;border-color:rgba(240,68,56,.5)}
.card .statusbar .spin{width:15px;height:15px;border-radius:50%;flex:0 0 auto;
  border:2px solid rgba(255,255,255,.22);border-top-color:var(--lc,#2E90FA);animation:lcspin .8s linear infinite}
@keyframes lcspin{to{transform:rotate(360deg)}}
/* an ESP rectification arriving in the feed: the blue left border is the one
   signal that the card is now the ESP's move, not the inspector's. */
.card.esp{border-left:3px solid #2E90FA}
.card .cardfoot .lcrow{display:flex;gap:8px;align-items:stretch}
.card .cardfoot .lcrow .statusbar{flex:1 1 auto;min-width:0;height:44px}
.card .cardfoot .lcrow .btn.vfy{flex:0 0 106px;height:44px;min-height:44px;font-size:var(--fz-body);
  background:var(--acc);color:var(--on-acc);border:0;border-radius:12px;font-weight:750;
  display:flex;align-items:center;justify-content:center;gap:6px}
.card .cardfoot .lcrow .btn.vfy svg{width:17px;height:17px}
/* the compact vertical timeline under the facts in the Details sheet */
/* the timeline takes its natural height and the RIGHT COLUMN scrolls; the
   decision row sticks to the bottom of that scroller so Mark Invalid is
   reachable at 604 px however long the timeline gets. */
#incSheet .tl{flex:0 0 auto;max-height:150px;overflow-y:auto;scrollbar-width:none}
#incSheet .tl::-webkit-scrollbar{display:none}
/* once the incident carries an ESP assignment the fact grid is 8 boxes deep —
   three columns keeps the timeline above the fold on a 604px screen. */
#incSheet .facts.f3{grid-template-columns:repeat(3,minmax(0,1fr))}
#incSheet .facts.f3 .wide{grid-column:span 2}
#incSheet .sacts{position:sticky;bottom:0;z-index:2;background:var(--bg);padding-top:10px}
#incSheet .tl[hidden]{display:none}
#incSheet .tl .tlh{font-size:var(--fz-cap);letter-spacing:.08em;text-transform:uppercase;color:var(--tx3);font-weight:700;margin-bottom:7px}
#incSheet .tl .tle{position:relative;padding:0 0 11px 15px;border-left:1px solid var(--hair2)}
#incSheet .tl .tle:last-child{border-left-color:transparent;padding-bottom:0}
#incSheet .tl .tle i{position:absolute;left:-4.5px;top:5px;width:8px;height:8px;border-radius:50%;background:var(--lc,var(--acc))}
#incSheet .tl .tle b{display:block;font-size:var(--fz-body);font-weight:650;color:var(--tx)}
#incSheet .tl .tle .sub{display:block;font-size:var(--fz-meta);color:var(--tx2)}
#incSheet .statusline .lcchip{flex:0 0 auto;height:22px;display:inline-flex;align-items:center;padding:0 9px;
  border-radius:999px;font-size:var(--fz-cap);font-weight:800;letter-spacing:.04em;text-transform:uppercase;
  background:var(--lc,var(--acc));color:#06120A}
#incSheet .statusline .lcchip[hidden]{display:none}
#incSheet .media.mode-rect .box2,#incSheet .media.mode-proof .box2{opacity:0;pointer-events:none}
#incSheet .media .segbtn[hidden]{display:none}

/* =====================================================================
   THE ESP ASSIGNMENT BEAT (revamp task 2)
   Tapping Valid is the one moment the cockpit does work FOR the inspector,
   and a grey spinner reading "Assigning inspector..." said neither what was
   happening nor to whom. It is two beats now:

     1.6 s   .assigning  a HUD loader — an outer ring pulsing outward under a
                         rotating 90deg arc, in the info blue — over
                         "Assigning to ESP..."
     2.2 s   .assigned   the ring snaps to a solid accent disc, the tick draws
                         itself on over 350 ms, and the bar takes ONE soft
                         green glow pulse under "ESP assigned - <name>"

   then the bar settles into the ordinary lifecycle row with its SLA chip.
   Under reduced motion the loader beat is skipped outright (decide()) and
   nothing here animates.
   ===================================================================== */
.card .statusbar .esp-ring{position:relative;width:18px;height:18px;flex:0 0 auto;display:block}
.card .statusbar .esp-ring>i{position:absolute;inset:0;border-radius:50%;display:block}
/* outer: a hairline ring breathing outward */
.card .statusbar .esp-ring>i:first-child{border:1.5px solid #4DA3FF;opacity:.9;
  animation:espPulse 1.2s cubic-bezier(.2,.6,.35,1) infinite}
/* inner: a 90deg arc of the same ring, spinning */
.card .statusbar .esp-ring>i:last-child{inset:3px;border:1.5px solid transparent;
  border-top-color:#4DA3FF;border-right-color:#4DA3FF;animation:espSpin 1s linear infinite}
@keyframes espPulse{from{transform:scale(1);opacity:.9}to{transform:scale(1.35);opacity:0}}
@keyframes espSpin{to{transform:rotate(360deg)}}
.card .statusbar.lc.assigning{--lc:#4DA3FF}
/* --- beat 2: the confirmation --- */
.card .statusbar .esp-ring.done{width:18px;height:18px;border-radius:50%;
  background:var(--acc);display:grid;place-items:center;animation:espPop .26s var(--ease) backwards}
.card .statusbar .esp-ring.done svg{width:12px;height:12px;fill:none;stroke:var(--on-acc,#06120A);
  stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round}
.card .statusbar .esp-ring.done svg path{stroke-dasharray:22;stroke-dashoffset:22;
  animation:espDraw .35s var(--ease-out-cubic,ease-out) .1s forwards}
@keyframes espPop{from{transform:scale(.4)}}
@keyframes espDraw{to{stroke-dashoffset:0}}
.card .statusbar.lc.assigned{--lc:var(--acc);animation:espGlow .6s var(--ease) .06s}
@keyframes espGlow{
  0%{box-shadow:0 0 0 0 rgba(34,200,130,0)}
  35%{box-shadow:0 0 0 4px rgba(34,200,130,.20),0 0 20px rgba(34,200,130,.34)}
  100%{box-shadow:0 0 0 0 rgba(34,200,130,0)}}

@media (prefers-reduced-motion: reduce){
  .card .statusbar .spin{animation:none}
  .card .statusbar .esp-ring>i:first-child,
  .card .statusbar .esp-ring>i:last-child,
  .card .statusbar .esp-ring.done,
  .card .statusbar .esp-ring.done svg path,
  .card .statusbar.lc.assigned{animation:none}
  .card .statusbar .esp-ring.done svg path{stroke-dashoffset:0}
}


/* =====================================================================
   THE FULL-CARD ASSIGNMENT PROCESS (revamp 10)
   Client, 14 Sep: "Assigning to ESP must take the FULL height of the card,
   as a process (big ring, big text), then 'ESP assigned' big — not a thin
   status bar." So for the 3.8 s the dispatch takes, the hero card IS the
   process: the evidence plate drops to a dimmed 120 px strip and everything
   under it — the text stack and the decision foot alike — is replaced by a
   72 px HUD ring and two lines of real type.

   The markup (procPane() in the inline block) is rendered on EVERY card that
   is mid-assignment, but only two hosts unfold it: the Drive hero (#cardSlot)
   and the details sheet's left column (#incProc). A card that has been demoted
   into #list keeps its ordinary 44 px lifecycle foot — one process at a time,
   and it belongs to the card under the inspector's thumb.
   ===================================================================== */
/* client pass 23: "The animation of ESP verified and ESP assigned needs to be
   VERTICAL. The ESP assigned and the actual ESP along with the SLA ... needs to
   be below." So the pane is a centred COLUMN -- ring, then the verdict, then
   the ESP and its SLA under it -- instead of a ring beside a left-aligned text
   block. Three stacked, centred beats read as one object in a glance, which the
   side-by-side pair never did.

   The card's OUTER BOX IS UNCHANGED: the pane is `flex:1 1 auto` inside a card
   whose height renderList() pins to what it measured before the assignment, and
   the stack (96 ring + 14 + ~34 + 6 + ~25 = ~175 px) still sits well inside the
   ~370 px the hidden evidence plate and decision row hand over. `min-height`
   goes to 176 px so the pane cannot collapse below the stack in the one host
   that is NOT height-pinned (the incident sheet). */
.procpane{display:none;flex-direction:column;align-items:center;justify-content:center;
  gap:14px;padding:20px 4px 16px;min-height:176px;text-align:center}
#view-drive #cardSlot .inc-card.proc .body,
#view-drive #cardSlot .inc-card.proc .cardfoot,
#view-drive #vhero .inc-card.proc .body,
#view-drive #vhero .inc-card.proc .cardfoot{display:none}
/* the card holds its height (renderList pins it), so the pane GROWS into all
   of the room the body and the foot gave up -- which is what "the full height
   of the card" means. */
#view-drive #cardSlot .inc-card.proc,
#view-drive #vhero .inc-card.proc{display:flex;flex-direction:column}
#view-drive #cardSlot .inc-card.proc .top,
#view-drive #vhero .inc-card.proc .top{flex:0 0 auto}
#view-drive #cardSlot .inc-card.proc .procpane,
#view-drive #vhero .inc-card.proc .procpane{display:flex;flex:1 1 auto}
/* client pass (14 Sep): "while assigning and when assigned we do not need to
   see the image". The plate goes entirely; the assignment IS the card for the
   length of the sequence, and the ring and its two lines get the whole box. */
#view-drive #cardSlot .inc-card.proc button.thumb,
#view-drive #vhero .inc-card.proc button.thumb{display:none}
/* ...and the WHOLE card carries the process, not just the ring inside it: a
   1.4 s breathing glow in the info blue while it is assigning, then a single
   600 ms green flash on the confirmation. The border itself is locked by an
   !important hairline in 13-coverage, so the glow is a box-shadow ring. */
#view-drive #cardSlot .inc-card.proc,
#view-drive #vhero .inc-card.proc{animation:cardAssigning 1.4s ease-in-out infinite}
#view-drive #cardSlot .inc-card.proc.procdone,
#view-drive #vhero .inc-card.proc.procdone{animation:cardAssigned .68s var(--ease) 1}
@keyframes cardAssigning{
  0%,100%{box-shadow:0 0 0 1px rgba(77,163,255,.20),0 0 0 rgba(77,163,255,0),var(--sh)}
  50%{box-shadow:0 0 0 3px rgba(77,163,255,.34),0 0 28px rgba(77,163,255,.32),var(--sh)}}
@keyframes cardAssigned{
  0%{box-shadow:0 0 0 1px rgba(34,200,130,.22),var(--sh)}
  30%{box-shadow:0 0 0 4px rgba(34,200,130,.70),0 0 38px rgba(34,200,130,.52),var(--sh)}
  60%{box-shadow:0 0 0 3px rgba(34,200,130,.42),0 0 26px rgba(34,200,130,.28),var(--sh)}
  100%{box-shadow:0 0 0 0 rgba(34,200,130,0),var(--sh)}}

/* --- beat 1: the ring turning over --------------------------------------
   Two concentric rings on a faint static track: the outer one breathes
   outward and fades, the inner sweeps a half arc, both in the info blue.
   Nothing here is a generic spinner — the two speeds are deliberately
   different (1.5 s vs 1.05 s) so the pair never reads as one solid object. */
.procring{position:relative;width:96px;height:96px;flex:0 0 auto;display:block}
.procring::before{content:"";position:absolute;inset:0;border-radius:50%;
  border:1px solid rgba(77,163,255,.24)}
.procring>i{position:absolute;inset:0;border-radius:50%;display:block}
.procring>i:first-child{border:2px solid #4DA3FF;opacity:.85;
  animation:procPulse 1.5s cubic-bezier(.2,.6,.35,1) infinite}
.procring>i:last-child{inset:12px;border:3px solid rgba(77,163,255,.14);
  border-top-color:#4DA3FF;border-right-color:#4DA3FF;
  animation:procSpin 1.05s linear infinite}
@keyframes procPulse{from{transform:scale(.92);opacity:.85}to{transform:scale(1.32);opacity:0}}
@keyframes procSpin{to{transform:rotate(360deg)}}
/* --- beat 2: the confirmation ------------------------------------------ */
.procring.done{border-radius:50%;background:var(--acc);display:grid;place-items:center;
  animation:procPop .3s cubic-bezier(.2,.8,.2,1) backwards,procGlow .9s var(--ease) .18s}
.procring.done::before{display:none}
.procring.done svg{width:46px;height:46px;fill:none;stroke:var(--on-acc,#06120A);
  stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}
.procring.done svg path{stroke-dasharray:24;stroke-dashoffset:24;
  animation:procDraw .42s var(--ease) .14s forwards}
@keyframes procPop{from{transform:scale(.5);opacity:0}}
@keyframes procDraw{to{stroke-dashoffset:0}}
@keyframes procGlow{
  0%{box-shadow:0 0 0 0 rgba(34,200,130,0)}
  40%{box-shadow:0 0 0 10px rgba(34,200,130,.16),0 0 34px rgba(34,200,130,.40)}
  100%{box-shadow:0 0 0 0 rgba(34,200,130,0)}}
/* --- the two lines ---
   client pass (14 Sep): both are read through a windscreen, so the title is
   28 px / 800 and the ESP name with its SLA is 20 px / 600. Neither may ever
   be ellipsised -- the name and the hours are the whole point of the pane --
   so they WRAP instead of cutting. */
.proctx{min-width:0;max-width:100%;flex:0 1 auto;display:flex;flex-direction:column;
  align-items:center;gap:6px;text-align:center}
.proctx b{font-size:28px;font-weight:800;line-height:1.08;letter-spacing:-.02em;color:var(--tx);
  min-width:0;white-space:normal;overflow:visible;text-overflow:clip}
.proctx span{font-size:20px;font-weight:600;line-height:1.25;color:var(--tx2);
  min-width:0;white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere}
.procpane.done .proctx b{color:var(--acc)}

@media (prefers-reduced-motion: reduce){
  /* reduced motion never sees beat 1 at all (decide() skips it): there is
     only the confirmation, and it does not move. */
  .procring>i:first-child,.procring>i:last-child,
  .procring.done,.procring.done svg path{animation:none}
  .procring.done svg path{stroke-dashoffset:0}
  /* the card-wide glow is motion too: reduced motion gets the states as
     static rings, never a pulse or a flash. */
  #view-drive #cardSlot .inc-card.proc,
  #view-drive #cardSlot .inc-card.proc.procdone,
  #view-drive #vhero .inc-card.proc,
  #view-drive #vhero .inc-card.proc.procdone{animation:none;
    box-shadow:0 0 0 2px rgba(77,163,255,.26),var(--sh)}
  #view-drive #cardSlot .inc-card.proc.procdone,
  #view-drive #vhero .inc-card.proc.procdone{box-shadow:0 0 0 2px rgba(34,200,130,.30),var(--sh)}
}

/* ---- verify pass (client, 14 Sep): the ESCALATED variant -----------------
   The verification decision card (#vhero, renderVerifyHero/vDecide) reuses
   this pane for its result: Verified is the green check above, unchanged;
   Escalate is the same pane in the critical red — the ring fills --crit, the
   glyph is the i-x cross drawing itself on, the title reads in red and the
   card's confirmation flash is red. */
.procpane.escalated .procring.done{background:var(--crit)}
.procpane.escalated .procring.done svg{stroke:#fff}
.procpane.escalated .procring.done svg path{stroke-dasharray:36;stroke-dashoffset:36}
.procpane.escalated .procring.done{animation:procPop .3s cubic-bezier(.2,.8,.2,1) backwards,procGlowRed .9s var(--ease) .18s}
.procpane.escalated .proctx b{color:var(--crit)}
#view-drive #vhero .inc-card.proc.procdone.escalated{animation:cardEscalated .68s var(--ease) 1}
@keyframes procGlowRed{
  0%{box-shadow:0 0 0 0 rgba(255,90,95,0)}
  40%{box-shadow:0 0 0 10px rgba(255,90,95,.16),0 0 34px rgba(255,90,95,.40)}
  100%{box-shadow:0 0 0 0 rgba(255,90,95,0)}}
@keyframes cardEscalated{
  0%{box-shadow:0 0 0 1px rgba(255,90,95,.22),var(--sh)}
  30%{box-shadow:0 0 0 4px rgba(255,90,95,.70),0 0 38px rgba(255,90,95,.52),var(--sh)}
  60%{box-shadow:0 0 0 3px rgba(255,90,95,.42),0 0 26px rgba(255,90,95,.28),var(--sh)}
  100%{box-shadow:0 0 0 0 rgba(255,90,95,0),var(--sh)}}
@media (prefers-reduced-motion: reduce){
  .procpane.escalated .procring.done{animation:none}
  .procpane.escalated .procring.done svg path{stroke-dashoffset:0}
  #view-drive #vhero .inc-card.proc.procdone.escalated{animation:none;box-shadow:0 0 0 2px rgba(255,90,95,.30),var(--sh)}
}
