/* tousana.com -- the design pass.
 *
 * Every colour, size and spacing decision is a token on :root. A redesign
 * is a change to these tokens and the rules below; it must never require
 * touching the HTML or the script. Class names describe what a thing IS
 * (mast, bio, project, video), not how it looks, so they survive a restyle.
 *
 * The register: white ground, greys for text, photographs carry all the
 * colour. Wayfinding (the menu, the cross-links, the CV's section headings,
 * the control labels) runs lowercase through --case-wayfinding; everything
 * that comes from site.json keeps its own casing.
 */

/* The typeface. Self-hosted, no external request. The family name is the
 * site's own alias, so the rest of the stylesheet never names the actual
 * font. Today the alias resolves to Unbounded at weight 200 (SIL Open Font
 * License; copy in assets/fonts/unbounded-OFL.txt), one static file
 * instanced from the Google Fonts variable source.
 *
 * The design is one weight everywhere. The stylesheet still asks for 400,
 * 500 and 600 through the --weight-* tokens, so all three weight slots
 * below point at the same 200 file: every declaration resolves to it, the
 * browser never synthesises bold from it, and no font-weight rule had to
 * change. Swapping in licensed Antipasto later means new woff2 files under
 * NEW names (the directory is immutable) and editing these three src lines. */
@font-face {
  font-family: "Tousana Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/unbounded-200.woff2") format("woff2");
}

@font-face {
  font-family: "Tousana Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/unbounded-200.woff2") format("woff2");
}

@font-face {
  font-family: "Tousana Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/unbounded-200.woff2") format("woff2");
}

:root {
  /* ground and the grey palette. The typeface is a hairline weight that
   * puts little ink on the page, so the greys sit a step darker than a
   * regular weight would need. --text is the floor for running text
   * (#4a4a4a is 8.9:1 on white); --dim is for metadata only (5.3:1) and
   * never carries a paragraph. --hairline is the one rule colour. */
  --bg: #ffffff;
  --ink: #1f1f1f;
  --text: #4a4a4a;
  --dim: #6b6b6b;
  --hairline: #e4e4e4;

  /* type: the stack, then the scale */
  --font-stack: "Tousana Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --size-meta: 0.8125rem;
  --size-nav: 0.9375rem;
  --size-landing-nav: 1.125rem;
  --size-body: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  --size-label: 1.1875rem;
  --size-lede: clamp(1.125rem, 1.05rem + 0.35vw, 1.3125rem);
  --size-display: clamp(3rem, 11vw, 9rem);
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-strong: 600;
  --leading-display: 0.95;
  --leading-tight: 1.25;
  --leading-body: 1.6;
  --tracking-display: -0.025em;
  --tracking-label: 0;
  --tracking-nav: 0.02em;
  --tracking-meta: 0.01em;
  --case-wayfinding: lowercase;

  /* space */
  --gutter: clamp(1.25rem, 4vw, 3.5rem);
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 2rem;
  --space-4: 4rem;
  --space-5: 8rem;
  --page-top: clamp(3rem, 8vw, 6rem);

  /* touch: every link is at least this tall and wide (CLAUDE.md, "Links
   * are easily tappable"); --tap-pad is the side padding that widens a
   * text link's box without moving its text. */
  --tap: 44px;
  --tap-pad: var(--space-1);

  /* motion: opacity and transform only, and none under reduced motion */
  --ease: 180ms ease-out;

  /* links: a hairline underline that appears on hover */
  --underline-offset: 0.3em;
  --underline-width: 1px;
  --focus-ring: 2px solid var(--ink);
  --focus-offset: 3px;

  /* the landing: the name stacked one word per line, the menu under it,
   * the block sitting a little above the optical centre of the field */
  --landing-lift: 8vh;
  --landing-nav-gap: var(--space-3);

  /* the reading column */
  --measure: 34em;
  --bio-offset: 0;

  /* the work page: a project is a strip of images at a shared height.
   * Projects with many images (seven or more) are a denser sheet at
   * --work-row-dense; the rest breathe at --work-row. Restored to the
   * pre-strip-layout scale (2026-09-23, client request: the strip
   * redesign had made every photo noticeably smaller than the original
   * one-project-at-a-time page, and an earlier tweak this session that
   * only widened phones to full-bleed hadn't fixed the size itself).
   * --work-row was clamp(14.5rem, 29vw, 26.5rem), before that
   * clamp(12rem, 24vw, 22rem); --work-row-dense was clamp(11rem, 20.5vw,
   * 19rem), before that clamp(9rem, 17vw, 16rem). Phones ignore both
   * (they size images by width, not height -- see the max-width: 40em
   * block below). */
  --work-row: clamp(16rem, 36vw, 34rem);
  --work-row-dense: clamp(13rem, 28vw, 26rem);
  --work-image-gap: 0.375rem;
  /* Phones: one photo fills the screen at a time, so the gap between them
   * is a deliberate pause, not a hairline -- was --space-1 (0.5rem). */
  --work-image-gap-phone: var(--space-3);
  /* The height a phone photo may reach before it is capped (see the
   * max-width: 40em block): svh, not vh, so a portrait doesn't jump
   * taller/shorter as the browser's address bar shows and hides. */
  --work-image-cap-phone: 90svh;
  --work-section-gap: clamp(4rem, 9vw, 7rem);
  --project-head-gap: var(--space-2);

  /* the video page */
  --video-max: 72rem;
  --video-gap: var(--space-4) var(--space-3);

  /* the quiet link between the work and video pages */
  --related-top: var(--space-3);

  /* the cv page: a heading column beside a reading column */
  --cv-measure: 52em;
  --cv-aside: 9rem;
  --cv-section-gap: var(--space-4);
  --cv-entry-gap: var(--space-3);
  --cv-list-indent: 1.1em;

  /* the back-to-top control: a white disc that floats bottom-right */
  --to-top-size: 3rem;
  --to-top-icon: 1rem;
}

/* On wide viewports the reading column steps off the corner the name sits
 * in, as a page in a book steps in from the spine. */
@media (min-width: 60em) {
  :root {
    --bio-offset: 22vw;
  }
}

html {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-stack);
  font-size: var(--size-body);
  font-weight: var(--weight-regular);
  line-height: var(--leading-body);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  box-sizing: border-box;
  background: var(--bg);
  padding: var(--gutter);
  padding-top: max(var(--gutter), env(safe-area-inset-top));
  padding-bottom: max(var(--gutter), env(safe-area-inset-bottom));
}

/* Hidden stays hidden even where a rule below sets display (the Links
 * nav item and list ship hidden until site.json has links). */
[hidden] {
  display: none !important;
}

/* Links: inherit their colour, and on hover darken to ink while a hairline
 * underline fades in. Focus from the keyboard gets a ring, never the
 * underline alone. */
a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: var(--underline-width);
  text-underline-offset: var(--underline-offset);
  transition: color var(--ease), text-decoration-color var(--ease);
}

a:hover {
  color: var(--ink);
  text-decoration-color: currentColor;
}

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

a:focus:not(:focus-visible) {
  outline: none;
}

@media (prefers-reduced-motion: reduce) {
  a,
  .to-top {
    transition: none;
  }
}

/* The mast: name and role at the top-left edge; site navigation at the
 * top-right, its text level with the name. */
.mast {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
  margin: 0;
}

.mast-name {
  margin: 0;
  color: var(--ink);
  font-size: var(--size-nav);
  font-weight: var(--weight-strong);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-label);
}

/* When the name is a link (every inner page) its tap box reaches down over
 * the role line, which is not interactive, so the text does not move. */
.mast-name a {
  display: inline-block;
  min-height: var(--tap);
  margin-bottom: calc(var(--leading-tight) * 1em - var(--tap));
}

.mast-role {
  margin: var(--space-1) 0 0;
  font-size: var(--size-meta);
  font-weight: var(--weight-regular);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-meta);
  color: var(--dim);
}

.mast-nav {
  display: flex;
  gap: var(--space-2);
  margin-right: calc(-1 * var(--tap-pad));
  font-size: var(--size-nav);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-nav);
  text-transform: var(--case-wayfinding);
}

/* Each nav link is a --tap tall box with the text at its top, level with
 * the name; side padding widens the box into the gutter and the gap.
 * border-box so the 44px minimum includes the padding: a short label
 * ("cv") is not padded out wider than its neighbours. */
.mast-nav a {
  display: block;
  box-sizing: border-box;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 var(--tap-pad);
}

.mast-nav a[aria-current="page"] {
  color: var(--dim);
}

/* Phones: four menu items do not fit beside the name, so the nav drops
 * under the role, wraps if it must, and its first link's text lines up
 * with the name. Wrapped rows are --tap tall, so never packed tighter. */
@media (max-width: 40em) {
  .mast {
    flex-direction: column;
    gap: var(--space-1);
  }

  .mast-nav {
    flex-wrap: wrap;
    margin-right: 0;
    margin-left: calc(-1 * var(--tap-pad));
  }
}

/* The landing: the name, one word per line, and the menu. The block sits
 * a little above the centre of the viewport, where the eye rests; the
 * rest of the page is white on purpose. */
body:has(> .landing) {
  display: flex;
  flex-direction: column;
}

.landing {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: var(--landing-lift);
}

.landing-name {
  width: min-content;
  margin: 0;
  color: var(--ink);
  font-size: var(--size-display);
  font-weight: var(--weight-medium);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
}

.landing-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-2);
  margin: var(--landing-nav-gap) 0 0 calc(-1 * var(--tap-pad));
  font-size: var(--size-landing-nav);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-nav);
  text-transform: var(--case-wayfinding);
}

.landing-nav a {
  display: flex;
  box-sizing: border-box;
  align-items: center;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 var(--tap-pad);
}

/* The film strip under the landing menu: 27 plain tiles of colour drifting
 * right to left on a seamless loop. Final client design (2026-11): the
 * colours ARE the design, not placeholders. Purely decorative -- aria-hidden,
 * not clickable, no cursor change. The tile colours and widths below are
 * design, not content: changing them is a code change here, they are not in
 * site.json and Studio cannot edit them. The markup holds the 27 tiles
 * twice so translateX(-50%) loops with no seam; the nth-child(27n + k)
 * rules colour both copies. Transforms only, no JS. The strip clips its
 * own overflow, so it can never cause sideways page scroll. A hovered tile
 * grows sideways only (client feedback 2026-11-09: growing in height read
 * as intrusive), so the strip needs no headroom above the tiles. */
.strip {
  --strip-gap: 9px;
  --strip-tile-h: 34px;
  --strip-grow: 3.6;
  margin-top: var(--space-4);
  overflow: hidden;
}

.strip-track {
  display: flex;
  gap: var(--strip-gap);
  width: max-content;
  padding-right: var(--strip-gap);
  animation: strip-drift 80s linear infinite;
}

.strip-tile {
  flex: 0 0 auto;
  height: var(--strip-tile-h);
  border-radius: 1px;
  transform-origin: center;
  transition: transform 0.35s ease;
}

@keyframes strip-drift {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* Pointer devices: hovering pauses the drift and widens the tile. Touch
 * devices just drift (a sticky :hover after a tap must not freeze it). */
@media (hover: hover) {
  .strip:hover .strip-track { animation-play-state: paused; }
  .strip-tile:hover { transform: scaleX(var(--strip-grow)); }
}

@media (hover: none) {
  .strip { --strip-tile-h: var(--tap); }
  .strip-tile { min-width: var(--tap); }
}

/* Reduced motion: no drift. The static wrapped grid, one copy of the
 * tiles, and no grow. */
@media (prefers-reduced-motion: reduce) {
  .strip-track {
    animation: none;
    flex-wrap: wrap;
    width: auto;
    padding-right: 0;
  }
  .strip-tile:nth-child(n + 28) { display: none; }
  .strip-tile { transition: none; }
  .strip-tile:hover { transform: none; }
}

.strip-tile:nth-child(27n + 1) { width: 28px; background: #7ec3d8; }
.strip-tile:nth-child(27n + 2) { width: 57px; background: #c9662e; }
.strip-tile:nth-child(27n + 3) { width: 42px; background: #8d4d3a; }
.strip-tile:nth-child(27n + 4) { width: 71px; background: #b48ac2; }
.strip-tile:nth-child(27n + 5) { width: 56px; background: #3f6fb5; }
.strip-tile:nth-child(27n + 6) { width: 41px; background: #d16d8e; }
.strip-tile:nth-child(27n + 7) { width: 70px; background: #5a5f6b; }
.strip-tile:nth-child(27n + 8) { width: 55px; background: #93a08b; }
.strip-tile:nth-child(27n + 9) { width: 40px; background: #c2b7a8; }
.strip-tile:nth-child(27n + 10) { width: 69px; background: #2e3e5c; }
.strip-tile:nth-child(27n + 11) { width: 54px; background: #7d8fae; }
.strip-tile:nth-child(27n + 12) { width: 39px; background: #42484f; }
.strip-tile:nth-child(27n + 13) { width: 68px; background: #d17a2f; }
.strip-tile:nth-child(27n + 14) { width: 53px; background: #b03a3a; }
.strip-tile:nth-child(27n + 15) { width: 38px; background: #87b3d1; }
.strip-tile:nth-child(27n + 16) { width: 67px; background: #5c6e52; }
.strip-tile:nth-child(27n + 17) { width: 52px; background: #c23b3b; }
.strip-tile:nth-child(27n + 18) { width: 37px; background: #2f4fa0; }
.strip-tile:nth-child(27n + 19) { width: 66px; background: #e0b52e; }
.strip-tile:nth-child(27n + 20) { width: 51px; background: #8c3b52; }
.strip-tile:nth-child(27n + 21) { width: 36px; background: #3b6fd1; }
.strip-tile:nth-child(27n + 22) { width: 65px; background: #8f8f8f; }
.strip-tile:nth-child(27n + 23) { width: 50px; background: #3d7a4f; }
.strip-tile:nth-child(27n + 24) { width: 35px; background: #20242b; }
.strip-tile:nth-child(27n + 25) { width: 64px; background: #111111; }
.strip-tile:nth-child(27n + 26) { width: 49px; background: #5b3d8f; }
.strip-tile:nth-child(27n + 27) { width: 34px; background: #2e8f86; }

/* The links page: a bare list, one --tap row each, lowercase like the
 * rest of the wayfinding -- these are outbound links, not bio content. */
.links-page {
  margin-top: var(--page-top);
  font-size: var(--size-nav);
  letter-spacing: var(--tracking-nav);
  text-transform: var(--case-wayfinding);
}

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

.links-list a {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  margin-left: calc(-1 * var(--tap-pad));
  padding: 0 var(--tap-pad);
}

/* The quiet cross-link: Video near the top of the work page, Portfolio
 * near the top of the video page. */
.related {
  margin: var(--related-top) 0 0;
  font-size: var(--size-nav);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-nav);
  text-transform: var(--case-wayfinding);
}

.related a {
  display: inline-flex;
  box-sizing: border-box;
  align-items: center;
  min-height: var(--tap);
  min-width: var(--tap);
  margin-left: calc(-1 * var(--tap-pad));
  padding: 0 var(--tap-pad);
}

/* One measured column, generous whitespace. The first paragraph is the
 * lede, larger and in ink; the rest read in the body grey. */
.bio {
  max-width: var(--measure);
  margin: var(--page-top) 0 0 var(--bio-offset);
}

.bio p {
  margin: 0;
}

.bio p:first-child {
  color: var(--ink);
  font-size: var(--size-lede);
  line-height: 1.4;
}

.bio p + p {
  margin-top: var(--space-2);
}

/* The work page: one section per project. The label and the role sit on
 * one line as a fragment, then the images in a strip at a shared height,
 * wrapping as they must, no chrome on any photograph. */
.work {
  margin-top: var(--page-top);
}

.project + .project {
  margin-top: var(--work-section-gap);
}

.project {
  scroll-margin-top: var(--gutter);
}

.project-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-2);
  margin: 0 0 var(--project-head-gap);
}

.project-label {
  margin: 0;
  color: var(--ink);
  font-size: var(--size-label);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-label);
}

.project-role {
  margin: 0;
  font-size: var(--size-meta);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-meta);
  color: var(--dim);
}

.project-images {
  --row: var(--work-row);
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--work-image-gap);
}

/* Seven images or more: a denser sheet. */
.project-images:has(> :nth-child(7)) {
  --row: var(--work-row-dense);
}

.project-image {
  margin: 0;
  flex: 0 0 auto;
  max-width: 100%;
}

/* Height-driven, width from the aspect ratio. A very wide image (a logo
 * strip) that would overflow the row is capped at the full width and
 * keeps its ratio inside the box, top-left, the spare space white. */
.project-image img {
  display: block;
  height: var(--row);
  width: auto;
  max-width: 100%;
  object-fit: contain;
  object-position: 0 0;
}

/* Narrow viewports: one photo per row, full-bleed, dominating the screen
 * as it did on the original (pre-strip) work page -- restored 2026-09-23
 * on the client's request, with one addition that page never had: a
 * height cap so a portrait can't run taller than the screen.
 *
 * The negative margins cancel the body's side gutter for this element
 * only, so the column (and so the images) run edge to edge while
 * .project-head above keeps the page gutter.
 *
 * This is a grid, not a column flexbox, on purpose. A column flex item
 * with flex-basis auto takes its main size from its content's intrinsic
 * height and a width-driven image then spills out of its box; a grid row
 * sizes to the laid-out content, so the box fits the image. */
@media (max-width: 40em) {
  .project-images {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--work-image-gap-phone);
    margin-left: calc(-1 * var(--gutter));
    margin-right: calc(-1 * var(--gutter));
  }

  /* width: 100% so every photo -- including the ~42 deck extracts under
   * 700px wide -- fills the screen the same as the rest (softness
   * accepted on those, see "Content" below; upscaling is only ever a
   * render-time stretch of the file already on disk, never a re-encode
   * of it). height: auto derives a portrait's height from that width,
   * same as any responsive image; max-height then caps it at
   * --work-image-cap-phone, and object-fit: contain is what makes that
   * cap safe -- it holds the photo's own ratio inside the shrunken box
   * instead of squashing it, so a capped portrait centers with a sliver
   * of white either side rather than distorting. */
  .project-image img {
    height: auto;
    width: 100%;
    max-width: 100%;
    max-height: var(--work-image-cap-phone);
    object-fit: contain;
    object-position: center;
  }
}

/* The video page: the players in two columns on wide viewports, one on
 * narrow, in the work page's rhythm. The frame's aspect ratio is the
 * --ratio custom property the renderer sets from the video's w/h, falling
 * back to 16:9. */
.videos {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--video-gap);
  max-width: var(--video-max);
  margin-top: var(--page-top);
}

@media (min-width: 50em) {
  .videos {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.video {
  margin: 0;
}

.video-head {
  margin: 0 0 var(--project-head-gap);
}

.video-title {
  margin: 0;
  color: var(--ink);
  font-size: var(--size-label);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-label);
}

.video-frame {
  width: 100%;
  aspect-ratio: var(--ratio, 16 / 9);
}

.video-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* The cv page: each section is a small lowercase heading in a column of
 * its own beside the reading column, the layout of a printed CV. On
 * narrow viewports the heading sits above its section instead. */
.cv {
  max-width: var(--cv-measure);
  margin-top: var(--page-top);
}

.cv-email {
  margin: 0;
}

.cv-email a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  margin-left: calc(-1 * var(--tap-pad));
  padding: 0 var(--tap-pad);
  color: var(--ink);
}

.cv-section {
  margin-top: var(--cv-section-gap);
}

.cv-heading {
  margin: 0 0 var(--space-2);
  font-size: var(--size-meta);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-meta);
  text-transform: var(--case-wayfinding);
  color: var(--dim);
}

.cv-summary {
  max-width: var(--measure);
  margin: 0;
  color: var(--ink);
  font-size: var(--size-lede);
  line-height: 1.4;
}

.cv-entry + .cv-entry {
  margin-top: var(--cv-entry-gap);
}

/* An entry's head: org and role on one line, place and dates on the next,
 * each pair a fragment rather than a stack of labels. The four are inline
 * and the break before the place is a block pseudo-element, so a pair
 * never splits across lines the way a wrapping row would. */
.cv-entry-head {
  line-height: var(--leading-body);
}

.cv-entry-head > * {
  display: inline;
  margin: 0 var(--space-2) 0 0;
}

.cv-place::before {
  content: "";
  display: block;
}

.cv-org {
  color: var(--ink);
  font-size: var(--size-label);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-label);
}

.cv-role {
  font-size: var(--size-body);
  line-height: var(--leading-tight);
}

.cv-place,
.cv-dates {
  font-size: var(--size-meta);
  letter-spacing: var(--tracking-meta);
  color: var(--dim);
}

.cv-bullets,
.cv-skills {
  max-width: var(--measure);
  margin: var(--space-2) 0 0;
  padding-left: var(--cv-list-indent);
}

.cv-bullets li + li,
.cv-skills li + li {
  margin-top: 0.25em;
}

.cv-bullets li::marker,
.cv-skills li::marker {
  color: var(--dim);
}

.cv-skills {
  margin-top: 0;
}

.cv-references {
  margin: var(--cv-section-gap) 0 0;
  font-size: var(--size-meta);
  letter-spacing: var(--tracking-meta);
  text-transform: var(--case-wayfinding);
  color: var(--dim);
}

@media (min-width: 50em) {
  .cv-section {
    display: grid;
    grid-template-columns: var(--cv-aside) minmax(0, 1fr);
    column-gap: var(--space-3);
  }

  .cv-heading {
    grid-column: 1;
    margin: 0;
    padding-top: 0.2em;
  }

  .cv-section > :not(.cv-heading) {
    grid-column: 2;
  }

  .cv-email,
  .cv-references {
    padding-left: calc(var(--cv-aside) + var(--space-3));
  }

  .cv-skills {
    columns: 2;
    column-gap: var(--space-3);
  }
}

/* Back to top: a white disc, bottom-right, clear of a phone's home
 * indicator, that appears once the first section has scrolled away
 * (site.js toggles .is-shown). Hidden it is also invisible to the tab
 * order and to pointers. Opacity and transform only. */
.to-top {
  position: fixed;
  right: var(--gutter);
  bottom: calc(var(--gutter) + env(safe-area-inset-bottom, 0px));
  width: var(--to-top-size);
  height: var(--to-top-size);
  min-width: var(--tap);
  min-height: var(--tap);
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 50%;
  background: var(--bg);
  color: var(--ink);
  display: grid;
  place-items: center;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(var(--space-1));
  transition: opacity var(--ease), transform var(--ease), border-color var(--ease), visibility 0s linear 180ms;
  -webkit-appearance: none;
  appearance: none;
  font: inherit;
}

.to-top.is-shown {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

.to-top:hover {
  border-color: var(--ink);
}

.to-top svg {
  width: var(--to-top-icon);
  height: var(--to-top-icon);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
