/*
  Droppy Code changelog page, ported from getdroppy.app/changelog.html
  =====================================================================
  Faithful port of Droppy's inline changelog <style> block, re-tokened for
  this site's dark palette. Geometry, spacing, type sizes and animations
  are Droppy's; only colours are mapped (light card fills to the dark
  wells, ink text to the dark text tokens, invisible borders and shadows
  to this site's flat tokens).

  Dropped (nothing to wear them on this page): the frame-sequence hero
  (.changelog-scrub), the light-paper hero ink override, the nightly/beta/
  iOS/playground/droppykit channel variants and .is-nightly/.is-beta/.is-ios
  rules, the channel note, entry actions, TestFlight, galleries, video,
  image, lightbox, whatsnew label, and the per-platform float-icon faces.
*/

:root {
  /* Channel pill identity. Only the stable pill is rendered on this page;
     these defaults match .changelog-entry__channel--stable below. */
  --channel-deep: #1e40af;
  --channel-bright: #3a6cf0;
  --channel-mid: #2c55d4;
  --channel-glow: rgba(30, 64, 175, 0.32);
  --channel-glyph: #dbe7ff;
  /* Headings on this site set Inter Tight first; the entry titles follow. */
  --font-display: "Inter Tight", "Instrument Sans", "Inter", ui-sans-serif, system-ui, sans-serif;
}

.changelog-hero {
  padding-top: clamp(120px, 18vw, 168px);
  padding-bottom: clamp(20px, 3vw, 40px);
  position: relative;
}

/* This site has no frame-sequence engine, so there is no hero art to veil;
   the kicker mark needs no recoloring either. */
.changelog-hero .hero__kicker-mark {
  filter: none;
}

.changelog-hero .container,
.changelog-section .container {
  position: relative;
  z-index: 1;
}

.changelog-hero__inner {
  max-width: 640px;
  margin-inline: auto;
}

.changelog-section {
  /* Hero hands off with the shared 120px (40 + 80); the list ends on
     the shared last-section 120px before the footer. */
  padding-top: clamp(40px, 6vw, 80px);
  padding-bottom: clamp(64px, 10vw, 120px);
}

.changelog-list {
  display: flex;
  flex-direction: column;
  gap: clamp(28px, 4vw, 48px);
  margin-inline: auto;
}

/* Loading / error / empty state card */
.changelog-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 16px;
  padding: clamp(48px, 8vw, 96px) 28px;
  border-radius: var(--radius-3xl);
  background: var(--bg-well);
  border: 1px solid var(--border-subtle);
}

.changelog-state__title {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
}

.changelog-state__text {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  line-height: 1.6;
  max-width: 420px;
}

.changelog-spinner {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2.5px solid var(--bg-well-raised);
  border-top-color: var(--accent);
  animation: changelog-spin 0.8s linear infinite;
}

@keyframes changelog-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .changelog-spinner {
    animation-duration: 2.4s;
  }
}

/* Release entries: two columns per release. The left column (date +
   display title) pins with position: sticky while the right column's
   notes scroll past, like a product-blog changelog. */
.changelog-list {
  max-width: 1040px;
}

.changelog-entry {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(28px, 6vw, 88px);
  padding-top: clamp(40px, 6vw, 72px);
  border-top: 1px solid var(--border-subtle);
}

.changelog-entry:first-child {
  padding-top: 0;
  border-top: 0;
}

.changelog-entry__pin {
  position: sticky;
  top: 108px;
}

/* The date row is the entry's one badge-bearing line, so it carries a
   single rhythm: the marker dot, the date, and the channel pill, all
   spaced by the same 12px gap rather than the pill adding a margin of
   its own on top of it. It wraps because the aside is a narrow column
   at 1040px and the whole page collapses to one column under 720px. */
.changelog-entry__date {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  row-gap: 8px;
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.changelog-entry__date::before {
  content: "";
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
  flex-shrink: 0;
}

/* Channel badge: one recipe, worn here only as the stable pill. The
   geometry, the 135deg three-stop gradient, white text, the soft
   coloured glow, the inline SVG glyph and the two overlay layers live
   on the base class; the --stable modifier sets the five custom
   properties. Stable is royal blue on purpose: it is the tier the
   download button gives you. */
.changelog-entry__channel {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px 4px 9px;
  border-radius: var(--radius-full);
  background: linear-gradient(
    135deg,
    var(--channel-deep) 0%,
    var(--channel-bright) 52%,
    var(--channel-mid) 100%
  );
  color: #ffffff;
  font-size: var(--text-xs);
  font-weight: 600;
  /* Pinned so the pill's height comes from the pill, not from whichever
     line-height the date paragraph happens to inherit. */
  line-height: 1.25;
  letter-spacing: 0.01em;
  white-space: nowrap;
  box-shadow: 0 1px 8px var(--channel-glow);
}

.changelog-entry__channel--stable {
  --channel-deep: #1e40af;
  --channel-bright: #3a6cf0;
  --channel-mid: #2c55d4;
  --channel-glow: rgba(30, 64, 175, 0.32);
  --channel-glyph: #dbe7ff;
}

/* The glyph is sized and tinted once: a droplet on the same 11px
   silhouette slot Droppy's moon and flask use. */
.changelog-entry__channel-glyph {
  display: block;
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  color: var(--channel-glyph);
}

/* Two overlay layers. They only ever change opacity, and they run on
   two different periods (5.4s and 7.1s) so the pill breathes unevenly
   rather than pulsing in unison. */
.changelog-entry__channel::before,
.changelog-entry__channel::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-repeat: no-repeat;
}

.changelog-entry__channel::before {
  animation: channel-breathe-a 5.4s ease-in-out infinite;
}

.changelog-entry__channel::after {
  animation: channel-breathe-b 7.1s ease-in-out infinite;
}

/* Stable: a blue droplet settling. Two slow streams of droplets fall
   through the blue on two periods. Each stream scrolls downward by
   exactly one tile height, so the loop is seamless and no droplet ever
   pops or respawns. */
.changelog-entry__channel--stable::before {
  background-image:
    radial-gradient(circle at 7px 4px, rgba(255, 255, 255, 0.90) 0 0.8px, rgba(255, 255, 255, 0) 2px),
    radial-gradient(circle at 14px 13px, rgba(219, 231, 255, 0.75) 0 0.6px, rgba(255, 255, 255, 0) 1.6px);
  background-size: 20px 20px;
  animation: stable-drizzle-a 4.2s linear infinite;
}

.changelog-entry__channel--stable::after {
  background-image:
    radial-gradient(circle at 4px 12px, rgba(219, 231, 255, 0.80) 0 0.7px, rgba(255, 255, 255, 0) 1.8px),
    radial-gradient(circle at 16px 5px, rgba(255, 255, 255, 0.70) 0 1px, rgba(255, 255, 255, 0) 2.3px);
  background-size: 24px 24px;
  background-position: 8px 0;
  animation: stable-drizzle-b 5.4s linear infinite;
}

@keyframes channel-breathe-a {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

@keyframes channel-breathe-b {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* The settle streams scroll their repeating tile by exactly one tile
   height, downwards. px endpoints so a seam is impossible at any
   rounding; the pattern at "to" is the same tile as at "from". */
@keyframes stable-drizzle-a {
  from { background-position: 0 -20px; }
  to { background-position: 0 0; }
}

@keyframes stable-drizzle-b {
  from { background-position: 8px -24px; }
  to { background-position: 8px 0; }
}

@media (prefers-reduced-motion: reduce) {
  .changelog-entry__channel::before,
  .changelog-entry__channel::after {
    animation: none;
    opacity: 0.9;
  }
}

/* The marker dot takes its channel's colour, so the row reads as one thing. */
.changelog-entry.is-stable .changelog-entry__date::before {
  background: #1e40af;
  box-shadow: 0 0 0 4px rgba(30, 64, 175, 0.18);
}

.changelog-entry__title {
  margin-top: 18px;
  font-family: var(--font-display);
  font-size: clamp(26px, 3vw, 38px);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.08;
  color: var(--text-primary);
  text-wrap: balance;
}

/* The exact build string, kept for support and bug reports. The headline
   above is the human name for the release; this is the thing you paste
   into a post, so it stays literal and stays quiet. */
.changelog-entry__version {
  margin-top: 9px;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: var(--text-xs);
  letter-spacing: 0.01em;
  color: var(--text-quaternary);
}

/* Reserved parking spot under each headline: the one floating glass
   icon glides between these as you scroll the releases. */
.changelog-entry__islot {
  width: 84px;
  height: 84px;
  margin-top: 22px;
}

.clog-float-icon {
  position: relative;
  display: block;
  width: 84px;
  height: 84px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 600ms ease;
  will-change: transform;
}

.clog-float-icon.is-on {
  opacity: 1;
}

/* The icon's single face: the Droppy Code artwork. It cross-dissolves
   with a little scale and blur on hand-off between entries, so the icon
   reads as gliding down the page rather than being swapped. Reduced
   motion keeps the dissolve and drops the movement. */
.clog-float-icon__face {
  position: absolute;
  inset: 0;
  width: 84px;
  height: 84px;
  object-fit: contain;
  opacity: 0;
  transform: scale(0.72) rotate(-8deg);
  filter: blur(7px);
  transition:
    opacity 520ms ease,
    transform 640ms cubic-bezier(0.22, 0.61, 0.36, 1),
    filter 520ms ease;
}

.clog-float-icon__face.is-front {
  opacity: 1;
  transform: scale(var(--face-scale, 1));
  filter: none;
}

@media (prefers-reduced-motion: reduce) {
  .clog-float-icon__face {
    transform: scale(var(--face-scale, 1));
    filter: none;
    transition: opacity 320ms ease;
  }
}

.changelog-entry__summary {
  font-size: var(--text-base);
  color: var(--text-secondary);
  line-height: 1.65;
}

.changelog-group {
  margin-top: 18px;
}

.changelog-group__icon {
  width: 15px;
  height: 15px;
  opacity: 0.92;
  flex-shrink: 0;
}

.changelog-group__label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 8px;
}

.changelog-item-list {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.changelog-item {
  position: relative;
  padding-left: 18px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.55;
}

.changelog-item::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.6em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--text-quaternary);
}

.changelog-more {
  margin-top: 12px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}

.changelog-more:hover {
  color: var(--text-primary);
}

@media (max-width: 720px) {
  .changelog-entry {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  .changelog-entry__pin {
    position: static;
  }
}

/* Soft halo that follows the hero letterforms. Droppy's is paper-coloured
   for its light art; here it is canvas-coloured for the dark page. One
   class on the copy group covers kicker, title and subtitle at once. */
.scrub-frost {
  text-shadow:
    0 0 0.05em rgba(11, 14, 20, 0.98),
    0 0 0.16em rgba(11, 14, 20, 0.94),
    0 0 0.4em rgba(11, 14, 20, 0.88),
    0 0 0.85em rgba(11, 14, 20, 0.72);
}
.scrub-frost :is(a[class*="btn"], button, .btn, .e2ee, .board-tools) {
  text-shadow: none;
}
