/* Justin Creasy — work-timeline.css
   Everything the Work page adds on top of styles.css: the reverse-chronological
   timeline with its sticky date rail and drawn rule, the role entry's capability
   list, and the lighter rows under "Other projects".

   Loaded after styles.css. Nothing here is required for the page to make sense:
   with JavaScript off the rule is a plain hairline, the nodes are filled, and
   every entry is simply present. */

/* ------------------------------------------------------------ the timeline */

.tl { position: relative; }

.tl__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.tl__entry { position: relative; }
.tl__entry + .tl__entry { margin-top: var(--s10); }

/* On a phone the rail is a date line above the entry and the rule is dropped. */
.tl__rail {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s1) var(--s4);
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--s5);
}
.tl__date { color: var(--ink); }
.tl__state { color: var(--ink-2); }

.tl__say {
  margin-top: var(--s5);
  font-size: 1.3125rem;
  line-height: 1.45;
  letter-spacing: -0.011em;
  max-width: 46ch;
}

.tl__fig { display: block; margin-top: var(--s7); }
.tl__fig figcaption { margin-top: var(--s3); }
.tl__fig--after { margin-top: var(--s8); }

.tl__split { margin-top: var(--s7); display: grid; gap: var(--s6); }
.tl__split .body { max-width: 56ch; }
.tl__split .run__facts { margin-top: 0; }

@media (min-width: 900px) {
  .tl__split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--s7);
    align-items: start;
  }
}

/* ------------------------------------------------- the rail and the rule */

.tl__rule,
.tl__node { display: none; }

@media (min-width: 960px) {
  /* Two of twelve columns for the rail, the rest for the entry. */
  .tl {
    --rail: 172px;
    --railgap: 48px;
  }

  .tl__entry {
    display: grid;
    grid-template-columns: var(--rail) minmax(0, 1fr);
    column-gap: var(--railgap);
    align-items: start;
  }

  .tl__rail {
    display: block;
    position: sticky;
    top: 96px;
    align-self: start;
    padding-bottom: 0;
    border-bottom: 0;
    margin-bottom: 0;
  }
  .tl__state { margin-top: var(--s2); }

  /* One hairline for the whole section, with the accent drawn down it. */
  .tl__rule {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--rail) + var(--railgap) / 2);
    width: 1px;
    background: var(--rule);
  }

  .tl__prog {
    position: absolute;
    inset: 0;
    background: var(--signal);
    transform: scaleY(var(--p, 0));
    transform-origin: top center;
  }

  .tl__node {
    display: block;
    position: absolute;
    top: 0.34em;
    left: calc(var(--rail) + var(--railgap) / 2);
    width: 11px;
    height: 11px;
    margin-left: -5px;
    border: 1px solid var(--rule);
    border-radius: 50%;
    background: var(--canvas);
    z-index: 1;
  }
  .tl__node i {
    position: absolute;
    inset: 1px;
    border-radius: 50%;
    background: var(--signal);
  }

  /* Hidden only while JavaScript is running; otherwise the node is simply filled. */
  .js .tl__node i {
    transform: scale(0.34);
    opacity: 0;
    transition: transform var(--t-reveal) var(--spring-settle),
                opacity var(--t-hover) var(--ease-out);
  }
  .js .tl__entry.is-current .tl__node i {
    transform: none;
    opacity: 1;
  }
}

/* --------------------------------------------------------- the role entry */
/* No imagery here on purpose, so the type carries it. */

.tl__title--role {
  font-size: clamp(2.125rem, 4.4vw, 3.25rem);
  font-weight: 600;
  letter-spacing: -0.028em;
  line-height: 1.04;
}
.tl__title--role br { display: none; }

@media (min-width: 900px) {
  .tl__title--role br { display: inline; }
}

.tl__where { margin-top: var(--s5); }
.tl__say--role { margin-top: var(--s6); max-width: 40ch; text-wrap: balance; }

.cap {
  margin-top: var(--s7);
  border-top: 1px solid var(--rule);
}
.cap > div {
  padding-block: var(--s5);
  border-bottom: 1px solid var(--rule);
}
.cap dt {
  font-weight: 600;
  letter-spacing: -0.012em;
}
.cap dd {
  margin: var(--s1) 0 0;
  color: var(--ink-2);
  max-width: 52ch;
}

@media (min-width: 860px) {
  .cap > div {
    display: grid;
    grid-template-columns: 13rem minmax(0, 1fr);
    gap: var(--s5);
    align-items: baseline;
  }
  .cap dd { margin-top: 0; }
}

.tl__note { margin-top: var(--s6); }

/* ------------------------------------------------------------ other work */
/* Deliberately lighter than the three above: smaller media, compact rows. */

/* No rule on top of the list: the section head already draws one. */
.alt {
  list-style: none;
  padding: 0;
  margin: 0;
}

.alt__row {
  display: grid;
  gap: var(--s5);
  padding-block: var(--s7);
  border-bottom: 1px solid var(--rule);
  align-items: start;
}
.alt__row:first-child { padding-top: 0; }

@media (min-width: 760px) {
  .alt__row {
    grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
    gap: var(--s7);
  }
}

.alt__media { display: grid; gap: var(--s3); }
.alt__media--pair { grid-template-columns: 1.7fr 1fr; align-items: start; }

.alt__text .meta { margin-top: var(--s2); }
.alt__text .meta + .meta { margin-top: var(--s1); }
.alt__text .body { margin-top: var(--s4); max-width: 58ch; }
.alt__text .body + .body { margin-top: var(--s4); }

/* --------------------------------------------------------- reduced motion */
/* The rule stays, static. Nothing draws and no node has to fill in. */

@media (prefers-reduced-motion: reduce) {
  .tl__prog { display: none; }
  .js .tl__node i { transform: none; opacity: 1; }
}

/* ---------------------------------------------------------------- narrow */

@media (max-width: 700px) {
  .tl__entry + .tl__entry { margin-top: var(--s9); }
  .tl__say { font-size: 1.1875rem; }
  .tl__fig { margin-top: var(--s6); }
  .tl__fig--after { margin-top: var(--s7); }
  .tl__split { margin-top: var(--s6); }
  .alt__row { padding-block: var(--s6); }
}
