/*
 * Bidlo dark palette.
 *
 * NOT generated by `npm run port` — this is the first deliberate divergence
 * from the capture. The captured dark mode is Cursor's warm near-black
 * (#14120b, olive-tinted), which reads muddy next to the Bidlo product. These
 * values are the dark tokens from bidlo-light-1.0/app/globals.css (`.dark`),
 * converted from oklch to sRGB hex:
 *
 *   --background      oklch(0.2134 0 274.03)      #191919
 *   --muted / --card  oklch(0.2435 0 274.03)      #202020
 *   --popover         oklch(0.26 0 0)             #242424
 *   --hover-opaque    oklch(0.3 0 0)              #2e2e2e
 *   --foreground      oklch(1 0.0001 274.03/81%)  #d3d3d3 over --background
 *
 * Everything else in the captured theme derives from --color-theme-bg and
 * --color-theme-fg through color-mix(), so retuning these base tokens carries
 * borders, secondary text, scrollbars and card gradients along with it. That
 * is why this file is short: the capture's token graph already matches the
 * shape of the app's, only the anchors differ.
 *
 * Selector is `:root[data-theme="dark"]` rather than `[data-theme="dark"]` so
 * it outranks the captured rules on specificity, not just source order — the
 * page stylesheets are 100KB+ of generated output and their bundle position
 * should not be load-bearing.
 */

:root[data-theme="dark"] {
  --color-theme-bg: #191919;
  --color-theme-fg: #d3d3d3;
  --color-theme-fg-02: #bfbfbf;

  /* Card ramp. The capture steps bg -> card -> 04 in roughly +7/+2/+2/+6/+5
     luminance; the same steps are kept, re-anchored on the app's neutrals so
     card/popover/hover land exactly on --muted/--popover/--hover-opaque. */
  --color-theme-card-hex: #202020;
  --color-theme-card-rgb: 32 32 32;
  --color-theme-card-01-hex: #222222;
  --color-theme-card-02-hex: #242424;
  --color-theme-card-03-hex: #2a2a2a;
  --color-theme-card-04-hex: #2e2e2e;
  --color-theme-card-hover-hex: #242424;
  --color-theme-card-hover-light-hex: #222222;

  /* The capture tints this one warm to separate it from --card-hex. The app's
     dark mode has no warm axis, so it separates by luminance instead. */
  --color-theme-card-warm-hex: #1e1e1e;
}

/* The phone mockup carries its own token set. Only the two warm anchors move;
   its status/accent hues are illustration, not chrome. */
:root[data-theme="dark"] .mobile-phone-screen {
  --mobile-phone-bg: #202020;
  --mobile-phone-text: #e8e8e8;
}
/* Base UI-style navigation menu — the shared morphing viewport.
 *
 * Hand-maintained, not emitted by `npm run port` (the pipeline never wipes
 * src/styles). It pairs with src/lib/nav-menu.js, which restructures the
 * captured header at runtime: the two `.mega-panel` dropdowns move out of their
 * per-item `.nav__sub-nav` positioners and into one shared popup, so switching
 * from Product to Resources morphs a single box instead of cross-fading two.
 *
 * Nothing here changes how a panel *looks*. No colour, radius, shadow, size or
 * spacing is declared: the popup and the panel both keep the captured
 * `.mega-panel` class, and this file only moves the chrome from one to the other
 * and drives the transitions.
 */

.nav-menu {
  /* Morph is slower than the captured --duration (.14s): the box is travelling
     and resizing at once, and .14s reads as a jump rather than a transition. */
  --nav-menu-morph: 0.28s;
  --nav-menu-fade: 0.16s;
  --nav-menu-ease: var(--ease-out-spring, cubic-bezier(0.25, 1, 0.5, 1));
  --nav-menu-shift: 28px;
  position: absolute;
  top: 0;
  left: 0;
}

/* This root must never carry a transform: it would become the containing block
   for the fixed positioner below, and the whole menu would hang off the header
   instead of the viewport. It is a direct div child of #site-header, so any
   header rule written as `#site-header > div` catches it — header-pinned.css
   did exactly that once. The doubled class outscores `#site-header.is-x > div`. */
#site-header > .nav-menu.nav-menu {
  transform: none;
  transition: none;
}

/* Fixed, like the captured `--mega-wide` positioner. --nav-menu-y is the nav
   item's bottom edge; --nav-menu-gap is the original positioner's padding-top,
   read off the capture at init so the trigger-to-panel gap is unchanged. */
.nav-menu__positioner {
  position: fixed;
  top: 0;
  left: 0;
  padding-top: var(--nav-menu-gap, 5.6px);
  transform: translate3d(var(--nav-menu-x, 0px), var(--nav-menu-y, 0px), 0);
  pointer-events: none;
  transition: transform var(--nav-menu-morph) var(--nav-menu-ease);
}

/* The popup carries the `mega-panel` class, so background, radius, shadow and
   both theme variants come from the captured rule rather than being copied here
   — retuning `.mega-panel` (in theme-dark.css, say) carries to the popup. Only
   the box itself is overridden: size is driven by the measured panel, and the
   padding belongs to the panel inside. #site-header outscores the captured
   `.mega-panel` and `[data-theme=dark] .mega-panel`. */
#site-header .nav-menu__popup {
  width: var(--nav-menu-w, 0px);
  height: var(--nav-menu-h, 0px);
  padding: 0;
  opacity: 0;
  visibility: hidden;
  /* -.4rem is the captured `.nav__sub-nav` entrance, kept as-is. It is also a
     translate rather than a scale on purpose: a scaled ancestor would shrink
     the panel rects nav-menu.js measures to size this box. */
  transform: translateY(-0.4rem);
  transition:
    width var(--nav-menu-morph) var(--nav-menu-ease),
    height var(--nav-menu-morph) var(--nav-menu-ease),
    opacity var(--nav-menu-fade) ease,
    transform var(--nav-menu-fade) var(--nav-menu-ease),
    visibility 0s linear var(--nav-menu-fade);
}

/* #site-header here is not decoration: the base rule above needs that prefix to
   outscore the captured `.mega-panel`, so every rule that overrides it needs one
   too or the popup opens still holding its closed transform. */
#site-header .nav-menu[data-open] .nav-menu__popup {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition-delay: 0s;
}

/* Reposition and resize without animating when the menu opens from closed —
   only the fade should be visible. Set for one frame by nav-menu.js. */
.nav-menu[data-instant] .nav-menu__positioner,
#site-header .nav-menu[data-instant] .nav-menu__popup {
  transition: none;
}

.nav-menu__viewport {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

/* The moved `.mega-panel`. Chrome is stripped (it lives on the popup now);
   width, padding and contents are the capture's. Absolute so the panel never
   reflows while the popup animates around it.
   #site-header outscores `[data-theme=dark] .mega-panel` in the inline tail. */
#site-header .nav-menu__content {
  position: absolute;
  top: 0;
  left: 0;
  background: none;
  border-radius: 0;
  box-shadow: none;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--nav-menu-fade) ease,
    transform var(--nav-menu-fade) var(--nav-menu-ease),
    visibility 0s linear var(--nav-menu-fade);
}

#site-header .nav-menu__content[data-active] {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

/* Directional slide. `data-leaving` marks the panel being replaced, so the
   outgoing panel exits the way the incoming one entered from. */
.nav-menu[data-direction='right'] .nav-menu__content {
  transform: translateX(var(--nav-menu-shift));
}
.nav-menu[data-direction='right'] .nav-menu__content[data-leaving] {
  transform: translateX(calc(var(--nav-menu-shift) * -1));
}
.nav-menu[data-direction='left'] .nav-menu__content {
  transform: translateX(calc(var(--nav-menu-shift) * -1));
}
.nav-menu[data-direction='left'] .nav-menu__content[data-leaving] {
  transform: translateX(var(--nav-menu-shift));
}

/* A leaving panel is mid-fade, so it must still paint. */
#site-header .nav-menu__content[data-leaving] {
  visibility: visible;
}

/* The captured chevron rule keys off `li:hover` / `li:focus-within`, which stops
   matching once the pointer is over the popup. Follow the open item instead. */
#site-header nav li[data-nav-open] > .nav__btn .clone-chevron {
  transform: rotate(180deg);
  opacity: 0.85;
}

@media (prefers-reduced-motion: reduce) {
  .nav-menu {
    --nav-menu-morph: 0s;
    --nav-menu-fade: 0s;
    --nav-menu-shift: 0px;
  }
}
/* Mobile navigation menu.
 *
 * Hand-maintained, like src/styles/nav-menu.css and src/styles/header-pinned.css:
 * tools/04-generate.mjs imports it into every page but never rewrites it. Pairs
 * with src/lib/mobile-menu.js, which builds the trigger and the sheet at load
 * out of the header's own captured nav.
 *
 * The breakpoint is 900px because that is what the capture's `lg:` compiles to
 * (`@media (min-width:900px){.lg\:block{...}}`) — the header hides its whole
 * `<nav>` below it. Tailwind's default 1024 would leave a 124px band with both
 * navs showing.
 *
 * Colour is deliberately thin here. The cards keep the captured `.mega-card`
 * rules — pastel fills, `#191918` ink, no dark variant — exactly as the desktop
 * dropdown renders them, so retuning that one rule carries to both. Only the
 * layout is restated: the capture stacks a card (art over title over copy) and
 * a phone wants it in a row.
 */

/* ------------------------------------------------------------------ *
 * Trigger
 * ------------------------------------------------------------------ */

/* Measured off cursor.com's own `button[aria-label="Open navigation"]` at
   390/500/768/899 — every value below is theirs, and constant across all four.
   Their box is `w-v1.5 h-v1.5`, which is a token this capture also carries and
   resolves identically (31.5px, off the same 15px root), so it is written as
   the token rather than as the number it happens to equal today. */
.mobile-menu-trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--spacing-v1\.5);
  height: var(--spacing-v1\.5);
  /* Their `ml-g1` on top of the cluster's own gap, and their `-mr-[0.3rem]`
     optical pull — the icon's viewBox insets the bars ~4px from the box edge,
     so the box has to hang out past the row for the ink to land on it. */
  margin-left: var(--spacing-g1);
  margin-right: -0.3rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-theme-fg);
  cursor: pointer;
}

/* Their `after:inset-[-0.5rem]`: the layout box stays exactly the icon and the
   touch area grows underneath it, rather than padding the button out and
   pushing the row around. */
.mobile-menu-trigger::after {
  position: absolute;
  inset: -0.5rem;
  content: '';
}

@media (min-width: 900px) {
  .mobile-menu-trigger {
    display: none;
  }
}

/* Both glyphs fill the button and are crossfaded, where cursor.com swaps one
   whole button for another (`Open navigation` / `Close navigation`, the close
   one lifted to z-51 over its overlay). One button is enough here — the header
   already outranks the sheet — and `display` cannot transition, so a stroke
   this thin popping in would read as a flicker.

   Both SVGs are `width=height=100%` on a 24 viewBox, so the box is the only
   size control: the menu path spans just 16.5 of those units and renders
   21.66px of visible bars inside 31.5px, which is why sizing the box to the
   ink came out a third too small. */
.mobile-menu-trigger__icons {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}

.mobile-menu-trigger__icon {
  position: absolute;
  inset: 0;
  transition: opacity 0.15s ease;
}

.mobile-menu-trigger__icon[data-icon='close'],
.mobile-menu-trigger[aria-expanded='true'] .mobile-menu-trigger__icon[data-icon='menu'] {
  opacity: 0;
}

.mobile-menu-trigger[aria-expanded='true'] .mobile-menu-trigger__icon[data-icon='close'] {
  opacity: 1;
}

/* ------------------------------------------------------------------ *
 * Sheet
 * ------------------------------------------------------------------ */

/* Below the header's fixed `z-50`, so the trigger stays live as the close X.
   Top edge is the header's own height, which the body already pads by. */
.mobile-menu {
  position: fixed;
  top: var(--site-header-height);
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  background-color: var(--color-theme-bg);
  opacity: 0;
  transform: translateY(-0.5rem);
  /* Transparent but still laid out for the length of the fade-out, so it must
     not keep swallowing clicks meant for the page behind it. */
  pointer-events: none;
  transition:
    opacity 0.18s ease,
    transform 0.22s var(--ease-out-spring, ease);
}

/* Author `display` beats the UA sheet's `[hidden]{display:none}`, so the rule
   above would leave a transparent full-screen sheet swallowing every click on
   the page behind it. It has to be restated here. */
.mobile-menu[hidden] {
  display: none;
}

.mobile-menu[data-open] {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

/* Scroll is locked on the page, not the sheet, so a long Resources drawer still
   scrolls. `overscroll-behavior` stops that scroll chaining to the frozen body,
   which iOS otherwise treats as a rubber-band on the whole page. */
.mobile-menu__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 1rem var(--spacing-g2, 1rem) 1.5rem;
}

.mobile-menu__cards {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}

/* The captured card is a column: art, title, copy. On a phone that is three
   full-width bands of nothing, so it becomes a row with the art parked right.
   Grid areas rather than order, because the source order is art-first. */
.mobile-menu .mega-card {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    'title art'
    'desc  art';
  align-items: center;
  column-gap: 0.875rem;
  padding: 0.875rem 1rem;
  border-radius: 12px;
}

.mobile-menu .mega-card__art {
  grid-area: art;
  height: 3.5rem;
  margin: 0;
}

.mobile-menu .mega-card__title {
  grid-area: title;
  font-size: 1.25rem;
  margin-bottom: 0.125rem;
}

.mobile-menu .mega-card__desc {
  grid-area: desc;
  font-size: 0.875rem;
}

/* Same blue as the panel head it was cloned out of — `.mega-panel__head a`,
   which no longer matches now that the link sits outside a panel. */
.mobile-menu__more {
  display: inline-block;
  margin: 0.875rem 0 0 0.125rem;
  color: #2383e2;
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
}

/* ------------------------------------------------------------------ *
 * Rows
 * ------------------------------------------------------------------ */

/* The whole sheet body — every menu is a row here, panel-backed or not — so it
   sits flush against the scroller's own padding. */
.mobile-menu__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.mobile-menu__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  padding: 0.75rem 0.125rem;
  border: 0;
  background: none;
  color: var(--color-theme-fg);
  font: inherit;
  font-size: 1.5rem;
  line-height: 1.25;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.mobile-menu__chevron {
  width: 0.9rem;
  height: 0.9rem;
  flex: none;
  opacity: 0.5;
  transition:
    transform 0.2s var(--ease-out-spring, ease),
    opacity 0.2s ease;
}

.mobile-menu__row[aria-expanded='true'] .mobile-menu__chevron {
  transform: rotate(180deg);
  opacity: 0.85;
}

/* `height: auto` is not interpolable, so the drawer animates a 0fr -> 1fr grid
   track and the inner element does the clipping. `min-height: 0` on the inner
   is what lets the track actually reach zero. */
.mobile-menu__drawer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.26s var(--ease-out-spring, ease);
}

.mobile-menu__drawer[data-open] {
  grid-template-rows: 1fr;
}

.mobile-menu__drawer-inner {
  min-height: 0;
  overflow: hidden;
}

.mobile-menu__drawer .mobile-menu__cards {
  padding: 0.25rem 0 0.5rem;
}

.mobile-menu__drawer .mobile-menu__more {
  margin-bottom: 0.75rem;
}

/* ------------------------------------------------------------------ *
 * Footer actions
 * ------------------------------------------------------------------ */

.mobile-menu__foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0.875rem var(--spacing-g2, 1rem) max(0.875rem, env(safe-area-inset-bottom));
  border-top: 1px solid var(--color-theme-border-02);
  background-color: var(--color-theme-bg);
}

.mobile-menu__cta.mobile-menu__cta {
  display: flex;
  width: 100%;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
}

/* ------------------------------------------------------------------ *
 * Page state while open
 * ------------------------------------------------------------------ */

/* The header's own float -> pin state is left exactly as header-pinned.css set
   it: opening the menu must not pin an unpinned header, and closing it must not
   unpin a pinned one. The sheet's top edge is the header's height either way.
   The one thing suppressed is the divider — while the sheet is up the header is
   its lid, and a hairline plus a drop shadow across the seam reads as two
   surfaces stacked rather than one. `html[data-menu-open] #site-header::before`
   outscores header-pinned.css's `#site-header::before` on element count, so this
   holds wherever the two files land in the bundle.

   It also has to survive the fade-out, or the hairline snaps back under a sheet
   still on screen — which is why mobile-menu.js holds `data-menu-open` until the
   sheet has finished leaving rather than dropping it on the click. */
html[data-menu-open] #site-header::before {
  border-bottom-color: transparent;
  box-shadow: none;
}

html[data-menu-open],
html[data-menu-open] body {
  overflow: hidden;
}

/* Locking the scroll takes the scrollbar with it, and the fixed header is
   width-100% — without this its contents slide right by the gutter's width the
   moment the menu opens. Only a desktop-width browser under 900px has one;
   mobile overlay scrollbars leave --mobile-menu-scrollbar unset, so this is the
   captured `px-g2` unchanged. Restated rather than added to, since the class
   sets `padding-inline` and there is no way to add to one side of that. */
html[data-menu-open] #site-header,
.mobile-menu__scroll,
.mobile-menu__foot {
  padding-right: calc(var(--spacing-g2, 1rem) + var(--mobile-menu-scrollbar, 0px));
}

/* Above the capture's `md` (660px) the sheet spans a full tablet and a card
   stretched across it is mostly empty fill. Hold the column to roughly the
   width the phone layout was drawn at. The foot keeps its full width so its
   top border still reads as the sheet's own edge; only the buttons narrow —
   and via max-width rather than an auto margin, which on a flex item would
   shrink-wrap the foot and take the border with it. Last in the file because
   these all lose to the margins and widths set above on source order. */
@media (min-width: 660px) {
  .mobile-menu__list {
    max-width: 32rem;
    margin-inline: auto;
  }

  .mobile-menu__cta.mobile-menu__cta {
    max-width: 32rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .mobile-menu,
  .mobile-menu__drawer,
  .mobile-menu__chevron,
  .mobile-menu-trigger__bars span {
    transition-duration: 0.01ms;
  }
}
/* Header float -> pin.
 *
 * Hand-maintained, like src/styles/theme-dark.css and src/styles/nav-menu.css:
 * tools/04-generate.mjs imports it into every page but never rewrites it. Pairs
 * with src/lib/header-pinned.js.
 *
 * Ported from bidlo-client-v3's <Header>. At the top of the page the header has
 * no surface of its own and its contents ride a few pixels low, so the chrome
 * reads as floating over the hero. Past 16px of scroll a background layer fades
 * in behind it with a hairline bottom border and a soft shadow, and the contents
 * settle up into place — the page now has a lid.
 *
 * The background is a ::before rather than the header's own background so it
 * can cross-fade without taking the nav text with it. The captured markup gives
 * the inner container `relative z-2`, so it already sits above the layer.
 *
 * The settle targets `> .container`, not `> div`. src/lib/nav-menu.js mounts the
 * dropdown popup as a second direct child of the header, and a transform on it
 * would make it the containing block for the fixed positioner inside — the menu
 * would hang 6px low and animate on every pin.
 *
 * Note the capture paints the header the same colour as the body, which means
 * the unpinned state is only visibly different in the 6px of settle — the
 * transparent background is a no-op until something coloured scrolls under it.
 */

#site-header {
  /* Beats the captured `.bg-theme-bg` on specificity, not source order. */
  background-color: transparent;
}

#site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  box-sizing: border-box;
  background-color: var(--color-theme-bg);
  border-bottom: 1px solid var(--color-theme-border-02);
  box-shadow: 0 1px 2px 0 color-mix(in oklab, var(--color-theme-fg) 6%, transparent);
  opacity: 0;
}

#site-header > .container {
  transform: translateY(var(--site-header-float, 6px));
}

#site-header.is-pinned::before {
  opacity: 1;
}

#site-header.is-pinned > .container {
  transform: none;
}

/* Transitions are held back until after the first paint so a page that loads
   already scrolled — a refresh, a back-navigation, an in-page anchor — snaps
   straight to the pinned state instead of animating into it. The script adds
   this class on the frame after it sets the opening state. */
#site-header.is-ready::before {
  transition: opacity 0.2s ease-out;
}

#site-header.is-ready > .container {
  transition: transform 0.2s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  #site-header.is-ready::before,
  #site-header.is-ready > .container {
    transition-duration: 0.01ms;
  }
}
/* Home-page integration cards, ported from bidlo-client-v3's
 * <IntegrationsSection>. Only the line-art graphics came across — the card
 * shell, type scale and spacing are this site's own (`.card`,
 * `.media-border-container`, `--spacing-*`), so the section sits in the grid
 * alongside the captured ones without a style of its own.
 *
 * Hand-maintained, like src/styles/theme-dark.css: 04-generate.mjs imports it
 * into every page but never rewrites it. The markup lives in
 * tools/fragments/integrations.html, which the generator swaps in for the
 * capture's "Stay connected" section.
 */

/* The art sits on the same surface as the enterprise page's card media —
   `--color-theme-card-03`, which is `--color-theme-fg-05` laid over
   `--color-theme-card-hex`. It is a *background-image*, not a colour, which is
   why the captured markup reaches it as `bg-[image:var(--color-theme-card-03)]`
   and why it is set here rather than as a utility class: `src/styles/*.css` is
   frozen Tailwind output, and index never emitted that arbitrary class. The
   panel reads a step above the page background, so the art is a raised surface
   inside the card rather than a window cut through it; the
   `.media-border-container` hairline from the captured stylesheet still edges
   it. This rule also sizes and centres it. */
.integration-art {
  background-image: var(--color-theme-card-03);
  display: flex;
  align-items: center;
  justify-content: center;
  /* 3:2 — the same ratio the captured stylesheet's `.aspect-3/2-box` uses. The
     art is ~1.875:1, so a wider box left it running edge to edge; this leaves
     roughly even margin on all four sides at every column width. */
  aspect-ratio: 3 / 2;
  /* Paired with the 300x181 viewBox the art now carries: at 8% the content area
     is 0.84w x 0.5067w, which is 1.658:1, so the svg fills it on both axes
     instead of letterboxing. Moving one without the other puts the air back. */
  padding: 8%;
}

/* The line art is drawn entirely in currentColor, so it follows the card's text
   colour into dark mode without a second palette. Dropping that colour to 80%
   of the foreground puts it on the enterprise card art's two ink levels: the
   plain-currentColor strokes land at 0.8 and the paths that already carry
   `stroke-opacity="0.5"` fall to 0.4, which is exactly where that graphic's
   primary and secondary marks sit. Setting `color` rather than `opacity` on the
   svg is deliberate — opacity would mute the brand logos too, and those are
   painted in fixed hex fills so they can stay recognisable.

   They scale with the box rather than sitting at a fixed 300px, so the card
   reads the same at one, two and three columns. */
.integration-art > svg {
  color: #26251ecc;
  width: 100%;
  height: auto;
  max-height: 100%;
}

@supports (color: color-mix(in lab, red, red)) {
  .integration-art > svg {
    color: color-mix(in oklab, var(--color-theme-fg) 80%, transparent);
  }
}

/* The MCP card carries the "Use the best model for every task" product mockup
   lifted verbatim from the captured "new way to build software" section rather
   than a line-art svg, so it opts out of `.integration-art` entirely — no
   padding, no 3:2 box, and the mockup's own `bg-theme-card-03-hex` puts it on
   the same raised surface as its siblings.

   The capture ran that mockup at `aspect-4/3-box md:aspect-1/1-box`; here it is
   square at every width. That cannot be written with the captured utilities:
   `src/styles/index.css` is frozen Tailwind output and its unprefixed
   `.aspect-1/1-box` only emits the `::after` spacer — the `display:grid` half
   lives on `.aspect-4/3-box` and `.md:aspect-1/1-box` alone, and stacking the
   two classes loses anyway because `.aspect-4/3-box::after` is declared later
   in the file. So both halves are restated here under one name. */
.integration-mockup-box {
  display: grid;
  grid-template-rows: repeat(1, minmax(0, 1fr));
  grid-template-columns: repeat(1, minmax(0, 1fr));
  position: relative;
  width: 100%;
  background-color: transparent;
}

.integration-mockup-box::after {
  content: "";
  pointer-events: none;
  grid-area: 1 / 1 / -1 / -1;
  padding-top: 100%;
}
/* Header "Get started" button — slide, don't snap.
 *
 * Hand-maintained, like src/styles/theme-dark.css: tools/04-generate.mjs
 * imports it into every page but never rewrites it, and it leaves
 * src/scripts/header-cta.js alone for the same reason.
 *
 * The button enters from the right once the hero CTA row scrolls away and
 * leaves to the left when the row comes back. Two directions means two hidden
 * states — `clone-cta-enter` (parked to the right, about to arrive) and
 * `clone-cta-hide` (on its way out to the left) — which is why the script
 * teleports the button to the enter state before showing it.
 *
 * These are new class names rather than the capture's `.clone-cta-off` on
 * purpose: that rule is `display:none!important`, inlined after this file in
 * the tail styles, and nothing animates out of `display:none`.
 *
 * Both hidden states collapse to zero width so the rest of the header sits
 * exactly where the capture puts it — the button contributes no space at all,
 * gap included.
 */

#site-header .clone-cta-anim {
  max-width: var(--clone-cta-w, 12rem);
  overflow: hidden;
  white-space: nowrap;
  transition:
    max-width 0.34s var(--ease-out-spring, ease),
    padding 0.34s var(--ease-out-spring, ease),
    margin 0.34s var(--ease-out-spring, ease),
    border-width 0.34s var(--ease-out-spring, ease),
    transform 0.34s var(--ease-out-spring, ease),
    opacity 0.22s var(--ease-out, ease);
}

/* Held for the one frame where the button is teleported to its start position,
   or measured at its natural width. Never with a transition in flight. */
#site-header .clone-cta-still {
  transition: none !important;
}

#site-header .clone-cta-hide,
#site-header .clone-cta-enter {
  max-width: 0;
  padding-left: 0;
  padding-right: 0;
  border-left-width: 0;
  border-right-width: 0;
  /* Swallows the flex row's gap, which would otherwise leave a hole. */
  margin-left: calc(-1 * var(--clone-cta-gap, 0px));
  opacity: 0;
  pointer-events: none;
}

#site-header .clone-cta-hide {
  transform: translateX(-1.25rem);
}

#site-header .clone-cta-enter {
  transform: translateX(1.25rem);
}

/* At rest, hand the button back to the capture's `display:none`. An element
   parked at opacity 0 still builds a stacking context, and that alone changes
   how the browser antialiases the rest of the header row — enough for
   `npm run verify` to see a few hundred differing pixels on text that never
   moved. The script adds this once the hide transition has finished. */
#site-header .clone-cta-gone {
  display: none !important;
}

@media (prefers-reduced-motion: reduce) {
  #site-header .clone-cta-anim {
    transition-duration: 0.01ms;
  }
}
/* Auth-aware CTAs.
 *
 * Hand-maintained: `pnpm run port` never writes this file, and
 * tools/04-generate.mjs imports it into every page.
 *
 * Both halves of the header CTA cluster ship in the HTML — the signed-out row
 * (Log In / Demo / Get started) and the signed-in one ("Open Bidlo") — and this
 * hides whichever does not apply. `data-auth` lands on <html> from an inline
 * bootstrap in <head>, so the choice is made before first paint: no flash, and
 * nothing is built at runtime. The mobile sheet gets it for free, because
 * src/lib/mobile-menu.js carries `data-auth-only` onto the copies it makes.
 *
 * Hide-only, deliberately. Restating a display value here would have to guess
 * between the `.btn`'s inline-flex and the `hidden lg:inline-flex` ladder the
 * Demo button rides; leaving the shown side alone lets its own classes decide.
 *
 * `!important` because src/styles/<page>.css is frozen Tailwind that hides the
 * same elements at some breakpoints — this has to win in both directions, not
 * just on specificity. */

html:not([data-auth='in']) [data-auth-only='in'],
html[data-auth='in'] [data-auth-only='out'] {
  display: none !important;
}
