/* ==========================================================================
   josephafif.se

   A code editor, not a terminal. One monospace face at every size, a fixed
   rail on the left holding the file tree, a numbered gutter, and a small set
   of syntax colours used for one job each and never for decoration.

   Every space value comes from the scale below. Every colour pairing here was
   measured against BOTH grounds, --bg and the lighter --raised: the raised
   panel is the one that fails first, so the numbers in the comments are the
   worst case. Nothing sits under 4.5:1 except the line numbers and the tree
   characters, which are decorative and hidden from screen readers.
   ========================================================================== */

:root{
  /* --- ground ----------------------------------------------------------- */
  --bg:      #0d0f12;
  --panel:   #111419;      /* the rail */
  --raised:  #171b22;      /* a section that wants to read as another file */
  --line:    rgba(215, 220, 227, 0.10);
  --line-2:  rgba(215, 220, 227, 0.05);

  /* --- foreground ------------------------------------------------------- */
  --text:    #d7dce3;      /* 13.9:1 */
  --muted:   #8b94a3;      /*  6.3:1 */
  --comment: #7d8894;      /*  5.4:1 on --bg, 4.8:1 on --raised */
  --gutter:  #6f7987;      /*  4.3:1, decorative and aria-hidden */

  /* --- syntax. One job each. -------------------------------------------- */
  --key:     #7ea6c9;      /*  7.5:1  field names, nav */
  --string:  #a3be8c;      /*  9.5:1  values, links */
  --number:  #d08770;      /*  6.8:1  years, counts */
  --accent:  #d08770;      /* interaction, same as number on purpose */

  /* --- spacing ---------------------------------------------------------- */
  --s1: 0.375rem;
  --s2: 0.625rem;
  --s3: 1rem;
  --s4: 1.5rem;
  --s5: 2.25rem;
  --s6: 3rem;
  --s7: 4rem;
  --s8: 5rem;

  /* --- type ------------------------------------------------------------- */
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;

  --t-body:  0.9375rem;                    /* 15px */
  --t-small: 0.875rem;                     /* 14px, secondary only */
  --t-lede:  clamp(1rem, 0.95rem + 0.35vw, 1.1875rem);
  --t-entry: clamp(1.125rem, 0.95rem + 1vw, 1.625rem);
  --t-sect:  clamp(1.25rem, 1rem + 1.3vw, 2rem);
  --t-hero:  clamp(2.25rem, 0.4rem + 7.6vw, 6.5rem);

  /* --- frame ------------------------------------------------------------ */
  --rail:   16rem;
  --nums:   3.25rem;       /* the numbered gutter */
  --pad:    clamp(1.25rem, 3vw, 3rem);
  --path-h: 2.75rem;
  --measure: 74ch;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* A section that should read as a different file open in the same editor.
   Not a light theme: editors do not invert halfway down a document. */
.raised{ background: var(--raised); }

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

html{
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--path-h) + var(--s4));
}

body{
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--mono);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.65;
  font-variant-ligatures: none;      /* code ligatures in prose are noise */
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3{ font-weight: 500; margin: 0; }
p{ margin: 0; }
ol, ul, dl, dd{ margin: 0; padding: 0; list-style: none; }
figure{ margin: 0; }

::selection{ background: var(--accent); color: var(--bg); }


/* --------------------------------------------------------------------------
   Links
   -------------------------------------------------------------------------- */

a{
  color: var(--string);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
a:hover{ border-bottom-color: currentColor; }

:focus-visible{
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.skip{
  position: absolute;
  left: var(--s3);
  top: -5rem;
  z-index: 80;
  padding: var(--s2) var(--s3);
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--text);
  transition: top 0.2s var(--ease);
}
.skip:focus{ top: var(--s3); }


/* --------------------------------------------------------------------------
   Grain. One ambient effect, kept from the old design because it is the only
   thing stopping a flat dark page from looking like a slide.
   -------------------------------------------------------------------------- */

.grain{
  position: fixed;
  inset: -60%;
  z-index: 40;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  will-change: transform;
  animation: grain 800ms steps(1) infinite;
}

@keyframes grain{
  0%   { transform: translate3d(0, 0, 0); }
  20%  { transform: translate3d(-3%, 2%, 0); }
  40%  { transform: translate3d(2%, -3%, 0); }
  60%  { transform: translate3d(-2%, -2%, 0); }
  80%  { transform: translate3d(3%, 1%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}


/* --------------------------------------------------------------------------
   The rail. Fixed, so the navigation is always reachable and the screen is
   carrying weight on both sides instead of only the left.
   -------------------------------------------------------------------------- */

.rail{
  position: fixed;
  top: 0; left: 0; bottom: 0;
  z-index: 30;
  width: var(--rail);
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: var(--s4) 0;
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  overflow-y: auto;
}

.rail__me{
  padding-inline: var(--s4);
  display: block;
  color: var(--text);
  border-bottom: 0;
  font-weight: 500;
}
.rail__me:hover{ color: var(--text); }

.rail__role{
  padding-inline: var(--s4);
  margin-top: var(--s1);
  color: var(--comment);
  font-size: var(--t-small);
}

.tree{ font-size: var(--t-small); }

.tree__branch{ padding-left: var(--s4); }

.tree a{
  display: block;
  padding: 0.2rem var(--s4) 0.2rem 0;
  color: var(--muted);
  border-bottom: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tree a:hover{ color: var(--text); }
.tree a.is-current{ color: var(--key); }

.tree__twig{ color: var(--gutter); }

.tree__sub a{ padding-left: var(--s3); color: var(--comment); }
.tree__sub a:hover{ color: var(--string); }

.rail__foot{
  margin-top: auto;
  padding-inline: var(--s4);
  font-size: var(--t-small);
  color: var(--comment);
}
.rail__foot p + p{ margin-top: var(--s1); }

.rail__toggle{
  display: none;
  font: inherit;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  padding: 0.25rem var(--s2);
  cursor: pointer;
}
.rail__toggle:hover{ color: var(--text); border-color: var(--accent); }


/* --------------------------------------------------------------------------
   The document
   -------------------------------------------------------------------------- */

.main{ margin-left: var(--rail); }

/* Breadcrumb, the way an editor shows you where the caret is. */
.path{
  position: sticky;
  top: 0;
  z-index: 25;
  height: var(--path-h);
  display: flex;
  align-items: center;
  gap: 0.5ch;
  padding-inline: var(--pad);
  background: rgba(13, 15, 18, 0.92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
  font-size: var(--t-small);
  color: var(--comment);
}
.path__file{ color: var(--key); }

.section{
  padding: var(--s8) var(--pad);
  border-bottom: 1px solid var(--line-2);
}
.section:last-of-type{ border-bottom: 0; }

/* The numbered gutter. Numbers come from a counter, so nothing has to be
   kept in sync by hand, and they are hidden from screen readers. */
.doc{
  counter-reset: ln;
  padding-left: var(--nums);
  max-width: 62rem;
}

.doc > *{ position: relative; }

.doc > *::before{
  counter-increment: ln;
  content: counter(ln, decimal-leading-zero);
  position: absolute;
  left: calc(var(--nums) * -1);
  width: calc(var(--nums) - var(--s3));
  text-align: right;
  font-size: 0.8125rem;
  line-height: inherit;
  color: var(--gutter);
  user-select: none;
}

.doc > * + *{ margin-top: var(--s4); }

/* A leading comment line, the way a file starts. */
.note{
  color: var(--comment);
  font-size: var(--t-small);
}

.head{
  font-size: var(--t-sect);
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--text);
}

.lede{
  font-size: var(--t-lede);
  line-height: 1.55;
  max-width: var(--measure);
}

.body{
  color: var(--muted);
  max-width: var(--measure);
}


/* --------------------------------------------------------------------------
   Title card
   -------------------------------------------------------------------------- */

.hero{
  min-height: calc(100svh - var(--path-h));
  display: flex;
  align-items: center;
  padding: var(--s7) var(--pad);
  border-bottom: 1px solid var(--line-2);
}

.hero__inner{ width: 100%; max-width: 62rem; }

.hero__name{
  font-size: var(--t-hero);
  line-height: 0.95;
  letter-spacing: -0.06em;
  font-weight: 500;
  margin-block: var(--s4);
}
.hero__name .ln{ display: block; }

.hero__line{
  font-size: var(--t-lede);
  line-height: 1.55;
  max-width: 52ch;
  color: var(--muted);
}

.hero__meta{
  margin-top: var(--s6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s5);
  color: var(--comment);
  font-size: var(--t-small);
}
.hero__meta b{ font-weight: 400; color: var(--string); }


/* --------------------------------------------------------------------------
   Field lists. The workhorse: used for About and inside every console.
   -------------------------------------------------------------------------- */

.fields{
  border-top: 1px solid var(--line);
  max-width: var(--measure);
}

.fields > div{
  display: grid;
  grid-template-columns: minmax(0, 11ch) minmax(0, 1fr);
  gap: var(--s4);
  padding-block: var(--s2);
  border-bottom: 1px solid var(--line-2);
}

.fields dt{ color: var(--key); }
.fields dd{ color: var(--text); overflow-wrap: anywhere; }
.fields dd a{ color: var(--string); }


/* --------------------------------------------------------------------------
   Entries. A dense listing, closer to `ls -l` than to a stack of cards:
   name, what it is, what it is built with, when. Eight of them fit on one
   screen, which a column of paragraphs never would. The console opens
   underneath the row it belongs to.
   -------------------------------------------------------------------------- */

.entry{
  display: grid;
  grid-template-columns:
    minmax(0, 10rem) minmax(0, 1fr) minmax(0, 12rem) 4.5rem auto;
  gap: var(--s2) var(--s4);
  align-items: baseline;
  padding-block: var(--s2);
  border-bottom: 1px solid var(--line-2);
}
.entry:hover{ background: rgba(215, 220, 227, 0.02); }

.entry__name{
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--key);
  letter-spacing: -0.01em;
}

.entry__what{ color: var(--muted); }

.entry__year{
  color: var(--number);
  font-variant-numeric: lining-nums tabular-nums;
  text-align: right;
}

.entry__act{ justify-self: end; }

/* Plain words, not chips. Three bordered tokens wrap onto three lines in a
   12rem column and turn the listing back into a stack of blocks. */
.stack{
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.85ch;
  color: var(--string);
  font-size: var(--t-small);
}

.entry .console{ grid-column: 1 / -1; }

/* In the row the button is a word, not a box: eight bordered buttons down a
   listing is a wall of rectangles. */
.entry .showme{
  border: 0;
  padding: 0;
  font-size: var(--t-small);
  color: var(--accent);
}
.entry .showme::before{ content: "> "; color: var(--gutter); }
.entry .showme:hover{ background: none; text-decoration: underline; }

/* --------------------------------------------------------------------------
   show me
   -------------------------------------------------------------------------- */

.entry__act{ margin-top: var(--s4); }

.showme{
  font: inherit;
  color: var(--accent);
  background: none;
  border: 1px solid var(--line);
  padding: var(--s2) var(--s4);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.showme::before{ content: "> "; color: var(--gutter); }
.showme:hover{ border-color: var(--accent); background: rgba(208, 135, 112, 0.07); }


/* --------------------------------------------------------------------------
   Contact
   -------------------------------------------------------------------------- */

.mail{
  font-size: clamp(1rem, 0.65rem + 1.5vw, 1.625rem);
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}

.elsewhere{
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
}

/* --------------------------------------------------------------------------
   Screenshots.

   Off by default. Each skin decides whether to show one and what it means
   there, which is the difference between a shared asset and a shared design.
   -------------------------------------------------------------------------- */

.shot{ display: none; margin: 0; }
.shot img{ display: block; width: 100%; height: auto; }
.shot figcaption{ display: none; }

/* The editor keeps its listing dense, so a picture only appears once you have
   asked to see the project. It comes in above the console, captioned with its
   own path, the way a file reference would be. */
[data-skin="editor"] .entry.is-showing .shot{
  display: block;
  grid-column: 1 / -1;
  order: 1;
  margin-top: var(--s3);
  border: 1px solid var(--line);
}
[data-skin="editor"] .entry.is-showing .shot figcaption{
  display: block;
  padding: var(--s1) var(--s2);
  border-top: 1px solid var(--line);
  background: var(--panel);
  color: var(--gutter);
  font-size: var(--t-small);
}
[data-skin="editor"] .entry .console{ order: 2; }


/* --------------------------------------------------------------------------
   The console. A project opens an output pane underneath itself, the way an
   editor drops a terminal into the bottom of the window. It is open in the
   page without JavaScript and collapsed by the script, so the content is
   always in the document, in the print, and for search engines.

   Height animates through max-height, set from the content's own height by
   the script. The tidier grid-template-rows trick does not survive here: an
   overflow:hidden child zeroes the track's automatic minimum, so the row
   collapses to nothing however open it is meant to be.
   -------------------------------------------------------------------------- */

.console{ margin-top: var(--s4); }

.js .console{
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.32s var(--ease);
}

.console__inner{
  border: 1px solid var(--line);
  background: var(--bg);
}

.console__bar{
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: var(--s4);
  min-height: 2.25rem;          /* so the stretched close button clears 24px */
  padding-left: var(--s4);
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  font-size: var(--t-small);
}

/* The prompt is the part that gives, so the close button is never pushed
   off a narrow screen. min-width:0 is what actually lets a flex item shrink
   below its content. */
.console__prompt{
  color: var(--comment);
  align-self: center;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.console__prompt::first-letter{ color: var(--string); }

/* A window control: flush into the corner, full height of the bar, and it
   fills on hover the way a titlebar close button does. The cross is drawn
   from two rules rather than typed, so no font can fail to render it. */
.console__close{
  flex: none;
  position: relative;
  width: 2.75rem;
  align-self: stretch;
  background: none;
  border: 0;
  border-left: 1px solid var(--line);
  cursor: pointer;
  transition: background 0.15s var(--ease);
}

.console__close::before,
.console__close::after{
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0.7rem;
  height: 1px;
  background: var(--muted);
  transition: background 0.15s var(--ease);
}
.console__close::before{ transform: translate(-50%, -50%) rotate(45deg); }
.console__close::after{ transform: translate(-50%, -50%) rotate(-45deg); }

.console__close:hover{ background: var(--accent); }
.console__close:hover::before,
.console__close:hover::after{ background: var(--bg); }

/* A window onto the output, not a card containing it. Fixed height with its
   own scrollbar: you are looking at part of a file, and you scroll it. */
.console__body{
  max-height: min(58vh, 25rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s4);
  font-size: var(--t-small);
  line-height: 1.6;
  scrollbar-width: thin;
  scrollbar-color: var(--gutter) transparent;
}

.console__body::-webkit-scrollbar{ width: 11px; }
.console__body::-webkit-scrollbar-track{ background: transparent; }
.console__body::-webkit-scrollbar-thumb{
  background: var(--line);
  border: 3px solid var(--bg);
  border-radius: 0;
}
.console__body:hover::-webkit-scrollbar-thumb{ background: var(--gutter); }

.console__body > * + *{ margin-top: var(--s3); }
.console__body p{ color: var(--muted); }

/* The command was `cat` on a markdown file, so the output is that file:
   hashes and hyphens included. */
.md-h1, .md-h2{ color: var(--key); }
.md-h1::before{ content: "# "; color: var(--gutter); }
.md-h2::before{ content: "## "; color: var(--gutter); }
.md-h2{ margin-top: var(--s4) !important; }

.console__body .fields{ max-width: none; }
.console__body .fields > div{ grid-template-columns: minmax(0, 14ch) minmax(0, 1fr); }

/* The prompt is real: you can type into it. Commands it does not know come
   back with an opinion. */
.console__log{ margin-top: var(--s3) !important; }
.console__log p{ margin-top: var(--s1); }
.console__log .echo{ color: var(--comment); }
.console__log .reply{ color: var(--string); }

.console__form{
  display: flex;
  align-items: baseline;
  gap: 0.6ch;
  margin-top: var(--s2) !important;
}

.console__sigil{ color: var(--string); }

.console__input{
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  color: var(--text);
  background: none;
  border: 0;
  padding: 0;
  caret-color: var(--accent);
}
.console__input::placeholder{ color: var(--gutter); }

/* Words in the console are present in the DOM from the first byte and only
   coloured in one by one. Animating colour costs no layout and no new paint
   layer, and because the words already occupy their space the panel's height
   is correct before a single one appears. */
.js .console .w{ transition: color 140ms linear; }

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


/* Output lines. A hanging indent so a wrapped line stays under its own text
   and the hyphens keep a clean column, the way a terminal list does. */
.bullets{ margin-top: var(--s2) !important; }

.bullets li{
  color: var(--muted);
  padding-block: 0.1rem;
  padding-left: 2ch;
  text-indent: -2ch;
}
.bullets li::before{ content: "- "; color: var(--gutter); }

/* What something used to be. Dimmed, never struck through: a line drawn
   across a word makes it harder to read and gains no information. */
.from{ color: var(--comment); }
.to{ color: var(--string); }
.num{ color: var(--number); font-variant-numeric: lining-nums tabular-nums; }


/* --------------------------------------------------------------------------
   Narrow screens. The rail lies down across the top.
   -------------------------------------------------------------------------- */

@media (max-width: 60rem){
  :root{ --s8: 4.5rem; --s7: 3.5rem; --nums: 2.75rem; }

  .rail{
    position: sticky;
    top: 0;
    bottom: auto;
    width: auto;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--s3);
    padding: var(--s3) var(--pad);
    border-right: 0;
    border-bottom: 1px solid var(--line);
    overflow: visible;
  }

  .rail__me, .rail__role{ padding-inline: 0; }
  .rail__role{ display: none; }

  .rail__toggle{ display: block; }

  .tree{
    flex: 1 0 100%;
    display: none;
    padding-top: var(--s2);
    border-top: 1px solid var(--line-2);
  }
  .tree.is-open{ display: block; }
  .tree__branch{ padding-left: 0; }
  .tree a{ padding-block: 0.4rem; }

  .rail__foot{ display: none; }

  .main{ margin-left: 0; }
  .path{ display: none; }

  .hero{ min-height: 72svh; }

  /* Name and year on one line, everything else stacked under it. */
  .entry{
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--s1) var(--s3);
    padding-block: var(--s4);
  }
  .entry__name{ grid-column: 1; }
  .entry__year{ grid-column: 2; }
  .entry__what,
  .entry .stack,
  .entry__act,
  .entry .console{ grid-column: 1 / -1; }
  .entry__act{ justify-self: start; margin-top: var(--s1); }

  .fields > div{ grid-template-columns: 1fr; gap: 0; padding-block: var(--s2); }
  .fields dt{ font-size: var(--t-small); }

}


/* --------------------------------------------------------------------------
   Respect the setting.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior: auto; }
  .grain{ animation: none; }
  .js .console{ transition: none; }
  .js .console .w{ color: inherit; transition: none; }

  *, *::before, *::after{
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}


/* --------------------------------------------------------------------------
   The gate.

   Skin-agnostic on purpose: it is the room you stand in before choosing a
   room, so it belongs to none of them. Shown only on a first visit and only
   with JavaScript, which is why the page underneath is plain HTML that a
   crawler or a reader without JavaScript sees straight away.
   -------------------------------------------------------------------------- */

.gate{ display: none; }

.js.choosing .rail,
.js.choosing .main,
.js.choosing .skinner{ display: none !important; }

.js.choosing .gate{
  display: grid;
  place-items: center;
  position: fixed;
  inset: 0;
  z-index: 90;
  padding: var(--s5) var(--s4);
  overflow-y: auto;
  background: #0b0d10;
  color: #e7ebf0;
  font-family: var(--mono);
}

.gate__inner{ width: 100%; max-width: 62rem; }

.gate__eyebrow{
  color: #7f8a99;
  font-size: 0.875rem;
}

.gate__q{
  margin-top: var(--s3);
  font-size: clamp(1.5rem, 1rem + 2.4vw, 2.75rem);
  letter-spacing: -0.035em;
  line-height: 1.1;
}

.gate__sub{
  margin-top: var(--s3);
  max-width: 44ch;
  color: #98a2b0;
}

.gate__choices{
  margin-top: var(--s6);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--s3);
}

.pick{
  display: block;
  width: 100%;
  height: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  background: none;
  border: 1px solid rgba(231, 235, 240, 0.14);
  padding: var(--s3);
  cursor: pointer;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.pick:hover{
  border-color: rgba(231, 235, 240, 0.5);
  background: rgba(231, 235, 240, 0.03);
}

.pick__name{
  display: block;
  margin-top: var(--s3);
  font-weight: 500;
}

.pick__what{
  display: block;
  margin-top: var(--s1);
  font-size: 0.875rem;
  line-height: 1.5;
  color: #98a2b0;
}

/* A shrunk-down impression of each skin, built from four bars rather than a
   screenshot so it can never fall out of date with the real thing. */
.pick__art{
  display: block;
  position: relative;
  height: 5.5rem;
  overflow: hidden;
}
.pick__art i{ position: absolute; display: block; }

.art--editor{ background: #0d0f12; }
.art--editor i:nth-child(1){ inset: 0 auto 0 0; width: 22%; background: #171b22; }
.art--editor i:nth-child(2){ top: 22%; left: 30%; width: 46%; height: 3px; background: #7ea6c9; }
.art--editor i:nth-child(3){ top: 40%; left: 30%; width: 58%; height: 3px; background: #4a525e; }
.art--editor i:nth-child(4){ top: 55%; left: 30%; width: 34%; height: 3px; background: #a3be8c; }

.art--poster{ background: #0a0a0a; }
.art--poster i:nth-child(1){ inset: 14% 8% auto 8%; height: 34%; background: #ff3b1f; }
.art--poster i:nth-child(2){ top: 58%; left: 8%; width: 60%; height: 9px; background: #f7f7f5; }
.art--poster i:nth-child(3){ top: 76%; left: 8%; width: 34%; height: 9px; background: #f7f7f5; }
.art--poster i:nth-child(4){ display: none; }

.art--grid{ background: #f2f2f0; }
.art--grid i:nth-child(1){ inset: 0 auto 0 33%; width: 1px; background: #d2d2ce; }
.art--grid i:nth-child(2){ inset: 0 auto 0 66%; width: 1px; background: #d2d2ce; }
.art--grid i:nth-child(3){ top: 16%; left: 8%; width: 40%; height: 5px; background: #e8320c; }
.art--grid i:nth-child(4){ top: 38%; left: 8%; width: 78%; height: 34%; border: 1px solid #c9c9c4; }

.gate__skip{ margin-top: var(--s5); }

.gate__link{
  font: inherit;
  font-size: 0.875rem;
  color: #7f8a99;
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(231, 235, 240, 0.25);
  padding: 0 0 0.15em;
  cursor: pointer;
}
.gate__link:hover{ color: #e7ebf0; }


/* --------------------------------------------------------------------------
   The way back to the question. Every skin restyles this; it is here so it
   always exists.
   -------------------------------------------------------------------------- */

.skinner{
  position: fixed;
  right: var(--s3);
  bottom: var(--s3);
  z-index: 50;
  font: inherit;
  font-size: var(--t-small);
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 0.3rem var(--s2);
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.skinner:hover{ opacity: 1; color: var(--text); border-color: var(--accent); }

@media print{ .gate, .skinner{ display: none !important; } }


/* --------------------------------------------------------------------------
   The CV. Hidden on screen, and the only thing that prints.

   Printing the page itself would put the contact details on the last sheet,
   unfold every console and carry the `$ cat` prompts onto paper. So the site
   is hidden entirely and this is printed instead.
   -------------------------------------------------------------------------- */

.cv{ display: none; }

@page{ margin: 15mm 14mm; }

@media print{
  .rail, .path, .main, .grain, .skip{ display: none !important; }

  html, body{ background: #fff; color: #000; }

  /* A skin sets font-family on body with more specificity than this block
     has, so the CV states its own. A rule on the element always beats an
     inherited one, which is why no !important is needed. */
  .cv{
    display: block;
    font-size: 9pt;
    line-height: 1.35;
  }
  .cv, .cv *{ font-family: var(--mono); color: #000; }
  .cv__when, .cv__reach{ color: #444; }
  .cv__dot{ color: #999; }

  .cv__head{
    border-bottom: 1.5pt solid #000;
    padding-bottom: 5pt;
    margin-bottom: 10pt;
  }

  .cv__name{
    font-size: 21pt;
    font-weight: 500;
    letter-spacing: -0.03em;
    line-height: 1.1;
  }

  .cv__title{ margin-top: 2pt; font-size: 10pt; }

  .cv__reach{
    margin-top: 5pt;
    font-size: 8.5pt;
  }
  .cv__dot{ padding: 0 0.7ch; }

  .cv__summary{
    margin-bottom: 8pt;
    max-width: 52em;
  }

  .cv__h{
    font-size: 8.5pt;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    border-bottom: 0.5pt solid #bbb;
    padding-bottom: 3pt;
    margin: 9pt 0 5pt;
    break-after: avoid;
  }

  .cv__job{
    display: grid;
    grid-template-columns: 12ch 1fr;
    gap: 2ch;
    margin-bottom: 6pt;
    break-inside: avoid;
  }

  .cv__when{ font-variant-numeric: lining-nums tabular-nums; }

  .cv__role{ font-weight: 500; }
  .cv__role span{ font-weight: 400; }

  .cv__job ul{ margin-top: 3pt; }

  .cv__job li{
    padding-left: 1.6ch;
    text-indent: -1.6ch;
    margin-top: 1.5pt;
    max-width: 54em;
  }
  .cv__job li::before{ content: "– "; color: #777; }

  .cv__list > div,
  .cv__facts > div{
    display: grid;
    grid-template-columns: 12ch 1fr;
    gap: 2ch;
    break-inside: avoid;
  }

  /* trafiksimulator is fifteen characters and was colliding with its own
     description in a twelve-character column. */
  .cv__list > div{ grid-template-columns: 17ch 1fr; }

  .cv__list > div{ margin-bottom: 4pt; }
  .cv__facts > div{ margin-bottom: 2pt; }

  .cv__list dt,
  .cv__facts dt{ font-weight: 500; }
  .cv__list dd{ max-width: 54em; }

  .cv, .cv *{ background: none; }
}
