/* heyiam.dk: one stylesheet for the whole site.

   The system, in short (reusable on any page or site):
   - Type: the system font (SF Pro on a Mac), one size (13.5px / 1.62). Two styles only:
       prominent = ink, weight 500   (names, links, the current nav item, questions)
       subtle    = ink-2, weight 400 (everything else)
     Two deliberate exceptions: platform badges (10.5px) and the legal line (11.5px).
   - Colour lives in the work. The chrome is ink, two greys, hairlines and soft fills;
     the only accent is the availability dot.
   - Space: --gutter on the sides, --edge at the top and bottom of the page.
   - Pieces: tags (.badge, soft fill), outline button (.go), underlined text links,
     hairline dividers, the nav with its fill line, the strip, the app list, the footer.
   Order below: tokens, base, shared pieces, then the page top to bottom,
   then the desktop roll, grid view, phone and motion. */

/* tokens ----------------------------------------------------------- */
:root {
  color-scheme: light;
  --bg: #fff;
  --ink: #151515;
  --ink-2: #858582;        /* subtle text, quiet on purpose (3.7:1, about Apple's secondary label) */
  --ink-3: #8c8c89;        /* strokes and hover underlines only, never text */
  --line: rgb(0 0 0 / .08);
  --line-2: rgb(0 0 0 / .18);
  --tile: #f3f3f2;         /* empty media frames */
  --soft: #f4f4f3;         /* tag fill */
  --soft-2: #ececea;       /* tag fill on hover */

  --available: #2f7cf6;    /* availability dot: data-status = available | short | busy */
  --short: #f2bd00;
  --busy: #8c8c89;

  --gutter: clamp(40px, 3.6vw, 64px);   /* sides */
  --edge: clamp(32px, 3vw, 52px);       /* top and bottom of the page */
  --radius: 10px;
  --strip-max: 640px;      /* tallest the work strip gets on big screens; the band centres it */
  --sans: system-ui, -apple-system, sans-serif;   /* the system font, no web fonts: SF Pro on Apple devices */
  --ease: cubic-bezier(.2, .7, .2, 1);
}
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --bg: #0e0e0e; --ink: #ececea; --ink-2: #73736f; --ink-3: #7b7b78;
    --line: rgb(255 255 255 / .09); --line-2: rgb(255 255 255 / .22);
    --tile: #1a1a19; --soft: #191918; --soft-2: #222221;
    --short: #f5c518;
  }
}

/* base ------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink-2);
  font: 400 13.5px/1.62 var(--sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}
img, video, svg { display: block; }
p, h1, h2, h3, ul, ol { margin: 0; }
h1, h2, h3 { font-size: inherit; line-height: inherit; }
ul, ol { padding: 0; list-style: none; }
a { color: var(--ink); text-decoration: none; }
a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; text-decoration-color: var(--ink-3); }
button { font: inherit; color: inherit; }
:focus-visible { outline: 1.5px solid var(--ink); outline-offset: 3px; border-radius: 4px; }
::selection { background: var(--ink); color: var(--bg); }
[hidden] { display: none !important; }
/* without JS: posters instead of empty players, and no controls that do nothing */
html:not(.js) .frame video, html:not(.js) .meter { display: none; }
h1:focus:not(:focus-visible), .section:focus:not(:focus-visible) { outline: none; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip { position: fixed; z-index: 50; left: var(--gutter); top: 10px; padding: 6px 10px; border-radius: 6px; background: var(--ink); color: var(--bg); transform: translateY(-200%); }
.skip:focus { transform: none; }
.nw { white-space: nowrap; }
.m-br { display: none; }

/* shared pieces ---------------------------------------------------- */
/* text link: prominent, with a quiet underline that firms up on hover */
h1 a, .lede a, .links a {
  font-weight: 500;
  text-decoration: underline;
  text-decoration-color: var(--line-2);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color .2s;
}
h1 a:hover, .lede a:hover, .links a:hover { text-decoration-color: currentColor; }

/* tag: a soft pill (Product Hunt, ratings, pre-release ...) */
.badge {
  display: inline-block;
  padding: 5px 10px;
  border-radius: 99px;
  background: var(--soft);
  font: 400 13.5px/1 var(--sans);
  color: var(--ink-2);
  white-space: nowrap;
}
a.badge { transition: color .2s, background-color .2s; }
a.badge:hover { color: var(--ink); background: var(--soft-2); text-decoration: none; }
.pills { display: flex; flex-wrap: wrap; gap: 6px; }

/* outline button: old work */
.go {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 14px;
  border: 1px solid var(--line-2);
  border-radius: 99px;
  font-weight: 500;
  color: var(--ink);
  transition: border-color .2s, background-color .2s;
}
.go:hover { border-color: var(--ink-3); text-decoration: none; }
.go span { display: inline-block; transition: transform .4s var(--ease); }
.go:hover span { transform: translate(2px, -2px); }

/* availability: a dot that pings, and a word */
.status { display: flex; align-items: center; gap: 8px; margin: 0; color: var(--ink-2); }
.dot { position: relative; width: 7px; height: 7px; border-radius: 50%; background: var(--short); flex: none; }
.dot::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: inherit; animation: ping 2.4s var(--ease) infinite; }
[data-status="available"] .dot { background: var(--available); }
[data-status="busy"] .dot { background: var(--busy); }
[data-status="busy"] .dot::after { animation: none; }
@keyframes ping { 0% { scale: 1; opacity: .55; } 70%, 100% { scale: 3.2; opacity: 0; } }

/* nav: top right, the sections --------------------------------------- */
.controls {
  position: fixed;
  z-index: 20;
  top: calc(var(--edge) - 3.5px);
  right: calc(var(--gutter) - 6px);
  display: flex;
  align-items: center;
  line-height: 1;
}
.controls a { position: relative; padding: 7px 6px; color: var(--ink-2); transition: color .2s; }
.controls a:hover { color: var(--ink); text-decoration: none; }
.controls a[aria-current] { color: var(--ink); font-weight: 500; }
/* the fill line under the current word grows as you move through its section */
.controls .fill {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(var(--p, 0));
  transform-origin: left;
  pointer-events: none;
}

/* logo: DK, top left, on the nav's line. It stays out of the way while the intro is on screen
   and fades in once the intro has scrolled off (.is-gone, set by site.js) */
.mark {
  position: fixed;
  z-index: 20;
  top: calc(var(--edge) - 3.5px);
  left: calc(var(--gutter) - 6px);
  padding: 7px 6px;
  line-height: 1;
  font-weight: 500;
  color: var(--ink);
  opacity: 0;
  filter: blur(4px);
  translate: 0 4px;
  visibility: hidden;
  transition: opacity .3s var(--ease), filter .3s var(--ease), translate .3s var(--ease), visibility 0s .3s;
}
.mark:hover { text-decoration: none; }
.is-gone .mark {
  opacity: 1;
  filter: none;
  translate: 0 0;
  visibility: visible;
  transition: opacity .6s var(--ease), filter .6s var(--ease), translate .6s var(--ease), visibility 0s;
}

/* intro, top left ---------------------------------------------------- */
.stage { position: relative; display: flex; flex-direction: column; padding: var(--edge) var(--gutter) calc(var(--edge) * .75); }
.corner { position: relative; z-index: 6; max-width: 384px; }
h1 { font-weight: 500; letter-spacing: -.004em; color: var(--ink); margin-bottom: 4px; }
.lede { text-wrap: pretty; }
/* the asterisk: black and white, turning (and turning faster with the roll) */
.ast { display: inline-block; width: .62em; height: .62em; margin: 0 .04em 0 .06em; vertical-align: .42em; color: var(--ink); }
.ast svg {
  width: 100%;
  height: 100%;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.9;
  stroke-linecap: square;
  rotate: var(--spin, 0deg);
  animation: turn 3s linear infinite;
}
@keyframes turn { to { transform: rotate(360deg); } }

/* the strip of work -------------------------------------------------- */
.reel { position: relative; margin: 0 calc(var(--gutter) * -1); }
.strip {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 6px var(--gutter) 10px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  scrollbar-width: none;
  --hm: clamp(240px, 100svh - 440px, 420px);
}
.strip::-webkit-scrollbar { display: none; }
.shot {
  container-type: inline-size;
  flex: none;
  width: min(80vw, var(--hm) * var(--s) * var(--r));
  scroll-snap-align: center;
  margin-top: calc((var(--y) + 1) * 9px);
}
.shot > a { display: block; color: inherit; text-decoration: none; -webkit-user-drag: none; }
.frame {
  position: relative;
  display: block;
  aspect-ratio: var(--r);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--tile);
  isolation: isolate;
  transform: translateZ(0);
}
.frame::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px var(--line); pointer-events: none; }
.frame img, .frame video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
  user-select: none;
  transition: transform .6s var(--ease);
}
.cap { display: flex; align-items: baseline; gap: 8px; padding-top: 9px; line-height: 1.4; }
.cap-main { flex: 1; min-width: 0; text-wrap: pretty; }
.no { margin-right: 6px; font-variant-numeric: tabular-nums; }
/* linked frames: the title underlines and the picture leans in; frames without a link stay still */
.shot > a[href]:hover .title,
.shot > a[href]:focus-visible .title { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.shot > a[href]:hover .frame img,
.shot > a[href]:hover .frame video { transform: scale(1.012); }
.shot > a:focus-visible { outline: none; }
.shot > a:focus-visible .frame { outline: 1.5px solid var(--ink); outline-offset: 4px; overflow: visible; }
.shot > a:focus-visible .frame img,
.shot > a:focus-visible .frame video { border-radius: var(--radius); }
/* end of the roll: old work, nothing else */
.fin { flex: none; align-self: center; display: flex; width: max-content; scroll-snap-align: center; }

/* progressive blur edges */
.edge {
  position: absolute;
  z-index: 2;
  top: -22px;
  bottom: -14px;
  width: 30px;
  --bk: .35;
  --dir: to left;
  pointer-events: none;
}
.edge-l { left: 0; --o: 0; }
.edge-r { right: 0; --dir: to right; }
.edge-t {
  position: fixed;
  z-index: 15;
  inset: 0 0 auto;
  width: auto;
  height: 76px;
  --dir: to top;
  --bk: .75;
  --o: 0;
  visibility: hidden;
  transition: visibility 0s .3s;
}
.edge-t > * { transition: opacity .3s; }
.is-float .edge-t { --o: 1; visibility: visible; transition: none; }
.edge > * { position: absolute; inset: 0; opacity: var(--o, 1); }
/* eight overlapping bands, each blurring twice as much as the last, so the blur ramps up on a curve.
   No tint on top: the work keeps its own colour right into the edge. */
.edge i {
  -webkit-backdrop-filter: blur(calc(var(--b) * var(--bk, 1)));
  backdrop-filter: blur(calc(var(--b) * var(--bk, 1)));
  --m: linear-gradient(var(--dir), #0000 calc(var(--n) * 12.5%), #000 calc((var(--n) + 1) * 12.5%) calc((var(--n) + 2) * 12.5%), #0000 calc((var(--n) + 3) * 12.5%));
  -webkit-mask: var(--m);
  mask: var(--m);
}
.edge i:nth-child(1) { --n: 0; --b: .5px; }
.edge i:nth-child(2) { --n: 1; --b: 1px; }
.edge i:nth-child(3) { --n: 2; --b: 2px; }
.edge i:nth-child(4) { --n: 3; --b: 4px; }
.edge i:nth-child(5) { --n: 4; --b: 8px; }
.edge i:nth-child(6) { --n: 5; --b: 16px; }
.edge i:nth-child(7) { --n: 6; --b: 24px; }
.edge i:nth-child(8) { --n: 7; --b: 36px; }

/* meter under the strip: count, hint and the strip / grid switch */
.meter { position: relative; z-index: 3; display: flex; align-items: center; gap: 14px; height: 28px; margin-top: 6px; line-height: 1; user-select: none; }
.count { font-variant-numeric: tabular-nums; min-width: 5ch; }
.count b { font-weight: 500; color: var(--ink); }
.hint { display: none; margin-left: auto; white-space: nowrap; transition: opacity .4s; }
.hint kbd { display: inline-block; min-width: 17px; margin-left: 3px; padding: 2px 3px 1px; border: 1px solid var(--line); border-radius: 4px; font: inherit; text-align: center; }
.is-moved .hint { opacity: 0; }
.views { display: flex; align-items: center; margin-left: auto; }
.views button { appearance: none; border: 0; background: none; padding: 7px 5px; color: var(--ink-2); cursor: pointer; transition: color .2s; }
.views button:hover { color: var(--ink); }
.views button:last-child { padding-right: 0; }
.views button[aria-pressed="true"] { color: var(--ink); font-weight: 500; }

/* after the strip: apps, hello, footer ------------------------------- */
/* at least a screen tall, so the pinned strip never peeks in at the end of the page */
.after { display: flex; flex-direction: column; min-height: 100vh; min-height: 100svh; }
.section { padding: clamp(96px, 16vh, 168px) var(--gutter) 0; }
/* heading and line read as one sentence: "apps" prominent, the rest subtle */
.section .label { display: inline; font-weight: 500; color: var(--ink); }
.section .label + .sub { display: inline; margin-left: .3em; text-wrap: pretty; }

/* apps: a plain list, icon first, a hairline between rows; the whole row links */
.applist { max-width: 630px; margin-top: 26px; }
.app { position: relative; display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 0 18px; padding: 24px 0; }
.app + .app::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: var(--line); transform: scaleY(.5); transform-origin: top; }
.app-body { max-width: 560px; }
.app-icon { width: 52px; height: 52px; border-radius: 12px; }
.app-icon--ring { box-shadow: 0 0 0 1px var(--line); }
.app h3 { font-weight: 500; color: var(--ink); }
.app h3 a { color: inherit; }
.app h3 a:hover, .app h3 a:focus-visible { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; text-decoration-color: currentColor; }
.stretch::after { content: ""; position: absolute; inset: 0; }
.stretch:focus-visible { outline: none; }
/* platforms: small outline badges, ink when released, subtle and dotted when coming */
.app .plat { display: inline-flex; flex-wrap: wrap; gap: 4px; margin-left: 8px; vertical-align: 1px; }
.app .plat > span { padding: 3px 5px 2px; border: 1px solid color-mix(in srgb, var(--ink) 40%, transparent); border-radius: 5px; font-size: 10.5px; font-weight: 500; line-height: 1; letter-spacing: .01em; color: var(--ink); }
.app .plat > .soon { --dots: color-mix(in srgb, var(--ink) 60%, transparent); position: relative; color: var(--ink-2); border-style: dotted; border-color: var(--dots); }
.app .plat > .soon.dots { border-color: transparent; }
.app .plat > .soon > svg { position: absolute; top: -1px; left: -1px; width: calc(100% + 2px); height: calc(100% + 2px); overflow: visible; pointer-events: none; }
.app .plat > .soon rect { fill: none; stroke: var(--dots); stroke-width: 1.2; stroke-linecap: round; }
.app .desc { text-wrap: pretty; }
.app .pills { margin-top: 12px; }
.app .pills a { position: relative; z-index: 1; } /* tag links sit above the row link */

/* hello: the question, then the links; pushed to the foot of the screen */
.outro { margin-top: auto; padding-bottom: clamp(72px, 12vh, 132px); }
.outro .say { max-width: 384px; text-wrap: pretty; }
.outro .links { margin-top: 4px; }
.outro .sep { margin: 0 10px 0 4px; } /* a little air before the comma, more after */

/* footer: availability left, the legal line right */
.colophon {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px 24px;
  padding: 0 var(--gutter) var(--edge);
}
.legal { margin-left: auto; font-size: 11.5px; }

/* 404 */
.lost .colophon { margin-top: auto; }
.lost h1 { font-weight: 400; color: var(--ink-2); } /* the line is subtle, the way back is prominent */

/* ------------------------------------------------------------------ */
/* desktop: pinned strip, vertical scroll drives the roll              */
/* ------------------------------------------------------------------ */
@media (min-width: 721px) and (min-height: 560px) and (hover: hover) and (pointer: fine) {
  .js [data-view="strip"] .stage {
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100svh;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    overflow: clip;
  }
  .js [data-view="strip"] .reel {
    container-type: size;
    overflow-x: hidden;
    overflow-x: clip;
    overflow-y: visible;
    cursor: grab;
  }
  .js [data-view="strip"] .reel .edge { top: -60px; bottom: -60px; --bk: 1; width: clamp(110px, 17vw, 300px); }
  .js [data-view="strip"] .reel.is-dragging { cursor: grabbing; }
  .js [data-view="strip"] .reel.is-dragging a { pointer-events: none; }
  .js [data-view="strip"] .strip {
    width: max-content;
    height: 100%;
    gap: 0;
    padding: 30px var(--gutter) 18px;
    overflow: visible;
    scroll-snap-type: none;
    will-change: transform;
  }
  .js [data-view="strip"] .strip .shot {
    /* frame height follows the band up to --strip-max (then it centres in the band), and never lets a
       landscape piece outgrow the screen */
    --mh: min(100cqh - 94px, 56cqw, var(--strip-max));
    --h: calc(var(--mh) * var(--s));
    width: calc(var(--h) * var(--r));
    margin-top: calc((100cqh - 94px - var(--mh)) / 2 + (var(--mh) - var(--h)) * (var(--y) + 1) / 2);
    margin-left: calc(var(--g) * var(--mh) / 85);
  }
  .js [data-view="strip"] .fin { margin-left: min(18cqh, 130px); }
  .js [data-view="strip"] .hint { display: block; }
  .js [data-view="strip"] .hint + .views { margin-left: 18px; }

  /* without JS, keep the native scroller but give it room */
  .strip { --hm: clamp(260px, 62vh, 520px); }
}

/* ------------------------------------------------------------------ */
/* grid view: a justified index sheet, real aspect ratios, in order    */
/* ------------------------------------------------------------------ */
[data-view="grid"] .meter { order: 1; margin: 30px 0 18px; }
[data-view="grid"] .reel { order: 2; }
[data-view="grid"] .strip {
  --row: clamp(200px, min(21vw, 150px + 10.5vw), 400px); /* same up to ~1440px, then keeps growing gently */
  flex-wrap: wrap;
  gap: clamp(30px, 1.8vw, 44px) clamp(20px, 1.2vw, 30px);
  padding: 0 var(--gutter) 20px;
  overflow: visible;
  scroll-snap-type: none;
}
[data-view="grid"] .strip::after { content: ""; flex: 10 1 0; }
[data-view="grid"] .strip .shot { flex: var(--r) 1 calc(var(--r) * var(--row)); width: auto; min-width: 0; margin-top: 0; }
[data-view="grid"] .fin,
[data-view="grid"] .reel .edge,
[data-view="grid"] .count,
[data-view="grid"] .hint { display: none; }

/* ------------------------------------------------------------------ */
/* phone                                                               */
/* ------------------------------------------------------------------ */
@media (max-width: 720px) {
  :root { --gutter: 22px; --edge: 26px; --radius: 9px; }
  .controls { top: 16px; }
  .controls a { padding: 12px 6px; }
  .mark { top: 16px; padding: 12px 6px; }
  .views button { padding: 11px 6px; }
  .stage { padding-top: 72px; } /* the intro starts under the nav */
  .corner { max-width: none; }
  .m-br { display: inline; }
  .reel { margin-top: 18px; }
  /* equal-ish area per frame on a phone, so portrait and landscape weigh the same */
  .shot { width: min(84vw, var(--q) * 250px, var(--r) * 270px); }
  .meter { margin-top: 8px; }
  .fin { align-self: flex-start; margin-top: 95px; }
  [data-view="grid"] .meter { margin: 22px 0 12px; }
  [data-view="grid"] .strip { --row: 96px; gap: 22px 10px; }
  [data-view="grid"] .strip .cap .no { display: none; }

  .section { padding-top: 88px; }
  .app { grid-template-columns: 44px minmax(0, 1fr); gap: 0 14px; padding: 20px 0; }
  .app-icon { width: 44px; height: 44px; border-radius: 10px; }
  .colophon { flex-direction: column; align-items: flex-start; }
  .legal { margin-left: 0; }
}

/* ------------------------------------------------------------------ */
/* motion                                                              */
/* ------------------------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  .js .frame { animation: develop 1s var(--ease) backwards; animation-delay: calc(var(--i, 0) * 90ms + 80ms); }
  .js .corner > * { animation: rise .9s var(--ease) backwards; }
  .js .corner > :nth-child(2) { animation-delay: 60ms; }
  .js .corner > :nth-child(3) { animation-delay: 120ms; }
  .js .meter, .js .controls { animation: rise .9s var(--ease) .5s backwards; }

  ::view-transition-group(*) { animation-duration: .6s; animation-timing-function: cubic-bezier(.3, .8, .2, 1); }
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: .35s; }
}
@keyframes develop { from { filter: blur(18px); opacity: 0; transform: scale(.97); } }
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; }
}
