/* ==========================================================================
   jlot.me
   Hand-written, dependency-free, dark only.
   Section map:
     1.  Tokens (verbatim from SPEC.md)
     2.  Derived layout values
     3.  Reset and base
     4.  Focus and skip link
     5.  Rail (desktop) / top bar (mobile)
     6.  Rail nav and the sliding dot
     7.  Page shell
     8.  Sticky section-heading band
     9.  I. About
     10. II. Projects
     11. III. CV
     12. IV. Contact
     13. Mobile overlay
     14. Mobile layout
     15. Motion
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  color-scheme: dark;

  /* ---- Color. Ratios measured against --bg, WCAG 2.1 ---- */
  --bg:          #060D0D;  /*   n/a                            Sketch "BG"      */
  --bg-chip:     #0A0F19;  /*  1.02:1  decorative   skip-link plate            */
  --text:        #FFFFFF;  /* 19.61:1  AA  PASS                                 */
  --text-muted:  #9BA0A0;  /*  7.40:1  AA  PASS     CV prose, project blurbs    */
  --text-dim:    #7E8383;  /*  5.10:1  AA  PASS     est. YYYY, dates, locations */
  --rule:        #1F2525;  /*  1.26:1  decorative   white @10% flattened        */
  --rule-strong: #444A4A;  /*  2.17:1  decorative   white @25% flattened        */

  /* The accent knob. One value drives links, the nav dot and focus rings.
     Swap this hex and re-check it stays >= 4.5:1 against --bg. */
  --accent:      #5FD4B4;  /* 10.80:1  AA  PASS                                 */
  --focus:       var(--accent);

  /* ---- Type. No webfonts (D9). Avenir Next ships on macOS/iOS and is what
         the mockup was drawn in; Segoe UI Variable Display is the Windows
         counterpart. Neither is a fallback-to-default. ---- */
  --font-display: "Avenir Next", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
  --font-sans:    "Avenir Next", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif;
  --font-mono:    ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --size-hero:    clamp(1.5rem, 1.1rem + 1.6vw, 2rem);  /* display, 1.3  */
  --size-h2:      1rem;                                 /* display, 1.4, chip */
  --size-body:    1.125rem;                             /* sans,    1.6  */
  --size-small:   0.875rem;                             /* sans,    1.5  */
  --size-mono:    0.8125rem;                            /* mono,    1.4, +0.02em */

  /* ---- Spacing, 4px base ---- */
  --space-1: .25rem;  --space-2: .5rem;   --space-3: .75rem;
  --space-4: 1rem;    --space-6: 1.5rem;  --space-8: 2rem;
  --space-12: 3rem;   --space-16: 4rem;   --space-24: 6rem;

  /* ---- Layout ---- */
  --rail-width:  204px;   /* Sketch: nav rule is 204px wide          */
  --gutter:      96px;    /* rail to content column                  */
  --content-max: 743px;   /* Sketch: the section rules are 743px     */
  --page-max:    1120px;  /* rail + gutter + content, then centered  */
  --header-h:    56px;    /* mobile bar; 44px target + 6px each side */
}


/* --------------------------------------------------------------------------
   2. Derived layout values
   Kept in their own block so the token block above stays verbatim.
   -------------------------------------------------------------------------- */

:root {
  --page-pad:    var(--space-8);   /* viewport inset for the whole assembly    */
  --band-bleed:  var(--space-4);   /* how far the sticky band paints past the
                                      content column so nothing peeks through */
  --sticky-top:  0px;              /* where the heading band parks            */
  --band-lead:   var(--space-12);  /* Sketch: chip top y=48                   */
  --band-tail:   5px;              /* Sketch: rule y=95, title bottom y=90    */
  --band-inner:  42px;             /* height of the band's type row. The rail
                                      bar uses it too, so both hairlines land
                                      on the same y: lead + inner + tail = 95 */
  --body-lead:   38px;             /* Sketch: first body line y=133           */
  --dot-size:    6px;
  --cv-inset:    25px;             /* CV text is 25px inboard of its rule     */
  --ring:        2px;
}


/* --------------------------------------------------------------------------
   3. Reset and base
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: var(--sticky-top);
}

body {
  min-height: 100%;
  background: var(--bg);
  color: var(--text);
  font: 400 var(--size-body) / 1.6 var(--font-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Set by site.js while the mobile overlay is open. showModal() alone does
   not stop iOS Safari from scrolling the page behind the dialog. */
body.is-locked { overflow: hidden; }

img {
  display: block;
  max-width: 100%;
  height: auto;
}

ul, ol { list-style: none; padding: 0; }

a { color: var(--accent); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Mono carries every numeral, date range and est. YYYY. */
.cv-meta, .project__est { letter-spacing: .02em; }


/* --------------------------------------------------------------------------
   4. Focus and skip link
   -------------------------------------------------------------------------- */

:focus-visible {
  outline: var(--ring) solid var(--focus);
  outline-offset: var(--ring);
  border-radius: 2px;
}

/* main takes focus from the skip link but should not draw a ring for it. */
.main:focus { outline: none; }

.skip-link {
  position: fixed;
  z-index: 100;
  top: var(--space-4);
  left: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-chip);
  border: 1px solid var(--rule-strong);
  border-radius: 4px;
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--size-small);
  font-weight: 500;
  text-decoration: none;
  transform: translateY(calc(-100% - var(--space-8)));
}

.skip-link:focus { transform: none; }


/* --------------------------------------------------------------------------
   5. Rail
   Desktop: fixed column pinned to the left edge of the centered page box.
   Mobile:  sticky --header-h bar. See section 14.
   -------------------------------------------------------------------------- */

.rail {
  position: fixed;
  z-index: 30;
  top: var(--band-lead);
  left: max(var(--page-pad), calc(50% - var(--page-max) / 2 + var(--page-pad)));
  width: var(--rail-width);
}

.rail__bar {
  /* Same construction as the section band: --band-inner of content, then
     --band-tail, then the rule. Both start at --band-lead, so both hairlines
     land on the same y and read as one continuous line across the page -
     which is how the Sketch file draws them. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* border-box, so min-height has to carry the tail as well or the rule sits
     --band-tail too high. */
  min-height: calc(var(--band-inner) + var(--band-tail));
  padding-bottom: var(--band-tail);
  border-bottom: 1px solid var(--rule-strong);
}

.rail__name {
  font-family: var(--font-display);
  font-size: 1.125rem;   /* 18px */
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: .01em;
}

.nav-toggle { display: none; }


/* --------------------------------------------------------------------------
   6. Rail nav and the sliding dot
   -------------------------------------------------------------------------- */

.rail__nav {
  position: relative;    /* offset parent for the dot */
  margin-top: var(--space-6);
}

.nav-list { display: flex; flex-direction: column; }

.nav-link {
  display: flex;
  padding-block: var(--space-2);
  color: var(--text);
  font-size: 1rem;       /* 16px */
  font-weight: 400;
  line-height: 1.5;
  text-decoration: none;
}

.nav-num {
  flex: 0 0 28px;
  color: var(--text-dim);
}

.nav-link:hover .nav-num,
.nav-link[aria-current] .nav-num { color: var(--text-muted); }

.nav-link:hover { color: var(--accent); }

.nav-link[aria-current] { font-weight: 500; }

.nav-dot {
  position: absolute;
  top: 0;
  left: calc(var(--space-4) * -1);
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;                 /* revealed once site.js has measured a target */
  transform: translateY(var(--dot-y, 0px));
}

.rail__nav.is-ready .nav-dot { opacity: 1; }


/* --------------------------------------------------------------------------
   7. Page shell
   -------------------------------------------------------------------------- */

.page {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-pad);
}

.main {
  max-width: var(--content-max);
  margin-left: calc(var(--rail-width) + var(--gutter));
  padding-bottom: var(--space-24);
}

/* Owned by the OUTGOING section, not the incoming one. A margin sits outside
   both section boxes, so no heading is sticky across it and the band blinks
   out; as padding it belongs to a section whose heading is still stuck. Total
   rhythm is unchanged: this plus the next band's own lead = --space-24. */
.section { padding-bottom: var(--band-lead); }


/* --------------------------------------------------------------------------
   8. Sticky section-heading band
   The heading itself is the sticky element and paints an opaque --bg across
   the full content column (plus a bleed) so scrolling content disappears
   cleanly beneath it. The chip and the hairline ride along inside the band.
   A sticky element cannot escape its section, so each band pushes out the one
   above it with no JS.
   -------------------------------------------------------------------------- */

.section-heading {
  position: sticky;
  z-index: 2;
  top: var(--sticky-top);
  margin-inline: calc(var(--band-bleed) * -1);
  padding: var(--band-lead) var(--band-bleed) var(--band-tail);
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

/* The hairline is drawn inset to the content column, not to the bleed. */
.section-heading::after {
  content: "";
  position: absolute;
  right: var(--band-bleed);
  bottom: 0;
  left: var(--band-bleed);
  height: 1px;
  background: var(--rule-strong);
}

.section-title {
  /* No plate behind the type. The band itself is already opaque --bg, which
     is what stops content showing through; the chip fill was decoration on
     top of that, not the mechanism. */
  display: flex;
  align-items: center;
  min-height: var(--band-inner);
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--size-h2);
  font-weight: 500;
  line-height: 1.4;
}

.numeral { margin-right: .3em; }

.section-body { padding-top: var(--body-lead); }


/* --------------------------------------------------------------------------
   9. I. About
   -------------------------------------------------------------------------- */

.hero {
  max-width: 20ch;
  font-family: var(--font-display);
  font-size: var(--size-hero);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -.01em;
  text-wrap: balance;
}

/* An underlined phrase in the hero must not break across lines. text-wrap:
   balance will happily split it, which leaves two underline fragments at
   different x positions and reads as a mistake. nowrap keeps it whole, and
   here it also restores the intended three-line stack at the same width.
   1px matches the rule weight used everywhere else on the page; the UA
   default underline is heavier than any other line in the design. */
.hero u {
  white-space: nowrap;
  text-decoration-thickness: 1px;
  text-underline-offset: .16em;
}

.about {
  margin-top: var(--space-6);
  max-width: 62ch;
  color: var(--text);
  text-wrap: pretty;
}

.skills {
  margin-top: var(--space-12);
  border-top: 1px solid var(--rule);
}

.skills__row {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  gap: var(--space-4);
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--rule);
}

.skills dt {
  color: var(--text-dim);
  font-family: var(--font-display);
  font-size: var(--size-small);
  font-weight: 500;
  line-height: 1.5;
  padding-top: 2px;   /* optical alignment against the 14px/1.5 dd */
}

.skills dd {
  color: var(--text-muted);
  font-size: var(--size-small);
  line-height: 1.5;
}

.sep { color: var(--text-dim); padding-inline: .15em; }


/* --------------------------------------------------------------------------
   10. II. Projects
   Linked cards carry an accent arrow so they read as links without hover.
   The unlinked card has no anchor, no pointer and no hover (D34).
   -------------------------------------------------------------------------- */

.projects {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-8);
}

/* Cards stretch to the tallest in the row, and est. is pushed to the bottom of
   its card. Without this a blurb that wraps to three lines drops its own est.
   line below the other two and the row stops reading as a row. */
.project {
  display: flex;
  flex-direction: column;
}

.project__link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.project__logo {
  display: flex;
  align-items: flex-end;
  min-height: 44px;
  margin-bottom: var(--space-6);
}

.project__blurb {
  display: block;
  color: var(--text-muted);
  font-size: var(--size-small);
  line-height: 1.5;
}

/* Both link arrows are drawn from the SAME font at the SAME size. They used
   to inherit their context - the project arrow from the sans stack at 14px,
   the resume arrow from mono at 13px - which rendered them at visibly
   different scales. */
.project__arrow,
.band-action__arrow {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: var(--size-small);
  line-height: 1;
  color: var(--accent);
}

.project__arrow { padding-left: .35em; }
.band-action__arrow { padding-left: .3em; }

.project__link:hover .project__arrow,
.project__link:focus-visible .project__arrow { transform: translate(3px, -3px); }

.project__link:hover .project__blurb,
.project__link:focus-visible .project__blurb { color: var(--text); }

.project__link:focus-visible { border-radius: 4px; }

.project__est {
  margin-top: auto;
  padding-top: var(--space-3);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: var(--size-mono);
  line-height: 1.4;
}


/* --------------------------------------------------------------------------
   11. III. CV
   -------------------------------------------------------------------------- */

.cv { display: flex; flex-direction: column; gap: var(--space-8); }

.cv-entry {
  /* Sketch: rule x=492, text x=517, content column x=480. */
  margin-left: var(--space-3);
  padding-left: var(--cv-inset);
  border-left: 1px solid var(--rule);
}

.cv-head {
  font-family: var(--font-display);
  font-size: 1.125rem;   /* 18px */
  font-weight: 600;
  line-height: 1.4;
}

.cv-company { color: var(--text); }


.cv-sep {
  color: var(--text-dim);
  font-weight: 400;
  padding-inline: .35em;
}

.cv-location { font-weight: 400; }

.cv-meta {
  margin-top: var(--space-1);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: var(--size-mono);
  line-height: 1.4;
}

.cv-bullet { padding-inline: .2em; }

.cv-summary {
  margin-top: var(--space-4);
  color: var(--text-muted);
  font-size: 1rem;   /* 16px */
  line-height: 1.6;
  max-width: 62ch;
}

.cv-highlights {
  margin-top: var(--space-2);
  padding-left: var(--space-6);
  color: var(--text-muted);
  font-size: 1rem;   /* 16px */
  line-height: 1.6;
  max-width: 62ch;
}

.cv-highlights li { padding-left: var(--space-4); }

.cv-highlights li::before {
  content: "\2022";
  display: inline-block;
  width: var(--space-4);
  margin-left: calc(var(--space-4) * -1);
  color: var(--text-dim);
}


/* --------------------------------------------------------------------------
   12. IV. Contact
   Same left-rule treatment as the CV entries.
   -------------------------------------------------------------------------- */

.contact__email {
  font-family: var(--font-display);
  font-size: var(--size-h2);
  font-weight: 500;
  line-height: 1.4;
}


.contact__meta {
  margin-top: var(--space-2);
  color: var(--text-dim);
  font-size: var(--size-small);
  line-height: 1.5;
}

.contact__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  margin-top: var(--space-6);
}

.contact__links a {
  display: inline-flex;
  align-items: center;
  font-size: 1rem;
}


/* --------------------------------------------------------------------------
   13. Mobile overlay
   -------------------------------------------------------------------------- */

.nav-dialog {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--bg);
  color: var(--text);
}

.nav-dialog::backdrop { background: var(--bg); }

.nav-dialog__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-pad);
}

.nav-dialog__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-h);
  border-bottom: 1px solid var(--rule);
}

.nav-dialog__title {
  font-family: var(--font-display);
  font-size: 1.125rem;
  font-weight: 600;
}

.nav-list--overlay { padding-top: var(--space-6); }

.nav-list--overlay .nav-link {
  position: relative;
  align-items: center;
  min-height: 44px;
  font-size: 1.125rem;
}

/* The overlay has no sliding dot, so the current item gets a static one.
   Weight alone would be too quiet a signal. */
.nav-list--overlay .nav-link[aria-current]::before {
  content: "";
  position: absolute;
  top: calc(50% - var(--dot-size) / 2);
  left: calc(var(--space-4) * -1);
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  background: var(--accent);
}

.nav-list--overlay .nav-num { flex-basis: 40px; }

/* Hamburger and close mark: three rules and a cross, drawn in CSS so the
   page still makes zero extra requests. */
.nav-toggle, .nav-close {
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-right: -10px;   /* optical: the glyph, not the target, sits flush */
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  cursor: pointer;
}

.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  width: 20px;
  height: 1px;
  background: currentColor;
}

.nav-toggle__bars { position: relative; }

.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
}

.nav-toggle__bars::before { top: -6px; }
.nav-toggle__bars::after  { top: 6px; }

.nav-close { display: inline-flex; }

.nav-close__mark {
  position: relative;
  display: block;
  width: 18px;
  height: 18px;
}

.nav-close__mark::before,
.nav-close__mark::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 18px;
  height: 1px;
  background: currentColor;
}

.nav-close__mark::before { transform: rotate(45deg); }
.nav-close__mark::after  { transform: rotate(-45deg); }


/* --------------------------------------------------------------------------
   14. Mobile layout (< 900px)
   The rail collapses into a sticky bar and the nav moves to the overlay.
   -------------------------------------------------------------------------- */

@media (max-width: 899px) {
  :root {
    --page-pad:   var(--space-6);
    --band-bleed: var(--space-6);   /* bleeds exactly to the viewport edges */
    --sticky-top: var(--header-h);
    --band-lead:  var(--space-8);
    --body-lead:  var(--space-8);
    /* 13px mono is below the 14px readability floor at arm's length. */
    --size-mono:  0.875rem;
  }

  .rail {
    position: sticky;
    top: 0;
    left: auto;
    width: 100%;
    background: var(--bg);
  }

  .rail__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    max-width: var(--page-max);
    height: var(--header-h);
    margin-inline: auto;
    padding-bottom: 0;
    padding-inline: var(--page-pad);
    border-bottom: 1px solid var(--rule);
  }

  .nav-toggle { display: inline-flex; }

  /* Hidden here because the nav lives in the overlay. The box is still set up
     so the <noscript> fallback can simply switch it back on. */
  .rail__nav {
    display: none;
    max-width: var(--page-max);
    margin-inline: auto;
    padding-inline: var(--page-pad);
  }

  .main {
    margin-left: 0;
    padding-bottom: var(--space-16);
  }

  .section { padding-bottom: var(--space-8); }   /* + --band-lead = --space-16 */

  .projects { grid-template-columns: minmax(0, 1fr); }

  .cv-entry, .contact {
    margin-left: 0;
    padding-left: var(--space-4);
  }

  .skills__row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-1);
  }

  .contact__links {
    flex-direction: column;
    gap: 0;
    align-items: flex-start;
  }
}


/* --------------------------------------------------------------------------
   15. Motion
   Every transition lives here, so reduced-motion users get none of it.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }

  .nav-dot {
    transition: transform .32s cubic-bezier(.2, .7, .3, 1), opacity .2s linear;
  }

  /* Only elements that actually change colour on hover AND do not carry
     .link-wipe. Listing a .link-wipe element here sets the `transition`
     SHORTHAND on it a second time, which replaces transition-property
     wholesale and kills the underline animation. */
  .nav-link,
  .project__blurb,
  .band-action { transition: color .15s ease; }

  /* Both arrows, one rule - so neither can end up animating while the other
     snaps, which is exactly what happened when they were specified apart. */
  .project__arrow,
  .band-action__arrow { transition: transform .18s ease, color .15s ease; }

  .link-wipe { transition: background-size .28s ease; }
}

/* --------------------------------------------------------------------------
   Animated link underline
   text-decoration cannot animate directionally, so the accent line is a
   background gradient sized from 0 to 100% across the inline box. The dim
   resting rule stays on text-decoration underneath it, so a link still reads
   as a link before you touch it - which is the whole affordance on a
   touchscreen, where hover never happens.
   -------------------------------------------------------------------------- */

.link-wipe {
  /* A wrapped link is two line boxes. Under the default "slice" the gradient
     is painted once across the combined box, so the first line gets no
     underline at all - "LearnGit.io (The Modern Coder Group LLC)" wraps at
     390px. "clone" gives every fragment its own background box. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  text-decoration: none;

  /* Two layers at the SAME position: the accent on top, the resting rule
     underneath. Only the accent's width animates, so the line appears to
     change colour left to right rather than a second line appearing below
     the first - which is what a text-decoration underline plus a gradient
     would give you, at two different heights. */
  background-image:
    linear-gradient(var(--accent), var(--accent)),
    linear-gradient(var(--rule-strong), var(--rule-strong));
  background-repeat: no-repeat;
  background-position: 0 100%, 0 100%;
  background-size: 0 1px, 100% 1px;
  padding-bottom: 2px;
}

.link-wipe:hover,
.link-wipe:focus-visible {
  background-size: 100% 1px, 100% 1px;
}

/* --------------------------------------------------------------------------
   Nav reveal and hover
   The four rail items rise into place once, on load. Everything else on the
   page is present immediately - a reviewer should never wait to read the
   sentence that says who Jack is.

   The animation is opt-IN via .is-ready, which site.js adds. With JS off, or
   with reduced motion, the items are simply there.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .rail__nav.is-ready .nav-link {
    animation: nav-rise .42s cubic-bezier(.22, .61, .36, 1) backwards;
  }
  .rail__nav.is-ready li:nth-child(1) .nav-link { animation-delay: .06s; }
  .rail__nav.is-ready li:nth-child(2) .nav-link { animation-delay: .11s; }
  .rail__nav.is-ready li:nth-child(3) .nav-link { animation-delay: .16s; }
  .rail__nav.is-ready li:nth-child(4) .nav-link { animation-delay: .21s; }

  /* The label slides, the numeral stays put - so the numerals keep forming a
     straight column while the words lean toward the content they point at. */
  .nav-link .nav-label { transition: transform .18s ease; }
  .nav-link:hover .nav-label,
  .nav-link:focus-visible .nav-label { transform: translateX(3px); }
}

@keyframes nav-rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Band action - the resume link pinned to the right of the CV band
   -------------------------------------------------------------------------- */

.band-action {
  flex: none;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: var(--size-mono);
  letter-spacing: .02em;
  text-decoration: none;
  white-space: nowrap;
}

.band-action:hover,
.band-action:focus-visible { color: var(--text); }

.band-action:hover .band-action__arrow,
.band-action:focus-visible .band-action__arrow { transform: translateY(3px); }
