/* pawan.bio
   One stylesheet. No framework, no external requests.
   The visual language is lifted from the app-overview PDFs: black canvas, one
   accent per app, mono kickers at wide tracking, a light display serif, hairlines.
   This design commits to a single dark look, so there is no light palette. */

/* ---------- fonts ---------- */
@font-face {
  font-family: "Gloock";
  src: url("fonts/Gloock-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Instrument Serif";
  src: url("fonts/InstrumentSerif-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("fonts/Figtree.woff2") format("woff2-variations");
  font-weight: 300 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Plex Mono";
  src: url("fonts/IBMPlexMono-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* ---- the apps' own typefaces ----
   Each component is set in the face its app actually ships, so it reads as that app
   and not as this website. Only open-licensed faces are here: Kefir (commercial) and
   Apple's SF Compact Rounded / SF Mono / New York cannot be served as webfonts, so
   those apps get the closest open substitute instead. */
@font-face {
  font-family: "Atkinson";
  src: url("fonts/AtkinsonHyperlegibleNext-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Atkinson";
  src: url("fonts/AtkinsonHyperlegibleNext-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Quicksand";
  src: url("fonts/Quicksand.woff2") format("woff2-variations");
  font-weight: 300 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Bebas Neue";
  src: url("fonts/BebasNeue.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Public Sans";
  src: url("fonts/PublicSans.woff2") format("woff2-variations");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Saira Condensed";
  src: url("fonts/SairaCondensed-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Saira Condensed";
  src: url("fonts/SairaCondensed-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/JetBrainsMono.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Courier Prime";
  src: url("fonts/CourierPrime.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* ---------- tokens ---------- */
:root {
  --bg: #000000;
  --ink: #ffffff;
  --ink-2: rgba(255, 255, 255, 0.58);
  --ink-3: rgba(255, 255, 255, 0.48);   /* 4.9:1 on black; 0.34 failed AA */
  --hairline: rgba(255, 255, 255, 0.11);
  --accent: #6ba3ff;
  /* The brand accent, lightened until it is legible as text on black. Used for type;
     the raw --accent stays for washes, fills, rings and glows. */
  --accent-ink: color-mix(in oklab, var(--accent) 58%, #ffffff);
  --focus: #ffffff;

  --display: "Gloock", "Instrument Serif", ui-serif, "New York", Georgia, serif;
  --sans: "Figtree", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --mono: "Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --page: 72rem;
  --ease: cubic-bezier(0.23, 1, 0.32, 1);

}

/* Scroll progress of a panel, 0 to 1. site.js writes it as an inline style on each
   visible panel, which wins over this rule. The default is 1, not 0, so that if the
   script never runs every component renders its FINISHED state rather than an empty
   one. A portfolio that shows nothing is a worse failure than one that doesn't move. */
.panel { --p: 1; }

/* ---- per-app type ----
   Redefining --display/--sans/--mono on the panel means every component picks up its
   own app's typography without knowing anything about it. Substitutions, where the
   real face cannot be licensed for the web:
     Kefir Light        -> Instrument Serif   (Moon, Enso, Flux, Slate)
     SF Compact Rounded -> Quicksand          (Pomodoro)
     New York           -> Instrument Serif   (WaterMyPlants)
     SF Mono            -> IBM Plex Mono      (everywhere) */
[data-app="moon"], [data-app="flux"], [data-app="enso"], [data-app="slate"],
[data-app="linen"], [data-app="watermyplants"] {
  --display: "Instrument Serif", ui-serif, Georgia, serif;
  --sans: "Figtree", -apple-system, system-ui, sans-serif;
}
[data-app="helia"], [data-app="flappomodoro"] {
  --display: "Atkinson", -apple-system, system-ui, sans-serif;
  --sans: "Atkinson", -apple-system, system-ui, sans-serif;
}
[data-app="haru"], [data-app="pomodoro"] {
  --display: "Quicksand", -apple-system, system-ui, sans-serif;
  --sans: "Quicksand", -apple-system, system-ui, sans-serif;
}
[data-app="stack"] {
  --display: "Bebas Neue", Impact, sans-serif;
  --sans: "Public Sans", -apple-system, system-ui, sans-serif;
}
[data-app="apex"] {
  --display: "Saira Condensed", "Arial Narrow", sans-serif;
  --sans: "Saira Condensed", "Arial Narrow", sans-serif;
}
[data-app="cabin"] {
  --sans: -apple-system, "Helvetica Neue", system-ui, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
}
[data-app="flaps"], [data-app="meridian"], [data-app="threshold"] {
  --display: "Figtree", -apple-system, system-ui, sans-serif;
  --sans: "Figtree", -apple-system, system-ui, sans-serif;
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
/* The sticky mechanic needs a stage that fits. One column, or a short viewport, and it
   does not: the stage grows past 100svh and the component slides under the copy. */
@media (max-width: 55.99rem), (max-height: 46rem) {
  .panel { min-height: 0; }
  .panel .stage { position: static; min-height: 0; padding: 4rem 0; }
}
/* Wide but short (a landscape phone) still wants the two-column layout. */
@media (min-aspect-ratio: 3/2) and (min-width: 40rem) {
  .panel .grid { grid-template-columns: 1fr 1fr; }
  .panel.flip .art { order: -1; }
}

@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 350;
  font-size: 1.0625rem;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--accent); color: #000; }
/* White, not the app accent: several accents are near-black and the ring vanished. */
:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 3px;
  box-shadow: 0 0 0 5px rgba(0, 0, 0, .9); border-radius: 2px;
}
/* A keyboard user can reach the panel's link before scroll has revealed the copy. */
.panel .copy:focus-within > * { opacity: 1; transform: none; }

.wrap { width: min(100% - 3rem, var(--page)); margin-inline: auto; }
@media (max-width: 40rem) { .wrap { width: calc(100% - 2rem); } }

.skip { position: absolute; left: -9999px; top: 0; z-index: 100; background: #fff; color: #000; padding: .75rem 1rem; }
.skip:focus { left: 0; }

/* ---------- shared chrome, straight off the PDF ---------- */
.kicker {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.28em;
  text-transform: uppercase; color: var(--accent-ink); margin: 0 0 1.1rem;
}
.runninghead {
  position: fixed; top: 1.4rem; left: 0; right: 0; z-index: 30;
  display: flex; justify-content: space-between; pointer-events: none;
  font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--ink-3);
  mix-blend-mode: difference;
}
.runninghead .wrap { display: flex; justify-content: space-between; width: min(100% - 3rem, var(--page)); }
.runninghead a { pointer-events: auto; }

h1, h2, h3 { font-family: var(--display); font-weight: 400; margin: 0; }

.display {
  font-size: clamp(2.6rem, 6.4vw, 4.6rem);
  line-height: 1.04; letter-spacing: -0.018em;
}
.lede { font-size: clamp(1.05rem, 1.5vw, 1.25rem); line-height: 1.6; color: var(--ink); font-weight: 350; }
.body-2 { font-size: 0.9688rem; line-height: 1.68; color: var(--ink-2); }
.platformline {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--ink-2);
}
.hairline { border: 0; border-top: 1px solid var(--hairline); margin: 0; }

/* ---------- cover ---------- */
.cover {
  min-height: 100svh; display: grid; align-content: center;
  padding: 7rem 0 5rem; position: relative; overflow: hidden;
}
.cover h1 {
  font-size: clamp(3.2rem, 11.5vw, 9rem); line-height: 0.92;
  letter-spacing: -0.025em; margin: 1.5rem 0 1.75rem;
}
.cover .tag { font-size: clamp(1.1rem, 2.2vw, 1.6rem); color: var(--ink); max-width: 26ch; line-height: 1.4; }
.cover .platformline { margin-top: 2.5rem; }

.starfield { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.starfield i {
  position: absolute; background: #fff; border-radius: 50%;
  width: 2px; height: 2px;
  animation: twinkle 5s ease-in-out infinite alternate;
}
@keyframes twinkle { from { opacity: .1 } to { opacity: .55 } }

.scrollcue {
  position: absolute; bottom: 2.5rem; left: 50%; translate: -50% 0;
  font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.28em;
  text-transform: uppercase; color: var(--ink-3);
}
.scrollcue::after {
  content: ""; display: block; width: 1px; height: 2.5rem; margin: .75rem auto 0;
  background: linear-gradient(var(--ink-3), transparent);
  animation: cue 2.4s var(--ease) infinite;
  transform-origin: top;
}
@keyframes cue { 0%,100% { transform: scaleY(.35); opacity: .3 } 50% { transform: scaleY(1); opacity: 1 } }

/* ---------- stat band ---------- */
.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 2rem 1rem; padding: 3rem 0;
  border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline);
}
.stat .v { font-family: var(--mono); font-size: clamp(1.6rem, 3.2vw, 2.4rem); line-height: 1; }
.stat .v .small { font-size: .55em; }
.stat .k { font-size: 0.75rem; letter-spacing: 0.1em; color: var(--ink-3); margin-top: .6rem; }

/* =======================================================================
   THE DECK — one panel per app.
   The stage sticks while the panel scrolls past, so the component has room
   to play through its whole range.
   ======================================================================= */
/* The panel is deliberately taller than the viewport. That extra height is the
   scroll distance over which the sticky stage stays pinned and the component
   plays through its full range. Without it there is nothing to animate against. */
.panel { position: relative; min-height: var(--len, 195svh); }
.panel .stage {
  position: sticky; top: 0; min-height: 100svh;
  display: grid; align-items: center;
  padding: 5.5rem 0 4rem;
  overflow: hidden;
}
/* The accent wash breathes in with its panel. */
.panel .stage::before {
  content: ""; position: absolute; inset: 0; z-index: -2;
  background: radial-gradient(120% 80% at 78% 42%,
    color-mix(in srgb, var(--accent) 34%, transparent) 0%, transparent 66%);
  opacity: calc(0.35 + var(--p) * 0.65);
}
.panel .grid {
  display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 56rem) {
  .panel .grid { grid-template-columns: 1fr 1fr; }
  .panel.flip .art { order: -1; }
}

.panel .copy .display { margin-bottom: .9rem; }
.panel .copy .tag { font-size: clamp(1.05rem, 1.7vw, 1.3rem); color: var(--ink); margin: 0 0 1.25rem; }
.panel .copy .body-2 { max-width: 46ch; }
.panel .copy .platformline { margin-top: 1.75rem; }
.panel .open {
  display: inline-flex; align-items: center; gap: .6rem; margin-top: 1.75rem;
  font-family: var(--mono); font-size: .75rem; letter-spacing: .18em; text-transform: uppercase;
  border-bottom: 1px solid var(--hairline); padding-bottom: .4rem;
  transition: color .3s var(--ease), border-color .3s var(--ease), gap .3s var(--ease);
}
.panel .open:hover { color: var(--accent); border-color: var(--accent); gap: 1rem; }

/* Copy rises as the panel takes over the viewport. */
.panel .copy > * {
  opacity: clamp(0, calc((var(--p) - 0.06) * 6), 1);
  transform: translateY(calc((1 - clamp(0, calc((var(--p) - 0.06) * 6), 1)) * 18px));
}

/* The art holds a fixed square-ish stage so components can be positioned freely. */
/* The component is the hero, so the stage gives it room and a container context. */
.art {
  position: relative;
  display: grid; place-items: center;
  max-height: 76svh; margin-inline: auto; width: 100%;
  container-type: inline-size;
}
.art > * { width: 100%; }

/* ---------- component: device screenshot (the fallback) ---------- */
.c-device {
  display: grid; place-items: center; width: 100%;
  padding: 2.5rem 1rem; border-radius: 2rem;
  background: radial-gradient(120% 90% at 50% 0%,
    color-mix(in srgb, var(--accent) 20%, transparent), transparent 70%);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.c-device img {
  width: min(15rem, 70%); border-radius: 1.6rem;
  box-shadow: 0 0 0 1px rgba(255,255,255,.14), 0 30px 80px rgba(0,0,0,.9);
  transform: translateY(calc((1 - var(--p)) * 40px)) scale(calc(0.94 + var(--p) * 0.06));
}

/* The placeholder components that lived here are gone: the real ones live in
   _build/parts/*.html and carry their own scoped styles. They are deleted rather
   than left dormant because some of these selectors (.c-flaps .row above all) still
   matched the real markup and fought it. */

/* ---------- app page ---------- */
.back {
  display: inline-flex; gap: .5rem; padding: 4.5rem 0 0;   /* clears the fixed running head */
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--ink-3);
}
.back:hover { color: var(--ink); }

.app-cover { min-height: 92svh; display: grid; align-content: center; padding: 4rem 0; }
.app-cover .icon { width: 5.5rem; height: 5.5rem; border-radius: 24%; margin-bottom: 2rem;
  box-shadow: 0 0 0 1px rgba(255,255,255,.12), 0 24px 60px rgba(0,0,0,.9); overflow: hidden; }
.app-cover .icon img { width: 100%; height: 100%; object-fit: cover; }
.app-cover .icon.mono { display: grid; place-items: center; background: var(--accent); color: #000; font-family: var(--display); font-size: 2.2rem; }
.app-cover h1 { font-size: clamp(3rem, 9vw, 6.5rem); line-height: .95; letter-spacing: -.025em; }
.app-cover .tag { font-size: clamp(1.1rem, 2.4vw, 1.6rem); margin-top: 1.25rem; max-width: 24ch; }

.section { padding: clamp(4rem, 10vh, 7rem) 0; }
.section .display { max-width: 18ch; margin-bottom: 1.75rem; }

.feature { padding: 1.1rem 0; border-top: 1px solid var(--hairline); }
.feature h4 { font-size: 1rem; font-weight: 600; margin: 0 0 .3rem; font-family: var(--sans); }
.feature p { margin: 0; font-size: .9063rem; line-height: 1.55; color: var(--ink-2); }

.two { display: grid; gap: clamp(2rem, 5vw, 4rem); grid-template-columns: 1fr; }
@media (min-width: 56rem) { .two { grid-template-columns: 1.05fr 1fr; } }

.shots { display: flex; gap: 1.25rem; overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: 1.5rem; margin: 0 calc(50% - 50vw);
  padding-inline: max(1.5rem, calc(50vw - var(--page) / 2)); }
.shots::-webkit-scrollbar { height: 6px; }
.shots::-webkit-scrollbar-thumb { background: var(--hairline); border-radius: 3px; }
.shots img {
  width: 16rem; height: auto; flex: none; scroll-snap-align: start;
  aspect-ratio: 1206 / 2622; border-radius: 1.6rem;
  box-shadow: 0 0 0 1px rgba(255,255,255,.14), 0 26px 70px rgba(0,0,0,.9);
}
@media (max-width: 34rem) { .shots img { width: 12.5rem; border-radius: 1.25rem; } }

.tech { padding: 1.1rem 0; border-top: 1px solid var(--hairline); display: grid; gap: .35rem 1.5rem; }
@media (min-width: 44rem) { .tech { grid-template-columns: 12rem 1fr; } }
.tech .name { font-size: 1rem; font-weight: 600; }
.tech .name .sub { display: block; font-family: var(--mono); font-size: .625rem; letter-spacing: .18em; color: var(--accent-ink); margin-top: .25rem; text-transform: uppercase; }
.tech .how { font-size: .9063rem; line-height: 1.55; color: var(--ink-2); }

.store-link {
  display: inline-flex; align-items: center; gap: .6rem; margin-top: 2rem;
  background: #fff; color: #000; padding: .8rem 1.4rem; border-radius: 999px;
  font-size: .9375rem; font-weight: 500;
  transition: transform .3s var(--ease);
}
.store-link:hover { transform: translateY(-2px); }

/* ---------- footer ---------- */
footer { padding: clamp(5rem, 12vh, 9rem) 0 4rem; border-top: 1px solid var(--hairline); }
footer h2 { font-size: clamp(2.2rem, 7vw, 4rem); letter-spacing: -.02em; margin-bottom: 1.5rem; }
.mail { font-family: var(--display); font-size: clamp(1.4rem, 4vw, 2.4rem); position: relative; overflow-wrap: anywhere; }
.mail::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: left;
  transition: transform .55s var(--ease);
}
.mail:hover::after { transform: scaleX(1); }
.colophon { margin-top: 4rem; color: var(--ink-3); font-family: var(--mono); font-size: .625rem;
  letter-spacing: .16em; text-transform: uppercase; display: flex; flex-wrap: wrap; gap: .6rem 2rem; }

/* ---------- legal pages ---------- */
.legal { padding: 3rem 0 6rem; }
.legal .prose { max-width: 40rem; }
.legal h1 { font-size: clamp(2rem, 6vw, 3rem); margin-bottom: .5rem; }
.legal h2 { font-size: 1.3rem; margin: 2.5rem 0 .6rem; font-family: var(--sans); font-weight: 600; }
.legal p, .legal li { color: var(--ink-2); font-size: .9688rem; }
.legal ul { padding-left: 1.1rem; }
.legal li { margin-bottom: .4rem; }
.legal .updated { color: var(--ink-3); font-size: .75rem; font-family: var(--mono); }
.legal .contact-box { margin-top: 3rem; padding: 1.25rem 1.5rem; border: 1px solid var(--hairline); border-radius: .75rem; }
.legal a[href^="mailto"], .legal table a {
  color: var(--ink); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 3px;
}

/* ---------- reveal ---------- */
.js .reveal { opacity: 0; transform: translateY(20px); }
.js .reveal.in { opacity: 1; transform: none; transition: opacity .8s var(--ease), transform .8s var(--ease); transition-delay: var(--d, 0ms); }

/* The sticky mechanic needs a stage that fits. One column, or a short viewport, and it
   does not: the stage grows past 100svh and the component slides under the copy. */
@media (max-width: 55.99rem), (max-height: 46rem) {
  .panel { min-height: 0; }
  .panel .stage { position: static; min-height: 0; padding: 4rem 0; }
}
/* Wide but short (a landscape phone) still wants the two-column layout. */
@media (min-aspect-ratio: 3/2) and (min-width: 40rem) {
  .panel .grid { grid-template-columns: 1fr 1fr; }
  .panel.flip .art { order: -1; }
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal, .js .reveal.in { opacity: 1; transform: none; transition: none; }
  .panel .copy > * { opacity: 1; transform: none; }
  .panel .stage { position: static; min-height: 0; }
  .panel .stage::before { opacity: .5; }
  .c-device img { transform: none; opacity: 1; }
  /* Each component pins itself: --p resolves to 1 below, which is their finished state. */
  .panel { --p: 1 !important; min-height: 0; }
  .starfield i, .scrollcue::after, .c-wave .fill::after { animation: none; }
}
