/* ============================================================
   Canonical marketing token layer (--m-*)
   Source of truth: docs/design/marketing.md Part III.
   The DevSwarm-dark anchor. Every color on the site resolves
   through these tokens; a hardcoded hex is a future theming bug.
   Downstream (Phase 1+) workspaces consume these names verbatim —
   they are the public API. Do not rename without updating the doc.
   ============================================================ */
:root {
  color-scheme: dark;

  /* ── Neutrals — the field desaturated to a whisper of violet ── */
  --m-bg: #0b0a11;
  --m-surface: #131118;
  --m-surface-2: #1b1823;
  --m-hairline: rgba(255, 255, 255, 0.1);
  --m-hairline-strong: rgba(255, 255, 255, 0.18);
  --m-text-1: #f2f0f7;
  --m-text-2: #b8b3c6;
  --m-text-3: #8f8aa1;

  /* ── Accent — purple is jewelry: fills, focus, the one glow ── */
  --m-accent: #5f2aff;
  --m-accent-text: #9f7fff;
  --m-accent-hover: #7b4fff;
  --m-accent-tint: rgba(95, 42, 255, 0.12);
  --m-glow: rgba(95, 42, 255, 0.35);

  /* ── Status — one each, semantic only ── */
  --m-success: #0ead68;
  --m-error: #f23030;

  /* ── Type — families. Geist (Vercel's technical grotesque) is the chosen
        brand font — a DESIGNED face rather than the OS default, which is what
        makes competitor headlines read intentional (Conductor/Superset ship
        Geist too). The system stack stays as the fallback so pre-swap paint
        looks like the old system-font direction. --m-font-mono is the single
        mono accent. ── */
  --m-font: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter var",
    "Inter", system-ui, "Helvetica Neue", Arial, sans-serif;
  --m-font-mono: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ── Radius scale (Part III §4). One scale for the whole marketing site;
        map every rounded surface to a role, never a raw px value:
          sm   (6px)  small bits — inline code, kbd, skeletons
          md   (10px) CONTROLS  — buttons, inputs, menus, tabs, pagination
          lg   (16px) BLOCKS    — cards, media/images, video, code blocks,
                                  banners, CTA blocks, modals
          xl   (24px) WELLS     — outer image frames that nest lg media
          full        PILLS     — chips, tags, badges, pill fields
        Circles stay border-radius:50%. Tiny 2-4px decorative bars/dots
        (dividers, accent lines, dot indicators, scrollbar) are off-scale
        on purpose — the scale would over-round them. ── */
  --m-radius-sm: 6px;
  --m-radius-md: 10px;
  --m-radius-lg: 16px;
  --m-radius-xl: 24px;
  --m-radius-full: 999px;

  /* ── Legacy --bwd-* block folded onto the tokens ── */
  --bwd-bg: var(--m-bg);
  --bwd-surface: var(--m-surface);
  --bwd-surface-2: var(--m-surface-2);
  --bwd-purple: var(--m-accent);
  --bwd-purple-light: var(--m-accent-hover);
  --bwd-purple-dim: var(--m-accent-tint);
  --bwd-text: var(--m-text-1);
  --bwd-text-muted: var(--m-text-2);
  --bwd-border: var(--m-hairline); /* purple furniture border → hairline */
  --bwd-radius: var(--m-radius-lg);

  /* ── Layout — the shared content column width. Every centered
        section (.content-container) and the footer resolve through
        this so all vertical edges line up. Widened from the old
        1080px Webflow default to fill more of a modern viewport
        (Cursor-style wide column) instead of stranding big side
        gutters on large screens. ── */
  --m-content-width: 1280px;
}

/* ============================================================
   Webflow variable aliases — the lever that re-skins the read-only
   250KB Webflow export without editing it. The Webflow :root vars
   below (defined in devswarm-webflow.css) are re-pointed onto the
   --m-* tokens; because this file loads AFTER the export, these win.
   The two Webflow deleted-variable references (--blue-1<…>, --blue-2<…>)
   are re-homed onto the token system here — no longer dangling literals.
   ============================================================ */
:root {
  --background: var(--m-bg);
  --main-font: var(--m-font);
  --code-font: var(--m-font-mono);
  --light-gray: var(
    --m-text-2
  ); /* was #353436c4 — the invisible body default */
  --header: var(--m-text-1);
  --header-gradient: var(--m-text-1); /* retire the gradient fade → solid */
  --primary: var(--m-accent);
  --dark-purple: var(--m-surface);
  --medium-purple: var(--m-surface-2);
  --text: var(--m-text-1);
  --placeholder: var(--m-text-3);
  --paragraphs-light: var(--m-text-2);
  --accent-green: var(--m-success);
  --accent-green-2: var(--m-success); /* unify the two greens */
  --white-transparent: var(--m-hairline);
  --input-field-background: var(--m-surface-2);
  --accent-pink: var(--m-accent); /* stray decorative hues → the one accent */
  --accent-blue: var(--m-accent);

  /* Webflow deleted-variable refs (24 + 14 uses across .link, button
     hovers, borders, and inset glows) — re-homed onto the accent token. */
  --blue-1\<deleted\|variable-b785d39e\>: var(--m-accent);
  --blue-2\<deleted\|variable-c11fa973\>: var(--m-accent);
}

/* ============================================================
   Content column width — widen the centered column.
   The Webflow base pins .content-container to max-width:1080px,
   which stranded wide side gutters on large displays and read as
   a narrow center. Re-point it (and the footer column) onto the
   shared --m-content-width token so sections fill more of the
   viewport on desktop. Scoped to >=768px so the mobile 640px cap
   and the .narrow / .video / .wide variants keep their own widths.
   Inner content (headings, prose, cards) still caps itself for
   readable line lengths. */
@media screen and (min-width: 768px) {
  .content-container:not(.narrow):not(.video):not(.wide) {
    max-width: var(--m-content-width);
  }
}

/* Smooth scrolling is for in-page anchor jumps only, never for page load.
   Unscoped `scroll-behavior: smooth` on <html> makes Chrome animate the
   initial scroll position of a NEWLY loaded document, which reads as the
   previous page's offset being carried across a navigation. Gating on
   :focus-within keeps it off until something in the document has been
   focused (i.e. the reader clicked an anchor), which is exactly the case
   we want it for. :where() keeps the specificity at 0 so this stays easy
   to override. */
@media (prefers-reduced-motion: no-preference) {
  :where(html:focus-within) {
    scroll-behavior: smooth;
  }
}

html {
  /* Page-root horizontal-overflow guard. The site has several elements that
     intentionally bleed past the content column and rely on being clipped
     (the 100vw ambient particle canvas, the 130%-wide hero/features glows,
     the 986px HiVE bg graphic). Nothing guarded the root, so on narrow
     viewports a few px of that bleed leaked into a horizontally-scrollable
     area, showing as a dark band down the right edge (bare --m-bg beyond the
     viewport-width sections). `clip` (not `hidden`) removes the band without
     establishing a scroll container, so position:sticky (FAQ rail) and the
     blog reading-progress bar keep working. */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--m-bg);
  /* was the Webflow #353436c4 default (dark translucent gray on a dark
     field → near-invisible unstyled text). Now the secondary-text token. */
  color: var(--m-text-2);
  overflow-x: clip;
}

/* ============================================================
   Phase 0 — desaturate the field (site-wide, tokens only)
   The neutral ladder is brought live through the :root aliases above;
   these rules handle what aliasing alone cannot reach. Component,
   card, button, blog and per-page-palette work is Phase 1.
   ============================================================ */

/* Retire the gradient-clip on headings (white → 70%-white, a pre-dimmed
   2021 Webflow trick — the brightest value step, faded). Headings become
   the plain --m-text-1 token. Accent word-spans (.text-span*) keep their
   own fill and are untouched (they carry their own -webkit-text-fill-color). */
h1,
h2,
h3,
h4,
h5,
h6,
.heading-hero,
.heading-hero.large,
.heading-hero.homepage,
.heading-hero.features {
  -webkit-text-fill-color: var(--m-text-1);
  background-image: none;
  color: var(--m-text-1);
}

/* The "Learn More" fix: purple-as-text links (.link-purple is var(--m-accent) in the
   read-only export — 3.19:1 on the field) move to the readable accent-text
   token. The one link recipe (underline/hover/arrow) is Phase 1 (WS-1a). */
.link-purple {
  color: var(--m-accent-text);
}

/* ============================================================
   Download page — minimalist installer panel.
   One framed surface split by a hairline instead of two competing
   cards, so nothing reads as a "selected" state. The markup defaults
   the filled CTA to macOS; an inline script on the page demotes it
   and promotes Windows when the visitor is on Windows, keeping the
   emphasis budget at one primary. Requirements live in a single
   shared mono ledger instead of duplicated per-OS bullet lists.
   ============================================================ */

/* The one sanctioned hero glow: a faint accent bloom behind the panel
   (same recipe as the /features hero field, quieter). */
[data-route="/download"] .section-hero.with-background {
  position: relative;
  overflow: hidden;
  padding-bottom: 96px;
}

[data-route="/download"] .section-hero.with-background::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 96px;
  transform: translateX(-50%);
  width: 900px;
  max-width: 100%;
  height: 520px;
  background: radial-gradient(
    ellipse 50% 45% at 50% 40%,
    var(--m-glow) 0%,
    transparent 70%
  );
  opacity: 0.4;
  pointer-events: none;
}

[data-route="/download"] .section-hero.with-background > * {
  position: relative;
  z-index: 1;
}

/* The original two OS cards, on the token card skin */
[data-route="/download"] .section---download.download {
  width: 100%;
  display: flex;
  justify-content: center;
}

[data-route="/download"] .download-grid {
  width: 100%;
  max-width: var(--m-content-width);
  margin-left: auto !important;
  margin-right: auto !important;
  justify-content: center !important;
}

[data-route="/download"] .wrapper-bg---download {
  border-radius: var(--m-radius-lg);
}

[data-route="/download"] .dl-footnote {
  margin: 48px auto 0;
  text-align: center;
  color: var(--m-text-3);
  font-size: var(--m-small-size);
  line-height: var(--m-small-lh);
}

/* Entrance: the hero brackets/headline run the shared bracket animation;
   the cards join with one rise, gated on the same .hero-anim class the
   page's inline script only adds when motion is allowed. */
@keyframes dl-rise {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.hero-anim [data-route="/download"] .download-grid {
  animation: dl-rise 0.4s ease-out 0.15s both;
}

/* .reminder-card skin (surface + hairline + radius-lg) is the one card spec —
   see the WS-1e card block near the end of this file. */

/* Download page — the "email me a link" card is a mobile / download-later
   affordance: on desktop the download buttons are right there, so it's hidden
   above the tablet breakpoint. When shown (tablet and below) it sits between
   the installer panel and the footnote, clear of the footer newsletter. */
[data-route="/download"] .dl-reminder {
  margin-top: 36px;
}

@media screen and (min-width: 992px) {
  [data-route="/download"] .dl-reminder {
    display: none;
  }
}

/* Download + pricing pages — hero subtitle (divider also used on /security) */
[data-route="/download"] .dl-hero-subtitle,
[data-route="/pricing"] .dl-hero-subtitle {
  text-align: center;
  color: var(--m-text-2);
  font-size: 18px;
  line-height: 1.6;
  max-width: 620px;
  margin: 16px auto 0;
}

[data-route="/download"] .dl-hero-subtitle strong,
[data-route="/pricing"] .dl-hero-subtitle strong {
  color: #fff;
  font-weight: 600;
}

.dl-hero-divider {
  width: 48px;
  height: 3px;
  background: var(--bwd-purple);
  border-radius: 2px;
  margin: 10px auto 0;
}

/* Team photo grid */
.team-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 24px;
  max-width: 960px;
  margin: 0 auto;
  padding: 32px 16px;
}
.team-photo-item {
  text-align: center;
}
.team-photo-placeholder {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
}
.team-photo-name {
  text-align: center;
  margin-top: 8px;
  font-size: 14px;
  color: var(--m-text-2);
}
.team-photo-role {
  font-size: 12px;
  color: var(--m-text-3);
}

#root {
  min-height: 100vh;
}

.rich-text-block figcaption {
  margin-bottom: 40px;
}

.content-narrow {
  padding-bottom: 48px;
}

.link-post-thumbnail:hover {
  border-color: var(--dark-gray) !important;
}

/* .block-post is routed through the one card spec (WS-1e block below):
   hover brightens the hairline instead of the old purple box-shadow glow. */

.collection-list-wrapper-faq {
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
}

/* ── FAQ accordion ──────────────────────────────────────────────────────
   FaqList renders each item as a native <details>/<summary>. The old rules
   here forced every answer open; the accordion now owns show/hide, so those
   overrides are gone. Questions collapse to a single-line row with a +/-
   toggle; opening reveals the answer with a small fade. */
.collection-list-faq {
  gap: 0;
  align-items: stretch;
  width: 100%;
}

.faq-accordion {
  display: block;
  width: 100%;
  border-bottom: 1px solid var(--m-hairline);
  padding: 0;
  gap: 0;
}
.faq-accordion:first-child {
  border-top: 1px solid var(--m-hairline);
}

.faq-accordion .faq-heading {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 24px;
  padding: 24px 4px;
}
.faq-accordion .faq-heading::-webkit-details-marker {
  display: none;
}

.faq-accordion .faq-question {
  margin: 0;
  font-size: var(--m-lead-size);
  line-height: var(--m-lead-lh);
  font-weight: 500;
  color: var(--m-text-1);
  transition: color 0.15s ease;
}
.faq-accordion:hover .faq-question {
  color: var(--m-accent-text);
}

/* +/- toggle drawn from two bars; the vertical bar collapses when open. */
.faq-toggle {
  position: relative;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
}
.faq-toggle::before,
.faq-toggle::after {
  content: "";
  position: absolute;
  background: var(--m-text-2);
  transition: transform 0.2s ease, background 0.2s ease;
}
.faq-toggle::before {
  top: 50%;
  left: 0;
  right: 0;
  height: 1.5px;
  transform: translateY(-50%);
}
.faq-toggle::after {
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1.5px;
  transform: translateX(-50%);
}
.faq-accordion[open] .faq-toggle::before,
.faq-accordion[open] .faq-toggle::after {
  background: var(--m-accent-text);
}
.faq-accordion[open] .faq-toggle::after {
  transform: translateX(-50%) scaleY(0);
}

.faq-accordion .answer-wrapper {
  overflow: hidden;
  display: block;
  padding: 0 4px 26px;
}
.faq-accordion[open] .answer-wrapper {
  animation: faqReveal 0.24s ease;
}
.faq-accordion .faq-answer {
  display: block;
  color: var(--m-text-2);
  font-size: var(--m-body-size);
  line-height: var(--m-body-lh);
  max-width: 62ch;
}

@keyframes faqReveal {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .faq-accordion[open] .answer-wrapper {
    animation: none;
  }
}

/* ── FAQ homepage section: editorial two-column ──────────────────────────
   Accordion takes the wide LEFT column; the intro (eyebrow, heading,
   subtitle, button) is the narrower sticky rail on the RIGHT. Columns are
   placed explicitly so the visual order never depends on DOM order. */
.faq-section.section-secondary.bg-purple {
  background-color: transparent;
  background-image: none;
}

.faq-section .content-container.faq-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: 80px;
  align-items: start;
  width: 100%;
}
/* Webflow's .w-container injects clearfix ::before/::after pinned to grid
   cell 1/1; as a grid these become phantom items that scramble column order.
   Remove them so the explicit placement below is the only thing that counts. */
.faq-section .content-container.faq-layout::before,
.faq-section .content-container.faq-layout::after {
  content: none;
  display: none;
}

.faq-items {
  grid-column: 2;
  grid-row: 1;
}
.faq-intro {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 18px;
  position: sticky;
  top: 120px;
}
.faq-intro .faq-title {
  margin: 0;
  text-align: left;
  font-size: var(--m-h2-size);
  line-height: 1.1;
}
.faq-subtitle {
  margin: 0;
  max-width: 34ch;
  color: var(--m-text-2);
  font-size: var(--m-body-size);
  line-height: var(--m-body-lh);
}
.faq-see-all {
  margin-top: 6px;
}

.faq-section .faq-items.collection-list-wrapper-faq {
  max-width: none;
  margin: 0;
  width: 100%;
}

@media (max-width: 991px) {
  .faq-section .content-container.faq-layout {
    grid-template-columns: 1fr;
    gap: 32px;
  }
  .faq-items,
  .faq-intro {
    grid-column: 1;
    grid-row: auto;
  }
  .faq-intro {
    position: static;
    top: auto;
    order: -1;
  }
}

/* Mobile nav: Webflow's [data-nav-menu-open] provides display:block !important
   which overrides the collapse=medium display:none. But its generic rule also
   sets background:#c8c8c8 and text-align:center. Override those to keep the
   dark-purple gradient already defined for .nav-menu in devswarm-webflow.css. */
@media screen and (max-width: 991px) {
  .w-nav .nav-menu.w-nav-menu[data-nav-menu-open] {
    display: flex !important;
    flex-direction: column;
    align-items: stretch;
    background-color: var(--dark-purple);
    background-image: linear-gradient(
      130deg,
      var(--dark-purple),
      var(--medium-purple)
    );
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    text-align: left;
  }

  /* Webflow's .w-nav-link ships margin-inline:auto. In the flex column those
     auto margins absorb the free space and center every row — which left the
     Product group left-aligned (its margins are reset below) while Blog /
     Changelog / About / Docs / Download stayed centered. Reset the margins on
     all top-level items so the collapsed menu is one consistent left stack. */
  .w-nav .nav-menu.w-nav-menu[data-nav-menu-open] .nav-link {
    width: 100%;
    margin-left: 0;
    margin-right: 0;
    text-align: left;
  }

  .w-nav .nav-menu.w-nav-menu[data-nav-menu-open] .nav-button {
    align-self: flex-start;
    margin-top: 0.4em;
  }
}

/* Eliminate white flash on Vimeo iframe initial paint. The cross-origin
   iframe document renders its own white canvas before Vimeo's HTML loads.
   color-scheme alone doesn't catch it (Vimeo's document sets its own), so we
   hide the iframe until its load event fires — the wrapper's dark background
   shows through in the meantime. marketing-runtime.js adds the `vimeo-loaded`
   class on each iframe's load event (with a fallback timer). */
iframe[src*="player.vimeo.com"] {
  color-scheme: dark;
  opacity: 0;
  transition: opacity 200ms ease-out;
}

iframe[src*="player.vimeo.com"].vimeo-loaded {
  opacity: 1;
}

/* Vimeo hero parity: force dark pillarbox area/background layers. */
.video-wrapper-block,
.video-wrapper-block [id^="hero-video"],
.video-wrapper-block iframe {
  background-color: #000000;
}

/* The hero video file has 5% black pillarbox bars baked into each side
   (16:10 content on a 16:9 canvas). The pages size the box to 16:10 and
   oversize the player horizontally to crop the bars — these rules clip the
   overhang on both the welcome-video div and the homepage wrapper (Webflow
   sets overflow:visible on .homepage at min-width:1920px). */
/* Webflow fixes the homepage hero video container's height per breakpoint
   (452/410/330/224px), all sized for the old 16:9 box — they clip the
   bottom of the taller 16:10 box. Let it follow the video height.
   The outer wrapper is rounded too: it paints the black fallback background
   (and the glow shadow), so rounding only the inner hero-video div leaves
   square black corners showing through behind it. */
.content-container.video {
  height: auto;
}

.video-wrapper-block,
.video-wrapper-block [data-video-id="hero-video"],
.video-wrapper-block.homepage {
  overflow: hidden;
  border-radius: var(--m-radius-lg);
}

.video-wrapper-block [data-video-id="hero-video"] iframe {
  max-width: none;
}

/* Pricing page parity: keep the three-card grid centered like production. */
[data-route="/pricing"] .pricing-section {
  width: 95%;
  max-width: var(--m-content-width);
  display: flex;
  justify-content: center;
  margin-left: auto !important;
  margin-right: auto !important;
}

[data-route="/pricing"] .pricing-grid {
  width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
  justify-content: center !important;
}

[data-route="/pricing"] .pricing-banner-card .button {
  min-width: 180px;
}

/* Footer: second group heading stacked in the same column (e.g. Community under Social). */
.subtitle-footer.stacked {
  margin-top: 24px;
}

/* Student page: single centered product card below the hero. */
[data-route="/student"] .pricing-section.student {
  width: 95%;
  max-width: 1080px;
  margin-left: auto !important;
  margin-right: auto !important;
  display: flex;
  justify-content: center;
  padding-top: 3em;
  padding-bottom: 5em;
}

[data-route="/student"] .pricing-grid.student {
  width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
  justify-content: center !important;
}

[data-route="/student"] .wrapper-bg.student {
  max-width: 560px;
}

/* Legal pages: extra breathing room below the nav. */
[data-route="/privacy-policy"] .section-main,
[data-route="/terms-of-service"] .section-main {
  padding-top: 4em;
}

@media screen and (min-width: 992px) {
  [data-route="/privacy-policy"] .section-main,
  [data-route="/terms-of-service"] .section-main {
    padding-top: 7em;
  }
}

/* Blog post social-share links: center horizontally. */
.social-share-block {
  justify-content: center;
}

/* Supported Agents page: agent card grid */
.agent-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 18px;
  margin-bottom: 24px;
}

/* Skin (surface + hairline + radius-lg) comes from the one card spec —
   see the WS-1e card block near the end of this file. */
.agent-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 28px 20px;
}

.agent-card-icon {
  width: 48px;
  height: 48px;
  object-fit: contain;
  margin-bottom: 12px;
}

.agent-card-icon-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--m-radius-md);
  background: var(--m-surface-2);
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--m-accent-text);
}

.agent-card-name {
  margin: 0 0 6px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--m-text-1);
}

.agent-card-description {
  margin: 0;
  font-size: 0.88rem;
  color: var(--m-text-2);
  line-height: 1.45;
}

/* Changelog page styles live in src/pages/changelog/[...page].astro
   as a scoped <style> block so markup + CSS always ship and reload in
   lockstep (avoids HTML/static-CSS cache mismatches). */

/* Features page: media frame on the radius scale (lg). The .multiagent-card
   radius + skin come from the one card spec (WS-1e block below). */
.features-image.stroke-purple {
  border-radius: var(--m-radius-lg);
}

/* Lock every listing thumbnail to the house 5:4 box so any image (including the
   OG-banner fallback for posts without a thumbnailImage) is cropped to a uniform
   card shape via object-fit: cover, instead of the card inheriting the image's
   own aspect ratio. The .news variant keeps its fixed 112x112 square. */
.link-post-thumbnail:not(.news) {
  aspect-ratio: 5 / 4;
  overflow: hidden;
}

.image-post-thumbnail {
  border-radius: var(--m-radius-lg);
}

/* Features page videos: blend pillarbox with section dark-purple background */
.vimeo-features,
.vimeo-features.w-embed,
.vimeo-features.w-iframe,
.vimeo-features > div,
.vimeo-features iframe {
  background-color: var(--m-surface);
}

.vimeo-features {
  overflow: hidden;
  border-radius: var(--m-radius-lg);
}

.vimeo-features iframe {
  transform: scale(1.07);
  transform-origin: center center;
}

/* Homepage video tabs: self-hosted native <video>. Dark fallback behind the
   poster covers any letterbox edge; no Vimeo chrome to crop, so the old
   scale() hacks are gone — object-fit:cover (set on the element) fills the box. */
.feature-tab-image,
.feature-tab-image.w-embed,
.feature-tab-image.w-iframe,
.feature-tab-image > div,
.feature-tab-image video {
  background-color: #000000;
}

.feature-tab-image {
  border-radius: var(--m-radius-lg);
  overflow: hidden;
  padding-left: 0;
  padding-right: 0;
  aspect-ratio: unset;
  height: auto;
}

/* Give the homepage product showcase much more room. Webflow caps the tabs
   container at 800px, which left the recording reading small inside the widened
   1280px hero column. Let it fill most of that column so the video/screenshot
   is the hero moment. Scoped to >=768px (same as the .content-container
   widening above) so mobile keeps Webflow's own 90%-width / 800px behavior and
   its side gutters — an unscoped width:100% here overrode the mobile rule and
   killed those gutters. */
@media screen and (min-width: 768px) {
  .feature-tabs-container {
    width: 100%;
    max-width: 1080px;
  }
}

.feature-tab-image > div {
  overflow: hidden;
  line-height: 0;
  font-size: 0;
}

/* object-fit: cover is set inline on the <video> in SelfHostedVideo.astro so the
   component is self-contained; no duplicate rule needed here. */

/* Feature-tab selector buttons: Webflow ships these at border-radius:80px, well
   off the control scale. Pull them onto --m-radius-md like every other control
   (only the focus ring shape is visible now that the fill is gone below). */
.button-tab {
  border-radius: var(--m-radius-md);
}

/* Feature tabs (homepage) — underline tabs, not buttons.
   Buttons on this page are filled rounded-rects (radius-md); any filled or
   ringed treatment on the tab row reads as a second set of CTAs under the
   video. So the selector borrows nothing from the button vocabulary: quiet
   labels on a shared hairline rail, and the active tab is marked by a 2px
   accent bar plus brighter text — no fill, no ring, no glow. */
.feature-tabs .tabs-menu.style-rounded {
  border: 0;
  border-radius: 0;
  background-color: transparent;
  gap: 8px;
  padding: 0;
  border-bottom: 1px solid var(--m-hairline);
}

.feature-tabs .button-tab {
  position: relative;
  background-color: transparent;
  box-shadow: none;
  padding: 10px 14px 12px;
  transition: none;
}

/* Active indicator: a 2px accent bar sitting on the rail. */
.feature-tabs .button-tab::after {
  content: "";
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: -1px;
  height: 2px;
  background-color: var(--m-accent);
  opacity: 0;
  transform: scaleX(0.6);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

/* Inactive labels recede rather than sitting at full white. */
.feature-tabs .button-tab .button-tab-text {
  color: var(--m-text-2);
  font-weight: 500;
  transition: color 0.2s ease;
}

/* Hover: text brightens; no surface, no purple flash. (Padding repeated
   because Webflow's .button-tab:hover re-asserts its own.) */
.feature-tabs .button-tab:hover {
  background-color: transparent;
  box-shadow: none;
  padding: 10px 14px 12px;
}
.feature-tabs .button-tab:hover .button-tab-text {
  color: var(--m-text-1);
}

/* Active: bar on, label at full strength. Still no fill. */
.feature-tabs .button-tab.w--current,
.feature-tabs .button-tab.w--current:hover {
  background-color: transparent;
  box-shadow: none;
}
.feature-tabs .button-tab.w--current::after {
  opacity: 1;
  transform: scaleX(1);
}
.feature-tabs .button-tab.w--current .button-tab-text {
  color: var(--m-text-1);
  font-weight: 600;
}

/* ≤479px: Webflow stacks the menu into a column, where a shared bottom rail
   and edge-to-edge underlines stop making sense. Drop the rail and mark the
   active tab with a short centered bar under its label instead. */
@media screen and (max-width: 479px) {
  .feature-tabs .tabs-menu.style-rounded {
    border-bottom: 0;
    gap: 2px;
  }
  .feature-tabs .button-tab::after {
    left: 50%;
    right: auto;
    width: 32px;
    bottom: 4px;
    transform: translateX(-50%) scaleX(0.6);
  }
  .feature-tabs .button-tab.w--current::after {
    transform: translateX(-50%) scaleX(1);
  }
}

.tabs-content.w-tab-content,
.tabs-content {
  border-radius: var(--m-radius-lg);
  overflow: hidden;
  background-color: transparent;
  position: relative;
}

.w-tab-pane {
  overflow: hidden;
  line-height: 0;
  font-size: 0;
}

/* Homepage: HiVE section image — media frame on the radius scale (lg).
   Cap the photo so it reads as a supporting visual, not the hero. */
.built-for-hive-image {
  border-radius: var(--m-radius-lg);
  width: 78%;
  max-width: 440px;
}

/* Company page: override leadership image to match live site */
.leadership-image {
  background-image: url(/images/698fcbb01708c44e37bfc0fe_company-leadership.jpg) !important;
  border-radius: var(--m-radius-lg);
}

/* .guarantee-card + .value-block skin/radius come from the one card spec
   (WS-1e block below); value-block keeps its seated-tab bottom corners. */

@media screen and (max-width: 767px) {
  .agent-grid {
    grid-template-columns: 1fr;
  }
}

/* Security page: scale down section H2s — these are informational labels, not marketing headlines */
[data-route="/security"] .heading-2a {
  font-size: 36px;
}

[data-route="/security"] .heading-4 {
  font-weight: 400;
}

/* Built with DevSwarm: prevent subheading from wrapping too early */
[data-route="/built-with-devswarm"] .subheading-hero {
  width: 100%;
  max-width: 800px;
}

/* Get Featured: prevent subheading from wrapping too early */
[data-route="/get-featured"] .subheading-hero {
  width: 100%;
  max-width: 700px;
}

/* Get Featured: reduce hero top padding */
[data-route="/get-featured"] .section-hero {
  padding-top: 64px;
}

/* The hero glow image (hero-take-5.avif) reads as a funky band that gets cut
   off behind the heading at this scale. Drop it site-wide so each hero flows
   seamlessly into the rest of the page. */
.section-hero.with-background {
  background-image: none;
}

/* Features hero: runs the same animated hex-grid field as the homepage hero
   (canvas + scripts are on the page, the route gate in hex-grid.js allows
   "/features"). The glow geometry below mirrors .section-hero.homepage::before
   so both heroes read as one background. */
[data-route="/features"] .section-hero.with-background {
  position: relative;
  overflow: hidden;
}

[data-route="/features"] .section-hero.with-background::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 130%;
  height: 78%;
  background: radial-gradient(
    ellipse 55% 60% at 50% 72%,
    var(--m-glow) 0%,
    transparent 66%
  );
  pointer-events: none;
  z-index: 0;
}

[data-route="/features"] .section-hero.with-background > * {
  position: relative;
  z-index: 1;
}

/* Security page: reduced section padding */
[data-route="/security"] .section-main {
  padding-top: 48px;
  padding-bottom: 48px;
}

/* Security page: wider list items */
[data-route="/security"] .features-content {
  width: 100% !important;
}

[data-route="/security"] .features-list-block {
  width: 100% !important;
  max-width: 800px !important;
}

/* Security page: smaller, centered card titles */
[data-route="/security"] .multiagent-card .heading-3 {
  font-size: var(--m-h3-size);
  text-align: center;
}

[data-route="/security"] .multiagent-card .text-card-body {
  text-align: center;
}

/* ============================================================
   Homepage hex grid canvas (replaces static AVIF hero bg)
   ============================================================ */

.section-hero.homepage {
  position: relative;
  /* Clears the 70px fixed nav; the announcement pill is the first hero
     element, spaced from the headline by its own margin-bottom. */
  padding-top: 84px;
  /* Clip the 130%-wide ::before glow to the hero so it can't spill past the
     edges and force a document-wide horizontal overflow (cut off content). */
  overflow: hidden;
}

/* Applied by hex-grid.js to whichever hero hosts the canvas (homepage or
   /features), so the static AVIF never shows through the animated field. */
.section-hero.hex-grid-active {
  background-image: none !important;
}

.section-hero.hex-grid-hybrid #hex-grid-canvas {
  background: transparent;
}

#hex-grid-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
  will-change: opacity;
}

.section-hero.homepage > .w-layout-blockcontainer {
  position: relative;
  z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
  #hex-grid-canvas {
    display: none;
  }
}

/* ============================================================
   Homepage ambient particles
   ============================================================ */

#particle-canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: -1;
}

@media (prefers-reduced-motion: reduce) {
  #particle-canvas {
    display: none;
  }
}

/* ============================================================
   Homepage scroll-reveal animations
   ============================================================ */

/* Base: fade up from 30px */
.scroll-reveal {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.4s ease-out, transform 0.4s ease-out;
}

.scroll-reveal.revealed {
  opacity: 1;
  transform: translateY(0);
}

/* Slide from left */
.scroll-reveal-left {
  opacity: 0;
  transform: translateX(-40px);
  transition: opacity 0.4s ease-out, transform 0.4s ease-out;
}

.scroll-reveal-left.revealed {
  opacity: 1;
  transform: translateX(0);
}

/* Slide from right */
.scroll-reveal-right {
  opacity: 0;
  transform: translateX(40px);
  transition: opacity 0.4s ease-out, transform 0.4s ease-out;
}

.scroll-reveal-right.revealed {
  opacity: 1;
  transform: translateX(0);
}

/* Stagger delays via data-delay attribute */
.scroll-reveal[data-delay="1"],
.scroll-reveal-left[data-delay="1"],
.scroll-reveal-right[data-delay="1"] {
  transition-delay: 0.1s;
}

.scroll-reveal[data-delay="2"],
.scroll-reveal-left[data-delay="2"],
.scroll-reveal-right[data-delay="2"] {
  transition-delay: 0.2s;
}

.scroll-reveal[data-delay="3"],
.scroll-reveal-left[data-delay="3"],
.scroll-reveal-right[data-delay="3"] {
  transition-delay: 0.3s;
}

/* ── CTA glow: a single brand-accent pulse when the banner reveals,
      then a restrained resting glow. Not the infinite loop, and on the
      brand accent (was Tailwind violet #8b5cf6). Part III §5. ── */

@keyframes glow-pulse {
  0% {
    box-shadow: 0 0 0 0 transparent;
  }
  40% {
    box-shadow: 0 4px 28px 0 var(--m-glow);
  }
  100% {
    box-shadow: 0 4px 16px 0 var(--m-accent-tint);
  }
}

.banner-section .block-banner.revealed .download-button {
  animation: glow-pulse 1.2s ease-out both;
}

/* ============================================================
   Hero bracket entrance animation
   Brackets start at center (touching), slide apart while headline fades in.
   Gated on .hero-anim class added by sync inline script.
   ============================================================ */

/* Initial hidden states */
.hero-anim .chevron-left,
.hero-anim .chevron-right {
  opacity: 0;
  transition: opacity 0.4s ease-out, transform 0.4s ease-out;
}

.hero-anim .chevron-left {
  transform: translateX(var(--bracket-offset-left, 40px)) scale(0.9);
}

.hero-anim .chevron-right {
  transform: translateX(var(--bracket-offset-right, -40px)) scale(0.9);
}

.hero-anim #homepage-headline {
  opacity: 0;
  transition: opacity 0.4s ease-out 0.1s;
}

/* Revealed state */
.hero-anim .chevron-left.hero-revealed,
.hero-anim .chevron-right.hero-revealed {
  opacity: 1;
  transform: translateX(0) scale(1);
}

.hero-anim #homepage-headline.hero-revealed {
  opacity: 1;
}

/* ── Homepage agent logo marquee ─────────────────────────────── */

@keyframes marquee-scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-25%);
  }
}

.block-clients {
  overflow: hidden;
}

.clients.scrolling {
  animation: marquee-scroll 20s linear infinite;
}

/* ── Reduced motion: disable all scroll animations ───────────── */

@media (prefers-reduced-motion: reduce) {
  .scroll-reveal,
  .scroll-reveal-left,
  .scroll-reveal-right {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .banner-section .block-banner.revealed .download-button {
    animation: none;
  }

  .clients.scrolling {
    animation: none;
  }

  /* Hero brackets: no animation */
  .hero-anim .chevron-left,
  .hero-anim .chevron-right,
  .hero-anim #homepage-headline {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* ============================================================
   Hive promo section (homepage) — unframed two-column split:
   intro text left, featured-project card right, directly on
   the transparent --m-bg (no panel, no background graphic).
   ============================================================ */

.bwd-hive-banner {
  /* .section-secondary is a flex column with align-items:center, so a child
     with only max-width won't stretch — it sizes to content and can overflow
     to the right instead of centering. width:100% + box-sizing makes it fill
     the available width (capped by max-width) and center via the auto margins. */
  width: 100%;
  max-width: var(--m-content-width);
  margin: 0 auto;
  padding: 0 24px;
  box-sizing: border-box;
}

.bwd-hive-banner-inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: center;
}

.bwd-hive-left {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.bwd-hive-left .heading-3,
.bwd-hive-left .text-body-medium {
  margin-bottom: 0;
}

.bwd-hive-card {
  background: var(--bwd-surface);
  border: 1px solid var(--bwd-border);
  border-radius: var(--m-radius-lg);
  overflow: hidden;
}

.bwd-hive-card-screen {
  aspect-ratio: 16/10;
  background: linear-gradient(
    160deg,
    var(--m-surface-2) 0%,
    color-mix(in srgb, var(--m-accent) 20%, var(--m-bg)) 50%,
    var(--m-surface) 100%
  );
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
}

.bwd-hive-ui {
  position: absolute;
  inset: 0;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.bwd-hive-ui-row {
  display: flex;
  gap: 8px;
}

.bwd-hive-ui-block {
  background: color-mix(in srgb, var(--m-accent) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-accent) 25%, transparent);
  border-radius: 6px;
  flex-shrink: 0;
}

.bwd-hive-ui-block.header {
  height: 28px;
  flex: 1;
}
.bwd-hive-ui-block.sidebar {
  width: 28%;
  height: 120px;
}
.bwd-hive-ui-block.main {
  flex: 1;
  height: 120px;
}
.bwd-hive-ui-block.stat {
  flex: 1;
  height: 52px;
}
.bwd-hive-ui-block.wide {
  flex: 2;
  height: 36px;
}
.bwd-hive-ui-block.narrow {
  flex: 1;
  height: 36px;
}

.bwd-hive-ui-accent {
  position: absolute;
  bottom: 16px;
  left: 16px;
  right: 16px;
  height: 3px;
  background: linear-gradient(90deg, var(--bwd-purple), transparent);
  border-radius: 2px;
  opacity: 0.6;
}

.bwd-hive-card-footer {
  padding: 14px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.bwd-hive-card-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.bwd-hive-featured-badge {
  font-size: var(--m-eyebrow-size);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--m-accent-text);
}

.bwd-hive-card-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--bwd-text);
}

.bwd-hive-card-arrow {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--bwd-purple-dim);
  border: 1px solid var(--bwd-border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bwd-purple-light);
  flex-shrink: 0;
  transition: background-color 0.3s ease, color 0.3s ease;
}

/* The hive card is an anchor to /built-with-devswarm/ — give it link affordances. */
a.bwd-hive-card {
  display: block;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}

a.bwd-hive-card:hover,
a.bwd-hive-card:focus-visible {
  border-color: var(--m-hairline-strong);
  box-shadow: 0 14px 44px var(--m-accent-tint);
}

a.bwd-hive-card:focus-visible {
  outline: 2px solid var(--m-accent);
  outline-offset: 2px;
}

a.bwd-hive-card:hover .bwd-hive-card-arrow {
  background: var(--bwd-purple);
  color: #fff;
}

/* Real featured-project covers, stacked over the skeleton mockup once the
   projects feed resolves. They crossfade as the showcase rotates. */
.bwd-hive-cover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  z-index: 2;
  opacity: 0;
  transform: scale(1.02);
  transition: opacity 0.6s ease, transform 6s ease;
}

.bwd-hive-cover.is-active {
  opacity: 1;
  /* Slow Ken Burns drift while a cover is showing. */
  transform: scale(1.06);
}

.bwd-hive-card.has-project .bwd-hive-ui,
.bwd-hive-card.has-project .bwd-hive-ui-accent {
  display: none;
}

/* Keep real (variable-length) titles/bylines to a single tidy line. */
.bwd-hive-card.has-project .bwd-hive-card-meta {
  flex: 1;
  min-width: 0;
}

.bwd-hive-card.has-project .bwd-hive-card-name,
.bwd-hive-card.has-project .bwd-hive-featured-badge {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Vignette for depth + legibility behind the position dots. */
.bwd-hive-card.has-project .bwd-hive-card-screen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--m-bg) 0%, transparent) 55%,
    color-mix(in srgb, var(--m-bg) 55%, transparent) 100%
  );
}

/* Position dots: indicative only (the card is a link), bottom-left. */
.bwd-hive-dots {
  position: absolute;
  left: 14px;
  bottom: 12px;
  z-index: 4;
  display: flex;
  gap: 6px;
  pointer-events: none;
}

.bwd-hive-dot-ind {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.35);
  transition: width 0.35s ease, background-color 0.35s ease;
}

.bwd-hive-dot-ind.is-active {
  width: 18px;
  border-radius: 4px;
  background: var(--bwd-purple-light);
}

@media (prefers-reduced-motion: reduce) {
  .bwd-hive-cover {
    transition: opacity 0.3s ease;
    transform: none;
  }

  .bwd-hive-cover.is-active {
    transform: none;
  }
}

/* The bwd primary button is retired — its call-sites now use the canonical
   m-btn-primary (WS-1a). See the WS-1a block near the end of this file. */

/* ── Case Study — purple em accent words ── */
em.cs-accent {
  font-style: normal !important;
  /* accent WORDS as text: the readable accent-text token, not the fill
     value (fixes the 3.19:1 contrast failure). !important beats the
     page's inline color hexes until WS-1d de-hardcodes case-study. */
  color: var(--m-accent-text) !important;
  -webkit-text-fill-color: var(--m-accent-text) !important;
}

@media (max-width: 860px) {
  .bwd-hive-banner {
    padding: 0 24px;
  }

  .bwd-hive-banner-inner {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .bwd-hive-right {
    max-width: 100%;
  }
}

/* ============================================================
   Blog — listing + article (redesign)
   Editorial magazine listing (featured hero + filterable grid) and a
   focused reading experience (sticky TOC, progress bar, refined prose,
   related posts). All classes are global so the MDX-rendered article body
   and the shared BlogCard/related-posts markup pick up the same styles.
   ============================================================ */

[data-route="/blog"] .blog-listing,
.blog-article,
.blog-related {
  /* Roboto Mono first, matching --m-font-mono, so blog chrome eyebrows use the
     same mono as the rest of the site instead of the OS default. */
  --blog-mono: "Roboto Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    Consolas, monospace;
  /* Text tones, brightest to dimmest. The dark --m-bg makes desaturated
     purple-grays read dimmer than their measured contrast, so every tier is
     kept well above WCAG AA: ink 17.4:1, soft 12.7:1, muted 8.2:1, faint
     5.9:1, accent 9.9:1. Keep the hierarchy: prose reads in ink, secondary
     copy in soft, metadata in muted, tiny labels in faint. Raw --m-text-2 /
     --m-text-3 are too dim for long-form reading text on this page; reserve
     them for metadata and decorative marks. */
  --blog-ink: var(--m-text-1);
  --blog-ink-soft: color-mix(in srgb, var(--m-text-1) 55%, var(--m-text-2));
  --blog-ink-muted: var(--m-text-2);
  --blog-ink-faint: color-mix(in srgb, var(--m-text-2) 55%, var(--m-text-3));
  /* Raw --m-accent-text sits at 6.5:1, fine beside muted UI text but faint
     beside 17:1 prose ink, so lift it toward text-1 for blog use. */
  --blog-accent-text: color-mix(
    in srgb,
    var(--m-accent-text) 60%,
    var(--m-text-1)
  );
}

/* ── Shared tag pill (colored dot + muted mono label) ── */

.blog-tag-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px 4px 9px;
  border-radius: var(--m-radius-full);
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.04);
  font-family: var(--blog-mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--blog-ink);
  white-space: nowrap;
}

.blog-tag-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--dot, var(--m-accent));
  box-shadow: 0 0 0 3px
    color-mix(in srgb, var(--dot, var(--m-accent)) 16%, transparent);
  flex-shrink: 0;
}

/* Category chip pinned to the top-left corner of a card/hero image */
.blog-chip-overlay {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 2;
  background: color-mix(in srgb, var(--m-bg) 66%, transparent);
  border-color: rgba(255, 255, 255, 0.18);
  color: var(--m-text-1);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.blog-hero-media .blog-chip-overlay {
  top: 16px;
  left: 16px;
}

/* ============================================================
   Listing page
   ============================================================ */

/* body-8 already adds margin-top:120px to clear the fixed navbar, so the
   listing only needs a small top gap of its own. */
[data-route="/blog"] .section-secondary.blog-page {
  padding: 20px 24px 64px;
}

[data-route="/blog"] .blog-listing {
  max-width: var(--m-content-width);
  margin: 0 auto;
}

/* ── Filter + search bar ── */

.blog-filter-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 40px;
  /* Clears the fixed navbar when "View all" scrolls the bar into view. */
  scroll-margin-top: 110px;
}

.blog-filter-chips {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.blog-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 20px;
  border-radius: var(--m-radius-full);
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: transparent;
  color: var(--blog-ink-soft);
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}

.blog-filter-chip:hover {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.28);
}

.blog-filter-chip.is-active {
  color: #fff;
  background: color-mix(in srgb, var(--m-accent) 16%, transparent);
  border-color: var(--m-accent);
}

.blog-search-wrap {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 320px;
  max-width: 480px;
}

.blog-search-icon {
  position: absolute;
  left: 14px;
  color: var(--blog-ink-faint);
  pointer-events: none;
}

.blog-search {
  height: 40px;
  width: 100%;
  min-width: 0;
  padding: 0 58px 0 38px;
  border-radius: var(--m-radius-md);
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: var(
    --input-field-background,
    color-mix(in srgb, var(--m-bg) 70%, transparent)
  );
  color: var(--m-text-1);
  font-family: inherit;
  font-size: 0.9rem;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}

.blog-search::placeholder {
  color: var(--blog-ink-faint);
}

.blog-search:focus {
  border-color: var(--m-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--m-accent) 18%, transparent);
}

/* OS-aware keyboard hint (⌘ F / Ctrl F) inside the search field */
.blog-search-kbd {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  border-radius: var(--m-radius-sm);
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.05);
  font-family: var(--blog-mono);
  font-size: 0.68rem;
  line-height: 1;
  letter-spacing: 0.02em;
  color: var(--blog-ink-muted);
  white-space: nowrap;
  pointer-events: none;
  transition: opacity 0.15s;
}

.blog-search-kbd:empty {
  display: none;
}

/* hide the hint while the user is actively searching */
.blog-search-wrap.is-active .blog-search-kbd {
  opacity: 0;
}

/* ── Featured post — split editorial card (image beside text) ── */

.blog-hero {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: 52px;
  align-items: center;
  margin-bottom: 44px;
  padding: 24px;
  border: 1px solid var(--m-hairline);
  border-radius: var(--m-radius-lg);
  color: inherit;
  text-decoration: none;
  transition: border-color 0.2s;
}

.blog-hero:hover {
  border-color: var(--m-hairline-strong);
}

.blog-hero-content {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
}

.blog-hero:hover .blog-hero-content h1 {
  color: var(--m-text-1);
}

.blog-hero-media {
  grid-column: 2;
  grid-row: 1;
  position: relative;
  aspect-ratio: 3 / 2;
  border-radius: var(--m-radius-lg);
  overflow: hidden;
  background: var(--m-surface);
}

.blog-hero-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.blog-hero-eyebrow {
  font-family: var(--blog-mono);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--blog-accent-text);
  margin-bottom: 16px;
}

/* The featured card renders the page h1 — undo the Webflow bare-h1
   treatment (4.75em gradient-fill display heading, centered, 940px cap). */
.blog-hero-content h1 {
  font-size: clamp(1.5rem, 2.4vw, 2.05rem);
  font-family: inherit;
  font-weight: 600;
  color: #fff;
  background-image: none;
  -webkit-text-fill-color: currentColor;
  text-align: left;
  max-width: none;
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin: 0 0 14px;
  padding: 0;
  transition: color 0.15s;
}

.blog-hero-desc {
  font-size: 1.02rem;
  color: var(--blog-ink-soft);
  line-height: 1.6;
  margin: 0 0 18px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.blog-hero-meta {
  display: flex;
  gap: 18px;
  color: var(--blog-ink-muted);
  font-size: 0.85rem;
}

/* ── Card grid: two-up rows of horizontal cards (text left, square image
   right, "Read blog" pill). ── */

.blog-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  column-gap: 64px;
  row-gap: 0;
}

.blog-card {
  display: flex;
  align-items: center;
  gap: 28px;
  padding: 28px 0;
  color: inherit;
  text-decoration: none;
}

.blog-card-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.blog-card-text h3 {
  font-size: 1.3rem;
  font-weight: 600;
  color: #fff;
  line-height: 1.28;
  letter-spacing: -0.01em;
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.blog-card:hover .blog-card-text h3 {
  color: var(--m-text-1);
}

.blog-card-meta {
  display: flex;
  gap: 18px;
  font-size: 0.88rem;
  color: var(--blog-ink-muted);
}

.blog-card-cta {
  margin-top: 4px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--blog-ink-soft);
  transition: color 0.15s;
}

.blog-card-cta span {
  transition: transform 0.15s;
}

.blog-card:hover .blog-card-cta {
  color: #fff;
}

.blog-card:hover .blog-card-cta span {
  transform: translateX(3px);
}

.blog-card-thumb {
  flex-shrink: 0;
  width: 150px;
  height: 150px;
  border-radius: var(--m-radius-lg);
  overflow: hidden;
  background: var(--m-surface);
}

.blog-card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ── Filter states ── */

.is-hidden {
  display: none !important;
}

.blog-empty {
  text-align: center;
  color: var(--blog-ink-muted);
  font-size: 1rem;
  padding: 48px 0;
  margin: 0;
}

/* ── Category sections (default browse view) ── */

.blog-sections {
  display: flex;
  flex-direction: column;
  gap: 52px;
}

.blog-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 22px;
}

.blog-section-title {
  margin: 0;
  font-size: 0.82rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--blog-ink-muted);
}

.blog-view-all {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 2px;
  background: transparent;
  border: 0;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--blog-ink-soft);
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.15s;
}

.blog-view-all span {
  transition: transform 0.15s;
}

.blog-view-all:hover {
  color: #fff;
}

.blog-view-all:hover span {
  transform: translateX(3px);
}

/* ============================================================
   Article (individual post)
   ============================================================ */

/* Reading progress bar pinned above the chrome */
.blog-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 200;
  pointer-events: none;
  background: transparent;
}

.blog-progress span {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--m-accent), var(--m-accent-hover));
  box-shadow: 0 0 10px color-mix(in srgb, var(--m-accent) 60%, transparent);
  transition: width 0.05s linear;
}

.blog-article {
  max-width: 1080px;
  margin: 0 auto;
  /* The body (.body-7) already adds margin-top:120px to clear the fixed navbar,
     so the article only needs a small gap of its own above the back link. */
  padding: 24px 24px 0;
}

.blog-article-header-inner {
  max-width: 760px;
  margin: 0 auto;
}

.blog-back-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.9rem;
  color: var(--blog-ink-soft);
  text-decoration: none;
  margin-bottom: 26px;
  transition: color 0.15s;
}

.blog-back-link:hover {
  color: #fff;
}

.blog-article-tags {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.blog-article-title {
  font-size: clamp(2rem, 4.4vw, 3rem);
  line-height: 1.12;
  font-weight: 600;
  color: #fff;
  letter-spacing: -0.02em;
  margin: 0 0 18px;
}

.blog-article-lead {
  font-size: 1.2rem;
  line-height: 1.55;
  color: var(--blog-ink-soft);
  margin: 0 0 24px;
}

.blog-article-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 0.92rem;
  color: var(--blog-ink-muted);
}

.blog-article-author {
  color: var(--m-text-1);
  font-weight: 600;
}

.blog-article-sep {
  color: var(--m-text-3);
}

.blog-article-hero-wrap {
  max-width: 960px;
  margin: 44px auto 8px;
}

.blog-article-hero {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--m-radius-lg);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.blog-article-body {
  max-width: 760px;
  margin: 48px auto 0;
}

.blog-prose-col {
  min-width: 0;
}

/* Posts with a table of contents get a wider layout: a full-width title band,
   then a two-column body — the article (subtitle, byline, hero, prose) on the
   left and a sticky TOC sidebar on the right. Only the title sits above the
   TOC; everything else lives beside it. Posts without a TOC are untouched
   (stay centered at 760px). */
.blog-article.has-toc {
  max-width: 1160px;
}

.blog-article.has-toc .blog-article-header-inner {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
}

.blog-article.has-toc .blog-article-body {
  max-width: none;
  margin: 28px 0 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 56px;
  align-items: start;
}

.blog-article.has-toc .blog-prose-col {
  grid-column: 1;
  grid-row: 1;
}

/* Subtitle, byline and hero now sit at the top of the left column, aligned with
   the TOC's top edge. */
.blog-article.has-toc .blog-article-lead {
  margin-top: 0;
}

.blog-article.has-toc .blog-article-hero-wrap {
  max-width: none;
  margin: 26px 0 4px;
}

.blog-article.has-toc .blog-prose {
  margin-top: 32px;
}

/* ── Table of contents (right sidebar) ── */

/* Sticky lives on the grid item itself: its containing block is the tall grid,
   so align-items:start keeps it content-height while it travels down the page.
   max-height + overflow keep a long TOC scrollable inside the viewport. */
.blog-toc {
  grid-column: 2;
  grid-row: 1;
  position: sticky;
  top: 104px;
  align-self: start;
  max-height: calc(100vh - 128px);
  overflow-y: auto;
}

.blog-toc-title {
  font-family: var(--blog-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--blog-ink-faint);
  margin: 0 0 14px;
}

.blog-toc nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-left: 1px solid rgba(255, 255, 255, 0.1);
}

.blog-toc-item a {
  display: block;
  padding: 5px 0 5px 14px;
  margin-left: -1px;
  border-left: 2px solid transparent;
  color: var(--blog-ink-muted);
  font-size: 0.86rem;
  line-height: 1.4;
  text-decoration: none;
  transition: color 0.15s, border-color 0.15s;
}

.blog-toc-item a:hover {
  color: var(--m-text-1);
}

.blog-toc-item a.is-active {
  color: #fff;
  border-left-color: var(--m-accent);
}

.blog-toc-item.depth-1 a {
  padding-left: 28px;
  font-size: 0.82rem;
}

.blog-toc-item.depth-2 a {
  padding-left: 40px;
  font-size: 0.8rem;
}

/* ── Prose (rendered MDX body) ── */

.blog-prose {
  color: var(--blog-ink);
  font-size: 1.075rem;
  line-height: 1.75;
}

.blog-prose > *:first-child {
  margin-top: 0;
}

.blog-prose p {
  margin: 0 0 1.4em;
}

.blog-prose h2,
.blog-prose h3,
.blog-prose h4,
.blog-prose h5 {
  color: #fff;
  scroll-margin-top: 100px;
}

.blog-prose h2 {
  font-size: 1.75rem;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 3.2em 0 0.45em;
}

.blog-prose h3 {
  font-size: 1.35rem;
  line-height: 1.3;
  font-weight: 600;
  margin: 2.6em 0 0.4em;
}

.blog-prose h4 {
  font-size: 1.12rem;
  font-weight: 600;
  color: var(--m-text-1);
  margin: 2.2em 0 0.35em;
}

.blog-prose h5 {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--m-text-1);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 2em 0 0.3em;
}

.blog-prose a {
  color: var(--blog-accent-text);
  text-decoration: none;
  border-bottom: 1px solid
    color-mix(in srgb, var(--blog-accent-text) 40%, transparent);
  transition: color 0.15s, border-color 0.15s;
}

.blog-prose a:hover {
  color: #fff;
  border-bottom-color: #fff;
}

.blog-prose strong {
  color: var(--m-text-1);
  font-weight: 600;
}

.blog-prose ul,
.blog-prose ol {
  margin: 0 0 1.4em;
  padding-left: 1.4em;
}

.blog-prose li {
  margin: 0 0 0.55em;
  padding-left: 0.3em;
}

.blog-prose ul li::marker {
  color: var(--m-accent);
}

.blog-prose ol li::marker {
  color: var(--m-text-2);
  font-variant-numeric: tabular-nums;
}

.blog-prose blockquote {
  margin: 1.6em 0;
  padding: 4px 0 4px 22px;
  border-left: 3px solid var(--m-accent);
  color: var(--blog-ink-soft);
  font-style: italic;
}

.blog-prose blockquote p:last-child {
  margin-bottom: 0;
}

.blog-prose img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--m-radius-lg);
  border: 1px solid rgba(255, 255, 255, 0.08);
  margin: 1.8em 0;
}

/* Self-hosted <SelfHostedVideo> clips render as an unclassed 16:9 wrapper div
   (inline padding/position, a <video> inside). Give the embeds room to breathe
   above and below, and round + frame them to match blog images. */
.blog-prose div:has(> video) {
  /* Bottom margin is intentionally larger than the h2 top margin (3.2em) so a
     clip followed by a heading still gets clear space below it, instead of the
     two margins collapsing to the heading's value. */
  margin: 3em 0 4em;
  border-radius: var(--m-radius-lg);
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.blog-prose hr {
  border: none;
  margin: 2.4em 0;
}

.blog-prose code {
  font-family: "Roboto Mono", var(--blog-mono);
  font-size: 0.88em;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--m-radius-sm);
  padding: 0.12em 0.4em;
  color: var(--m-text-1);
}

.blog-prose pre {
  background: var(--m-surface);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--m-radius-lg);
  padding: 18px 20px;
  overflow-x: auto;
  margin: 1.6em 0;
  font-size: 0.9rem;
  line-height: 1.6;
}

.blog-prose pre code {
  background: none;
  border: none;
  padding: 0;
  color: var(--m-text-1);
  font-size: inherit;
}

.blog-prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.6em 0;
  font-size: 0.95rem;
}

.blog-prose th,
.blog-prose td {
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.blog-prose th {
  color: #fff;
  font-weight: 600;
}

/* ── Share row ── */

.blog-share-row {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 48px;
  padding-top: 28px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.blog-share-label {
  font-family: var(--blog-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--blog-ink-faint);
}

.blog-share-row .social-share-block {
  display: inline-flex;
  gap: 10px;
  margin: 0;
}

/* ── Related posts ── */

.blog-related {
  margin-top: 64px;
  padding: 72px 0 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.blog-related-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 24px;
}

.blog-related-title {
  font-size: 1.6rem;
  font-weight: 600;
  color: #fff;
  margin: 0 0 28px;
}

/* ============================================================
   Blog responsive
   ============================================================ */

/* Not enough room for the TOC sidebar — collapse to a single centered column
   with the TOC stacked above the prose, and re-center the header/hero. */
@media (max-width: 1023px) {
  .blog-article.has-toc {
    max-width: 760px;
  }

  .blog-article.has-toc .blog-article-header-inner,
  .blog-article.has-toc .blog-article-hero-wrap {
    margin-left: auto;
    margin-right: auto;
  }

  .blog-article.has-toc .blog-article-body {
    grid-template-columns: 1fr;
    gap: 0;
    margin-left: auto;
    margin-right: auto;
  }

  /* Reset the desktop grid placement: leaving the TOC at grid-column:2 in a
     single-column grid spawns an implicit second column and squeezes the prose.
     With auto placement they stack in DOM order (TOC above the article). */
  .blog-article.has-toc .blog-prose-col,
  .blog-toc {
    grid-column: auto;
    grid-row: auto;
  }

  .blog-toc {
    position: static;
    max-height: none;
    overflow: visible;
    margin-bottom: 32px;
    padding-bottom: 28px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  }
}

/* At Webflow's medium breakpoint .body-7 loses its 120px margin-top (rule in
   the read-only devswarm-webflow.css), so the article must clear the fixed
   navbar pill itself or the back link hides underneath it. */
@media (max-width: 991px) {
  .blog-article {
    padding-top: 100px;
  }
}

@media (max-width: 860px) {
  .blog-hero {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  /* Reset the desktop column placement so the image and text stack in DOM
     order (image, then text) instead of spawning an implicit column. */
  .blog-hero-content,
  .blog-hero-media {
    grid-column: auto;
    grid-row: auto;
  }

  .blog-hero-media {
    aspect-ratio: 16 / 9;
  }
}

@media (max-width: 900px) {
  .blog-grid {
    grid-template-columns: 1fr;
    column-gap: 0;
  }
}

@media (max-width: 560px) {
  .blog-grid {
    grid-template-columns: 1fr;
  }

  .blog-card {
    gap: 16px;
    padding: 22px 0;
  }

  .blog-card-text h3 {
    font-size: 1.12rem;
  }

  .blog-card-thumb {
    width: 100px;
    height: 100px;
    border-radius: var(--m-radius-lg);
  }

  .blog-hero-content {
    padding: 26px;
  }

  .blog-filter-bar {
    flex-direction: column;
    align-items: stretch;
  }

  /* In column mode the `flex: 1 1 320px` basis becomes a HEIGHT, ballooning the
     wrapper to 320px tall. Reset to natural height and full width. */
  .blog-search-wrap {
    flex: none;
    width: 100%;
    max-width: none;
  }

  .blog-search {
    min-width: 0;
    width: 100%;
  }

  /* No physical Cmd/Ctrl on touch devices — drop the hint. */
  .blog-search-kbd {
    display: none;
  }
}

/* ============================================================
   Scrollbar styling — dark theme, visible only on hover
   ============================================================ */

html {
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
  transition: scrollbar-color 0.2s ease;
}

html:hover {
  scrollbar-color: var(--m-hairline-strong) var(--m-bg);
}

html::-webkit-scrollbar {
  width: 8px;
}

html::-webkit-scrollbar-track {
  background: var(--m-bg);
}

html::-webkit-scrollbar-thumb {
  background-color: transparent;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

html:hover::-webkit-scrollbar-thumb {
  background-color: var(--m-hairline-strong);
}

/* ============================================================
   Footer composition
   All selectors scoped under .section-footer so generic Webflow
   class names (.div-block-36, .form-block, .container-28) used
   elsewhere on the site are untouched. Webflow base CSS is
   read-only; every fix lives here.

   Layout, top to bottom:
   - a newsletter band (copy left, signup right) divided off;
   - the brand + four link columns;
   - a slim legal bar, divided off, left-aligned.
   Everything shares one 1200px content column so all edges and
   both dividers line up. The newsletter node lives at the top of
   Footer.astro as .footer-newsletter-band.
   ============================================================ */

/* Shared content column for every footer row — resolves through
   --m-content-width so footer edges line up with the widened
   .content-container sections above. */
.section-footer .footer-newsletter-band,
.section-footer .div-block-36,
.section-footer .footer-content {
  width: 100%;
  max-width: var(--m-content-width);
  margin-inline: auto;
}

/* --- Newsletter band ------------------------------------------ */
/* Copy on the left, signup on the right; wraps to stacked on
   narrow widths. Hairline divider sets it off from the columns. */
.section-footer .footer-newsletter-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px 48px;
  padding-bottom: 40px;
  margin-bottom: 48px;
  border-bottom: 1px solid var(--m-hairline);
}

.section-footer .footer-newsletter-copy {
  flex: 1 1 280px;
  min-width: 0;
}

.section-footer .footer-newsletter-title {
  color: var(--m-text-1);
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
}

.section-footer .footer-newsletter-sub {
  color: var(--m-text-2);
  font-size: 0.95rem;
  line-height: 1.5;
  margin-top: 6px;
  max-width: 48ch;
  text-wrap: pretty;
}

/* Signup form: a fixed-ish module on the right of the band. */
.section-footer .form-block {
  flex: 0 1 540px;
  width: 100%;
  max-width: 540px;
  margin: 0;
  padding-top: 0;
}

.section-footer .footer-field {
  width: 100%;
  min-width: 0;
  margin: 0;
}

/* Input + button row; wraps the button below if it ever gets too
   tight so the placeholder never clips. */
.section-footer .div-block-15 {
  flex-wrap: wrap;
}

/* Match the email input to the Subscribe button (both on the --m-radius-md
   control radius) so the two read as one control rather than two shapes.
   The button keeps the standard 40px control height from the base CSS; the
   input has no base height (its .8em vertical padding grows it to ~54px),
   so pin it to the same 40px. */
.section-footer .text-field.footer {
  border-radius: var(--m-radius-md);
  height: 40px;
  padding-block: 0;
  padding-inline: 1.25em;
  flex: 1 1 180px;
  min-width: 180px;
}

/* --- Link columns --------------------------------------------- */
/* Webflow's tablet link grid uses a zero column-gap, so columns
   can touch when space is tight. Guarantee separation. */
@media screen and (min-width: 768px) and (max-width: 991px) {
  .section-footer .grid-footer {
    column-gap: 24px;
  }
}

/* --- Legal bar ------------------------------------------------- */
/* Hairline divider, then disclaimer + copyright left-aligned on
   the shared content axis. */
.section-footer .footer-content {
  align-items: flex-start;
  margin-top: 56px;
  padding-top: 28px;
  border-top: 1px solid var(--m-hairline);
}

.section-footer .container-28 {
  align-items: flex-start;
  width: 100%;
  max-width: none;
  margin-bottom: 0;
}

.section-footer .text-block-16,
.section-footer .text-footer-down {
  text-align: left;
}

/* Free the copyright line from the Webflow width:150px cage so the
   full trademark sentence reads naturally; the redundant © image
   was removed in Footer.astro (the text already carries a © glyph). */
.section-footer .block-footer-down {
  width: auto;
  max-width: none;
  margin-inline: 0;
  margin-bottom: 0;
}

/* Balanced bottom padding (replaces the original ~112px void). */
.section-footer {
  padding-bottom: 3.5em;
}

/* Accessible-only label: the band shows a visible title, but the
   input keeps its programmatic label for screen readers. */
.section-footer .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   For Teams (/devswarm-for-teams/) + homepage team sections
   ============================================================ */

/* Homepage: team benefit checklist */
/* Checklist used by the /devswarm-for-teams page (image + benefits layout). */
.teams-checklist {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 48px;
  margin-top: 40px;
}

.teams-checklist-item {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 18px 0;
}

.teams-checklist-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  margin-top: 4px;
}

.teams-checklist-title {
  font-weight: 600;
  color: var(--header);
  margin: 0 0 4px;
}

.teams-checklist-sub {
  margin: 0;
  opacity: 0.7;
}

@media (max-width: 600px) {
  .teams-checklist {
    grid-template-columns: 1fr;
    gap: 0;
  }
}

/* ── For Teams: editorial ledger ─────────────────────────────────────────
   Same two-column recipe as the homepage FAQ section: the intro (eyebrow,
   heading, lead, CTA) is the narrower sticky rail on the LEFT; the numbered
   ledger takes the wide RIGHT column. No panel frame — the rows sit
   directly on the transparent --m-bg, matching the FAQ accordion. */
.teams-band .teams-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: 80px;
  align-items: start;
  width: 100%;
  text-align: left;
}

/* Intro on the left, list on the right — same placement as the FAQ. */
.teams-ledger {
  grid-column: 2;
  grid-row: 1;
}

.teams-intro {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 18px;
  position: sticky;
  top: 120px;
}

.teams-intro-title {
  margin: 0;
  text-align: left;
  font-size: var(--m-h2-size);
  line-height: 1.1;
}

.teams-intro-lead {
  margin: 0;
  max-width: 34ch;
  color: var(--m-text-2);
  font-size: var(--m-body-size);
  line-height: var(--m-body-lh);
}

.teams-intro-cta {
  margin-top: 6px;
}

/* Ledger — bare list; only row dividers. */
.teams-ledger {
  list-style: none;
  margin: 0;
  padding: 0;
}

.teams-row {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 28px;
  padding: 22px 16px 22px 20px;
  border-bottom: 1px solid var(--m-hairline);
  transition: background 0.3s var(--m-ease, ease),
    padding-left 0.3s var(--m-ease, ease);
}

.teams-row:last-child {
  border-bottom: none;
}

/* Accent rail that draws in on hover */
.teams-row::before {
  content: "";
  position: absolute;
  left: 0;
  top: -1px;
  bottom: -1px;
  width: 2px;
  background: var(--m-accent);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 0.3s var(--m-ease, ease);
}

.teams-row:hover {
  background: linear-gradient(90deg, var(--m-accent-tint), transparent 62%);
  padding-left: 28px;
}

.teams-row:hover::before {
  transform: scaleY(1);
}

.teams-row-index {
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--m-text-3);
  font-variant-numeric: tabular-nums;
  padding-top: 3px;
  transition: color 0.25s var(--m-ease, ease);
}

.teams-row:hover .teams-row-index {
  color: var(--m-accent-text);
}

.teams-row-title {
  margin: 0;
  font-size: var(--m-body-size);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--m-text-1);
}

.teams-row-sub {
  margin: 6px 0 0;
  max-width: 48ch;
  color: var(--m-text-2);
  line-height: 1.55;
}

/* Tablet: stack the intro above the ledger, drop the sticky behavior
   (same breakpoint + stacking as the FAQ section). */
@media (max-width: 991px) {
  .teams-band .teams-layout {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .teams-ledger,
  .teams-intro {
    grid-column: 1;
    grid-row: auto;
  }

  .teams-intro {
    position: static;
    top: auto;
    order: -1;
  }

  .teams-intro-lead {
    max-width: 52ch;
  }
}

@media (max-width: 600px) {
  .teams-row {
    gap: 18px;
    padding: 20px 4px 20px 16px;
  }

  .teams-row:hover {
    padding-left: 20px;
  }
}

/* Teams page: hero CTA wrapper */
.ft-hero-cta {
  display: flex;
  justify-content: center;
  margin-top: 40px;
}

/* Teams page: workflow steps narrow container */
.ft-workflow-container {
  max-width: 900px;
}

/* Shared: section subheading below a heading-2a */
.ft-section-subheading {
  font-size: 18px;
  color: var(--paragraphs-light);
  max-width: 860px;
  margin: 14px auto 0;
  line-height: 1.65;
  text-align: center;
}

/* Teams page: before/after compare */
.ft-compare-section {
  padding-top: 0;
}

.ft-compare-heading {
  font-size: var(--m-h2-size);
  font-weight: 600;
  color: var(--header);
  line-height: 1.4;
  text-align: left;
  margin: 0 0 40px;
}

.ft-compare-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  width: 100%;
}

.ft-compare-card {
  padding: 0 40px;
}

.ft-compare-card--without {
  padding-left: 0;
  border-right: 1px solid var(--primary);
}

.ft-compare-card--with {
  padding-right: 0;
}

.ft-compare-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 16px;
  color: var(--paragraphs-light);
}

.ft-compare-card--with .ft-compare-label {
  color: var(--primary);
}

.ft-compare-card-heading {
  font-size: 20px;
  font-weight: 600;
  color: var(--header);
  line-height: 1.4;
  margin: 0 0 16px;
}

.ft-compare-body {
  font-size: 18px;
  color: var(--paragraphs-light);
  line-height: 1.7;
  margin: 0;
}

.ft-compare-image {
  display: block;
  width: 100%;
  max-width: 420px;
  height: auto;
  margin: 32px auto 0;
}

.ft-compare-card--with .ft-compare-image {
  margin-top: 8px;
}

/* Teams page: problem section two-column layout */
.ft-problem-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: start;
}

.ft-problem-header {
  display: flex;
  flex-direction: column;
  gap: 20px;
  align-items: flex-start;
}

.ft-problem-header .heading-3 {
  margin: 0;
  color: var(--header);
}

/* Teams page: problem block. Skin (surface + hairline + radius-lg) comes from
   the one card spec (WS-1e block below). */
.ft-problem-block {
  padding: 36px;
  margin-top: 0;
}

.ft-bullet-list {
  list-style: none;
  padding: 0;
  margin: 16px 0 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ft-bullet-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.ft-bullet-list .icon-list {
  margin-top: -1px;
}

/* Teams page: role cards (Section C) */
.ft-role-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px;
  margin-top: 40px;
}

/* Skin (surface + hairline + radius-lg) comes from the one card spec
   (WS-1e block below). */
.ft-role-card {
  padding: 30px;
}

.ft-role-label {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--primary);
  margin: 0 0 8px;
}

.ft-role-heading {
  font-size: 18px;
  font-weight: 600;
  color: var(--header);
  margin: 0 0 8px;
}

.ft-role-desc {
  font-size: 15px;
  color: var(--paragraphs-light);
  margin: 0 0 18px;
  line-height: 1.55;
}

.ft-role-benefits {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ft-role-benefits li {
  font-size: 15px;
  color: var(--paragraphs-light);
  line-height: 1.45;
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.ft-role-benefits li .icon-list {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  margin-top: 4px;
}

/* Teams page: comparison grid (Section D) */
.ft-comp-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin-top: 40px;
  padding: 16px 24px 16px 16px;
}

.ft-comp-wrapper {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1.4fr 1.4fr;
  grid-template-rows: repeat(6, auto);
  position: relative;
  min-width: 700px;
}

.ft-comp-ds-bg {
  position: absolute;
  grid-column: 4 / 5;
  grid-row: 1 / 7;
  inset: 0;
  box-shadow: 0 0 16px 6px color-mix(in srgb, var(--m-accent) 55%, transparent);
  border: 1px solid color-mix(in srgb, var(--m-accent) 40%, transparent);
  z-index: 0;
  pointer-events: none;
}

.ft-comp-cell {
  padding: 24px 16px;
  font-size: 15px;
  color: var(--paragraphs-light);
  line-height: 1.5;
  border-bottom: 1px solid var(--m-hairline);
  background: transparent;
  position: relative;
  z-index: 1;
}

.ft-comp-cell.ft-comp-header {
  font-size: 16px;
  font-weight: 600;
  color: var(--header);
  border-bottom: 1px solid var(--m-hairline-strong);
  text-transform: none;
  letter-spacing: 0;
}

.ft-comp-cell.ft-comp-label {
  font-size: 16px;
  font-weight: 600;
  color: var(--header);
  border-right: 1px solid var(--m-hairline);
}

.ft-comp-cell.ft-comp-ds {
  color: var(--header);
  font-weight: 500;
}

.ft-comp-cell.ft-comp-ds.ft-comp-header {
  font-weight: 600;
}

.ft-comp-cell.ft-comp-last {
  border-bottom: none;
}

/* Teams page: integrations section (videos) */
.ft-integration-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: 56px;
  margin-top: 56px;
}

.ft-integration-row--reverse .ft-integration-media {
  order: 2;
}

.ft-integration-media {
  width: 100%;
}

.ft-integration-content .tag {
  margin-bottom: 16px;
  display: inline-block;
  width: auto;
  align-self: flex-start;
}

.ft-integration-heading {
  font-size: var(--m-h2-size);
  font-weight: 600;
  color: var(--header);
  line-height: 1.25;
  margin: 0 0 16px;
}

.ft-integration-body {
  font-size: 18px;
  color: var(--paragraphs-light);
  line-height: 1.6;
  margin: 0;
}

@media (max-width: 767px) {
  .ft-integration-row,
  .ft-integration-row--reverse {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .ft-integration-row--reverse .ft-integration-media {
    order: 0;
  }
}

/* Teams page: workflow steps (Section E) */
.ft-workflow {
  margin-top: 40px;
  display: flex;
  flex-direction: column;
}

.ft-workflow-step {
  display: flex;
  gap: 20px;
  padding: 0 0 28px;
  align-items: flex-start;
  position: relative;
}

.ft-workflow-step:last-child {
  padding-bottom: 0;
}

.ft-workflow-num {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--m-accent) 10%, transparent);
  border: 1px solid var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--header);
  flex-shrink: 0;
  margin-top: -4px;
  position: relative;
  z-index: 1;
}

.ft-workflow-step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 36px;
  bottom: 8px;
  left: 18px;
  transform: translateX(-50%);
  width: 1px;
  background: var(--primary);
}

.ft-workflow-content {
  flex: 1;
}

.ft-workflow-content strong {
  display: block;
  font-size: 16px;
  font-weight: 600;
  color: var(--header);
  margin-bottom: 4px;
}

.ft-workflow-content span {
  font-size: 15px;
  color: var(--paragraphs-light);
  line-height: 1.55;
}

/* Workflow chain label */
.ft-workflow-chain {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 32px;
  padding: 20px 24px;
  background: color-mix(in srgb, var(--m-accent) 8%, transparent);
  border: 1px solid var(--bwd-border);
  border-radius: var(--m-radius-lg);
}

.ft-chain-item {
  font-size: 13px;
  font-weight: 600;
  color: var(--header);
  background: color-mix(in srgb, var(--m-accent) 10%, transparent);
  border: 1px solid rgba(255, 255, 255, 0.08);
  padding: 4px 12px;
  border-radius: var(--m-radius-full);
  white-space: nowrap;
}

.ft-chain-arrow {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* Teams page: CTA section (Section G) */
.ft-cta-section .content-container {
  width: 100%;
}

.ft-cta-block {
  width: 100%;
  align-self: stretch;
  box-sizing: border-box;
  text-align: center;
  padding: 72px 48px;
  /* De-baked (WS-2): the slider-background.png purple art + dark scrim are
     replaced by token-driven gradients so the block can follow any theme. */
  background-image: radial-gradient(
      70% 80% at 50% 100%,
      var(--m-glow) 0%,
      transparent 70%
    ),
    linear-gradient(
      color-mix(in srgb, var(--m-bg) 60%, transparent),
      color-mix(in srgb, var(--m-bg) 60%, transparent)
    );
  background-position: 50% 100%, 0 0;
  background-repeat: no-repeat, repeat;
  background-size: cover, auto;
  border-radius: var(--m-radius-lg);
  border: 1px solid var(--m-hairline);
  box-shadow: none;
  margin-top: 16px;
}

.ft-cta-block .tag {
  display: inline-block;
  margin-bottom: 16px;
}

.ft-cta-note {
  margin-top: 20px;
  font-size: 14px;
  color: var(--paragraphs-light);
  opacity: 0.6;
  max-width: 480px;
  margin-left: auto;
  margin-right: auto;
  line-height: 1.5;
}

.ft-cta-buttons {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 28px;
}

/* Layout only — the look comes from the canonical .m-btn-secondary group
   (WS-1a block), which .ft-btn-secondary is aliased into. */
.ft-btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  font-size: 16px;
  padding: 12px 32px;
}

@media (max-width: 767px) {
  .ft-role-grid {
    grid-template-columns: 1fr;
  }

  .ft-compare-grid {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .ft-compare-card--without {
    border-right: none;
    padding-right: 0;
  }

  .ft-problem-layout {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .ft-problem-block {
    padding: 28px 20px;
  }
}

/* ============================================
   Events page — minimal "date leaf" list
   ============================================ */

[data-route="/events"] .events-section {
  padding: 24px 24px 140px;
}

/* Matches .bwd-gallery-section so /events and /built-with-devswarm share
   the same content width (both resolve through --m-content-width). */
[data-route="/events"] .events-list {
  max-width: var(--m-content-width);
  margin: 0 auto;
}

[data-route="/events"] .events-label {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 88px 0 8px;
  font-family: var(--code-font);
  font-size: var(--m-eyebrow-size);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--paragraphs-light);
}

[data-route="/events"] .events-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--white-transparent);
}

[data-route="/events"] .events-label-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent-green);
  animation: events-pulse 2.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
}

@keyframes events-pulse {
  0% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--m-success) 55%, transparent);
  }
  70% {
    box-shadow: 0 0 0 10px color-mix(in srgb, var(--m-success) 0%, transparent);
  }
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--m-success) 0%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-route="/events"] .events-label-dot {
    animation: none;
  }
}

[data-route="/events"] .event-row {
  display: grid;
  grid-template-columns: 96px 1fr 340px;
  gap: 44px;
  align-items: center;
  padding: 44px 0;
  border-bottom: 1px solid var(--white-transparent);
}

/* Every row keeps the same layout (date on the left, image on the right) so
   the dates stay aligned in a single column and the list is easy to skim. */
[data-route="/events"] .event-row:last-child {
  border-bottom: none;
}

[data-route="/events"] .event-date {
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: center;
  gap: 4px;
  font-family: var(--code-font);
}

[data-route="/events"] .event-date-month {
  font-size: var(--m-eyebrow-size);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--m-accent-text);
}

[data-route="/events"] .event-date-day {
  font-size: var(--m-h2-size);
  font-weight: 600;
  line-height: 1.1;
  color: var(--white);
}

[data-route="/events"] .event-date-year {
  font-size: 15px;
  font-weight: 600;
  color: var(--paragraphs-light);
}

[data-route="/events"] .event-body {
  align-self: center;
}

[data-route="/events"] .event-location {
  margin: 0 0 8px;
  font-family: var(--code-font);
  font-size: var(--m-eyebrow-size);
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--m-accent-text);
}

[data-route="/events"] .event-title {
  margin: 0 0 10px;
  font-size: 26px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--white);
}

[data-route="/events"] .event-desc {
  margin: 0;
  font-size: 16px;
  line-height: 1.65;
  color: var(--paragraphs-light);
}

/* Square image card. */
[data-route="/events"] .event-media {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
}

[data-route="/events"] .event-media img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--m-radius-lg);
  border: 1px solid var(--white-transparent);
  transition: transform 0.4s ease, border-color 0.4s ease;
}

[data-route="/events"] .event-row:hover .event-media img {
  border-color: var(--m-hairline-strong);
}

[data-route="/events"] .event-row.is-past .event-date-month {
  color: var(--paragraphs-light);
}

[data-route="/events"] .event-row.is-past .event-date-day {
  color: var(--paragraphs-light);
}

[data-route="/events"] .event-row.is-past .event-location {
  color: var(--paragraphs-light);
}

/* Register + Load more render as canonical .m-btn-secondary (set in the
   page's JS); only per-page layout lives here. */
[data-route="/events"] .event-link {
  margin-top: 18px;
}

[data-route="/events"] .events-load-more-wrap {
  display: flex;
  justify-content: center;
  padding-top: 40px;
}

@media screen and (max-width: 767px) {
  [data-route="/events"] .event-row {
    grid-template-columns: 1fr;
    gap: 18px;
    padding: 36px 0;
  }

  /* Stacked layout: back to a fixed-ratio image above the text. */
  [data-route="/events"] .event-media {
    order: -1;
    min-height: 0;
  }

  [data-route="/events"] .event-media img {
    position: static;
    height: auto;
    aspect-ratio: 16 / 10;
  }

  [data-route="/events"] .event-date {
    flex-direction: row;
    align-items: baseline;
    gap: 10px;
  }

  [data-route="/events"] .event-date-day {
    font-size: var(--m-h3-size);
  }

  [data-route="/events"] .events-label {
    margin-top: 56px;
  }
}

/* ============================================
   Feed loading skeletons (events + projects)
   ============================================ */

.ds-skeleton {
  position: relative;
  display: block;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--m-radius-sm);
}

.ds-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.09),
    transparent
  );
  animation: ds-skeleton-shimmer 1.5s ease-in-out infinite;
}

@keyframes ds-skeleton-shimmer {
  100% {
    transform: translateX(100%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ds-skeleton::after {
    animation: none;
  }
}

/* Events page skeleton mirrors the event-row grid */
[data-route="/events"] .event-skeleton-label {
  width: 160px;
  height: 14px;
  margin: 88px 0 8px;
}

[data-route="/events"] .event-skeleton-row {
  display: grid;
  grid-template-columns: 96px 1fr 340px;
  gap: 44px;
  align-items: center;
  padding: 44px 0;
  border-bottom: 1px solid var(--white-transparent);
}

[data-route="/events"] .event-skeleton-row:last-child {
  border-bottom: none;
}

[data-route="/events"] .event-skeleton-date {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

[data-route="/events"] .event-skeleton-date-month {
  width: 44px;
  height: 14px;
}

[data-route="/events"] .event-skeleton-date-day {
  width: 56px;
  height: 40px;
}

[data-route="/events"] .event-skeleton-date-year {
  width: 38px;
  height: 12px;
}

[data-route="/events"] .event-skeleton-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

[data-route="/events"] .event-skeleton-location {
  width: 140px;
  height: 12px;
}

[data-route="/events"] .event-skeleton-title {
  width: 60%;
  height: 24px;
}

[data-route="/events"] .event-skeleton-line {
  width: 90%;
  height: 14px;
}

[data-route="/events"] .event-skeleton-line.is-short {
  width: 55%;
}

[data-route="/events"] .event-skeleton-media {
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: var(--m-radius-lg);
}

@media screen and (max-width: 767px) {
  [data-route="/events"] .event-skeleton-row {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  [data-route="/events"] .event-skeleton-date {
    flex-direction: row;
    align-items: center;
  }
}

/* Events page empty / error notice */
[data-route="/events"] .events-notice {
  max-width: 560px;
  margin: 96px auto 64px;
  text-align: center;
}

[data-route="/events"] .events-notice-title {
  margin: 0 0 10px;
  font-size: 24px;
  font-weight: 600;
  color: var(--white);
}

[data-route="/events"] .events-notice-detail {
  margin: 0;
  font-size: 16px;
  line-height: 160%;
  color: var(--paragraphs-light);
}

/* Built with DevSwarm: gallery skeleton tile shapes live in the page's own
   style block (built-with-devswarm.astro); only the shared .ds-skeleton
   shimmer base is defined here. */

/* ============================================================
   Type ramp + eyebrow/mono voice — Phase 1 · WS-1c
   The single site-wide scale (Part III §3), replacing the five
   prior scales (Webflow hero + heading-2a, case-study's own,
   blog rem-soup, page-local ft- and events). Sizes + line-heights
   are tokens; the .m-* role classes AND the existing Webflow
   heading/body/eyebrow classes both resolve through them.
   TYPE PROPERTIES ONLY (font-size/line-height/weight/letter-
   spacing/text-transform) — color stays with WS-1d. Weight (600)
   and headline tracking (−0.02em) for headings are set in the
   FONT TUNING block just below and still layer on top of this.
   Public API (consumed downstream — do not rename without the doc):
   role classes .m-display/.m-h2/.m-h3/.m-lead/.m-body/.m-small/
   .m-eyebrow/.m-mono-ui and the --m-<role>-size / --m-<role>-lh
   tokens.
   ============================================================ */
:root {
  /* role — size / line-height (Part III §3 ramp) */
  --m-display-size: 56px;
  --m-display-lh: 1.08;
  --m-display-size-mobile: 36px;
  --m-h2-size: 36px;
  --m-h2-lh: 1.15;
  --m-h3-size: 24px;
  --m-h3-lh: 1.25;
  --m-lead-size: 19px;
  --m-lead-lh: 1.55;
  --m-body-size: 16px;
  --m-body-lh: 1.6;
  --m-small-size: 14px;
  --m-small-lh: 1.5;
  --m-eyebrow-size: 12px;
  --m-eyebrow-lh: 1.2;
  --m-eyebrow-tracking: 0.08em;
  --m-mono-ui-size: 13px;
  --m-mono-ui-lh: 1.4;
}

/* ── Role utility classes — the ramp as an explicit vocabulary ── */
.m-display {
  font-size: var(--m-display-size);
  line-height: var(--m-display-lh);
  font-weight: 600;
  letter-spacing: -0.02em;
}
.m-h2 {
  font-size: var(--m-h2-size);
  line-height: var(--m-h2-lh);
  font-weight: 600;
  letter-spacing: -0.02em;
}
.m-h3 {
  font-size: var(--m-h3-size);
  line-height: var(--m-h3-lh);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.m-lead {
  font-size: var(--m-lead-size);
  line-height: var(--m-lead-lh);
  font-weight: 400;
}
.m-body {
  font-size: var(--m-body-size);
  line-height: var(--m-body-lh);
  font-weight: 400;
}
.m-small {
  font-size: var(--m-small-size);
  line-height: var(--m-small-lh);
  font-weight: 400;
}
.m-eyebrow {
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  line-height: var(--m-eyebrow-lh);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
}
.m-mono-ui {
  font-family: var(--m-font-mono);
  font-size: var(--m-mono-ui-size);
  line-height: var(--m-mono-ui-lh);
  font-weight: 400;
}

/* ── Webflow heading/body classes mapped onto the ramp. This file
      loads after devswarm-webflow.css, so equal-specificity rules
      win here — collapsing Webflow's per-breakpoint size steps into
      the one scale. Compound variants are listed so their higher
      specificity doesn't out-rank the ramp. ── */

/* display — the one hero headline per page */
.heading-hero,
.heading-hero.large,
.heading-hero.homepage,
.heading-hero.features,
.heading-hero.mobile {
  font-size: var(--m-display-size);
  line-height: var(--m-display-lh);
}

@media screen and (max-width: 767px) {
  .heading-hero,
  .heading-hero.large,
  .heading-hero.homepage,
  .heading-hero.features,
  .heading-hero.mobile {
    font-size: var(--m-display-size-mobile);
  }
}

/* h2 — section headings */
.heading-2,
.heading-2a,
.heading-banner,
.heading-banner.banner-card {
  font-size: var(--m-h2-size);
  line-height: var(--m-h2-lh);
}

/* h3 — card / sub-section headings */
.heading-3,
.heading-4 {
  font-size: var(--m-h3-size);
  line-height: var(--m-h3-lh);
}

/* lead — hero subheading + section intros */
.subheading-hero,
.text-body-large {
  font-size: var(--m-lead-size);
  line-height: var(--m-lead-lh);
  font-weight: 400;
}

/* body — default copy */
.paragraph,
.text-body-medium,
.text-card-body {
  font-size: var(--m-body-size);
  line-height: var(--m-body-lh);
}

/* small — metadata, captions, footnotes */
.text-body-small {
  font-size: var(--m-small-size);
  line-height: var(--m-small-lh);
}

/* eyebrow — the ONE uppercase-mono eyebrow. `.tag` is Webflow's
   eyebrow (36 uses, always above a heading); it converges here with
   the custom eyebrows consolidated further below. */
.tag {
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  line-height: var(--m-eyebrow-lh);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
}

/* ── Eyebrow consolidation (WS-1c). The custom uppercase-mono eyebrows
      across the site (blog, hive badge, teams) converge on
      the ONE eyebrow token — mono, 12px, weight 500, tracking 0.08em,
      uppercase. Retires the 0.12/0.13/0.14em tracking spread and the
      700-weight / sans-serif variants. Together with `.tag` above and the
      events + case-study + bwd page-local eyebrows (normalized in their
      own files), this is the single eyebrow. TYPE PROPERTIES ONLY — color,
      background, padding and the tag-dot art stay with WS-1d / WS-1e. ── */
.blog-hero-eyebrow,
.blog-section-title,
.blog-share-label,
.blog-toc-title,
.bwd-hive-featured-badge,
.ft-role-label,
.ft-compare-label {
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  line-height: var(--m-eyebrow-lh);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
}

/* Events page eyebrows (route-scoped, so matched at equal specificity).
   Retires the 0.25em / 0.3em / 0.14em tracking extremes down to 0.08em. */
[data-route="/events"] .events-label,
[data-route="/events"] .event-location,
[data-route="/events"] .event-date-month {
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  line-height: var(--m-eyebrow-lh);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
}

/* ── Page-local heading normalization onto the ramp (WS-1c). Teams and
      events headings snap to the ramp tokens and 700/800 weights retire
      to 600. TYPE PROPERTIES ONLY (color stays with each owner).
      The blog is deliberately NOT remapped: it is a bespoke editorial
      surface (long-form reading, ~68ch measure) and keeps its own smaller
      heading scale defined in the blog block above — the ramp's display/h2
      steps read as oversized there. Blog eyebrows/labels still join the
      one-eyebrow group above. ── */

/* teams (devswarm-for-teams) headings: snap out-of-ramp 28px → h3, retire
   700/800 weights to 600. Smaller card headings keep their size, weight 600. */
.ft-compare-heading,
.ft-integration-heading {
  font-size: var(--m-h3-size);
  line-height: var(--m-h3-lh);
  font-weight: 600;
}
.ft-compare-card-heading,
.ft-role-heading,
.ft-comp-cell.ft-comp-header,
.ft-comp-cell.ft-comp-ds.ft-comp-header {
  font-weight: 600;
}

/* events title → h3 */
[data-route="/events"] .event-title {
  font-size: var(--m-h3-size);
  line-height: var(--m-h3-lh);
  font-weight: 600;
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ WS-1a — THE ONE BUTTON + ONE LINK RECIPE (Phase 1, Part III §5/§6).   ║
   ║ Public API the fleet consumes:                                        ║
   ║   .m-btn-primary   — filled accent, white text, 500, radius-md        ║
   ║   .m-btn-secondary — transparent, hairline-strong border, text-1      ║
   ║   .m-link          — accent-text link, 500, underline on hover        ║
   ║   .m-link-arrow    — .m-link + a → suffix that slides 3px on hover     ║
   ║ bwd-btn-primary is MIGRATED (its call-sites now use .m-btn-*).         ║
   ║ FILL POLICY (2026-07-23): the filled primary is reserved for Download  ║
   ║ CTAs — buttons linking to /download/ or a direct installer, plus the   ║
   ║ nav Download. So .download-button and .nav-button route through the    ║
   ║ primary group; .button and the form submits (.submit-button,           ║
   ║ .submit-button-2, footer newsletter) route through SECONDARY. The      ║
   ║ legacy Webflow classes keep their own width/centering/icon layout —    ║
   ║ only color+shape+hover come from the group (Part V fallback rule).     ║
   ║ Retired here: the dual inset ink-smear hover,                          ║
   ║ the scale(1.1) hover, the re-pill-on-hover, the white/inverted        ║
   ║ .button.for-dark, the green .button/.download-button variant, and     ║
   ║ (2026-07-23) the pill-radius Download exception — every button now    ║
   ║ renders --m-radius-md, no exceptions. The /events mono buttons        ║
   ║ (.event-link, .events-load-more) carry .m-btn-secondary in markup.    ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* Canonical buttons are self-contained so a bare <a>/<button> renders right;
   the routed legacy classes below keep their own Webflow layout and only get
   the color/shape treatment. This layout rule is scoped to .m-btn-* only. */
.m-btn-primary,
.m-btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0.72em 1.5em;
  font-family: inherit;
  font-size: 1em;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
  cursor: pointer;
}

/* ── PRIMARY: the one filled CTA — reserved for Download actions only
      (buttons that link to /download/ or a direct installer, plus the nav
      Download). Only color/shape/weight/shadow are set so each host keeps
      its own padding/size/layout. ── */
.m-btn-primary,
.download-button,
.download-button.green,
.download-button.mobile,
.nav-button {
  background-color: var(--m-accent);
  background-image: none;
  color: #fff;
  -webkit-text-fill-color: #fff;
  border: 1px solid var(--m-accent);
  border-radius: var(--m-radius-md);
  font-weight: 500;
  box-shadow: none;
  transition: background-color 0.2s ease, border-color 0.2s ease,
    box-shadow 0.2s ease;
}

/* Restrained hover: brighten to accent-hover + one soft glow. No ink-smear,
   no scale, no re-pill. Compound selectors match Webflow's specificity so
   source order wins. */
.m-btn-primary:hover,
.download-button:hover,
.download-button.green:hover,
.download-button.mobile:hover,
.nav-button:hover {
  background-color: var(--m-accent-hover);
  background-image: none;
  border-color: var(--m-accent-hover);
  border-radius: var(--m-radius-md);
  color: #fff;
  box-shadow: 0 4px 24px var(--m-glow);
  transform: none;
}

/* ── SECONDARY: transparent, hairline-strong border, text-1. Hover brightens
      the border toward a light neutral. Every non-Download button routes
      here — .button (404, company), form submits, the footer newsletter. ── */
.m-btn-secondary,
.button,
.button.green,
.button.hero,
.button.for-dark,
.button.secondary,
.download-button.secondary,
.button-small.secondary,
.submit-button,
.submit-button.footer,
.submit-button-2,
.ft-btn-secondary {
  background-color: transparent;
  background-image: none;
  color: var(--m-text-1);
  border: 1px solid var(--m-hairline-strong);
  border-radius: var(--m-radius-md);
  font-weight: 500;
  box-shadow: none;
  transition: background-color 0.2s ease, border-color 0.2s ease,
    color 0.2s ease;
}

.m-btn-secondary:hover,
.button:hover,
.button.green:hover,
.button.hero:hover,
.button.for-dark:hover,
.button.secondary:hover,
.download-button.secondary:hover,
.button-small.secondary:hover,
.submit-button:hover,
.submit-button.footer:hover,
.submit-button-2:hover,
.ft-btn-secondary:hover {
  background-color: transparent;
  background-image: none;
  border-color: var(--m-text-2);
  color: var(--m-text-1);
  box-shadow: none;
  transform: none;
}

/* Focus ring — 2px accent, everywhere (Part III §5). Global, not an
   allowlist: every keyboard-focusable element (links, buttons, form
   fields, summary, tabs) gets the same ring. :focus-visible keeps it
   off mouse clicks. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--m-accent);
  outline-offset: 2px;
}

/* ============================================================
   Radius normalization — Webflow ships dozens of ad-hoc radii
   (.62em, 28px, 40px, 48px, 50em, 1.2em …) on furniture the
   canonical specs above don't name. These are the classes that
   still render on live pages; pin each to its role on the scale
   so corners match site-wide. Compound selectors mirror
   Webflow's specificity so source order wins. See the :root
   legend for the tier→role map.
   ============================================================ */

/* CONTROLS → md — every remaining button/submit/menu/tab control.
   (.submit-button.footer was the 28px pill next to the 10px email
   field; both now read as one control.) */
.button-small,
.submit-button,
.submit-button.full-width,
.submit-button.footer,
.submit-button-2,
.submit-button-2.reminder,
.menu-button,
.nav-menu,
.tabs-menu.style-rounded {
  border-radius: var(--m-radius-md);
}

/* Form status notices → md */
.success-message,
.error-message,
.error-message-2,
.text-field-2 {
  border-radius: var(--m-radius-md);
}

/* BLOCKS → lg — generic card / banner / section wrappers */
.wrapper-bg,
.wrapper-bg.green,
.block-banner,
.block-banner.pricing,
.results-banner {
  border-radius: var(--m-radius-lg);
}

/* ── THE ONE TEXT-LINK RECIPE. .m-link is canonical; the two accent-text
      link treatments in the export (.link-purple = var(--m-accent) fill-as-text,
      3.19:1; .link-19 = accent-blue) are routed onto it — both were the
      "Learn More" contrast failures. Generic .link, footer/nav links and the
      blog-prose links are owned by sibling packages and left untouched. ── */
.m-link,
.m-link-arrow,
.link-purple,
.link-19 {
  color: var(--m-accent-text);
  font-weight: 500;
  text-decoration: none;
  transition: color 0.15s ease;
}

.m-link:hover,
.m-link-arrow:hover,
.link-purple:hover,
.link-19:hover {
  color: var(--m-accent-hover);
  text-decoration: underline;
}

/* Opt-in arrow suffix (a single → glyph that translates 3px on hover). Kept
   opt-in so it never lands on mid-sentence inline links. */
.m-link-arrow::after {
  content: "→";
  display: inline-block;
  margin-left: 0.35em;
  text-decoration: none;
  transition: transform 0.15s ease;
}

.m-link-arrow:hover::after {
  transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
  .m-link-arrow::after {
    transition: none;
  }
}

/* ── PRICING de-green + one-primary triage (Part III §6). Scoped to /pricing
      so the shape both out-specifies Webflow's .green rules and never reaches
      the /student Pro card. Tier distinction now comes from position + the
      "Most popular" eyebrow + the single filled button, not a green fill /
      border / glow. Pro's button stays primary (via the group above); Free /
      Team buttons carry .secondary, and the two banner CTAs are secondary. ── */
/* The .pricing-card-pro.green skin (de-greened surface + hairline border +
   accent-tint wash + elevation shadow) is consolidated into the one WS-1e card
   rule further down this file (search "pricing-card-pro.green"). */

[data-route="/pricing"] .tag.green {
  border-color: transparent;
  background-image: none;
  background-color: var(--m-accent-tint);
  color: var(--m-accent-text);
}

[data-route="/pricing"] .heading-3.color-green {
  color: var(--m-text-1);
}

[data-route="/pricing"] .pricing-features.color-green {
  border-top-color: var(--m-hairline);
  border-left-color: var(--m-hairline);
}

/* Unify the tier checkmarks onto a token: the blue (check-b) and green
   (check-g) baked-gradient SVGs are replaced by a mask tinted with
   --m-accent-text, so the check color is theming-ready CSS, not image pixels.
   Rendered as a <span class="icon-list pricing-check"> by PricingCard.astro. */
.pricing-check {
  display: inline-block;
  background-color: var(--m-accent-text);
  -webkit-mask: url(/images/684228174606b26ec8e3e3fa_check-b.svg) center /
    contain no-repeat;
  mask: url(/images/684228174606b26ec8e3e3fa_check-b.svg) center / contain
    no-repeat;
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ FONT TUNING — Phase 0 commit 3, the revert parachute.                ║
   ║ EVERYTHING that tunes the system-font swap lives in this one fenced   ║
   ║ block (plus the --m-font value in :root and the <link> in Head.astro).║
   ║ To undo the font swap: `git revert` this commit. Do not scatter       ║
   ║ font rules elsewhere — keep this block the single home so the revert  ║
   ║ stays clean.                                                          ║
   ║                                                                       ║
   ║ Why: the site adopts the product's own OS-native UI stack (--m-font,  ║
   ║ mirrors apps/desktop/ui/src/styles.css). System faces at 600 read     ║
   ║ like Nunito at 700, so the Webflow 700 headings/bold remap to 600.    ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* Weight remap 700/800 → 600 on the site-wide heading system + bold text.
   (Component/blog/bwd weights are Phase 1.) */
h1,
h2,
h3,
h4,
h5,
h6,
strong,
b,
label,
.heading-hero,
.heading-1,
.heading-2,
.heading-2a,
.heading-3,
.heading-4,
.heading-banner,
.display {
  font-weight: 600;
}

/* Headline tracking: −0.02em (Part III §3, −0.01…−0.02em range). */
h1,
h2,
h3,
h4,
h5,
h6,
.heading-hero,
.heading-1,
.heading-2,
.heading-2a,
.heading-3,
.heading-banner,
.display {
  letter-spacing: -0.02em;
}

/* Body line-height eases from Nunito's 1.7 to ~1.6 for the system stack. */
body {
  line-height: 1.6;
}
/* ╚═════════════════════ end FONT TUNING block ═════════════════════════╝ */

/* ============================================================
   Phase 1 — WS-1b: flat nav + footer tokens + hive-news strip
   Overrides the read-only Webflow export (devswarm-webflow.css);
   this file loads after it, so these win. Tokens only.
   ============================================================ */

/* --- Flat nav bar --------------------------------------------- */
/* Retire the floating blur-pill (.nav-block: border-radius 50em +
   #000c bg inside a transparent .navbar). The bar now reads as the
   page's top edge (Zed/Conductor): full page width, --m-bg at ~92%
   opacity + blur, a single --m-hairline bottom seam. The content
   row (.nav-block) keeps its 74em max-width and centers; only its
   chrome is stripped. */
.navbar {
  padding: 0;
  background-color: color-mix(in srgb, var(--m-bg) 92%, transparent);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--m-hairline);
}

.nav-block {
  background-color: transparent;
  border: 0;
  border-radius: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  padding: 0.7em 1.8em;
}

/* --- Footer: link hover + heading ladder ---------------------- */
/* Column links rest at --m-text-1 (via the Webflow --text alias) and
   light to the readable accent-text on hover — the export's hover
   only went text-1 → text-1 (no visible change). The uppercase group
   labels drop to --m-text-3 so the footer reads as a muted eyebrow /
   link / body ladder. */
.section-footer .link-footer:hover {
  color: var(--m-accent-text);
}

.section-footer .subtitle-footer {
  color: var(--m-text-3);
}

/* --- Homepage announcement pill (above the hero headline) ----- */
/* A compact, centered, fully-clickable pill that sits INSIDE the hero,
   directly above the headline — no separate band section. The whole pill
   is the link (arrow affordance); purple stays as jewelry (badge + arrow).
   The hero's own top padding clears the fixed nav. */
.home-announce {
  display: inline-flex;
  align-items: center;
  align-self: center;
  gap: 10px;
  /* vw-relative (not 100%): the pill lives inside the hero's flex column, so a
     %-based cap is circular and its nowrap text widens the container past the
     viewport on mobile. Cap against the viewport instead. */
  max-width: min(900px, calc(100vw - 48px));
  margin-bottom: 28px;
  padding: 5px 14px 5px 5px;
  border-radius: var(--m-radius-full);
  background-color: var(--m-surface);
  border: 1px solid var(--m-hairline);
  color: var(--m-text-2);
  text-decoration: none;
  font-size: 0.9rem;
  line-height: 1.2;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.home-announce:hover {
  border-color: var(--m-hairline-strong);
  color: var(--m-text-1);
}

.home-announce-badge {
  flex-shrink: 0;
  padding: 3px 10px;
  border-radius: var(--m-radius-full);
  background-color: var(--m-accent-tint);
  color: var(--m-accent-text);
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  line-height: var(--m-eyebrow-lh);
  font-weight: 500;
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
}

.home-announce-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  font-weight: 500;
}

/* Inner scroller — when the title overflows the pill, JS adds .is-marquee and
   sets --marquee-shift/--marquee-duration so the full title scrolls into view
   and back (paused at each end). Non-overflowing titles never animate; the
   ellipsis above is the prefers-reduced-motion fallback. */
.home-announce-scroll {
  display: inline-block;
  white-space: nowrap;
}

.home-announce-text.is-marquee {
  text-overflow: clip;
}

.home-announce-text.is-marquee .home-announce-scroll {
  animation: home-announce-marquee var(--marquee-duration, 8s) ease-in-out
    infinite alternate;
}

@keyframes home-announce-marquee {
  0%,
  14% {
    transform: translateX(0);
  }
  86%,
  100% {
    transform: translateX(var(--marquee-shift, 0));
  }
}

/* Pause the scroll while hovering so a reader can stop and finish the line. */
.home-announce:hover .home-announce-text.is-marquee .home-announce-scroll {
  animation-play-state: paused;
}

.home-announce-arrow {
  flex-shrink: 0;
  color: var(--m-accent-text);
  transition: transform 0.15s ease;
}

.home-announce:hover .home-announce-arrow {
  transform: translateX(3px);
}

/* --- Hero CTA: standard filled-accent primary, lifted by a resting
   glow. The hex-grid field behind it is near-black, so the accent
   fill reads clearly; the glow (the page's one hero glow moment)
   separates it from the field's own purple strokes. The former
   white/inverted hero variant is retired (2026-07-23). Scoped so the
   shared-id CTA in the testimonials block keeps the base spec. */
.section-hero.homepage .download-button {
  box-shadow: 0 4px 24px var(--m-glow);
}

.section-hero.homepage .download-button:hover {
  box-shadow: 0 6px 32px var(--m-glow);
}

/* --- Hero CTA group: Download (primary, white) + Learn how it works
   (secondary, outlined) side by side, wrapping to a column on narrow
   viewports. ------------------------------------------------------- */
.section-hero.homepage .hero-cta-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

/* The Webflow base pins .download-button to width:304px + margin:auto; inside
   the flex group let both CTAs size to their content (padding owns the side
   spacing — no min-width, so short labels don't collect slack around the
   icon) and let the gap own the spacing between the two CTAs. */
.section-hero.homepage .hero-cta-group .download-button {
  width: auto;
  min-width: 0;
  margin-left: 0;
  margin-right: 0;
  padding-left: 1.4em;
  padding-right: 1.4em;
}

/* On mobile the Webflow base pins the primary (.download-button.mobile) to
   height:32px while the secondary keeps 48px/40px — so the two hero CTAs read
   at mismatched heights. Force both to one height + padding in the hero group,
   overriding the .mobile shrink so they stay visually paired. */
@media screen and (max-width: 767px) {
  /* Center the stacked CTAs to match the centered headline/subheading. */
  .section-hero.homepage .hero-cta-group {
    justify-content: center;
  }

  .section-hero.homepage .hero-cta-group .download-button,
  .section-hero.homepage .hero-cta-group .download-button.mobile {
    height: 44px;
    padding-top: 0;
    padding-bottom: 0;
  }
}

/* OS-detected download icon (Apple / Windows) inside the hero primary.
   Painted as a white CSS mask (the PNG's alpha shape) so the glyph matches
   the white label on the accent fill. JS reveals it and sets the mask URL
   (--os-icon-url) once the platform is known; sits inline before the label.
   No margin of its own — the button's flex gap owns the icon↔label spacing. */
.section-hero.homepage .download-button .hero-dl-icon {
  display: inline-block;
  width: 18px;
  height: 18px;
  flex: none;
  background-color: #fff;
  -webkit-mask: var(--os-icon-url) center / contain no-repeat;
  mask: var(--os-icon-url) center / contain no-repeat;
}

/* The hero glow override above also matches .download-button.secondary
   (higher specificity than the base secondary rule), so re-assert the
   canonical secondary spec for the hero's secondary CTA on the dark field. */
.section-hero.homepage .download-button.hero-secondary-cta,
.section-hero.homepage .download-button.hero-secondary-cta:hover {
  background-color: transparent;
  background-image: none;
  border: 1px solid var(--m-hairline-strong);
  border-radius: var(--m-radius-md);
  box-shadow: none;
  transition: border-color 0.2s ease;
}

.section-hero.homepage .download-button.hero-secondary-cta:hover {
  border-color: var(--m-text-2);
}

.section-hero.homepage .download-button.hero-secondary-cta .text-block-5 {
  color: var(--m-text-1);
  -webkit-text-fill-color: var(--m-text-1);
}

/* --- Hero purple glow under the embedded demo ----------------- */
/* Restore the "gradient to purple" pooling under the hero demo, tuned
   to cohere with the desaturated field: a soft radial from --m-glow
   (accent at low alpha) up into the dark bg, NOT the old s72% var(--m-surface)
   band. Drawn as a ::before so the hex-grid JS's `background-image:
   none !important` (on .hex-grid-active) can't wipe it; sits at z-0
   behind the hex canvas (hex pattern overlays the glow) and behind the
   z-1 content. */
.section-hero.homepage::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 130%;
  height: 78%;
  background: radial-gradient(
    ellipse 55% 60% at 50% 72%,
    var(--m-glow) 0%,
    transparent 66%
  );
  pointer-events: none;
  z-index: 0;
}

/* --- Feature-section ambience: FULL-WIDTH purple field + specular ---- */
/* The purple ambient must read edge-to-edge. Putting the specular glows on the
   ~940px .features-block boxes made the BRIGHT purple stop at the content column
   (the subtle field behind didn't register) — so it read as "cut off". Fix: the
   whole ambient — swell + specular — lives on the FULL-WIDTH SECTION as large
   soft radials that reach past the content to the screen edges, so nothing has a
   hard purple boundary. The glows alternate sides down the section; no per-block
   backgrounds (those were the confined layer). Homepage keeps the export's top
   black fade (blends into the black video section above); /features has none
   (its hero sits directly above). */
/* The one per-page feature-section field (marketing.md Amendment 1). The
   bg-transition rule below carries the top black fade (blends into the black
   video section above it). /features gets its own tuned field (search
   [data-route="/features"] .section-main below); home-features overrides this
   with a whispered version (search .home-features.bg-transition). The earlier
   no-black-fade duplicate of this rule was dead — this rule plus those two
   overrode it at equal specificity — and has been removed. */
.section-secondary.bg-transition {
  background-image: linear-gradient(180deg, #000 0%, transparent 9%),
    radial-gradient(70% 26% at 74% 16%, var(--m-glow) 0%, transparent 62%),
    radial-gradient(70% 26% at 26% 50%, var(--m-glow) 0%, transparent 62%),
    radial-gradient(70% 26% at 74% 84%, var(--m-glow) 0%, transparent 62%),
    linear-gradient(
      180deg,
      transparent 0%,
      color-mix(in srgb, var(--m-accent) 10%, transparent) 42%,
      color-mix(in srgb, var(--m-accent) 10%, transparent) 60%,
      transparent 100%
    );
  background-repeat: no-repeat;
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ WS-1e — THE ONE CARD SPEC + RADIUS NORMALIZATION (Phase 1, Part III   ║
   ║ §4/§5). One card recipe: --m-surface fill, 1px --m-hairline border,   ║
   ║ radius-lg, hover brightens the border to --m-hairline-strong. No      ║
   ║ purple borders, no glow. Highlighted cards (pricing Pro) earn         ║
   ║ emphasis from a restrained --m-accent-tint wash + a brighter          ║
   ║ hairline — never a saturated border or glow.                          ║
   ║ Radius drift (20px / 48px / 1.2em / 1em / .62em) is retired onto the  ║
   ║ scale: cards + media frames = radius-lg, inputs = radius-md. Only the ║
   ║ skin is unified here; each host keeps its own padding/grid/flex       ║
   ║ layout — mirrors the WS-1a Button approach. This file loads after the ║
   ║ read-only Webflow export, so equal-specificity rules win here.        ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* ── The one card skin: --m-surface fill, one hairline border. Kills the
      export's purple borders + purple-tinted gradients and the component
      rules' one-off rgba/hex borders above. ── */
.pricing-card,
.pricing-card-pro,
.guarantee-card,
.multiagent-card,
.agent-card,
.review,
.block-post,
.ft-role-card,
.ft-problem-block,
.value-block,
.reminder-card,
.pricing-banner-card {
  background-color: var(--m-surface);
  background-image: none;
  border: 1px solid var(--m-hairline);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/* Cards + the media frames alongside them all speak radius-lg. */
.pricing-card,
.pricing-card-pro,
.guarantee-card,
.multiagent-card,
.agent-card,
.review,
.block-post,
.ft-role-card,
.ft-problem-block,
.reminder-card,
.pricing-banner-card {
  border-radius: var(--m-radius-lg);
}

/* .value-block keeps its seated-tab bottom (it meets the row below), now
   expressed on the scale: lg top corners, sm bottom corners. */
.value-block {
  border-radius: var(--m-radius-lg) var(--m-radius-lg) var(--m-radius-sm)
    var(--m-radius-sm);
}

/* Hover: brighten the hairline. Link cards (.block-post, .blog-hero, the hive
   card) keep their own arrival/lift; static cards just gain a subtle edge. */
.pricing-card:hover,
.guarantee-card:hover,
.multiagent-card:hover,
.agent-card:hover,
.review:hover,
.block-post:hover,
.ft-role-card:hover,
.ft-problem-block:hover,
.value-block:hover,
.reminder-card:hover {
  border-color: var(--m-hairline-strong);
}

/* ── Highlighted "Pro" card: subtle emphasis only. WS-1a already de-greened
      the /pricing Pro card onto --m-surface + a neutral elevation shadow;
      here its border moves off the saturated accent to a brighter hairline,
      with a restrained accent-tint wash for warmth. No purple border, no
      glow. ── */
[data-route="/pricing"] .pricing-card-pro.green {
  border-color: var(--m-hairline-strong);
  background-color: var(--m-surface);
  background-image: linear-gradient(var(--m-accent-tint), var(--m-accent-tint));
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
  border-radius: var(--m-radius-lg);
}

/* ── Inputs onto the scale (radius-md). The footer email pill and the blog
      search pill keep their own higher-specificity pill radius. ── */
.text-field,
.textarea,
.select-field {
  border-radius: var(--m-radius-md);
}

/* ── The pricing "kickstart" banner (.options-banner): the outer band is
      retired entirely, same treatment as the home blog strip and changelog
      preview. The container is now an invisible layout row; the two inner
      cards wear the standard card skin (--m-surface + hairline, above)
      directly on the page background. ── */
.options-banner,
.options-banner.pricing {
  background-color: transparent;
  background-image: none;
  border: none;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
}
/* ╚═════════════════════ end WS-1e card + radius block ══════════════════╝ */

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ TYPOGRAPHY HIERARCHY — weight ladder + role weight caps               ║
   ║ Placed last so equal-specificity rules win on source order. Fixes:    ║
   ║ (1) the flat 600 ramp — the 56px display was the SAME weight as a 24px ║
   ║     card title, so it read heavy-but-weak. Weight now goes DOWN as     ║
   ║     size goes UP: display 500; section/card headings stay 600.         ║
   ║ (2) the effective 700 survivors that slipped past the 600-max remap,   ║
   ║     unified to each role's weight (labels 500, metrics/titles/strong   ║
   ║     600). 600 is now the true ceiling — no 700/800 renders.            ║
   ║ (3) feature "statement" headlines lifted off body weight so they read  ║
   ║     as headings, not paragraphs.                                       ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* (1) Display: lighter than the section headings below it — the 56px size
   commands, so the weight relaxes. Section h2 / card h3 stay 600 (the ramp). */
.heading-hero,
.heading-hero.large,
.heading-hero.homepage,
.heading-hero.features,
.heading-hero.mobile,
.m-display,
[data-route="/case-study"] .cs-hero h1 {
  font-weight: 500;
}

/* (2) Cap the effective-700 survivors at their role weight. Labels → 500. */
.sf-upload-label {
  font-weight: 500;
}

/* Body emphasis → 600 everywhere (was 700 in blog/ft/download, 600 in cs). */
.blog-prose strong,
.blog-prose th,
.ft-workflow-content strong,
[data-route="/download"] .dl-hero-subtitle strong,
[data-route="/pricing"] .dl-hero-subtitle strong {
  font-weight: 600;
}

/* Metrics, the stray card title, and decorative glyphs → 600. */
.ft-workflow-num,
[data-route="/events"] .event-date-year,
.teams-checklist-title,
.agent-card-icon-placeholder,
.bwd-hero-placeholder-letter {
  font-weight: 600;
}

/* (3) Feature-block "statement" headlines (h2.text-body-large) were 400 (body
   weight) and read as paragraphs — lift to 500 so they register as headings.
   Scoped to .features-block so the ~18 large-body <p class="text-body-large">
   paragraphs elsewhere stay at reading weight (400). */
.features-block .text-body-large {
  font-weight: 500;
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ NAV — "Product" dropdown (Features + Pricing)                         ║
   ║ Desktop: a floating panel on --m-surface with a hairline + radius-md, ║
   ║ opened on hover or click. Mobile (<=991px, the Webflow collapse       ║
   ║ breakpoint): the panel flattens to an inline indented list that is    ║
   ║ always visible, so the links stay reachable in the collapsed menu.    ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

.m-navdrop {
  position: relative;
  display: inline-flex;
}

/* The trigger is a <button> wearing .nav-link so it matches the sibling nav
   items; only reset the UA button chrome (font-size/weight come from .nav-link). */
.m-navdrop-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: inherit;
}

.m-navdrop-chevron {
  flex-shrink: 0;
  transition: transform 0.15s ease;
}

.m-navdrop.is-open .m-navdrop-chevron {
  transform: rotate(180deg);
}

.m-navdrop-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  min-width: 176px;
  display: flex;
  flex-direction: column;
  padding: 6px;
  background-color: var(--m-surface);
  border: 1px solid var(--m-hairline);
  border-radius: var(--m-radius-md);
  box-shadow: 0 14px 34px -10px rgba(0, 0, 0, 0.65);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateX(-50%) translateY(-4px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
  z-index: 60;
}

.m-navdrop.is-open .m-navdrop-panel {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}

/* Invisible hover bridge spanning the 6px gap between the trigger and the
   panel. Without it, moving the pointer down into the menu crosses empty
   space and fires mouseleave, snapping the panel shut before you arrive.
   It's only hittable while the panel is open (pointer-events inherit from
   the panel, which is `none` when closed), so it never opens the menu on its
   own. */
.m-navdrop-panel::before {
  content: "";
  position: absolute;
  top: -8px;
  left: 0;
  right: 0;
  height: 8px;
}

.m-navdrop-panel a {
  display: block;
  /* .nav-link's auto side-margins center the item and defeat the flex stretch,
     leaving a narrow centered box — reset them so each row fills the panel. */
  width: 100%;
  margin: 0;
  padding: 8px 12px;
  border-radius: var(--m-radius-sm);
  white-space: nowrap;
  /* .nav-link centers its text for the top bar; menu items read left. */
  text-align: left;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.m-navdrop-panel a:hover {
  background-color: var(--m-surface-2);
  color: var(--m-text-1);
}

@media screen and (max-width: 991px) {
  .m-navdrop {
    display: block;
    width: 100%;
  }

  .m-navdrop-trigger {
    width: 100%;
    justify-content: flex-start;
  }

  .m-navdrop-chevron {
    display: none;
  }

  .m-navdrop-panel {
    position: static;
    transform: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    min-width: 0;
    padding: 0 0 0 12px;
    background: none;
    border: 0;
    box-shadow: none;
  }

  /* No floating panel, no gap to bridge. */
  .m-navdrop-panel::before {
    display: none;
  }
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ /features — FEATURE BLOCKS: framed media, quiet field                 ║
   ║                                                                       ║
   ║ The old treatment let each screenshot float unframed on a bright      ║
   ║ purple radial, so the media had no edge and the purple read as the    ║
   ║ subject. Inverted here: the FIELD goes quiet and the MEDIA is         ║
   ║ contained — an outer well (hairline + radius-xl + padding) holding an ║
   ║ inner media card (hairline + radius-lg + a deep shadow that lifts it  ║
   ║ off the page). Asymmetric 58/42 split so the product image carries    ║
   ║ the section and the copy reads as its caption. Purple survives only   ║
   ║ as the bullet marker and the link — jewelry, per the token doc.       ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* ── The field: same ambient structure as before at ~40% strength, so the
      framed media out-contrasts the background instead of competing with it. ── */
[data-route="/features"] .section-main {
  background-image: radial-gradient(
      70% 26% at 74% 16%,
      color-mix(in srgb, var(--m-glow) 40%, transparent) 0%,
      transparent 62%
    ),
    radial-gradient(
      70% 26% at 26% 50%,
      color-mix(in srgb, var(--m-glow) 40%, transparent) 0%,
      transparent 62%
    ),
    radial-gradient(
      70% 26% at 74% 84%,
      color-mix(in srgb, var(--m-glow) 40%, transparent) 0%,
      transparent 62%
    ),
    linear-gradient(
      180deg,
      transparent 0%,
      color-mix(in srgb, var(--m-accent) 4%, transparent) 42%,
      color-mix(in srgb, var(--m-accent) 4%, transparent) 60%,
      transparent 100%
    );
}

[data-route="/features"] .features-container {
  grid-column-gap: 128px;
  grid-row-gap: 128px;
  margin-top: 72px;
}

/* Kill the export's per-block radial — that confined purple blob was the
   "cut off" edge. Ambience is the section's job now; the media card carries
   its own edge. Gaps match the homepage rows (.home-features). */
[data-route="/features"] .features-block {
  background-image: none;
  align-items: center;
  grid-column-gap: 72px;
  grid-row-gap: 40px;
}

/* ── Media column: no frame — the media card sits directly on the section.
   Width and glow match the homepage feature rows so both surfaces read the
   same. ── */
[data-route="/features"] .features-image-block {
  position: relative;
  width: 48%;
  isolation: isolate;
}

/* Soft glow bleeding out from behind the media (same recipe as
   .home-features). Default: leans right (rows where media is on the left). */
[data-route="/features"] .features-image-block::before {
  content: "";
  position: absolute;
  inset: -14%;
  z-index: -1;
  background: radial-gradient(
    46% 52% at 68% 42%,
    var(--m-glow) 0%,
    transparent 70%
  );
  opacity: 0.7;
  pointer-events: none;
}

/* Rows where the copy comes first in source order (media on the right):
   flip the glow to lean left so it always bleeds toward the page center. */
[data-route="/features"]
  .features-block
  .features-content:first-child
  ~ .features-image-block::before {
  background: radial-gradient(
    46% 52% at 32% 42%,
    var(--m-glow) 0%,
    transparent 70%
  );
}

/* ── The media card — same skin as the homepage rows ── */
[data-route="/features"] .features-image,
[data-route="/features"] .vimeo-features {
  display: block;
  width: 100%;
  border: 0;
  border-radius: var(--m-radius-lg);
  box-shadow: 0 26px 64px rgba(0, 0, 0, 0.6);
  overflow: hidden;
}

/* The export gave this one a saturated purple stroke; the media card already
   carries its own radius + shadow, so the stroke reads as noise — drop it. */
[data-route="/features"] .features-image.stroke-purple {
  border: 0;
}

/* ── Copy column: heading → lead → bullets, each step dimmer and smaller ── */
[data-route="/features"] .features-content {
  width: 52%;
  grid-row-gap: 16px;
}

[data-route="/features"] .features-block .heading-3 {
  margin: 0;
  font-size: var(--m-h3-size);
  letter-spacing: -0.015em;
}

/* On the homepage .text-body-large is a statement headline (500); here it is a
   supporting paragraph under an h3, so it drops back to reading weight. */
[data-route="/features"] .features-block .text-body-large {
  margin: 0;
  color: color-mix(in srgb, var(--m-text-1) 60%, var(--m-text-2));
  font-size: var(--m-lead-size);
  line-height: var(--m-lead-lh);
  font-weight: 400;
}

[data-route="/features"] .features-list-block {
  width: 100%;
  grid-row-gap: 10px;
  margin-top: 4px;
}

[data-route="/features"] .features-list-item {
  grid-column-gap: 12px;
}

/* Replaces the bullet-point PNG that used to sit in the markup. */
[data-route="/features"] .features-list-item::before {
  content: "";
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  margin-top: 0.6em;
  border-radius: var(--m-radius-full);
  background-color: var(--m-accent-text);
}

/* Bullets read at body size — same as the homepage rows' paragraph copy —
   so the copy column holds its own against the media instead of shrinking
   into caption text. */
[data-route="/features"] .features-list-item .text-body-medium {
  margin: 0;
  color: color-mix(in srgb, var(--m-text-1) 60%, var(--m-text-2));
  font-size: var(--m-body-size);
  line-height: var(--m-body-lh);
}

[data-route="/features"] .features-content .link-white {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  color: var(--m-accent-text);
  font-size: var(--m-body-size);
  font-weight: 500;
  text-decoration: none;
}

[data-route="/features"] .features-content .link-white::after {
  content: "→";
  transition: transform 0.15s ease;
}

[data-route="/features"] .features-content .link-white:hover {
  color: var(--m-accent-hover);
}

[data-route="/features"] .features-content .link-white:hover::after {
  transform: translateX(3px);
}

/* Stacked below the Webflow collapse breakpoint: media fills the narrow column. */
@media screen and (max-width: 991px) {
  [data-route="/features"] .features-container {
    grid-column-gap: 80px;
    grid-row-gap: 80px;
  }

  [data-route="/features"] .features-block {
    grid-column-gap: 32px;
    grid-row-gap: 28px;
  }

  [data-route="/features"] .features-image-block,
  [data-route="/features"] .features-content {
    width: 100%;
  }

  [data-route="/features"] .features-block .heading-3 {
    font-size: 24px;
  }
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ HOME — narrative feature rows (.home-features)                        ║
   ║                                                                       ║
   ║ Same inversion as /features: quiet the field, frame the media, let    ║
   ║ purple survive only as jewelry. The homepage rows earn a bit more     ║
   ║ editorial structure than /features because they carry the product     ║
   ║ story — a numbered mono eyebrow indexes each row (01…04), the         ║
   ║ statement reads as a real heading, and the media sits directly on the ║
   ║ section with a soft directional glow bleeding out behind it           ║
   ║ (alternating sides, matching the zig-zag image/copy order).           ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* Field: keep the export's top black fade (blends into the black hero video
   above) but drop the specular radials to a whisper so the framed media wins.
   .home-features.bg-transition (0,2,0) placed after the base rule → wins. */
.home-features.bg-transition {
  /* Opaque dark base occludes the fixed #particle-canvas dots within this
     section (they stay elsewhere on the page) — dark shading only here. */
  background-color: var(--m-bg);
  background-image: linear-gradient(180deg, #000 0%, transparent 9%),
    radial-gradient(
      60% 24% at 78% 20%,
      color-mix(in srgb, var(--m-glow) 32%, transparent) 0%,
      transparent 64%
    ),
    radial-gradient(
      60% 24% at 22% 66%,
      color-mix(in srgb, var(--m-glow) 32%, transparent) 0%,
      transparent 64%
    );
  background-repeat: no-repeat;
}

.home-features .features-container {
  grid-column-gap: 128px;
  grid-row-gap: 128px;
}

.home-features .features-block {
  background-image: none;
  align-items: center;
  grid-column-gap: 72px;
  grid-row-gap: 40px;
}

/* ── Media column + directional glow: no frame — media sits directly.
   Width matches the /features rows so both surfaces read the same. ── */
.home-features .features-image-block {
  position: relative;
  width: 48%;
  isolation: isolate;
}

/* Soft glow bleeding out from behind the media. Default: leans right (rows
   where media is on the left). */
.home-features .features-image-block::before {
  content: "";
  position: absolute;
  inset: -14%;
  z-index: -1;
  background: radial-gradient(
    46% 52% at 68% 42%,
    var(--m-glow) 0%,
    transparent 70%
  );
  opacity: 0.7;
  pointer-events: none;
}

/* Rows where the copy comes first in source order (media on the right):
   flip the glow to lean left so it always bleeds toward the page center. */
.home-features
  .features-block
  .features-content:first-child
  ~ .features-image-block::before {
  background: radial-gradient(
    46% 52% at 32% 42%,
    var(--m-glow) 0%,
    transparent 70%
  );
}

/* ── The media card ── */
.home-features .features-image,
.home-features .vimeo-features {
  display: block;
  width: 100%;
  margin: 0;
  border: 0;
  border-radius: var(--m-radius-lg);
  box-shadow: 0 26px 64px rgba(0, 0, 0, 0.6);
  overflow: hidden;
}

/* HiveControl row: render the media slightly smaller so the block sits shorter */
.home-features .features-block:nth-child(2) .features-image {
  max-width: 84%;
  margin: 0 auto;
}

/* Transparent artwork (HiveControl, Integrations): the art carries its own
   baked shadows and floats on alpha, so the media-card skin would paint a
   ghost rectangle (radius clip + rect shadow) behind the transparent
   regions. Let it sit bare on the section instead. */
.home-features .features-image.features-image-art {
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}

/* ── Copy column ── */
.home-features .features-content {
  width: 52%;
  grid-row-gap: 16px;
  align-items: flex-start;
}

/* Mono eyebrow */
.home-features .hf-eyebrow {
  display: inline-flex;
  align-items: center;
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  line-height: var(--m-eyebrow-lh);
  letter-spacing: var(--m-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--m-text-3);
}

/* Statement → real heading. .text-body-large is a 500 statement elsewhere;
   here it steps up to display-adjacent size + tighter tracking. */
.home-features .features-content .text-body-large {
  margin: 0;
  color: var(--m-text-1);
  font-size: var(--m-h3-size);
  letter-spacing: -0.015em;
}

.home-features .features-content .text-body-medium {
  margin: 0;
  color: var(--m-text-2);
  font-size: var(--m-body-size);
  line-height: var(--m-body-lh);
}

.home-features .hf-links {
  display: flex;
  gap: 24px;
  margin-top: 2px;
}

.home-features .features-content .link-purple {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  color: var(--m-accent-text);
  font-weight: 500;
  text-decoration: none;
}

.home-features .hf-links .link-purple {
  margin-top: 0;
}

.home-features .features-content .link-purple::after {
  content: "→";
  transition: transform 0.15s ease;
}

.home-features .features-content .link-purple:hover {
  color: var(--m-accent-hover);
}

.home-features .features-content .link-purple:hover::after {
  transform: translateX(3px);
}

@media screen and (max-width: 991px) {
  .home-features .features-container {
    grid-column-gap: 80px;
    grid-row-gap: 80px;
  }

  .home-features .features-block {
    grid-column-gap: 32px;
    grid-row-gap: 28px;
  }

  .home-features .features-image-block,
  .home-features .features-content {
    width: 100%;
  }
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ HOME — changelog preview strip (.changelog-preview)                   ║
   ║                                                                       ║
   ║ A four-up row of the most recent releases: a version pill + date on   ║
   ║ top, a short headline below. Each card is the one card skin           ║
   ║ (--m-surface + hairline) and links to /changelog. Transparent section ║
   ║ with a centered eyebrow + heading; "See everything new" sits below,   ║
   ║ centered. Collapses 4→2→1.                                            ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* Transparent section on --m-bg, matching the Testimonials / Built-for-HiVE
   editorial sections. The old --m-surface + purple radial band is retired: it
   double-spent the one-glow-per-page budget (hero already owns it) and, paired
   with the blog strip's identical band, read as a foreign block near the page
   foot. .section-secondary already supplies the vertical rhythm. */

/* Flip the centering container to flex-start so title / grid / link sit left. */
.changelog-preview .content-container {
  align-items: flex-start;
}

/* Section intro: eyebrow over heading, centered. */
.changelog-preview-head {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
  margin-bottom: 40px;
}

.changelog-preview-head .tag {
  margin-bottom: 0;
}

.changelog-preview-head .heading-2a {
  margin: 0;
  text-align: center;
}

.changelog-preview-all {
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 28px;
  color: var(--m-accent-text);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

.changelog-preview-all span {
  transition: transform 0.15s ease;
}

.changelog-preview-all:hover {
  color: var(--m-accent-hover);
}

.changelog-preview-all:hover span {
  transform: translateX(3px);
}

.changelog-preview-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  width: 100%;
}

.changelog-preview-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px;
  border: 1px solid var(--m-hairline);
  border-radius: var(--m-radius-lg);
  background-color: var(--m-surface);
  text-decoration: none;
}

.changelog-preview-meta {
  display: flex;
  align-items: center;
  gap: 10px;
}

.changelog-preview-version {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  border-radius: var(--m-radius-full);
  border: 1px solid var(--m-hairline);
  color: var(--m-text-2);
  font-family: var(--m-font-mono);
  font-size: var(--m-eyebrow-size);
  letter-spacing: 0.02em;
}

.changelog-preview-date {
  color: var(--m-text-3);
  font-size: var(--m-mono-ui-size);
}

.changelog-preview-title {
  margin: 0;
  color: var(--m-text-1);
  font-size: 15px;
  line-height: 1.4;
  font-weight: 400;
  letter-spacing: -0.005em;
}

@media screen and (max-width: 991px) {
  .changelog-preview-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media screen and (max-width: 600px) {
  .changelog-preview-grid {
    grid-template-columns: 1fr;
  }
}

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║ HOME — blog strip (.blog-strip)                                       ║
   ║                                                                       ║
   ║ The recent posts sit in a framed panel (hairline border + --m-surface ║
   ║ fill on the .content-container) on the transparent --m-bg.            ║
   ║ Hard-left eyebrow + heading; "View all blog                           ║
   ║ posts" sits bottom-left.                                              ║
   ║ Each item is a BORDERLESS horizontal row — meta + two-line title +     ║
   ║ read time on the left, a small fixed-width thumbnail to the right (no  ║
   ║ author). Two columns of rows on desktop, one column stacked on mobile. ║
   ║ Only the thumbnail reacts to hover, with a gentle zoom.               ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

/* Transparent section on --m-bg (see the changelog-preview note above): the old
   --m-surface + purple radial band is retired so the recent posts read as one
   calm editorial section alongside Testimonials / Built-for-HiVE, not a boxed-in
   band. .section-secondary already supplies the vertical rhythm. */

/* .content-container is a centering column flex (align-items:center). Flip it
   to flex-start for THIS section so the title, the row, and the bottom link all
   sit hard-left without per-child width hacks. It also becomes the framed
   panel: a hairline border + surface fill wraps the whole blog section.
   --m-radius-xl / 48px inset give the border room; the section still sits on
   the transparent --m-bg. */
.blog-strip .content-container {
  align-items: flex-start;
  background: var(--m-surface);
  border: 1px solid var(--m-hairline);
  border-radius: var(--m-radius-xl);
  padding: 48px;
}

/* Section intro: eyebrow over heading, hard-left. */
.blog-strip-head {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 14px;
  margin-bottom: 40px;
}

.blog-strip-head .tag {
  margin-bottom: 0;
}

.blog-strip-head .heading-2a {
  margin: 0;
  text-align: left;
}

.blog-strip-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 28px 48px;
  width: 100%;
}

/* Bottom-left, under the grid. align-self overrides the container's alignment. */
.blog-strip-all {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 28px;
  color: var(--m-accent-text);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

.blog-strip-all span {
  transition: transform 0.15s ease;
}

.blog-strip-all:hover {
  color: var(--m-accent-hover);
}

.blog-strip-all:hover span {
  transform: translateX(3px);
}

/* Horizontal card: text on the left, a small thumbnail to the right of it.
   Borderless / fill-less — it reads as a list item, not a boxed card. */
.blog-strip-card {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 20px;
  border: 0;
  background: none;
  text-decoration: none;
}

.blog-strip-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0;
}

/* The smaller image, held to the right at a fixed width. */
.blog-strip-thumb {
  flex: 0 0 148px;
  width: 148px;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: var(--m-radius-md);
  background-color: var(--m-surface-2);
}

.blog-strip-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

.blog-strip-meta {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--m-text-3);
  font-size: 13px;
}

.blog-strip-title {
  margin: 0;
  color: var(--m-text-1);
  font-size: 16px;
  line-height: 1.4;
  font-weight: 400;
  letter-spacing: -0.005em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.blog-strip-read {
  margin-top: 2px;
  color: var(--m-text-3);
  font-size: 13px;
}

@media screen and (max-width: 991px) {
  .blog-strip-grid {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .blog-strip .content-container {
    padding: 32px;
  }
}

@media screen and (max-width: 480px) {
  .blog-strip-thumb {
    flex-basis: 112px;
    width: 112px;
  }

  .blog-strip .content-container {
    padding: 24px;
  }
}

/* ============================================================
   Brand-font re-pin — the Webflow export hardwires "Nunito Sans"
   on these classes. Nunito no longer loads (Geist + Roboto Mono
   only), so without this they silently fall back to the generic
   system sans instead of the brand face (live on the /download
   email form + footer legal bar).
   ============================================================ */
.copyright-text,
.copyright-text.desktop,
.submit-button-2,
.text-block-2,
.text-block-3,
.field-label-2,
.text-field-2 {
  font-family: var(--m-font);
}

/* ============================================================
   Skip link — keyboard/AT shortcut past the fixed nav to <main>.
   Visually hidden offscreen until focused (a11y quality floor).
   ============================================================ */
.skip-link {
  position: fixed;
  top: -56px;
  left: 16px;
  z-index: 1000;
  padding: 10px 16px;
  background: var(--m-surface);
  color: var(--m-text-1);
  border: 1px solid var(--m-hairline-strong);
  border-radius: var(--m-radius-md);
  text-decoration: none;
  font-weight: 500;
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: 16px;
}

/* Homepage hero — hide the subheading copy but keep the vertical gap it
   occupies (visibility:hidden preserves the layout box; text stays in the
   DOM for SEO/AT). */
[data-route="/"] .subheading-hero {
  visibility: hidden;
}

/* On phones the hidden paragraph wraps to 7+ lines, so its preserved box
   becomes a ~220px blank gap between the headline and the CTAs. Collapse
   the box; the flex column's own gaps keep the headline/CTA spacing. */
@media screen and (max-width: 767px) {
  [data-route="/"] .subheading-hero {
    height: 0;
    margin: 0;
    overflow: hidden;
  }
}

/* ============================================================
   Homepage — statement title opening the features section, typed
   out on scroll into view (script in index.astro). Lives inside
   .home-features so it sits on that section's own background.
   ============================================================ */
[data-route="/"] .statement-band-title {
  margin: 110px auto 150px;
  max-width: 22ch;
  text-align: center;
  font-size: clamp(2.25rem, 4.5vw, 3.25rem);
  font-weight: 400;
  line-height: 1.15;
}

/* Terminal-style caret in the brand accent. Blinks while typing and
   keeps blinking after the line completes. */
[data-route="/"] .statement-caret {
  display: inline-block;
  width: 0.08em;
  height: 0.85em;
  margin-left: 0.1em;
  background: var(--m-accent);
  vertical-align: -0.06em;
  animation: statement-caret-blink 1.06s steps(1) infinite;
}

@keyframes statement-caret-blink {
  0%,
  49% {
    opacity: 1;
  }
  50%,
  100% {
    opacity: 0;
  }
}

@media (max-width: 767px) {
  [data-route="/"] .statement-band-title {
    margin: 64px auto 96px;
  }
}

/* ============================================================
   Contact-style form pages (/contact-us, /support,
   /get-featured): a shared bracket hero on top, then a split
   section — context aside on the left, the form in one tokenized
   surface panel on the right. All three pages use the SAME
   .contact-* classes, so the rules are class-scoped (not
   route-scoped) — those classes only exist on these pages.
   Everything resolves through the --m-* tokens
   (docs/design/marketing.md): hairline borders, radius-lg panel,
   radius-md controls, eyebrow/mono voice for labels. No purple
   furniture.

   Background image removed: .body-contact-page (shared by these
   pages, defined in the Webflow export) painted a purple
   slider-background.png behind the whole page — it's dropped so
   each hero flows into the neutral --m-bg field.
   ============================================================ */

.body-contact-page {
  background-image: none;
  min-height: 0;
  margin-top: 0; /* was 120px — the big empty band above the hero. The
                    hero's own .section-hero padding now matches /company. */
}

.section-secondary:has(.contact-split) {
  padding-top: 40px;
}

.contact-split {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 6fr);
  gap: 64px;
  align-items: start;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
}

/* ── Aside: eyebrow, heading, copy, and a keyed detail list ── */

.contact-eyebrow {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--m-accent-text);
  margin-bottom: 16px;
}

.contact-aside-heading {
  margin: 0 0 12px;
  color: var(--m-text-1);
  font-size: 32px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.contact-aside-copy {
  margin: 0 0 32px;
  color: var(--m-text-2);
  font-size: 16px;
  line-height: 1.6;
}

.contact-aside-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.contact-aside-list li {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 0;
  border-top: 1px solid var(--m-hairline);
}

.contact-aside-item-label {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--m-text-3);
}

.contact-aside-item-value {
  color: var(--m-text-1);
  font-size: 15px;
  line-height: 1.5;
}

.contact-aside-list .m-link-arrow {
  font-size: 15px;
  line-height: 1.5;
  align-self: flex-start;
}

/* ── Form panel: the one card spec (surface, hairline, lg) ── */

.contact-form-panel {
  background-color: var(--m-surface);
  border: 1px solid var(--m-hairline);
  border-radius: var(--m-radius-lg);
  padding: 40px;
}

.contact-form-panel .contact-form-block {
  margin: 0;
}

.contact-form-panel .form {
  grid-column-gap: 20px;
  grid-row-gap: 20px;
}

.contact-form-panel .field-group {
  grid-column-gap: 20px;
  grid-row-gap: 20px;
}

.contact-form-panel .field-label {
  font-family: var(--m-font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--m-text-2);
  margin-bottom: 8px;
}

.contact-form-panel .text-field,
.contact-form-panel .textarea {
  background-color: var(--m-surface-2);
  border: 1px solid var(--m-hairline);
  border-radius: var(--m-radius-md);
  color: var(--m-text-1);
  font-size: 15px;
  line-height: 1.4;
  padding: 12px 14px;
}

.contact-form-panel .text-field:hover,
.contact-form-panel .textarea:hover {
  border-color: var(--m-hairline-strong);
}

.contact-form-panel .text-field:focus,
.contact-form-panel .textarea:focus {
  background-color: var(--m-surface-2);
  border-color: var(--m-accent);
}

.contact-form-panel .textarea {
  min-height: 160px;
  max-height: 400px;
}

/* ── Issue-type radios (support page): a stack of hairline pill
      rows; the selected row goes accent-tinted with an accent
      border. The native input stays visually hidden but focusable
      (the visible .radio-button dot carries the state). ── */

.contact-form-panel .div-block-24 {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.contact-form-panel .radio-button-field {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--m-hairline);
  border-radius: var(--m-radius-md);
  background-color: var(--m-surface-2);
  color: var(--m-text-1);
  font-size: 15px;
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}

.contact-form-panel .radio-button-field:hover {
  border-color: var(--m-hairline-strong);
}

.contact-form-panel .radio-button-field:has(input:checked) {
  border-color: var(--m-accent);
  background-color: var(--m-accent-tint);
}

.contact-form-panel .radio-button-field:has(input:focus-visible) {
  outline: 2px solid var(--m-accent);
  outline-offset: 2px;
}

.contact-form-panel .radio-button {
  width: 16px;
  height: 16px;
  margin: 0;
  border: 1px solid var(--m-hairline-strong);
  border-radius: var(--m-radius-full);
  background-color: transparent;
  flex-shrink: 0;
}

.contact-form-panel .radio-button-field:has(input:checked) .radio-button {
  border-color: var(--m-accent);
  background-color: var(--m-accent);
  box-shadow: inset 0 0 0 3px var(--m-surface-2);
}

.contact-form-panel .radio-button-label {
  margin: 0;
  color: inherit;
  font-size: 15px;
  line-height: 1.4;
}

/* Submit: the canonical secondary spec supplies color and shape;
   here it sizes to content instead of the old full-width slab. */
.contact-form-panel .submit-button.full-width {
  width: auto;
  min-width: 160px;
  padding: 0.72em 1.5em;
  font-size: 15px;
}

.contact-form-panel .success-message {
  border: 0;
  padding: 0;
}

.contact-form-panel .success-message .text-success {
  color: var(--m-text-2);
  font-weight: 400;
  line-height: 1.6;
}

@media (max-width: 991px) {
  .contact-split {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
    max-width: 640px;
  }
}

@media (max-width: 479px) {
  .contact-form-panel {
    padding: 24px 20px;
  }

  .contact-form-panel .submit-button.full-width {
    width: 100%;
  }
}
