/* meesbeuk sky kit v2 — the shared design system for meesbeuk.com,
   transfer.meesbeuk.com and thumbnails.meesbeuk.com.
   Served from https://meesbeuk.com/kit/v2/kit.css. Everything is prefixed sk-.

   The world: one Dutch polder under a big old-master sky, photographed at
   different hours. Morning is meesbeuk.com, fog is transfer, dusk is
   thumbnails. Every plate is in sky/ at 800/1600/2880 wide, AVIF + WebP.

   Rules every component here follows:
     · colours, sizes and curves come from tokens.css, never a literal
     · lines are ink and 2px; shadows are hard offsets (the "pressed key")
     · only transform, opacity, filter and clip-path animate
     · ease-out for entering and responding, ease-in-out for travelling,
       never ease-in; UI stays under 300ms and exits faster than it enters
     · hover effects only exist on devices that can hover
     · reduced motion keeps fades and drops every movement (tokens.css) */

@import url('tokens.css');
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Caveat:wght@500;700&display=swap');

/* ------------------------------------------------------------------ base */

:where(.sk-reset) *,
:where(.sk-reset) *::before,
:where(.sk-reset) *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sk-body {
  background: var(--sk-bg);
  color: var(--sk-fg);
  font-family: var(--sk-font-sans);
  font-size: var(--sk-size-base);
  line-height: 1.55;
  letter-spacing: var(--sk-track-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-feature-settings: 'cv11', 'ss01', 'ss03';
}

.sk-body ::selection {
  background: var(--sk-ember-soft);
  color: var(--sk-ink);
}

:where(.sk-reset) :where(img, picture, video, svg) {
  display: block;
  max-width: 100%;
}

:where(.sk-reset) a {
  color: inherit;
}

:where(.sk-reset) :where(button, input, select, textarea) {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

:where(.sk-reset) :focus-visible {
  outline: 2px solid var(--sk-focus);
  outline-offset: 3px;
  border-radius: var(--sk-radius-sm);
}

.sk-wrap {
  width: min(100% - 32px, 1120px);
  margin-inline: auto;
}

.sk-wrap--narrow {
  width: min(100% - 32px, 720px);
  margin-inline: auto;
}

/* ------------------------------------------------------------------ type */

/* The headline voice: big, tight, two greys. Wrap the words that matter in
   <b> and the rest stays the quieter grey, the way a person stresses a
   sentence out loud. */
.sk-display {
  font-size: var(--sk-size-display);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: var(--sk-track-display);
  color: var(--sk-fg-3);
  text-wrap: balance;
}

.sk-display b,
.sk-display strong {
  color: var(--sk-fg);
  font-weight: 600;
}

.sk-title {
  font-size: clamp(28px, 3.6vw, var(--sk-size-3xl));
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: var(--sk-track-display);
  text-wrap: balance;
}

.sk-h3 {
  font-size: var(--sk-size-lg);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: var(--sk-track-title);
}

.sk-lede {
  font-size: var(--sk-size-md);
  line-height: 1.55;
  color: var(--sk-fg-2);
  text-wrap: pretty;
}

.sk-small {
  font-size: var(--sk-size-sm);
  color: var(--sk-fg-3);
}

.sk-mono {
  font-family: var(--sk-font-mono);
  font-size: 0.92em;
  letter-spacing: 0;
}

/* Marker handwriting, for annotations next to things ("new!", "try it").
   Never for anything the reader must read to use the page. */
.sk-hand {
  font-family: var(--sk-font-hand);
  font-weight: 700;
  font-size: var(--sk-size-xl);
  line-height: 1;
  letter-spacing: 0;
}

/* Inline photo pill inside a headline, like a word you can see. */
.sk-inline-photo {
  display: inline-block;
  width: 1.7em;
  height: 0.9em;
  border-radius: 0.2em;
  vertical-align: -0.06em;
  object-fit: cover;
  box-shadow: var(--sk-shadow-chip);
}

/* ------------------------------------------------------------------ pill */

/* The pressed key: ink border, hard shadow under it. Pressing sinks the key
   into its own shadow, which is the feedback. Hover lifts it a hair. */
.sk-pill {
  --_lift: 0px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sk-space-2);
  min-height: 44px;
  padding: 0 var(--sk-space-5);
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: var(--sk-radius-md);
  background: var(--sk-surface);
  color: var(--sk-fg);
  font-weight: 600;
  font-size: var(--sk-size-base);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--sk-shadow-press);
  transform: translateY(var(--_lift));
  transition:
    transform var(--sk-dur-press) var(--sk-ease-out),
    box-shadow var(--sk-dur-press) var(--sk-ease-out),
    background-color var(--sk-dur-fast) var(--sk-ease-hover);
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

.sk-pill svg,
.sk-pill img {
  width: 18px;
  height: 18px;
  flex: none;
}

@media (hover: hover) and (pointer: fine) {
  .sk-pill:hover {
    --_lift: var(--sk-lift);
    box-shadow: 0 6px 0 var(--sk-edge);
  }
}

.sk-pill:active {
  --_lift: 3px;
  box-shadow: var(--sk-shadow-press-down);
}

.sk-pill:disabled,
.sk-pill[aria-disabled='true'] {
  cursor: not-allowed;
  color: var(--sk-fg-4);
  --_lift: 3px;
  box-shadow: var(--sk-shadow-press-down);
}

/* A key that cannot be pressed yet reads as unlit, whatever its colour: grey
   text on a black key looks broken, not waiting. */
.sk-pill--ink:is(:disabled, [aria-disabled='true']),
.sk-pill--ember:is(:disabled, [aria-disabled='true']) {
  background: var(--sk-surface-3);
  border-color: var(--sk-edge-soft);
}

.sk-pill--ink {
  background: var(--sk-fg);
  color: var(--sk-bg);
}

.sk-pill--ember {
  background: var(--sk-accent);
  color: var(--sk-ink);
}

.sk-pill--sm {
  min-height: 34px;
  padding: 0 var(--sk-space-3);
  font-size: var(--sk-size-sm);
  border-radius: var(--sk-radius-sm);
}

.sk-pill--icon {
  width: 44px;
  padding: 0;
}

.sk-pill--sm.sk-pill--icon {
  width: 34px;
}

/* The big key: for the one call to action on a page, and for rows of brand
   keys under it. Icon-only big keys are wide, like a keyboard's space-ish
   keys, not square. */
.sk-pill--lg {
  min-height: 58px;
  padding: 0 var(--sk-space-8);
  border-radius: var(--sk-radius-lg);
  font-size: var(--sk-size-md);
  gap: var(--sk-space-3);
}

.sk-pill--lg svg,
.sk-pill--lg img {
  width: 28px;
  height: auto;
}

/* A doodle on a key is text-sized; on the big key it is dock-sized. */
.sk-pill .sk-doodle {
  width: var(--sk-doodle-sm);
}

.sk-pill--lg .sk-doodle {
  width: var(--sk-doodle-md);
}

.sk-pill--lg.sk-pill--icon {
  width: 96px;
  min-height: 64px;
  padding: 0;
}

.sk-pill.sk-pill--lg .sk-social {
  width: 32px;
}

@media (max-width: 640px) {
  .sk-pill--lg.sk-pill--icon {
    width: 80px;
    min-height: 56px;
  }

  .sk-pill.sk-pill--lg .sk-social {
    width: 28px;
  }
}

/* Toggle: a pill with aria-pressed="true" stays pressed down, sunk into its
   own shadow and lit with the accent, like a key that latched. */
.sk-pill[aria-pressed='true'] {
  --_lift: 3px;
  background: var(--sk-accent);
  color: var(--sk-ink);
  box-shadow: var(--sk-shadow-press-down);
}

@media (hover: hover) and (pointer: fine) {
  .sk-pill[aria-pressed='true']:hover {
    --_lift: 2px;
    box-shadow: 0 2px 0 var(--sk-edge);
  }
}

.sk-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sk-space-3);
  align-items: center;
}

/* ------------------------------------------------------------------ tag */

/* Small status pill: live dot + label. */
.sk-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--sk-space-2);
  padding: 4px 10px;
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface);
  font-size: var(--sk-size-sm);
  font-weight: 600;
  box-shadow: 0 3px 0 var(--sk-edge);
  white-space: nowrap;
}

.sk-tag i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--sk-go);
  font-style: normal;
}

.sk-tag--stop i { background: var(--sk-stop); }
.sk-tag--idle i { background: var(--sk-fg-4); }
.sk-tag--ember i { background: var(--sk-accent); }

/* Working: the dot breathes while something runs. Opacity only, so reduced
   motion keeps it (it is a status, not a movement). */
.sk-tag--busy i {
  background: var(--sk-accent);
  animation: sk-pulse var(--sk-dur-pulse) var(--sk-ease-in-out) infinite alternate;
}

@keyframes sk-pulse {
  to { opacity: 0.25; }
}

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

/* A white card drawn with a marker: the ink lines overshoot the corners like
   a quick sketch. ::before is the overshooting frame, ::after a second,
   slightly offset pass so the line reads as drawn rather than ruled. */
.sk-card {
  --_o: 12px;
  position: relative;
  background: var(--sk-surface);
  padding: var(--sk-space-5);
  isolation: isolate;
}

.sk-card::before,
.sk-card::after {
  content: '';
  position: absolute;
  inset: calc(var(--_o) * -1);
  pointer-events: none;
  z-index: -1;
  --_c: var(--sk-edge);
  background:
    linear-gradient(var(--_c), var(--_c)) 0 var(--_o) / 100% 2px no-repeat,
    linear-gradient(var(--_c), var(--_c)) 0 calc(100% - var(--_o)) / 100% 2.5px no-repeat,
    linear-gradient(var(--_c), var(--_c)) var(--_o) 0 / 2.5px 100% no-repeat,
    linear-gradient(var(--_c), var(--_c)) calc(100% - var(--_o)) 0 / 2px 100% no-repeat;
}

.sk-card::after {
  inset: calc(var(--_o) * -1 + 3px) calc(var(--_o) * -1 - 2px) calc(var(--_o) * -1 - 4px) calc(var(--_o) * -1 + 2px);
  opacity: 0.9;
  background:
    linear-gradient(var(--_c), var(--_c)) 6% var(--_o) / 70% 1.5px no-repeat,
    linear-gradient(var(--_c), var(--_c)) 40% calc(100% - var(--_o)) / 64% 3px no-repeat,
    linear-gradient(var(--_c), var(--_c)) var(--_o) 30% / 1.5px 60% no-repeat,
    linear-gradient(var(--_c), var(--_c)) calc(100% - var(--_o)) 80% / 3px 70% no-repeat;
}

/* the card sits on its own white so the sky behind never shows through the
   gap between the frame and the body */
.sk-card > * {
  position: relative;
}

.sk-card__media {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  background: var(--sk-surface-2);
}

.sk-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sk-space-3);
  margin-top: var(--sk-space-4);
}

.sk-card__body {
  margin-top: var(--sk-space-2);
  color: var(--sk-fg-2);
  text-wrap: pretty;
}

.sk-card__foot {
  margin-top: var(--sk-space-4);
}

/* Linked card: the whole card is the hit area, the arrow key answers it. */
a.sk-card {
  display: block;
  text-decoration: none;
  color: inherit;
}

.sk-arrow {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  background: var(--sk-fg);
  color: var(--sk-bg);
  box-shadow: 3px 3px 0 var(--sk-fg-4);
  transition: transform var(--sk-dur-fast) var(--sk-ease-out);
}

.sk-arrow svg {
  width: 16px;
  height: 16px;
}

@media (hover: hover) and (pointer: fine) {
  a.sk-card:hover .sk-arrow {
    transform: translate(2px, -2px);
  }
}

a.sk-card:active .sk-arrow {
  transform: scale(var(--sk-press-scale));
}

/* Plain panel for tools: rounded, soft edge, no sketch. */
.sk-panel {
  background: var(--sk-surface);
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: var(--sk-radius-lg);
  box-shadow: var(--sk-shadow-card);
  padding: var(--sk-space-6);
}

.sk-panel--soft {
  border-color: var(--sk-edge-soft);
  box-shadow: var(--sk-shadow-float);
}

/* ------------------------------------------------------------------ chip */

/* A tilted photo, the kind you pin to a wall. --r sets the tilt. On hover it
   straightens and lifts; it follows a spring because it is decorative. */
.sk-chip {
  --r: -6deg;
  display: block;
  width: 72px;
  aspect-ratio: 1;
  object-fit: cover;
  border: 3px solid #fff;
  border-radius: 14px;
  box-shadow: var(--sk-shadow-chip);
  transform: rotate(var(--r));
  transition: transform var(--sk-dur-slow) var(--sk-spring);
}

@media (hover: hover) and (pointer: fine) {
  .sk-chip:hover {
    transform: rotate(0deg) scale(1.06);
  }
}

/* ------------------------------------------------------------------ doodle */

/* Every hand-drawn doodle in doodles/ is a marker stroke on the same 48
   grid with the same stroke width, used as a mask, so it takes currentColor
   like text does and two doodles at one size always share a line weight.
   <i class="sk-doodle" style="--d: url(.../smiley.svg)"></i>, or the named
   helpers below. Size them with the four tiers, not ad hoc widths:
   --sm inline in keys and text, --md the dock and nav, --lg decorative (the
   default), --xl hero and footer features. */
.sk-doodle {
  --d: none;
  display: inline-block;
  width: var(--sk-doodle-lg);
  aspect-ratio: 1;
  background: currentColor;
  -webkit-mask: var(--d) center / contain no-repeat;
  mask: var(--d) center / contain no-repeat;
  flex: none;
}

/* Drawn in: the doodle wipes on left to right once it scrolls into view,
   like the pen going across. kit.js adds .is-in. */
.sk-doodle[data-sk-draw] {
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--sk-dur-draw) var(--sk-ease-in-out);
  transition-delay: calc(var(--i, 0) * var(--sk-stagger));
}

.sk-doodle[data-sk-draw].is-in {
  clip-path: inset(0 0 0 0);
}

.sk-doodle.sk-doodle--sm { width: var(--sk-doodle-sm); }
.sk-doodle.sk-doodle--md { width: var(--sk-doodle-md); }
.sk-doodle.sk-doodle--lg { width: var(--sk-doodle-lg); }
.sk-doodle.sk-doodle--xl { width: var(--sk-doodle-xl); }

.sk-doodle--smiley { --d: url('doodles/smiley.svg'); }
.sk-doodle--arrow-curl-down { --d: url('doodles/arrow-curl-down.svg'); }
.sk-doodle--arrow-right { --d: url('doodles/arrow-right.svg'); }
.sk-doodle--arrow-up { --d: url('doodles/arrow-up.svg'); }
.sk-doodle--burst { --d: url('doodles/burst.svg'); }
.sk-doodle--cloud { --d: url('doodles/cloud.svg'); }
.sk-doodle--windmill { --d: url('doodles/windmill.svg'); }
.sk-doodle--sparkle { --d: url('doodles/sparkle.svg'); }
.sk-doodle--squiggle { --d: url('doodles/squiggle.svg'); }
.sk-doodle--scribble-circle { --d: url('doodles/scribble-circle.svg'); }
.sk-doodle--heart { --d: url('doodles/heart.svg'); }
.sk-doodle--new { --d: url('doodles/new.svg'); }
.sk-doodle--play-me { --d: url('doodles/play-me.svg'); }
.sk-doodle--hi { --d: url('doodles/hi.svg'); }
.sk-doodle--tulip { --d: url('doodles/tulip.svg'); }
.sk-doodle--paper-plane { --d: url('doodles/paper-plane.svg'); }
.sk-doodle--bolt { --d: url('doodles/bolt.svg'); }
.sk-doodle--crown { --d: url('doodles/crown.svg'); }
.sk-doodle--coffee { --d: url('doodles/coffee.svg'); }
.sk-doodle--controller { --d: url('doodles/controller.svg'); }
.sk-doodle--download { --d: url('doodles/download.svg'); }
.sk-doodle--camera { --d: url('doodles/camera.svg'); }
.sk-doodle--planet { --d: url('doodles/planet.svg'); }
.sk-doodle--check { --d: url('doodles/check.svg'); }

/* ------------------------------------------------------------------ signature */

/* The mark. Used as the logo on every site and as a watermark. */
.sk-signature {
  display: inline-block;
  width: 96px;
  aspect-ratio: 4440 / 1596;
  background: currentColor;
  -webkit-mask: url('signature.svg') center / contain no-repeat;
  mask: url('signature.svg') center / contain no-repeat;
}

.sk-signature[data-sk-draw] {
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--sk-dur-draw) var(--sk-ease-in-out);
  transition-delay: calc(var(--i, 0) * var(--sk-stagger));
}

.sk-signature[data-sk-draw].is-in {
  clip-path: inset(0 0 0 0);
}

/* ------------------------------------------------------------------ social */

/* Other companies' marks in their own colours, drawn at icon size.
   <i class="sk-social sk-social--instagram" aria-hidden="true"></i>
   The tile is CSS (brand tokens), the glyph a mask from social/, so one class
   gives the real mark with no inline SVG to copy around. Size with width. */
.sk-social {
  position: relative;
  display: inline-block;
  width: 20px;
  aspect-ratio: 1;
  flex: none;
  border-radius: 24%;
}

.sk-social::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--sk-paper);
  -webkit-mask: var(--_glyph) center / contain no-repeat;
  mask: var(--_glyph) center / contain no-repeat;
}

.sk-social--instagram {
  --_glyph: url('social/instagram.svg');
  background: radial-gradient(circle at 30% 107%, var(--sk-brand-instagram-1) 0%, var(--sk-brand-instagram-1) 5%, var(--sk-brand-instagram-2) 45%, var(--sk-brand-instagram-3) 60%, var(--sk-brand-instagram-4) 90%),
    var(--sk-brand-instagram-5);
}

.sk-social--linkedin {
  --_glyph: url('social/linkedin.svg');
  border-radius: 12%;
  background: var(--sk-brand-linkedin);
}

.sk-social--x {
  --_glyph: url('social/x.svg');
  border-radius: 0;
}

.sk-social--x::after {
  inset: 6%;
  background: var(--sk-brand-x);
}

/* X is a black mark; in the dark it is printed white, as X itself does */
[data-sk-theme='dusk'] .sk-social--x::after {
  background: var(--sk-fg);
}

.sk-pill .sk-social {
  width: 18px;
}

/* The icon: the signature's m in a marker circle, for square spaces
   (favicon, avatars, app icons, small logo spots). It takes currentColor, so
   it is ink on light and paper on dusk without a second file. The favicon
   (icon/favicon.svg) switches itself with the browser's colour scheme. */
.sk-icon {
  display: inline-block;
  width: 32px;
  aspect-ratio: 1;
  background: currentColor;
  -webkit-mask: url('icon/icon.svg') center / contain no-repeat;
  mask: url('icon/icon.svg') center / contain no-repeat;
  flex: none;
}

/* The icon in a UI row, next to doodles (the dock, a nav, a key): the same m
   and circle restroked on the doodle grid, so it matches their line weight
   at the same size. icon.svg stays the brand mark for big and standalone
   uses. The dock picks this up on its own. */
.sk-icon--ui {
  -webkit-mask-image: url('icon/icon-ui.svg');
  mask-image: url('icon/icon-ui.svg');
}

/* ------------------------------------------------------------------ sky */

/* A full-bleed plate from the world. Pick the hour with a modifier; the
   picture swaps to the portrait crop on tall narrow screens. Content goes in
   .sk-sky__inner and sits over the plate. */
.sk-sky {
  --plate: image-set(url('sky/hero-2880.avif') type('image/avif'), url('sky/hero-2880.webp') type('image/webp'));
  --plate-sm: image-set(url('sky/hero-portrait-1600.avif') type('image/avif'), url('sky/hero-portrait-1600.webp') type('image/webp'));
  --plate-tone: var(--sk-sky);
  position: relative;
  /* sideways only: the foreground clouds hang below the section and over
     whatever comes next, which is what hides the seam */
  overflow-x: clip;
  background: var(--plate-tone) var(--plate) center bottom / cover no-repeat;
}

@media (max-aspect-ratio: 4/5) {
  .sk-sky {
    background-image: var(--plate-sm);
  }
}

@media (max-width: 1100px) and (min-aspect-ratio: 4/5) {
  .sk-sky {
    --plate: image-set(url('sky/hero-1600.avif') type('image/avif'), url('sky/hero-1600.webp') type('image/webp'));
  }
}

.sk-sky--evening {
  --plate: image-set(url('sky/evening-2880.avif') type('image/avif'), url('sky/evening-2880.webp') type('image/webp'));
  --plate-sm: image-set(url('sky/evening-1600.avif') type('image/avif'), url('sky/evening-1600.webp') type('image/webp'));
  --plate-tone: var(--sk-horizon);
}

.sk-sky--fog {
  --plate: image-set(url('sky/fog-2880.avif') type('image/avif'), url('sky/fog-2880.webp') type('image/webp'));
  --plate-sm: image-set(url('sky/fog-1600.avif') type('image/avif'), url('sky/fog-1600.webp') type('image/webp'));
  --plate-tone: var(--sk-sky-haze);
}

.sk-sky--dusk {
  --plate: image-set(url('sky/dusk-2880.avif') type('image/avif'), url('sky/dusk-2880.webp') type('image/webp'));
  --plate-sm: image-set(url('sky/dusk-1600.avif') type('image/avif'), url('sky/dusk-1600.webp') type('image/webp'));
  --plate-tone: var(--sk-dusk-1);
}

.sk-sky--lost {
  --plate: image-set(url('sky/lost-2880.avif') type('image/avif'), url('sky/lost-2880.webp') type('image/webp'));
  --plate-sm: image-set(url('sky/lost-1600.avif') type('image/avif'), url('sky/lost-1600.webp') type('image/webp'));
  --plate-tone: var(--sk-cloud);
}

/* Climbing back out of the white: the top quarter of the plate is pure paper,
   then the evening cloud bank, then the polder. Whatever sits above it on
   white flows straight into the clouds with no seam. The footer's plate. */
.sk-sky--ascent {
  --plate: image-set(url('sky/ascent-2880.avif') type('image/avif'), url('sky/ascent-2880.webp') type('image/webp'));
  --plate-sm: image-set(url('sky/ascent-portrait-1600.avif') type('image/avif'), url('sky/ascent-portrait-1600.webp') type('image/webp'));
  --plate-tone: var(--sk-paper);
}

/* Portrait-only plates keep their landscape picture in a narrow window: the
   portrait crop only exists for the morning hero. */
@media (max-aspect-ratio: 4/5) {
  .sk-sky--evening,
  .sk-sky--fog,
  .sk-sky--dusk,
  .sk-sky--lost {
    background-image: var(--plate-sm);
    background-position: 50% 100%;
  }
}

/* Fixed variant: the plate stays put while the page scrolls over it. Tools
   use this so the sky is the room they work in. */
.sk-sky--fixed {
  background: var(--plate-tone);
  /* keeps the fixed plate (z-index -1) above this element's own tone when
     the room is a wrapper div rather than <body> */
  isolation: isolate;
}

.sk-sky--fixed::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--plate-tone) var(--plate) center bottom / cover no-repeat;
}

@media (max-aspect-ratio: 4/5) {
  .sk-sky--fixed::before {
    background-image: var(--plate-sm);
  }
}

.sk-sky__inner {
  position: relative;
  z-index: 2;
}

/* Cloud tops in front of everything, screen-blended so their black
   background vanishes and only the lit cloud is left. */
.sk-sky__fore {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -12%;
  height: 62%;
  z-index: 3;
  pointer-events: none;
  background: image-set(url('sky/foreclouds-2880.avif') type('image/avif'), url('sky/foreclouds-2880.webp') type('image/webp')) center bottom / cover no-repeat;
  mix-blend-mode: screen;
  will-change: transform;
  -webkit-mask-image: linear-gradient(#000 62%, transparent);
  mask-image: linear-gradient(#000 62%, transparent);
}

@media (max-width: 800px) {
  .sk-sky__fore {
    height: 42%;
    bottom: -6%;
    background-size: 200% auto;
  }
}

/* The cloud bank you fall into: continues a .sk-sky above it and lands on
   white. Overlap it onto the section above with a negative margin. The plate
   is anchored to the bottom so it always lands on white however tall the
   section is; what gets cropped is the top, which the mask fades out anyway. */
.sk-descent {
  position: relative;
  z-index: 1;
  /* explicit, or a max-height clamp shrinks the width to keep the ratio */
  width: 100%;
  /* the plate's own shape, so the whole bank shows by default. The plate
     ends where the cloud has dissolved into white, so whatever follows lands
     straight under it with no empty band. */
  aspect-ratio: 1600 / 1705;
  max-height: 1500px;
  background:
    image-set(url('sky/descent-1600.avif') type('image/avif'), url('sky/descent-1600.webp') type('image/webp')) center bottom / 100% auto no-repeat,
    var(--sk-paper);
  -webkit-mask-image: linear-gradient(transparent, #000 22%);
  mask-image: linear-gradient(transparent, #000 22%);
}

@media (max-width: 800px) {
  .sk-descent {
    background-image:
      image-set(url('sky/descent-828.avif') type('image/avif'), url('sky/descent-828.webp') type('image/webp'));
    background-size: 180% auto;
    /* the 828 plate drawn at 180% width: 1.8 x 883 tall per 828 wide */
    aspect-ratio: 828 / 1589;
  }
}

/* ------------------------------------------------------------------ nav */

/* The floating bar: signature left, links right, glass over the sky. */
.sk-nav {
  position: sticky;
  top: 12px;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sk-space-4);
  width: min(100% - 24px, 1120px);
  margin: 12px auto 0;
  padding: 8px 8px 8px 18px;
  border-radius: var(--sk-radius-lg);
  background: color-mix(in oklab, var(--sk-surface) 72%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid color-mix(in oklab, var(--sk-surface) 60%, transparent);
  box-shadow: var(--sk-shadow-float);
  transition: background-color var(--sk-dur-base) var(--sk-ease-hover);
}

/* At dusk the glass is the same navy as the panels scrolling under it, so it
   gets a denser fill and a visible rim to stay a separate object. */
[data-sk-theme='dusk'] .sk-nav {
  background: color-mix(in oklab, var(--sk-surface) 88%, transparent);
  border-color: var(--sk-edge-soft);
}

/* Once the page has scrolled, content passes under the bar, so the glass
   frosts over and nothing reads as colliding with it. kit.js sets it. */
.sk-nav.is-scrolled {
  background: color-mix(in oklab, var(--sk-surface) 92%, transparent);
}

.sk-nav__home {
  display: inline-flex;
  color: var(--sk-fg);
  padding: 6px 0;
}

.sk-nav__home .sk-signature {
  width: 78px;
}

.sk-nav__links {
  display: flex;
  gap: var(--sk-space-1);
  align-items: center;
}

.sk-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 12px;
  border-radius: var(--sk-radius-sm);
  font-size: var(--sk-size-sm);
  font-weight: 600;
  text-decoration: none;
  color: var(--sk-fg-2);
  transition:
    background-color var(--sk-dur-fast) var(--sk-ease-hover),
    color var(--sk-dur-fast) var(--sk-ease-hover),
    transform var(--sk-dur-press) var(--sk-ease-out);
}

/* a press sinks it a hair, the way the pills do */
.sk-nav__link:active {
  transform: scale(var(--sk-press-scale));
}

@media (hover: hover) and (pointer: fine) {
  .sk-nav__link:hover {
    background: color-mix(in oklab, var(--sk-fg) 7%, transparent);
    color: var(--sk-fg);
  }
}

.sk-nav__link[aria-current='page'] {
  color: var(--sk-fg);
}

/* ------------------------------------------------------------------ dock */

/* The navigation on every site: a pressed-key dock floating at the bottom,
   where a thumb already is. Icons are the doodles (or .sk-icon for home);
   each item carries its name in aria-label and data-label, and the label
   shows as a tooltip on hover or keyboard focus.

   <nav class="sk-dock" aria-label="Main">
     <a class="sk-dock__item" href="/" aria-current="page" aria-label="home"
        data-label="home"><span class="sk-icon"></span></a>
     <i class="sk-dock__sep"></i>
     <a class="sk-dock__item" href="/#games" aria-label="games"
        data-label="games"><i class="sk-doodle sk-doodle--controller"></i></a>
   </nav>

   Tooltips wait a beat the first time so a pointer passing over the dock
   does not flash labels, then open instantly while you move along it
   (kit.js adds .is-warm), the way a good toolbar behaves. */
.sk-dock {
  position: fixed;
  left: 50%;
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px;
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: 20px;
  background: var(--sk-surface);
  color: var(--sk-fg);
  box-shadow: var(--sk-shadow-press);
  transform: translateX(-50%);
}

/* nothing on the page may end up hidden under it */
body:has(> .sk-dock),
body:has(.sk-dock) {
  padding-bottom: calc(96px + env(safe-area-inset-bottom));
}

/* A page that ends in the footer plate lets the plate run under the dock to
   the very last pixel instead: the footer takes the dock's room inside its
   own bottom padding, so there is no white strip under the polder. */
body:has(.sk-dock):has(.sk-foot) {
  padding-bottom: 0;
}

.sk-dock__item {
  position: relative;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* keeps the sliding "you are here" key (below) under the icon */
  isolation: isolate;
  transition:
    transform var(--sk-dur-press) var(--sk-ease-out),
    background-color var(--sk-dur-fast) var(--sk-ease-hover);
}

.sk-dock__item .sk-doodle,
.sk-dock__item .sk-icon {
  width: var(--sk-doodle-md);
}

.sk-dock__item .sk-icon {
  -webkit-mask-image: url('icon/icon-ui.svg');
  mask-image: url('icon/icon-ui.svg');
}

.sk-dock__item[aria-current='page'],
.sk-dock__item[aria-current='true'] {
  background: var(--sk-surface-3);
}

@media (hover: hover) and (pointer: fine) {
  .sk-dock__item:hover {
    background: var(--sk-surface-2);
  }

  .sk-dock__item[aria-current]:hover {
    background: var(--sk-surface-3);
  }
}

.sk-dock__item:active {
  transform: scale(var(--sk-press-scale-icon));
}

.sk-dock__sep {
  width: 2px;
  height: 28px;
  margin: 0 4px;
  border-radius: 2px;
  background: var(--sk-edge-soft);
}

/* the label: rises out of the item, from its bottom edge */
.sk-dock__item::after {
  content: attr(data-label);
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  padding: 4px 9px;
  border-radius: 8px;
  background: var(--sk-fg);
  color: var(--sk-bg);
  font-size: var(--sk-size-xs);
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 4px) scale(0.97);
  transform-origin: 50% 100%;
  transition:
    opacity var(--sk-dur-exit) var(--sk-ease-out),
    transform var(--sk-dur-exit) var(--sk-ease-out);
}

.sk-dock__item:not([data-label])::after {
  content: none;
}

@media (hover: hover) and (pointer: fine) {
  .sk-dock__item:hover::after {
    opacity: 1;
    transform: translate(-50%, 0) scale(1);
    transition-duration: var(--sk-dur-fast);
    transition-delay: var(--sk-dur-slow);
  }

  .sk-dock.is-warm .sk-dock__item:hover::after {
    transition-duration: 0ms;
    transition-delay: 0ms;
  }
}

.sk-dock__item:focus-visible::after {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
  transition-duration: 0ms;
}

/* Tucked away: a page can send the dock below the screen edge while it is in
   the way (a game being played on a phone) and call it back later. Leaves
   faster than it returns; with reduced motion it only fades. */
.sk-dock {
  transition:
    transform var(--sk-dur-base) var(--sk-ease-out),
    opacity var(--sk-dur-base) var(--sk-ease-out);
}

.sk-dock.is-away {
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, calc(100% + 24px));
  transition-duration: var(--sk-dur-exit);
}

@media (prefers-reduced-motion: reduce) {
  .sk-dock.is-away {
    transform: translateX(-50%);
  }
}

/* ------------------------------------------------------------------ form */

.sk-field {
  display: grid;
  gap: 6px;
}

.sk-label {
  font-size: var(--sk-size-sm);
  font-weight: 600;
  color: var(--sk-fg-2);
}

.sk-input {
  width: 100%;
  min-height: 46px;
  padding: 0 14px;
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: var(--sk-radius-md);
  background: var(--sk-surface);
  color: var(--sk-fg);
  font-size: var(--sk-size-base);
  outline: none;
  transition: box-shadow var(--sk-dur-fast) var(--sk-ease-out);
}

textarea.sk-input {
  min-height: 96px;
  padding: 12px 14px;
  resize: vertical;
}

.sk-input::placeholder {
  color: var(--sk-fg-4);
}

.sk-input:focus-visible {
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--sk-focus) 35%, transparent);
}

.sk-input[aria-invalid='true'] {
  border-color: var(--sk-stop);
}

/* Combo: one key that holds a field and its button, for one-line forms like
   a name for a score board. The lead cell carries context (the score), the
   button sits inside the frame, so the row reads as a single object. */
.sk-combo {
  display: flex;
  align-items: stretch;
  min-height: 48px;
  padding: 4px;
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: var(--sk-radius-md);
  background: var(--sk-surface);
  box-shadow: var(--sk-shadow-press);
  transition: box-shadow var(--sk-dur-fast) var(--sk-ease-out);
}

.sk-combo:focus-within {
  box-shadow:
    var(--sk-shadow-press),
    0 0 0 4px color-mix(in oklab, var(--sk-focus) 35%, transparent);
}

.sk-combo__lead {
  display: flex;
  align-items: center;
  padding: 0 var(--sk-space-3) 0 var(--sk-space-2);
  border-right: var(--sk-stroke-hair) solid var(--sk-edge-soft);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--sk-fg);
  white-space: nowrap;
}

.sk-combo .sk-input {
  flex: 1;
  min-width: 0;
  min-height: 0;
  padding: 0 var(--sk-space-3);
  border: 0;
  background: transparent;
  box-shadow: none;
}

/* the frame already carries the key shadow, so the inner button presses by
   scale instead of sinking */
.sk-combo .sk-pill {
  min-height: 0;
  border-width: 0;
  box-shadow: none;
  transform: none;
  transition:
    transform var(--sk-dur-press) var(--sk-ease-out),
    background-color var(--sk-dur-fast) var(--sk-ease-hover);
}

.sk-combo .sk-pill:active:not(:disabled) {
  transform: scale(var(--sk-press-scale));
}

/* Drop zone: a dashed marker box that fills when something is dragged in. */
.sk-drop {
  display: grid;
  place-items: center;
  gap: var(--sk-space-3);
  min-height: 220px;
  padding: var(--sk-space-8);
  border: 2px dashed var(--sk-fg-3);
  border-radius: var(--sk-radius-lg);
  background: color-mix(in oklab, var(--sk-surface) 70%, transparent);
  text-align: center;
  cursor: pointer;
  transition:
    background-color var(--sk-dur-fast) var(--sk-ease-hover),
    border-color var(--sk-dur-fast) var(--sk-ease-hover),
    transform var(--sk-dur-base) var(--sk-ease-out);
}

.sk-drop.is-over,
.sk-drop:focus-visible {
  border-color: var(--sk-fg);
  background: var(--sk-surface);
}

/* only a drag swells it: focus arrives by keyboard, and keyboard gets no motion */
.sk-drop.is-over {
  transform: scale(var(--sk-swell));
}

/* Segmented control: a few options in one sunken track, the chosen one
   raised as a white key. Radio labels (label > input + span) or buttons with
   aria-pressed. The colour change is the only motion: it is picked often. */
.sk-seg {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px;
  border: var(--sk-stroke-hair) solid var(--sk-edge-soft);
  border-radius: var(--sk-radius-md);
  background: var(--sk-surface-2);
}

.sk-seg > button,
.sk-seg > label {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding: 0 var(--sk-space-3);
  border: 0;
  border-radius: var(--sk-radius-sm);
  background: transparent;
  color: var(--sk-fg-3);
  font-size: var(--sk-size-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--sk-dur-fast) var(--sk-ease-hover),
    color var(--sk-dur-fast) var(--sk-ease-hover),
    box-shadow var(--sk-dur-fast) var(--sk-ease-hover),
    transform var(--sk-dur-press) var(--sk-ease-out);
}

.sk-seg > label > input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.sk-seg > label:has(input:focus-visible) {
  outline: 2px solid var(--sk-focus);
  outline-offset: 2px;
}

@media (hover: hover) and (pointer: fine) {
  .sk-seg > button:hover,
  .sk-seg > label:hover {
    color: var(--sk-fg);
  }
}

.sk-seg > button:active,
.sk-seg > label:active {
  transform: scale(var(--sk-press-scale));
}

.sk-seg > [aria-pressed='true'],
.sk-seg > label:has(input:checked) {
  background: var(--sk-surface);
  color: var(--sk-fg);
  box-shadow: inset 0 0 0 var(--sk-stroke-hair) var(--sk-edge), 0 2px 0 var(--sk-edge);
}

/* A block of code or a command to paste: soft box, wraps instead of
   scrolling, so a long signed URL never widens the page. */
.sk-code {
  display: block;
  padding: var(--sk-space-3) 14px;
  border: var(--sk-stroke-hair) solid var(--sk-edge-soft);
  border-radius: var(--sk-radius-md);
  background: var(--sk-surface-2);
  color: var(--sk-fg);
  font-family: var(--sk-font-mono);
  font-size: var(--sk-size-sm);
  line-height: 1.5;
  letter-spacing: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ------------------------------------------------------------------ progress */

.sk-progress {
  --v: 0;
  position: relative;
  height: 14px;
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: var(--sk-radius-pill);
  background: var(--sk-surface);
  overflow: hidden;
}

/* scaleX, not width: the bar moves on the compositor */
.sk-progress::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(-45deg, var(--sk-fg) 0 6px, var(--sk-fg-2) 6px 12px);
  transform-origin: left;
  transform: scaleX(var(--v));
  transition: transform var(--sk-dur-base) linear;
}

.sk-progress--ember::after {
  background: repeating-linear-gradient(-45deg, var(--sk-accent) 0 6px, var(--sk-ember-soft) 6px 12px);
}

/* ------------------------------------------------------------------ toast */

.sk-toasts {
  position: fixed;
  left: 50%;
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 100;
  display: grid;
  gap: var(--sk-space-2);
  transform: translateX(-50%);
  width: min(100% - 32px, 420px);
  pointer-events: none;
}

/* with a dock on the page, toasts stack above it instead of over it
   (dock: 48px item + 2 x 6px padding + 2 x 2px border + 4px shadow, then a gap) */
body:has(.sk-dock) .sk-toasts {
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + 80px);
}

.sk-toast {
  display: flex;
  align-items: center;
  gap: var(--sk-space-3);
  padding: 12px 16px;
  border: var(--sk-stroke-ink) solid var(--sk-edge);
  border-radius: var(--sk-radius-md);
  background: var(--sk-surface);
  color: var(--sk-fg);
  font-weight: 500;
  box-shadow: var(--sk-shadow-press);
  pointer-events: auto;
  transition:
    transform var(--sk-dur-base) var(--sk-ease-out),
    opacity var(--sk-dur-base) var(--sk-ease-out);
}

@starting-style {
  .sk-toast {
    opacity: 0;
    transform: translateY(100%);
  }
}

.sk-toast.is-leaving {
  opacity: 0;
  transform: translateY(100%);
  transition-duration: var(--sk-dur-exit);
}

/* ------------------------------------------------------------------ reveal */

/* Scroll reveal: fades up out of a light blur, once. kit.js adds .is-in.
   Set --i on siblings for a stagger. Stagger is decoration and never blocks
   input: the element is interactive the whole time. */
[data-sk-reveal] {
  opacity: 0;
  transform: translateY(var(--sk-reveal-y));
  filter: blur(var(--sk-reveal-blur));
  transition:
    opacity var(--sk-dur-reveal) var(--sk-ease-out),
    transform var(--sk-dur-reveal) var(--sk-ease-out),
    filter var(--sk-dur-reveal) var(--sk-ease-out);
  transition-delay: calc(var(--i, 0) * var(--sk-stagger));
}

[data-sk-reveal].is-in {
  opacity: 1;
  transform: none;
  filter: none;
}

/* no JS, no hidden content */
.sk-no-js [data-sk-reveal] {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Pop: a sticker slapped on. Grows out of a smaller, more tilted self on the
   spring, so it lands with a hair of overshoot. For photos and stickers, not
   for text. */
[data-sk-reveal='pop'] {
  filter: none;
  transform: scale(var(--sk-pop-scale)) rotate(var(--sk-pop-tilt));
  transition:
    opacity var(--sk-dur-base) var(--sk-ease-out),
    transform var(--sk-dur-slow) var(--sk-spring);
  transition-delay: calc(var(--i, 0) * var(--sk-stagger));
}

/* Tilt: sheets of paper laid down by hand. Each one settles out of a slight
   tilt, alternating left and right along the row. For cards in a grid. */
[data-sk-reveal='tilt'] {
  --_tilt: var(--sk-reveal-tilt);
  transform: translateY(var(--sk-reveal-y)) rotate(var(--_tilt));
}

[data-sk-reveal='tilt']:nth-child(2n) {
  --_tilt: calc(var(--sk-reveal-tilt) * -1);
}

/* ------------------------------------------------------------------ words */

/* A headline that says itself: each word rises out of the blur a beat after
   the one before, like the reveal but per word. kit.js splits the text of
   [data-sk-words] into .sk-word spans (static text only: it rewrites the
   text nodes) and keeps the whole sentence as the heading's accessible name.
   --i delays the whole line, the same way it does a reveal. */
[data-sk-words] .sk-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(var(--sk-reveal-y));
  filter: blur(var(--sk-reveal-blur));
  transition:
    opacity var(--sk-dur-reveal) var(--sk-ease-out),
    transform var(--sk-dur-reveal) var(--sk-ease-out),
    filter var(--sk-dur-reveal) var(--sk-ease-out);
  transition-delay: calc(var(--i, 0) * var(--sk-stagger) + var(--w, 0) * var(--sk-stagger-word));
}

/* punctuation glued to the word before it ("Mees,") stays inline text, so it
   can never wrap onto a line of its own; it fades with that word */
[data-sk-words] .sk-word--tail {
  display: inline;
  transform: none;
}

[data-sk-words].is-in .sk-word {
  opacity: 1;
  transform: none;
  filter: none;
}

/* ------------------------------------------------------------------ idle */

/* Float: something pinned in the sky bobs on the air, slowly, out of step
   with its neighbours (set --f, 0 to 1, to offset one). It uses the
   translate property, so it stacks on top of any transform the element
   already has (a tilt, a hover). kit.js pauses it while it is off screen;
   reduced motion never starts it. */
[data-sk-float] {
  animation: sk-float var(--sk-dur-float) var(--sk-ease-sine) infinite alternate both;
  animation-delay: calc(var(--sk-dur-float) * var(--f, 0) * -1);
  animation-play-state: paused;
}

[data-sk-float].is-near {
  animation-play-state: running;
}

@keyframes sk-float {
  from {
    translate: 0 0;
  }
  to {
    translate: 0 var(--sk-float-y);
  }
}

/* The foreground clouds drift. Very slowly, sideways, and back: over a minute
   they travel about a thumb's width. The layer is a little wider than its
   sky so no edge ever shows. Paused off screen like the float. */
.sk-sky__fore {
  left: calc(var(--sk-drift-x) * 1.2);
  right: calc(var(--sk-drift-x) * 1.2);
  animation: sk-drift var(--sk-dur-drift) var(--sk-ease-sine) infinite alternate both;
  animation-play-state: paused;
}

.sk-sky__fore.is-near {
  animation-play-state: running;
}

@keyframes sk-drift {
  from {
    translate: 0 0;
  }
  to {
    translate: var(--sk-drift-x) 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-sk-float],
  .sk-sky__fore {
    animation: none;
  }
}

/* ------------------------------------------------------------------ doodle life */

/* Wiggle: a doodle you point at shakes itself like it was just drawn.
   On the doodle, or on anything holding doodles ([data-sk-wiggle] on a link
   wiggles the doodles inside it). Once per hover; pointers that hover only. */
@media (hover: hover) and (pointer: fine) {
  .sk-doodle[data-sk-wiggle]:hover,
  [data-sk-wiggle]:hover .sk-doodle {
    animation: sk-wiggle var(--sk-dur-wiggle) var(--sk-ease-out);
  }
}

@keyframes sk-wiggle {
  0%,
  100% {
    transform: none;
  }
  30% {
    transform: rotate(var(--sk-wiggle));
  }
  65% {
    transform: rotate(calc(var(--sk-wiggle) * -0.6));
  }
}

/* Twinkle: a drawn sparkle catches the light once, as its line finishes.
   Put data-sk-twinkle next to data-sk-draw. Once per page. */
[data-sk-twinkle].is-in {
  animation: sk-twinkle var(--sk-dur-twinkle) var(--sk-ease-out) calc(var(--sk-dur-draw) * 0.7 + var(--i, 0) * var(--sk-stagger)) both;
}

@keyframes sk-twinkle {
  0%,
  100% {
    transform: none;
  }
  35% {
    transform: scale(var(--sk-twinkle-scale)) rotate(calc(var(--sk-wiggle) * 2));
  }
}

/* A paper plane or an arrow on a key leans the way it is going when the key
   is hovered: the key is about to send you somewhere. */
.sk-pill .sk-doodle--paper-plane,
.sk-pill .sk-doodle--arrow-right {
  transition: transform var(--sk-dur-base) var(--sk-ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .sk-pill:hover .sk-doodle--paper-plane {
    transform: translate(var(--sk-sketch-shift), calc(var(--sk-sketch-shift) * -1)) rotate(calc(var(--sk-wiggle) * -1));
  }

  .sk-pill:hover .sk-doodle--arrow-right {
    transform: translateX(var(--sk-sketch-shift));
  }
}

/* ------------------------------------------------------------------ card life */

/* A linked card lifts off the page under the pointer, and its second pencil
   pass slides a hair, as if the sketch were redrawn. Pressed, the whole card
   gives. Individual transform properties, so a card can still carry its own
   transform (a reveal, a tilt) without the two fighting. */
a.sk-card {
  transition:
    translate var(--sk-dur-base) var(--sk-ease-out),
    scale var(--sk-dur-press) var(--sk-ease-out);
}

a.sk-card::after {
  transition: translate var(--sk-dur-base) var(--sk-ease-out);
}

@media (hover: hover) and (pointer: fine) {
  a.sk-card:hover {
    translate: 0 var(--sk-lift-card);
  }

  a.sk-card:hover::after {
    translate: var(--sk-sketch-shift) calc(var(--sk-sketch-shift) * -1);
  }
}

a.sk-card:active {
  scale: var(--sk-press-scale);
}

/* a pinned photo that is a link gives under the press too */
a:active > .sk-chip {
  transform: rotate(0deg) scale(var(--sk-press-scale));
  transition-duration: var(--sk-dur-press);
}

/* ------------------------------------------------------------------ dock life */

/* The "you are here" key: kit.js moves one .sk-dock__here into whichever item
   is aria-current, and slides it there when the dock stays on the page. */
.sk-dock__here {
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--sk-surface-3);
  pointer-events: none;
}

.sk-dock__item[aria-current]:has(> .sk-dock__here) {
  background: transparent;
}

/* When a site swaps pages inside a view transition, the dock sits the swap
   out: it is lifted out of the page's crossfade and shown live, as it is on
   the new page, from the first frame. No two docks crossfading, no dock
   sliding to a new size, and the key's own slide (kit.js) plays inside it. */
.sk-dock {
  view-transition-name: sk-dock;
}

::view-transition-group(sk-dock),
::view-transition-new(sk-dock) {
  animation: none;
}

::view-transition-old(sk-dock) {
  display: none;
}

/* Named view transitions (anything a site names) travel on screen:
   ease-in-out, a little longer than a click. */
::view-transition-group(*) {
  animation-duration: var(--sk-dur-morph);
  animation-timing-function: var(--sk-ease-in-out);
}

/* ------------------------------------------------------------------ footer */

/* The evening plate with the signature large, signed at the bottom of the
   page the way a letter is. */
.sk-foot {
  /* always the ascent plate, whatever hour modifier sits beside it: its own
     top is white, so the page runs into it without a mask or a band */
  --plate: image-set(url('sky/ascent-2880.avif') type('image/avif'), url('sky/ascent-2880.webp') type('image/webp'));
  --plate-sm: image-set(url('sky/ascent-portrait-1600.avif') type('image/avif'), url('sky/ascent-portrait-1600.webp') type('image/webp'));
  --plate-tone: var(--sk-paper);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sk-space-6);
  /* the plate's own 16:9, so the white top, the clouds and the polder all
     show; the content sits in the cloud band between the white and the land */
  min-height: min(56.25vw, 900px);
  padding: max(17vw, var(--sk-space-32)) var(--sk-space-4) max(9vw, var(--sk-space-12));
  color: var(--sk-ink);
  text-align: center;
  background-position: center bottom;
}

@media (max-width: 1100px) and (min-aspect-ratio: 4/5) {
  .sk-foot {
    --plate: image-set(url('sky/ascent-1600.avif') type('image/avif'), url('sky/ascent-1600.webp') type('image/webp'));
  }
}

/* portrait plate: 828 x 1480, the land is the bottom tenth */
@media (max-aspect-ratio: 4/5) {
  .sk-foot {
    min-height: calc(100vw * 1480 / 828);
    padding: 44vw var(--sk-space-4) 30vw;
  }
}

body:has(.sk-dock) .sk-foot {
  padding-bottom: calc(max(9vw, var(--sk-space-12)) + 96px + env(safe-area-inset-bottom));
}

@media (max-aspect-ratio: 4/5) {
  body:has(.sk-dock) .sk-foot {
    padding-bottom: calc(30vw + 96px + env(safe-area-inset-bottom));
  }
}

.sk-foot .sk-signature {
  width: min(360px, 70vw);
}

.sk-foot__meta {
  margin-top: var(--sk-space-2);
  font-size: var(--sk-size-sm);
  color: var(--sk-ink-2);
}

/* ------------------------------------------------------------------ app */

/* The shell every tool page on a *.meesbeuk.com subdomain stands on, so the
   signature, the headline and the first panel land in the same place on
   every one of them. Only the plate and the theme differ between sites.

   <div class="sk-app">
     <header class="sk-app__top">
       <a class="sk-app__home" href="/"><span class="sk-lockup"><span class="sk-signature" aria-hidden="true"></span><span class="sk-lockup__name">Transfer</span></span></a>
       <div class="sk-app__aside">optional: a name tag, a status</div>
     </header>
     <main class="sk-app__main">
       <div class="sk-app__hero"><h1 class="sk-display"><b>Drop it in.</b> <span class="sk-app__line">It fades out on its own.</span></h1></div>
       <section class="sk-panel">the tool, one column wide</section>
       <section class="sk-app__section">
         <header class="sk-app__head"><h2 class="sk-h3">On the server</h2><span class="sk-app__meta">3 files</span></header>
         ...
       </section>
       <section class="sk-app__section sk-app__wide">results that want the room</section>
       <footer class="sk-app__foot">Files delete themselves.</footer>
     </main>
   </div>

   The main column is 720px; the hero may run to 1000px so its lines stay
   whole, and anything marked __wide takes the full 1280px (a grid of
   pictures). The dock sits where the kit always puts it. */
.sk-app {
  --sk-app-col: 720px;
  --sk-app-hero: 1000px;
  --sk-app-wide: 1280px;
  position: relative;
  /* flow-root: the header's top margin stays inside instead of collapsing
     through and making every short page scroll */
  display: flow-root;
  min-height: 100dvh;
  padding-bottom: var(--sk-space-8);
}

/* the kit sets display on panels, drops and bars, and a class-level display
   beats the hidden attribute's own display: none */
.sk-app [hidden] {
  display: none !important;
}

/* The signature alone, centred. The side slot sits in the right column so
   whatever goes there never pushes the signature off centre. */
.sk-app__top {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sk-space-3);
  width: min(100% - var(--sk-space-8), 1120px);
  margin: var(--sk-space-5) auto 0;
}

.sk-app__home {
  grid-column: 2;
  display: inline-flex;
  align-items: center;
  color: var(--sk-fg);
  text-decoration: none;
}

.sk-app__home .sk-signature {
  width: 96px;
}

/* The lockup: the signature, a hairline, and the tool's name in spaced
   caps, as one slot the home link wraps. The name is written in title case
   ("Thumbnails") so a screen reader says the word, not the letters.
   <span class="sk-lockup"><span class="sk-signature" aria-hidden="true"></span><span class="sk-lockup__name">Transfer</span></span> */
.sk-lockup {
  display: inline-flex;
  align-items: center;
  gap: var(--sk-space-3);
  color: var(--sk-fg);
}

.sk-lockup .sk-signature,
.sk-app__home .sk-lockup .sk-signature {
  width: 104px;
}

.sk-lockup__name {
  display: inline-flex;
  align-items: center;
  gap: var(--sk-space-3);
  font-size: var(--sk-size-sm);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--sk-track-caps);
  text-transform: uppercase;
  white-space: nowrap;
  /* tracking also trails the last letter; taking it back keeps the
     lockup's optical centre on the page's centre */
  margin-right: calc(var(--sk-track-caps) * -1);
}

.sk-lockup__name::before {
  content: '';
  width: 1px;
  height: 24px;
  background: color-mix(in oklab, var(--sk-fg) 30%, transparent);
}

/* phones: the same lockup, smaller, so the longest name fits at 360 */
@media (max-width: 640px) {
  .sk-lockup .sk-signature,
  .sk-app__home .sk-lockup .sk-signature {
    width: 84px;
  }

  .sk-lockup__name {
    font-size: var(--sk-size-xs);
  }

  .sk-lockup__name::before {
    height: 20px;
  }
}

.sk-app__aside {
  grid-column: 3;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: var(--sk-space-2);
  min-width: 0;
}

/* phones: the lockup keeps the centre, so a tag in the side slot folds to
   its dot (the words stay for screen readers) instead of running into it */
@media (max-width: 440px) {
  .sk-app__aside .sk-tag {
    gap: 0;
    padding: var(--sk-space-2);
  }

  .sk-app__aside .sk-tag > :not(i) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

.sk-app__main {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: var(--sk-space-8);
  width: min(100% - var(--sk-space-8), var(--sk-app-wide));
  margin-inline: auto;
  padding-top: clamp(var(--sk-space-10), 9vh, var(--sk-space-24));
}

.sk-app__main > * {
  width: min(100%, var(--sk-app-col));
}

/* phones: the first panel comes up closer, the sections sit tighter */
@media (max-width: 640px) {
  .sk-app__main {
    gap: var(--sk-space-6);
    padding-top: var(--sk-space-8);
  }
}

.sk-app__main > .sk-app__hero {
  width: min(100%, var(--sk-app-hero));
}

.sk-app__main > .sk-app__wide {
  width: 100%;
}

/* A block the page shows or hides as one (signed-in view, a share page)
   without adding a layout level: its children size as the shell's rows. */
.sk-app__group {
  display: contents;
}

.sk-app__group > * {
  width: min(100%, var(--sk-app-col));
}

.sk-app__group > .sk-app__hero {
  width: min(100%, var(--sk-app-hero));
}

.sk-app__group > .sk-app__wide {
  width: 100%;
}

.sk-app__hero {
  display: grid;
  justify-items: center;
  gap: var(--sk-space-4);
  text-align: center;
}

/* the second line of a two-part headline, kept whole */
.sk-app__line {
  display: block;
  text-wrap: balance;
}

.sk-app__section {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sk-space-3);
}

/* a section's heading row: the title left, quiet facts or keys right */
.sk-app__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sk-space-2) var(--sk-space-3);
  padding-inline: var(--sk-space-1);
}

.sk-app__meta {
  font-size: var(--sk-size-sm);
  color: var(--sk-fg-3);
  font-variant-numeric: tabular-nums;
}

.sk-app__foot {
  padding-top: var(--sk-space-4);
  font-size: var(--sk-size-sm);
  color: var(--sk-fg-2);
  text-align: center;
}

/* ------------------------------------------------------------------ gate */

/* Sign in, no access, signed out, something broke: one card in the middle
   of the shell, with a marker note above it. Goes inside .sk-app__main.

   <div class="sk-gate">
     <p class="sk-gate__note" aria-hidden="true"><span class="sk-hand">files fade out on their own</span><i class="sk-doodle sk-doodle--arrow-curl-down" data-sk-draw></i></p>
     <form class="sk-panel sk-gate__card">
       <h1 class="sk-title sk-gate__title"><b>Hi.</b> Who's this?</h1>
       <label class="sk-field">...</label>
       <button class="sk-pill sk-pill--ink sk-gate__go">Sign in</button>
     </form>
   </div> */
.sk-gate {
  display: grid;
  justify-items: center;
  padding-top: clamp(0px, 6vh, var(--sk-space-16));
}

/* written on the sky in marker; the tilt pivots on the note's left foot, so
   it lifts away from the card, and the arrow's tip stops above the edge */
.sk-gate__note {
  display: flex;
  align-items: flex-end;
  gap: var(--sk-space-1);
  width: min(100%, 420px);
  padding-left: var(--sk-space-8);
  margin-bottom: var(--sk-space-4);
  color: var(--sk-fg-2);
  transform: rotate(-3deg);
  transform-origin: left bottom;
}

.sk-gate__note .sk-hand {
  font-size: var(--sk-size-lg);
}

.sk-gate__note .sk-doodle {
  width: var(--sk-doodle-lg);
  transform: translateY(var(--sk-space-2));
}

.sk-gate__card {
  display: grid;
  gap: var(--sk-space-4);
  width: min(100%, 420px);
}

/* the title speaks like the display: the word that matters in ink */
.sk-gate__title {
  color: var(--sk-fg-3);
}

.sk-gate__title b {
  color: var(--sk-fg);
  font-weight: inherit;
}

.sk-gate__go {
  width: 100%;
  margin-top: var(--sk-space-2);
}

/* ------------------------------------------------------------------ misc */

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

.sk-divider {
  height: 2px;
  border: 0;
  background: var(--sk-edge-soft);
}

/* A divider with a word in the middle ("or"): hairlines either side, drawn in
   the text colour at low strength so it works on paper and on a plate.
   <div class="sk-or"><span class="sk-hand">or</span></div> */
.sk-or {
  display: flex;
  align-items: center;
  gap: var(--sk-space-3);
  width: 100%;
}

.sk-or::before,
.sk-or::after {
  content: '';
  flex: 1;
  height: var(--sk-stroke-hair);
  border-radius: var(--sk-radius-pill);
  background: currentColor;
  opacity: 0.35;
}

.sk-or .sk-hand {
  font-size: var(--sk-size-lg);
}

.sk-kbd {
  display: inline-block;
  padding: 1px 6px;
  border: 1.5px solid var(--sk-edge);
  border-radius: 6px;
  box-shadow: 0 2px 0 var(--sk-edge);
  font-family: var(--sk-font-mono);
  font-size: 0.85em;
}

/* ------------------------------------------------------------------ scroll */

/* Inertial scroll (kit.js, on <html data-sk-smooth>) is Lenis, vendored in
   vendor/ (MIT, darkroom.engineering). These are its own few rules. */
html.lenis,
html.lenis body {
  height: auto;
}

.lenis:not(.lenis-autoToggle).lenis-stopped {
  overflow: clip;
}

.lenis [data-lenis-prevent],
.lenis [data-sk-native] {
  overscroll-behavior: contain;
}

.lenis.lenis-smooth iframe {
  pointer-events: none;
}

/* Scroll-driven motion. Every effect here is tied to the scroll position
   itself (CSS scroll timelines, off the main thread), so it plays forwards
   and backwards with the reader's hand. Browsers without scroll timelines
   keep what they had: a static plate, and the one-shot reveals and draws
   ([data-sk-reveal], [data-sk-draw]) that sit on the same elements.

   [data-sk-scroll="away"]  the first screen's content drifts, shrinks and
                            blurs out as you scroll off it (top of page only)
   [data-sk-scroll="rise"]  rises in as it enters from the bottom; --i
                            staggers columns. Pair with data-sk-reveal.
   [data-sk-scroll="draw"]  a doodle draws with the scroll. Pair with
                            data-sk-draw.
   [data-sk-scroll="lag"]   drifts down a little as the page scrolls, so it
                            reads as further away (top of page only)
   .sk-sky--depth           the plate lags behind as its sky scrolls away
   .sk-sky--climb           the plate climbs into place as its sky scrolls in
                            (the footer) */
@supports (animation-timeline: view()) {
  [data-sk-scroll='away'] {
    animation: sk-away linear both;
    animation-timeline: scroll(root);
    animation-range: 0 var(--sk-scroll-away-range);
    transform-origin: 50% 30%;
  }

  [data-sk-scroll='lag'] {
    animation: sk-lag linear both;
    animation-timeline: scroll(root);
    animation-range: 0 100vh;
  }

  [data-sk-scroll='rise'],
  [data-sk-reveal][data-sk-scroll='rise'] {
    opacity: 1;
    filter: none;
    transform: none;
    transition: none;
    animation: sk-rise var(--sk-ease-out) both;
    animation-timeline: view();
    animation-range:
      entry calc(var(--sk-scroll-rise-start) + var(--i, 0) * var(--sk-scroll-stagger))
      entry calc(var(--sk-scroll-rise-end) + var(--i, 0) * var(--sk-scroll-stagger));
  }

  [data-sk-scroll='draw'],
  [data-sk-draw][data-sk-scroll='draw'] {
    transition: none;
    animation: sk-draw-scroll linear both;
    animation-timeline: view();
    animation-range: entry var(--sk-scroll-draw-start) cover var(--sk-scroll-draw-end);
  }

  /* The plate moves to a layer of its own so it can travel without the
     content; the element keeps only its tone. The layer sits under the
     sky's content (z 2 and up) and under whatever overlaps the sky's bottom,
     and it only ever moves down, into space the page already covers. */
  .sk-sky--depth,
  .sk-sky--climb {
    background-image: none;
    view-timeline: --sk-plate block;
  }

  .sk-sky--depth::before,
  .sk-sky--climb::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: var(--plate-tone) var(--plate) center bottom / cover no-repeat;
    animation: sk-plate-lag linear both;
    animation-timeline: --sk-plate;
    animation-range: exit 0% exit 100%;
  }

  /* climbing: the footer is the end of the page, so its plate starts low and
     lands exactly as the last pixel scrolls in. Clipped, so the lowered plate
     never lengthens the page. */
  .sk-sky--climb {
    overflow: clip;
    isolation: isolate;
  }

  .sk-sky--climb::before {
    z-index: -1;
    animation-name: sk-plate-climb;
    animation-range: entry 0% entry 100%;
  }

  @media (max-aspect-ratio: 4/5) {
    .sk-sky--depth::before,
    .sk-sky--climb::before {
      background-image: var(--plate-sm);
    }
  }
}

@keyframes sk-away {
  to {
    opacity: 0;
    transform: translateY(var(--sk-scroll-away-y)) scale(var(--sk-scroll-away-scale));
    filter: blur(var(--sk-scroll-away-blur));
  }
}

@keyframes sk-lag {
  to {
    transform: translateY(var(--sk-scroll-lag-y));
  }
}

@keyframes sk-rise {
  from {
    opacity: 0;
    transform: translateY(var(--sk-scroll-rise-y)) rotate(var(--_tilt, 0deg));
  }
}

@keyframes sk-draw-scroll {
  from {
    clip-path: inset(0 100% 0 0);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}

@keyframes sk-plate-lag {
  to {
    transform: translateY(var(--sk-scroll-plate-y));
  }
}

@keyframes sk-plate-climb {
  from {
    transform: translateY(var(--sk-scroll-climb-y));
  }
}

/* reduced motion: the words still fade as you leave, cards still fade in,
   doodles are simply there, nothing travels (the tokens zero the travel) */
@media (prefers-reduced-motion: reduce) {
  [data-sk-scroll='draw'],
  [data-sk-draw][data-sk-scroll='draw'] {
    animation: none;
    clip-path: none;
  }
}
