/* ==========================================================================
   bridge.css — the FashionJet case: events in, one truth, two marketplaces.

   The picture is the bridge read left to right: webhooks from the shop pass
   the signature check and become rows in PostgreSQL; prices and stock go out
   over three lanes to Wildberries and Yandex Market; the catalogue goes out
   as one package; and monitoring, under the database, keeps comparing
   the marketplace with the database and fixes the row that drifted.

   Division of labour, the same one every other figure here uses:

     this file            the frame and the look
     bridge-timeline.css  where everything stands and when it happens, both
                          generated from the one schedule in tools/bridge.py

   Everything takes the page's own colour (--c, blue on this case) except
   the three lanes, which take three neighbouring steps of the ramp — by
   class, never by a style attribute: the site's CSP forbids inline styles.

   Under 1000px it stops being a picture: the line goes and the nine stations
   become a list in reading order, each with the one line the drawing had no
   room for. That state and prefers-reduced-motion share one block at the
   foot of this file — both want the bridge with everything landed, still.
   ========================================================================== */

.fj {
  --fj-cycle: 24s;           /* bridge-timeline.css sets the real ones */
  --fj-ratio: 1440 / 440;
  --fj-row-fill: color-mix(in oklab, currentColor 32%, var(--bg));
  --fj-row-stroke: currentColor;
  --fj-bad-fill: color-mix(in oklab, var(--c-coral) 45%, var(--bg));
  --fj-good-fill: color-mix(in oklab, var(--c-teal) 45%, var(--bg));
  margin: var(--s-5) 0 var(--s-6);
}
.fj, .fj figure { margin-inline: 0; }

/* --- the caption ---------------------------------------------------------- */
.fj__cap {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2);
  margin-bottom: var(--s-4);
}
.fj__h {
  font-weight: 640; font-size: var(--fs-md); letter-spacing: -.015em;
  color: var(--ink);
}
.fj__note {
  flex: 1 1 22ch; min-width: 0;
  font-size: var(--fs-3xs); line-height: 1.45; color: var(--ink-3);
}

/* --- the stage ------------------------------------------------------------ */
.fj__body { position: relative; aspect-ratio: var(--fj-ratio); }
.fj__art {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; pointer-events: none; overflow: visible;
  color: var(--c, var(--c-blue));
}

/* --- the names, over their bands ------------------------------------------ */
.fj__names { position: absolute; inset: 0; z-index: 2; }
.fj-name {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--w); height: var(--h);
  display: grid; align-content: center; justify-items: center;
  padding: 0 4px;
  opacity: .3;
}
.fj-name__n {
  font-weight: 640; font-size: var(--fs-2xs); letter-spacing: -.01em;
  line-height: 1.2; color: var(--ink); text-align: center;
}
.fj-name__s { display: none; }
/* The shop and the database are lit from the start. */
.fj-name--src, .fj-name--db, .fj-name--table, .fj-name--tile { opacity: 1; }
/* A lane's name is one line of mono, left of centre, next to its LED; a
   table's label is the same, at the left of its rows. */
.fj-name--lane, .fj-name--table { justify-items: start; padding: 0; }
.fj-name--lane .fj-name__n, .fj-name--table .fj-name__n {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-3xs);
  letter-spacing: 0; text-align: left; color: var(--ink); white-space: nowrap;
}
.fj-name--table .fj-name__n { color: var(--ink-2); }
.fj-name--wb, .fj-name--ym { justify-items: start; padding: 0; }
.fj-name--wb .fj-name__n, .fj-name--ym .fj-name__n { text-align: left; }

/* --- the wires ------------------------------------------------------------ */
.fj-wire {
  fill: none; stroke: currentColor; stroke-width: 1.3;
  stroke-linecap: round; opacity: .22;
  vector-effect: non-scaling-stroke;
}

/* --- the ray -------------------------------------------------------------- */
/* A curve that is not drawn at rest. What is visible is a dash running along
   it — the comparison happening right now — and the hairline it leaves. */
.fj-ray__trace {
  fill: none; stroke: currentColor; stroke-width: 1;
  stroke-dasharray: 3 4; opacity: 0;
  vector-effect: non-scaling-stroke;
}
.fj-ray {
  fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round;
  stroke-dasharray: 80 1000; stroke-dashoffset: 1160; opacity: 0;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 3px color-mix(in oklab, currentColor 60%, transparent));
}

/* --- a box ---------------------------------------------------------------- */
.fj-box { opacity: .28; }
.fj-box__r {
  fill: color-mix(in oklab, currentColor 7%, var(--surface));
  stroke: color-mix(in oklab, currentColor 30%, var(--line));
  stroke-width: 1.2;
}
.fj-box--src, .fj-box--db { opacity: 1; }
.fj-fig { opacity: .1; }
.fj-fig--src, .fj-fig--db { opacity: 1; }
.fj-glyph path {
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.fj-led {
  fill: currentColor; opacity: .15;
  filter: drop-shadow(0 0 4px currentColor);
}

/* the lanes take their own colour, one step of the ramp each */
.fj-lane { opacity: .28; }
.fj-lane--0 { color: var(--c-cyan); }
.fj-lane--1 { color: var(--c-indigo); }
.fj-lane--2 { color: var(--c-violet); }

/* --- the shop's three doors ----------------------------------------------- */
/* A door per way the data comes out — the database, or a CSV export —
   stacked, each a glyph and its label; it brightens as a change leaves. */
.fj-tile { opacity: .55; }
.fj-tile__r {
  fill: color-mix(in oklab, currentColor 10%, var(--bg));
  stroke: color-mix(in oklab, currentColor 45%, var(--line)); stroke-width: 1;
}
.fj-name--tile { justify-items: start; padding: 0; }
.fj-name--tile .fj-name__n {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-3xs);
  letter-spacing: 0; text-align: left; color: var(--ink); white-space: nowrap;
}

/* --- the tables ----------------------------------------------------------- */
/* A row is three cells; the rows already there are solid, the ones waiting
   for an event are dashed, and the one an event lands on becomes solid. */
.fj-row__c {
  fill: var(--fj-row-fill);
  stroke: var(--fj-row-stroke); stroke-width: 1;
}
.fj-slot__c {
  fill: none; stroke: currentColor; stroke-width: 1;
  stroke-dasharray: 3 3; opacity: .55;
}
/* The landed row is drawn over its dashed slot: the same cells, solid. */
.fj-land { opacity: 0; }

/* --- the gate ------------------------------------------------------------- */
.fj-gate__glow {
  fill: currentColor; opacity: 0;
  filter: blur(14px);
}

/* --- the marketplaces ----------------------------------------------------- */
.fj-mslot {
  fill: none; stroke: currentColor; stroke-width: 1.1;
  stroke-dasharray: 3 3; opacity: 1;
}
/* the still copy of the package, for when nothing may move */
.fj-ghost { display: none; }

/* --- what travels --------------------------------------------------------- */
.fj-go { opacity: 0; }
.fj-env__box {
  fill: color-mix(in oklab, currentColor 30%, var(--bg));
  stroke: currentColor; stroke-width: 1.1;
}
.fj-env__flap {
  fill: none; stroke: currentColor; stroke-width: 1.1;
  stroke-linecap: round; stroke-linejoin: round;
}
.fj-dot__c { fill: currentColor; }
.fj-rowbar__c { fill: var(--fj-row-fill); stroke: currentColor; stroke-width: 1; }
.fj-csv__page {
  fill: color-mix(in oklab, currentColor 25%, var(--bg));
  stroke: currentColor; stroke-width: 1.1; stroke-linejoin: round;
}
.fj-csv__line { fill: none; stroke: currentColor; stroke-width: 1.1; stroke-linecap: round; }
.fj-card__box {
  fill: color-mix(in oklab, currentColor 20%, var(--bg));
  stroke: currentColor; stroke-width: 1.1;
}
.fj-card__pic {
  fill: none; stroke: currentColor; stroke-width: 1.1;
  stroke-linecap: round; stroke-linejoin: round;
}
.fj-card__line { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; opacity: .7; }
.fj-chg, .fj-ev, .fj-dot, .fj-pkg-go {
  filter: drop-shadow(0 0 4px color-mix(in oklab, currentColor 55%, transparent));
}
/* the verdict on a repeat: a cross in coral, in the envelope's corner */
.fj-badge { opacity: 0; }
.fj-badge circle { fill: var(--c-coral); stroke: var(--bg); stroke-width: 1; }
.fj-badge path {
  fill: none; stroke: var(--bg); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}

/* --- one clock ------------------------------------------------------------ */
.fj-box, .fj-fig, .fj-name, .fj-led, .fj-tile, .fj-gate__glow, .fj-land, .fj-mslot,
.fj-badge, .fj-ray, .fj-ray__trace, .fj-row--watch .fj-row__c, .fj-go {
  animation-duration: var(--fj-cycle);
  animation-timing-function: var(--ease-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.fj-go, .fj-led, .fj-gate__glow, .fj-ray {
  animation-timing-function: linear;
}

/* --------------------------------------------------------------------------
   Below 1000px the picture is a list.

   Ten stations across need ten columns; there is room for two, and a line
   drawn over a stack says nothing. So the drawing goes and the stations
   become what they always were — ten named things in the order the work
   passes through them, each with the one line the drawing had no room for.
   -------------------------------------------------------------------------- */
@media (max-width: 1000px) {
  .fj__body { aspect-ratio: auto; }
  .fj__art { display: none; }
  .fj__names {
    position: static;
    display: grid; gap: var(--s-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .fj-name--table, .fj-name--tile { display: none; }
  .fj-name, .fj-name--lane {
    position: relative; left: auto; top: auto;
    width: auto; height: auto; min-height: 0;
    align-content: center; justify-items: start;
    padding: var(--s-3);
    border: 1px solid var(--line); border-radius: 8px;
    background: color-mix(in oklab, var(--c, var(--c-blue)) 5%, var(--surface));
  }
  .fj-name__n, .fj-name--lane .fj-name__n {
    font-family: inherit; font-weight: 640; font-size: var(--fs-2xs);
    text-align: left; white-space: normal;
  }
  .fj-name__s {
    display: block; margin-top: 2px;
    font-family: var(--font-mono); font-size: var(--fs-3xs);
    line-height: 1.3; color: var(--ink-2); text-align: left;
  }
}

@media (max-width: 620px) {
  .fj__names { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   The bridge, finished and still — for the two contexts that want a picture
   that has stopped: every row landed, the catalogue on both marketplaces,
   the drifted row fixed, and the hairline of the comparison left in place.
   -------------------------------------------------------------------------- */
@media (max-width: 1000px), (prefers-reduced-motion: reduce) {
  .fj-go { display: none; }
  .fj-box, .fj-fig, .fj-name, .fj-led, .fj-tile, .fj-gate__glow, .fj-land, .fj-mslot,
  .fj-badge, .fj-ray, .fj-ray__trace, .fj-row--watch .fj-row__c {
    animation: none;
  }
  .fj-box, .fj-fig, .fj-name { opacity: 1; }
  .fj-lane { opacity: .8; }
  .fj-tile { opacity: .8; }
  .fj-led { opacity: .6; }
  .fj-ray__trace { opacity: .35; }
  .fj-land { opacity: 1; }
  .fj-row--watch .fj-row__c { fill: var(--fj-good-fill); stroke: var(--c-teal); }
  .fj-mslot { display: none; }
  .fj-ghost { display: block; }
}
