/* ==========================================================================
   learning.css — the corporate learning case, as a running simulation.

   The picture: the operations centre with its three square modules, the two
   systems that were kept beside it in one column, and one data bus joining
   those two into the centre. Nothing was rewritten in the project, so the old
   systems are drawn as separate boxes with a dashed edge.

   What plays on it is one employee's pass through the platform — queue,
   onboarding, a course in the LMS, tests the admin assigned, articles in the
   wiki, tests again, gone — with every finished activity sending one visible
   packet along the bus into reporting.

   This file is the frame and the parts. Every keyframe is generated from the
   schedule in tools/learning.py into learning-timeline.css, which is linked
   after it; the names wired up here are the ones it defines.
   ========================================================================== */

.lrn { margin: var(--s-5) 0 var(--s-6); }

/* --- the queue ------------------------------------------------------------ */
/* Who is waiting: the third block in the left-hand column, under the two old
   systems, and a corridor rather than a list — hatched floor, and an open mouth
   on the right where the walk into the platform begins. The line serves one
   person a cycle, closes up behind them and takes one more at the back, which
   is why the figure that walks the platform starts here. */
.lrn__queue {
  position: relative; isolation: isolate;
  display: flex; align-items: center; justify-content: flex-end;
  gap: 9px; min-height: 26px;
  margin-top: auto; padding: 3px 20px 3px 8px;
}
.lrn__queue::before {                /* the floor, fading out at the mouth */
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  background: repeating-linear-gradient(90deg,
    color-mix(in oklab, var(--line) 55%, transparent) 0 1px,
    transparent 1px 14px);
  -webkit-mask-image: linear-gradient(to right, #000 82%, transparent);
          mask-image: linear-gradient(to right, #000 82%, transparent);
}

.lrn-q {
  display: block; width: 15px; height: 20px;
  animation-timing-function: var(--ease-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-q .lrn-actor__fig { width: 15px; height: 20px; fill: var(--ink-3); }
.lrn-q--0 { animation-name: lrn-q-front; }
.lrn-q--0 .lrn-actor__fig {         /* the one about to go in */
  fill: var(--c);
  filter: drop-shadow(0 0 6px color-mix(in oklab, var(--c) 45%, transparent));
}
.lrn-q--1 .lrn-actor__fig {         /* and the one that becomes it */
  animation-name: lrn-q-next;
  animation-timing-function: steps(1, end);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-q--new { animation-name: lrn-q-new; }

/* --- the stage ------------------------------------------------------------ */
.lrn__body {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.78fr) clamp(74px, 9vw, 128px) minmax(0, 1.3fr);
  align-items: stretch;              /* the left column matches the centre's height */
  padding-bottom: 34px;              /* the strip the bus runs along, outside
                                        every block it serves */
}

/* --- the two systems that were kept -------------------------------------- */
/* Three blocks — the two old systems and the people waiting — sharing the
   height of the operations centre beside them, a third each. */
.lrn__legs {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-rows: repeat(3, minmax(0, 1fr));
  gap: var(--s-3);
}
.lrn-leg {
  display: grid; gap: 3px; align-content: start;
  padding: var(--s-3) var(--s-4);
  border: 1px dashed var(--line); border-radius: var(--r-md);
  background: rgba(12, 18, 32, .45);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-leg__tag {
  font-family: var(--font-mono); font-size: var(--fs-3xs);
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3);
}
.lrn-leg__n { font-size: var(--fs-sm); font-weight: 640; color: var(--ink-2); }
.lrn-leg__t { font-size: var(--fs-3xs); line-height: 1.45; color: var(--ink-3); }
.lrn-leg--users {                   /* not a system: no dashed border, a floor */
  border-style: solid;
  border-color: color-mix(in oklab, var(--line) 70%, transparent);
  background: rgba(10, 15, 28, .4);
}

/* --- the bus -------------------------------------------------------------- */
.lrn__lanes { position: relative; align-self: stretch; }
/* The bus is named at the head of its own spine, in the one part of its column
   nothing else uses: the corridor sits at the foot of the column now, and no
   walk goes above the landing. Centred on the spine rather than on the column,
   and in the same blue, weight and tracking as the rest of the bus, so the name
   reads as part of it. */
.lrn__bus {
  position: absolute; left: 0; right: 17%; top: 3%;
  font-family: var(--font-mono); font-size: var(--fs-3xs); font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  line-height: 1.45; text-align: center;
  color: color-mix(in oklab, var(--c-blue) 80%, var(--ink-3));
  text-shadow: 0 0 10px color-mix(in oklab, var(--c-blue) 25%, transparent);
}

/* One drawing over the whole figure, so the bus, the lane into reporting and
   every packet share one set of coordinates. Above the panels, because the
   lane runs across the centre's own bottom padding. */
.lrn__wire {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%; pointer-events: none; overflow: visible;
}
.lrn__wire path { vector-effect: non-scaling-stroke; }

/* One bus, one drawing language: the same blue, the same round caps, the same
   weight everywhere — spine and branches alike, because it is all one
   conductor. Only the junctions where something taps in are heavier. */
.lrn-wire__line, .lrn-wire__trunk {
  fill: none; stroke: var(--c-blue);
  stroke-linecap: round; stroke-linejoin: round;
}
.lrn-wire__trunk,
.lrn-wire__line  { stroke-width: 2px; opacity: .7; }
/* The platform's own wiring, inside its frame: same geometry language, half a
   step quieter, because it is not the shared bus. */
.lrn-wire__in {
  fill: none; stroke: var(--c-blue); opacity: .35;
  stroke-width: 1.4px; stroke-linecap: round; stroke-linejoin: round;
}

/* A junction is a round dot, not an oval: a circle in a viewBox stretched this
   far comes out as an ellipse, so each one is a zero-length path with a round
   cap and a stroke that does not scale — the cap's width is its diameter, in
   screen pixels, whatever the figure's aspect. */
.lrn-wire__tap {
  fill: none; stroke: var(--c-blue); opacity: .9;
  stroke-width: 6px; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.lrn-wire__tap--in { stroke-width: 4.5px; opacity: .5; }   /* inside, quieter */

/* What travels the channel is a named thing, not a dot: a course finished, a
   test marked, an article read. The block pops out of the module it came from,
   rides the bus and the lane, and closes into reporting. */
/* An activity in progress and the fact it sends: one object with two faces.
   The bar stands in the card while the work is going on and fills; when it is
   full the block turns over to its label, and that is what travels. Both faces
   carry the footprint of the bar they end up on. */
.lrn-blk {
  position: absolute; z-index: 5;
  transform: translate(-50%, -50%);
  perspective: 320px;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-blk__flip {
  position: relative; display: block;
  transform-style: preserve-3d;
  animation-timing-function: var(--ease-in-out, ease-in-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-blk__face {
  box-sizing: border-box;
  min-width: 96px; height: 17px;
  display: flex; align-items: center; gap: 5px;
  padding: 0 6px;
  border: 1px solid color-mix(in oklab, var(--c-teal) 60%, transparent);
  border-radius: 5px;
  background: color-mix(in oklab, var(--c-teal) 14%, var(--bg));
  color: color-mix(in oklab, var(--c-teal) 75%, var(--ink));
  font: 600 9px/1 var(--f-mono, ui-monospace, monospace);
  letter-spacing: .02em; white-space: nowrap;
  backface-visibility: hidden;
}
.lrn-blk__face--bar { padding: 3px; }        /* a track, and nothing else */
.lrn-blk__face--bar > b {
  display: block; width: 100%; height: 100%;
  border-radius: 3px;
  transform-origin: left center;
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--c-teal) 60%, transparent),
    color-mix(in oklab, var(--c) 55%, transparent));
  animation-timing-function: linear;         /* it fills at one rate */
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-blk--grade {                            /* a verdict, not a record */
  --c-star: #f2c14e;
}
.lrn-blk--grade .lrn-blk__face {
  border-color: color-mix(in oklab, var(--c-star) 55%, transparent);
  background: color-mix(in oklab, var(--c-star) 10%, var(--bg));
  color: color-mix(in oklab, var(--c-star) 80%, var(--ink));
}
.lrn-blk--grade .lrn-blk__face--bar > b {
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--c-star) 60%, transparent),
    color-mix(in oklab, var(--c-star) 32%, transparent));
}
.lrn-blk__face--star {                       /* the star itself, in gold */
  position: absolute; top: 0; left: 0;
  transform: rotateY(180deg);
  justify-content: center;
  border-color: color-mix(in oklab, var(--c-star) 75%, transparent);
  background: color-mix(in oklab, var(--c-star) 18%, var(--bg));
  color: var(--c-star);
  box-shadow: 0 0 12px color-mix(in oklab, var(--c-star) 45%, transparent);
  font-weight: 700;
}

.lrn-blk__face--done {                       /* the fact, on the back */
  position: absolute; top: 0; left: 0;
  transform: rotateY(180deg);
  background: color-mix(in oklab, var(--c-teal) 22%, var(--bg));
  box-shadow: 0 0 10px color-mix(in oklab, var(--c-teal) 35%, transparent);
}
.lrn-blk__face--done::before {               /* the discrete-fact mark */
  content: ""; flex: none;
  width: 5px; height: 5px;
  background: var(--c-teal);
}


/* --- the centre, and its modules ----------------------------------------- */
.lrn__centre {
  position: relative; z-index: 1;
  padding: var(--s-4);               /* nothing runs inside it any more */
  border: 1px solid color-mix(in oklab, var(--c) 40%, var(--line));
  border-radius: var(--r-lg);
  background:
    radial-gradient(ellipse 90% 130% at 50% 0%, color-mix(in oklab, var(--c) 10%, transparent), transparent 70%),
    var(--surface);
}
.lrn__centre-n {
  display: block;
  font-size: clamp(17px, 1.4vw, 22px); font-weight: 700; letter-spacing: -.015em;
}
.lrn__centre-t { display: block; margin-top: 2px; font-size: var(--fs-2xs); color: var(--ink-3); }
.lrn__mods {
  list-style: none; margin: var(--s-3) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-2);
}
.lrn-mod {
  position: relative; aspect-ratio: 1;
  display: grid; gap: 5px; align-content: start;
  padding: var(--s-3);
  border: 1px solid var(--line); border-top: 2px solid var(--line);
  border-radius: var(--r-sm);
  background: rgba(9, 14, 26, .5);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-mod__n { font-size: var(--fs-xs); font-weight: 640; line-height: 1.25; }
.lrn-mod__t { font-size: var(--fs-3xs); line-height: 1.45; color: var(--ink-3); }
/* Pinned to the foot of its square: align-content packs the rows at the top, so
   there is no spare space for an auto margin to take. */
/* How much of the pass has landed here. The bar has exactly a test label's
   footprint — same width, same height, same radius — so the label that arrives
   lands on it and covers it. The count sits just above its right end. All the states sit in one place and take turns,
   because a keyframe cannot rewrite text. */
.lrn-mod__pct {
  position: absolute; right: var(--s-3); bottom: calc(var(--s-3) + 22px);
  font: 700 9px/1 var(--f-mono, ui-monospace, monospace);
  letter-spacing: .04em;
  color: color-mix(in oklab, var(--c-teal) 75%, var(--ink));
  animation-timing-function: steps(1, end);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}

.lrn-mod__bar {
  position: absolute; right: var(--s-3); bottom: var(--s-3);
  display: block; width: 96px; height: 17px;
  border: 1px solid color-mix(in oklab, var(--c-teal) 30%, transparent);
  border-radius: 4px;
  background: rgba(255, 255, 255, .05);
  overflow: hidden;
}
.lrn-mod__bar > i {
  display: block; width: 100%; height: 100%;
  transform-origin: left center;
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--c-teal) 55%, transparent),
    color-mix(in oklab, var(--c) 55%, transparent));
  animation-name: lrn-bar;
  animation-timing-function: steps(1, end);   /* a quarter per arrival, not a slide */
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}

/* The admin's own bar: the work of setting the tests, before they exist. Same
   footprint and colour as everything else that fills. */
.lrn-set {
  position: absolute; z-index: 4;
  box-sizing: border-box;
  width: 96px; height: 17px; padding: 3px;
  border: 1px solid color-mix(in oklab, var(--c-blue) 55%, transparent);
  border-radius: 5px;
  background: color-mix(in oklab, var(--c-blue) 10%, var(--bg));
  transform: translate(-50%, -50%);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-set > b {
  display: block; width: 100%; height: 100%;
  border-radius: 3px;
  transform-origin: left center;
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--c-blue) 60%, transparent),
    color-mix(in oklab, var(--c) 45%, transparent));
  animation-name: lrn-set-fill;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}

/* The tests the admin creates, stacked in the testing module where it makes
   them. Two faces on one card: the test, and the test passed. Sitting it turns
   the card over — the only thing that ever happens to a label. Size and shape
   are set here and never animated; only its position and its face change. */
.lrn-card {
  position: absolute; z-index: 5;
  transform: translate(-50%, -50%);
  perspective: 320px;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-card__flip {
  position: relative; display: block;
  transform-style: preserve-3d;
  animation-timing-function: var(--ease-in-out, ease-in-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-card__face {
  display: flex; align-items: center; gap: 4px;
  padding: 2px 5px;
  border: 1px solid color-mix(in oklab, var(--c-blue) 70%, transparent);
  border-radius: 4px;
  background: color-mix(in oklab, var(--c-blue) 16%, var(--surface));
  box-shadow: 0 1px 0 rgba(0, 0, 0, .35);
  color: color-mix(in oklab, var(--c-blue) 45%, var(--ink));
  font: 600 8px/1.35 var(--f-mono, ui-monospace, monospace);
  letter-spacing: .01em; white-space: nowrap;
  box-sizing: border-box;
  min-width: 96px; height: 17px;    /* the bar's footprint, exactly */
  backface-visibility: hidden;
}
.lrn-card__face::before {           /* the tick box that makes it a test card */
  content: ""; flex: none;
  width: 4px; height: 4px; border-radius: 1px;
  background: var(--c-blue);
}
.lrn-card__fill {                   /* sitting it: the label fills up */
  position: absolute; inset: 1px;
  border-radius: 3px;
  transform: scaleX(0); transform-origin: left center;
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--c-teal) 42%, transparent),
    color-mix(in oklab, var(--c) 34%, transparent));
  backface-visibility: hidden;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-card__face--done {             /* passed: green, ticked, and on the back */
  position: absolute; top: 0; left: 0;
  transform: rotateY(180deg);
  border-color: color-mix(in oklab, var(--c-teal) 75%, transparent);
  background: color-mix(in oklab, var(--c-teal) 18%, var(--surface));
  color: color-mix(in oklab, var(--c-teal) 80%, var(--ink));
  box-shadow: 0 0 10px color-mix(in oklab, var(--c-teal) 30%, transparent);
}
.lrn-card__face--done::before {                          /* the tick itself */
  width: 3px; height: 6px;
  border: solid var(--c-teal); border-width: 0 1.6px 1.6px 0;
  border-radius: 0;
  background: none;
  transform: rotate(40deg) translate(0, -1px);
}

/* --- the people ----------------------------------------------------------- */
/* Positioned on the stage by percentages the timeline animates; the station
   coordinates were measured from the rendered layout, not guessed. */
.lrn-actor {
  position: absolute; z-index: 3;
  margin: -11px 0 0 -8px;                    /* centre the figure on its station */
  animation-timing-function: var(--ease-in-out, ease-in-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-actor__fig { display: block; width: 15px; height: 20px; fill: var(--c); }
.lrn-actor__star {                  /* what they are given for the grade */
  position: absolute; left: 50%; bottom: 100%;
  margin-bottom: 2px; transform: translateX(-50%);
  color: #f2c14e; font-size: 11px; line-height: 1; font-style: normal;
  text-shadow: 0 0 8px color-mix(in oklab, #f2c14e 60%, transparent);
  animation-name: lrn-star;
  animation-timing-function: var(--ease-out, ease-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.lrn-actor--user {
  position: absolute;               /* the star hangs off the figure */
  animation-name: lrn-user, lrn-user-in;
  filter: drop-shadow(0 0 8px color-mix(in oklab, var(--c) 55%, transparent));
}
/* The admin is the other person here, and has to read as one from the
   pictogram alone: cooler, badged, and with a cog on its shoulder. It never
   leaves the operations centre — it only steps down to assign the tests. */
.lrn-actor--admin {
  animation-name: lrn-admin; z-index: 4;
  display: flex; align-items: center; gap: 3px;
  margin: -13px 0 0 -31px;          /* the label sits beside it, not under it,
                                       which keeps the figure's box short
                                       enough for the lane it walks */
}
.lrn-actor--admin .lrn-actor__fig {
  width: 26px; height: 26px; margin-left: -4px;
  fill: var(--c-blue); color: var(--c-blue);
  filter: drop-shadow(0 0 7px color-mix(in oklab, var(--c-blue) 50%, transparent));
}
.lrn-actor__role {
  display: block;
  color: color-mix(in oklab, var(--c-blue) 70%, var(--ink-3));
  font: 600 8px/1 var(--f-mono, ui-monospace, monospace);
  letter-spacing: .1em; text-transform: uppercase; text-align: center;
  white-space: nowrap;
}

/* --- narrow: a layout, not a simulation ---------------------------------- */
/* The simulation needs room: the modules are squares, their labels are not
   scaled down with them, and below this width the cascade of assignments
   would sit on top of the note above them and the people below. Under it
   the figure is a plain layout — the width was measured, not guessed. */
@media (max-width: 1200px) {          /* one slot less of corridor */
  .lrn-q--11 { display: none; }
}
@media (max-width: 1100px) {
  .lrn__body { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
  .lrn__lanes, .lrn__wire, .lrn-actor, .lrn-blk { display: none; }
  .lrn-card { display: none; }
  .lrn__centre { padding-bottom: var(--s-4); }
  .lrn__legs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lrn-mod, .lrn-leg, .lrn-mod__bar > i, .lrn-q { animation: none; }
  .lrn-mod__bar, .lrn-mod__pct { display: none; }
  .lrn__queue { display: none; }
}
@media (max-width: 560px) {
  .lrn__legs, .lrn__mods { grid-template-columns: minmax(0, 1fr); }
  .lrn-mod { aspect-ratio: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .lrn-actor, .lrn-blk, .lrn-q { display: none; }
  .lrn-card { display: none; }
  .lrn-mod, .lrn-leg, .lrn-mod__bar > i { animation: none; }
  .lrn-mod__bar > i { transform: scaleX(1); }
  .lrn-mod__pct { display: none; }
  .lrn-mod__pct--4 { display: block; }
}
