/* ============================================================
   eye/teaser-graph — the landing page's questions as a git graph
   ============================================================

   Every teaser forks off one trunk onto a lane of its own, and all of
   them merge back into the closing line. The trunk is the page title's
   connector carried on down: on desktop it runs in the stub's column,
   21px into the gutter, and frontend.js joins it to the rule.

   Lanes are handed out from the outside in — the first teaser gets the
   lane furthest from the trunk, the last the nearest — so a fork never
   has to cross a lane that is already open, and the merges fan in
   without crossing each other either. Nothing in the drawing overlaps.

   Geometry is all here; frontend.js only measures where the nodes ended
   up and draws the lines between them.
   ------------------------------------------------------------ */
.tgraph {
  /* x of the trunk, from the reading column's left edge: the middle of
     .connector__stub, which is 1px wide and stands at --gutter - 21px. */
  --tg-trunk: -20.5px;
  --tg-lane: 8px;
  --tg-node: 9px;
  /* Text clears the outermost lane by the same distance the copy keeps
     from the page title's bullet. */
  --tg-indent: calc(var(--tg-trunk) + var(--branches, 1) * var(--tg-lane) + 24px);
  /* The branch pill: a size under the news list, and as tall as its own line
     plus the border. */
  --tg-pill-size: calc(var(--news-size) * 0.8);
  --tg-pill-height: calc(var(--tg-pill-size) * 1.5 + 2px);

  position: relative;
  /* The copy's alignment with the menu (layout.css, --copy-gap) is reckoned
     in the em of the box it lands on, and puts the top of a line box 0.344em
     above the menu's cap height. Here that box is the list, at this size; its
     padding gives the 0.344em back, so the first pill's top edge — the first
     thing in the graph — stands on the menu's cap height. */
  font-size: var(--news-size);
}

.tgraph__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  fill: none;
  stroke: var(--ink);
  stroke-width: 1px;
}

.content .tgraph__commits {
  position: relative;          /* the rows' nodes stand over the lines */
  margin: 0;
  padding: 0.344em 0 0;
  list-style: none;
}

.tgraph__commit + .tgraph__commit { margin-top: calc(var(--news-line) * 0.75); }

/* The whole row is the target, not a word in it. */
.content .tgraph__row {
  position: relative;
  display: block;
  padding-left: var(--tg-indent);
  text-decoration: none;
}

.tgraph__row > span { display: block; }

/* The branch name, as a git client tags a commit with its ref: a pill.
   `display: table` so it is a block of its own (the question starts on the
   next line) yet only as wide as the name. */
.tgraph__row > .tgraph__branch {
  display: table;
  margin-bottom: 6px;
  padding: 0 0.7em;
  border: 1px solid color-mix(in srgb, var(--ink) 35%, transparent);
  border-radius: calc(var(--tg-pill-height) / 2);
  font-family: var(--font-mono);
  font-size: var(--tg-pill-size);
  line-height: 1.5;
  white-space: nowrap;
  color: var(--ink);
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

/* The branch everything merges into is the main line: solid, the way a
   client marks the checked-out branch. */
.tgraph__commit--merge .tgraph__branch {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

.tgraph__text { font-size: var(--body-size); }

/* The answer, held open on every row so that showing it moves nothing:
   the page stays exactly where it is, only the line fades in. */
.tgraph__detail {
  font-family: var(--font-mono);
  font-size: var(--news-size);
  line-height: var(--news-line);
  color: var(--eye-red);
  opacity: 0;
  transition: opacity 0.2s ease;
}

/* ---- nodes -------------------------------------------------- */
/* Centred on the first line of the row: the branch pill when there is one,
   the text's first line when there is not. Lane k = --branches - --i. */
.tgraph__node {
  position: absolute;
  top: calc(var(--body-size) * var(--line) / 2 - var(--tg-node) / 2);
  left: calc(
    var(--tg-trunk)
    + (var(--branches, 1) - var(--i, 0)) * var(--tg-lane)
    - var(--tg-node) / 2
  );
  width: var(--tg-node);
  height: var(--tg-node);
  border: 1px solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.tgraph__row:has(.tgraph__branch) .tgraph__node {
  top: calc(var(--tg-pill-height) / 2 - var(--tg-node) / 2);
}

/* The merge commit stands on the trunk and carries the page title's own
   bullet: where every line ends up. */
.tgraph__commit--merge .tgraph__node {
  --tg-node: var(--bullet);
  left: calc(var(--tg-trunk) - var(--tg-node) / 2);
  border: 0;
  background: var(--eye-red);
}

/* ---- hover: "which one are you?" ---------------------------- */
.tgraph__row:hover .tgraph__node,
.tgraph__row:focus-visible .tgraph__node,
.tgraph__row:hover .tgraph__branch,
.tgraph__row:focus-visible .tgraph__branch {
  border-color: var(--eye-red);
  background: var(--eye-red);
}

.tgraph__row:hover .tgraph__branch,
.tgraph__row:focus-visible .tgraph__branch { color: var(--paper); }

.tgraph__row:hover .tgraph__detail,
.tgraph__row:focus-visible .tgraph__detail { opacity: 1; }

a.tgraph__row:hover .tgraph__text { text-decoration: underline; text-underline-position: from-font; }

/* ---- the featured row --------------------------------------- */
/* frontend.js features one teaser per visit, in turn: larger, in red, its
   answer already open. Set before the first paint, so nothing jumps. */
.tgraph__commit.is-featured .tgraph__text {
  font-size: var(--lead-size);
  color: var(--eye-red);
}

.tgraph__commit.is-featured .tgraph__node,
.tgraph__commit.is-featured .tgraph__branch {
  border-color: var(--eye-red);
  background: var(--eye-red);
}

.tgraph__commit.is-featured .tgraph__branch { color: var(--paper); }

.tgraph__commit.is-featured .tgraph__detail { opacity: 1; }

/* The lead-in from the title's rule to the first fork. 2px dashes, 3px
   gaps: still a line at a glance, and dashed only up close. */
.tgraph__lines .tgraph__lead { stroke-dasharray: 2 3; }

/* ---- drawing it, once --------------------------------------- */
/* Only on the first visit; frontend.js sets .is-drawing for it. pathLength
   is 1 on every path, so one dash covers each whatever its length. */
.tgraph.is-drawing .tgraph__lines path:not(.tgraph__lead) {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: tgraph-draw 0.7s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  animation-delay: calc(var(--d, 0) * 0.12s);
}

.tgraph.is-drawing .tgraph__lead {
  opacity: 0;
  animation: tgraph-node 0.3s ease forwards;
}

.tgraph.is-drawing .tgraph__node {
  opacity: 0;
  animation: tgraph-node 0.3s ease forwards;
  animation-delay: calc(0.3s + var(--i, 0) * 0.12s);
}

.tgraph.is-drawing .tgraph__commit--merge .tgraph__node {
  animation-delay: calc(0.6s + var(--branches, 1) * 0.12s);
}

@keyframes tgraph-draw { to { stroke-dashoffset: 0; } }
@keyframes tgraph-node { to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .tgraph__detail,
  .tgraph__node,
  .tgraph__branch { transition: none; }
}

/* ---- compact ------------------------------------------------ */
/* No stub down here: the title's bullet sits on the rule at the column's
   left edge, so the trunk takes the bullet's centre instead and the lanes
   close up to leave the text its room. */
@media (max-width: 1023px) {
  .tgraph {
    --tg-trunk: calc(var(--bullet) / 2);
    --tg-lane: 6px;
    --tg-node: 7px;
    --tg-indent: calc(var(--tg-trunk) + var(--branches, 1) * var(--tg-lane) + 16px);
  }
}
