/* =============================================================
   smitsutake.neocities.org
   "the person behind the terminal" — shared design system

   TWO LAYERS, ONE RULE:
     terminal layer (machine) -> accent is GREEN
     paper    layer (human)   -> accent is PURPLE
   In either layer, the accent colour means: important, or touchable.
   ============================================================= */

:root {
  /* --- terminal layer : accent = green --- */
  --void:   #000000;   /* home background, boot screen */
  --green:  #00FF00;   /* terminal accent: headings, active nav, buttons */
  --mint:   #8FE3A8;   /* terminal accent, softened: nav links, hover text */
  --signal: #D9D9D9;   /* secondary text on dark, [ WARN ] */
  --dark:   #0D0F0D;   /* nav strip, inverted hover background */

  /* --- paper layer : accent = purple --- */
  --paper:  #F4F2EC;   /* sheet + content background */
  --ink:    #21261F;   /* body text, borders */
  --purple: #8E6BC2;   /* paper accent: links */
  --plum:   #5B3F86;   /* paper accent, darker: labels, sub-headings, focus */

  /* --- utility --- */
  --field-border: #C9C5B8;
  --field-bg:     #FFFFFF;
  --caption:      #666356;   /* darkened: #8A8778 was 3.22:1 on paper, under AA */
  --footer:       #666356;   /* darkened: #8B887C was 3.17:1 on paper, under AA */
  --rule:         #E2DFD5;

  /* --- typefaces --- */
  --f-mono: 'JetBrains Mono', monospace;
  --f-sans: 'DM Sans', sans-serif;

  /* --- type scale (fluid) --- */
  --t-display: clamp(5rem, 18vw, 14rem);      /* WELCOME only */
  --t-h1:      clamp(2.75rem, 9vw, 4.5rem);   /* page title */
  --t-h2:      clamp(2.25rem, 7vw, 3.5rem);   /* sheet heading */
  --t-h3:      1.375rem;                      /* sub-heading */
  --t-lead:    1.125rem;                      /* sheet prose */
  --t-body:    1rem;
  --t-ui:      0.85rem;                       /* nav, chips */
  --t-code:    0.9rem;
  --t-caption: 0.75rem;

  --measure: 640px;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--f-sans);
  font-size: var(--t-body);
  line-height: 1.6;
  background: var(--paper);
  color: var(--ink);
}

body.locked { overflow: hidden; }

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

/* keyboard focus stays visible everywhere, and states the layer rule */
:focus-visible { outline: 2px solid var(--green); outline-offset: 3px; }
.sheet :focus-visible,
.paper :focus-visible { outline-color: var(--plum); }

.skip {
  position: absolute; left: -9999px;
  background: var(--green); color: var(--void);
  font-family: var(--f-mono); font-size: var(--t-ui);
  padding: 0.6rem 1rem; z-index: 2000;
}
.skip:focus { left: 0.5rem; top: 0.5rem; }

/* =============================================================
   NAV — shell prompt. same component on every page.
   ============================================================= */

.nav {
  background: var(--dark);
  padding: 0.7rem 1.2rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: center;
  font-family: var(--f-mono);
  font-size: var(--t-ui);
}

/* the wordmark IS the logo. no graphic mark. */
.wordmark {
  color: var(--green);
  text-decoration: none;
  white-space: nowrap;
}
.wordmark::after {
  content: "\2588";
  opacity: 0;
  margin-left: 0.15em;
}
.wordmark:hover::after,
.wordmark:focus-visible::after {
  opacity: 1;
  animation: blink 1s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.nav a:not(.wordmark) {
  color: var(--mint);
  opacity: 0.75;
  text-decoration: none;
  padding: 0 0.25em;          /* reserved so the hover fill shifts nothing */
  transition: background 0.12s, color 0.12s, opacity 0.12s;
}

/* Reverse video follows the pointer, not the page. The inverted block is
   the strongest thing in the strip, so it belongs on what you are about
   to click rather than on where you already are. */
.nav a:not(.wordmark):hover,
.nav a:not(.wordmark):focus-visible {
  background: var(--green);
  color: var(--void);
  opacity: 1;
  text-decoration: none;
}

/* "you are here" is still marked, just quietly: full strength plus a
   rule under it. aria-current carries it for screen readers. */
.nav a.here {
  opacity: 1;
  text-decoration: underline;
  text-underline-offset: 0.3em;
}
.nav a.here:hover,
.nav a.here:focus-visible { text-decoration: none; }

/* =============================================================
   PAPER SURFACE — content pages and sheets share this
   ============================================================= */

.paper {
  background: var(--paper);
  color: var(--ink);
}

.column {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 3.5rem 1.5rem 4rem;
}

.page-title {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: var(--t-h1);
  line-height: 0.95;
  letter-spacing: -0.02em;
  margin: 0 0 1.5rem;
}
/* The masthead carries its own translation: TodaiarY is a coinage, and a
   visitor should not have to work it out. */
.title-gloss {
  font-size: 0.4em;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--caption);
  white-space: nowrap;
}

/* diary is a human surface: it speaks in DM Sans */
.page-title.human {
  font-family: var(--f-sans);
  letter-spacing: -0.03em;
}

.sub-heading {
  font-family: var(--f-sans);
  font-weight: 700;
  font-size: var(--t-h3);
  color: var(--plum);
  margin: 2.5rem 0 0.5rem;
}

/* Prose links use --plum, not --purple. --purple on --paper measures
   3.7:1, which fails WCAG AA at body size; --plum measures 7.5:1.
   --purple survives on borders and marks, where size carries it. */
.paper a,
.sheet a {
  color: var(--plum);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.caption {
  font-size: var(--t-caption);
  color: var(--caption);
}

.rule { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }

/* link chips — bordered, invert on hover like everything else */
.links { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 1.5rem 0; }
.links a {
  font-family: var(--f-mono);
  font-size: var(--t-ui);
  border: 1.5px solid var(--purple);
  padding: 0.5rem 1rem;
  text-decoration: none;
  color: var(--plum);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.links a:hover,
.links a:focus-visible {
  background: var(--purple);
  color: var(--paper);
  border-color: var(--purple);
}

/* code block + copy button */
.code {
  position: relative;
  font-family: var(--f-mono);
  font-size: var(--t-code);
  background: var(--dark);
  color: var(--mint);
  padding: 1rem 3.5rem 1rem 1rem;
  overflow-x: auto;
  white-space: pre;
}
.copy {
  position: absolute; top: 0.5rem; right: 0.5rem;
  font-family: var(--f-mono); font-size: var(--t-caption);
  background: none; border: 1px solid var(--mint);
  color: var(--mint); padding: 0.25rem 0.5rem; cursor: pointer;
}
.copy:hover, .copy:focus-visible { background: var(--mint); color: var(--dark); }
.copy.done { border-color: var(--green); color: var(--green); background: none; }

.foot {
  font-family: var(--f-mono);
  font-size: var(--t-caption);
  color: var(--footer);
  padding: 2rem 1.5rem 3rem;
  text-align: center;
}

/* =============================================================
   HOME
   ============================================================= */

#home {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--void);
  color: var(--green);
  font-family: var(--f-mono);
}

.home-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1rem;
  padding: 2rem 0 4rem;
}

.section-label {
  font-size: var(--t-ui);
  color: var(--signal);
  letter-spacing: 0.08em;
  padding: 0 max(1.5rem, calc((100% - 62rem) / 2));
}

.scroll-cue {
  color: var(--signal);
  opacity: 0.6;
  margin-left: 0.9rem;
  letter-spacing: 0.04em;
}

.term-btn {
  position: relative;
  background: none;
  border: 2px solid var(--green);
  color: var(--green);
  font-family: var(--f-mono);
  font-size: 1.4rem;
  padding: 1.1rem 2.4rem;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.term-btn:hover,
.term-btn:focus-visible { background: var(--green); color: var(--void); }

/* the buttons carry the same ??? arch as the cards. see ARCH below. */

/* =============================================================
   UPDATES CAROUSEL — CSS scroll-snap. degrades to plain scroll.
   ============================================================= */

.carousel {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding: 0.75rem max(1.5rem, calc((100% - 62rem) / 2)) 1.25rem;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.carousel::-webkit-scrollbar { display: none; }

.card {
  flex: 0 0 clamp(15rem, 26vw, 19rem);
  scroll-snap-align: center;
  border: 1.5px solid var(--green);
  background: var(--void);
  text-decoration: none;
  color: var(--green);
  display: flex;
  flex-direction: column;
}
.card:hover, .card:focus-visible { background: var(--dark); }

.card img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-bottom: 1.5px solid var(--green);
}
.card-text { padding: 0.9rem 1rem 1.1rem; }
.card-type {
  font-size: var(--t-caption);
  color: var(--mint);
  letter-spacing: 0.06em;
  display: block;
  margin-bottom: 0.35rem;
}
.card-title {
  font-weight: 700;
  font-size: var(--t-body);
  line-height: 1.3;
  margin: 0 0 0.5rem;
}
.card-excerpt {
  font-family: var(--f-sans);
  font-size: var(--t-caption);
  line-height: 1.5;
  color: var(--signal);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}



/* clones exist only to make the belt endless; they are duplicates of
   real cards and are hidden from assistive tech in the markup. */
.carousel .clone { pointer-events: auto; }

/* =============================================================
   BOOT SEQUENCE — theatre and loading state at once. ~2s.
   ============================================================= */

#intro {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--void);
  will-change: transform;
  transition: transform 0.8s cubic-bezier(0.65, 0, 0.35, 1);
}
#intro.gone { transform: translateY(-100%); }

.boot-screen, .welcome-screen {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.boot-screen { padding: 2rem; opacity: 1; transition: opacity 0.8s ease; }
.boot-screen.fade-out { opacity: 0; }

#boot-text {
  font-family: var(--f-mono);
  color: var(--green);
  font-size: 1.1rem;
  line-height: 1.6;
  white-space: pre-wrap;
  margin: 0;
}
#boot-text .warn { color: var(--signal); }

.welcome-screen {
  flex-direction: column;
  gap: 2rem;
  visibility: hidden;
}
.welcome-screen.active { visibility: visible; }

#welcome-text {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: var(--t-display);
  color: var(--green);
  letter-spacing: 0.04em;
  line-height: 1;
  margin: 0;
}
#welcome-text .letter { opacity: 0; transition: opacity 0.9s ease; }
#welcome-text .letter.on { opacity: 1; }

#scroll-hint {
  position: absolute;
  bottom: 8vh;
  font-family: var(--f-mono);
  color: var(--signal);
  font-size: 1.15rem;
  letter-spacing: 0.08em;
  opacity: 0;
  transition: opacity 0.8s ease;
  display: flex;
  align-items: center;
  gap: 0.5em;
}
#scroll-hint.visible { opacity: 1; }
/* Pointing up, the same way the overlay leaves: the hint and the motion
   it triggers now agree. Rotating -90deg also turns the bounce's local
   +X into screen-up, so the nudge follows the arrow. */
#scroll-hint .chev {
  display: inline-block;
  transform: rotate(-90deg);
  font-size: 1.3em;
  animation: tonton 1.5s ease-in-out infinite;
}
@keyframes tonton {
  0%, 50%, 100% { transform: rotate(-90deg) translateX(0); }
  10% { transform: rotate(-90deg) translateX(5px); }
  20% { transform: rotate(-90deg) translateX(0); }
  30% { transform: rotate(-90deg) translateX(5px); }
  40% { transform: rotate(-90deg) translateX(0); }
}

/* =============================================================
   SHEET — about / contact. slides over home. not a page.
   ============================================================= */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  visibility: hidden;
}
.overlay.open { visibility: visible; }

.overlay .backdrop {
  position: absolute;
  inset: 0;
  background: rgba(5, 6, 5, 0.75);
  opacity: 0;
  transition: opacity 0.3s ease;
  border: 0;
  width: 100%;
  cursor: pointer;
}
.overlay.open .backdrop { opacity: 1; }

.sheet {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 105%);
  transition: transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
  width: min(680px, 100%);
  height: 92vh;
  background: var(--paper);
  color: var(--ink);
  border-radius: 18px 18px 0 0;
  box-shadow: 0 -12px 60px rgba(0, 0, 0, 0.6);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.overlay.open .sheet { transform: translate(-50%, 0); }

.sheet-close {
  position: sticky;
  top: 0.9rem;
  margin-left: calc(100% - 3.4rem);
  margin-right: 0.9rem;
  width: 2.5rem; height: 2.5rem;
  border-radius: 50%;
  border: 1.5px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-mono);
  font-size: 1.05rem;
  cursor: pointer;
  z-index: 5;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.sheet-close:hover, .sheet-close:focus-visible {
  background: var(--dark); color: var(--mint); border-color: var(--dark);
}

.sheet-inner {
  max-width: 560px;
  margin: 0 auto;
  padding: 0.5rem 1.5rem 4rem;
  font-family: var(--f-sans);
}
.sheet-inner h2 {
  font-family: var(--f-sans);
  font-weight: 700;
  font-size: var(--t-h2);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin: 0 0 1.25rem;
}
.sheet-inner p { font-size: var(--t-lead); line-height: 1.55; }

.avatar {
  width: 160px; height: 160px;
  image-rendering: pixelated;
  margin: 0 auto 1.5rem;
}

/* =============================================================
   ENTRY LIST — projects.html and diary.html
   A directory listing, not a grid of tiles: one row per entry,
   because that is how a folder actually reads.
   ============================================================= */

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

.entry {
  border-top: 1px solid var(--rule);
}
.entry:last-child { border-bottom: 1px solid var(--rule); }

.entry a {
  display: block;
  padding: 1.5rem 0.75rem;
  text-decoration: none;
  color: inherit;
  transition: background 0.15s, color 0.15s;
}
/* The fill is plum, not purple. Purple was chosen for the border, where
   contrast does not have to carry text; using the same value behind paper
   text gave 3.73:1, under AA. Plum behind paper is 7.08:1. The border stays
   purple, so the row still reads as the same family. */
.entry a:hover,
.entry a:focus-visible {
  background: var(--plum);
  color: var(--paper);
}
.entry a:hover .entry-meta,
.entry a:focus-visible .entry-meta,
.entry a:hover .entry-blurb,
.entry a:focus-visible .entry-blurb { color: var(--paper); }

.entry-meta {
  font-family: var(--f-mono);
  font-size: var(--t-caption);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--plum);
  display: block;
  margin-bottom: 0.35rem;
}
.entry-name {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1.2;
  display: block;
  margin-bottom: 0.35rem;
}
.entry-name.human { font-family: var(--f-sans); }
.entry-blurb {
  font-size: var(--t-body);
  color: var(--caption);
  margin: 0;
}

/* project detail hero shot */
.hero-shot {
  width: 100%;
  border: 1.5px solid var(--rule);
  margin: 0 0 2rem;
}

/* diary entry date stamp */
.datestamp {
  font-family: var(--f-mono);
  font-size: var(--t-caption);
  letter-spacing: 0.1em;
  color: var(--plum);
  margin: 0 0 0.75rem;
}

/* =============================================================
   404 — not an error screen. the one path that does not exist,
   so finding it is an achievement rather than a dead end.
   ============================================================= */

#notfound {
  min-height: 100vh;
  background: var(--void);
  color: var(--green);
  font-family: var(--f-mono);
  display: flex;
  flex-direction: column;
}
#notfound .column { text-align: center; padding-top: 2.5rem; }

/* The transcript at the top stays flush left — a shell prompt that is
   centred is not a shell prompt — but it is held in the same measure as
   the console below it so the two share an edge. */
.nf-shell {
  text-align: left;
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--green);
  max-width: 34rem;
  margin: 0 auto 3rem;
}
.nf-shell .err { color: var(--signal); }


.nf-title {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: clamp(2rem, 7vw, 3.25rem);
  letter-spacing: 0.06em;
  line-height: 1.1;
  color: var(--green);
  margin: 0 0 1rem;
}
.nf-body {
  font-family: var(--f-sans);
  color: var(--signal);
  max-width: 34rem;
  margin: 0 auto 2.5rem;
}
/* Centred: once the prompt is spent there is no column of text left to
   align an answer against, and the page is symmetrical either side of the
   drawing. Left-aligned it read as a stray fragment. */
.nf-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
}

/* Pulled clear of the console line above it. At the old spacing the border
   sat close enough to the last line of log to look like part of it. */
#after { margin-top: 2.25rem; }

.nf-links a {
  font-family: var(--f-mono);
  font-size: var(--t-ui);
  border: 1.5px solid var(--green);
  color: var(--green);
  padding: 0.75rem 1.5rem;   /* the frame needs air around the words */
  text-decoration: none;
  transition: background 0.15s, color 0.15s;
}
.nf-links a:hover,
.nf-links a:focus-visible { background: var(--green); color: var(--void); }


/* =============================================================
   PLAIN WORDS — every diary entry carries one. The terminal layer
   assumes the reader is technical; the paper layer must not.
   ============================================================= */

.plain {
  border-left: 3px solid var(--purple);
  background: rgba(142, 107, 194, 0.06);
  padding: 1.1rem 1.25rem;
  margin: 2rem 0;
}
.plain-label {
  font-family: var(--f-mono);
  font-size: var(--t-caption);
  letter-spacing: 0.08em;
  color: var(--plum);
  display: block;
  margin-bottom: 0.5rem;
}
.plain-label::before { content: "$ "; color: var(--purple); }
.plain p {
  font-size: 0.95rem;
  line-height: 1.6;
  margin: 0 0 0.75rem;
}
.plain p:last-child { margin-bottom: 0; }


/* -------------------------------------------------------------
   404 art: a laptop with a coffee mug tipped into it. Drawn as inline
   SVG rather than an image file so it uses the site's own tokens
   and stays sharp at any size, like the rest of the pixel work.
   ------------------------------------------------------------- */

.nf-art {
  width: min(320px, 70vw);
  height: auto;
  display: block;
  margin: 0 auto 2rem;
}
.nf-art .case    { fill: var(--signal); }
.nf-art .case-dk { fill: #8B887C; }
.nf-art .glass   { fill: #050705; }
.nf-art .glitch  { fill: var(--green); opacity: 0.55; }
.nf-art .key     { fill: #8B887C; }
.nf-art .mug     { fill: var(--signal); }   /* ceramic */
.nf-art .mug-dk  { fill: #8B887C; }         /* rim and base in shadow */
.nf-art .coffee  { fill: var(--plum); }     /* dark liquid, still on-palette */
.nf-art .smoke rect { fill: var(--signal); animation: rise 3.5s ease-in-out infinite; }
.nf-art .smoke rect:nth-child(2) { animation-delay: 0.4s; }
.nf-art .smoke rect:nth-child(3) { animation-delay: 0.9s; }
.nf-art .smoke rect:nth-child(4) { animation-delay: 1.3s; }
@keyframes rise {
  0%   { transform: translateY(2px); opacity: 0; }
  40%  { opacity: 0.45; }
  100% { transform: translateY(-4px); opacity: 0; }
}

/* the machine is dead: once repaired or accepted, it stops smoking */
#notfound.settled .nf-art .smoke { display: none; }
#notfound.settled .nf-art .glitch { opacity: 0; }

/* a coffee mug for a cursor, on this page only. leaving restores it. */
#notfound {
  cursor: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 14 14' shape-rendering='crispEdges'>\
<rect x='2' y='4' width='8' height='9' fill='%23D9D9D9'/>\
<rect x='2' y='4' width='8' height='2' fill='%235F3D9E'/>\
<rect x='10' y='6' width='2' height='1' fill='%23D9D9D9'/>\
<rect x='11' y='7' width='1' height='3' fill='%23D9D9D9'/>\
<rect x='10' y='10' width='2' height='1' fill='%23D9D9D9'/>\
</svg>") 4 2, auto;
}

/* Centred, not a left-aligned box that happens to sit in the middle.
   The log lines here are short answers to a centred prompt, under a
   centred drawing and a centred title; flush left they hung off the
   edge of a column nothing else on the page uses. */
.nf-console {
  font-family: var(--f-mono);
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--green);
  text-align: center;
  max-width: 34rem;
  margin: 0 auto;
  min-height: 1.6em;
  white-space: pre-wrap;
}

/* A wrong key is a note, not an alarm — until the third one, which the
   log itself escalates. */
.nf-console.warn { color: var(--signal); }

.nf-prompt {
  font-family: var(--f-mono);
  font-size: clamp(1.1rem, 3.5vw, 1.4rem);
  color: var(--green);
  margin: 1.5rem 0 2.5rem;
}
.nf-prompt.spent { display: none; }
.nf-prompt .slash { opacity: 0.6; }
.nf-prompt .caret {
  margin-left: 0.4em;
  animation: blink 1s steps(1) infinite;
}

.key-btn {
  font-family: var(--f-mono);
  font-size: inherit;
  background: none;
  border: none;
  color: var(--green);
  padding: 0.1em 0.35em;
  cursor: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.key-btn:hover, .key-btn:focus-visible {
  background: var(--green);
  color: var(--void);
  text-decoration: none;
}

/* on mobile the two answers become real tap targets */
@media (max-width: 640px) {
  .nf-prompt { display: flex; flex-direction: column; gap: 0.75rem; align-items: stretch; }
  .nf-prompt .slash, .nf-prompt .caret { display: none; }
  .key-btn { border: 1.5px solid var(--green); padding: 0.9rem; text-decoration: none; }
}


/* -------------------------------------------------------------
   CAROUSEL WAVE. Driven frame by frame from script, not by a CSS
   keyframe, for one reason: it has to answer to scrolling. A
   keyframe animation can only be running or removed, and removing
   one snaps the cards back to zero. The script keeps a single
   amplitude value that rises while the visitor scrolls and eases
   back to nothing when they stop, so the row settles instead of
   stopping dead. There is no transform transition here on purpose:
   it would fight the per-frame updates.
   ------------------------------------------------------------- */

/* -------------------------------------------------------------
   ??? over a thumbnail. The same curiosity beat as the buttons,
   scaled up and bent into an arch so it reads as a set, not a
   repeat. Decorative only: nothing is lost without hover.
   ------------------------------------------------------------- */

.thumb-wrap { position: relative; display: block; }
.thumb-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0);
  transition: background 0.2s ease;
  pointer-events: none;
}
.thumb-wrap:hover::after { background: rgba(0, 0, 0, 0.5); }

.arch {
  position: absolute;
  left: 50%;
  top: 34%;
  width: 0;
  pointer-events: none;
}
.arch i {
  position: absolute;
  left: 0;
  font-family: var(--f-mono);
  font-weight: 700;
  font-style: normal;
  font-size: 2rem;
  line-height: 1;
  color: var(--green);
  margin-left: -0.32em;
  opacity: 0;
  transition: opacity 0.16s ease,
              transform 0.34s cubic-bezier(0.34, 1.62, 0.64, 1);
}
.arch i:nth-child(1) { --x: -3.1rem; --y:  0.7rem; --r: -26deg; transition-delay: 0s; }
.arch i:nth-child(2) { --x:  0rem;   --y: -0.5rem; --r:   0deg; transition-delay: 0.05s; }
.arch i:nth-child(3) { --x:  3.1rem; --y:  0.7rem; --r:  26deg; transition-delay: 0.1s; }

.arch i { transform: translate(var(--x), calc(var(--y) + 1.1rem)) rotate(var(--r)); }

/* one trigger list, so the beat is identical everywhere it appears */
.thumb-wrap:hover .arch i,
.term-btn:hover .arch i,
.term-btn:focus-visible .arch i,
.entry a:hover .arch i,
.entry a:focus-visible .arch i {
  opacity: 1;
  transform: translate(var(--x), var(--y)) rotate(var(--r));
}

/* On a button the arch has to clear the frame completely. At -1.15rem it
   still overlapped the top edge, and the button fills with green on hover,
   so green question marks landed on green and vanished. It read as a
   colour problem and it was a position problem: lifted clear of the frame,
   the arch sits on black and green is legible again. The terminal layer
   keeps its one accent. */
.term-btn { overflow: visible; }
.term-btn .arch { top: -2.4rem; }
.term-btn .arch i { font-size: 1.6rem; }
.term-btn .arch i:nth-child(1) { --x: -2.3rem; --y: 0.5rem; }
.term-btn .arch i:nth-child(2) { --x:  0rem;   --y: -0.4rem; }
.term-btn .arch i:nth-child(3) { --x:  2.3rem; --y: 0.5rem; }

/* On a listing row the arch sits on the title line, not over the middle
   of the row: centred absolutely, it landed on top of the blurb text.
   It is paper-coloured because the row inverts to purple on hover. */
.entry-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.35rem;
}
.entry-head .entry-name { margin-bottom: 0; }

.entry .arch {
  position: relative;
  left: auto;
  top: auto;
  display: inline-block;
  flex: 0 0 auto;
  width: 4.2rem;
  height: 1.3em;
}
.entry .arch i { left: 50%; color: var(--paper); font-size: 1.35rem; }
.entry .arch i:nth-child(1) { --x: -1.45rem; --y: 0.2rem; }
.entry .arch i:nth-child(2) { --x:  0rem;    --y: -0.35rem; }
.entry .arch i:nth-child(3) { --x:  1.45rem; --y: 0.2rem; }

/* -------------------------------------------------------------
   GATES — one panel per index page, image and text alternating
   sides so the pair reads as a spread rather than a list.
   ------------------------------------------------------------- */

.gates {
  width: min(1100px, 100% - 3rem);
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  margin: 3rem auto 0;
}

.gate {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 1.5rem;
  align-items: center;
  border: 1.5px solid var(--green);
  padding: 1.25rem;
  text-decoration: none;
  color: var(--green);
  transition: background 0.15s, color 0.15s;
}
/* The two project boxes both put the image first; the diary box mirrors
   them. Two the same way then one the other reads as "these, and also
   that", which is what the three of them are. */
.gate.flip .gate-img  { order: 2; }
.gate.flip .gate-text { order: 1; }

.gate:hover, .gate:focus-visible { background: var(--green); color: var(--void); }

.gate-img { display: block; }
.gate-img img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border: 1.5px solid currentColor;
}

.gate-text { display: block; }
.gate-name {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 1.5rem;
  display: block;
  margin-bottom: 0.5rem;
}
.gate-desc {
  font-family: var(--f-sans);
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--signal);
  display: block;
  margin-bottom: 0.75rem;
}
.gate:hover .gate-desc, .gate:focus-visible .gate-desc { color: var(--void); }
.gate-go {
  font-family: var(--f-mono);
  font-size: var(--t-ui);
  display: block;
}

/* -------------------------------------------------------------
   HOME ACTIONS — about and contact, side by side, carrying the
   button design that whoami used to carry alone.
   ------------------------------------------------------------- */

.home-actions {
  display: flex;
  gap: 1.25rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 3rem;
}

@media (max-width: 640px) {
  .gate { grid-template-columns: 1fr; }
  .gate.flip .gate-img  { order: 0; }
  .gate.flip .gate-text { order: 0; }
  .home-actions { flex-direction: column; width: min(22rem, 100%); margin-inline: auto; }
  .home-actions .term-btn { width: 100%; }
}

/* =============================================================
   MOBILE — no breakpoint for layout. these are tap-target rules.
   ============================================================= */

@media (max-width: 640px) {
  .sheet { width: 100%; height: 100vh; border-radius: 0; }
  .card { flex: 0 0 82vw; }
}

/* =============================================================
   REDUCED MOTION — everything stops. content is identical.
   ============================================================= */

@media (prefers-reduced-motion: reduce) {
  #intro,
  .boot-screen,
  #welcome-text .letter,
  #scroll-hint,
  #scroll-hint .chev,
  .overlay .backdrop,
  .sheet,
  .card,
  .arch i,
  .thumb-wrap::after,
  .gate,
  .sheet-close,
  .wordmark::after,
  .term-btn::after {
    transition: none !important;
    animation: none !important;
  }
  .carousel { scroll-behavior: auto; }
  .nf-art .smoke rect,
  .nf-prompt .caret { animation: none !important; }
  .term-btn:hover::after,
  .term-btn:focus-visible::after { transform: translateY(-6px); }
}