/* ==========================================================================
   WBIP — motion & interaction layer (companion to fx.js)

   ADDITIVE ONLY. This file never redefines a class that exists in app.css.
   Everything here is either a new `.fx-*` class, a `[data-*]` hook, or a
   `--fx-*` custom property. Existing tokens (--blue, --gold, --panel, --line,
   --text, --muted, --radius) are assumed to exist and are only ever read.

   STYLE F retune: no hook was renamed and no logic moved — only the easing,
   the durations and the palette. F settles rather than springs, so the shared
   ease is cubic-bezier(.2,.85,.25,1) and hover work happens in 170-220ms
   while a value that has to be read (a bar, a ring, a counter) takes ~900ms.

   Load order: app.css first, then fx.css.
   ========================================================================== */

:root{
  /* F's ease: a firm start that settles, never a spring that overshoots. */
  --fx-ease:cubic-bezier(.2,.85,.25,1);
  --fx-ease-soft:cubic-bezier(.33,1,.68,1);
  --fx-fast:170ms;
  --fx-mid:220ms;
  --fx-slow:420ms;
  /* The time a figure takes to arrive: scales, rings and counters share it. */
  --fx-settle:900ms;

  /* Local aliases with fallbacks so fx.css is safe to drop into any page. */
  --fx-blue:var(--blue,#2C3E8C);
  --fx-blue-deep:var(--blue-deep,#1F2C68);
  --fx-gold:var(--gold,#8A5209);
  --fx-text:var(--text,#1A1A19);
  --fx-muted:var(--muted,#6E6862);
  --fx-line:var(--line,#E6E3DE);
  --fx-panel:var(--panel,#FFFFFF);
  --fx-radius:var(--radius,10px);
}

/* ==========================================================================
   1. Reveal on scroll  —  [data-reveal] / [data-reveal-delay]
   Only armed once fx.js has run (html.fx-js), so content is never trapped
   invisible if the script fails to load.
   ========================================================================== */

html.fx-js [data-reveal]{
  --fx-d:0ms;
  opacity:0;
  translate:0 10px;
  transition:
    opacity 520ms var(--fx-ease) var(--fx-d),
    translate 520ms var(--fx-ease) var(--fx-d);
}
html.fx-js [data-reveal].fx-in{opacity:1;translate:none}

/* Older engines without the independent `translate` property. */
@supports not (translate:0 1px){
  html.fx-js [data-reveal]{
    transform:translateY(10px);
    transition:
      opacity 520ms var(--fx-ease) var(--fx-d),
      transform 520ms var(--fx-ease) var(--fx-d);
  }
  html.fx-js [data-reveal].fx-in{transform:none}
}

/* Page enter: content fades and rises 8px. */
html.fx-js .fx-page{animation:fx-page-in 460ms var(--fx-ease) both}
@keyframes fx-page-in{
  from{opacity:0;translate:0 8px}
  to{opacity:1;translate:none}
}

/* Counters keep a fixed digit width so nothing reflows while ticking. */
[data-count-to]{font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1}

/* ==========================================================================
   2. The ground  —  .fx-aurora is retired
   The aurora was a warm radial wash drifting behind the page, and it lost on
   measurement: it only ever cost contrast. Where it was strongest in an open
   margin it took 13px --muted on the ground from 5.23:1 to 5.20:1, and it
   bought nothing a ledger ground does not say better.

   It was also never what it looked like in the source. `.fx-aurora::before`
   and `body::before` were the same pseudo element, and the class selector
   won: its `background` shorthand erased the paper grain that app.css set
   there with `background-image`, while the aurora inherited that layer's
   `opacity:.032`. So the wash rendered at 3% and the "faint grain" the
   comment promised was never painted at all. Both are fixed now: the whole
   ground -- warm paper, real grain, the 24px ruling and the binder rule --
   lives in one place, on <body> in app.css, and it scrolls with the document
   instead of being pinned to the glass.
   The selector stays as an explicit no-op so that any markup still carrying
   the class gets nothing rather than a resurrected wash.
   ========================================================================== */

.fx-aurora::before,
.fx-aurora::after{display:none}

/* ==========================================================================
   3. Surfaces  —  .fx-glass / .fx-edge / .fx-lift
   ========================================================================== */

.fx-glass{
  background:rgba(255,255,255,.86);
  -webkit-backdrop-filter:blur(10px);
  backdrop-filter:blur(10px);
  border:1px solid var(--fx-line);
  border-radius:var(--fx-radius);
  box-shadow:
    0 1px 2px rgba(26,26,25,.05),
    0 6px 16px -10px rgba(26,26,25,.16),
    inset 0 1px 0 rgba(255,255,255,.9);
}

/* Warm 1px top edge on its own, for panels that keep their own bg. */
.fx-edge{box-shadow:inset 0 1px 0 rgba(255,255,255,.9)}

/* Hover lift + border catches the light. */
.fx-lift{
  transition:
    transform var(--fx-mid) var(--fx-ease),
    border-color var(--fx-mid) var(--fx-ease),
    box-shadow var(--fx-mid) var(--fx-ease);
}
.fx-lift:hover{
  transform:translateY(-2px);
  border-color:rgba(44,62,140,.30);
  box-shadow:0 2px 4px rgba(26,26,25,.05), 0 16px 30px -14px rgba(44,62,140,.30);
}

/* ==========================================================================
   4. Tilt cards  —  [data-tilt]
   fx.js owns the transform; this file owns the specular glare.
   ========================================================================== */

.fx-tilt{
  position:relative;
  transform-style:preserve-3d;
  backface-visibility:hidden;
}

.fx-tilt-glare{
  position:absolute;
  inset:0;
  border-radius:inherit;
  overflow:hidden;
  pointer-events:none;
  opacity:var(--fx-glare,0);
  transition:opacity var(--fx-mid) var(--fx-ease);
  background:
    radial-gradient(
      42% 55% at var(--fx-gx,50%) var(--fx-gy,50%),
      rgba(255,255,255,.16),
      rgba(126,192,246,.07) 42%,
      transparent 68%
    );
}
/* A hairline of light along the edge the card is tilting toward. */
.fx-tilt-glare::after{
  content:'';
  position:absolute;
  inset:0;
  border-radius:inherit;
  background:linear-gradient(
    var(--fx-glare-angle,120deg),
    rgba(255,255,255,.10),
    transparent 38%
  );
  opacity:.6;
}
/* v4.1 readability: every tilted .card is a text card, so the moving specular
   pool never crosses its copy. The tilt keeps the depth, the edge hairline
   keeps the light; only the roaming radial goes. */
.card .fx-tilt-glare,
.card.fx-tilt > .fx-tilt-glare{background:none}

/* ==========================================================================
   5. Magnetic buttons  —  [data-magnetic]
   ========================================================================== */

.fx-magnetic{
  position:relative;
  transform-origin:center;
}
/* While the pointer drives it, fx.js is the only animator — kill any
   inherited transform transition so the two don't fight. */
.fx-magnetic.fx-mag-on{transition-property:box-shadow,background,border-color,color}

/* Ripples live in their own clipped overlay so the button itself never needs
   overflow:hidden (which would shift an inline-flex button's baseline). */
.fx-ink{
  position:absolute;
  inset:0;
  border-radius:inherit;
  overflow:hidden;
  pointer-events:none;
}

.fx-ripple{
  position:absolute;
  border-radius:50%;
  pointer-events:none;
  background:radial-gradient(circle, rgba(255,255,255,.42), rgba(255,255,255,.06) 60%, transparent 70%);
  transform:translate(-50%,-50%) scale(0);
  opacity:.75;
  animation:fx-ripple 520ms var(--fx-ease) forwards;
}
@keyframes fx-ripple{
  to{transform:translate(-50%,-50%) scale(1);opacity:0}
}

/* ==========================================================================
   6. Radial score gauge  —  [data-gauge]
   --fx-p is a unitless 0–100 progress driven per frame by fx.js.
   ========================================================================== */

.fx-gauge{
  --fx-p:0;
  --fx-gauge-size:88px;
  --fx-gauge-w:7px;
  --fx-gauge-color:var(--fx-muted);
  position:relative;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:var(--fx-gauge-size);
  height:var(--fx-gauge-size);
  flex-shrink:0;
  line-height:1;
}
.fx-gauge--gold {--fx-gauge-color:var(--fx-gold)}
.fx-gauge--blue {--fx-gauge-color:var(--fx-blue)}
.fx-gauge--muted{--fx-gauge-color:var(--fx-muted)}

.fx-gauge-ring{
  position:absolute;
  inset:0;
  border-radius:50%;
  background:conic-gradient(
    from -90deg,
    var(--fx-gauge-color) calc(var(--fx-p) * 1%),
    var(--track,#EAE7E2) 0
  );
  -webkit-mask:radial-gradient(circle farthest-side,
    transparent calc(100% - var(--fx-gauge-w)),
    #000 calc(100% - var(--fx-gauge-w) + .5px));
  mask:radial-gradient(circle farthest-side,
    transparent calc(100% - var(--fx-gauge-w)),
    #000 calc(100% - var(--fx-gauge-w) + .5px));
}

/* F reads the ring, it does not light it: the wash stays a whisper and the
   bloom is gone. Kept as a class so nothing that references it breaks. */
.fx-gauge-glow{
  position:absolute;
  inset:0;
  border-radius:50%;
  background:radial-gradient(circle at 50% 50%, rgba(44,62,140,.035), transparent 70%);
  opacity:calc(var(--fx-p) / 100);
}

/* The luminous head of the sweep. */
.fx-gauge-head{
  position:absolute;
  left:50%;
  top:50%;
  width:0;
  height:0;
  transform:rotate(calc(var(--fx-p) * 3.6deg));
  opacity:calc(var(--fx-p) / 6);
}
/* The head of the sweep is a tick on a drafting rule, not a lamp. */
.fx-gauge-head::after{
  content:'';
  position:absolute;
  left:-2px;
  top:calc((var(--fx-gauge-size) - var(--fx-gauge-w)) / -2 - 2px);
  width:4px;
  height:4px;
  border-radius:50%;
  background:var(--fx-gauge-color);
}

.fx-gauge-val{
  position:relative;
  font-family:var(--font-display,inherit);
  font-weight:600;
  font-variant-numeric:tabular-nums;
  font-size:calc(var(--fx-gauge-size) * .28);
  letter-spacing:-.035em;
  color:var(--fx-text);
}
.fx-gauge--gold .fx-gauge-val{color:var(--gold-ink,#7A4708)}

.fx-gauge-sm{--fx-gauge-size:60px;--fx-gauge-w:5px}
.fx-gauge-lg{--fx-gauge-size:124px;--fx-gauge-w:9px}

/* ==========================================================================
   7. Flow ribbon  —  [data-ribbon]
   ========================================================================== */

.fx-ribbon{position:relative}

.fx-ribbon-svg{
  display:block;
  width:100%;
  height:auto;
  overflow:visible;
}

.fx-ribbon-base{
  fill:none;
  stroke:var(--fx-ribbon-track, rgba(44,62,140,.26));
  stroke-width:var(--fx-ribbon-w, 2);
  stroke-linecap:round;
  stroke-linejoin:round;
}

.fx-ribbon-flow{
  fill:none;
  stroke-width:calc(var(--fx-ribbon-w, 2) * 1.7);
  stroke-linecap:round;
  filter:drop-shadow(0 1px 3px rgba(44,62,140,.28));
}

.fx-ribbon-dot{filter:drop-shadow(0 1px 3px rgba(138,82,9,.38))}

/* Stops along the ribbon light up in sequence once it has drawn itself. */
.fx-ribbon [data-ribbon-node]{
  --fx-d:0ms;
  opacity:.42;
  transition:opacity 520ms var(--fx-ease) var(--fx-d),
             filter 520ms var(--fx-ease) var(--fx-d);
}
.fx-ribbon [data-ribbon-node].fx-lit{
  opacity:1;
  filter:drop-shadow(0 1px 3px rgba(44,62,140,.2));
}

/* ==========================================================================
   8. Pipeline track  —  [data-pipeline] > [data-stage]
   Self-contained: use plain [data-stage] elements (not .stage) so this never
   collides with the flat pipeline already in app.css.
   ========================================================================== */

.fx-pipeline{
  display:flex;
  align-items:flex-start;
  gap:0;
  overflow-x:auto;
  overflow-y:hidden;
  padding:4px 2px 8px;
  scrollbar-width:thin;
  -webkit-overflow-scrolling:touch;
}

.fx-stage{
  --fx-d:0ms;
  --fx-stage-color:var(--fx-muted);
  position:relative;
  flex:1 1 0;
  min-width:112px;
  padding:30px 12px 0 0;
}
.fx-stage--done   {--fx-stage-color:var(--fx-gold)}
.fx-stage--current{--fx-stage-color:var(--fx-blue)}

/* Connector from this node to the next. */
.fx-stage-rail{
  position:absolute;
  left:26px;
  right:6px;
  top:9px;
  height:2px;
  border-radius:2px;
  background:rgba(110,104,98,.16);
  overflow:hidden;
}
.fx-stage--last .fx-stage-rail{display:none}
.fx-stage-rail > i{
  display:block;
  height:100%;
  width:100%;
  transform:scaleX(0);
  transform-origin:left center;
  background:linear-gradient(90deg, var(--fx-gold), rgba(138,82,9,.32));
  transition:transform 760ms var(--fx-ease) var(--fx-d);
}
.fx-pipe-in .fx-stage--done .fx-stage-rail > i{transform:scaleX(1)}
/* The segment leading into the live stage fills half way, then waits. */
.fx-pipe-in .fx-stage--current .fx-stage-rail > i{
  transform:scaleX(.35);
  background:linear-gradient(90deg, var(--fx-blue), rgba(44,62,140,.18));
}

.fx-stage-dot{
  position:absolute;
  left:0;
  top:0;
  width:20px;
  height:20px;
  border-radius:50%;
  background:var(--fx-panel);
  border:2px solid rgba(110,104,98,.28);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
  transition:background var(--fx-slow) var(--fx-ease) var(--fx-d),
             border-color var(--fx-slow) var(--fx-ease) var(--fx-d),
             box-shadow var(--fx-slow) var(--fx-ease) var(--fx-d);
}

/* Completed: gold fill + a drawn check (no icon font). */
.fx-pipe-in .fx-stage--done .fx-stage-dot{
  background:linear-gradient(140deg,#E8CB7E,var(--fx-gold));
  border-color:rgba(138,82,9,.55);
  box-shadow:0 2px 6px -2px rgba(138,82,9,.34);
}
.fx-stage--done .fx-stage-dot::after{
  content:'';
  position:absolute;
  left:5px;
  top:3px;
  width:5px;
  height:9px;
  border:solid #221A05;
  border-width:0 2px 2px 0;
  transform:rotate(42deg) scale(0);
  transform-origin:center;
  transition:transform 420ms var(--fx-ease) calc(var(--fx-d) + 180ms);
}
.fx-pipe-in .fx-stage--done .fx-stage-dot::after{transform:rotate(42deg) scale(1)}

/* Live stage: blue core + a breathing ring. */
.fx-pipe-in .fx-stage--current .fx-stage-dot{
  background:radial-gradient(circle at 50% 50%, var(--fx-blue) 0 34%, rgba(44,62,140,.10) 36%);
  border-color:rgba(44,62,140,.75);
  box-shadow:0 2px 6px -2px rgba(44,62,140,.34);
}
.fx-stage--current .fx-stage-dot::before{
  content:'';
  position:absolute;
  inset:-6px;
  border-radius:50%;
  border:1px solid rgba(44,62,140,.5);
  opacity:0;
}
.fx-pipe-in .fx-stage--current .fx-stage-dot::before{
  animation:fx-pulse 2400ms var(--fx-ease-soft) infinite;
}
@keyframes fx-pulse{
  0%  {transform:scale(.72);opacity:.75}
  70% {transform:scale(1.32);opacity:0}
  100%{transform:scale(1.32);opacity:0}
}

.fx-stage-label{
  display:block;
  font-size:13px;
  font-weight:800;
  letter-spacing:.09em;
  text-transform:uppercase;
  color:var(--fx-muted);
  transition:color var(--fx-slow) var(--fx-ease) var(--fx-d);
  overflow-wrap:break-word;
}
.fx-pipe-in .fx-stage--done .fx-stage-label{color:rgba(138,82,9,.86)}
.fx-pipe-in .fx-stage--current .fx-stage-label{color:var(--fx-text)}

@media (max-width:600px){
  .fx-stage{min-width:104px;padding-right:10px}
}

/* ==========================================================================
   9. Chart bars  —  [data-bar] + data-bar-value
   ========================================================================== */

html.fx-js [data-bar]{
  --fx-bar:0%;
  --fx-d:0ms;
  transition:
    height var(--fx-settle) var(--fx-ease) var(--fx-d),
    width  var(--fx-settle) var(--fx-ease) var(--fx-d),
    opacity 420ms var(--fx-ease) var(--fx-d);
}
html.fx-js [data-bar]:not([data-bar-dir="right"]){height:var(--fx-bar);min-height:2px}
html.fx-js [data-bar][data-bar-dir="right"]{width:var(--fx-bar);min-width:2px}

/* ==========================================================================
   10. Sliding sidebar pill  —  [data-nav-pill]
   ========================================================================== */

[data-nav-pill]{position:relative;isolation:isolate}
[data-nav-pill] > a{position:relative;z-index:1}

.fx-nav-pill{
  position:absolute;
  left:0;
  top:0;
  z-index:0;
  width:0;
  height:0;
  border-radius:var(--radius-sm,8px);
  opacity:0;
  pointer-events:none;
  background:var(--blue-wash,#EEEFF6);
  transition:
    transform 300ms var(--fx-ease),
    width 300ms var(--fx-ease),
    height 300ms var(--fx-ease),
    opacity var(--fx-mid) var(--fx-ease);
}
.fx-nav-pill.is-on{opacity:1}
.fx-nav-pill.fx-nav-pill--hover{opacity:.5}
.fx-nav-pill--instant{transition:opacity var(--fx-mid) var(--fx-ease)}

/* The leading edge is a rule, 2px of solid accent — no glow. */
.fx-nav-pill::before{
  content:'';
  position:absolute;
  left:0;
  top:0;
  bottom:0;
  width:2px;
  border-radius:2px 0 0 2px;
  background:var(--fx-blue);
}

/* ==========================================================================
   11. Skeleton shimmer  —  .fx-skel
   ========================================================================== */

.fx-skel{
  position:relative;
  overflow:hidden;
  border-radius:8px;
  background:rgba(110,104,98,.08);
  min-height:12px;
}
.fx-skel::after{
  content:'';
  position:absolute;
  inset:0;
  transform:translateX(-100%);
  background:linear-gradient(90deg, transparent, rgba(246,245,242,.11) 46%, transparent);
  animation:fx-shimmer 1500ms linear infinite;
}
.fx-skel-line{height:12px;margin-bottom:10px}
.fx-skel-line:last-child{margin-bottom:0;width:62%}
.fx-skel-title{height:20px;width:44%;margin-bottom:16px;border-radius:6px}
.fx-skel-block{height:112px;border-radius:var(--fx-radius)}

@keyframes fx-shimmer{
  to{transform:translateX(100%)}
}

/* ==========================================================================
   12. Layered page enter  —  [data-cascade]
   Children rise in one after another; once everyone has arrived
   (.fx-cascade-done) the rules withdraw completely so child hover
   transitions and sticky positioning behave exactly as before.
   ========================================================================== */

html.fx-js .fx-page[data-cascade]{animation:none}

html.fx-js .fx-cascade:not(.fx-cascade-done) > *{
  opacity:0;
  transform:translateY(10px);
  transition:
    opacity 560ms var(--fx-ease) var(--fx-cd,0ms),
    transform 560ms var(--fx-ease) var(--fx-cd,0ms);
}
html.fx-js .fx-cascade.fx-cascade-in:not(.fx-cascade-done) > *{
  opacity:1;
  transform:none;
}

/* ==========================================================================
   13. Living shell  —  [data-shell-live]
   Corridor underlay + lazy cursor light. Both sit at z-index:-1: above the
   paper (body background propagates to the canvas) and below every panel.
   ========================================================================== */

.fx-shell-flow{
  position:fixed;
  inset:0;
  z-index:-1;
  pointer-events:none;
}
.fx-shell-flow svg{
  display:block;
  width:100%;
  height:100%;
}
.fx-shell-line{
  fill:none;
  stroke:rgba(44,62,140,.055);
  stroke-width:1;
  stroke-linecap:round;
}
.fx-shell-line--v{stroke:rgba(138,82,9,.05)}

/* Travelling impulses: one dash per two periods of a pathLength-normalised
   path, so the light enters, crosses and leaves with a long quiet gap. */
.fx-shell-pulse{
  fill:none;
  stroke:rgba(44,62,140,.16);
  stroke-width:1.2;
  stroke-linecap:round;
  stroke-dasharray:10 190;
  stroke-dashoffset:200;
  animation:fx-shell-travel 9s linear infinite;
}
.fx-shell-pulse--2{animation-duration:12s;animation-delay:-4s}
.fx-shell-pulse--3{
  stroke:rgba(138,82,9,.14);
  animation-duration:14s;
  animation-delay:-9s;
}
.fx-shell-pulse--4{
  stroke:rgba(63,107,95,.13);
  animation-duration:11s;
  animation-delay:-6s;
}
@keyframes fx-shell-travel{to{stroke-dashoffset:0}}

.fx-shell-light{
  position:fixed;
  left:0;
  top:0;
  z-index:-1;
  width:720px;
  height:720px;
  pointer-events:none;
  opacity:0;
  transition:opacity 900ms var(--fx-ease);
  background:radial-gradient(
    circle closest-side,
    rgba(255,255,255,.22),
    rgba(44,62,140,.025) 46%,
    transparent 70%
  );
  will-change:transform;
}
.fx-shell-light.is-on{opacity:1}

/* ==========================================================================
   14. Corridor flow line  —  [data-flow-line]
   The host element provides the box (give it a height for "h", a width for
   "v", both for "curve"); the SVG stretches to fill it.
   ========================================================================== */

.fx-flow{position:relative;display:block}
.fx-flow--h{min-height:14px}
.fx-flow--v{min-width:14px}
.fx-flow svg{
  display:block;
  width:100%;
  height:100%;
  overflow:visible;
}
.fx-flow--h svg{min-height:14px}

.fx-flow-base{
  fill:none;
  stroke:rgba(44,62,140,.14);
  stroke-linecap:round;
}
/* fx.js drives dasharray/dashoffset/opacity per frame in the shared loop
   (dash pattern lives in pixel space; the pulse pauses offscreen). Hidden
   until driven so the undashed path can never flash fully lit. */
.fx-flow-pulse{
  fill:none;
  stroke:var(--fx-flow-color,#2C3E8C);
  stroke-linecap:round;
  opacity:0;
  filter:drop-shadow(0 1px 2px rgba(44,62,140,.3));
}
.fx-flow--gold{--fx-flow-color:#8A5209}
.fx-flow--gold .fx-flow-base{stroke:rgba(138,82,9,.13)}
.fx-flow--gold .fx-flow-pulse{filter:drop-shadow(0 1px 2px rgba(138,82,9,.34))}
.fx-flow--teal{--fx-flow-color:#3F6B5F}
.fx-flow--teal .fx-flow-base{stroke:rgba(63,107,95,.13)}
.fx-flow--teal .fx-flow-pulse{filter:drop-shadow(0 1px 2px rgba(63,107,95,.3))}

/* ==========================================================================
   15. Trust ring  —  [data-trust-ring]
   Seven arc segments, thickness by component weight, CSS-3D cursor tilt,
   metal sheen, hover lifts a segment and names its score.
   ========================================================================== */

.fx-trust{
  position:relative;
  width:240px;
  max-width:100%;
  perspective:700px;
}
.fx-trust--s{width:124px}

.fx-trust-tilt{
  position:relative;
  transform-style:preserve-3d;
  will-change:transform;
}

.fx-trust-svg{
  display:block;
  width:100%;
  height:auto;
  overflow:visible;
}

.fx-trust-seg{
  outline:none;
  cursor:default;
  transition:transform 260ms var(--fx-ease), filter 260ms var(--fx-ease);
}
.fx-trust-seg:hover,
.fx-trust-seg:focus-visible{
  transform:translate(var(--fx-lx,0), var(--fx-ly,0));
  filter:drop-shadow(0 4px 8px rgba(31,44,104,.3));
}
.fx-trust-seg:focus-visible .fx-trust-track{stroke:rgba(44,62,140,.35)}

.fx-trust-track{
  fill:none;
  stroke:rgba(26,26,25,.10);
  stroke-linecap:butt;
}
.fx-trust-fill{
  fill:none;
  stroke:var(--fx-trust-fill,#2C3E8C);
  stroke-linecap:butt;
  transition:stroke-dasharray 1s var(--fx-ease);
}
.fx-trust-seg--full .fx-trust-fill{stroke:var(--fx-trust-full,#8A5209)}

/* Brushed-metal sheen: a rotating conic highlight masked to the ring band. */
.fx-trust-sheen{
  position:absolute;
  inset:0;
  border-radius:50%;
  pointer-events:none;
  background:conic-gradient(
    from 0deg,
    transparent 0deg 40deg,
    rgba(255,255,255,.42) 78deg,
    transparent 118deg 208deg,
    rgba(255,255,255,.20) 240deg,
    transparent 274deg 360deg
  );
  -webkit-mask:radial-gradient(circle closest-side,
    transparent 55%, #000 61%, #000 88%, transparent 93%);
  mask:radial-gradient(circle closest-side,
    transparent 55%, #000 61%, #000 88%, transparent 93%);
  mix-blend-mode:soft-light;
  animation:fx-trust-sheen-spin 16s linear infinite;
}
@keyframes fx-trust-sheen-spin{to{transform:rotate(360deg)}}

.fx-trust-center{
  position:absolute;
  inset:22%;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  text-align:center;
  pointer-events:none;
  transform:translateZ(24px);
}
.fx-trust-total{
  font-family:var(--font-display,serif);
  font-weight:700;
  font-variant-numeric:tabular-nums;
  font-size:clamp(18px, 22cqw, 44px);
  line-height:1;
  color:var(--blue-deep,#1F2C68);
}
.fx-trust--l .fx-trust-total{font-size:44px}
.fx-trust--s .fx-trust-total{font-size:24px}
.fx-trust-sub{
  margin-top:4px;
  font-size:13px;
  font-weight:800;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--muted,#5C5751);
}
.fx-trust--s .fx-trust-sub{display:none}

.fx-trust-tip{
  position:absolute;
  left:50%;
  top:0;
  z-index:2;
  transform:translate(-50%,-110%);
  padding:5px 10px;
  border-radius:8px;
  background:var(--panel,#FFFFFF);
  border:1px solid var(--line,rgba(26,26,25,.16));
  box-shadow:0 8px 22px -10px rgba(31,44,104,.35);
  font-size:13px;
  font-weight:700;
  color:var(--text,#1A1A19);
  white-space:nowrap;
  pointer-events:none;
  opacity:0;
  transition:opacity 200ms var(--fx-ease);
}
.fx-trust-tip.is-on{opacity:1}

/* ==========================================================================
   16. Deal track  —  [data-deal-track] > [data-stage]
   A tilted plane: minted gold behind, a pulsing present, a hazy future.
   Labels counter-rotate so they stand up from the plane like signposts.
   ========================================================================== */

.fx-deal{
  --fx-deal-tilt:24deg;
  --fx-deal-fill:0;
  perspective:820px;
  overflow-x:auto;
  overflow-y:hidden;
  padding:10px 2px 6px;
  scrollbar-width:thin;
  -webkit-overflow-scrolling:touch;
}

.fx-deal-plane{
  position:relative;
  display:flex;
  align-items:flex-start;
  min-width:0;
  /* Side clearance absorbs the widened near edge of the tilt: the perspective
     magnifies the plane's bottom corners and the counter-rotated labels, and
     without this margin their transformed boxes poke past the padding box and
     summon a permanent horizontal scrollbar on a track that fits. */
  margin:0 clamp(24px, 3%, 44px);
  transform:rotateX(var(--fx-deal-tilt));
  transform-style:preserve-3d;
  transform-origin:center 20%;
}

/* The plane is transformed, so it is the containing block for the rail. */
.fx-deal-rail{
  position:absolute;
  top:11.5px;
  height:3px;
  border-radius:2px;
  background:rgba(26,26,25,.12);
}
.fx-deal--mini .fx-deal-rail{top:6.5px;height:2px}
.fx-deal-rail > i{
  display:block;
  height:100%;
  width:100%;
  border-radius:2px;
  transform:scaleX(0);
  transform-origin:left center;
  background:linear-gradient(90deg, var(--fx-gold,#8A5209), #D8A63A);
  box-shadow:0 2px 5px -1px rgba(138,82,9,.3);
  transition:transform 900ms var(--fx-ease) 120ms;
}
.fx-deal-in .fx-deal-rail > i{transform:scaleX(var(--fx-deal-fill))}

.fx-deal-pulsedot{
  position:absolute;
  left:0;
  top:50%;
  width:11px;
  height:11px;
  margin-left:-5px;
  border-radius:50%;
  background:radial-gradient(circle, #FFF6D9, #8A5209 70%);
  box-shadow:0 2px 6px -1px rgba(138,82,9,.45);
  opacity:0;
  transform:translate3d(0,-50%,0);
  pointer-events:none;
}
.fx-deal-pulsedot.is-run{opacity:1}

.fx-dstage{
  --fx-d:0ms;
  position:relative;
  flex:1 1 0;
  min-width:96px;
  padding:0 6px;
  text-align:center;
  transform-style:preserve-3d;
}
.fx-deal--mini .fx-dstage{min-width:44px;padding:0 2px}

.fx-dstage-coin{
  position:relative;
  display:inline-flex;
  width:26px;
  height:26px;
  margin-bottom:9px;
  border-radius:50%;
  background:var(--bg2,#F6F5F2);
  border:2px solid rgba(26,26,25,.18);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5);
  opacity:0;
  transform:translateY(8px) scale(.7);
  transition:
    opacity 460ms var(--fx-ease) var(--fx-d),
    transform 460ms var(--fx-ease) var(--fx-d),
    background 460ms var(--fx-ease) var(--fx-d),
    border-color 460ms var(--fx-ease) var(--fx-d),
    box-shadow 460ms var(--fx-ease) var(--fx-d);
}
.fx-deal--mini .fx-dstage-coin{width:16px;height:16px;margin-bottom:6px;border-width:1.5px}
.fx-deal-in .fx-dstage-coin{opacity:1;transform:none}

/* Done: struck gold. The engraving is a drawn check, not an icon font. */
.fx-deal-in .fx-dstage--done .fx-dstage-coin{
  background:
    radial-gradient(circle at 34% 30%, #F2DEA0, transparent 58%),
    linear-gradient(145deg, #D8A63A, var(--fx-gold,#8A5209));
  border-color:rgba(138,82,9,.6);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.65),
    inset 0 -2px 3px rgba(26,26,25,.35),
    0 3px 10px -2px rgba(138,82,9,.55);
}
.fx-dstage--done .fx-dstage-coin::after{
  content:'';
  position:absolute;
  left:50%;
  top:50%;
  width:6px;
  height:11px;
  margin:-7px 0 0 -3px;
  border:solid #4A3608;
  border-width:0 2px 2px 0;
  transform:rotate(42deg) scale(0);
  transition:transform 380ms var(--fx-ease) calc(var(--fx-d) + 200ms);
}
.fx-deal--mini .fx-dstage--done .fx-dstage-coin::after{
  width:4px;height:7px;margin:-4.5px 0 0 -2px;border-width:0 1.5px 1.5px 0;
}
.fx-deal-in .fx-dstage--done .fx-dstage-coin::after{transform:rotate(42deg) scale(1)}

/* Current: lazur core with a breathing ring. */
.fx-deal-in .fx-dstage--current .fx-dstage-coin{
  background:radial-gradient(circle at 50% 45%, var(--blue,#2C3E8C) 0 40%, rgba(44,62,140,.14) 44%);
  border-color:rgba(44,62,140,.75);
  box-shadow:0 2px 6px -2px rgba(44,62,140,.34);
}
.fx-dstage--current .fx-dstage-coin::before{
  content:'';
  position:absolute;
  inset:-7px;
  border-radius:50%;
  border:1px solid rgba(44,62,140,.55);
  opacity:0;
}
.fx-deal-in .fx-dstage--current .fx-dstage-coin::before{
  animation:fx-pulse 2400ms var(--fx-ease-soft) infinite;
}

/* Future: present but in haze, deeper into the plane. */
.fx-dstage--todo{opacity:.55}
.fx-dstage--todo .fx-dstage-coin{transform:translateY(8px) scale(.7) translateZ(-14px)}
.fx-deal-in .fx-dstage--todo .fx-dstage-coin{transform:translateZ(-14px)}

.fx-dstage-label{
  display:block;
  font-size:13px;
  font-weight:800;
  letter-spacing:.07em;
  text-transform:uppercase;
  line-height:1.35;
  color:var(--muted,#5C5751);
  transform:rotateX(calc(var(--fx-deal-tilt) * -1));
  transform-origin:center top;
  overflow-wrap:break-word;
}
.fx-deal--mini .fx-dstage-label{font-size:13px;letter-spacing:.04em}
.fx-dstage--done .fx-dstage-label{color:var(--gold-ink,#7A4708)}
.fx-dstage--current .fx-dstage-label{color:var(--blue-deep,#1F2C68)}

/* The stamp: scale-snap + a gold flash ring. Ceremony, not confetti. */
.fx-dstage--minting .fx-dstage-coin{
  animation:fx-deal-stamp 780ms var(--fx-ease) both;
}
.fx-dstage--minting .fx-dstage-coin::before{
  content:'';
  position:absolute;
  inset:-4px;
  border-radius:50%;
  border:2px solid rgba(138,82,9,.9);
  animation:fx-deal-flash 780ms var(--fx-ease) both;
}
@keyframes fx-deal-stamp{
  0%  {transform:scale(1)}
  38% {transform:scale(1.42)}
  62% {transform:scale(.94)}
  100%{transform:scale(1)}
}
@keyframes fx-deal-flash{
  0%  {opacity:1;transform:scale(.6)}
  100%{opacity:0;transform:scale(2.1)}
}

/* ==========================================================================
   17. Depth stack  —  [data-depth-stack] > [data-depth]
   fx.js drives the transforms; layers only need to composite cleanly.
   ========================================================================== */

.fx-depth{position:relative}
.fx-depth [data-depth]{will-change:transform}

/* ==========================================================================
   18. Slide-over drawer  —  [data-drawer] (built by fx.js)
   A quick-action layer over the existing full-page forms. Glass panel from
   the right; full-screen at 700px; the page behind is scroll-locked.
   ========================================================================== */

/* display rules would beat the UA [hidden] default, so pin it explicitly:
   a closed drawer must never sit invisibly over the page eating clicks. */
.fx-drawer[hidden],
.fx-drawer-overlay[hidden]{display:none}

.fx-drawer-overlay{
  position:fixed;
  inset:0;
  z-index:90;
  background:rgba(26,26,25,.38);
  opacity:0;
  transition:opacity 260ms var(--fx-ease);
}
.fx-drawer-overlay.is-open{opacity:1}

.fx-drawer{
  position:fixed;
  top:0;
  right:0;
  bottom:0;
  z-index:95;
  width:min(560px, 100vw);
  display:flex;
  flex-direction:column;
  background:var(--panel,#FFFFFF);
  border-left:1px solid var(--fx-line);
  box-shadow:
    inset 1px 0 0 rgba(255,255,255,.9),
    -24px 0 54px -30px rgba(26,26,25,.34);
  transform:translateX(102%);
  transition:transform 320ms var(--fx-ease);
}
.fx-drawer.is-open{transform:none}
.fx-drawer:focus{outline:none}

.fx-drawer-head{
  display:flex;
  align-items:center;
  gap:14px;
  padding:18px 22px 14px;
  border-bottom:1px solid var(--fx-line);
  flex-shrink:0;
}
.fx-drawer-title{
  margin-right:auto;
  font-family:var(--font-display,sans-serif);
  font-size:19px;
  font-weight:600;
  letter-spacing:-.028em;
  line-height:1.25;
  color:var(--text,#1A1A19);
  overflow-wrap:break-word;
  min-width:0;
}
.fx-drawer-close{
  width:34px;
  height:34px;
  flex-shrink:0;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  border-radius:var(--radius-sm,8px);
  border:1px solid var(--fx-line);
  background:var(--bg2,#F6F5F2);
  color:var(--fx-muted);
  cursor:pointer;
  transition:color var(--fx-fast) var(--fx-ease),
             border-color var(--fx-fast) var(--fx-ease),
             background var(--fx-fast) var(--fx-ease);
}
.fx-drawer-close svg{width:12px;height:12px}
.fx-drawer-close:hover{
  color:var(--fx-text);
  border-color:rgba(44,62,140,.5);
  background:rgba(44,62,140,.08);
}

.fx-drawer-body{
  flex:1;
  overflow-y:auto;
  padding:20px 22px 30px;
  -webkit-overflow-scrolling:touch;
}
/* Inside the narrow panel every two-column form row folds to one. */
.fx-drawer-body .grid.g2{grid-template-columns:1fr}

/* The page behind holds still while the dialog is up. */
html.fx-drawer-lock,
html.fx-drawer-lock body{overflow:hidden}

@media (max-width:700px){
  .fx-drawer{width:100vw;border-left:0}
}

/* ==========================================================================
   19. Reduced motion — every effect jumps straight to its end state.
   ========================================================================== */

@media (prefers-reduced-motion: reduce){
  html.fx-js [data-reveal],
  html.fx-js [data-reveal].fx-in{
    opacity:1;
    translate:none;
    transform:none;
    transition:none;
  }
  html.fx-js .fx-page{animation:none}
  html.fx-js [data-bar]{transition:none}

  .fx-lift,
  .fx-tilt-glare,
  .fx-stage-dot,
  .fx-stage-rail > i,
  .fx-stage-label,
  .fx-stage-dot::after,
  .fx-nav-pill,
  .fx-ribbon [data-ribbon-node]{transition:none}

  .fx-lift:hover{transform:none}
  .fx-pipe-in .fx-stage--current .fx-stage-dot::before{animation:none;opacity:0}
  .fx-skel::after{animation:none;opacity:.35;transform:none}
  .fx-ripple{animation:none;display:none}
  .fx-ribbon-flow{opacity:.5}

  /* v4 hooks: full static equivalents, never emptiness. */
  html.fx-js .fx-cascade:not(.fx-cascade-done) > *,
  html.fx-js .fx-cascade.fx-cascade-in:not(.fx-cascade-done) > *{
    opacity:1;
    transform:none;
    transition:none;
  }

  /* Corridors stay drawn; each keeps one still highlight mid-route
     (fx.js paints the flow-line equivalent inline). */
  .fx-shell-pulse{
    animation:none;
    stroke-dashoffset:130;
    opacity:.5;
  }
  .fx-shell-light{display:none}

  .fx-trust-seg,
  .fx-trust-fill,
  .fx-trust-tip{transition:none}
  .fx-trust-seg:hover,
  .fx-trust-seg:focus-visible{transform:none}
  .fx-trust-sheen{animation:none}
  .fx-trust-tilt{transform:none !important}

  .fx-deal-rail > i,
  .fx-dstage-coin,
  .fx-dstage-coin::after{transition:none}
  .fx-deal-in .fx-deal-rail > i{transform:scaleX(var(--fx-deal-fill))}
  .fx-deal-in .fx-dstage--current .fx-dstage-coin::before{animation:none;opacity:0}
  .fx-dstage--minting .fx-dstage-coin{animation:none}
  .fx-dstage--minting .fx-dstage-coin::before{animation:none;opacity:0}
  .fx-deal-pulsedot{display:none}

  .fx-depth [data-depth]{transform:none !important;will-change:auto}

  /* Drawer appears in place, no slide. */
  .fx-drawer,
  .fx-drawer-overlay,
  .fx-drawer-close{transition:none}
  .fx-drawer{transform:none;visibility:hidden}
  .fx-drawer.is-open{visibility:visible}
}

/* ==========================================================================
   Stage 2 additions (additive). Grouped sidebar sections and the shared media
   library pieces. Nothing here redefines an app.css class.
   ========================================================================== */

/* ---- Sidebar sections (base.html, helpers.nav_sections) ----
   The nav pill of fx.js measures a link against the nav container, so the
   section wrappers stay unpositioned and every link is lifted above the
   pill exactly like a direct child. */
[data-nav-pill] .nav-link{position:relative;z-index:1}
.nav-section{margin-top:10px}
.nav-section-head{
  position:relative;z-index:1;
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  width:100%;margin:0;padding:8px 12px 6px;border:0;border-radius:8px;
  background:none;cursor:pointer;text-align:left;
  font-family:var(--font-body);font-size:13px;font-weight:600;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted-2,var(--fx-muted));
  transition:background var(--fx-fast) var(--fx-ease),color var(--fx-fast) var(--fx-ease);
}
.nav-section-head:hover{background:var(--sunk,#F6F5F2);color:var(--text,var(--fx-text))}
.nav-section-head:focus-visible{outline:2px solid rgba(44,62,140,.65);outline-offset:-2px}
.nav-section-head svg{
  width:12px;height:12px;flex-shrink:0;color:var(--muted,var(--fx-muted));
  transition:transform var(--fx-mid) var(--fx-ease);
}
.nav-section.is-open .nav-section-head svg{transform:rotate(180deg)}
.nav-section.is-admin .nav-section-head{color:var(--gold-ink,var(--fx-gold))}
.nav-section-body{display:grid;grid-template-rows:0fr}
/* The fold animates only after the first paint (base.html adds .nav-anim a
   frame later), so the initial state never animates into place and the nav
   pill measures settled rows. */
.nav-anim .nav-section-body{transition:grid-template-rows var(--fx-mid) var(--fx-ease)}
/* Vertical padding cancelled by a negative margin: the box clips nothing of a
   focus ring above or below, yet measures 0px when the track is collapsed. */
.nav-section-body > div{
  min-height:0;overflow:hidden;display:flex;flex-direction:column;gap:2px;
  padding:4px 0;margin:-4px 0;
}
.nav-section.is-open .nav-section-body{grid-template-rows:1fr}
/* Without the script nothing can open a section, so every section is open. */
html:not(.fx-js) .nav-section-body{grid-template-rows:1fr}
html:not(.fx-js) .nav-section-head svg{display:none}
.nav-pinned{display:flex;flex-direction:column;gap:2px}
/* The rail scrolls vertically only: a long section label (RU "Администрирование")
   must tighten and ellipse, never paint a horizontal scrollbar into the rail. */
.nav{scrollbar-width:thin;overflow-x:hidden}
.nav-section-head{gap:6px;padding-right:10px}
.nav-section-head > span{
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  letter-spacing:.02em;
}

/* ---- Avatar with a real logo (logo_url replaces the monogram) ---- */
.avatar img,.avatar-logo img{
  width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;
}
.avatar.has-logo{background:var(--panel,var(--fx-panel));box-shadow:inset 0 0 0 1px var(--line,var(--fx-line))}
.avatar.has-logo img{object-fit:contain;padding:3px;background:var(--panel,#FFFFFF)}

/* ---- Media slots, gallery and upload form ---- */
.media-form{display:flex;flex-direction:column;gap:6px}
.media-hint,.media-empty{color:var(--muted,var(--fx-muted));font-size:13px;line-height:1.55;margin:0 0 10px}
.media-empty{margin:4px 0 8px}
.media-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:6px}
.media-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));gap:12px;
  margin:4px 0 14px;
}
.media-grid-compact{grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:8px;margin:4px 0 10px}
.media-item{display:flex;flex-direction:column;gap:6px;min-width:0;margin:0}
.media-thumb{
  position:relative;display:flex;align-items:center;justify-content:center;
  aspect-ratio:1/1;overflow:hidden;border-radius:var(--radius,10px);
  background:var(--bg2,#F6F5F2);border:1px solid var(--line,var(--fx-line));
  color:var(--blue-deep,var(--fx-blue-deep));text-decoration:none;
  transition:border-color var(--fx-fast) var(--fx-ease),box-shadow var(--fx-fast) var(--fx-ease),transform var(--fx-fast) var(--fx-ease);
}
.media-thumb:hover{border-color:rgba(44,62,140,.45);box-shadow:0 10px 24px -16px rgba(31,44,104,.35);transform:translateY(-1px)}
.media-thumb:focus-visible{outline:2px solid rgba(44,62,140,.65);outline-offset:2px}
.media-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.media-thumb-logo img{object-fit:contain;padding:10px;background:var(--panel,#FFFFFF)}
.media-thumb-doc,.media-thumb-link{flex-direction:column;gap:4px;padding:8px;text-align:center}
.media-thumb-label{
  font-family:var(--font-display);font-weight:600;font-size:15px;letter-spacing:-.02em;
}
.media-thumb-name{
  font-size:13px;color:var(--muted,var(--fx-muted));max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.media-caption{
  font-size:13px;color:var(--muted,var(--fx-muted));
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.media-delete{margin:0}
.btn-danger-soft{color:var(--red,#A33A2A)}
.btn-danger-soft:hover{border-color:rgba(163,58,42,.45);color:var(--red,#A33A2A)}
.media-video{
  position:relative;grid-column:1 / -1;aspect-ratio:16/9;border-radius:var(--radius,10px);overflow:hidden;
  border:1px solid var(--line,var(--fx-line));background:var(--bg2,#F6F5F2);
}
.media-video iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.media-slots{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}
.media-slot{
  display:flex;flex-direction:column;gap:4px;padding:12px 14px;border-radius:var(--radius,10px);
  background:var(--panel,#FFFFFF);border:1px solid var(--line,var(--fx-line));min-width:0;
}
.media-slot-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px}
.media-slot-label{
  font-size:13px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;
  color:var(--muted-2,var(--fx-muted));
}
.media-slot-count{font-size:13px;color:var(--muted,var(--fx-muted))}
.media-slot-btn{align-self:flex-start;margin-top:auto}
input[type=file]{padding:8px 10px;font-size:14px;cursor:pointer}
input[type=file]::file-selector-button{
  margin-right:10px;padding:6px 12px;border-radius:6px;border:1px solid var(--line,var(--fx-line));
  background:var(--bg2,#F6F5F2);color:var(--text,var(--fx-text));font-family:var(--font-body);
  font-weight:600;font-size:13px;cursor:pointer;
  transition:border-color var(--fx-fast) var(--fx-ease),background var(--fx-fast) var(--fx-ease);
}
input[type=file]::file-selector-button:hover{border-color:var(--blue-edge,rgba(44,62,140,.30));background:var(--panel,#FFFFFF)}

@media (max-width:600px){
  .media-slots{grid-template-columns:1fr}
  .media-grid{grid-template-columns:repeat(auto-fill,minmax(104px,1fr))}
}

@media (prefers-reduced-motion:reduce){
  .nav-section-body,.nav-section-head,.nav-section-head svg,.media-thumb,
  input[type=file]::file-selector-button{transition:none}
  .media-thumb:hover{transform:none}
}
