/* ─── Ted Kwee-Bintoro — signed document ─────────────────────────────── */

@font-face {
  font-family: "Redaction";
  src: url("../fonts/Redaction-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: block;
}
@font-face {
  font-family: "Redaction";
  src: url("../fonts/Redaction-Italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: block;
}
@font-face {
  font-family: "Redaction";
  src: url("../fonts/Redaction-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: block;
}

:root {
  --paper: #faf6ec;
  --ink: #111110;
  --ink-soft: #3d3d3a;
  --hairline: #d8d6cd;
  --hws: 0.235;           /* handwriting display scale */
  --sn-col: 15.5rem;      /* sidenote column width */
  /* the sheet: fine tooth + sizing mottle + edge shading over warm paper */
  --paperbg:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='5' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.28  0 0 0 0 0.23  0 0 0 0 0.13  0 0 0 0.055 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='560' height='560'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.011' numOctaves='3' seed='9' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.45  0 0 0 0 0.36  0 0 0 0 0.18  0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='560' height='560' filter='url(%23m)'/%3E%3C/svg%3E"),
    radial-gradient(140% 110% at 50% 42%, rgba(0, 0, 0, 0) 58%, rgba(103, 78, 40, 0.075)),
    var(--paper);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  /* no mobile font boosting: inflated text overflows the viewport-fit page */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* nothing pans, ever — hidden tooltips overhang the viewport edge */
  overflow: hidden;
  overflow: clip;
}

html, body { height: 100%; }

body {
  height: 100dvh;
  overflow: hidden;
  display: grid;
  grid-template-rows: 1fr;
  background: var(--paperbg);
  color: var(--ink);
  font-family: "Redaction", "New Century Schoolbook", Georgia, serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

/* ─── signature ──────────────────────────────────────────────────────── */
/* two sibling SVGs: the lettering flies to the header when the signing
   ends; the face doodle drifts into the page background and fades */

/* at rest the signature IS the colophon's centre word: sized like the old
   masked span (the word image is 1059px wide, baseline 43px up), recolours
   with the hovered word via --hc (fills are currentColor), and collapses
   for the business swap. 3px of padding gives the grain room to breathe;
   the negative margins cancel it out of the layout. */
#sigwrap {
  display: block;
  width: calc(var(--hws) * 1059px);
  margin-bottom: calc(var(--hws) * -43px);  /* hang the descenders */
  overflow: hidden;
  /* width only — never opacity: the fixed-position background face lives
     inside this wrapper, and ancestor opacity would take it down too */
  transition: width 0.45s cubic-bezier(0.6, 0.15, 0.15, 1);
}
#sigwrap.flying { overflow: visible; }   /* let the FLIP leave the box */
#sigstage { display: flex; align-items: flex-start; }
/* the svgs keep their size while the wrapper collapses — the business
   swap must CROP the word from the right, not scale it away */
#sigstage svg { flex-shrink: 0; }

/* no flash of the finished name between first paint and the driver's
   dash setup at window load: hold the mask strokes dark until the pen
   moves (the driver's inline stroke-opacity takes over from here) */
body.intro #sigstage .ms { stroke-opacity: 0; }
#sig {
  display: block;
  width: calc(var(--hws) * var(--lw, 1084) * 1px);
  height: auto;
  color: var(--hc, var(--ink));
  overflow: visible;
  filter: url(#inkgrain);
  transition: color 0.25s ease;
}

/* the face: by default (signing over, or no JS) it IS the page background;
   JS keeps --facey on the essay block's vertical centre. It lives inside
   #page now, so it slips behind the text with a negative z-index. */
#sigface {
  position: fixed;
  left: 50%;
  top: var(--facey, 50%);
  transform: translate(-50%, -50%);
  width: min(78vmin, 660px);
  height: auto;
  color: var(--ink);
  opacity: 0.05;
  z-index: -1;
  pointer-events: none;
  overflow: visible;
  filter: url(#inkgrain);
}

/* intro takeover: the signing is the first (and only) thing on screen
   (visibility beats the hidden #page it sits inside) */
body.intro #sigwrap {
  --sigw: min(80vw, 730px);
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  width: auto;
  padding: 0 6vw;
  margin: 0;
  overflow: visible;
  visibility: visible;
  background: var(--paperbg);
}
body.intro #sig { width: calc(var(--sigw) * var(--lr)); }
body.intro #sigface {
  position: static;
  width: calc(var(--sigw) * var(--fr));
  margin-top: calc(var(--sigw) * var(--fyr));
  transform: none;
  opacity: 1;
  pointer-events: auto;
}

#sig.flipping { transition: transform 0.6s cubic-bezier(0.22, 0.75, 0.2, 1); }
#sigface.flipping {
  transition: transform 0.9s cubic-bezier(0.3, 0.6, 0.2, 1), opacity 0.9s ease;
}

/* ─── page ───────────────────────────────────────────────────────────── */

#page {
  position: relative;
  z-index: 1;
  display: grid;
  /* the colophon always keeps its row — a tall essay may overlap it, but
     can never push it off the bottom of the viewport */
  grid-template-rows: minmax(0, 1fr) auto;
  min-height: 0;
  padding: 0 16px;
}
body.intro #page { visibility: hidden; }

/* the entrance animates the essay and the word row only — the colophon
   itself must stay untransformed (opacity/transform there would swallow
   the fixed-position intro stage and the background face inside it) */
.essay, .words {
  transition: opacity 0.5s ease, transform 0.5s ease;
}
body.js.intro .essay, body.js.intro .words {
  opacity: 0;
  transform: translateY(14px);
}
body.js:not(.intro) .essay { transition-delay: 0.08s; }
body.js:not(.intro) .words { transition-delay: 0.2s; }

/* ─── essay, gwern-style ─────────────────────────────────────────────── */

.essay {
  position: relative;
  align-self: center;
  justify-self: center;
  width: min(50ch, 100%);
  font-size: clamp(16px, 1.55vh + 9px, 22px);
  line-height: 1.58;
}

.bio { text-align: justify; }

/* the opening phrase in full caps; the name in small caps */
.uc { text-transform: uppercase; letter-spacing: 0.035em; }
.sc { font-variant-caps: small-caps; letter-spacing: 0.035em; }

/* M spans line-1 cap top to line-2 baseline, flush with the copy block
   (Redaction: cap .70em, font box 1.2em; bio line 1.58em) */
.dropcap {
  float: left;
  font-weight: 700;
  font-size: 3.257em;
  line-height: 0.8;
  padding: 0.12em 0.07em 0 0;
  margin-left: 0.02em;
}

.lnk, #bio-close {
  color: inherit;
  text-decoration: none;
  background-image: linear-gradient(var(--ink-soft), var(--ink-soft));
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 96%;
  transition: background-size 0.25s ease;
}
.lnk:hover, #bio-close:hover, #bio-close:focus-visible { background-size: 0% 1px; background-position: 100% 96%; }

/* the closing line: underlined like a link; each pull backspaces the
   old sentence character by character, then types the new one in */
#bio-close {
  cursor: pointer;
}
#bio-close .ch { transition: opacity 12ms ease; }

.snref {
  text-decoration: none;
  color: var(--ink);
  font-weight: 700;
}
.snref sup { position: relative; font-size: 0.62em; letter-spacing: 0.02em; }
.snref:hover sup, .snref:focus-visible sup, .snref.open sup { text-decoration: underline; }

/* footnotes appear as tooltips, gwern-popup-fashion */
.snref .tip {
  position: absolute;
  bottom: calc(100% + 0.55em);
  left: 50%;
  transform: translate(-50%, 4px);
  width: max-content;
  max-width: min(30ch, 74vw);
  padding: 0.6em 0.85em 0.65em;
  background: var(--paper);
  border: 1px solid var(--ink);
  box-shadow: 3px 3px 0 rgba(17, 17, 16, 0.12);
  font-weight: 400;
  font-style: italic;
  font-variant: normal;   /* fn1 sits inside the small-caps name span */
  text-transform: none;
  font-size: clamp(12px, 0.55vw + 8px, 14px);
  letter-spacing: 0;
  line-height: 1.45;
  text-align: left;
  color: var(--ink-soft);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s;
  z-index: 5;
}
.snref .tip::after {          /* little tail */
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -4px;
  border: 4px solid transparent;
  border-top-color: var(--ink);
}
.snref:hover .tip, .snref:focus-visible .tip, .snref.open .tip {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition: opacity 0.22s ease, transform 0.22s ease;
}
/* keep the leftmost tooltip inside the column */
.snref .tip { --shift: 0px; transform: translate(calc(-50% + var(--shift)), 4px); }
.snref:hover .tip, .snref:focus-visible .tip, .snref.open .tip {
  transform: translate(calc(-50% + var(--shift)), 0);
}

/* ─── colophon: handwritten links ────────────────────────────────────── */

.colophon {
  justify-self: center;
  width: 100%;
  max-width: 880px;
  padding: 0 0 clamp(16px, 3.2vh, 34px);
  text-align: center;
}

.hw {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  min-height: calc(var(--hws) * 200px);
  margin-bottom: clamp(14px, 2.6vh, 26px);
  text-decoration: none;
}

.slot { display: flex; align-items: flex-end; }

/* the ink is a CSS mask over a paintable background, so the whole address
   can take the hovered word's colour (--hc, set by JS) */
.hw .c, .hw .f {
  display: block;
  height: calc(var(--hws) * var(--fh) * 1px);
  margin-bottom: calc(var(--hws) * var(--fb) * -1px); /* hang descenders below the shared baseline */
  background-color: var(--hc, var(--ink));
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: auto 100%;
  mask-size: auto 100%;
  filter: url(#inkgrain);   /* match the signature's distressed ink */
  pointer-events: none;
  user-select: none;
}

.pre .f[data-for~="instagram"] {
  -webkit-mask-image: url("../../images/handwriting/instagram.png");
  mask-image: url("../../images/handwriting/instagram.png");
}
.pre .f[data-for~="twitter"] {
  -webkit-mask-image: url("../../images/handwriting/xcom.png");
  mask-image: url("../../images/handwriting/xcom.png");
}
.pre .f[data-for~="linkedin"] {
  -webkit-mask-image: url("../../images/handwriting/linkedin.png");
  mask-image: url("../../images/handwriting/linkedin.png");
}
.pre .f[data-for~="personal"] {
  -webkit-mask-image: url("../../images/handwriting/ted-at.png");
  mask-image: url("../../images/handwriting/ted-at.png");
}
#mid-katy {
  -webkit-mask-image: url("../../images/handwriting/katytechnologies.png");
  mask-image: url("../../images/handwriting/katytechnologies.png");
}
.post .f[data-frag="period"] {
  -webkit-mask-image: url("../../images/handwriting/period.png");
  mask-image: url("../../images/handwriting/period.png");
}
.post .f[data-for~="substack"]:not([data-frag]) {
  -webkit-mask-image: url("../../images/handwriting/substack.png");
  mask-image: url("../../images/handwriting/substack.png");
}
.post .f[data-for~="personal"] {
  -webkit-mask-image: url("../../images/handwriting/dotcom.png");
  mask-image: url("../../images/handwriting/dotcom.png");
}

.hw .c {
  width: calc(var(--hws) * var(--fw) * 1px);
  -webkit-mask-position: left bottom;
  mask-position: left bottom;
  transition: width 0.45s cubic-bezier(0.6, 0.15, 0.15, 1), opacity 0.3s ease,
    background-color 0.25s ease;
}

/* business email swaps the centre word for katytechnologies; both emails
   share the same handwritten ted@ prefix */
.hw .c.swap { width: 0; opacity: 0; }
.hw[data-active="business"] #sigwrap { width: 0; }
.hw[data-active="business"] #mid-katy {
  width: calc(var(--hws) * var(--fw) * 1px);
  opacity: 1;
}

/* fragments reveal by growing from the centre word outward */
.hw .f {
  width: 0;
  opacity: 0;
  transition: width 0.45s cubic-bezier(0.6, 0.15, 0.15, 1), opacity 0.3s ease,
    background-color 0.25s ease;
}
.pre .f  { -webkit-mask-position: right bottom; mask-position: right bottom; }
.post .f { -webkit-mask-position: left bottom;  mask-position: left bottom; }

/* the name huddles closer to the slashes */
.slot.mid { transition: margin-left 0.45s cubic-bezier(0.6, 0.15, 0.15, 1); }
.hw[data-active="twitter"] .slot.mid { margin-left: calc(var(--hws) * -72px); }
.hw[data-active="instagram"] .slot.mid { margin-left: calc(var(--hws) * -48px); }

.hw[data-active="instagram"] .f[data-for~="instagram"],
.hw[data-active="twitter"]   .f[data-for~="twitter"],
.hw[data-active="linkedin"]  .f[data-for~="linkedin"],
.hw[data-active="substack"]  .f[data-for~="substack"],
.hw[data-active="personal"]  .f[data-for~="personal"],
.hw[data-active="business"]  .f[data-for~="business"] {
  width: calc(var(--hws) * var(--fw) * 1px);
  opacity: 1;
}

/* small breathing gap only around the period fragment */
.post .f[data-frag="period"] { margin-left: calc(var(--hws) * 14px); margin-right: calc(var(--hws) * 10px); }

/* ─── colophon: the words ────────────────────────────────────────────── */

.words {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: clamp(0.9rem, 2.3vw, 1.9rem);
  row-gap: 0.35rem;
  font-size: clamp(14px, 1.05vh + 8px, 17px);
  letter-spacing: 0.045em;
}

.word {
  color: var(--ink);
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 1px;
  background-position: 0 100%;
  transition: color 0.22s ease, background-size 0.3s ease;
}
.word:hover, .word:focus-visible, .word.on {
  color: var(--c);
  background-size: 100% 1px;
}
.word:focus-visible { outline: none; }

/* when the row must wrap, break it cleanly: the four networks on one
   line, both emails together on the next */
.words .brk { display: none; }
@media (max-width: 720px) {
  .words .brk { display: block; flex-basis: 100%; height: 0; }
}

/* ─── small viewports ────────────────────────────────────────────────── */

@media (max-width: 720px) {
  :root { --hws: 0.155; }
  .essay { font-size: clamp(14.5px, 1.2vh + 8px, 18px); }
}
@media (max-width: 430px) {
  :root { --hws: 0.125; }
}
/* short screens: shave the colophon's breathing room before anything
   can slip below the fold */
@media (max-height: 700px) {
  .hw { min-height: calc(var(--hws) * 170px); margin-bottom: 12px; }
  .colophon { padding-bottom: 12px; }
  .words { row-gap: 0.2rem; }
}
@media (max-height: 620px) {
  body.intro #sigwrap { --sigw: min(46vw, 365px); }
  .essay { font-size: clamp(13px, 1.1vh + 8px, 16px); }
}

/* ─── reduced motion ─────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .essay, .words, .hw .c, .hw .f, .slot.mid, .word, .lnk, #bio-close,
  #bio-close .ch, #sigwrap, #sig, #sig.flipping, #sigface.flipping {
    transition: none !important;
  }
}
