/* Loom Systems — site styles. Styles through tokens.css only; never
   redefine color/spacing values here, add or adjust tokens instead.

   Contrast note: --gold and --fg-muted read strong as *decorative* marks
   (dividers, thread insets, icon strokes, focus rings — all judged at the
   3:1 non-text threshold) but fall short of 4.5:1 against --bg/--card for
   real text at normal sizes. Real content always uses --fg or --indigo,
   both of which clear AA in both themes; --gold/--fg-muted are reserved
   for non-text or graphical-icon use only. */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  font-size: 16px;
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--fg);
  background-color: var(--bg);
  line-height: 1.6;
  min-height: 100vh;
  position: relative;
}

/* Faded warp ground: vertical warp lines visible near the top of the
   viewport, dissolving into the flat page background further down. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--bg);
  background-image:
    linear-gradient(180deg, transparent 0%, transparent 12%, var(--bg) 52%),
    repeating-linear-gradient(90deg,
      var(--rule) 0, var(--rule) 1px,
      transparent 1px, transparent var(--warp-spacing));
}

img, svg { max-width: 100%; display: block; }

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; }

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }

input, textarea { font: inherit; color: inherit; }

.container {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 var(--space-6);
  position: relative;
  z-index: 1;
}

.container--narrow { max-width: 760px; }

/* ============================
   ACCESSIBILITY
   ============================ */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--indigo);
  color: var(--bg);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  font-weight: 600;
}
.skip-link:focus { left: var(--space-4); top: var(--space-4); }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================
   TYPE
   ============================ */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.15;
  color: var(--fg);
}

p { color: var(--fg); }

.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold-text);
  margin-bottom: var(--space-3);
}

.mono { font-family: var(--font-mono); }

/* ============================
   BUTTONS
   ============================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--text-sm);
  cursor: pointer;
  border: 1px solid transparent;
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}
.btn-primary {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
  box-shadow: inset 0 -3px 0 var(--btn-primary-line);
}
.btn-primary:hover { filter: brightness(1.12); }
.btn-outline { background: transparent; border-color: var(--rule); color: var(--fg); }
.btn-outline:hover { border-color: var(--gold); color: var(--indigo); }

/* ============================
   HEADER / NAV
   ============================ */
.site-header {
  padding: var(--space-4) 0;
  position: sticky;
  top: 0;
  background: var(--bg);
  border-bottom: 1px solid var(--rule);
  z-index: 2;
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}
.site-logo { display: flex; align-items: center; }
.logo-lockup { height: 28px; width: auto; }

/* Inline logo colors follow the active theme (tokens), unlike img-loaded
   SVGs whose internal media queries only see the OS preference. */
.lg-warp { stroke: var(--indigo); }
.lg-ink { stroke: var(--fg); }
.lg-sys { fill: var(--fg-muted); font-family: var(--font-mono); }

/* Patch-plate mark: fixed brand colors in both themes (Kevin, 2026-07-16) */
.lg-plate { fill: #2A3B63; }
.lg-pwarp { stroke: #7288C0; }
.lg-pink { stroke: #EDE7DA; }

/* Weave layer: fixed canvas above the static warp ground, below content */
#weaveLayer {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.weave-mode-toggle {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  color: var(--fg-muted);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-3);
}
.weave-mode-toggle[data-mode="hand"] {
  color: var(--bg);
  background: var(--indigo);
  border-color: var(--indigo);
}
.weave-mode-toggle[data-mode="off"] { opacity: 0.55; }
@media (pointer: coarse) {
  .weave-mode-toggle { display: none; }
}

/* Nav brand slot: toggle borrows the space during the hero; the logo
   stitches in once the hero scrolls out (index only, JS-armed). */
.nav-brand-slot { display: flex; align-items: center; min-height: 32px; }
.links-toggle { display: none; }

.stitch-armed .site-logo .lg-warp,
.stitch-armed .site-logo .lg-ink,
.stitch-armed .site-logo .lg-pwarp,
.stitch-armed .site-logo .lg-pink {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
.stitch-armed .site-logo .lg-sys { opacity: 0; }
.stitch-armed .site-logo .lg-plate { opacity: 0; }
.stitch-armed .site-logo {
  position: absolute;
  pointer-events: none;
}
.stitch-armed .slot-toggle { display: inline-block; }

.stitch-armed.past-hero .site-logo {
  position: static;
  pointer-events: auto;
}
.stitch-armed.past-hero .site-logo .lg-plate {
  opacity: 1;
  transition: opacity 0.25s ease-out;
}
.stitch-armed.past-hero .site-logo .lg-warp,
.stitch-armed.past-hero .site-logo .lg-pwarp {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.4s ease-out 0.1s;
}
.stitch-armed.past-hero .site-logo .lg-ink,
.stitch-armed.past-hero .site-logo .lg-pink {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.45s ease-out 0.28s;
}
.stitch-armed.past-hero .site-logo .lg-sys {
  opacity: 1;
  transition: opacity 0.3s ease-out 0.65s;
}
.stitch-armed.past-hero .slot-toggle { display: none; }
.stitch-armed.past-hero .links-toggle { display: inline-block; margin-right: var(--space-3); }
@media (pointer: coarse) {
  .stitch-armed .site-logo { position: static; pointer-events: auto; }
  .stitch-armed .site-logo .lg-warp,
  .stitch-armed .site-logo .lg-ink,
  .stitch-armed .site-logo .lg-pwarp,
  .stitch-armed .site-logo .lg-pink { stroke-dasharray: none; stroke-dashoffset: 0; }
  .stitch-armed .site-logo .lg-sys { opacity: 1; }
  .stitch-armed .site-logo .lg-plate { opacity: 1; }

  /* Hide the weave toggle on touch for real. The earlier
     `@media (pointer: coarse) { .weave-mode-toggle { display: none } }`
     (0,1,0) is outranked by `.stitch-armed .slot-toggle` (0,2,0) and
     `.stitch-armed.past-hero .links-toggle` (0,3,0) - media queries add no
     specificity, so the button reappeared on every phone. These selectors
     match those weights and come later in the file, so they win.
     The weave canvas is disabled on touch entirely (weave.js), so the
     control has nothing to control here. */
  .stitch-armed .slot-toggle,
  .stitch-armed.past-hero .links-toggle { display: none; }
}

.header-nav {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}
.nav-links { display: flex; align-items: center; gap: var(--space-6); }
.nav-link {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--fg);
}
.nav-link:hover { color: var(--indigo); }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--rule);
  color: var(--fg-muted);
  transition: color 160ms ease, border-color 160ms ease;
}
.theme-toggle:hover { color: var(--gold); border-color: var(--gold); }

@media (max-width: 720px) {
  .nav-links { display: none; }
}

/* ============================
   WEFT DIVIDER (decorative)
   ============================ */
.weft-rule {
  display: flex;
  align-items: center;
  gap: 0;
  height: 2px;
  margin: var(--space-16) 0;
}
.weft-rule .run { flex: 1; height: 1px; background: var(--rule); }
.weft-rule .gold {
  width: 40px;
  height: 2px;
  border-radius: 1px;
  background: var(--gold);
  flex: 0 0 auto;
}
.weft-rule .run.short { flex: 0 0 32px; }

/* ============================
   HERO
   ============================ */
.hero { padding: var(--space-24) 0 var(--space-12); }
.hero-inner { max-width: 760px; }

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(180px, 1fr);
  gap: var(--space-16);
  align-items: center;
}
.hero-word {
  width: 200px;
  height: auto;
  margin-bottom: var(--space-6);
}
.hero-mark { display: flex; justify-content: center; }
.hero-mark svg {
  width: 100%;
  max-width: 300px;
  height: auto;
}
@media (max-width: 720px) {
  .hero-grid { grid-template-columns: 1fr; gap: var(--space-8); }
  .hero-mark { order: -1; justify-content: flex-start; }
  .hero-mark svg { max-width: 140px; }
  .hero-word { width: 160px; }
}
.hero h1 {
  font-size: var(--text-hero);
  margin-bottom: var(--space-6);
}
.hero-lede {
  font-size: var(--text-md);
  max-width: 56ch;
  margin-bottom: var(--space-8);
}
.hero-actions { display: flex; gap: var(--space-4); flex-wrap: wrap; }

/* ============================
   SECTIONS
   ============================ */
.section { padding: var(--space-16) 0; }
.section-head { max-width: 640px; margin-bottom: var(--space-12); }
.section-head h2 { font-size: var(--text-2xl); margin-bottom: var(--space-3); }
.section-head p { font-size: var(--text-base); }

/* ============================
   CATALOG (selvage cards)
   ============================ */
.catalog-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-6);
}
.selvage-card {
  border-radius: var(--radius-md);
  border: 1px solid var(--rule);
  background-color: var(--card);
  background-image: linear-gradient(90deg,
    transparent 0, transparent 9px,
    var(--gold) 9px, var(--gold) 11px,
    transparent 11px);
  padding: var(--space-6) var(--space-6) var(--space-6) calc(var(--space-6) + 10px);
}
.selvage-card h3 {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--indigo);
  margin-bottom: var(--space-3);
}
.selvage-card .card-lead {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--fg);
  margin-bottom: var(--space-4);
}
.selvage-card ul { display: flex; flex-direction: column; gap: var(--space-3); }
.selvage-card li {
  font-size: var(--text-sm);
  color: var(--fg);
  line-height: 1.5;
  padding-left: var(--space-4);
  position: relative;
}
.selvage-card li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--indigo);
}

@media (max-width: 860px) {
  .catalog-grid { grid-template-columns: 1fr; }
}

/* ============================
   CONNECTIONS
   ============================ */
.connections-motion-controls {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-3);
}
.connections-motion-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 9rem;
  background: var(--bg);
  transition: color 160ms ease, border-color 160ms ease, background-color 160ms ease;
}
.connections-motion-toggle:hover,
.connections-motion-toggle:focus-visible {
  color: var(--gold);
  border-color: var(--gold);
}
.connections-motion-toggle:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
.connections-motion-toggle[aria-pressed="true"] {
  color: var(--bg);
  background: var(--indigo);
  border-color: var(--indigo);
}
.connections-motion-toggle[hidden] { display: none; }
.connections-stage { position: relative; }
.connections-center-stitch { display: none; }
.connections-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.connections-grid li {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--space-3);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--fg);
  font-size: var(--text-xs);
  line-height: 1.25;
  scroll-snap-align: start;
}
.connections-grid li[data-carousel-clone] { display: none; }
.connection-logo {
  display: grid;
  flex: 0 0 2rem;
  width: 2rem;
  height: 2rem;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--bg-raised);
  transition: transform 180ms ease;
}
.connection-logo::before {
  width: 1.35rem;
  height: 1.35rem;
  background-color: var(--logo-rest, var(--indigo));
  content: "";
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
  transition: background-color 180ms ease;
}
.connections-grid li:is(.is-active, :hover, :focus-visible) .connection-logo {
  transform: scale(1.08);
}
.connections-grid li:is(.is-active, :hover, :focus-visible) .connection-logo::before {
  background-color: var(--brand-active, var(--brand));
}
:root[data-theme="dark"] .connection-logo { --logo-rest: var(--fg); }
/* Simple Icons v12.4.0. */
.connection-logo--gmail { --icon: url("connection-icons/gmail.svg"); --brand: #EA4335; }
.connection-logo--outlook { --icon: url("connection-icons/microsoftoutlook.svg"); --brand: #0078D4; }
.connection-logo--google-calendar { --icon: url("connection-icons/googlecalendar.svg"); --brand: #4285F4; }
.connection-logo--calendly { --icon: url("connection-icons/calendly.svg"); --brand: #006BFF; }
.connection-logo--slack { --icon: url("connection-icons/slack.svg"); --brand: #4A154B; }
.connection-logo--telegram { --icon: url("connection-icons/telegram.svg"); --brand: #26A5E4; }
.connection-logo--whatsapp { --icon: url("connection-icons/whatsapp.svg"); --brand: #25D366; }
.connection-logo--twilio { --icon: url("connection-icons/twilio.svg"); --brand: #F22F46; }
.connection-logo--google-sheets { --icon: url("connection-icons/googlesheets.svg"); --brand: #34A853; }
.connection-logo--excel { --icon: url("connection-icons/microsoftexcel.svg"); --brand: #217346; }
.connection-logo--google-drive { --icon: url("connection-icons/googledrive.svg"); --brand: #4285F4; }
.connection-logo--notion { --icon: url("connection-icons/notion.svg"); --brand: #000000; }
.connection-logo--airtable { --icon: url("connection-icons/airtable.svg"); --brand: #18BFFF; }
.connection-logo--typeform { --icon: url("connection-icons/typeform.svg"); --brand: #262627; }
.connection-logo--google-forms { --icon: url("connection-icons/googleforms.svg"); --brand: #7248B9; }
.connection-logo--quickbooks { --icon: url("connection-icons/quickbooks.svg"); --brand: #2CA01C; }
.connection-logo--stripe { --icon: url("connection-icons/stripe.svg"); --brand: #008CDD; }
.connection-logo--paypal { --icon: url("connection-icons/paypal.svg"); --brand: #003087; }
.connection-logo--shopify { --icon: url("connection-icons/shopify.svg"); --brand: #7AB55C; }
.connection-logo--square { --icon: url("connection-icons/square.svg"); --brand: #3E4348; }
.connection-logo--trello { --icon: url("connection-icons/trello.svg"); --brand: #0052CC; }
.connection-logo--clickup { --icon: url("connection-icons/clickup.svg"); --brand: #7B68EE; }
.connection-logo--asana { --icon: url("connection-icons/asana.svg"); --brand: #F06A6A; }
.connection-logo--jira { --icon: url("connection-icons/jira.svg"); --brand: #0052CC; }
.connection-logo--hubspot { --icon: url("connection-icons/hubspot.svg"); --brand: #FF7A59; }
.connection-logo--mailchimp { --icon: url("connection-icons/mailchimp.svg"); --brand: #FFE01B; }
.connection-logo--zendesk { --icon: url("connection-icons/zendesk.svg"); --brand: #03363D; }
:root[data-theme="dark"] .connection-logo--slack { --brand-active: #89678A; }
:root[data-theme="dark"] .connection-logo--excel { --brand-active: #378159; }
:root[data-theme="dark"] .connection-logo--notion { --brand-active: #737373; }
:root[data-theme="dark"] .connection-logo--typeform { --brand-active: #727273; }
:root[data-theme="dark"] .connection-logo--google-forms { --brand-active: #805AC0; }
:root[data-theme="dark"] .connection-logo--paypal { --brand-active: #4D6EAB; }
:root[data-theme="dark"] .connection-logo--square { --brand-active: #6E7276; }
:root[data-theme="dark"] .connection-logo--trello,
:root[data-theme="dark"] .connection-logo--jira { --brand-active: #266CD4; }
:root[data-theme="dark"] .connection-logo--zendesk { --brand-active: #5B7C81; }
/* Carousel + pause-to-badge weave is the treatment at EVERY width, not just
   mobile (Kevin, 2026-08-04). Kept as a media block so these rules continue to
   override the earlier grid rules by source order; the condition is always
   true. Do not re-gate this to max-width without Kevin's say-so - the desktop
   four-column grid with visible names is not the intended design. */
@media (min-width: 0px) {
  .connections-stage {
    overflow: hidden;
    /* Declared on the STAGE, not the grid, because the pause badge
       (.connections-center-stitch) is a sibling of the grid and would not
       inherit it from there. Everything carousel-sized derives from this. */
    --conn-icon: clamp(3.76rem, 5.5vw, 5rem);
  }
  .connections-center-stitch {
    position: absolute;
    z-index: 0;
    top: var(--stitch-y, 50%);
    left: var(--stitch-x, 50%);
    display: block;
    /* Must stay proportionally larger than the icon it frames. The badge sits
       at z-index 0, behind the logos, so if it does not clearly exceed the
       icon it vanishes behind it - which is what happened on wide screens
       when this was a fixed 5.75rem against a growing icon. */
    width: calc(var(--conn-icon) * 1.55);
    height: calc(var(--conn-icon) * 1.55);
    overflow: visible;
    opacity: 0;
    pointer-events: none;
    filter: drop-shadow(0 1px 0 var(--bg));
    transform: translate(-50%, -50%) rotate(-1deg) scale(0.96);
    transition: opacity 140ms ease, transform 640ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  .connections-patch-yarn {
    transform-box: fill-box;
    transform-origin: center;
  }
  .connections-patch-warp line,
  .connections-patch-weft line {
    fill: none;
    stroke-linecap: round;
    transform-box: fill-box;
  }
  .connections-patch-warp line {
    stroke: var(--indigo);
    stroke-width: 5.5;
    opacity: 0.68;
    transform: scaleY(0);
    transform-origin: center top;
  }
  .connections-patch-warp line:nth-child(1) { --stitch-in: 0ms; --stitch-out: 400ms; }
  .connections-patch-warp line:nth-child(2) { --stitch-in: 30ms; --stitch-out: 370ms; }
  .connections-patch-warp line:nth-child(3) { --stitch-in: 60ms; --stitch-out: 340ms; }
  .connections-patch-warp line:nth-child(4) { --stitch-in: 90ms; --stitch-out: 310ms; }
  .connections-patch-warp line:nth-child(5) { --stitch-in: 120ms; --stitch-out: 280ms; }
  .connections-patch-warp line:nth-child(6) { --stitch-in: 150ms; --stitch-out: 250ms; }
  .connections-patch-warp line:nth-child(7) { --stitch-in: 180ms; --stitch-out: 220ms; }
  .connections-patch-warp line:nth-child(even) { transform-origin: center bottom; }
  .connections-patch-weft line {
    stroke: var(--gold);
    stroke-width: 6;
    stroke-dasharray: 0.095 0.07;
    opacity: 0.86;
    filter: drop-shadow(0 1px 0 var(--bg));
    transform: scaleX(0);
    transform-origin: left center;
  }
  .connections-patch-weft line:nth-child(1) { --stitch-in: 220ms; --stitch-out: 180ms; }
  .connections-patch-weft line:nth-child(2) { --stitch-in: 250ms; --stitch-out: 150ms; }
  .connections-patch-weft line:nth-child(3) { --stitch-in: 280ms; --stitch-out: 120ms; }
  .connections-patch-weft line:nth-child(4) { --stitch-in: 310ms; --stitch-out: 90ms; }
  .connections-patch-weft line:nth-child(5) { --stitch-in: 340ms; --stitch-out: 60ms; }
  .connections-patch-weft line:nth-child(6) { --stitch-in: 370ms; --stitch-out: 30ms; }
  .connections-patch-weft line:nth-child(7) { --stitch-in: 400ms; --stitch-out: 0ms; }
  .connections-patch-weft line:nth-child(even) {
    stroke-dashoffset: 0.0825;
    transform-origin: right center;
  }
  .connections-stage.is-user-paused .connections-center-stitch,
  .connections-stage[data-motion-state="releasing"] .connections-center-stitch {
    opacity: 0.56;
    transform: translate(-50%, -50%) rotate(-1deg) scale(1);
  }
  .connections-stage.is-user-paused .connections-patch-warp line {
    animation: connections-patch-warp-in 240ms cubic-bezier(0.22, 1, 0.36, 1) var(--stitch-in) both;
  }
  .connections-stage.is-user-paused .connections-patch-weft line {
    animation: connections-patch-weft-in 260ms cubic-bezier(0.22, 1, 0.36, 1) var(--stitch-in) both;
  }
  .connections-stage.is-user-paused .connections-patch-yarn {
    animation: connections-patch-tighten 820ms cubic-bezier(0.22, 1, 0.36, 1) both;
  }
  .connections-stage[data-motion-state="releasing"] .connections-patch-warp line {
    animation: connections-patch-warp-out 220ms cubic-bezier(0.65, 0, 0.35, 1) var(--stitch-out) both;
  }
  .connections-stage[data-motion-state="releasing"] .connections-patch-weft line {
    animation: connections-patch-weft-out 220ms cubic-bezier(0.65, 0, 0.35, 1) var(--stitch-out) both;
  }
  @keyframes connections-patch-warp-in {
    from { transform: scaleY(0); }
    to { transform: scaleY(1); }
  }
  @keyframes connections-patch-weft-in {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }
  @keyframes connections-patch-warp-out {
    from { transform: scaleY(1); }
    to { transform: scaleY(0); }
  }
  @keyframes connections-patch-weft-out {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
  }
  @keyframes connections-patch-tighten {
    0%, 78% { transform: scale(1.035); }
    89% { transform: scale(0.96); }
    100% { transform: scale(1); }
  }
  .connections-grid {
    display: flex;
    gap: 0;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    /* Full-bleed: the strip must run off both screen edges to read as a
       carousel rather than a contained widget. Centering padding is
       viewport-based because the element is now wider than its container. */
    width: 100vw;
    margin-inline: calc(50% - 50vw);
    padding: var(--space-3) calc(50vw - (var(--conn-icon) * 0.75)) var(--space-4);
    scroll-behavior: smooth;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  }
  .connections-grid::-webkit-scrollbar { display: none; }
  .connections-grid li {
    /* Neighbors must stay legible as logos - at 0.58/0.34 they read as
       ghosts and the strip loses its sense of a queue moving through. */
    --carousel-scale: 0.72;
    --carousel-drop: 12px;
    --carousel-opacity: 0.6;
    /* Slot scales with viewport so roughly the same NUMBER of icons is visible
       at any width. Fixed 4.75rem slots put ~5 icons on a phone but ~25 on a
       wide desktop, where one step is a tiny fraction of the strip and the
       outer icons sit on the flat tail of the swell - a cluster animates in
       the middle while everything else looks static, which reads as moving in
       groups. Floor keeps mobile exactly as it is now. */
    flex: 0 0 calc(var(--conn-icon) * 1.5);
    justify-content: center;
    /* Row must stay taller than the icon or the stage clips its top. */
    height: calc(var(--conn-icon) * 1.6);
    padding: 0;
    border: 0;
    background: transparent;
    opacity: var(--carousel-opacity);
    scroll-snap-align: center;
    /* Dock behavior: icons share a bottom baseline and rise as they magnify.
       Growing downward from a fixed top (transform-origin: center top) reads
       as icons hanging rather than swelling off a shelf. */
    align-items: flex-end;
    transform: scale(var(--carousel-scale));
    transform-origin: center bottom;
    /* NO transform transition. updateCarouselState() recomputes every icon's
       scale per animation frame from live scroll position, so the smooth
       scroll itself supplies the motion. A CSS transition on top makes each
       per-frame value chase a target that has already moved, which reads as
       laggy and steppy. Opacity keeps a short transition - it is not
       recomputed as tightly and benefits from the softening. */
    transition: opacity 220ms ease;
    will-change: transform, opacity;
  }
  .connections-grid li[data-carousel-clone] { display: flex; }
  .connections-grid li:is(.is-active, :hover, :focus-visible) .connection-logo { transform: none; }
  .connections-grid li.is-active { z-index: 2; }
  .connection-logo {
    /* Laid out at the LARGEST displayed size (previously 5.5vw at scale 1.75).
       Icons are masks in a composited layer, so the raster is produced at this
       layout size; every carousel scale is <= 1 and therefore only ever
       samples down, which stays crisp. Scaling up from a smaller layout box
       is what made them grainy. */
    flex-basis: var(--conn-icon);
    width: var(--conn-icon);
    height: var(--conn-icon);
    border: 0;
    background: transparent;
  }
  .connection-logo::before {
    /* This pseudo-element carries the mask, so THIS is the box that gets
       rasterized - sizing .connection-logo alone does nothing for crispness.
       Laid out at the largest displayed size (old 2.15rem x 1.75 max scale)
       and scaled down from there, and viewport-scaled to match the slot. */
    width: var(--conn-icon);
    height: var(--conn-icon);
  }
  .connection-logo + span {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .connections-center-stitch { transition: none; }
  .connections-stage.is-user-paused .connections-patch-yarn,
  .connections-stage.is-user-paused .connections-patch-warp line,
  .connections-stage.is-user-paused .connections-patch-weft line { animation: none; }
  .connections-grid li {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .connection-logo {
    transform: none;
    transition: none;
  }
  .connection-logo::before {
    background-color: var(--brand-active, var(--brand));
    transition: none;
  }
}
@media (min-width: 721px) and (pointer: coarse) {
  .connection-logo::before { background-color: var(--brand-active, var(--brand)); }
}
@media (prefers-reduced-motion: reduce) {
  .connections-grid { scroll-behavior: auto; }
}
.connections-more {
  margin-top: var(--space-6);
  font-size: var(--text-sm);
  color: var(--fg);
  max-width: 60ch;
}
.connections-cta { margin-top: var(--space-6); }

/* ============================
   PATTERN CARDS (workshop / on the loom)
   ============================ */
.pattern-cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-6);
}
.pattern-card {
  border-radius: var(--radius-md);
  border: 1px solid var(--rule);
  background-color: var(--card);
  padding: var(--space-4) var(--space-6) var(--space-4) calc(var(--space-6) + 10px);
}
.pattern-card p {
  font-size: var(--text-sm);
  color: var(--fg);
  line-height: 1.6;
  margin: 0;
}
.pattern-card p strong { color: var(--fg); font-weight: 600; }

.pattern-chip {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-3);
}

/* Workshop: built and running — solid selvage stripe (reuses the
   catalog selvage-card motif), legible category chip */
.pattern-card--built {
  background-image: linear-gradient(90deg,
    transparent 0, transparent 9px,
    var(--gold) 9px, var(--gold) 11px,
    transparent 11px);
}
.pattern-card--built .pattern-chip {
  color: var(--gold-text);
  border: 1px solid var(--rule);
  background: var(--bg-raised);
}

/* On the loom: in progress — dashed weft rule across the TOP of the card,
   not the left edge like Workshop's solid stripe. Different placement (not
   just solid-vs-dashed) so the two card types read as genuinely distinct,
   and the rule sitting above the content rather than flush with it gives
   the card a little more depth. Decorative-only "Concept" chip at reduced
   opacity per the light-mode gold exception. */
.pattern-card--concept {
  position: relative;
  padding: calc(var(--space-4) + 8px) var(--space-6) var(--space-4);
  background-image: none;
}
.pattern-card--concept::before {
  content: "";
  position: absolute;
  top: 0;
  left: 18px;
  right: 18px;
  height: 2px;
  background-image: repeating-linear-gradient(90deg,
    var(--weave-thread-bg) 0, var(--weave-thread-bg) 6px,
    transparent 6px, transparent 12px);
}
.pattern-card--concept .pattern-chip {
  color: var(--gold);
  opacity: 0.6;
  border: 1px solid var(--rule);
  background: transparent;
}

@media (max-width: 720px) {
  .pattern-cards { grid-template-columns: 1fr; }
}

/* ============================
   HOW IT WORKS
   ============================ */
.steps {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-8);
  margin-bottom: var(--space-8);
}
.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--gold);
  color: var(--indigo);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  margin-bottom: var(--space-3);
}
.step h3 {
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--fg);
  margin-bottom: var(--space-2);
}
.step p { font-size: var(--text-sm); }

@media (max-width: 720px) {
  .steps { grid-template-columns: 1fr; }
}

/* ============================
   ABOUT
   ============================ */
.about-body { max-width: 640px; display: flex; flex-direction: column; gap: var(--space-4); }
.about-body p { font-size: var(--text-base); }

/* ============================
   CONTACT
   ============================ */
.contact-layout {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: var(--space-12);
  align-items: start;
}
.contact-intro { max-width: 40ch; }
.contact-intro p { margin-top: var(--space-4); font-size: var(--text-sm); }
.contact-reply-note {
  margin-top: var(--space-6);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--fg);
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.form-field { display: flex; flex-direction: column; gap: var(--space-2); }
.form-field label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--fg);
}
.form-field input,
.form-field textarea {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--rule);
  background: var(--bg);
  color: var(--fg);
}
.form-field input:focus-visible,
.form-field textarea:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 1px;
}
.form-field textarea { resize: vertical; min-height: 120px; }

.form-note {
  font-size: var(--text-xs);
  color: var(--fg);
}
.form-note a { color: var(--indigo); }
.form-note a:hover { text-decoration: underline; }

.form-honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.form-field-error {
  font-size: var(--text-xs);
  color: var(--error);
}

.form-status {
  font-size: var(--text-sm);
  color: var(--error);
}

.form-success h3 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}
.form-success p {
  font-size: var(--text-sm);
  color: var(--fg);
}

@media (max-width: 860px) {
  .contact-layout { grid-template-columns: 1fr; }
}

/* ============================
   FOOTER
   ============================ */
.site-footer {
  padding: var(--space-12) 0 var(--space-16);
  border-top: 1px solid var(--rule);
  position: relative;
  z-index: 1;
}
.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
}
.footer-brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-xs);
  color: var(--fg);
}
.footer-mark { width: 18px; height: 18px; opacity: 0.8; }
.footer-links {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--fg);
}
.footer-links a:hover { color: var(--indigo); }
.footer-meta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--text-xs);
  color: var(--fg);
}
.footer-meta .theme-toggle { width: 30px; height: 30px; }

/* ============================
   SIMPLE PAGES (privacy / 404)
   ============================ */
.simple-page { padding: var(--space-16) 0 var(--space-24); }
.simple-page h1 { font-size: var(--text-2xl); margin-bottom: var(--space-3); }
.simple-page .page-updated {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--fg);
  margin-bottom: var(--space-8);
}
.simple-page .page-body { max-width: 68ch; display: flex; flex-direction: column; gap: var(--space-4); }
.simple-page .page-body p { font-size: var(--text-base); }
.simple-page .back-link {
  display: inline-block;
  margin-top: var(--space-8);
  font-weight: 600;
  color: var(--indigo);
}
.simple-page .back-link:hover { text-decoration: underline; }
.simple-page .page-actions { display: flex; gap: var(--space-4); flex-wrap: wrap; margin-top: var(--space-8); }
