/* The say hi page: the four ways to reach Nhi, drawn as a git graph.

   The about page is a linked list, so this one is the other shape the same
   node-and-edge language can make: a trunk that forks. Email is `main`, because
   it is where everything lands by default; the other three are branches, and
   the branch name is the v1 joke written as a ref. `nhi/likes-to-code` reads as
   the sentence and is a real ref at the same time.

   Vertical at every width. The horizontal version was drawn and dropped: it
   forced the destination out of the row to fit across the trunk, and a contact
   page should not hide addresses to make a diagram work. */

.hi { max-width: 470px; }

/* The `<ul>` is the graph, and it is also the positioning context the rail and
   the HEAD tag are drawn against.

   It used to hold that `<svg>` and `<span>` as direct children, which is not a
   list: a `<ul>` takes `<li>` and nothing else, and the two strays were counted
   into the list a screen reader announces. They moved into one presentational
   `<li>` rather than out to a wrapper. A wrapper would have meant this reset
   moving to a new class, and a stylesheet is cached independently of the
   document that uses it: every reader holding the previous `contact.css` would
   have got the new markup with no reset on it, which is a bulleted, indented
   list with the rail detached beside it. Markup that keeps working against the
   stylesheet already in the reader's cache is worth an extra element. */
.graph {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Holds the rail and the HEAD tag. Both are absolutely positioned, so this is
   zero high and contributes nothing to the column of rows. */
.graph__chrome {
  height: 0;
}

/* The rail: trunk, forks, drawn once as a single SVG behind the rows. Its
   geometry and the rows' fixed heights are the same numbers, so a row's dot
   lands on the end of its curve. Change one and change the other. */
.graph__rail {
  position: absolute;
  left: 0;
  top: 0;
  color: var(--line);
  pointer-events: none;
}

.branch { height: 68px; }

.branch__link {
  position: relative;
  display: grid;
  grid-template-columns: 16px 1fr;
  align-items: center;
  align-content: center;
  column-gap: var(--s4);
  height: 100%;
  padding-left: 58px;
  text-decoration: none;
  color: inherit;
  /* All four rows are anchors. main was a <button> until 2026-09-17, when it
     went back to being a `mailto:` that contact.js upgrades, so the button
     chrome cleared below is no longer load-bearing; it stays because the row
     becomes a button again the moment anyone reaches for one, and five
     declarations are cheaper than rediscovering which ones mattered. */
  background: none;
  border: 0;
  font: inherit;
  text-align: left;
  width: 100%;
  cursor: pointer;
}
/* main sits on the trunk itself, so its mark is in the trunk's lane. */
.branch--main .branch__link { padding-left: 8px; }

/* The node on the rail is the platform's own mark rather than a plain commit
   dot: four identical circles said nothing the ref did not already say, and the
   marks are the fastest way to see what a row is before reading it. They are
   the brand glyphs as single filled paths, in `currentColor`, so they theme
   with everything else and never bring a brand color onto the page. */
.branch__icon {
  width: 16px;
  height: 16px;
  display: block;
  /* Ground behind the glyph so the rail stops at the mark instead of running
     through its transparent parts. Invisible against the page; it is the same
     job the old dot's ground fill did. */
  background: var(--ground);
  color: var(--muted);
  transition: color var(--dur-base) var(--ease);
}

.branch__ref {
  /* Not stretched to the grid column: the HEAD tag is placed against this
     element's right edge, so the box has to end where the text ends. */
  justify-self: start;
  font-family: var(--mono);
  font-size: 15px;
  color: var(--muted);
  white-space: nowrap;
  transition: color var(--dur-base) var(--ease);
}
/* The repo prefix is quieter than the branch name but still real text, so it
   takes --muted rather than the hairline color: --line on the ground is a
   border value and would not pass contrast as type. */
.branch__ref .dim { color: var(--muted); }

/* The verb. It says what the click does before the click happens: `check out`
   on the three branches, `copy` on main, which puts the address on the
   clipboard instead of navigating. It sits on the line under the ref, where it
   can never meet the HEAD tag, and the row holds that line whether the verb is
   showing or not, so only opacity changes and nothing reflows. */
.branch__act {
  grid-column: 2;
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
/* Touch has no pointer to reveal the verb with, so the verb is simply there.
   Without this the contact page on a phone is four rows with no sign that any
   of them does anything, which is the one page whose entire job is being
   pressed. Same guard projects.css and about.css both already carry. */
@media (hover: none) {
  .branch__act { opacity: 1; }
}

.branch__link:hover .branch__act,
.branch__link:focus-visible .branch__act,
.branch__act.is-copied { opacity: 1; }
.branch__act.is-copied { color: var(--ink); }
.branch__link:hover .branch__ref,
.branch__link:focus-visible .branch__ref { color: var(--ink); }
.branch__link:hover .branch__icon,
.branch__link:focus-visible .branch__icon { color: var(--ink); }

.branch__link:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
  border-radius: var(--radius-sm);
}

/* Checked out. Set by a click and by nothing else: hovering considers a branch,
   clicking is what moves HEAD, which is the same distinction the nav's
   traveling rule makes between pointing at a page and being on one. */
.branch.is-head .branch__icon { color: var(--ink); }
.branch.is-head .branch__ref { color: var(--ink); }


/* The HEAD tag. One element placed by measurement and moved by `translate`, so
   it travels between branches instead of blinking on and off. It does not
   exist until the first click, because nothing is checked out before then. */
.graph__head {
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-full);
  padding: 1px 7px;
  pointer-events: none;
  opacity: 0;
  white-space: nowrap;
}
.graph__head.is-placed { opacity: 1; }
.graph__head.is-traveling { transition: transform var(--dur-base) var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .graph__head.is-traveling { transition: none; }
}

@media (max-width: 480px) {
  .branch__ref { font-size: 14px; }
}
