/* ============================================================
   Korevra SureDesk — motion & imagery layer
   ------------------------------------------------------------
   A layer, not a rewrite. Nothing here changes what a portal
   screen renders; it only changes how it arrives on screen.

   Rules obeyed throughout:
     · every colour comes from a design token in app.css
     · only transform / opacity are animated (two documented
       exceptions are marked EXCEPTION and are paint-only)
     · nothing animates longer than 600ms (the toast progress
       bar is a dismiss timer, not a flourish — marked below)
     · infinite animation is limited to two hairline elements
     · everything collapses under prefers-reduced-motion

   Rules that could hide content if motion.js failed to load are
   gated behind `html.mo-on`, a class motion.js sets on parse.
   ============================================================ */

:root {
  --mo-spring: cubic-bezier(.2, .9, .26, 1);
  --mo-ease: cubic-bezier(.2, .8, .25, 1);
  --mo-fast: 180ms;
  --mo-mid: 320ms;
  --mo-slow: 520ms;
}

/* ============================================================
   1 · SPLASH
   ============================================================ */
.mo-splash {
  position: fixed; inset: 0; z-index: 900;
  display: grid; place-items: center;
  background: var(--navy);
  opacity: 1;
  transition: opacity 400ms ease;
  overflow: hidden;
  contain: strict;
}
.mo-splash.mo-out { opacity: 0; pointer-events: none; }

.mo-splash-bg {
  position: absolute; inset: -2%;
  background-image: url("/img/hero-noc.jpg");
  background-size: cover; background-position: center;
  opacity: .22;
  filter: grayscale(1) contrast(1.05);
  transform: scale(1.06);
}
html.mo-on .mo-splash-bg { animation: mo-splash-bg 600ms var(--mo-ease) both; }
@keyframes mo-splash-bg { from { opacity: 0; transform: scale(1.12); } }

.mo-splash-scrim {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 68% 62% at 50% 46%, transparent 8%, color-mix(in srgb, var(--navy) 82%, transparent) 72%),
    linear-gradient(160deg, color-mix(in srgb, var(--navy) 72%, transparent), color-mix(in srgb, var(--accent) 26%, transparent));
}

.mo-splash-in {
  position: relative; text-align: center; padding: 24px;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}

.mo-mark { display: block; width: 68px; height: 68px; }
.mo-mark-plate { fill: none; stroke: color-mix(in srgb, var(--gold) 70%, transparent); stroke-width: 1.4; opacity: .55; }
.mo-mark-ring { fill: none; stroke: #e8b64c; stroke-width: 2.4; stroke-linecap: round; }
.mo-mark-hand { fill: none; stroke: #fffdf8; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }

/* the mark draws itself: ring first, then the hand */
html.mo-on .mo-mark-ring {
  stroke-dasharray: 54; stroke-dashoffset: 54;
  animation: mo-draw 560ms var(--mo-ease) forwards;
}
html.mo-on .mo-mark-hand {
  stroke-dasharray: 12; stroke-dashoffset: 12;
  animation: mo-draw 260ms var(--mo-ease) 300ms forwards;
}
@keyframes mo-draw { to { stroke-dashoffset: 0; } }

.mo-splash-name {
  font-family: var(--display); font-size: 27px; font-weight: 500;
  color: #fffdf8; letter-spacing: -.02em; line-height: 1.1;
}
.mo-splash-name b { font-weight: 750; }
.mo-splash-promise {
  font-family: var(--font); font-size: 13px; font-weight: 600;
  color: color-mix(in srgb, #fffdf8 68%, transparent);
  max-width: 30ch;
}
html.mo-on .mo-splash-name { animation: mo-rise 460ms var(--mo-ease) 220ms both; }
html.mo-on .mo-splash-promise { animation: mo-rise 460ms var(--mo-ease) 320ms both; }
@keyframes mo-rise { from { opacity: 0; transform: translate3d(0, 10px, 0); } }

/* the one hairline that loops — a 2px seam under the mark */
.mo-splash-seam {
  position: relative; width: 128px; height: 2px; border-radius: 2px; overflow: hidden;
  background: color-mix(in srgb, #fffdf8 14%, transparent);
}
.mo-splash-seam::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--accent-ink), transparent);
  transform: translate3d(-100%, 0, 0);
}
html.mo-on .mo-splash-seam::after { animation: mo-seam 1200ms linear infinite; }
@keyframes mo-seam { to { transform: translate3d(100%, 0, 0); } }

/* ============================================================
   2 · PAGE TRANSITION
   ============================================================ */
html.mo-on .mo-enter { animation: mo-fade var(--mo-fast) var(--mo-ease) both; }
@keyframes mo-fade { from { opacity: 0; } }

html.mo-on .mo-in {
  animation: mo-in var(--mo-mid) var(--mo-ease) both;
  animation-delay: calc(var(--mo-i, 0) * 34ms);
}
@keyframes mo-in { from { opacity: 0; transform: translate3d(0, 10px, 0); } }

/* View Transition API — keep the root cross-fade short so it never
   holds the main thread on a low-end device. */
::view-transition-old(root),
::view-transition-new(root) { animation-duration: 170ms; animation-timing-function: var(--mo-ease); }

/* ============================================================
   3 · LANDING CARD PHOTOGRAPHY
   The photo layer is NOT gated on mo-on: it is imagery, and the
   markup ships from ui.js whether or not motion.js runs.
   ============================================================ */
.land-card { padding: 0; gap: 0; overflow: hidden; }

.lc-photo {
  position: relative; display: block;
  height: 112px; flex: none;
  background: var(--surface2);
  overflow: hidden;
  isolation: isolate;              /* keeps the duotone blend local */
}
.lc-photo img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  filter: grayscale(1) contrast(1.06) brightness(.97);
  transform: scale(1.02);
  transition: transform 420ms var(--mo-ease);
  will-change: transform;
}
.lc-duo {
  position: absolute; inset: 0;
  background: linear-gradient(148deg,
    color-mix(in srgb, var(--navy) 92%, transparent) 0%,
    color-mix(in srgb, var(--accent) 78%, transparent) 58%,
    color-mix(in srgb, var(--gold) 52%, transparent) 100%);
  mix-blend-mode: color;
  opacity: .9;
  transition: opacity 300ms var(--mo-ease);
}
.lc-photo::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--surface) 90%, transparent) 0%,
    color-mix(in srgb, var(--surface) 26%, transparent) 38%,
    transparent 72%);
}
.land-card:hover .lc-photo img { transform: scale(1.07); }
.land-card:hover .lc-duo { opacity: 1; }

.lc-body {
  padding: 14px 18px 17px;
  display: flex; flex-direction: column; gap: 8px; flex: 1; min-width: 0;
}

/* pointer tilt needs depth on the grid; the JS writes the transform */
html.mo-on .land-grid { perspective: 1100px; }
html.mo-on .land-card { transform-style: preserve-3d; }
html.mo-on .land-card.mo-tilt { will-change: transform; transition: box-shadow 200ms ease, border-color 200ms ease; }

/* ============================================================
   4 · LIVE DATA MOTION
   ============================================================ */

/* clock chip crossing a threshold — a single 480ms swell */
html.mo-on .clockchip.mo-cross { animation: mo-cross 480ms var(--mo-spring); }
@keyframes mo-cross {
  0% { transform: scale(1); }
  38% { transform: scale(1.11); }
  100% { transform: scale(1); }
}
/* app.css pulses every breached chip forever; on a queue of 60 tickets
   that is 60 infinite animations. Bound it to three breaths. */
html.mo-on .clockchip.cd-bad { animation: pulse 1.6s ease-in-out 3; }
html.mo-on .clockchip.cd-bad.mo-cross { animation: mo-cross 480ms var(--mo-spring), pulse 1.6s ease-in-out 480ms 3; }

/* a row that changed because of a sync update flashes once.
   EXCEPTION: background-color, not transform/opacity — a row cannot
   carry a pseudo-element overlay. Paint-only, 520ms, capped at 12 rows. */
html.mo-on tr.mo-sync > td { animation: mo-sync var(--mo-slow) ease-out; }
@keyframes mo-sync {
  0% { background-color: color-mix(in srgb, var(--accent) 26%, transparent); }
  100% { background-color: transparent; }
}

/* count-up keeps the glyph box stable while digits change */
html.mo-on .kpi-value[data-mo-count] { font-variant-numeric: tabular-nums; }

/* ============================================================
   5 · TOAST / NOTIFICATION
   ============================================================ */
#toasts { gap: 10px; bottom: 24px; }

html.mo-on .toast.mo-toast {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: auto 1fr; align-items: start;
  gap: 11px;
  min-width: 268px; max-width: min(430px, 92vw);
  padding: 12px 16px 13px;
  border-radius: 14px;
  text-align: left;
  background: color-mix(in srgb, var(--navy) 94%, transparent);
  border: 1px solid color-mix(in srgb, #fffdf8 14%, transparent);
  box-shadow: var(--shadow-lg), 0 1px 0 color-mix(in srgb, #fffdf8 12%, transparent) inset;
  animation: mo-toast-in 380ms var(--mo-spring) both;
}
html.mo-on .toast.mo-toast.good { background: color-mix(in srgb, var(--good) 92%, var(--navy)); }
html.mo-on .toast.mo-toast.bad { background: color-mix(in srgb, var(--bad) 92%, var(--navy)); }
@keyframes mo-toast-in {
  from { opacity: 0; transform: translate3d(0, 16px, 0) scale(.96); }
}
html.mo-on .toast.mo-toast.out { transform: translate3d(0, 6px, 0) scale(.97); transition: opacity 400ms ease, transform 400ms ease; }

.mo-t-ic {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 9px; flex: none;
  background: color-mix(in srgb, #fffdf8 16%, transparent);
  color: #fffdf8;
}
.mo-t-ic svg { display: block; }
.mo-t-body { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mo-t-title { font-size: 13.5px; font-weight: 700; line-height: 1.35; color: #fffdf8; }
.mo-t-note {
  font-size: 12px; font-weight: 500; line-height: 1.4;
  color: color-mix(in srgb, #fffdf8 74%, transparent);
}
.mo-t-bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: color-mix(in srgb, #fffdf8 60%, transparent);
  transform-origin: 0 50%;
}
/* the only other looping-length animation: a linear dismiss timer that
   mirrors core.js's 3200ms window. It is a clock, not a flourish. */
html.mo-on .mo-t-bar { animation: mo-t-bar 3200ms linear forwards; }
@keyframes mo-t-bar { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* ============================================================
   6 · MENU & NAVIGATION CHOREOGRAPHY
   ============================================================ */
html.mo-on .side-nav { position: relative; }
html.mo-on .snav { position: relative; z-index: 1; transition: color 160ms ease; }
html.mo-on .snav.active { background: transparent; }

.mo-snav-ind {
  position: absolute; left: 0; right: 0; top: 0;
  border-radius: 9px; background: var(--accent);
  z-index: 0; pointer-events: none;
  opacity: 0;
}
html.mo-on .mo-snav-ind.mo-live { opacity: 1; }
html.mo-on .mo-snav-ind.mo-slide { transition: transform 340ms var(--mo-spring), opacity 200ms ease; }

/* topbar elevation on scroll — an opacity fade on a shadow strip,
   so no box-shadow value is ever animated */
html.mo-on .topbar::after {
  content: ""; position: absolute; left: 0; right: 0; top: 100%;
  height: 16px; pointer-events: none; opacity: 0;
  transition: opacity 240ms ease;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--ink) 13%, transparent), transparent);
}
html.mo-on .topbar.mo-lift::after { opacity: 1; }

/* ============================================================
   7 · MODAL / DRAWER
   ============================================================ */
/* The scrim carries a constant blur and fades in on opacity alone.
   Opacity < 1 groups the element, so the blur fades with it — no
   backdrop-filter value is ever interpolated. */
html.mo-on .modal-back {
  opacity: 0;
  backdrop-filter: blur(7px) saturate(1.15);
  transition: opacity 220ms ease;
}
html.mo-on .modal-back.open { opacity: 1; }
html.mo-on .modal {
  transform: translate3d(0, 14px, 0) scale(.975); opacity: 0;
  transition: transform 340ms var(--mo-spring), opacity 220ms ease;
}
html.mo-on .modal-back.open .modal { transform: none; opacity: 1; }

html.mo-on .drawer-back {
  opacity: 0;
  backdrop-filter: blur(6px) saturate(1.1);
  transition: opacity 220ms ease;
}
html.mo-on .drawer-back.open { opacity: 1; }
html.mo-on .drawer { transform: translate3d(36px, 0, 0); transition: transform 360ms var(--mo-spring), opacity 220ms ease; }

html.mo-on .modal :focus-visible,
html.mo-on .drawer :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================
   8 · SKELETON + ILLUSTRATED EMPTY STATE
   ============================================================ */
.mo-skel { display: flex; flex-direction: column; gap: 10px; }
.mo-skel-line {
  position: relative; overflow: hidden;
  height: 12px; border-radius: 7px;
  background: var(--surface2);
}
.mo-skel-line.tall { height: 34px; border-radius: 10px; }
.mo-skel-line::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ink) 8%, transparent), transparent);
  transform: translate3d(-100%, 0, 0);
}
html.mo-on .mo-skel-line::after { animation: mo-shimmer 1100ms linear infinite; }
@keyframes mo-shimmer { to { transform: translate3d(100%, 0, 0); } }

.mo-void {
  display: grid; gap: 14px; justify-items: center; text-align: center;
  padding: 26px 16px 30px;
}
.mo-void-photo {
  position: relative; width: min(220px, 62%); aspect-ratio: 3 / 2;
  border-radius: var(--r-lg); overflow: hidden; isolation: isolate;
  border: 1px solid var(--line);
}
.mo-void-photo img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: grayscale(1) contrast(1.04) brightness(1.02);
}
.mo-void-photo::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--navy) 74%, transparent),
    color-mix(in srgb, var(--accent) 62%, transparent));
  mix-blend-mode: color;
}
.mo-void h4 { font-family: var(--display); font-size: 16px; font-weight: 600; }
.mo-void p { font-size: 12.5px; color: var(--ink2); max-width: 44ch; }
html.mo-on .mo-void { animation: mo-in var(--mo-mid) var(--mo-ease) both; }

/* ============================================================
   9 · REDUCED MOTION — everything above collapses to a still frame
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html.mo-on .mo-splash-bg,
  html.mo-on .mo-splash-name,
  html.mo-on .mo-splash-promise,
  html.mo-on .mo-splash-seam::after,
  html.mo-on .mo-mark-ring,
  html.mo-on .mo-mark-hand,
  html.mo-on .mo-enter,
  html.mo-on .mo-in,
  html.mo-on .mo-void,
  html.mo-on .clockchip.mo-cross,
  html.mo-on .clockchip.cd-bad,
  html.mo-on .clockchip.cd-bad.mo-cross,
  html.mo-on tr.mo-sync > td,
  html.mo-on .toast.mo-toast,
  html.mo-on .mo-t-bar,
  html.mo-on .mo-skel-line::after,
  .clockchip.cd-bad {
    animation: none !important;
  }
  html.mo-on .mo-in,
  html.mo-on .mo-enter,
  html.mo-on .mo-void,
  html.mo-on .mo-splash-name,
  html.mo-on .mo-splash-promise,
  html.mo-on .toast.mo-toast { opacity: 1 !important; transform: none !important; }

  html.mo-on .mo-mark-ring,
  html.mo-on .mo-mark-hand { stroke-dashoffset: 0 !important; }
  html.mo-on .mo-t-bar { transform: scaleX(1); }
  .mo-splash { transition: none; }

  html.mo-on .mo-snav-ind.mo-slide,
  html.mo-on .modal-back, html.mo-on .modal,
  html.mo-on .drawer-back, html.mo-on .drawer,
  .lc-photo img, .lc-duo,
  html.mo-on .topbar::after { transition: none !important; }

  html.mo-on .modal-back.open, html.mo-on .drawer-back.open { backdrop-filter: none; }
  html.mo-on .land-card.mo-tilt { transform: none !important; }
  html.mo-on .land-card { transform-style: flat; }
}
