/* Paper tool site - reusable stylesheet.
   Order matters: structure, then the Paper theme, then the screen effects
   that sit on top of everything. Every colour lives in the theme block, so
   restyling means editing tokens, not rules. */

/* ============ 1. structure ============ */
/* Zero colour decisions live in this section: the theme block supplies them. */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--size-body, 17px);
  font-weight: var(--w-body, 400);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 { margin: 0; font-family: var(--font-display); }
figure { margin: 0; }   /* the UA sheet puts 40px side margins on figures */
p { margin: 0; }
a { color: inherit; }
svg { display: block; }

::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.wrap { max-width: var(--wrap, 1180px); margin: 0 auto; padding: 0 28px; }

/* ---------- micro label ---------- */

.eyebrow {
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- nav ---------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--nav-bg);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(12px);
}
.nav .wrap {
  height: var(--nav-h, 66px);
  display: flex;
  align-items: center;
  gap: 20px;
}
.nav-brand {
  font-family: var(--font-display);
  font-weight: var(--w-brand, 600);
  font-size: 16px;
  letter-spacing: -0.01em;
  text-decoration: none;
}
.nav-brand span { color: var(--muted); font-weight: 400; }
.nav-links { margin-left: auto; display: flex; gap: 4px; }
.nav-links a {
  font-size: 14.5px;
  color: var(--muted);
  text-decoration: none;
  padding: 7px 12px;
  border-radius: var(--r-pill);
}
.nav-links a:hover { color: var(--fg); background: var(--surface); }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: var(--btn-pad, 13px 22px);
  border-radius: var(--r-btn, 999px);
  border: 1px solid transparent;
  font-family: var(--font-body);
  font-size: 15.5px;
  font-weight: var(--w-btn, 500);
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
}
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { opacity: 0.88; }

.btn-ghost { background: transparent; color: var(--fg); border-color: var(--line-strong); }
.btn-ghost:hover { background: var(--surface); }

.btn-meta {
  font-family: var(--font-mono);
  font-size: 11.5px;
  opacity: 0.72;
}

/* ---------- hero ---------- */

.hero { padding: var(--hero-pad, 108px 0 64px); position: relative; }

.hero h1 {
  font-size: var(--h1, clamp(42px, 6.6vw, 88px));
  font-weight: var(--w-h1, 500);
  letter-spacing: var(--ls-h1, -0.035em);
  line-height: var(--lh-h1, 1.02);
  max-width: 15ch;
}
.hero h1 em { font-style: normal; color: var(--accent); }

.hero-sub {
  margin-top: 22px;
  max-width: 46ch;
  font-size: var(--sub, 18px);
  color: var(--muted);
}

.hero-cta { margin-top: 32px; display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }

.hero-note {
  margin-top: 18px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

/* ---------- features ---------- */

.features { padding: 0 0 96px; }

.features h2,
.band h2 {
  font-size: var(--h2, clamp(26px, 3.4vw, 38px));
  font-weight: var(--w-h2, 500);
  letter-spacing: var(--ls-h2, -0.025em);
  line-height: 1.1;
}

.feat-head { margin-bottom: 34px; }
.feat-head p { margin-top: 14px; color: var(--muted); max-width: 56ch; }

.grid3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap, 18px);
}

.feat {
  padding: var(--card-pad, 26px);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--card-bg);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.feat h3 {
  font-size: 17px;
  font-weight: var(--w-h3, 500);
  letter-spacing: -0.012em;
}
.feat p { font-size: 14.5px; color: var(--muted); }

.feat-mark {
  width: 34px;
  height: 34px;
  margin-bottom: 6px;
  border-radius: var(--r-mark, 10px);
  display: grid;
  place-items: center;
  background: var(--mark-bg);
  color: var(--mark-fg);
}
.feat-mark svg { width: 17px; height: 17px; }

/* ---------- keys row ---------- */

.keys {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  padding: 22px 0 0;
  margin-top: 34px;
  border-top: 1px solid var(--line);
}
.keys span {
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--muted);
}
.keys b {
  color: var(--fg);
  font-weight: 500;
  margin-right: 7px;
}

/* ---------- closing band ---------- */

.band {
  padding: var(--band-pad, 86px 0 96px);
  border-top: 1px solid var(--line);
  background: var(--band-bg, transparent);
}
.band .wrap { display: flex; flex-direction: column; align-items: var(--band-align, flex-start); gap: 24px; }
.band p { color: var(--muted); max-width: 48ch; }
.band-row { display: flex; flex-wrap: wrap; gap: 12px; }

/* ---------- footer ---------- */

.foot {
  border-top: 1px solid var(--line);
  padding: 22px 0;
  font-size: 13.5px;
  color: var(--muted);
}
.foot .wrap { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.foot a { color: var(--fg); text-decoration: none; }

@media (max-width: 900px) {
  .grid3 { grid-template-columns: 1fr; }
  .hero { padding-top: 72px; }
}
@media (max-width: 620px) {
  .wrap { padding: 0 20px; }
  /* the nav must stay on one line: drop the owner suffix and tighten the
     links before the last item can overflow and get clipped */
  .nav-brand span { display: none; }
  .nav-links { gap: 0; }
  .nav-links a { padding: 7px 8px; font-size: 13.5px; }

  .features { padding-bottom: 64px; }
}

/* ---------- media slots (gif / video per feature) ----------
   The <video> sits on top of a placeholder. Drop a file in and it covers the
   placeholder; leave it out and the slot still reserves the exact space, so
   the layout never shifts when the real footage lands. */

.shot {
  position: relative;
  margin: 0;
  aspect-ratio: var(--shot-ratio, 16 / 10);
  border-radius: var(--r-card);
  border: 1px solid var(--line);
  background: var(--shot-bg, var(--surface));
  overflow: hidden;
}

.shot-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  background: var(--shot-bg, var(--surface));
}

.shot-ph {
  position: absolute;
  inset: 8px;
  display: grid;
  place-content: center;
  gap: 5px;
  text-align: center;
  padding: 16px;
  border: 1px dashed var(--line-strong);
  border-radius: calc(var(--r-card) - 4px);
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 9px,
    var(--surface) 9px 18px
  );
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}
.shot-ph b { display: block; font-weight: 500; color: var(--fg); font-size: 12.5px; }
.shot-ph span { font-size: 11px; opacity: 0.8; }
.shot.has-media .shot-ph { display: none; }

figcaption.shot-cap {
  margin-top: 12px;
  font-size: 13.5px;
  color: var(--muted);
}

/* ---------- feature rows ---------- */

.row2 {
  display: grid;
  grid-template-columns: var(--row-cols, 1fr 1.15fr);
  gap: var(--row-gap, 54px);
  align-items: center;
  padding-bottom: var(--row-pad, 96px);
}
/* the media column stays the wider one whichever side it sits on */
.row2.flip { --row-cols: 1.15fr 1fr; }
.row2.flip > .row2-copy { order: 2; }
.row2-copy h2 { margin-bottom: 16px; }
.row2-copy p { color: var(--muted); max-width: 46ch; }
.row2-copy .eyebrow { display: block; margin-bottom: 14px; }

.two-up {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--row-gap, 54px);
  padding-bottom: var(--row-pad, 96px);
}
.two-up h3 {
  font-size: 20px;
  font-weight: var(--w-h2, 500);
  letter-spacing: -0.02em;
  margin: 18px 0 8px;
}
.two-up p { color: var(--muted); font-size: 15px; max-width: 40ch; }

.wide {
  padding-bottom: var(--row-pad, 96px);
  text-align: center;
}
.wide .wide-copy { max-width: 58ch; margin: 0 auto 32px; }
.wide .wide-copy p { color: var(--muted); margin-top: 14px; }

@media (max-width: 900px) {
  .row2, .two-up { grid-template-columns: 1fr; gap: 26px; }
  .row2.flip > .row2-copy { order: 0; }
  .row2 { --row-pad: 62px; }
  .two-up { --row-pad: 62px; }
  .wide { --row-pad: 62px; }
}

/* ---------- skip link ---------- */

.skip {
  position: absolute;
  left: 16px;
  top: -60px;
  z-index: 300;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 10px 18px;
  border-radius: var(--r-btn, 8px);
  font-weight: 500;
  text-decoration: none;
  transition: top 0.2s ease;
}
.skip:focus-visible { top: 12px; }

/* ---------- install steps ---------- */

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap, 18px);
}
.steps li {
  padding: var(--card-pad, 26px);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--card-bg);
}
.step-n {
  display: block;
  margin-bottom: 14px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}
.steps h3 {
  font-size: 17px;
  font-weight: var(--w-h3, 600);
  letter-spacing: -0.012em;
  margin-bottom: 8px;
}
.steps p { font-size: 14.5px; color: var(--muted); }
.steps strong { color: var(--fg); font-weight: 600; }

/* ---------- faq ---------- */

.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 18px 2px;
  font-size: 16.5px;
  font-weight: var(--w-h3, 600);
  letter-spacing: -0.01em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  flex: none;
  width: 11px;
  height: 11px;
  border-right: 1.6px solid var(--muted);
  border-bottom: 1.6px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.22s ease;
}
.faq details[open] summary::after { transform: rotate(-135deg) translate(-3px, -3px); }
.faq-body { padding: 0 40px 20px 2px; }
.faq-body p { font-size: 15px; color: var(--muted); max-width: 64ch; }
.faq strong { color: var(--fg); font-weight: 600; }

@media (max-width: 900px) {
  .steps { grid-template-columns: 1fr; }
}

/* ============ 2. paper theme ============ */
/* PAPER - warm light ground, ink type, hairline grid, mono labels.
   The precise, technical, documentation-adjacent register. */

:root {
  color-scheme: light;

  --bg: #f4f2ee;
  --fg: #16161a;
  --muted: #6a6a72;
  --line: rgba(22, 22, 26, 0.13);
  --line-strong: rgba(22, 22, 26, 0.34);
  --surface: rgba(22, 22, 26, 0.035);
  --card-bg: #fbfaf8;
  --nav-bg: rgba(244, 242, 238, 0.82);

  --accent: #16161a;
  --accent-ink: #fbfaf8;
  --mark-bg: #16161a;
  --mark-fg: #fbfaf8;

  --font-display: "Inter Tight", system-ui, sans-serif;
  --font-body: "Inter Tight", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;

  --h1: clamp(40px, 5.4vw, 68px);
  --w-h1: 500;
  --ls-h1: -0.033em;
  --lh-h1: 1.04;
  --h2: clamp(25px, 3vw, 34px);
  --w-h2: 500;
  --w-h3: 600;
  --sub: 18px;

  --r-card: 10px;
  --r-btn: 8px;
  --r-pill: 8px;
  --r-mark: 8px;
  --btn-pad: 13px 20px;
  --hero-pad: 112px 0 60px;
  --wrap: 1120px;
}

.ss {
  --ss-desk: #22242b;
  --ss-win: rgba(255, 255, 255, 0.08);
  --ss-win-line: rgba(255, 255, 255, 0.18);
  --ss-bar-bg: #14151a;
  --ss-bar-fg: #f4f4f6;
  --ss-accent: #4c86e0;
  --ss-note: #ffe9a8;
  --ss-note-ink: #211c08;
  --ss-radius: 8px;
}

/* faint ruled grid, the technical-paper tell */
body {
  background-image:
    linear-gradient(rgba(22, 22, 26, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(22, 22, 26, 0.045) 1px, transparent 1px);
  background-size: 100% 46px, 46px 100%;
}

.nav { backdrop-filter: blur(10px); }
.feat { box-shadow: 0 1px 2px rgba(22, 22, 26, 0.05); }
.band { --band-bg: #fbfaf8; }
.hero h1 { max-width: 16ch; }

/* screen-effect tuning for a light ground: grain multiplies into the paper
   like tooth, the vignette stays warm rather than grey */
:root {
  --grain-blend: multiply;
  --grain-subtle: 0.17;
  --grain-medium: 0.30;
  --grain-strong: 0.48;
  --scan-line: rgba(22, 22, 26, 0.55);
  --scan-gap: 3px;
  --scan-opacity: 0.13;
  --vig-color: rgba(58, 48, 32, 0.30);
}

/* ============ 3. grain, scanlines, vignette ============ */
/* Screen effects: film grain, scanlines, vignette.
   One fixed, pointer-events-none layer stack. Never attached to a scrolling
   container, so the GPU repaints it once instead of on every scroll frame.
   Caddis renders its grain inside a WebGL shader; this is the CSS equivalent. */

.fx {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  contain: strict;
}

.fx > i {
  position: absolute;
  inset: -60px;          /* overscan so the grain jitter never shows an edge */
  display: block;
  opacity: 0;
  transition: opacity 0.25s ease;
}

/* ---- grain ---------------------------------------------------------- */

.fx-grain {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 220px 220px;
  mix-blend-mode: var(--grain-blend, multiply);
}

body[data-grain="subtle"] .fx-grain { opacity: var(--grain-subtle, 0.22); }
body[data-grain="medium"] .fx-grain { opacity: var(--grain-medium, 0.36); }
body[data-grain="strong"] .fx-grain { opacity: var(--grain-strong, 0.55); }

/* film jitter: shift the tile in whole steps, cheap and only transform-based */
@media (prefers-reduced-motion: no-preference) {
  body[data-grain-move="1"] .fx-grain {
    animation: fx-jitter 0.7s steps(1) infinite;
  }
}
@keyframes fx-jitter {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-13px, 8px); }
  40%  { transform: translate(9px, -14px); }
  60%  { transform: translate(-7px, -6px); }
  80%  { transform: translate(14px, 11px); }
}

/* ---- scanlines ------------------------------------------------------ */

.fx-scan {
  background-image: repeating-linear-gradient(
    to bottom,
    var(--scan-line, rgba(0, 0, 0, 0.5)) 0 1px,
    transparent 1px var(--scan-gap, 3px)
  );
}
body[data-scan="1"] .fx-scan { opacity: var(--scan-opacity, 0.16); }

/* ---- vignette ------------------------------------------------------- */

.fx-vig {
  inset: 0;
  background: radial-gradient(
    125% 95% at 50% 50%,
    transparent 52%,
    var(--vig-color, rgba(20, 18, 14, 0.36)) 100%
  );
}
body[data-vignette="1"] .fx-vig { opacity: 1; }

/* ============ 4. chromatic aberration ============ */
/* Chromatic aberration tied to scroll position.

   Clean in the middle of the screen, splitting further apart the further a
   thing travels from centre. Because the value comes from the view() scroll
   timeline, it only means anything for content that actually moves through
   the viewport, so it reads as motion rather than as a static smudge.

   Sticky and fixed furniture is deliberately excluded: the nav never leaves
   centre, so a permanent fringe on it would look like a rendering fault.

   --ca is a registered property so it can interpolate. Without @property or
   scroll timelines it falls back to a faint constant split, and failing that
   resolves to 0px and renders nothing. Paragraph text is never targeted:
   fringed body copy is harder to read and costs more to repaint. */

@property --ca {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

:root {
  --ca-red: rgba(255, 26, 88, 0.8);
  --ca-cyan: rgba(0, 170, 255, 0.8);
}

/* peak split, reached at the very top and bottom of the viewport */
body { --ca-max: 0px; --ca-box-max: calc(var(--ca-max) * 1.6); }
body[data-ca="low"]    { --ca-max: 0.9px; }
body[data-ca="medium"] { --ca-max: 1.4px; }
body[data-ca="strong"] { --ca-max: 2.2px; }

/* dead centre is clean, so the split reads as something that happens
   while scrolling rather than a permanent treatment */
@keyframes ca-lens {
  0%   { --ca: var(--ca-max); }
  32%  { --ca: 0px; }
  68%  { --ca: 0px; }
  100% { --ca: var(--ca-max); }
}

/* Panels and buttons run the curve backwards: strongest as they cross the
   middle of the screen, resolving to clean at the top and bottom edges. No
   text lives on these boxes, so there is nothing to make harder to read. */
@keyframes ca-lens-inv {
  0%   { --ca: 0px; }
  50%  { --ca: var(--ca-box-max); }
  100% { --ca: 0px; }
}

/* Scrolling content only. Nothing here lives inside the sticky nav. */
main :is(h1, h2, h3, .eyebrow, .keys b, .btn-meta) {
  text-shadow:
    calc(-1 * var(--ca) * var(--ca-scale, 1)) 0 0 var(--ca-red),
    calc(var(--ca) * var(--ca-scale, 1)) 0 0 var(--ca-cyan);
}

/* small type reads worst under a fixed pixel split, so it gets a fraction */
main :is(.eyebrow, .keys b, .btn-meta) { --ca-scale: 0.5; }
main :is(h3) { --ca-scale: 0.75; }

main :is(.btn, .shot, .feat-mark) {
  box-shadow:
    calc(-1 * var(--ca)) 0 0 var(--ca-red),
    var(--ca) 0 0 var(--ca-cyan);
}

/* Faint constant split where scroll timelines are unavailable, or for anyone
   on reduced motion. Where the timeline works, the animation outranks this. */
body[data-ca="low"]    main :is(h1, h2, h3, .eyebrow, .keys b, .btn-meta, .btn, .shot, .feat-mark) { --ca: 0.35px; }
body[data-ca="medium"] main :is(h1, h2, h3, .eyebrow, .keys b, .btn-meta, .btn, .shot, .feat-mark) { --ca: 0.55px; }
body[data-ca="strong"] main :is(h1, h2, h3, .eyebrow, .keys b, .btn-meta, .btn, .shot, .feat-mark) { --ca: 0.8px; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    body:not([data-ca="off"]) main :is(
      h1, h2, h3, .eyebrow, .keys b, .btn-meta
    ) {
      animation: ca-lens linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    body:not([data-ca="off"]) main :is(.btn, .shot, .feat-mark) {
      animation: ca-lens-inv linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
  }
}

/* ============================================================
   HUB ONLY - the tool index
   Rows of a printed catalogue: hairline rule, mono index number,
   the name, one line of what it is, and the way in. No cards:
   this is a list of things, not a grid of products.
   ============================================================ */

.index { border-top: 1px solid var(--line); }

.entry {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) minmax(0, 286px);
  gap: 30px;
  align-items: start;
  padding: 38px 0;
  border-bottom: 1px solid var(--line);
}

.entry-n {
  font-family: var(--font-mono);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--muted);
  padding-top: 9px;
}

.entry-main h3 {
  font-size: clamp(23px, 2.7vw, 31px);
  font-weight: 500;
  letter-spacing: -0.022em;
  line-height: 1.06;
}
.entry-main > p {
  margin-top: 12px;
  color: var(--muted);
  max-width: 50ch;
}

.entry-meta {
  margin-top: 18px;
  display: flex;
  flex-wrap: wrap;
  gap: 7px 20px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

/* the media slot fills the column; the tag and the button must not, or
   they stretch into bars that read as headers rather than controls */
.entry-side { display: flex; flex-direction: column; gap: 14px; }
.entry-side > .shot { align-self: stretch; }
.entry-side > .tag,
.entry-side > .btn { align-self: flex-start; }

/* status tag. Ink fill means you can use it today. */
.tag {
  display: inline-block;
  margin-bottom: 14px;
  padding: 4px 9px 3px;
  border-radius: 5px;
  border: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}
.tag-live {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

@media (max-width: 900px) {
  .entry {
    grid-template-columns: 40px minmax(0, 1fr);
    gap: 8px 20px;
    padding: 30px 0;
  }
  .entry-side { grid-column: 2; margin-top: 4px; }
}

/* Empty media slots sit on a faintly inked ground, which drags the
   placeholder caption to 4.47:1 against it. Nudge it back over 4.5.
   Only affects the placeholder state, never real footage. */
.shot-ph,
.shot-ph span { color: #63636b; }
