/* aside — the site navigation, built entirely out of bars.

   There is no background panel: the black column IS the stack of bars, touching
   one another. Some bars carry an item (label + link), the rest are just bars.
   That is what lets the aside assemble line by line, top to bottom.

   site/src/scripts/aside.js builds it and reads every custom property below,
   so this is the one place to tune.

   The rule of the drawing: column and bar share the same colour. At rest the
   column is solid and all you read are the labels, on the right. What reveals the
   bars is the pointer — it stretches them out, and it cools them, and the cooling
   stays on the bar after the pointer has moved on. */

.aside {
  /* ---- the column ---- */
  /* How WIDE the column is lives in tokens.css as --bar-w: it is a decision about
     the page, not about the column — the body needs it too, to know how much space
     the piece has left. */
  --bar-pad: 42px;        /* inner margin: the mark at the top, labels at the sides */
  --bars: 0;              /* 0 = as many bars as the height takes; a positive
                             number fixes the count; a negative one means one bar
                             per item, which is what the navbar needs */
  /* Transparent, and it has to stay that way: the bars arrive by travelling INTO
     the column, so what makes the arrival visible is the contrast between a black
     bar and the paper behind it. A black background here would swallow the whole
     animation — the bars would still fly, and nobody would see it.

     The hairline between two bars is not this variable's problem: it is fixed in
     the JS, by giving every bar a whole number of pixels (see column()). */
  --bar-bg: transparent;

  /* ---- the mark ---- */
  --mark-w: 156px;        /* the white SVG in site/assets/marca/ */

  /* ---- the bars ---- */
  --line: 66px;           /* NOMINAL bar height: the JS divides the column height
                             by it to know how many bars fit, then spreads the
                             remainder, so the stack closes at exactly 100% */
  --label-size: 20px;      /* the face decides this: a display face reads small */
  --line-pad: 42px;       /* where the label starts, measured from the right */
  --line-bg: var(--ink);  /* a bar is black, like the column */
  --radius: 0;            /* no radius: it is a straight line. Raise it to round */

  /* ---- the pointer ---- */
  --reach: 135px;          /* how far away it already starts pulling */
  --stretch: 84px;        /* how far a bar WITH an item stretches out, at most */
  --empty: 0.4;           /* how much of that a bar WITHOUT an item answers */
  --hover: 18px;          /* how much further the hovered bar stretches */
  --cool: #550000;        /* the colour every bar cools towards near the pointer */
  --ease: 0.2;            /* how fast a bar answers the pointer, per frame */
  --cool-ease: 0.03;      /* how slowly the colour lets go: lower is a longer tail */

  /* ---- the arrival ---- */
  /* How the assembly is paced. They live here and not in the JS because the two
     layouts need different rhythms: the column is long and builds at a whisper,
     the navbar is four bars and has to be seen landing one after the other. */
  --delay: 240ms;         /* how far the mark goes ahead of the first bar */
  --per-line: 45ms;       /* the gap between one bar and the next */
  --flight: 620ms;        /* how long one bar takes to arrive */

  position: fixed;
  inset-block: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  width: var(--bar-w);
  background: var(--bar-bg);
  z-index: 2;
  /* hidden until it arrives: nobody tabs into a link that is not there yet */
  visibility: hidden;
}

.aside.ready { visibility: visible; }

/* The mark floats above the stack, centred on the aside's axis. To centre it
   vertically as well, swap top for 50% and the translate for -50% -50%.
   z-index matters: the bars are positioned too and come later in the DOM, so
   without it they would paint straight over the mark. */
.mark {
  position: absolute;
  top: var(--bar-pad);
  left: 50%;
  translate: -50% 0;
  width: var(--mark-w);
  height: auto;
  z-index: 1;
  /* the mark does not steal the pointer from the bars passing under it */
  pointer-events: none;
}

/* One bar of the stack. Width, height and background are written by the JS —
   width is where the stretch happens, height is what closes the column. */
.bar {
  position: relative;
  display: block;
  flex: none;
  height: var(--line);
  background: var(--line-bg);
  border-radius: var(--radius);
}

/* An item is a bar with a label inside it. */
.item {
  color: var(--paper);
  text-decoration: none;
}

/* The label sits against the right end of the bar and travels with it: the
   stretch grows the right tip, so the label rides out with the tip. That is why
   the stretch is a width and never a scale — stretched text is broken text. */
.label {
  position: absolute;
  top: 50%;
  right: var(--line-pad);
  translate: 0 -50%;
  font-size: var(--label-size);
  line-height: 1;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* The keyboard has no pointer to come near: it gets an outline inside the bar,
   which never fights the values the JS writes. */
.item:focus-visible {
  outline: 1px solid var(--paper);
  outline-offset: -6px;
}

/* The arrival. The starting points (--from-x and --from-y) are written inline by
   the JS — they depend on where the thing rests and how big it is — and each
   layout uses the axis that suits it. When the arrival is over the JS drops the
   class, so nothing stays animated or composited afterwards.

   The column comes in from the left: a stack of bars arriving sideways, one after
   the next, top to bottom. */
@keyframes arrive-x {
  from { transform: translateX(var(--from-x, -100vw)); }
  to   { transform: translateX(0); }
}

/* The navbar drops. A row of bars arriving sideways would cross each other on the
   way and pass under the mark; falling from above, each one lands where it stands. */
@keyframes arrive-y {
  from { transform: translateY(var(--from-y, -100vh)); }
  to   { transform: translateY(0); }
}

.aside.arriving .mark,
.aside.arriving .bar {
  animation-name: arrive-x;
  animation-timing-function: cubic-bezier(0.2, 0.85, 0.2, 1);
  animation-fill-mode: both;
}

/* Each bar's delay is inline, written by the JS from --delay and --per-line.
   The mark goes first. */
.aside.arriving .mark {
  animation-duration: var(--flight);
}

@media (prefers-reduced-motion: reduce) {
  .aside.arriving .mark,
  .aside.arriving .bar { animation: none; }
}

/* Narrow. The column becomes a navbar: the same bars, side by side in one row,
   anchored at the top of the screen instead of stacked down the left edge.

   What changes beyond the numbers:
     position   fixed at the top instead of the left, with the page keeping the
                row's height (--bar-h, measured by the JS and used by base.css);
     direction  row, so the bars stand shoulder to shoulder;
     --bars     negative: exactly one bar per item, no fillers — the column's
                "as many as fit the height" belongs to a column with a height;
     the axis   the arrival falls instead of sliding. A row of bars flying in from
                the left would cross each other on the way and pass under the mark;
                dropping from above, each one lands where it stands — and it lands
                with the page, not with the piece (site/src/scripts/aside.js);
     the edge   the bottom one: a torn strip hanging below each bar, past its box,
                and a pixel of overlap between two cells so the row has no seam
                (see .bar and .bar::after);
     the pull   switched off: on a touch screen there is no pointer to come near. */
@media (max-width: 900px) {
  .aside {
    position: fixed;
    inset: 0 0 auto 0;
    width: 100%;
    /* One row: the row is the height. */
    height: auto;
    flex-direction: row;
    align-items: stretch;
    --bars: -1;
    --line: 56px;
    --bar-pad: 16px;
    --label-size: 13px;
    --line-pad: 18px;
    --mark-w: 64px;
    /* Every bar one step lower than the one on its left: the row reads as a
       staircase, which is also what makes each item its own thing instead of a
       piece of an even band. */
    --step: 12px;
    --tear: 2px;            /* how far the torn edge reaches below the bar */
    /* Fewer bars, so each one has to be more clearly its own beat: a wider gap and
       a shorter fall. Four bars at the column's 45ms landed almost together. */
    --delay: 120ms;
    --per-line: 130ms;
    --flight: 460ms;
    --stretch: 0px;
    --hover: 0px;
    --empty: 0;
  }

  /* The mark takes the head of the row — and it needs a bar of its own to sit on.
     On the left of a column the mark floats over black; in a row it would float
     over the paper, and a white logo on white is nothing at all. So it gets the
     same black the bars have, with the logo held inside it. */
  .mark {
    position: static;
    translate: none;
    flex: none;
    align-self: flex-start;
    margin: 0;
    inline-size: calc(var(--mark-w) + var(--bar-pad) * 2);
    block-size: var(--line);
    padding-inline: var(--bar-pad);
    background: var(--line-bg);
    object-fit: contain;
  }

  /* Side by side: flex-basis 0 wins over the width the JS writes, so the row shares
     the width evenly — and the pull, off here, could not disturb it.

     Each bar starts a pixel before the one on its left, so two cells overlap. That
     pixel is what keeps the row solid: a boundary landing between two device pixels
     would otherwise be covered half by each side, and the paper would show through
     as a hairline between two bars. The overlap costs the row nothing — every bar is
     a pixel wider and the last one still ends on the edge of the screen. */
  .bar {
    flex: 1 1 0;
    margin-inline-start: -1px;
    /* The step happens at the BOTTOM. Every bar starts on the same line — aligned
       tops, no hole above the ones on the right — and the further right a bar is,
       the lower it reaches.

       That is why this is padding and not a margin: a margin would push the top
       down and leave the hole. And it is why the box is content-box here: the
       height written by the JS is the bar itself (--line, the same for all of
       them), and the padding is the extra length the staircase adds to it. */
    box-sizing: content-box;
    align-self: flex-start;
    padding-block-end: calc(var(--i, 0) * var(--step));

    /* The bottom edge itself stays straight: the tear does not cut into the bar. The
       bar stops where its box stops, and the torn strip hangs below, past that edge —
       .bar::after, just underneath. */
  }

  /* The tear. After the item, not inside it: a clip-path on the bar could only ever
     take material away from the bar, because a clip never paints past the box it
     clips. A strip of its own, hanging under the bar, can.

     The pitch is a percentage, so it holds across the different widths; the depth is
     a length, so it stays the same across the different heights. Both corners end on
     a valley: the cut is the edge, not a decoration sitting on it. */
  .bar::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    /* 100% is the bar's own bottom edge: an absolute child is placed against the
       padding box, and the bar has no border, so this lands exactly where it stops. */
    top: 100%;
    block-size: var(--tear);
    /* The bar cools towards --cool under the pointer; the tear cools with it,
       instead of staying black against a bar that no longer is. */
    background-color: inherit;
    clip-path: polygon(
      0 0,
      100% 0,
      93.75% 100%,
      87.50% 0,
      81.25% 100%,
      75% 0,
      68.75% 100%,
      62.50% 0,
      56.25% 100%,
      50% 0,
      43.75% 100%,
      37.50% 0,
      31.25% 100%,
      25% 0,
      18.75% 100%,
      12.50% 0,
      6.25% 100%
    );
  }

  /* Centred in each cell: a column's label hangs on the right, a navbar's does not. */
  .label {
    right: auto;
    left: 50%;
    translate: -50% -50%;
    text-align: center;
  }

  /* And the arrival keeps falling — now into a row. */
  .aside.arriving .mark,
  .aside.arriving .bar { animation-name: arrive-y; }
}
