/* ============================================================
   Scrollmark Open Source — scrollmark.github.io
   Sticker Energy design system. Tokens mirror
   platform/socialgpt/frontend/app/globals.css — keep in sync
   with that file, don't invent new values here.
   ============================================================ */

/* ── Tokens ── */
:root {
  color-scheme: light;

  --font-display: "Bricolage Grotesque", sans-serif;
  --font-body: "Hanken Grotesk", ui-sans-serif, system-ui, sans-serif;
  --font-annotation: "Shantell Sans", cursive;
  --font-mono: "Fragment Mono", ui-monospace, monospace;

  /* Sticker Energy core */
  --paper: #fffdf8;
  --ink: #211633;
  /* Constant dark ink for text/icons on saturated accent fills — never flips */
  --plum: #211633;
  --se-pink: #ff4d8d;
  /* Darkened from #7b5bff, where white was 4.39:1 and ink 3.90:1 — neither
     reached AA. At #7556f2 white is 4.79:1. */
  --se-violet: #7556f2;
  --se-lime: #c5f53c;
  --se-sky: #53cdff;
  --se-tangerine: #ff8a3d;
  --se-butter: #ffd43f;
  --pink-tint: #ffe9f2;
  --violet-tint: #efeaff;
  --lime-tint: #f3fbdc;
  --sky-tint: #e4f7ff;
  --butter-tint: #fff5d6;
  --tangerine-tint: #ffebdc;

  /* Readable text companions for saturated tokens */
  --se-lime-text: #51740a;
  --se-sky-text: #0e6e80;
  --se-tangerine-text: #b35309;
  --se-butter-text: #7a6310;
  --se-pink-text: #d61f66;
  --se-violet-text: #5a42c4;

  --background: var(--paper);
  --foreground: var(--ink);
  --card: #ffffff;
  --border: rgba(33, 22, 51, 0.1);
  --border-hover: rgba(33, 22, 51, 0.18);
  --ring: var(--se-violet);

  --text-primary: rgba(33, 22, 51, 0.92);
  --text-secondary: rgba(33, 22, 51, 0.7);
  /* 0.52 was 3.52:1 on paper — under AA. 0.62 is 4.82:1. This token
     dresses mini-note, skill-when, inline code, the footer and the card
     sub-labels, so the whole muted tier moves with it. */
  --text-tertiary: rgba(33, 22, 51, 0.62);

  --shadow-sticker: 0 2px 0 rgba(33, 22, 51, 0.16), 0 10px 24px rgba(33, 22, 51, 0.12);
  --shadow-card: 0 1px 2px rgba(33, 22, 51, 0.06), 0 16px 40px rgba(33, 22, 51, 0.1);
  --shadow-lift: 0 6px 0 rgba(33, 22, 51, 0.12), 0 24px 48px rgba(33, 22, 51, 0.16);

  --ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);

  --radius-card: 1.25rem;
  --radius-media: 1.75rem;
}

.dark {
  color-scheme: dark;

  /* Ink-canvas variant — warm plum surfaces, never neutral black */
  --background: #16101f;
  --foreground: rgba(255, 253, 248, 0.92);
  --card: #1d1430;
  --border: rgba(255, 253, 248, 0.1);
  --border-hover: rgba(255, 253, 248, 0.16);
  --ring: #937bff;

  --pink-tint: rgba(255, 77, 141, 0.16);
  --violet-tint: rgba(123, 91, 255, 0.18);
  --lime-tint: rgba(197, 245, 60, 0.14);
  --sky-tint: rgba(83, 205, 255, 0.14);
  --butter-tint: rgba(255, 212, 63, 0.14);
  --tangerine-tint: rgba(255, 138, 61, 0.16);

  --se-lime-text: #d3f76a;
  --se-sky-text: #7fdbff;
  --se-tangerine-text: #ffa666;
  --se-butter-text: #ffe070;
  --se-pink-text: #ff7fae;
  --se-violet-text: #ab98ff;

  --text-primary: rgba(255, 253, 248, 0.92);
  --text-secondary: rgba(255, 253, 248, 0.62);
  /* 0.42 was 4.04:1 on #16101f — under AA. 0.52 is ~5.4:1. */
  --text-tertiary: rgba(255, 253, 248, 0.52);

  --shadow-sticker: 0 2px 0 rgba(0, 0, 0, 0.35), 0 10px 24px rgba(0, 0, 0, 0.3);
  --shadow-card: 0 4px 12px rgba(0, 0, 0, 0.25);
  --shadow-lift: 0 8px 24px rgba(0, 0, 0, 0.3);
}

/* ── Base ── */
* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--background);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--se-violet-text);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Theme switch cross-fade (brief class added on <html> during toggle) */
.theme-fade body,
.theme-fade body * {
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

/* Paper grain — decoration only */
.paper-grain {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0.04;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

.dark .paper-grain {
  mix-blend-mode: screen;
  opacity: 0.03;
}

.wrap {
  max-width: 1140px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ── Typography ── */
h1,
h2,
h3 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  color: var(--text-primary);
  margin: 0;
}

.display {
  font-size: clamp(2.8rem, 7vw, 5.5rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 0.98;
}

.heading-2 {
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

.heading-3 {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.eyebrow {
  font-weight: 600;
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 0 0 12px;
}

.annotation {
  font-family: var(--font-annotation);
  font-weight: 500;
}

.sub {
  color: var(--text-secondary);
  max-width: 38rem;
}

/* ── Icons ── */
.icon {
  width: 1.05em;
  height: 1.05em;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: -0.15em;
}

/* ── Sticker (the signature element) ── */
.sticker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 999px;
  border: 3px solid #fff;
  box-shadow: var(--shadow-sticker);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--plum);
  transform: rotate(var(--tilt, -2deg));
  transition: transform 0.15s ease-out;
}

.sticker:hover {
  transform: rotate(0deg) scale(1.05);
}

.sticker--lime { background: var(--se-lime); }
.sticker--sky { background: var(--se-sky); }
.sticker--butter { background: var(--se-butter); }
/* Ink, not white: white on this pink is 3.14:1, ink is 5.46:1. --plum is
   :root-only and never re-themed, so it stays ink in the dark too. */
.sticker--pink { background: var(--se-pink); color: var(--plum); }
.sticker--violet { background: var(--se-violet); color: #fff; }
/* Tangerine reached the palette, the card variants and the text tokens, but
   never got a sticker. `.chip` carries NO background of its own, so the two
   tangerine chips in skills.html rendered as bare text. Ink, not white:
   #211633 on #ff8a3d is 7.3:1, white is 2.35:1. */
.sticker--tangerine { background: var(--se-tangerine); }

/* `.chip` is defined below and sets ink, which silently beat the white these
   two variants ask for — same specificity, later in the file. Violet needs
   white to pass, so it says so at chip specificity as well. */
/* `.fact-chip` is a separate class that also lands after the variants and
   also sets its own colour, so it needs naming too — catching only `.chip`
   left the one fact-chip on the home and MCP pages at 3.56:1. */
.chip.sticker--violet,
.fact-chip.sticker--violet { color: #fff; }

/* Small code-name chips inside cards */
.chip {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 999px;
  border: 2px solid #fff;
  box-shadow: var(--shadow-sticker);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--plum);
  transform: rotate(var(--tilt, -1.5deg));
  transition: transform 0.15s ease-out;
}

.chip:hover {
  transform: rotate(0deg) scale(1.04);
}

/* ── Buttons ── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 16px 28px;
  min-height: 44px;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.18s ease-out, box-shadow 0.18s ease-out;
}

.btn:hover {
  text-decoration: none;
}

.btn--primary {
  background: var(--ink);
  color: var(--paper);
  border: none;
}

.dark .btn--primary {
  /* Buttons are ink-colored objects — keep them plum-on-paper in the dark */
  background: var(--plum);
  color: #fffdf8;
  border: 2px solid rgba(255, 253, 248, 0.25);
}

.btn--primary:hover {
  transform: translateY(-2px);
  box-shadow: 4px 4px 0 var(--se-pink);
}

.btn--secondary {
  background: transparent;
  color: var(--text-primary);
  border: 2px solid var(--foreground);
}

.btn--secondary:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-sticker);
}

/* ── Nav (the nav is a sticker too) ── */
.nav {
  position: sticky;
  top: 16px;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 1140px;
  margin: 16px auto 0;
  padding: 8px 10px 8px 16px;
  border-radius: 999px;
  border: 2px solid var(--foreground);
  background: color-mix(in srgb, var(--background) 86%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 3px 0 color-mix(in srgb, var(--foreground) 60%, transparent);
}

@media (max-width: 1188px) {
  .nav {
    margin-left: 24px;
    margin-right: 24px;
  }
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--text-primary);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: -0.02em;
}

.brand:hover {
  text-decoration: none;
}

.brand .mark {
  /* A reserved square box with object-fit, NOT this asset's own ratio. The
     previous rule hardcoded 355/261 from the old mark's viewBox, so the
     moment the mark was replaced with a square one it rendered as an oval —
     a rule that was right for one file and silently wrong for the next.
     A square box holds the space either way and letterboxes anything wide. */
  width: 34px;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  display: block;
  transition: transform 0.28s var(--ease-pop);
}

.brand:hover .mark {
  transform: translateY(-2px) rotate(-6deg) scale(1.08);
}

.brand .oss-tag {
  padding: 2px 10px;
  font-size: 0.7rem;
  border-width: 2px;
  box-shadow: 0 1px 0 rgba(33, 22, 51, 0.16), 0 4px 10px rgba(33, 22, 51, 0.12);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}

.nav-links a {
  padding: 10px 14px;
  border-radius: 999px;
  color: var(--text-secondary);
  font-weight: 600;
  font-size: 0.95rem;
}

.nav-links a:hover {
  color: var(--text-primary);
  background: var(--butter-tint);
  text-decoration: none;
}

@media (max-width: 560px) {
  .nav-links a.nav-optional,
  .brand .oss-tag {
    display: none;
  }

  .nav {
    padding: 6px 8px 6px 12px;
  }

  .brand {
    font-size: 1rem;
    gap: 8px;
  }

  .nav-links a {
    padding: 10px 10px;
    font-size: 0.9rem;
  }
}

/* Theme switch — a three-way sticker: system / light / dark. The knob
   slides behind the active segment (sky → butter → violet). */
.theme-switch {
  --segw: 32px;
  --step: calc(var(--segw) + 2px); /* segment width + gap */
  position: relative;
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  flex-shrink: 0;
  border-radius: 999px;
  border: 2px solid var(--foreground);
  background: var(--card);
  box-shadow: var(--shadow-sticker);
  transform: rotate(-2deg);
}

.theme-switch::before {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: var(--segw);
  height: var(--segw);
  border-radius: 999px;
  background: var(--se-sky); /* system */
  transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.5, 1), background-color 0.28s;
}

.theme-switch.pos-light::before {
  transform: translateX(var(--step));
  background: var(--se-butter);
}

.theme-switch.pos-dark::before {
  transform: translateX(calc(var(--step) * 2));
  background: var(--se-violet);
}

.theme-switch button {
  position: relative;
  width: var(--segw);
  height: var(--segw);
  border: none;
  border-radius: 999px;
  background: transparent;
  padding: 0;
  display: grid;
  place-items: center;
  color: var(--text-tertiary);
  cursor: pointer;
}

.theme-switch button[aria-checked="true"] {
  color: var(--plum);
}

.theme-switch.pos-dark button[aria-checked="true"] {
  color: #fff;
}

.theme-switch button .icon {
  width: 16px;
  height: 16px;
}

@media (max-width: 560px) {
  .theme-switch {
    --segw: 26px;
  }

  .theme-switch button .icon {
    width: 14px;
    height: 14px;
  }
}

@media (max-width: 420px) {
  .nav {
    margin-left: 12px;
    margin-right: 12px;
  }

  .nav-links a {
    padding: 10px 8px;
    font-size: 0.85rem;
  }
}

/* ── Hero ── */
.hero {
  position: relative;
  padding: clamp(72px, 10vw, 128px) 0 clamp(56px, 8vw, 96px);
  overflow: clip;
}

.hero-washes {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.hero-washes span {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  opacity: 0.55;
}

.hero-washes .w1 { width: 480px; height: 380px; top: -120px; left: -80px; background: var(--violet-tint); }
.hero-washes .w2 { width: 420px; height: 360px; top: 40px; right: -100px; background: var(--butter-tint); }
.hero-washes .w3 { width: 360px; height: 300px; bottom: -140px; left: 34%; background: var(--sky-tint); }

.dark .hero-washes span {
  opacity: 0.4;
}

.hero-grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 48px;
  align-items: center;
}

@media (max-width: 860px) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.hero .eyebrow {
  color: var(--se-pink-text);
}

.hero .sub {
  margin: 24px 0 32px;
  font-size: 1.15rem;
}

.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

.hero-ctas .annotation {
  color: var(--text-tertiary);
  font-size: 1rem;
}

/* Marker underline under 1–2 headline words — SVG, never text-decoration */
.marker {
  position: relative;
  white-space: nowrap;
}

.marker svg {
  position: absolute;
  left: -2%;
  bottom: -0.12em;
  width: 104%;
  height: 0.28em;
  overflow: visible;
}

.marker svg path {
  fill: none;
  stroke: var(--se-butter);
  stroke-width: 10;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  animation: draw-marker 0.6s ease-out 1s forwards;
}

@keyframes draw-marker {
  to { stroke-dashoffset: 0; }
}

/* Hero sticker pile (decoration, aria-hidden) */
.hero-pile {
  position: relative;
  min-height: 300px;
}

.hero-pile .polaroid {
  position: relative;
  margin: 0 auto;
  max-width: 340px;
  background: var(--card);
  border-radius: var(--radius-card);
  padding: 12px;
  box-shadow: var(--shadow-card);
  transform: rotate(2deg);
}

.hero-pile .polaroid .screen {
  border-radius: 14px;
  background:
    radial-gradient(ellipse at 20% 10%, color-mix(in srgb, var(--violet-tint) 70%, transparent), transparent 70%),
    radial-gradient(ellipse at 90% 90%, color-mix(in srgb, var(--pink-tint) 60%, transparent), transparent 70%),
    var(--background);
  border: 1px solid var(--border);
  padding: 22px;
  display: grid;
  gap: 12px;
}

.hero-pile .polaroid .caption {
  padding: 10px 6px 2px;
  color: var(--text-tertiary);
  font-size: 0.95rem;
  text-align: center;
}

.fake-line {
  height: 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--foreground) 12%, transparent);
}

.fake-line.short { width: 62%; }
.fake-line.tiny { width: 38%; }

.fake-bars {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  height: 74px;
  margin-top: 6px;
}

.fake-bars span {
  flex: 1;
  border-radius: 6px 6px 3px 3px;
  background: var(--se-violet);
  opacity: 0.85;
  transform-origin: bottom;
  animation: bar-grow 0.7s ease-out both;
}

.fake-bars span:nth-child(1) { height: 34%; animation-delay: 1s; }
.fake-bars span:nth-child(2) { height: 52%; animation-delay: 1.07s; }
.fake-bars span:nth-child(3) { height: 40%; animation-delay: 1.14s; }
.fake-bars span:nth-child(4) { height: 88%; background: var(--se-pink); animation-delay: 1.21s; }
.fake-bars span:nth-child(5) { height: 64%; animation-delay: 1.28s; }

@keyframes bar-grow {
  from { transform: scaleY(0); }
  to { transform: scaleY(1); }
}

.hero-pile .sticker {
  position: absolute;
}

.hero-pile .s1 { top: -18px; left: 2%; --tilt: -5deg; }
.hero-pile .s2 { bottom: 34px; right: -2%; --tilt: 3deg; animation: float 4s ease-in-out 2s infinite; }
.hero-pile .s3 { bottom: -14px; left: 10%; --tilt: -2deg; }

@keyframes float {
  0%, 100% { transform: rotate(var(--tilt, 3deg)) translateY(0); }
  50% { transform: rotate(var(--tilt, 3deg)) translateY(-6px); }
}

@media (max-width: 860px) {
  .hero-pile {
    max-width: 420px;
  }
}

/* Hero entrance choreography (the one orchestrated moment on this page) */
.rise {
  opacity: 0;
  transform: translateY(20px);
  animation: rise 0.7s ease-out forwards;
}

.rise-1 { animation-delay: 0.05s; }
.rise-2 { animation-delay: 0.15s; }
.rise-3 { animation-delay: 0.3s; }
.rise-4 { animation-delay: 0.42s; }

@keyframes rise {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.pop {
  scale: 0;
  animation: pop 0.55s var(--ease-pop) forwards;
}

.pop-1 { animation-delay: 0.7s; }
.pop-2 { animation-delay: 0.82s; }
.pop-3 { animation-delay: 0.94s; }

@keyframes pop {
  to { scale: 1; }
}

/* ── Sections ── */
.section {
  padding: clamp(72px, 10vw, 128px) 0;
}

.section-head {
  max-width: 40rem;
  margin-bottom: clamp(36px, 5vw, 56px);
}

.section-head .sub {
  margin: 14px 0 0;
}

/* ── Project cards ── */
.projects-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 28px;
}

@media (max-width: 860px) {
  .projects-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.card {
  position: relative;
  border-radius: var(--radius-media);
  padding: clamp(24px, 3.5vw, 40px);
  box-shadow: var(--shadow-card);
  background: var(--card);
  transform: rotate(var(--tilt, 0deg));
  transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
}

.card:hover {
  transform: rotate(0deg) translateY(-4px);
  box-shadow: var(--shadow-lift);
}

/* Tint fill cards on the paper section (layered so translucent dark tints
   still sit on a card surface) */
.card--lime { background: linear-gradient(var(--lime-tint), var(--lime-tint)), var(--card); }
.card--sky { background: linear-gradient(var(--sky-tint), var(--sky-tint)), var(--card); }
.card--violet { background: linear-gradient(var(--violet-tint), var(--violet-tint)), var(--card); }
.card--tangerine { background: linear-gradient(var(--tangerine-tint), var(--tangerine-tint)), var(--card); }
.card--pink { background: linear-gradient(var(--pink-tint), var(--pink-tint)), var(--card); }
.card--butter { background: linear-gradient(var(--butter-tint), var(--butter-tint)), var(--card); }

.card .eyebrow {
  margin-bottom: 8px;
}

.card--lime .eyebrow { color: var(--se-lime-text); }
.card--sky .eyebrow { color: var(--se-sky-text); }
.card--violet .eyebrow { color: var(--se-violet-text); }
.card--tangerine .eyebrow { color: var(--se-tangerine-text); }
.card--pink .eyebrow { color: var(--se-pink-text); }
.card--butter .eyebrow { color: var(--se-butter-text); }

.card p {
  color: var(--text-secondary);
  margin: 12px 0 20px;
}

/* Corner sticker physically attached to the card edge */
.card .corner-sticker {
  position: absolute;
  top: -16px;
  right: 22px;
  font-size: 0.8rem;
  padding: 6px 14px;
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 8px;
  margin: 0 0 22px;
}

.card-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: var(--text-primary);
  margin-top: 18px;
  min-height: 44px;
}

.card-link:hover {
  text-decoration: none;
  color: var(--se-pink-text);
}

/* ── Code blocks (ink objects — constant plum, both themes) ── */
.codebox {
  position: relative;
  background: var(--plum);
  border-radius: 14px;
  padding: 16px 18px;
}

/* The pre scrolls, not the box — so the absolutely-positioned copy
   button stays put while long commands scroll under it */
.codebox pre {
  margin: 0;
  padding-right: 84px;
  overflow-x: auto;
}

/* Inline <code> in prose. Without this the tag falls back to the browser's
   default monospace, so the site carried ~45 copies of an inline
   font-family style to compensate — and the few that lacked it rendered in a
   different mono from their neighbours. One rule; the codebox and matrix set
   their own sizes below and are unaffected. */
code {
  font-family: var(--font-mono);
}

.codebox code {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.55;
  color: #fffdf8;
  white-space: pre;
}

.codebox .code-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 253, 248, 0.5);
  margin-bottom: 8px;
}

.copy-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 4px 14px;
  border-radius: 999px;
  border: 2px solid rgba(255, 253, 248, 0.3);
  background: rgba(255, 253, 248, 0.08);
  color: #fffdf8;
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease-out, transform 0.15s ease-out;
}

.copy-btn:hover {
  background: rgba(255, 253, 248, 0.18);
  transform: translateY(-1px);
}

.copy-btn .icon {
  width: 0.9em;
  height: 0.9em;
}

/* Compact one-liner variant: copy button sits beside the command, not over it */
.codebox--row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.codebox--row pre {
  flex: 1;
  order: 1;
  overflow-x: auto;
  padding-right: 0;
}

.codebox--row .copy-btn {
  position: static;
  order: 2;
  flex-shrink: 0;
}

/* ── Segmented pill tabs (a control — never tilted) ── */
.tabs {
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  border: 2px solid var(--foreground);
  background: var(--card);
  margin-bottom: 14px;
  max-width: 100%;
  overflow-x: auto;
}

.tabs button {
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  padding: 8px 16px;
  min-height: 38px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}

.tabs button.active {
  background: var(--foreground);
  color: var(--background);
}

/* ── Placeholder cards (empty slots on the shelf) ── */
.more-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 28px;
  margin-top: 28px;
}

@media (max-width: 860px) {
  .more-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.placeholder {
  border-radius: var(--radius-media);
  border: 2px dashed var(--border-hover);
  background: transparent;
  box-shadow: none;
  display: grid;
  place-content: center;
  text-align: center;
  gap: 6px;
  min-height: 220px;
  color: var(--text-tertiary);
}

.placeholder:hover {
  box-shadow: none;
  transform: rotate(var(--tilt, 0deg));
}

.placeholder .annotation {
  font-size: 1.25rem;
  color: var(--text-secondary);
}

.placeholder .micro {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
}

/* ── Skill detail cards (skills.html) ── */
.hero--page {
  padding: clamp(48px, 7vw, 88px) 0 clamp(40px, 6vw, 72px);
}

.skill-name {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 1.05rem;
  font-weight: 400;
  letter-spacing: 0;
}

.skill-name .icon {
  width: 22px;
  height: 22px;
}

.skill-when {
  display: block;
  font-size: 0.9rem;
  color: var(--text-tertiary);
  margin: -8px 0 18px;
}

.skill-when strong {
  color: var(--text-secondary);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  margin-right: 6px;
}

/* Install picker on the skills page hero */
.install-box {
  max-width: 46rem;
}

.mini-note {
  font-size: 0.88rem;
  color: var(--text-tertiary);
  margin: 10px 0 0;
}

.mini-note a {
  color: var(--text-secondary);
  font-weight: 600;
}

/* Small fact chips (tool categories, platforms) */
.fact-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 8px;
  margin: 0 0 20px;
}

.fact-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  border: 2px solid #fff;
  box-shadow: var(--shadow-sticker);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--plum);
  transform: rotate(var(--tilt, -1.5deg));
  transition: transform 0.15s ease-out;
}

.fact-chip:hover {
  transform: rotate(0deg) scale(1.04);
}

/* MCP setup page: wrap the many client tabs instead of scrolling */
.setup-box .tabs {
  flex-wrap: wrap;
  border-radius: 22px;
}

.client-panel {
  max-width: 46rem;
}

/* "Just ask" example prompts */
.ask-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px 40px;
  max-width: 60rem;
}

.ask-grid p {
  margin: 0;
  font-size: 1.15rem;
  color: var(--text-secondary);
}

@media (max-width: 700px) {
  .ask-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Card-footer link pairs */
.card-links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 24px;
  align-items: center;
}

/* Decorative "just ask" quote */
.ask-quote {
  margin: 16px 0 0;
  font-size: 1rem;
  color: var(--text-tertiary);
}

@media (prefers-reduced-motion: reduce) {
  .fact-chip {
    transition: none;
  }
  .fact-chip:hover {
    transform: rotate(var(--tilt, -1.5deg));
  }
}

/* ── About strip (butter tint section) ── */
.about {
  background: linear-gradient(var(--butter-tint), var(--butter-tint)), var(--background);
}

.about-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  gap: 40px;
  align-items: center;
}

@media (max-width: 860px) {
  .about-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.about .eyebrow {
  color: var(--se-tangerine-text);
}

.about-links {
  display: grid;
  gap: 14px;
}

.about-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 22px;
  border-radius: var(--radius-card);
  background: var(--card);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
  transform: rotate(var(--tilt, -1deg));
  transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
}

.about-card:hover {
  text-decoration: none;
  transform: rotate(0deg) translateY(-3px);
  box-shadow: var(--shadow-lift);
}

.about-card strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

.about-card span {
  color: var(--text-tertiary);
  font-size: 0.9rem;
}

.about-card .icon {
  width: 34px;
  height: 34px;
  color: var(--se-violet);
}

.about-card .icon-brand {
  /* Two different marks sit in this slot — Scrollmark's square PNG and
     SocialGPT's wider SVG — so the box cannot borrow either one's ratio. */
  width: 42px;
  /* `.about-card .icon` above sets an explicit height, and an explicit height
     makes aspect-ratio inert — so it has to be cleared here for the square
     box to apply. */
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  display: block;
}

/* ── Footer ── */
footer {
  border-top: 1px solid var(--border);
  padding: 48px 0 60px;
}

.footer-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 28px 48px;
  justify-content: space-between;
  align-items: flex-start;
}

.footer-brand {
  max-width: 22rem;
  color: var(--text-tertiary);
  font-size: 0.95rem;
}

.footer-brand .brand {
  margin-bottom: 10px;
}

.footer-col h3 {
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-tertiary);
  margin: 0 0 12px;
}

.footer-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
}

.footer-col a {
  color: var(--text-secondary);
  font-weight: 500;
  display: inline-flex;
  min-height: 32px;
  align-items: center;
}

.footer-col a:hover {
  color: var(--text-primary);
}

.footer-note {
  margin-top: 40px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  justify-content: space-between;
  color: var(--text-tertiary);
  font-size: 0.9rem;
}

/* ── Reduced motion — mandatory ── */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .rise,
  .pop {
    animation: none;
    opacity: 1;
    transform: none;
    scale: 1;
  }

  .marker svg path {
    animation: none;
    stroke-dashoffset: 0;
  }

  .fake-bars span {
    animation: none;
  }

  .hero-pile .s2 {
    animation: none;
  }

  .sticker,
  .chip,
  .card,
  .about-card,
  .btn,
  .brand .mark,
  .theme-switch::before,
  .copy-btn {
    transition: none;
  }

  .sticker:hover { transform: rotate(var(--tilt, -2deg)); }
  .chip:hover { transform: rotate(var(--tilt, -1.5deg)); }
  .card:hover,
  .about-card:hover { transform: rotate(var(--tilt, 0deg)); }
}

/* Datastar did not run — see the comment beside the script tag. Reveal every
   conditional block rather than leaving the page with one visible panel and no
   way to reach the other twelve. */
.no-ds [data-show] { display: block !important; }

/* Tool catalog. There is no table style on this site and one table would have
   been a whole new primitive for a single block, so the twenty-six tools reuse
   the card grid and read as chips. `tool--write` marks the only tool that can
   post — the distinction the reader is actually scanning for. */
.tool-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.tool-list li {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.45);
  white-space: nowrap;
}
.dark .tool-list li { background: rgba(255, 255, 255, 0.06); }
.tool-list li.tool--write {
  border-color: var(--se-pink);
  border-width: 2px;
  font-weight: 700;
}

/* The dependency matrix. This IS tabular — sixteen skills against the same
   four questions — so it is a table rather than cards, and it scrolls inside
   its own container so the page body never scrolls sideways on a phone. */
.matrix-scroll {
  overflow-x: auto;
  border-radius: var(--radius-card);
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.4);
}
.dark .matrix-scroll { background: rgba(255, 255, 255, 0.04); }
.matrix {
  border-collapse: collapse;
  width: 100%;
  min-width: 640px;
  font-size: 0.92rem;
}
.matrix th,
.matrix td {
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.matrix thead th {
  font-family: var(--font-display);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.matrix tbody tr:last-child td { border-bottom: 0; }
.matrix td code { font-family: var(--font-mono); font-size: 0.85rem; }
.matrix .num { text-align: center; }
.matrix .chip { font-size: 0.72rem; padding: 2px 10px; white-space: nowrap; }

/* ── Template gallery ──
   A card previews a pairing by painting the style's own colours: the four
   custom properties below are set inline from the preset's values, so the
   preview changes when the preset does rather than being a drawing of it. */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 28px;
}

@media (max-width: 1080px) {
  .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .gallery-grid { grid-template-columns: minmax(0, 1fr); }
}

.tpl-card {
  display: flex;
  flex-direction: column;
  padding: clamp(18px, 2vw, 24px);
}

.tpl-card .heading-3 { margin: 6px 0 0; }
.tpl-card p { margin: 10px 0 16px; }

.tpl-preview {
  position: relative;
  display: flex;
  flex-direction: column;
  /* `space-between` put the title at the top and the caption at the bottom.
     A placed title leaves the flow entirely, and a lone flex child under
     `space-between` sits at the START -- which floated every caption to the
     top of its frame. `flex-end` holds the caption at the bottom whether or
     not the title is still in the flow, and an unplaced title is pinned to
     the top by the rule below. */
  justify-content: flex-end;
  align-items: center;
  margin: 0 auto 18px;
  width: 100%;
  max-width: 220px;
  padding: 14px 12px;
  border-radius: var(--radius-card);
  border: 1px solid var(--border);
  /* A photograph, because a template is type over a picture. Without one the
     card is a swatch: the colours are right and it shows nothing being done
     with them. The title card is painted as a band over the picture rather
     than as a full bleed, which is also what the engine draws -- a card has a
     `rect` and occupies a fifth of the frame, not all of it. */
  /* So type can be sized against the frame rather than against the page: the
     preset states a size in pixels of a 1920-tall frame, and this element is
     that frame. `inline-size` rather than `size` -- a size container takes its
     height from nothing but itself, which collapses a box whose height comes
     from `aspect-ratio`, and the grid below it went with it. So the height is
     derived instead: `--tpl-tall` is the frame's height over its width. */
  container-type: inline-size;
  --tpl-tall: 1.7778;
  background-color: #1b1b1f;
  background-image: var(--tpl-shot, none);
  background-size: cover;
  background-position: center;
  overflow: hidden;
}

.tpl-preview-title {
  background: var(--tpl-bg);
  padding: 6px 10px;
  border-radius: 3px;
  align-self: stretch;
  /* Hold the top, since the container now packs its flow children to the
     bottom for the caption's sake. A placed title overrides this. */
  margin-bottom: auto;
}

/* The frame the format is composed for, drawn at that ratio. */
.tpl-preview--9x16 { aspect-ratio: 9 / 16; max-width: 168px; --tpl-tall: 1.7778; }
.tpl-preview--16x9 { aspect-ratio: 16 / 9; max-width: 100%; --tpl-tall: 0.5625; }
/* `source` means the format keeps the footage's own frame, so there is no
   ratio to draw. A neutral 3:2 stands in, and the spec row says `source`. */
.tpl-preview--source { aspect-ratio: 3 / 2; max-width: 100%; --tpl-tall: 0.6667; }

.tpl-preview-title {
  /* The preset's own face, falling back to this site's display face for a
     preset that names none. A preview set in the site's typeface shows the
     palette and hides the thing being sold. */
  font-family: var(--tpl-title-face, var(--font-display));
  font-weight: 800;
  /* The preset's own fraction of frame height, not one size for every card.
     A fixed size drew a 232px swash title and a 58px quiet one at the same
     scale, which is the loudest thing most of these presets do and it was
     invisible. `--tpl-tall` converts the frame's height into its width, since
     this is an inline-size container. */
  font-size: calc(var(--tpl-title-scale, 0.0437) * var(--tpl-tall) * 100cqw);
  line-height: 1.02;
  white-space: normal;
  text-wrap: balance;
  max-width: 100%;
  color: var(--tpl-fg);
  align-self: flex-start;
  text-align: left;
}

/* Where the preset actually puts it. A preset states `rect` as
   `[x, y, w, h]` fractions of the frame and `align` for the horizontal, and
   the rule above pinned every title to the top left corner regardless --
   which is wrong for two thirds of the collection and wrong for four of the
   five recreations, whose reference images centre the title on both axes.
   (`postcard-serif` is the exception and stays high, because its own rect
   puts it there: 0.10 to 0.24, centre 0.17.)

   `top` is the rect's CENTRE and the transform lifts the type back by half
   its own height, so a one-line and a two-line title sit on the same
   centreline -- the same thing the renderer does when it centres type in a
   card box. Placed absolutely rather than with flex offsets because the
   vertical position is a fraction of the frame, and a flex item's position is
   a consequence of everything above it. */
/* Scoped to the frame so it outranks the shared `position: relative`
   further down, which sets the stacking context these two share. At
   equal specificity that rule won on source order, and the placed
   element stayed in the flow with `top`/`left` read as relative nudges
   -- which happened to land the title near the right spot, because the
   flow put it at the top and the offsets pushed it down by roughly the
   right amount, and left the caption 8px off centre. */
.tpl-preview .tpl-preview-title--placed {
  position: absolute;
  /* Straight fractions of the frame. An absolutely positioned box resolves
     its percentages against the padding box of its positioned ancestor --
     which IS the picture, give or take the 1px border -- so the preview's own
     insets do not enter into it. */
  top: calc(var(--tpl-title-top) * 100%);
  left: calc(var(--tpl-title-left) * 100%);
  width: calc(var(--tpl-title-width) * 100%);
  max-width: none;
  transform: translateY(-50%);
  text-align: var(--tpl-title-align, left);
}

/* A caption is outlined type over a picture, so that is what this draws.
   It used to sit on a band of its own stroke colour, which solved the contrast
   problem by inventing a pill no renderer draws. The outline is the real
   answer and the preset already states it: `strokeWidth / fontSize`, the same
   ratio the editor stores, so it holds at any size.
   `paint-order: stroke fill` puts the stroke behind the letter instead of
   through it -- without it a 0.13em stroke eats a third of the letterform.
   The contrast is unchanged by dropping the band: what surrounds a glyph is
   still the stroke colour, which is what it was always measured against.
   Measured: every caption is 7:1 or better against its own outline, and that
   number is checked rather than remembered -- see `check-accuracy.py`. */
.tpl-preview-caption {
  font-family: var(--tpl-caption-face, var(--font-mono));
  /* True scale, with a floor. A caption is about 3% of a frame's height --
     56px on a 1920 frame, right in a video and 7px in a thumbnail this size,
     which is a grey smudge rather than a caption. The floor is the one place
     this preview knowingly departs from the proportion, and it departs only
     where the card is small: at full width the honest size wins. */
  font-size: max(
    calc(var(--tpl-caption-scale, 0.0292) * var(--tpl-tall) * 100cqw),
    11px
  );
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--tpl-caption);
  -webkit-text-stroke: calc(var(--tpl-caption-stroke-em, 0.09) * 1em) var(--tpl-stroke);
  paint-order: stroke fill;
  /* A little lift off the picture, which the composer gets from the shadow it
     draws under a caption. */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.32);
  text-align: center;
  max-width: 92%;
}

/* Where the preset puts it, measured up from the bottom edge -- so a larger
   number is HIGHER on screen, which is the opposite of how the name reads.
   Every preset states one and the preview honoured none, holding all 21
   against the bottom edge: near enough for the eighteen asking 0.10-0.16, and
   out by a third of a frame for the two `pov` presets, whose captions sit
   around the middle because that is where a piece to camera wants them.

   The insets are undone the same way the title's are: the fraction is of the
   frame the picture fills, not of the box the padding leaves. */
/* Scoped to the frame so it outranks the shared `position: relative`
   further down, which sets the stacking context these two share. At
   equal specificity that rule won on source order, and the placed
   element stayed in the flow with `top`/`left` read as relative nudges
   -- which happened to land the title near the right spot, because the
   flow put it at the top and the offsets pushed it down by roughly the
   right amount, and left the caption 8px off centre. */
.tpl-preview .tpl-preview-caption--placed {
  position: absolute;
  bottom: calc(var(--tpl-caption-bottom) * 100%);
  /* The frame's width, not the padded box's. A caption band runs nearly the
     full width of a video, and `max-width: 92%` was measuring 92% of the box
     the padding leaves -- 132px in a 168px thumbnail. `pov-serif` needs 157
     for its one line, so it wrapped to two, and because a placed caption is
     anchored by its BOTTOM edge the second line grew upward into the title.

     Both edges are pinned rather than centred with `left: 50%` and a
     translate: that version came out 3px off centre, because the offset and
     the width percentage do not resolve against the same box. Pinning both
     sides is symmetric whichever box wins. 4px keeps the outline off the
     edge. */
  left: 3px;
  right: 3px;
  width: auto;
  max-width: none;
}

.tpl-preview-caption--upper { text-transform: uppercase; letter-spacing: 0.04em; }

.tpl-preview-caption em {
  font-style: normal;
  color: var(--tpl-accent);
}

.tpl-specs {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px;
  margin: 0 0 16px;
  font-size: 0.85rem;
}

.tpl-specs div {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 1px solid var(--border);
}

.tpl-specs div:last-child { border-bottom: 0; }

.tpl-specs dt {
  font-family: var(--font-display);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

.tpl-specs dd {
  margin: 0;
  text-align: right;
}

.tpl-specs dd code { font-family: var(--font-mono); font-size: 0.8rem; }

.tpl-card .codebox--row { margin-top: auto; }

/* A command that scrolls shows `video-studio formats` and hides the half that
   says which format — a syntactically plausible line that does nothing. These
   cards are narrow, so the command wraps instead. */
.tpl-card .codebox--row pre,
.tpl-card .codebox--row pre code {
  white-space: pre-wrap;
  word-break: break-word;
  min-width: 0;
}

/* ── Previews that move ──
   The clip sits under the type and over the still, which is its own poster, so
   the first frame is the picture the card already showed and nothing jumps
   when it starts. It plays on hover and on keyboard focus and never on its
   own: five autoplaying videos is a page that drains a battery to show what a
   still already shows. */
.tpl-clip {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.25s ease-out;
}

.tpl-card:hover .tpl-clip,
.tpl-card:focus-within .tpl-clip {
  opacity: 1;
}

/* Asked for less motion: the still stays, and the script never calls play(). */
@media (prefers-reduced-motion: reduce) {
  .tpl-card:hover .tpl-clip,
  .tpl-card:focus-within .tpl-clip {
    opacity: 0;
  }
}

.tpl-preview-title,
.tpl-preview-caption {
  position: relative;
  z-index: 1;
}

/* A card that moves sits on a slightly different ground.
   The hue does the grouping -- you see which ones are alive before you read
   anything -- so the marker on the frame can shrink to a play glyph. Built
   from the same tint token the project cards use, so it follows the theme
   into dark mode instead of being a colour that only works in one. */
.tpl-card--moves {
  background: linear-gradient(var(--violet-tint), var(--violet-tint)), var(--card);
}

/* The cards that move say so, quietly.
   Inside the frame's own padding rather than straddling its edge: the preview
   clips its overflow to keep the picture in its rounded corners, so a sticker
   hung off the top was cut in half and unreadable. And no sticker colour --
   a lime badge shouts louder than the thing it is pointing at. */
.tpl-moves {
  position: absolute;
  top: 9px;
  right: 9px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 3px;
  border-radius: 999px;
  background: rgba(12, 12, 16, 0.5);
  color: rgba(255, 255, 255, 0.82);
  pointer-events: none;
  animation: tpl-breathe 3.4s ease-in-out infinite;
}

.tpl-moves svg {
  width: 10px;
  height: 10px;
  opacity: 0.9;
}

/* Out of the way once it is actually playing -- the label has done its job by
   then, and the point of the card is the picture. */
.tpl-card:hover .tpl-moves,
.tpl-card:focus-within .tpl-moves {
  opacity: 0;
  transition: opacity 0.2s ease-out;
}

/* A breath, not a blink: it should read as alive when you notice it and be
   invisible when you are reading the card next to it. */
@keyframes tpl-breathe {
  0%, 100% { opacity: 0.62; }
  50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .tpl-moves {
    animation: none;
    opacity: 0.8;
  }
}
