/* ============================================================
   hannahahn.world — What's in my bag
   ------------------------------------------------------------
   1. Tokens
   2. Reset + base type
   3. Corner menu
   4. Home: bag
   5. Home: spill stage + selection frames
   6. Home: legend
   7. Page transitions
   8. Interior pages
   9. Receipt (resume)
   10. Story spine (What I've Grown)
   11. Responsive
   ============================================================ */

/* ---------- 1. Tokens ---------- */
:root {
  --paper:      #fdfdfb;
  --ink:        #16160f;
  --ink-soft:   #55554c;
  --ink-faint:  #8d8d83;
  --rule:       #dcdcd1;

  /* selection frames */
  --frame:      #3355e6;   /* standalone items */
  --frame-eco:  #16794f;   /* the 3a–3d sustainability group */

  /* palette */
  --sage:   #b7cfa6;
  --sage-d: #8fae7c;
  --lilac:  #c5b8e3;
  --butter: #f4e4a6;
  --blush:  #f4c2cd;
  --clay:   #dd8f6b;
  --sky:    #a9d2e3;
  --gold:   #c9a24a;
  --rose:   #c8465c;

  --sans:  "Space Grotesk", "Helvetica Neue", Arial, sans-serif;
  --mono:  "DM Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --ease-out:  cubic-bezier(.22, .68, 0, 1);
  --ease-land: cubic-bezier(.34, 1.42, .64, 1);
}

/* ---------- 2. Reset + base type ---------- */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 350;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; }

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

h1, h2, h3 { font-weight: 400; margin: 0; }

::selection { background: var(--butter); color: var(--ink); }

.u-mono { font-family: var(--mono); }

.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: var(--paper);
  padding: .6em 1em; z-index: 200; font: 500 13px/1 var(--mono);
}
.skip:focus { left: 12px; top: 12px; }

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

/* ---------- 3. Corner menu ---------- */
/* A small stack of plain text tabs (no borders, no slant), tucked in the top-right. */
.corner {
  position: fixed;
  top: 20px; right: 20px;
  z-index: 90;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.corner__toggle {
  display: none;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  background: var(--paper);
  border: 0;
  color: var(--ink);
  padding: 7px 11px;
  cursor: pointer;
}

.corner__list {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  gap: 4px;
  justify-items: end;
}

.corner__list a {
  display: block;
  background: var(--paper);
  border: 0;
  padding: 5px 13px 4px;
  text-decoration: none;
  transition: background .22s, color .22s;
}

.corner__list a:hover,
.corner__list a:focus-visible,
.corner__list a[aria-current="page"] {
  background: var(--ink);
  color: var(--paper);
}

/* wordmark, bottom-left. It sits on a small paper chip so it never collides
   with body text on narrower screens, where there is no gutter to hide in. */
.mark {
  position: fixed;
  left: 14px; bottom: 12px;
  background: var(--paper);
  padding: 7px 9px 6px;
  z-index: 90;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
  white-space: nowrap;
}
.mark b { color: var(--ink); font-weight: 500; }

/* ============================================================
   4. HOME — the bag
   ============================================================ */

/* The landing page runs on its own two-colour palette, sampled from
   references/colorpalette2.jpeg: a pale sage ground and a deep indigo.
   Scoped to .home so the interior pages keep the white paper. */
.home {
  /* A plain, "default Windows page" look: pure white, black body text,
     classic hyperlink blue for every outline and accent. */
  --paper:     #ffffff;
  --ink:       #000000;
  --ink-soft:  #000000;
  --ink-faint: #808080;
  --rule:      #c0c0c0;
  --frame:     #0000ee;
  /* the 3a-3d group shares the same blue; it stays legible as a family
     through its dashed inner rule, sprout badge and dashed legend number */
  --frame-eco: #0000ee;

  min-height: 100vh;
}

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

/* Total typography override for this page only: Arial everywhere, no
   exceptions, so it reads as a plain default page rather than a designed
   one. !important is deliberate here — this rule exists specifically to
   beat every other font-family declaration in the file regardless of its
   own specificity or how the stylesheet evolves later. */
body.home, body.home * {
  font-family: Arial, Helvetica, sans-serif !important;
}

.stageWrap {
  position: relative;
  min-height: 100vh;
  display: grid;
  grid-template-rows: 1fr auto;
}

/* --- opening view --- */
.opening {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 40;
  transition: opacity .5s ease;
}

.opening__inner {
  display: grid;
  justify-items: center;
  gap: 4px;
  padding: 0 18px;
  /* --bag-lift is written by home.js so the script and the stylesheet agree
     on exactly how far the bag rises; the fallback is only for no-JS. */
  transition: transform .64s cubic-bezier(.5, .02, .3, 1);
}
.is-opening .opening__inner {
  transform: translateY(var(--bag-lift, -26vh));
}

/* now a real <button> — a second, equally valid way to open the bag,
   alongside clicking the bag image itself — so it needs a button reset */
.opening__kicker {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0 0 clamp(6px, 1vw, 12px);
  font: inherit;
  color: var(--ink);
  line-height: 1;
  cursor: pointer;
}

/* The two labels share one grid cell, so swapping them shifts nothing;
   the cell is as wide as the wider of the two. */
.kicker__swap {
  display: inline-grid;
  align-items: center;
  justify-items: center;
}
.kicker__swap > span {
  grid-area: 1 / 1;
  transition: opacity .3s ease, transform .42s var(--ease-out);
}

/* Much smaller than the bag it sits above: a quiet caption, not a headline.
   20px / 13px are the resting (desktop) sizes; see the clamp()s for the
   responsive range down to a 375px phone. */
.kicker__ko {
  font-family: "Noto Sans KR", var(--sans);
  font-weight: 500;
  font-size: clamp(16px, 1.8vw, 20px);
  letter-spacing: 0;
  opacity: 1;
}
.kicker__en {
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(11px, 1.15vw, 13px);
  letter-spacing: 0;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(10px);
}

/* revealed by hovering either the words or the bag — wired up in home.js */
.is-hinting .kicker__ko { opacity: 0; transform: translateY(-10px); }
.is-hinting .kicker__en { opacity: 1; transform: none; }

/* the heading is kept for screen readers and search engines, not shown */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* the bag button */
.bagBtn {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: block;
  width: clamp(210px, 30vw, 330px);
  transform-origin: 50% 50%;
  transition: transform .62s cubic-bezier(.62, .02, .3, 1);
}
.bagBtn:hover   { transform: scale(1.03) rotate(-1.5deg); }
.bagBtn:active  { transform: scale(.99); }

/* The bag tips right over, then leaves by the side of the page.
   No squash, and no fade — it stays fully opaque the whole way out. */
.bagImg { width: 100%; height: auto; }

.is-opening .bagBtn { transform: rotate(180deg); }

/* opening view exits */
.is-opening .opening__kicker { opacity: 0; transform: translateY(-8px); transition: opacity .4s ease, transform .4s ease; }

/* the bag docks to the bottom-left as a "pack it up" control */
.bagDock {
  position: fixed;
  left: 16px; bottom: 46px;
  z-index: 60;
  width: 62px;
  background: none; border: 0; padding: 0;
  cursor: pointer;
  opacity: 0;
  transform: translateY(14px) scale(.9);
  line-height: 0;
  pointer-events: none;
  transition: opacity .45s ease .9s, transform .55s var(--ease-land) .9s;
}
.is-spilled .bagDock {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.bagDock img { width: 100%; height: auto; }
.bagDock:hover { transform: rotate(-4deg) scale(1.05); }
.bagDock__label {
  display: block;
  margin-top: 4px;
  font: 500 9px/1.2 var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}

/* ============================================================
   5. HOME — spill stage + selection frames
   ============================================================ */
.stage {
  position: relative;
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  aspect-ratio: 100 / 52;
  padding: 0 16px;
  opacity: 0;
  pointer-events: none;
}
.is-spilled .stage { opacity: 1; pointer-events: auto; }

/* one spilled object */
.item {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  display: block;
  text-decoration: none;
  z-index: 2;
  /* centred on its own point, then given its resting tilt */
  transform: translate(-50%, -50%) rotate(var(--r));
  transition: z-index 0s;
  /* draggable like a desktop window: grab-hand cursor, no native touch
     scroll/text-select fighting the drag, no long-press callout on mobile */
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.item:hover:not(.item--static), .item:focus-visible:not(.item--static) { z-index: 20; }
/* while actively being dragged: front of the stack, hand closes, no
   transition lag on the frame's own hover styling fighting the pointer */
.item.is-dragging {
  cursor: grabbing;
  transition: none;
}
.item.is-dragging .art { transition: none; }

/* the three nested layers that make the arc */
.item__x,
.item__y { display: block; }

/* Sideways travel LAGS the vertical drop, so each object clears the mouth
   before it fans out — that lag is what makes it read as pouring rather than
   flying. */
.item__x {
  transform: translateX(var(--dx, 0px));
  transition: transform .74s cubic-bezier(.25, .64, .3, 1) calc(var(--d, 0ms) + 90ms);
}
/* the drop: accelerates away from the mouth, overshoots its resting place a
   touch, then settles back — weight, not a glide */
.item__y {
  transform: translateY(var(--dy, 0px));
  transition: transform .86s cubic-bezier(.36, 1.22, .5, 1) var(--d, 0ms);
}
/* Objects tumble out at near full size. They are NOT faded in: opacity flips
   in a single frame at the moment the object leaves the mouth, which keeps it
   solid all the way down without leaving it visibly stacked at the opening
   while it waits its turn in the stagger. */
.item__spin {
  display: block;
  transform: rotate(var(--r0, -40deg)) scale(.72);
  opacity: 0;
  transition:
    transform .9s cubic-bezier(.32, 1.14, .5, 1) var(--d, 0ms),
    opacity   10ms linear                       var(--d, 0ms);
}
.is-spilled .item__x,
.is-spilled .item__y { transform: none; }
.is-spilled .item__spin { transform: none; opacity: 1; }

/* --- the selection frame ---
   The frame fades in AFTER its object has landed; the artwork itself is
   visible the whole way out, so the flight reads as a real object. */
.frame {
  position: relative;
  display: block;
  padding: clamp(8px, 9%, 22px);
}

.frame::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid var(--fc, var(--frame));
  opacity: 0;
  transition: opacity .3s ease calc(var(--d, 0ms) + 840ms), border-color .2s, background .2s;
}
.is-spilled .frame::before { opacity: 1; }
.item:hover:not(.item--static) .frame::before,
.item:focus-visible:not(.item--static) .frame::before {
  border-color: var(--fc, var(--frame));
  background: color-mix(in srgb, var(--fc, var(--frame)) 6%, transparent);
}
/* the eco group gets a dashed inner rule — the subtle family resemblance */
.item--eco .frame::after {
  content: "";
  position: absolute;
  inset: 3px;
  border: 1px dashed color-mix(in srgb, var(--frame-eco) 45%, transparent);
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s ease calc(var(--d, 0ms) + 900ms);
}
.is-spilled .item--eco .frame::after { opacity: 1; }

/* eight square selection handles */
.frame i {
  position: absolute;
  width: 6px; height: 6px;
  background: var(--paper);
  border: 1px solid var(--fc, var(--frame));
  transform: scale(0);
  transition: transform .26s var(--ease-land) calc(var(--d, 0ms) + 900ms + var(--hd, 0ms));
}
.is-spilled .frame i { transform: scale(1); }

.frame i:nth-of-type(1) { left: -3px;  top: -3px;                      --hd:  0ms; }
.frame i:nth-of-type(2) { left: 50%;   top: -3px;  margin-left: -3px;  --hd: 26ms; }
.frame i:nth-of-type(3) { right: -3px; top: -3px;                      --hd: 52ms; }
.frame i:nth-of-type(4) { right: -3px; top: 50%;   margin-top: -3px;   --hd: 78ms; }
.frame i:nth-of-type(5) { right: -3px; bottom: -3px;                   --hd:104ms; }
.frame i:nth-of-type(6) { left: 50%;   bottom: -3px; margin-left: -3px;--hd:130ms; }
.frame i:nth-of-type(7) { left: -3px;  bottom: -3px;                   --hd:156ms; }
.frame i:nth-of-type(8) { left: -3px;  top: 50%;   margin-top: -3px;   --hd:182ms; }

.item:hover:not(.item--static) .frame i,
.item:focus-visible:not(.item--static) .frame i { background: var(--fc, var(--frame)); }

/* the artwork itself */
.art {
  position: relative;
  display: block;
  transition: transform .34s var(--ease-land), filter .34s ease;
  filter: drop-shadow(0 6px 10px rgba(20,20,10,.075));
}
.item:hover:not(.item--static) .art,
.item:focus-visible:not(.item--static) .art {
  transform: translateY(-5px) scale(1.035);
  filter: drop-shadow(0 12px 18px rgba(20,20,10,.14));
}
.art svg,
.art img { width: 100%; height: auto; overflow: visible; }

/* number badge, top-left of the frame */
.badge {
  position: absolute;
  left: -1px; top: -1px;
  transform: translateY(-100%);
  background: var(--fc, var(--frame));
  color: #fff;
  font: 500 10px/1 var(--mono);
  letter-spacing: .04em;
  padding: 3px 5px 3px 4px;
  display: flex;
  align-items: center;
  gap: 3px;
  white-space: nowrap;
  opacity: 0;
  transition: opacity .28s ease calc(var(--d, 0ms) + 960ms);
}
.is-spilled .badge { opacity: 1; }

/* the sprout tick that marks the 3a–3d family */
.badge svg { width: 7px; height: 7px; opacity: .92; }

/* hover name label */
.tag {
  position: absolute;
  left: 50%; bottom: -6px;
  transform: translate(-50%, 100%) translateY(-4px);
  background: var(--ink);
  color: var(--paper);
  font: 400 11px/1 var(--mono);
  letter-spacing: .02em;
  padding: 5px 8px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s ease, transform .22s var(--ease-out);
  z-index: 30;
}
.item:hover:not(.item--static) .tag,
.item:focus-visible:not(.item--static) .tag {
  opacity: 1;
  transform: translate(-50%, 100%) translateY(0);
}

/* reciprocal highlight: hovering a legend row lights the object.
   :not(.item--static) means items 4–6 (no destination page) never react to
   hover or focus at all — the class exclusion above already covers native
   :hover/:focus-visible; this covers the JS-driven .is-lit path too. */
.item.is-lit:not(.item--static) .frame::before { background: color-mix(in srgb, var(--fc, var(--frame)) 9%, transparent); }
.item.is-lit:not(.item--static) .frame i { background: var(--fc, var(--frame)); }
.item.is-lit:not(.item--static) .art { transform: translateY(-5px) scale(1.035); filter: drop-shadow(0 12px 18px rgba(20,20,10,.14)); }
.item.is-lit:not(.item--static) .tag { opacity: 1; transform: translate(-50%, 100%) translateY(0); }

/* ============================================================
   6. HOME — the scattered legend
   ============================================================ */
.legend {
  position: relative;
  width: 100%;
  max-width: 1060px;
  /* pulled up so the list sits against (and partly over) the lowest objects */
  margin: -120px auto 0;
  padding: 4px 22px 92px;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .6s ease 1.65s, transform .6s var(--ease-out) 1.65s;
}
.is-spilled .legend { opacity: 1; transform: none; }
.legend a, .legend [data-hobby] { pointer-events: auto; }
.legend [data-hobby] { cursor: pointer; }


/* a scattered field, not a list — each row is placed at its own x/y,
   like the captions thrown around references/site2.jpeg rather than
   read top-to-bottom */
.legend__list {
  list-style: none;
  position: relative;
  margin: 0;
  padding: 0;
  height: 250px;
}

.legend__list li {
  position: absolute;
  left: var(--lx, 0);
  top: var(--ly, 0);
  rotate: var(--lr, 0deg);
  width: max-content;
  max-width: min(78vw, 340px);
  margin: 0;
}

.legend__link {
  display: inline-flex;
  align-items: baseline;
  gap: .5em;
  text-decoration: none;
  font-size: clamp(15px, 1.5vw, 19px);
  line-height: 1.3;
  padding: 2px 0;
}

.legend__n {
  font-family: var(--mono);
  font-size: .72em;
  color: var(--ink-faint);
  min-width: 2.1em;
  transition: color .15s;
}

.legend__label { transition: color .15s; }

/* the only hover effect anywhere in the legend: text turns the accent
   colour. No size change, no motion, no swapped-in text — and none of
   this applies to the four-through-six rows (is-static): they don't open
   anything, so hovering them does nothing at all. */
.legend__list li:not(.is-static) .legend__link:hover .legend__n,
.legend__list li:not(.is-static) .legend__link:focus-visible .legend__n,
.legend__list li:not(.is-static) .legend__link:hover .legend__label,
.legend__list li:not(.is-static) .legend__link:focus-visible .legend__label {
  color: var(--fc, var(--frame));
}

/* eco rows carry the same dashed cue as their frames */
.legend__list li.is-eco .legend__n {
  border-bottom: 1px dashed color-mix(in srgb, var(--frame-eco) 55%, transparent);
}

.legend__note {
  margin: 30px 0 0;
  padding-left: 3%;
  max-width: 34ch;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-faint);
  rotate: -.5deg;
}
.legend__note b { color: var(--ink-soft); font-weight: 500; }

/* ============================================================
   7. Page transitions
   ============================================================ */
/* leaving home: the chosen object steps forward, the rest recede */
.is-leaving .item { transition: opacity .3s ease, transform .42s var(--ease-out); }
.is-leaving .item:not(.is-chosen) { opacity: 0; transform: translate(-50%, -50%) rotate(var(--r)) scale(.9); }
.is-leaving .item.is-chosen .art  { transform: scale(1.14); }
.is-leaving .item.is-chosen .frame::before,
.is-leaving .item.is-chosen .frame i,
.is-leaving .item.is-chosen .badge,
.is-leaving .item.is-chosen .tag { opacity: 0; transition: opacity .2s ease; }
.is-leaving .legend,
.is-leaving .bagDock,
.is-leaving .corner { opacity: 0; transition: opacity .28s ease; }

/* arriving on any page */
.enter > * {
  animation: enterUp .62s var(--ease-out) both;
}
@keyframes enterUp {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================
   8. Interior pages
   ============================================================ */
.page {
  max-width: 1120px;
  margin: 0 auto;
  padding: 92px 22px 120px;
}

.back {
  display: inline-flex;
  align-items: center;
  gap: .55em;
  font: 400 11px/1 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  margin-bottom: 46px;
}
.back span { transition: transform .24s var(--ease-out); display: inline-block; }
.back:hover span { transform: translateX(-5px); }
.back:hover { color: var(--ink); }

.prose { max-width: 62ch; }
.prose p { margin: 0 0 1.15em; }
.prose a { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }

.eyebrow {
  font: 400 11px/1 var(--mono);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

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


/* ============================================================
   9. Receipt (resume)
   ============================================================ */
.receipt {
  position: relative;
  max-width: 460px;
  margin: 0 auto;
  color: #2a3fc4;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.62;
  padding: 34px 30px 40px;
  rotate: -.6deg;
  /* the slightly-crumpled paper look: fine fiber grain (tiled), a handful
     of soft creases (each a light/dark edge pair, like a fold catching the
     light unevenly), a gentle vignette toward two corners, on a white base */
  background:
    url("../assets/paper-grain.png") 0 0 / 96px 96px repeat,
    linear-gradient(97deg,  transparent 13%, rgba(20,16,10,.06) 13.6%, rgba(255,255,255,.22) 14.1%, transparent 15.2%),
    linear-gradient(114deg, transparent 34%, rgba(255,255,255,.18) 34.6%, rgba(20,16,10,.05) 35.3%, transparent 36.4%),
    linear-gradient(83deg,  transparent 58%, rgba(20,16,10,.045) 58.6%, rgba(255,255,255,.2) 59.2%, transparent 60.3%),
    linear-gradient(101deg, transparent 79%, rgba(255,255,255,.16) 79.6%, rgba(20,16,10,.04) 80.3%, transparent 81.5%),
    radial-gradient(130% 85% at 26% 6%, rgba(0,0,0,.03), transparent 55%),
    radial-gradient(120% 90% at 88% 97%, rgba(0,0,0,.025), transparent 55%),
    #fff;
  /* a few pixels of irregular displacement (defined once, near the top of
     the page) so the paper reads as slightly uneven rather than perfectly
     flat — see the note by the <filter> itself for why the scale is small */
  filter: url(#paperWarp);
}
/* torn edges, top and bottom */
.receipt::before,
.receipt::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 9px;
  background:
    linear-gradient(-45deg, var(--paper) 50%, transparent 0) 0 0 / 11px 11px repeat-x,
    linear-gradient( 45deg, var(--paper) 50%, transparent 0) 0 0 / 11px 11px repeat-x;
}
.receipt::before { top: 0; transform: scaleY(-1); }
.receipt::after  { bottom: 0; }

.receipt__brand {
  text-align: center;
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -.01em;
  margin-bottom: 1px;
}
.receipt__role {
  text-align: center;
  font-size: 11px;
  letter-spacing: .05em;
  margin-bottom: 14px;
}
.receipt__meta {
  text-align: center;
  font-size: 10.5px;
  line-height: 1.7;
  margin-bottom: 4px;
}
.receipt__meta a { text-decoration: none; border-bottom: 1px solid currentColor; }

.receipt hr {
  border: 0;
  border-top: 1px dashed currentColor;
  margin: 12px 0;
  opacity: .8;
}

.receipt h3 {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .02em;
  margin: 16px 0 7px;
}

.receipt__line {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: baseline;
}
.receipt__line + .receipt__sub { margin-top: 0; }
.receipt__line strong { font-weight: 500; }
.receipt__line .yr { font-size: 11px; white-space: nowrap; opacity: .85; }
.receipt__sub {
  font-size: 11px;
  padding-left: 12px;
  margin: 0 0 9px;
  opacity: .9;
}
.receipt__sub li { margin-bottom: 2px; }
.receipt ul { margin: 0; padding-left: 14px; }

.receipt__total {
  display: grid;
  grid-template-columns: 1fr auto;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .04em;
  margin-top: 4px;
}
.receipt__stars {
  text-align: center;
  font-size: 11px;
  margin: 14px 0 4px;
}
.receipt__barcode {
  display: flex;
  gap: 1.5px;
  justify-content: center;
  height: 34px;
  margin: 16px 0 6px;
  align-items: flex-end;
}
.receipt__barcode i {
  display: block;
  width: var(--bw, 2px);
  height: 100%;
  background: currentColor;
}
.receipt__no {
  text-align: center;
  font-size: 9.5px;
  letter-spacing: .22em;
}

/* The receipt is the whole page now: one centred column, the PDF links
   sitting quietly underneath rather than beside it in a sidebar. */
.resumeLayout {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(28px, 5vw, 48px);
  padding-top: clamp(24px, 5vw, 56px);
}
.resumeAside {
  max-width: 460px;
  text-align: center;
}
.resumeAside p {
  color: var(--ink-soft);
  font: 15px/1.5 Arial, Helvetica, sans-serif;
  font-style: normal;
}
.resumeAside a { color: #0000ee; text-decoration: none; }
.resumeAside a:hover, .resumeAside a:focus-visible { text-decoration: underline; }

/* ============================================================
   10. Story sections — What I've Grown
   ============================================================ */
.story { position: relative; }

.chapter {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 96px) minmax(0, 1fr);
  gap: 0 20px;
  padding: 0 0 clamp(64px, 9vw, 116px) 0;
  scroll-margin-top: 84px;
}
.chapter:last-of-type { padding-bottom: 46px; }

.chapter__rail { position: relative; }

.chapter h2 {
  font-family: Arial, Helvetica, sans-serif;
  font-size: clamp(18px, 2vw, 24px);
  line-height: 1.2;
  letter-spacing: -.01em;
  max-width: 34ch;
  margin: -.1em 0 14px;
}

/* the years: body-sized, black, sitting right beside the section title */
.chapter__meta {
  font-size: inherit;
  line-height: 1.5;
  color: var(--ink);
  margin: 0 0 6px;
}
/* in the rail the meta stacks; below the breakpoint it runs inline */
.chapter__meta span { display: block; }
.chapter__meta span + span { color: color-mix(in srgb, var(--ink-faint) 82%, transparent); }

.chapter__body { max-width: 62ch; }
.chapter__body p { margin: 0 0 1.1em; }

.figure { margin: 30px 0 0; max-width: 260px; }
.figure img { width: 100%; height: auto; border: 1px solid var(--rule); }

.closing {
  margin-left: 0;
  padding-top: 40px;
  border-top: 1px solid var(--rule);
  max-width: 62ch;
}

/* ---- About page bits ---- */
.aboutLayout {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
  gap: clamp(30px, 6vw, 72px);
  align-items: start;
}
/* framed like a spilled object */
.framed { position: relative; padding: 10px; }
.framed::before { content: ""; position: absolute; inset: 0; border: 1px solid var(--fc, var(--frame)); }
.framed i {
  position: absolute; width: 6px; height: 6px;
  background: var(--paper); border: 1px solid var(--fc, var(--frame));
}
.framed i:nth-of-type(1) { left: -3px; top: -3px; }
.framed i:nth-of-type(2) { right: -3px; top: -3px; }
.framed i:nth-of-type(3) { right: -3px; bottom: -3px; }
.framed i:nth-of-type(4) { left: -3px; bottom: -3px; }

.facts { list-style: none; margin: 0; padding: 0; }
.facts li {
  display: grid;
  grid-template-columns: 8.5em 1fr;
  gap: 14px;
  padding: 11px 0;
  border-top: 1px solid var(--rule);
  font-size: 15px;
}
.facts li:last-child { border-bottom: 1px solid var(--rule); }
.facts dt, .facts b {
  font: 400 11px/1.5 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ---- 35mm-slide photogrid ----
   Each .slide35 is a square cell the same shape as assets/slide-mount.png's
   canvas (1940x1940). The photo/video sits UNDER the mount image, positioned
   with the mount's window rect as a percentage of that full square canvas —
   left/top/width/height below are (card padding + window offset) / canvas
   size, not the window-vs-card fractions documented in the README. Reused
   across all 12 cells so the frame image is only downloaded once.
   Sized down and pinned to the right of its column (references/35mmslides.jpg
   is a tight-packed binder page, not a full-bleed gallery) with a tight gap
   between mounts, matching that reference. */
.photogrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  width: 100%;
  max-width: 420px;
  margin-left: auto;
}
.slide35 { position: relative; aspect-ratio: 1 / 1; }
.slide35__photo,
.slide35__video {
  position: absolute;
  left: 17.273%;
  top: 26.364%;
  width: 65.455%;
  height: 43.636%;
  object-fit: cover;
}
.slide35__video { filter: grayscale(1) contrast(1.05) brightness(.95); }
.slide35__mount {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ---- Contact page bits ---- */
.contactGrid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
  gap: clamp(30px, 6vw, 70px);
  align-items: start;
}
.contactList { list-style: none; margin: 0; padding: 0; }
.contactList li { border-top: 1px solid var(--rule); }
.contactList li:last-child { border-bottom: 1px solid var(--rule); }
.contactList a,
.contactList span {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 2px;
  text-decoration: none;
  font-family: Arial, Helvetica, sans-serif;
  font-size: clamp(20px, 2.6vw, 30px);
  transition: padding-left .26s var(--ease-out), color .2s;
}
.contactList a:hover { padding-left: 12px; }
.contactList em {
  font: 400 11px/1 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-style: normal;
  white-space: nowrap;
}

/* ============================================================
   11. Responsive
   ============================================================ */
@media (max-width: 900px) {
  .contactGrid { grid-template-columns: minmax(0, 1fr); }
  .aboutLayout { grid-template-columns: minmax(0, 1fr); }
  .photogrid { max-width: none; margin-left: 0; }
}

@media (max-width: 760px) {
  body { font-size: 15.5px; }

  .corner { top: 14px; right: 14px; }
  .corner__toggle { display: block; }
  .corner__list {
    display: grid;
    gap: 4px;
    margin-top: 6px;
  }
  .corner[data-open="false"] .corner__list { display: none; }

  .mark { left: 14px; bottom: 12px; font-size: 10px; }

  .bagBtn { width: min(62vw, 250px); }

  /* the mobile stage is much taller: seven objects need the room, and it
     starts below the corner menu so nothing sits under it */
  .stage {
    aspect-ratio: 100 / 330;
    padding: 0 10px;
    margin-top: 54px;
  }

  /* no hover on touch: every object keeps its label on screen */
  .tag {
    opacity: 1;
    transform: translate(-50%, 100%) translateY(0);
    font-size: 9.5px;
    padding: 3px 5px;
    transition-delay: calc(var(--d, 0ms) + 1040ms);
  }
  .item .tag { transition: opacity .3s ease calc(var(--d, 0ms) + 1040ms); }
  .stage:not(.is-ready) .tag { opacity: 0; }

  .legend { padding-bottom: 76px; margin-top: 0; }
  /* True 2D scatter only really works with a wide desktop canvas to spread
     across; at phone width it would just overlap. So on a phone the list
     goes back to a plain top-to-bottom stack, keeping only a mild left
     nudge (borrowed from the desktop --lx, scaled way down) for a bit of
     the same hand-placed feel without the collision risk. */
  .legend__list {
    height: auto;
  }
  .legend__list li {
    position: relative;
    left: calc(var(--lx, 0%) * .3);
    top: auto;
    margin: 14px 0 0;
    max-width: 82vw;
  }
  .legend__list li:first-child { margin-top: 0; }

  /* on a phone the dock moves to the top-left, clear of the legend */
  .bagDock { width: 36px; left: 12px; top: 10px; bottom: auto; }
  .is-spilled .bagDock { transform: none; }
  /* the icon carries it on a phone; the caption would collide with badge 1 */
  .bagDock__label { display: none; }
  .mark { display: none; }

  .page { padding: 74px 16px 96px; }
  .back { margin-bottom: 30px; }

  .receipt { max-width: 100%; padding: 30px 20px 36px; font-size: 12px; }
  .chapter { grid-template-columns: minmax(0, 1fr); }
  .chapter__meta span { display: inline; }
  .chapter__meta span + span::before { content: " · "; }
  .facts li { grid-template-columns: minmax(0, 1fr); gap: 3px; }
  .photogrid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 420px) {
  .stage { aspect-ratio: 100 / 352; }
  .frame i { width: 5px; height: 5px; }
}

/* ---------- Reduced motion: show the laid-out result, no flight ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .item__x, .item__y, .item__spin,
  .frame::before, .frame::after, .frame i, .badge, .legend, .bagDock, .art {
    transition-duration: .01ms !important;
    transition-delay: 0ms !important;
    animation: none !important;
  }
  .enter > * { animation: none; }
}

/* ============================================================
   12. State glue — bag flies to the dock, packing up, instant open
   ============================================================ */

/* once the objects are out, the bag leaves by the left-hand side — it
   accelerates away rather than fading, so it reads as leaving, not vanishing.
   (Flip the sign to send it out the right instead.) */
.opening {
  transform-origin: 50% 50%;
  transition: transform .72s cubic-bezier(.55, 0, .82, .3) .12s;
}
.is-spilled .opening {
  pointer-events: none;
  transform: translateX(-135vw);
}

/* packing up: everything retracts together, no stagger, no long waits */
.is-packing .item__x,
.is-packing .item__y,
.is-packing .item__spin,
.is-packing .frame::before,
.is-packing .frame::after,
.is-packing .frame i,
.is-packing .badge,
.is-packing .tag,
.is-packing .legend,
.is-packing .bagDock {
  transition-delay: 0ms !important;
  transition-duration: .34s !important;
}
.is-packing .opening { transition-delay: 0s; transition-timing-function: var(--ease-out); }

/* restoring the open state on a return visit — no flight, just there */
.is-instant .item__x,
.is-instant .item__y,
.is-instant .item__spin,
.is-instant .frame::before,
.is-instant .frame::after,
.is-instant .frame i,
.is-instant .badge,
.is-instant .legend,
.is-instant .bagDock,
.is-instant .opening {
  transition: none !important;
}


/* What I've Grown and About: every bit of text on these pages is Arial. */
body.grown, body.grown *,
body.about, body.about * { font-family: Arial, Helvetica, sans-serif !important; }

/* ---- About: contact row (spaced off from the list, no rules) ---- */
.facts--bare { margin-top: 34px; }
.facts.facts--bare li { border: 0; }
.facts--bare a { color: inherit; text-decoration: none; }
.facts--bare a:hover, .facts--bare a:focus-visible { color: #0000ee; }

/* ---- About: "Currently" terms that open a scatter of photos ----
   A .hobby is a real <button>, styled as plain text. js/about.js adds
   .is-live only when that hobby has images; without it the term never
   reacts, same as the plain-text terms beside it. */
.hobby {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: default;
}
.hobby.is-live { cursor: pointer; }
.hobby.is-live:hover,
.hobby.is-live:focus-visible,
.hobby.is-live[aria-pressed="true"] { color: #0000ee; }

.hobbyLayer {
  position: absolute;
  left: 0; top: 0;
  width: 100%; height: 0;
  z-index: 60;
  pointer-events: none;
}
/* same selection frame as the objects tipped out of the bag, but with the
   border sitting directly on the image (no white gap) */
.hobbyPic {
  position: absolute;
  margin: 0;
  border: 1px solid #0000ee;
  line-height: 0;
  cursor: grab;
  pointer-events: auto;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.hobbyPic.is-dragging { cursor: grabbing; }
.hobbyPic img { display: block; width: 100%; height: auto; pointer-events: none; }
.hobbyPic i {
  position: absolute;
  width: 6px; height: 6px;
  background: #fff;
  border: 1px solid #0000ee;
}
.hobbyPic i:nth-of-type(1) { left: -4px;  top: -4px; }
.hobbyPic i:nth-of-type(2) { left: 50%;   top: -4px;  margin-left: -4px; }
.hobbyPic i:nth-of-type(3) { right: -4px; top: -4px; }
.hobbyPic i:nth-of-type(4) { right: -4px; top: 50%;   margin-top: -4px; }
.hobbyPic i:nth-of-type(5) { right: -4px; bottom: -4px; }
.hobbyPic i:nth-of-type(6) { left: 50%;   bottom: -4px; margin-left: -4px; }
.hobbyPic i:nth-of-type(7) { left: -4px;  bottom: -4px; }
.hobbyPic i:nth-of-type(8) { left: -4px;  top: 50%;   margin-top: -4px; }

/* the file name, laid over the photo itself */
.hobbyPic figcaption {
  position: absolute;
  left: 6px; right: 6px; bottom: 5px;
  font: 700 12px/1.15 Arial, Helvetica, sans-serif;
  text-align: left;
  color: #0000ee;
  pointer-events: none;
  overflow-wrap: anywhere;
}

/* "Close all images": plain blue text pinned bottom-right while photos are out */
.hobbyClose {
  position: fixed;
  right: 20px; bottom: 16px;
  z-index: 95;
  appearance: none;
  background: none;
  border: 0;
  padding: 4px 0;
  font-size: 14px;
  color: #0000ee;
  cursor: pointer;
}
.hobbyClose:hover, .hobbyClose:focus-visible { text-decoration: underline; }
.hobbyClose[hidden] { display: none; }
