/* The landing (/), loaded only by layouts/home. Ported from the design export
   (pgrun-export 3/style.css); nearly all styling is inline in pages/home.html.erb, so this
   holds only what inline styles cannot: keyframes, hover states and scrollbars. The IBM Plex
   Mono @import moved to <link> tags in the layout. */

* { box-sizing: border-box; }
body { margin: 0; background: #0b0d0f; color: oklch(0.92 0.005 250); font-family: 'IBM Plex Mono', monospace; font-size: 14.5px; line-height: 1.6; }
::selection { background: oklch(0.55 0.10 235); color: #fff; }
a { color: oklch(0.78 0.10 235); text-decoration: none; }
a:hover { color: oklch(0.66 0.01 250); }

/* Not in the export: the top-right corner of the page. ::before is the dot grid from the
   sign-in page (auth.css), mirrored into this corner; ::after is a soft white light after
   resend.com — a haze plus a few blurred rays falling down-left across the hero. Both sit
   behind all content (isolation + negative z-index), and the site menu drops its background
   here so they run under it. overflow-x:clip keeps the oversized light layer from widening
   the page. */
.home-root { position: relative; isolation: isolate; overflow-x: clip; }
/* The menu is translucent everywhere now, so it no longer has to go fully clear here —
   but it is lightened over this corner so the dot grid and the light still read through it
   while the page is at the top, and the blur still carries the content that scrolls under. */
.home-root header.site-menu { background: rgba(11, 13, 15, 0.55); }
.home-root::before {
  content: ""; position: absolute; top: 0; right: 0; z-index: -2; pointer-events: none;
  width: min(760px, 100%); height: 680px;
  background: radial-gradient(rgba(255, 255, 255, 0.10) 1px, transparent 1.4px) 0 0/11px 11px;
  -webkit-mask-image: radial-gradient(100% 100% at 100% 0, #000 16%, transparent 70%);
          mask-image: radial-gradient(100% 100% at 100% 0, #000 16%, transparent 70%);
}
/* Hover effect on that grid: the dots near the pointer turn black — nothing else changes.
   Same box and grid as ::before, so each black dot lands on (and covers) a white one. The
   mask is the pointer circle intersected with ::before's corner vignette, so only dots that
   are actually showing change colour. z-index -1 puts it above ::before and the ::after
   light (both -2), which would otherwise wash the black out. home.js sets --mx/--my to the
   pointer's position in this box and toggles .is-on while the pointer is inside it. */
.home-dots-lens {
  --mx: 50%; --my: 50%;
  position: absolute; top: 0; right: 0; z-index: -1; pointer-events: none;
  width: min(760px, 100%); height: 680px;
  background: radial-gradient(#000 1.3px, transparent 1.7px) 0 0/11px 11px;
  -webkit-mask-image:
    radial-gradient(circle 150px at var(--mx) var(--my), #000 45%, transparent 100%),
    radial-gradient(100% 100% at 100% 0, #000 16%, transparent 70%);
  -webkit-mask-composite: source-in;
  mask-image:
    radial-gradient(circle 150px at var(--mx) var(--my), #000 45%, transparent 100%),
    radial-gradient(100% 100% at 100% 0, #000 16%, transparent 70%);
  mask-composite: intersect;
  opacity: 0; transition: opacity 0.35s ease;
}
.home-dots-lens.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .home-dots-lens { transition: none; } }

/* The rays fan out from the corner: a conic gradient centred there, where 180deg points
   straight down and 270deg straight left, so the bands fall down-left and flatten toward the
   left. Oversized past the top and right edges so the blur does not fade the source. */
.home-root::after {
  content: ""; position: absolute; top: -80px; right: -80px; z-index: -2; pointer-events: none;
  width: min(1520px, calc(100% + 80px)); height: 1080px;
  background:
    radial-gradient(52% 62% at 100% 0%, rgba(255, 255, 255, 0.17), rgba(255, 255, 255, 0.06) 38%, transparent 72%),
    conic-gradient(from 180deg at 100% 0%,
      transparent 0deg,
      rgba(255, 255, 255, 0.05) 22deg, transparent 34deg,
      rgba(255, 255, 255, 0.09) 50deg, transparent 60deg,
      rgba(255, 255, 255, 0.05) 67deg, transparent 77deg,
      transparent 90deg);
  filter: blur(16px);
  -webkit-mask-image: radial-gradient(100% 100% at 100% 0%, #000 0%, rgba(0, 0, 0, 0.55) 40%, transparent 85%);
          mask-image: radial-gradient(100% 100% at 100% 0%, #000 0%, rgba(0, 0, 0, 0.55) 40%, transparent 85%);
}

/* ---------- the demo terminal (#demo) ---------- */
/* A window: hairline border with a lit top edge, a faint blue glow under it, and
   overflow:hidden so every child is clipped to the rounded corners (the panes' square
   backgrounds used to cut the border at the bottom). */
/* ---- terminal stage (landing) -------------------------------------------
   cursor.com stands its app window on a backdrop panel instead of on the page
   itself. Here that panel is "Midnight Glass Ribbons" (term-backdrop.webp, the
   operator's artwork — the 1.3MB PNG re-encoded to 34KB of WebP): its ribbons
   sit in the margin around the window while its dark middle stays under it, so
   the terminal still reads first. The scrim on top pulls the art down towards
   the page ink and keeps the window's edges legible against it. */
.term-stage {
  position: relative;
  width: 100%;
  max-width: 1040px;
  padding: 40px;
  display: flex;
  justify-content: center;
  border: 1px solid oklch(0.26 0.01 275);
  border-radius: 22px;
  overflow: hidden;
  background: #0b0d0f url("/assets/term-backdrop-fccd09fd.webp") center / cover no-repeat;
}
.term-stage::before {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, oklch(0.13 0.005 250 / 0.3) 0%, oklch(0.13 0.005 250 / 0.55) 100%);
  pointer-events: none;
}
.term-stage > .term { position: relative; }

@media (max-width: 720px) { .term-stage { padding: 16px; border-radius: 18px; } }
@media (max-width: 560px) { .term-stage { padding: 10px; border-radius: 14px; } }

.term {
  --term-line: rgba(255, 255, 255, 0.07);
  width: 100%; max-width: 880px; overflow: hidden;
  background: #0b0d0f;
  border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 14px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 40px 100px -30px rgba(0, 0, 0, 0.9),
    0 0 90px -30px oklch(0.72 0.11 235 / 0.22);
}

.term-bar {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 12px 16px; border-bottom: 1px solid var(--term-line);
  background: linear-gradient(180deg, #16191e 0%, #101216 100%);
}
.term-lights { display: flex; gap: 8px; }
.term-lights span { width: 11px; height: 11px; border-radius: 50%; box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.35); }
.term-lights span:nth-child(1) { background: #ff5f57; }
.term-lights span:nth-child(2) { background: #febc2e; }
.term-lights span:nth-child(3) { background: #28c840; }
.term-title { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: oklch(0.85 0.01 250); }
.term-title-dim { font-weight: 400; color: oklch(0.52 0.01 250); }
.term-actions { display: flex; justify-content: flex-end; align-items: center; gap: 8px; }
.term-key {
  display: flex; align-items: center; justify-content: center; width: 24px; height: 22px;
  border: 1px solid oklch(0.3 0.008 250); border-radius: 6px; background: rgba(255, 255, 255, 0.02);
  color: oklch(0.6 0.01 250); font-size: 11px; cursor: pointer; transition: color 0.15s, border-color 0.15s;
}
.term-key:hover { color: oklch(0.92 0.005 250); border-color: oklch(0.45 0.01 250); }
.term-key-info { width: 22px; border-radius: 50%; font-style: italic; font-weight: 700; cursor: default; }
.info-wrap { position: relative; }
.info-tooltip {
  position: absolute; top: 30px; right: 0; z-index: 10; width: 240px;
  padding: 12px 14px; border: 1px solid oklch(0.28 0.008 250); border-radius: 8px; background: #12141a;
  box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.7);
  font-size: 12px; line-height: 1.6; color: oklch(0.66 0.01 250);
}

.term-panes { display: flex; }

/* Left: the branch tree, on a slightly lifted sidebar tint. */
.term-tree {
  width: 42%; max-height: 420px; overflow-y: auto; padding: 14px 10px;
  border-right: 1px solid var(--term-line); background: #0d0f12;
  font-size: 13px; line-height: 2;
}
.term-tree-head {
  display: flex; align-items: center; gap: 8px; padding: 0 8px 8px; margin-bottom: 6px;
  border-bottom: 1px dashed var(--term-line); color: oklch(0.88 0.01 250); font-weight: 700;
}
/* "live" wears the blue of the site's "sign up" button, like the branch times below it. */
.term-badge {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6px; padding: 0 8px;
  border: 1px solid oklch(0.72 0.11 235 / 0.4); border-radius: 999px; background: oklch(0.72 0.11 235 / 0.1);
  font-size: 11px; line-height: 20px; font-weight: 600; color: oklch(0.78 0.1 235);
}
.term-badge i { width: 6px; height: 6px; border-radius: 50%; background: oklch(0.72 0.11 235); box-shadow: 0 0 6px oklch(0.72 0.11 235); animation: pulse 2s ease-in-out infinite; }

.branch-row { display: flex; gap: 7px; align-items: center; padding: 0 8px; border-radius: 6px; animation: fadeIn 0.35s ease-out; transition: background 0.15s; }
.branch-row:hover { background: rgba(255, 255, 255, 0.035); }
.branch-row.removing { animation: fadeOut 0.3s ease-out forwards; }
.br-connector { color: oklch(0.36 0.008 250); white-space: pre; }
.br-icon { flex-shrink: 0; }
.br-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: oklch(0.8 0.03 235); }
.br-status { min-width: 84px; font-size: 12px; font-variant-numeric: tabular-nums; }

/* Right: the shell. */
.term-history { width: 58%; height: 420px; overflow-y: scroll; padding: 16px 18px; font-size: 13px; line-height: 1.8; }
.hb-block { margin-bottom: 16px; }
.hb-head { display: flex; align-items: center; gap: 8px; line-height: 1.5; }
.hb-path { flex-shrink: 0; color: oklch(0.72 0.11 235); }
.hb-dollar { flex-shrink: 0; color: oklch(0.52 0.01 250); }
/* pre-wrap keeps the typed spaces but lets a long command wrap on a narrow pane. */
.hb-cmd { min-width: 0; white-space: pre-wrap; overflow-wrap: break-word; color: oklch(0.92 0.005 250); }
.hb-cursor {
  display: inline-block; width: 7px; height: 15px; margin-left: -6px; flex-shrink: 0;
  background: oklch(0.72 0.11 235); box-shadow: 0 0 8px oklch(0.72 0.11 235 / 0.8);
  animation: blink 1s step-end infinite;
}
.hb-outputs { padding-left: 16px; border-left: 1px solid var(--term-line); margin: 6px 0 0 3px; }
.hb-line { margin-top: 4px; animation: fadeIn 0.25s ease-out; }

/* Bottom: a tmux-like status bar; #termCount is kept current by home.js. */
.term-status {
  display: flex; align-items: stretch; border-top: 1px solid var(--term-line); background: #0d0f12;
  font-size: 11.5px; line-height: 28px; color: oklch(0.62 0.01 250);
}
.term-status-seg { display: inline-flex; align-items: center; gap: 7px; padding: 0 12px; border-right: 1px solid var(--term-line); white-space: nowrap; }
.term-status-mode { background: oklch(0.72 0.11 235); color: #0b0d0f; font-weight: 700; border-right: 0; }
.term-status-dot { width: 6px; height: 6px; border-radius: 50%; background: oklch(0.75 0.11 160); }
.term-status-dim { color: oklch(0.5 0.01 250); }
.term-status-right { margin-left: auto; border-right: 0; border-left: 1px solid var(--term-line); font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .term-badge i, .hb-cursor { animation: none; }
  .term-key, .branch-row { transition: none; }
}

/* "Why pgrun" cards lift their border a step on hover. !important: the border is inline. */
.home-why-card { transition: border-color 0.15s ease; }
.home-why-card:hover { border-color: oklch(0.36 0.008 250) !important; }
@media (prefers-reduced-motion: reduce) { .home-why-card { transition: none; } }

/* ---- testimonial marquee (landing) ------------------------------------
   The "Beyond expectations" band, built like resend.com's: wide quote cards
   drifting right to left, lit from the top, with no bottom border — the
   .home-mq-fade overlay dissolves each card into the page instead.

   Each row is one track carrying the same six cards twice, so animating it to
   -50% lands exactly where it started and the loop has no seam. The trailing
   padding on a set (rather than a gap on the track) is what keeps the two
   halves the same width, which is what makes -50% exact. */
.home-mq {
  overflow: hidden;
  /* Cards run off both edges instead of stopping at a container, so fade them
     out rather than cutting them. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.home-mq-track {
  display: flex;
  width: max-content;
  animation: home-mq-scroll var(--mq-speed, 78s) linear infinite;
  animation-delay: var(--mq-delay, 0s);
  will-change: transform;
}
.home-mq-set { display: flex; gap: 20px; padding-right: 20px; }
@keyframes home-mq-scroll { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-50%, 0, 0); } }

/* Reading a moving quote is impossible, so any pointer or keyboard focus over
   the row stops it. */
.home-mq:hover .home-mq-track,
.home-mq:focus-within .home-mq-track { animation-play-state: paused; }

.home-mq-card {
  position: relative;
  flex: 0 0 auto;
  width: 420px;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 26px 28px 28px;
  border: 1px solid oklch(0.26 0.008 250);
  border-bottom: 0;
  border-radius: 14px 14px 0 0;
  background: linear-gradient(180deg, oklch(0.32 0.01 250 / 0.28) 0%, oklch(0.32 0.01 250 / 0) 70%);
}
/* Covers the card, not its text (the quote and byline sit above it), so the
   border and the top light fade into the page towards the bottom edge. */
.home-mq-fade {
  position: absolute; inset: -1px -1px 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(11, 13, 15, 0) 55%, #0b0d0f 100%);
}
.home-mq-quote {
  position: relative;
  margin: 0 0 24px;
  font-size: 14px;
  line-height: 1.75;
  color: oklch(0.8 0.01 250);
}
.home-mq-by { position: relative; margin-top: auto; display: flex; align-items: center; gap: 13px; }

/* Two overlapping discs standing in for Resend's company logo + photo pair. */
.home-mq-discs { display: flex; align-items: center; flex-shrink: 0; }
.home-mq-disc {
  width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.02em;
}
.home-mq-disc-org {
  background: linear-gradient(140deg, oklch(0.72 0.11 235 / 0.22), oklch(0.72 0.11 235 / 0.06));
  border: 1px solid oklch(0.72 0.11 235 / 0.35);
  color: oklch(0.82 0.09 235);
}
.home-mq-disc-person {
  margin-left: -11px;
  background: linear-gradient(140deg, oklch(0.34 0.01 250), oklch(0.22 0.008 250));
  border: 1px solid oklch(0.38 0.008 250);
  /* A ring of the page colour so the two discs read as stacked, not merged. */
  box-shadow: 0 0 0 3px #0b0d0f;
  color: oklch(0.88 0.005 250);
}
.home-mq-who { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.home-mq-name { font-size: 13.5px; font-weight: 600; color: oklch(0.9 0.005 250); }
.home-mq-role { font-size: 12.5px; color: oklch(0.52 0.01 250); }

/* The heading takes Resend's light-to-grey wash instead of flat white. */
.home-quotes-title {
  background: linear-gradient(180deg, oklch(0.98 0.003 250) 30%, oklch(0.62 0.01 250) 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

@media (prefers-reduced-motion: reduce) {
  /* No drifting: one static set the reader can push sideways themselves. */
  .home-mq-track { animation: none; }
  .home-mq { overflow-x: auto; }
  .home-mq-set + .home-mq-set { display: none; }
}
@media (max-width: 560px) {
  .home-mq-card { width: 84vw; padding: 22px 22px 24px; }
  .home-mq-set { gap: 14px; padding-right: 0; }
  /* A drifting row on a 390px screen never shows a whole card — it shows two sliced ones,
     so the quotes read as broken text. Stop the drift and hand the row to the thumb: one
     card snaps into place at a time, and the duplicate set (there only to hide the loop's
     seam) goes with the animation. */
  .home-mq-track { animation: none; }
  .home-mq-set + .home-mq-set { display: none; }
  .home-mq {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-left: 20px;
    padding: 0 20px;
    -webkit-mask-image: none;
    mask-image: none;
    scrollbar-width: none;
  }
  .home-mq::-webkit-scrollbar { display: none; }
  .home-mq-card { scroll-snap-align: start; }
  /* The band is full-bleed on purpose — the phone gutter every other section
     gets would stop the row short of both edges. The heading keeps its own. */
  .home-quotes-band { padding-left: 0 !important; padding-right: 0 !important; }
  .home-quotes-head { padding-left: 20px !important; padding-right: 20px !important; }
}

#historyPane { scrollbar-width: none; }
#historyPane::-webkit-scrollbar { display: none; }
.branches-pane { scrollbar-width: none; }
.branches-pane::-webkit-scrollbar { display: none; }

@keyframes fadeIn { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
/* ---- "never touch production" (landing) --------------------------------
   The guarantees on the left, the path on the right: three nodes joined by
   dotted rails, with the one route that does not exist struck out under them.
   Same card vocabulary as the lifecycle pipeline, so the two read as a pair. */
.safe-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: start; }
.safe-list { list-style: none; margin: 0; padding: 6px 0 0; display: flex; flex-direction: column; gap: 26px; }
.safe-item { display: flex; gap: 14px; text-align: left; }
.safe-check {
  flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: oklch(0.8 0.1 160);
  background: oklch(0.75 0.11 160 / 0.1);
  border: 1px solid oklch(0.75 0.11 160 / 0.3);
}
.safe-item-text { display: flex; flex-direction: column; gap: 4px; }
.safe-item-text strong { font-size: 14.5px; font-weight: 600; color: oklch(0.9 0.005 250); }
.safe-item-text span { font-size: 14px; line-height: 1.6; color: oklch(0.6 0.01 250); }

.safe-flow {
  padding: 26px 26px 22px;
  border: 1px solid oklch(0.24 0.008 250);
  border-radius: 16px;
  background: radial-gradient(circle, oklch(1 0 0 / 0.045) 1px, transparent 1.2px) 0 0 / 16px 16px;
}
.safe-node {
  display: flex; align-items: center; gap: 13px;
  padding: 13px 16px;
  border: 1px solid oklch(0.26 0.008 250);
  border-radius: 10px;
  background: oklch(0.17 0.006 250);
}
.safe-node-dot { flex-shrink: 0; width: 9px; height: 9px; border-radius: 50%; }
.safe-node-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.safe-node-text strong { font-size: 14px; font-weight: 600; color: oklch(0.9 0.005 250); }
.safe-node-text span { font-size: 11.5px; color: oklch(0.5 0.01 250); }
.safe-node-prod .safe-node-dot { background: oklch(0.6 0.01 250); }
.safe-node-pgrun .safe-node-dot { background: oklch(0.72 0.11 235); box-shadow: 0 0 8px oklch(0.72 0.11 235 / 0.7); }
.safe-node-pgrun strong { color: oklch(0.82 0.09 235); }
.safe-node-branch .safe-node-dot { background: oklch(0.75 0.11 160); box-shadow: 0 0 8px oklch(0.75 0.11 160 / 0.6); }
.safe-node-branch strong { color: oklch(0.84 0.1 160); }
/* The join between two nodes — a dotted drop, like the pipeline's connectors. */
.safe-rail { height: 22px; margin-left: 20px; border-left: 1.5px dotted oklch(0.3 0.008 250); }
.safe-blocked {
  display: flex; align-items: center; gap: 10px;
  width: fit-content; margin: 22px auto 0; padding: 9px 18px;
  border: 1px dashed oklch(0.63 0.19 25 / 0.35);
  border-radius: 999px;
}
.safe-blocked span:first-child { color: oklch(0.63 0.19 25); font-size: 12px; }
.safe-blocked span:last-child { font-size: 12.5px; color: oklch(0.5 0.01 250); text-decoration: line-through; }
@media (max-width: 820px) { .safe-grid { grid-template-columns: 1fr; gap: 36px; } }

/* ---- "it's just Postgres" (landing) ------------------------------------
   The connection string is the whole argument, so it is a code card now
   rather than an outlined chip; the three absences sit under it struck out. */
.pg-code {
  display: flex; align-items: center; gap: 14px;
  max-width: 560px; margin: 0 auto 28px;
  padding: 16px 18px;
  border: 1px solid oklch(0.3 0.008 250);
  border-radius: 12px;
  background: oklch(0.16 0.006 250);
  box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.05), 0 20px 50px -30px rgba(0, 0, 0, 0.9);
  text-align: left;
}
.pg-code-tag {
  flex-shrink: 0; padding: 3px 8px; border-radius: 5px;
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: oklch(0.6 0.01 250); background: oklch(0.24 0.008 250);
}
.pg-code code { font-size: 14.5px; min-width: 0; overflow-wrap: anywhere; }
.pg-code-key { font-weight: 700; color: oklch(0.92 0.005 250); }
.pg-code-eq { color: oklch(0.5 0.01 250); }
.pg-code-scheme { color: oklch(0.78 0.09 235); }
.pg-code-rest { color: oklch(0.55 0.01 250); }

.pg-nos { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-bottom: 48px; }
.pg-no {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px; border-radius: 999px;
  border: 1px solid oklch(0.22 0.008 250);
  font-size: 13px; color: oklch(0.5 0.01 250);
}
.pg-no i { font-style: normal; font-size: 11px; color: oklch(0.63 0.19 25 / 0.8); }
.pg-no s { text-decoration: line-through; }

.pg-label {
  display: block; margin-bottom: 18px;
  font-size: 12px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: oklch(0.45 0.008 250);
}
.pg-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.pg-chip {
  padding: 10px 18px; border-radius: 8px;
  border: 1px solid oklch(0.24 0.008 250);
  font-size: 14px; font-weight: 600; color: oklch(0.7 0.01 250);
  transition: border-color 0.15s ease, color 0.15s ease;
}
.pg-chip:hover { border-color: oklch(0.36 0.008 250); color: oklch(0.88 0.005 250); }
@media (prefers-reduced-motion: reduce) { .pg-chip { transition: none; } }

/* ---- free-credit voucher (landing) -------------------------------------
   The amount carries the section, so it gets the near-white weight and the
   card gets the accent bloom; the copy sits beside it across a hairline. */
.credit-card {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 32px;
  padding: 34px 36px;
  border: 1px solid oklch(0.26 0.01 250);
  border-radius: 16px;
  background: radial-gradient(circle, oklch(1 0 0 / 0.04) 1px, transparent 1.2px) 0 0 / 16px 16px;
}
.credit-card::before {
  content: ""; position: absolute; left: -60px; top: 50%;
  width: 320px; height: 320px; transform: translateY(-50%);
  background: radial-gradient(50% 50% at 50% 50%, oklch(0.72 0.11 235 / 0.14), transparent 70%);
  pointer-events: none;
}
.credit-amount { position: relative; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 2px; }
.credit-amount strong { font-size: 44px; font-weight: 700; letter-spacing: -0.03em; color: oklch(0.97 0.003 250); line-height: 1; }
.credit-amount span { font-size: 11.5px; letter-spacing: 0.06em; text-transform: uppercase; color: oklch(0.55 0.01 250); }
.credit-rule { flex-shrink: 0; align-self: stretch; width: 1px; background: oklch(0.26 0.008 250); }
.credit-body { position: relative; }
.credit-body h2 { font-size: 20px; font-weight: 700; margin: 0 0 8px; color: oklch(0.92 0.005 250); letter-spacing: -0.01em; }
.credit-body p { font-size: 14.5px; line-height: 1.7; color: oklch(0.62 0.01 250); margin: 0; }
@media (max-width: 620px) {
  .credit-card { flex-direction: column; gap: 20px; text-align: center; padding: 28px 22px; }
  .credit-rule { width: auto; height: 1px; align-self: stretch; }
}

/* ---- branch lifecycle pipeline (landing) --------------------------------
   Ported from the design export (~/Downloads/pgrun Pipeline Section.html) with
   its palette mapped onto this page's: the export's #101113 / #E4E4E4 pair
   becomes our elevated grey and the same near-white highlight, so a lit pill
   reads like the lamp dot in the menu rather than a second accent colour.

   One 14s lap drives everything. Each step owns three keyframe sets keyed by
   its position in the lap — vizNode<n> (the pill), vizLabel<n> (its text) and
   dotTravel<n> (the dot crossing the connector that follows it) — so the pills
   light in sequence and stay lit until the lap resets at 84–88%. */
.pl-card {
  position: relative;
  max-width: 820px;
  margin: 0 auto;
  padding: 56px 60px 66px;
  border: 1px solid oklch(0.24 0.008 250);
  border-radius: 20px;
  /* The export's dotted field, in this page's ink. */
  background: radial-gradient(circle, oklch(1 0 0 / 0.05) 1px, transparent 1.2px) 0 0 / 16px 16px;
}
.pl-row { display: flex; align-items: center; gap: 10px; position: relative; }
.pl-row-bottom { margin-top: 56px; }
.pl-cell { position: relative; display: flex; flex-direction: column; align-items: center; gap: 8px; }

.pl-pill {
  padding: 12px 22px;
  border-radius: 10px;
  background: oklch(0.19 0.006 250);
  border: 1px solid oklch(0.3 0.008 250);
  animation-duration: 14s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  transition: background 0.6s ease, border-color 0.6s ease;
}
.pl-lab {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 500; white-space: nowrap;
  color: oklch(0.62 0.01 250);
  animation-duration: 14s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  transition: color 0.6s ease;
}
.pl-lab svg { flex-shrink: 0; }
.pl-cap { font-size: 11px; color: oklch(0.5 0.01 250); white-space: nowrap; text-align: center; }

/* The run between two pills. It stretches, so the row spaces itself. */
.pl-dots {
  position: relative; flex: 1; min-width: 20px;
  border-top: 1.5px dotted oklch(0.3 0.008 250);
  margin-top: 20px; align-self: flex-start;
}
.pl-dot, .pl-conn-dot {
  position: absolute; width: 6px; height: 6px; border-radius: 50%;
  background: #fff; box-shadow: 0 0 6px 1px rgba(255, 255, 255, 0.7);
  opacity: 0;
  animation-duration: 14s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.pl-dot { top: -3px; left: 0; }

/* Test drops to the second row, which is walked right to left. */
.pl-conn {
  position: absolute; left: 50%; top: 100%;
  width: 1.5px; height: 56px;
  border-left: 1.5px dotted oklch(0.3 0.008 250);
  transform: translateX(-50%);
}
.pl-conn-dot { left: -3px; top: 0; }

@keyframes dotTravel0 { 0%, 2% { opacity: 0; left: 0%; } 3% { opacity: 1; } 9% { left: 100%; opacity: 1; } 10% { opacity: 0; } 100% { opacity: 0; } }
@keyframes dotTravel1 { 0%, 14% { opacity: 0; } 15% { opacity: 1; left: 0%; } 21% { left: 100%; opacity: 1; } 22% { opacity: 0; } 100% { opacity: 0; } }
@keyframes dotTravel2 { 0%, 26% { opacity: 0; } 27% { opacity: 1; left: 0%; } 33% { left: 100%; opacity: 1; } 34% { opacity: 0; } 100% { opacity: 0; } }
@keyframes dotTravel4 { 0%, 52% { opacity: 0; } 53% { opacity: 1; left: 100%; } 55% { left: 0%; opacity: 1; } 56% { opacity: 0; } 100% { opacity: 0; } }
@keyframes dotTravel5 { 0%, 64% { opacity: 0; } 65% { opacity: 1; left: 100%; } 67% { left: 0%; opacity: 1; } 68% { opacity: 0; } 100% { opacity: 0; } }
@keyframes connDown { 0%, 42% { opacity: 0; top: 0%; } 43% { opacity: 1; } 45% { top: 100%; opacity: 1; } 46% { opacity: 0; } 100% { opacity: 0; } }

/* PR is lit for the whole lap — it is what started the run. */
@keyframes vizNode0 { 0%, 100% { background: #e4e4e4; border-color: #e4e4e4; } }
@keyframes vizNode1 { 0%, 4% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } 8%, 84% { background: #e4e4e4; border-color: #e4e4e4; } 88%, 100% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } }
@keyframes vizNode2 { 0%, 16% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } 20%, 84% { background: #e4e4e4; border-color: #e4e4e4; } 88%, 100% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } }
@keyframes vizNode3 { 0%, 28% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } 32%, 84% { background: #e4e4e4; border-color: #e4e4e4; } 88%, 100% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } }
@keyframes vizNode4 { 0%, 40% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } 44%, 84% { background: #e4e4e4; border-color: #e4e4e4; } 88%, 100% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } }
@keyframes vizNode5 { 0%, 52% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } 56%, 84% { background: #e4e4e4; border-color: #e4e4e4; } 88%, 100% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } }
@keyframes vizNode6 { 0%, 64% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } 68%, 84% { background: #e4e4e4; border-color: #e4e4e4; } 88%, 100% { background: oklch(0.19 0.006 250); border-color: oklch(0.3 0.008 250); } }
@keyframes vizLabel0 { 0%, 100% { color: #0b0d0f; } }
@keyframes vizLabel1 { 0%, 4% { color: oklch(0.62 0.01 250); } 8%, 84% { color: #0b0d0f; } 88%, 100% { color: oklch(0.62 0.01 250); } }
@keyframes vizLabel2 { 0%, 16% { color: oklch(0.62 0.01 250); } 20%, 84% { color: #0b0d0f; } 88%, 100% { color: oklch(0.62 0.01 250); } }
@keyframes vizLabel3 { 0%, 28% { color: oklch(0.62 0.01 250); } 32%, 84% { color: #0b0d0f; } 88%, 100% { color: oklch(0.62 0.01 250); } }
@keyframes vizLabel4 { 0%, 40% { color: oklch(0.62 0.01 250); } 44%, 84% { color: #0b0d0f; } 88%, 100% { color: oklch(0.62 0.01 250); } }
@keyframes vizLabel5 { 0%, 52% { color: oklch(0.62 0.01 250); } 56%, 84% { color: #0b0d0f; } 88%, 100% { color: oklch(0.62 0.01 250); } }
@keyframes vizLabel6 { 0%, 64% { color: oklch(0.62 0.01 250); } 68%, 84% { color: #0b0d0f; } 88%, 100% { color: oklch(0.62 0.01 250); } }

@media (prefers-reduced-motion: reduce) {
  /* Nothing cycles: the lap holds with PR lit and the dots parked. */
  .pl-pill, .pl-lab, .pl-dot, .pl-conn-dot { animation: none; }
  .pl-dot, .pl-conn-dot { display: none; }
}
/* Phones: the export's own rules (rows wrap, captions go rather than squeeze
   the pills), minus its connectors. A wrapped row left dotted runs ending in
   mid-air and a dot crossing empty space, and nothing joins any more once the
   pills stack — so the lines go, and the bottom row flips back to lap order
   (Verify → Merge → Clean up) since it is no longer being read right to left. */
@media (max-width: 720px) {
  .pl-card { padding: 40px 24px; }
  .pl-row { flex-wrap: wrap; gap: 12px; justify-content: center; }
  .pl-row-bottom .pl-cell { order: var(--pl-order, 0); }
  .pl-cap { display: none; }
  .pl-dots, .pl-conn { display: none; }
}

/* The menu's own rules (products dropdown, flashing dot, phone wrap) live in chrome.css with
   the shared/_site_nav partial, which every public page renders. */
.info-tooltip { display: none; }
.info-wrap:hover .info-tooltip { display: block; animation: fadeIn 0.15s ease-out; }

/* Not in the export, which only laid out for desktop: phones scrolled sideways (the
   five-column steps grid and the footer links overflowed) and the two demo panes were
   squeezed to half of ~310px. Phone-only, so desktop renders exactly as designed.
   !important because the rules it overrides are inline. */
@media (max-width: 720px) {
  .term-panes { flex-direction: column; }
  .term-tree { width: 100%; max-height: 220px; border-right: 0; border-bottom: 1px solid var(--term-line); }
  .term-history { width: 100%; height: 320px; }
}
@media (max-width: 560px) {
  /* 320px of shell under a 220px tree is mostly empty space on a phone. */
  .term-history { height: 210px; }
  .term-tree { max-height: 170px; }
  /* The title bar is a 1fr/auto/1fr grid: by 320px the centre column had nothing left, so
     the title wrapped onto two lines and its mark landed on top of the traffic lights.
     Drop the mark and let the title sit left of the keys instead of dead centre. */
  .term-bar { gap: 10px; }
  .term-title { font-size: 12px; justify-content: flex-start; }
  .term-title svg { display: none; }
  .term-title-dim, .term-status-dim, .term-status-conn { display: none; }
  .home-steps { grid-template-columns: repeat(2, 1fr) !important; row-gap: 48px; }
  /* Five cells over two columns left the last one's top rule stopping mid-row. */
  .home-steps > div:last-child { grid-column: 1 / -1; }
  /* 60px between the number and the title is desktop rhythm; on a phone it is a hole. */
  .home-steps > div > div:first-child { margin-bottom: 24px !important; }
  .home-foot-links { flex-wrap: wrap; row-gap: 8px; }
}

/* ---------- phones ----------
   The page is the design export, so its spacing and type sizes live in inline styles and
   need !important to move. Everything here is width only: nothing changes above 560px. */
@media (max-width: 560px) {
  /* One gutter for every band, instead of the export's 40px. */
  .home-root > header,
  .home-root > section,
  .home-root > footer { padding-left: 20px !important; padding-right: 20px !important; }
  .home-root > header { padding-top: 36px !important; }

  /* 42px set three lines of "Every agent gets its own Postgres." on a 390px screen and
     pushed everything else below the fold. */
  .home-root h1 { font-size: clamp(27px, 8.4vw, 40px) !important; line-height: 1.12 !important; }
  .home-root h2 { font-size: clamp(21px, 6vw, 30px) !important; line-height: 1.2 !important; }

  /* The install command broke mid-URL with the copy label stranded beside it. Stack it:
     prompt + command on one line that may wrap at the slashes, copy underneath. */
  /* Flex wrapping put the `$` on a line of its own, because a flex item wraps whole rather
     than breaking mid-token. Plain inline text wraps the way a command should: the prompt
     stays with the command, and the URL breaks only where it runs out of room. */
  #copyInstall {
    display: block !important;
    text-align: center;
    padding: 12px 14px !important;
    font-size: 12.5px !important;
    max-width: 100%;
  }
  #copyInstall > span { display: inline; }
  #copyInstall > span:nth-child(2) { word-break: break-word; }
  #copyLabel { display: block !important; margin-top: 8px; }

  /* ---- phone rhythm ----
     The bands are spaced for a 1280px canvas: 160–180px of air between sections reads as
     dead screens when the viewport is 390px wide. One uniform, tighter step instead —
     except the demo, which is deliberately tucked under the headline. */
  .home-root > section:not(#demo) { padding-top: 96px !important; }
  .home-root > section:last-of-type { padding-bottom: 64px !important; }

  /* The two closing buttons sat side by side at ~45% each, so both labels broke mid-phrase
     ("Connect your / Postgres"). Full width, stacked, one line each. */
  .cta-actions { flex-direction: column; width: 100%; }
  .cta-actions a { text-align: center; }
}

@media (max-width: 400px) {
  /* The install one-liner is 44 characters; at 12.5px it overflowed by a hair and orphaned
     "| sh" on its own line. */
  #copyInstall { font-size: 11.5px !important; padding: 12px 10px !important; }
}

/* ---- pgbot mascot (hero), ported from pgbot.dev ------------------------
   A 76px white disc that sits above the headline: two eyes lean toward the
   pointer, 0/1 digits drift and scatter under it, a soft glow follows it
   inside the face. Everything is inside the disc, so it reads the same on
   this dark hero as it does on pgbot.dev's light one. Sizes/timings are the
   original's; the eye colour is the landing's ink rather than pure black.
   home.js only runs the pointer maths — with JS off (or reduced motion) the
   disc still renders, eyes centred. */
.pgb {
  width: 76px; height: 76px; border-radius: 50%; margin: 0 0 26px;
  background: #fff; border: 2px solid #0b0d0f; position: relative; overflow: hidden;
  box-shadow: 0 10px 24px rgba(0,0,0,.45), 0 3px 8px rgba(0,0,0,.3),
              inset -8px -10px 18px rgba(0,0,0,.08), inset 8px 10px 16px rgba(255,255,255,.8);
}
.pgb-shine { position: absolute; width: 32px; height: 19px; left: 12px; top: 10px; border-radius: 50%;
  background: rgba(255,255,255,.78); filter: blur(4px); z-index: 3; pointer-events: none; }
.pgb-digits { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.pgb-digit { position: absolute; color: rgba(11,13,15,.28); font-size: 10px; font-weight: 900; line-height: 1;
  user-select: none; transition: transform .16s ease-out, opacity .16s ease-out, color .16s ease-out;
  will-change: transform, opacity; }
.pgb-glow { position: absolute; width: 29px; height: 29px; border-radius: 50%;
  background: radial-gradient(circle, rgba(11,13,15,.08), rgba(11,13,15,0));
  transform: translate(-50%,-50%); pointer-events: none; z-index: 2; opacity: .8;
  transition: left .08s linear, top .08s linear; }
.pgb-eyes { position: absolute; left: 50%; top: 50%; width: 39px; height: 25px; transform: translate(-50%,-50%);
  transition: transform .06s linear; z-index: 4; }
.pgb-eye { position: absolute; width: 11px; height: 21px; background: #0b0d0f; border-radius: 999px;
  transform-origin: center; transition: transform .08s linear;
  box-shadow: 0 1px 2px rgba(0,0,0,.2), inset 0 -1px 1px rgba(255,255,255,.08); }
.pgb-eye.left { left: 4px; top: 1px; transform: rotate(16deg) scale(1); }
.pgb-eye.right { right: 4px; top: 1px; transform: rotate(16deg) scale(1); }
@media (prefers-reduced-motion: reduce) {
  .pgb-digit, .pgb-glow, .pgb-eyes, .pgb-eye { transition: none; }
}

/* ---------- /contact (pages in this design that need form controls) ----------
   Inline styles can't do :focus or placeholders, so the form's fields live here. */
.ct-field {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 14px; color: oklch(0.92 0.005 250);
  background: oklch(0.15 0.006 250); border: 1px solid oklch(0.28 0.008 250); border-radius: 6px;
  padding: 10px 12px; transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.ct-field::placeholder { color: oklch(0.48 0.01 250); }
.ct-field:focus { outline: none; border-color: oklch(0.72 0.11 235); box-shadow: 0 0 0 3px oklch(0.72 0.11 235 / 0.18); }
select.ct-field { appearance: none; padding-right: 34px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, oklch(0.62 0.01 250) 50%), linear-gradient(135deg, oklch(0.62 0.01 250) 50%, transparent 50%);
  background-position: calc(100% - 17px) 55%, calc(100% - 12px) 55%; background-size: 5px 5px; background-repeat: no-repeat; }
textarea.ct-field { resize: vertical; min-height: 140px; line-height: 1.6; }
.ct-label { display: block; font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: oklch(0.62 0.01 250); margin-bottom: 8px; }
.ct-btn { font: inherit; font-size: 13.5px; font-weight: 600; color: #0b0d0f; background: oklch(0.72 0.11 235);
  border: 0; border-radius: 6px; padding: 11px 22px; cursor: pointer; transition: background 0.15s ease; }
.ct-btn:hover { background: oklch(0.78 0.10 235); }
.ct-btn:focus-visible { outline: 2px solid oklch(0.78 0.10 235); outline-offset: 3px; }
/* Honeypot: off-screen, not display:none (some bots skip hidden inputs). */
.ct-hp { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- /pricing: calculator sliders, breakdown, managed table, FAQ ---------- */
.pc-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 10px; }
.pc-row label { font-size: 13.5px; color: oklch(0.78 0.01 250); }
.pc-row output { font-size: 15px; font-weight: 600; color: oklch(0.92 0.005 250); font-variant-numeric: tabular-nums; }
.pc-range { --pct: 50%; -webkit-appearance: none; appearance: none; width: 100%; height: 22px; margin: 0; background: transparent; cursor: pointer; }
.pc-range::-webkit-slider-runnable-track { height: 4px; border-radius: 2px;
  background: linear-gradient(to right, oklch(0.72 0.11 235) var(--pct), oklch(0.28 0.008 250) var(--pct)); }
.pc-range::-moz-range-track { height: 4px; border-radius: 2px; background: oklch(0.28 0.008 250); }
.pc-range::-moz-range-progress { height: 4px; border-radius: 2px; background: oklch(0.72 0.11 235); }
.pc-range::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; margin-top: -6px; border-radius: 50%;
  background: #fff; border: 2px solid oklch(0.72 0.11 235); box-shadow: 0 0 0 4px oklch(0.72 0.11 235 / 0.15); }
.pc-range::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: #fff; border: 2px solid oklch(0.72 0.11 235); }
.pc-range:focus-visible { outline: none; }
.pc-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 5px oklch(0.72 0.11 235 / 0.35); }
.pc-scale { display: flex; justify-content: space-between; font-size: 11.5px; color: oklch(0.5 0.01 250); margin-top: 4px; }
.pc-help { font-size: 12.5px; line-height: 1.6; color: oklch(0.55 0.01 250); margin: 10px 0 0; }
.pc-help code { color: oklch(0.78 0.01 250); }
.pc-line { border-top: 1px solid oklch(0.24 0.008 250); padding: 14px 0; }
.pc-line-h { display: flex; justify-content: space-between; gap: 12px; font-size: 14.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.pc-line-d { font-size: 12.5px; color: oklch(0.55 0.01 250); margin-top: 4px; line-height: 1.55; }
.pc-cta { display: block; text-align: center; margin-top: 22px; color: #0b0d0f !important; background: oklch(0.72 0.11 235);
  padding: 11px 20px; border-radius: 6px; font-weight: 600; font-size: 13.5px; }
.pc-cta:hover { background: oklch(0.78 0.10 235); }
.home-root .pr-tbl { width: 100%; border-collapse: collapse; font-size: 14px; }
.home-root .pr-tbl th, .home-root .pr-tbl td { text-align: left; padding: 12px 18px; border-bottom: 1px solid oklch(0.24 0.008 250); color: oklch(0.7 0.01 250); }
.home-root .pr-tbl th { font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: oklch(0.5 0.01 250); font-weight: 600; }
.home-root .pr-tbl tr:last-child td { border-bottom: 0; }
.home-root .pr-tbl td:first-child { color: oklch(0.92 0.005 250); font-weight: 600; }
.pc-faq { border-top: 1px solid oklch(0.24 0.008 250); }
.pc-q { border-bottom: 1px solid oklch(0.24 0.008 250); }
.pc-q summary { list-style: none; cursor: pointer; padding: 18px 32px 18px 0; position: relative; font-size: 15px; font-weight: 600; color: oklch(0.92 0.005 250); }
.pc-q summary::-webkit-details-marker { display: none; }
.pc-q summary::after { content: "+"; position: absolute; right: 4px; top: 16px; font-size: 18px; color: oklch(0.72 0.11 235); }
.pc-q[open] summary::after { content: "−"; }
.pc-q p { margin: 0 0 18px; font-size: 14px; line-height: 1.7; color: oklch(0.62 0.01 250); }
