/* ============================================================
   Shared styles for Creme project pages.
   Tokens mirror index.html exactly (one design system, no drift).
   ============================================================ */

:root {
  --primary: #5F4A8B;
  --primary-dark: #49366E;
  --primary-deep: #251B3A;
  --secondary: #EAA824;
  --secondary-dark: #C68910;
  --tertiary: #846FAE;
  --neutral-bg: #FEF8D4;
  --accent-lavender: #D8CCE8;
  --accent-moss: #52B788;
  --surface-terracotta: #C1440E;
  --text-main: #241A38;
  --text-muted: #5F5474;
  --border-subtle: rgba(95, 74, 139, 0.15);
  --border-medium: rgba(95, 74, 139, 0.28);

  --font-display: 'Nunito', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;

  --radius-sm: 20px;
  --radius-md: 40px;
  --radius-btn: 24px;

  --transition-spring: 220ms cubic-bezier(0.34, 1.4, 0.64, 1);
  --transition-standard: 200ms ease;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-display);
  background-color: var(--neutral-bg);
  color: var(--text-main);
  line-height: 1.6;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.container {
  width: 100%;
  max-width: 880px;
  margin: 0 auto;
  padding: 0 1.5rem;
  position: relative;
}

/* ---------- Top bar ---------- */
.top-bar {
  padding: 1.25rem 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--primary);
  font-weight: 700;
  text-decoration: none;
  transition: color var(--transition-standard);
}

.back-link:hover {
  color: var(--primary-dark);
}

.repo-link {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--primary);
  text-decoration: none;
  transition: color var(--transition-standard);
}

.repo-link:hover {
  color: var(--secondary-dark);
}

/* ---------- Hero ---------- */
.project-hero {
  padding: 1rem 0 2.5rem;
}

.project-hero h1 {
  font-size: clamp(2.4rem, 5vw, 3.4rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--primary-deep);
  margin-bottom: 0.75rem;
}

.project-tagline {
  font-size: 1.15rem;
  color: var(--text-muted);
  max-width: 62ch;
  text-align: justify;
  text-justify: inter-word;
  hyphens: auto;
}

/* Project logo — sits leftmost inside the dark download card */
.download-logo {
  width: clamp(56px, 9vw, 88px);
  height: auto;
  flex-shrink: 0;
  user-select: none;
}

/* ---------- Download card ---------- */
.download-card {
  background: var(--primary-deep);
  color: var(--neutral-bg);
  border-radius: var(--radius-md);
  padding: clamp(1.75rem, 4vw, 2.5rem);
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
  margin-bottom: 2.5rem;
}

.download-info {
  flex: 1 1 auto;
  min-width: 0;
}

.download-action {
  margin-left: auto;
}

.download-info h2 {
  font-size: 1.3rem;
  font-weight: 800;
  margin-bottom: 0.35rem;
}

.download-meta {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: rgba(254, 248, 212, 0.7);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 700;
  padding: 0.85rem 1.5rem;
  border-radius: var(--radius-btn);
  text-decoration: none;
  cursor: pointer;
  border: 1.5px solid transparent;
  line-height: 1;
  transition: all var(--transition-spring);
}

.btn:active {
  transform: translateY(1px);
}

.btn-solar {
  background-color: var(--secondary);
  color: #3b2800;
}

.btn-solar:hover {
  background-color: var(--secondary-dark);
  transform: translateY(-2px);
}

.btn-ghost {
  background: rgba(254, 248, 212, 0.06);
  border-color: rgba(254, 248, 212, 0.25);
  color: var(--neutral-bg);
}

.btn-ghost:hover {
  background: rgba(254, 248, 212, 0.12);
  border-color: var(--secondary);
}

/* ---------- Screenshot carousel (project pages) ---------- */
/* An endless, half-size, centred belt of app screenshots that loops LEFT → RIGHT
   and parks while the pointer hovers it, while focus is inside it, or when the
   Pause button (§3.5) says so. It is driven by a short inline script, not a CSS
   animation: the script measures ONE loop — the exact pixel distance from a shot
   to its first clone, gaps included — and drifts the track by it, so the wrap is
   pixel-perfect whatever the viewport, the gap, or the number of copies it takes
   to fill the screen. Two things follow from that: it keeps moving for readers
   whose OS asks for reduced motion (the belt just halves its pace), and it never
   depends on browser cache timing of an animation declaration to be alive. */
.carousel {
  /* 2026-10-06: twice at Ash's request — the shots went
     clamp(74px, 13vw, 124px) → clamp(148px, 26vw, 248px) → this. The script
     measures whatever this resolves to, so the loop maths never need touching. */
  --item-h: clamp(180px, 30vw, 340px);
  --item-gap: 0.85rem;
  max-width: min(100%, 1040px);
  margin-inline: auto;
  margin-bottom: 2.5rem;
}

.carousel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.85rem;
}

.carousel-head h2 {
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--primary-deep);
}

/* rotation control — the APG carousel pattern owes an auto-rotating strip a
   real, focusable stop/start button; the label swaps Pause ⇄ Play */
.carousel-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.75rem;
  border: 1px solid var(--border-subtle);
  border-radius: 999px;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  cursor: pointer;
}

.carousel-toggle:hover {
  background: rgba(95, 74, 139, 0.08);
  border-color: var(--secondary);
  color: var(--primary-deep);
}

/* the carousel's one keyboard control, so it has to be findable */
.carousel-toggle:focus-visible {
  outline: 2px solid var(--secondary);
  outline-offset: 2px;
  color: var(--primary-deep);
}

/* the "belt is running" blip; it goes dim and still when the belt is parked */
.carousel-toggle::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--secondary);
  animation: carousel-blip 2.4s ease-in-out infinite;
}

.carousel[data-paused='true'] .carousel-toggle::before {
  animation: none;
  opacity: 0.35;
}

.carousel-frame {
  /* the shots are big enough to want more room than the 880px text column, so
     the frame bleeds out of it — but never past the window (the 3rem also
     swallows a classic scrollbar) and never off-centre: both margins are pulled
     by the same amount, so the frame stays centred on the page. */
  --bleed: min(1080px, calc(100vw - 3rem));
  width: var(--bleed);
  margin-inline: calc(50% - var(--bleed) / 2);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: rgba(95, 74, 139, 0.05);
  padding: 0.75rem;
}

.carousel-viewport {
  overflow: hidden;
  /* shots fade in and out at the edges instead of being chopped off */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  scrollbar-width: none;
}

.carousel-viewport::-webkit-scrollbar {
  display: none;
}

/* until the script takes over — or for anyone browsing with JS off — the belt is
   a plain, swipeable row of stills rather than a stalled animation */
.carousel:not(.is-live) .carousel-viewport {
  overflow-x: auto;
}

.carousel-track {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
  will-change: transform;
}

.carousel-item {
  flex-shrink: 0;
  margin: 0 var(--item-gap) 0 0;
  list-style: none;
}

.carousel-item img {
  display: block;
  height: var(--item-h);
  width: auto;
  border: 1px solid var(--border-medium);
  border-radius: 8px;
  background: #FFFFFF;
  -webkit-user-drag: none;
  user-select: none;
}

/* every shot is a control: click (or Enter) it for the full-size frame — see the
   lightbox below. A button keeps that honest for the keyboard. */
.carousel-shot {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 8px;
  line-height: 0;
  cursor: zoom-in;
}

.carousel-shot img {
  transition: box-shadow 0.2s ease, filter 0.2s ease;
}

.carousel-shot:hover img {
  filter: brightness(1.03);
  box-shadow: 0 10px 24px rgba(37, 27, 58, 0.18);
}

.carousel-shot:focus-visible {
  outline: 2px solid var(--secondary);
  outline-offset: 3px;
}

@keyframes carousel-blip {

  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.3;
  }
}

/* ---------- Screenshot lightbox (project pages) ---------- */
/* A <dialog>, so the browser hands us the top layer, a focus trap, Escape and
   focus restore for free; we only add the look. Clicking a shot slides it in over
   a blurred, oversized copy of itself, and the pointer then moves the two planes
   by different amounts — the crisp shot drifts against the pointer, the blur with
   it and twice as far. That difference in response is the whole parallax. */
html:has(dialog.lightbox[open]),
html:has(dialog.lightbox[open]) body {
  overflow: hidden;            /* the page behind must not scroll away */
  scrollbar-gutter: stable;    /* and must not jump when its bar disappears */
}

.lightbox {
  position: fixed;
  inset: 0;
  width: auto;
  max-width: none;
  height: 100dvh;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  z-index: 100;                /* only matters if <dialog> is an old browser */
  overflow: hidden;
  overscroll-behavior: contain;
  background: rgba(20, 15, 32, 0.55);
  color: var(--neutral-bg);
}

.lightbox::backdrop {
  background: rgba(20, 15, 32, 0.55);
}

/* the far plane: the same shot again, blurred and blown up to fill the window */
.lightbox-bg {
  position: absolute;
  inset: 0;
  transition: transform 0.45s ease-out;
}

.lightbox-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.16);      /* hides the soft edges blur leaves behind */
  filter: blur(26px) saturate(1.15);
  opacity: 0.6;
}

.lightbox-stage {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: clamp(0.75rem, 3vw, 2rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  transform: scale(0.985);
  opacity: 0;
  transition: opacity 0.28s ease-out, transform 0.28s ease-out;
  /* clicks fall through the empty part of the stage, so the dim behind the shot
     stays clickable and still closes the viewer */
  pointer-events: none;
}

.lightbox-stage > * {
  pointer-events: auto;
}

.lightbox.is-open .lightbox-stage {
  transform: none;
  opacity: 1;
}

/* the near plane: the whole shot, never cropped (contain), and never upscaled
   past its 1282 real pixels — the blur behind does the filling */
.lightbox-img {
  max-width: min(100%, 1282px);
  max-height: calc(100% - 3.5rem);
  width: auto;
  height: auto;
  border-radius: 10px;
  background: #FFFFFF;
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.55);
  -webkit-user-drag: none;
  user-select: none;
}

/* it tracks the pointer 1:1, so only the trip back to centre is eased */
.lightbox.is-recentering .lightbox-img {
  transition: transform 0.45s ease-out;
}

.lightbox-cap {
  max-width: min(72ch, 100%);
  font-size: 0.85rem;
  line-height: 1.5;
  text-align: center;
  text-wrap: balance;
  color: rgba(254, 248, 212, 0.82);
  opacity: 0;
  transition: opacity 0.32s ease-out 0.08s;
}

.lightbox.is-open .lightbox-cap {
  opacity: 1;
}

.lightbox-close {
  position: absolute;
  top: clamp(0.75rem, 2vw, 1.25rem);
  right: clamp(0.75rem, 2vw, 1.25rem);
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid rgba(254, 248, 212, 0.3);
  border-radius: 50%;
  background: rgba(37, 27, 58, 0.6);
  color: var(--neutral-bg);
  font-size: 0.95rem;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.28s ease-out, background 0.2s ease;
}

.lightbox.is-open .lightbox-close {
  opacity: 1;
}

.lightbox-close:hover {
  background: var(--secondary);
  border-color: var(--secondary);
  color: var(--primary-deep);
}

.lightbox-close:focus-visible {
  outline: 2px solid var(--secondary);
  outline-offset: 3px;
  opacity: 1;
}

/* ---------- README body ---------- */
.readme {
  background: #FFFFFF;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: clamp(1.75rem, 4vw, 3rem);
  margin-bottom: 3rem;
}

.readme h2 {
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--primary-deep);
  margin: 2.25rem 0 0.75rem;
  letter-spacing: -0.01em;
}

.readme h2:first-child {
  margin-top: 0;
}

.readme h3 {
  font-size: 1.15rem;
  font-weight: 800;
  color: var(--primary-dark);
  margin: 1.75rem 0 0.5rem;
}

.readme p {
  color: var(--text-main);
  font-size: 1rem;
  max-width: 72ch;
  margin-bottom: 0.9rem;
  text-align: justify;
  text-justify: inter-word;
  hyphens: auto;
}

.readme ul,
.readme ol {
  margin: 0 0 1rem 1.25rem;
  max-width: 68ch;
}

.readme li {
  margin-bottom: 0.45rem;
}

.readme a {
  color: var(--primary);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1.5px solid rgba(95, 74, 139, 0.35);
  transition: color var(--transition-standard), border-color var(--transition-standard);
}

.readme a:hover {
  color: var(--secondary-dark);
  border-color: var(--secondary-dark);
}

.readme code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  background: rgba(95, 74, 139, 0.08);
  color: var(--primary-dark);
  padding: 0.15em 0.45em;
  border-radius: 6px;
}

.readme pre {
  background: var(--primary-deep);
  color: var(--neutral-bg);
  font-family: var(--font-mono);
  font-size: 0.84rem;
  line-height: 1.65;
  padding: 1.15rem 1.35rem;
  border-radius: var(--radius-sm);
  overflow-x: auto;
  margin: 0 0 1.25rem;
}

.readme pre code {
  background: none;
  color: inherit;
  padding: 0;
  font-size: inherit;
}

.readme blockquote {
  border-left: 3px solid var(--secondary);
  background: rgba(234, 168, 36, 0.08);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 0.9rem 1.15rem;
  margin: 0 0 1.25rem;
  color: var(--text-main);
  max-width: 68ch;
}

.readme blockquote p {
  margin-bottom: 0;
  text-align: justify;
  text-justify: inter-word;
  hyphens: auto;
}


/* ---------- Footer ---------- */
footer {
  padding: 2rem 0 3rem;
  border-top: 1px solid var(--border-subtle);
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-top: auto;
}

.footer-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
}

footer a {
  color: var(--text-muted);
  font-weight: 700;
  text-decoration: none;
  transition: color var(--transition-standard);
}

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

/* ---------- Icons ---------- */
.glyph {
  font-size: 18px;
  line-height: 1;
  display: inline-block;
  vertical-align: middle;
  flex-shrink: 0;
}

.glyph-sm {
  font-size: 15px;
}

/* ---------- Mobile ---------- */
@media (max-width: 768px) {
  .download-card {
    flex-direction: column;
    align-items: flex-start;
  }

  .download-logo {
    width: 64px;
  }

  .download-action {
    margin-left: 0;
    width: 100%;
  }

  .download-action .btn {
    width: 100%;
  }

  .carousel-frame {
    padding: 0.5rem;
  }

  .readme {
    padding: 1.5rem 1.25rem;
  }
}

.readme table {
  width: 100%;
  max-width: 68ch;
  border-collapse: collapse;
  font-size: 0.92rem;
  margin-bottom: 1.25rem;
}

.readme th,
.readme td {
  text-align: left;
  padding: 0.55rem 0.85rem;
  border: 1px solid var(--border-subtle);
}

.readme th {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primary-dark);
  background: rgba(95, 74, 139, 0.06);
}

.readme td code {
  white-space: nowrap;
}

.readme hr {
  border: none;
  border-top: 1px solid var(--border-subtle);
  margin: 2rem 0;
}

/* WIP state (no release yet) */
.wip-note {
  font-size: 0.95rem;
  color: rgba(254, 248, 212, 0.85);
  max-width: 52ch;
  text-align: justify;
  text-justify: inter-word;
  hyphens: auto;
}

/* For anyone who asked the OS for less motion the blip stops blinking, the belt
   halves its pace (the script reads the same query) and the lightbox drops its
   transitions and its parallax entirely — the full shot simply appears, centred.
   The belt keeps drifting rather than freezing, because the carousel's own Pause
   button is the stop the pattern asks for, and a still belt read as a broken belt
   (§3.5). */
@media (prefers-reduced-motion: reduce) {
  .carousel-toggle::before {
    animation: none;
  }

  .lightbox-stage,
  .lightbox-cap,
  .lightbox-close {
    transform: none;
    opacity: 1;
    transition: none;
  }

  .lightbox-bg,
  .lightbox.is-recentering .lightbox-img {
    transition: none;
  }
}
