/* =============================================================================
   Bitsa AI marketing home
   =============================================================================
   Hand-written CSS, no framework. Everything is driven by the custom
   properties in :root, so new sections inherit the same scale and palette.
   Add sections below the hero rather than introducing new tokens.

   The palette, the gradient and the button shapes are deliberately identical
   to public/seo/seo.css, which styles the 31 static landing pages. Any change
   here should be mirrored there, or the site stops looking like one product.
   ========================================================================== */

/* -----------------------------------------------------------------------------
   Tokens
-------------------------------------------------------------------------------*/
:root {
  /* Light theme surfaces. Dark-theme values (still used by seo.css and the 31
     static landing pages) are kept alongside for reference:
     bg #040404, panel #0f0f14, panel-alt #16161d,
     border rgb(255 255 255 / 12%), border-strong rgb(255 255 255 / 22%). */
  --bg: #ffffff;
  --panel: #f5f5f7;
  --panel-alt: #eef0f2;
  --border: rgb(0 0 0 / 10%);
  --border-strong: rgb(0 0 0 / 18%);

  /* Text. Dark-theme values: text #f4f4f4, muted #a3a3ad, subtle #6f6f7a. */
  --text: #141416;
  --muted: #55555f;
  --subtle: #86868f;

  /* Brand. The gradient is the signature: it carries buttons, badges and
     highlighted words across the whole site. The flat purple/orange are for
     borders, icons and hover states where a gradient cannot be used. */
  --grad: linear-gradient(104deg, #b15af8 12%, #ff8951 94%);
  --purple: #b15af8;
  --orange: #ff8951;
  --purple-dim: rgb(177 90 248 / 12%);

  /* Type. Archivo is the site face, loaded from Google Fonts in <head>
     using the exact same URL as the landing pages so it is a cache hit.
     Domaine is the display face, self-hosted from fonts/ and used only by the
     main headings; body copy and the landing pages stay on Archivo. */
  --font: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: domaine, ui-sans-serif, system-ui, sans-serif,
    'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';

  /* Layout. --shell is the one place the content width is set: the header, the
     hero, every section and the footer all sit inside a .shell, so changing it
     here moves the whole page together. */
  --shell: 1300px;
  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --header-h: 64px;

  --radius: 0.5rem;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* -----------------------------------------------------------------------------
   Fonts
-------------------------------------------------------------------------------*/
/* Only the 400 weight is shipped: the headings that use Domaine are all set at
   400, so bold/medium cuts would be dead bytes. swap keeps the headline
   readable in Archivo while the file downloads. */
@font-face {
  font-family: domaine;
  src: url('fonts/domaine-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* -----------------------------------------------------------------------------
   Base
-------------------------------------------------------------------------------*/
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* The hero art bleeds past the viewport edge; without this the page gains a
     horizontal scrollbar on narrow screens. */
  overflow-x: hidden;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: inherit;
  text-decoration: none;
}

:where(a, button):focus-visible {
  outline: 2px solid var(--purple);
  outline-offset: 3px;
  border-radius: 4px;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.75rem 1rem;
  background: var(--purple);
  color: #fff;
  font-weight: 700;
}

.skip-link:focus {
  left: 0;
}

/* Available to a screen reader, absent from the page. Used where decorative
   artwork carries meaning that has to be stated somewhere: clip rather than
   display:none, because a hidden element is skipped by assistive tech too. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Gradient-filled text, the same treatment the landing pages use on headings.
   The background-clip fallback matters: if a browser drops the clip, the text
   would render transparent and vanish, so colour is set first. */
.grad-text {
  color: var(--purple);
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* -----------------------------------------------------------------------------
   Buttons: shape, weight and casing mirror .btn-grad / .btn-outline in seo.css
-------------------------------------------------------------------------------*/
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 0.85rem 1.6rem;
  border: 2px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font);
  font-size: 0.8rem;
  font-weight: 800;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
  cursor: pointer;
  transition: transform 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.btn--grad {
  background: var(--grad);
  color: #fff;
}

.btn--grad:hover {
  transform: translateY(-2px);
}

.btn--outline {
  border-color: var(--border);
  color: var(--text);
}

.btn--outline:hover {
  border-color: var(--purple);
  color: var(--purple);
}

.btn--sm {
  padding: 0.65rem 1.4rem;
}

.btn__arrow {
  transition: transform 0.2s ease;
}

.btn:hover .btn__arrow {
  transform: translateX(3px);
}

/* -----------------------------------------------------------------------------
   Header: matches .site-header in seo.css (64px, blurred near-black, hairline)
-------------------------------------------------------------------------------*/
.header {
  position: sticky;
  top: 0;
  z-index: 40;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: rgb(255 255 255 / 88%);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.header__logo img {
  height: 36px;
  width: auto;
  border-radius: 8px;
}

/* Seven items now, so the gap flexes: 2rem only once the header is wide
   enough for it, tightening to 1.25rem before the nav collapses. */
.nav {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 1.9vw, 2rem);
  font-size: 0.925rem;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

.nav a {
  position: relative;
  padding-block: 0.25rem;
  transition: color 0.18s var(--ease);
}

.nav a:hover {
  color: var(--text);
}

/* The current page reads as brand-coloured with the signature underline, so
   the header says where you are without a second visual language. */
.nav a[aria-current='page'] {
  color: var(--text);
}

.nav a[aria-current='page']::after {
  content: '';
  position: absolute;
  inset: auto 0 -0.35rem;
  height: 2px;
  border-radius: 2px;
  background: var(--grad);
}

.header__actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.header__login {
  font-size: 0.925rem;
  font-weight: 500;
  color: var(--muted);
}

.header__login:hover {
  color: var(--text);
}

/* Mobile menu button, hidden until the nav collapses. */
.nav-toggle {
  display: none;
  width: 40px;
  height: 40px;
  padding: 0;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  cursor: pointer;
}

/* -----------------------------------------------------------------------------
   Hero
   -----------------------------------------------------------------------------
   Copy only, centred in a single column. The artwork and the drifting orb layer
   that used to fill the right half were removed: the dashboard showcase
   directly below is now the first thing a visitor sees, and it is a real
   product shot, so a decorative render above it was competing with it rather
   than supporting it.

   That leaves the headline as the LCP element, which is why the Domaine preload
   in <head> matters more here than it did before.
-------------------------------------------------------------------------------*/
/* svh, not vh: on mobile Safari and Chrome vh is the tallest possible viewport,
   so with the address bar showing, 100vh puts the marquee below the fold and
   the hero looks broken until the visitor scrolls. svh is the smallest state,
   which is what is actually on screen at load. The vh line is the fallback for
   browsers that predate the unit.

   The header is sticky, so it takes real space in the flow above this section.
   A flat 100svh would therefore push the hero's own bottom edge, and with it
   the marquee, exactly one header height below the fold. Subtracting it is what
   makes "full screen" mean the screen.

   Four rows: two flexible spacers doing the optical centring, the copy between
   them, and the marquee on its own auto row so it keeps its natural height and
   sits on the bottom edge. Putting the marquee in a 1fr row instead lets a tall
   band overflow a short row, which is the same bug in a different place. */
.hero {
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto 1fr auto;
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
  padding-block: 1.5rem 0;
  overflow: hidden;
}

.hero__inner {
  grid-row: 2;
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* Ambient violet wash, the one piece of the old decorative orb layer worth
   keeping, so the hero is not flatly black behind the type. */
.hero__wash {
  position: absolute;
  inset: -30% -10% auto -10%;
  height: 120%;
  background: radial-gradient(closest-side, rgb(177 90 248 / 22%), transparent);
  pointer-events: none;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0 0 1.25rem;
  padding: 0.4rem 0.9rem 0.4rem 0.65rem;
  background: var(--purple-dim);
  border: 1px solid rgb(177 90 248 / 28%);
  border-radius: 999px;
  color: #7c3aed;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1;
}

.eyebrow__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--grad);
  box-shadow: 0 0 0 3px rgb(177 90 248 / 22%);
}

/* Domaine 400, run as large as the viewport allows. The vw term is what makes
   the headline hold the same proportion of the screen at every width instead
   of stranding it in the middle of a desktop monitor; the clamp floor keeps it
   readable on a phone and the ceiling stops it going comical past ~1900px.

   line-height below 1 is deliberate and safe here because the lines are split
   by hand: nothing wraps unexpectedly into a collision. */
.hero__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(3rem, 10.5vw, 9.5rem);
  font-weight: 400;
  line-height: 0.92;
  letter-spacing: -0.035em;
  color: rgb(20 20 22);
}

/* The clip window each line slides up out of. The padding is the reason it is
   not simply overflow:hidden on the line box: Domaine's descenders and the
   gradient fill both extend past the em box, and a tight clip shears them. The
   negative margin takes the added space back out of the layout. */
.hero__line {
  display: block;
  overflow: hidden;
  padding-block: 0.12em;
  margin-block: -0.12em;
}

.hero__line > span {
  display: block;
  will-change: transform;
}

/* Narrower than the headline above it on purpose: at the centred width the
   sub would run to a single very long line and stop reading as a paragraph. */
.hero__sub {
  margin: 1.25rem auto 0;
  max-width: 34rem;
  color: var(--muted);
  font-size: clamp(1rem, 1.15vw, 1.075rem);
  line-height: 1.65;
  text-wrap: pretty;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 2rem;
}

/* --- horizontal scroll track ----------------------------------------------
   Technique from the GSAP homepage's .home-animate__text-track: an over-wide
   strip of oversized type translated on X by a scrubbed ScrollTrigger. Here it
   also loops on its own so the hero is alive before a visitor scrolls at all.

   The band is full-bleed rather than inside .shell, because a marquee that
   stops at the content gutter reads as a component; one that runs off both
   edges reads as the page continuing past the screen. */
.hero__marquee {
  grid-row: 4;
  position: relative;
  z-index: 1;
  width: 100%;
  padding-block: 1.25rem;
  border-top: 1px solid var(--border);
  /* Fades both ends into the page so words enter and leave rather than being
     chopped off at the viewport edge. */
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
  overflow: hidden;
}

.hero__track {
  display: flex;
  width: max-content;
  will-change: transform;
}

/* The two groups are identical and sit flush, so translating the track by
   exactly -50% is a seamless reset. The gap has to be on the group's inside
   edge as padding, not a gap between the groups, or the seam gains a gutter
   the loop does not account for. */
.hero__group {
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 3.5vw, 3.25rem);
  padding-right: clamp(1.5rem, 3.5vw, 3.25rem);
  flex: none;
}

.hero__word {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 4.5vw, 4.25rem);
  line-height: 1;
  letter-spacing: -0.02em;
  white-space: nowrap;
  color: rgb(20 20 22 / 88%);
}

/* Every other word is outlined instead of filled. Alternating the two keeps a
   long strip of large type from turning into a wall, and the outline is what
   makes the filled words read as the emphasis. */
.hero__word--line {
  color: transparent;
  -webkit-text-stroke: 1px rgb(20 20 22 / 38%);
}

.hero__glyph {
  flex: none;
  width: clamp(1rem, 2vw, 1.75rem);
  height: clamp(1rem, 2vw, 1.75rem);
}

/* -----------------------------------------------------------------------------
   Entrance
   -----------------------------------------------------------------------------
   Everything above rests in its FINAL state, so the hero is complete with no
   JavaScript at all. These rules apply the hidden start positions, and they are
   gated on .js-anim, which the inline script in <head> sets and js/hero.js
   removes again if GSAP did not load. Without that gate a failed script would
   leave a permanently blank hero.

   Also gated on no-preference: a visitor who asked for reduced motion never
   gets the hidden state in the first place, so there is nothing to animate back
   and nothing that can strand itself invisible.
-------------------------------------------------------------------------------*/
@media (prefers-reduced-motion: no-preference) {
  /* Opacity, deliberately, not a transform. A CSS transform written as a
     percentage arrives at GSAP already resolved to pixels by getComputedStyle,
     which it then reads as a `y` offset; a later yPercent tween is fighting
     that leftover pixel value and the headline never comes back. Hiding the
     title instead leaves GSAP the sole author of the transform. */
  .js-anim .hero__title {
    opacity: 0;
  }

  .js-anim .eyebrow,
  .js-anim .hero__sub,
  .js-anim .hero__actions,
  .js-anim .hero__marquee {
    opacity: 0;
  }
}

/* -----------------------------------------------------------------------------
   Responsive
-------------------------------------------------------------------------------*/
/* The nav collapses on its own breakpoint, above the 900px one the rest of the
   page uses: seven links plus the logo and the two actions stop fitting at
   roughly 1080px, well before the hero needs to stack. */
@media (max-width: 1080px) {
  .nav,
  .header__login {
    display: none;
  }

  .nav-toggle {
    display: inline-flex;
  }

  /* Open state is toggled by the inline script in index.html. */
  .nav.is-open {
    position: absolute;
    inset: var(--header-h) 0 auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0.5rem var(--gutter) 1.25rem;
    background: var(--panel);
    border-bottom: 1px solid var(--border);
  }

  .nav.is-open a {
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--border);
    font-size: 1rem;
  }

  /* The underline is an inline-width cue; stacked full-width rows would draw
     it across the whole sheet, so the open menu marks the page with colour. */
  .nav.is-open a[aria-current='page']::after {
    display: none;
  }

  .nav.is-open a[aria-current='page'] {
    color: #7c3aed;
  }
}

@media (max-width: 900px) {
  .hero__wash {
    inset: -10% -20% auto -20%;
    height: 110%;
  }
}

@media (max-width: 560px) {
  /* Full-bleed CTAs, so the two buttons stack instead of sitting as a pair of
     narrow pills under a headline this large. The hero's own padding is left
     alone: it is a grid row, not a spacer, and overriding it collapses the
     centring. */
  .hero__actions .btn {
    flex: 1 1 100%;
  }

  /* The band gives back vertical space on a phone, where the headline and CTAs
     need every pixel of the one visible screen. */
  .hero__marquee {
    padding-block: 0.9rem;
  }
}

/* =============================================================================
   Below the hero
   =============================================================================
   Everything past this point is the scrolling body of the page. It reuses the
   :root tokens above without adding new ones, so the sections and the hero
   cannot drift apart. Two shapes do all the work: `.section` for vertical
   rhythm and `.grid` for the card rows.
   ========================================================================== */

/* -----------------------------------------------------------------------------
   Section scaffolding
-------------------------------------------------------------------------------*/
.section {
  position: relative;
  padding-block: clamp(3.25rem, 7vh, 5.25rem);
}

/* The header is sticky, so an in-page jump (#pricing, #faq) would otherwise
   land with the heading tucked underneath it. */
.section[id] {
  scroll-margin-top: calc(var(--header-h) + 1.5rem);
}

/* Alternating band. Hairlines rather than a hard colour change, because against a
   near-black page a filled panel the full width reads as a seam. */
.section--alt {
  background: var(--panel);
  border-block: 1px solid var(--border);
}

.section__head {
  max-width: 44rem;
  margin: 0 auto clamp(2rem, 4vw, 3rem);
  text-align: center;
}

.section__eyebrow {
  display: block;
  margin: 0 0 0.75rem;
  color: var(--orange);
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  line-height: 1;
}

/* Same Domaine 400 face and colour as the hero. The size stays on the section
   scale: 96px belongs to the one headline, not to eight of them. */
.section__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.65rem, 3.1vw, 2.35rem);
  font-weight: 400;
  line-height: 1.18;
  letter-spacing: -0.025em;
  color: rgb(20 20 22);
  text-wrap: balance;
}

.section__lead {
  margin: 0.9rem 0 0;
  color: var(--muted);
  font-size: clamp(0.975rem, 1.1vw, 1.05rem);
  text-wrap: pretty;
}

/* auto-fit rather than a fixed column count: the rows below hold 3, 4 and 6
   items and all of them should reflow on the same breakpoints. */
.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}

/* Every card is a flex column so anything that must sit on the bottom edge
   (a CTA, a "Explore" link, an attribution) can claim the leftover space with
   margin-top:auto. Grid already stretches cards in a row to a common height,
   so that leftover space is exactly what makes those bottom rows line up
   across cards whose copy runs to different lengths. */
.card {
  display: flex;
  flex-direction: column;
  padding: 1.6rem;
  background: var(--panel-alt);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) * 2);
  transition: border-color 0.25s ease, transform 0.25s var(--ease);
}

.card:hover {
  border-color: var(--border-strong);
  transform: translateY(-3px);
}

/* On the alternating band the cards would sit panel-on-panel and disappear;
   dropping them to --bg keeps the same amount of contrast, inverted. */
.section--alt .card {
  background: var(--bg);
}

.card__title {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
}

.card__text {
  margin: 0;
  color: var(--muted);
  font-size: 0.925rem;
  line-height: 1.6;
}

/* Icon plate. The artwork in img/icons/ already carries its own glossy black
   squircle and neon rim, so this element only positions it: no fill, no border
   and no radius, or they would double up around the rendered tile. */
/* align-self keeps the plate square: as a flex item it would otherwise stretch
   to the card's full width. flex:none stops it being squashed when the copy
   above it is long. */
.card__icon {
  display: inline-flex;
  align-self: flex-start;
  flex: none;
  width: 56px;
  height: 56px;
  margin-bottom: 1rem;
}

/* The files are 128px so they stay sharp on a 2x display at this 56px box.
   The glow in the artwork bleeds to transparent at the edges, so it blends
   into the card rather than sitting on a visible square. */
.card__icon img {
  display: block;
  width: 100%;
  height: 100%;
  transition: transform 0.35s var(--ease);
}

/* Matches the lift the card itself gets on hover, so the tile reads as part of
   the same surface instead of a sticker pinned to it. */
.card:hover .card__icon img {
  transform: translateY(-2px) scale(1.04);
}

@media (prefers-reduced-motion: reduce) {
  .card__icon img {
    transition: none;
  }
}

/* -----------------------------------------------------------------------------
   Product showcase
   -----------------------------------------------------------------------------
   A browser frame around the real dashboard capture. The screenshot is dark on
   a dark page, so the separation comes from a hairline that catches the brand
   gradient rather than from a filled panel behind it.
-------------------------------------------------------------------------------*/
.showcase {
  overflow: hidden; /* the glow is wider than the shell */
}

/* Two brand-coloured pools sitting behind the frame. One element with two
   radial gradients rather than two elements, so it costs a single paint. */
.showcase__glow {
  position: absolute;
  inset: 18% -10% 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(42% 55% at 28% 30%, rgb(177 90 248 / 26%), transparent 70%),
    radial-gradient(38% 50% at 76% 42%, rgb(255 137 81 / 16%), transparent 70%);
  filter: blur(28px);
}

.showcase .shell {
  position: relative;
  z-index: 1;
}

/* The gradient lives on the border box and the frame's own background is
   painted over the padding box, which leaves the gradient showing as a 1px
   edge. A plain border cannot hold a gradient. */
/* The two custom properties are driven from the scroll handler in index.html:
   the frame enters pitched back and a little oversized, then straightens as it
   travels up the viewport. They default to flat, so with scripting off or
   reduced motion on, the frame simply sits square and nothing else is needed.
   The perspective is on the element itself rather than an extra wrapper, which
   is why the rotation reads as depth without a parent to hold it. */
.showcase__frame {
  --tilt: 0deg;
  --zoom: 1;

  position: relative;
  max-width: 1220px;
  margin-inline: auto;
  padding: 1px;
  border-radius: 14px;
  background: linear-gradient(160deg, rgb(177 90 248 / 55%), rgb(255 137 81 / 22%) 45%, var(--border) 75%);
  box-shadow: 0 30px 80px -30px rgb(0 0 0 / 20%), 0 0 60px -20px rgb(177 90 248 / 35%);
  transform: perspective(1600px) rotateX(var(--tilt)) scale(var(--zoom));
  transform-origin: center 40%;
}

/* Fake browser chrome. Sells the screenshot as a real product surface and, more
   practically, gives the top edge something to be, since the capture starts
   flush against the app's own header. */
.showcase__bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.85rem;
  border-radius: 13px 13px 0 0;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
}

.showcase__dots {
  display: flex;
  flex: none;
  gap: 0.375rem;
}

.showcase__dots i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgb(0 0 0 / 16%);
}

.showcase__url {
  flex: 1;
  min-width: 0;
  padding: 0.2rem 0.7rem;
  border-radius: 999px;
  background: rgb(0 0 0 / 5%);
  color: var(--subtle);
  font-size: 0.75rem;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The capture is 2.1:1 and very tall once it spans the shell, so the lower
   third is clipped and faded out. The visitor gets the metrics row and the
   chart, which is the part worth showing, and the page keeps moving. */
.showcase__shot {
  position: relative;
  border-radius: 0 0 13px 13px;
  overflow: hidden;
  background: var(--bg);
}

.showcase__shot img {
  width: 100%;
  /* Anything past this is legend and whitespace in the capture. */
  max-height: 560px;
  object-fit: cover;
  object-position: top center;
}

/* Dissolves the cut edge instead of ending on a hard line. Purely decorative,
   so it sits in a pseudo-element and never blocks a click. */
.showcase__shot::after {
  content: '';
  position: absolute;
  inset: auto 0 0;
  height: 140px;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--bg));
}

/* The one claim the screenshot cannot make, because it happens on a phone.
   Overlapping the frame's bottom edge marks it as an annotation rather than
   part of the dashboard UI. */
.showcase__alert {
  position: absolute;
  left: 50%;
  bottom: -1.35rem;
  z-index: 2;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  margin: 0;
  padding: 0.65rem 1.15rem;
  width: max-content;
  max-width: calc(100% - 1.5rem);
  border: 1px solid rgb(177 90 248 / 35%);
  border-radius: 999px;
  background: rgb(15 15 20 / 94%);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 16px 40px -16px rgb(0 0 0 / 45%), 0 0 24px -6px rgb(177 90 248 / 25%);
  /* This chip keeps its dark background regardless of page theme (see
     background above), so its text stays fixed light rather than following
     --text, which is now dark for the light theme. */
  color: #f4f4f4;
  font-size: clamp(0.74rem, 2.2vw, 0.82rem);
  font-weight: 600;
  text-align: center;
  white-space: nowrap;
  box-sizing: border-box;
}

.showcase__alert-text {
  display: inline-block;
}

.showcase__pulse {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #34d399;
  box-shadow: 0 0 0 0 rgb(52 211 153 / 70%);
  animation: showcase-pulse 2.4s ease-out infinite;
}

@keyframes showcase-pulse {
  70% {
    box-shadow: 0 0 0 9px rgb(52 211 153 / 0%);
  }
  100% {
    box-shadow: 0 0 0 0 rgb(52 211 153 / 0%);
  }
}

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

@media (max-width: 640px) {
  .showcase__alert {
    bottom: -1.2rem;
    padding: 0.55rem 0.95rem;
    gap: 0.5rem;
  }
}

@media (max-width: 480px) {
  .showcase__alert {
    bottom: -1.25rem;
    padding: 0.5rem 0.85rem;
    white-space: normal;
    text-align: center;
    border-radius: 14px;
    line-height: 1.35;
    width: auto;
    max-width: calc(100% - 1.25rem);
  }

  .showcase__alert-text {
    display: inline;
  }

  .showcase__shot img {
    max-height: 300px;
  }
}

/* -----------------------------------------------------------------------------
   Trust / integration strip
   -----------------------------------------------------------------------------
   Text wordmarks, not logo files: the real marks are trademarked, and six
   image requests immediately under the LCP element would compete with it.
-------------------------------------------------------------------------------*/
.strip {
  padding-block: 1.75rem;
  border-block: 1px solid var(--border);
}

.strip__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1rem clamp(1.5rem, 4vw, 3rem);
  text-align: center;
}

.strip__label {
  margin: 0;
  color: var(--subtle);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  line-height: 1.4;
}

.strip__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem clamp(1.25rem, 3vw, 2.25rem);
  margin: 0;
  padding: 0;
  list-style: none;
}

.strip__list li {
  color: var(--muted);
  font-size: 0.975rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  transition: color 0.2s ease;
}

.strip__inner:hover .strip__list li {
  color: var(--text);
}

/* -----------------------------------------------------------------------------
   How it works: numbered steps
-------------------------------------------------------------------------------*/
.steps {
  counter-reset: step;
  margin: 0;
  padding: 0;
  list-style: none;
}

.steps > li {
  counter-increment: step;
  position: relative;
  padding: 1.75rem 1.6rem 1.6rem;
  background: var(--panel-alt);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) * 2);
}

.section--alt .steps > li {
  background: var(--bg);
}

/* The number is generated, so the markup stays a plain ordered list and the
   step order survives with CSS off. */
.steps > li::before {
  content: counter(step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin-bottom: 0.9rem;
  background: var(--grad);
  border-radius: 50%;
  color: #fff;
  font-size: 0.9rem;
  font-weight: 900;
  line-height: 1;
}

/* Connector between the cards on desktop only, because once the row wraps to a
   single column it would point sideways into empty space. */
@media (min-width: 861px) {
  .steps > li:not(:last-child)::after {
    content: '';
    position: absolute;
    top: 2.65rem;
    right: calc(-1rem - 1px);
    width: 1rem;
    height: 1px;
    background: var(--border-strong);
  }
}

/* -----------------------------------------------------------------------------
   Live demo
   -----------------------------------------------------------------------------
   A scripted preview of the widget, not the widget itself: the real embed
   needs a client id and a network round trip, and this sits high enough on
   the page that it must paint instantly. Swap in the real snippet when a
   public demo bot exists. See the note in index.html.
-------------------------------------------------------------------------------*/
.demo {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(1.75rem, 4vw, 3.5rem);
}

.demo__points {
  margin: 1.5rem 0 0;
  padding: 0;
  list-style: none;
}

.demo__points li {
  position: relative;
  margin-bottom: 0.75rem;
  padding-left: 1.85rem;
  color: var(--muted);
  font-size: 0.95rem;
}

/* The mask keeps only the tick's stroke, so what shows through is the brand
   gradient in the shape of a checkmark: one element, no glyph, and it stays
   crisp at any zoom. */
.demo__points li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.45rem;
  width: 18px;
  height: 18px;
  background: var(--grad);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath d='M4 9.4l3 3L14 5.6' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 100% no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath d='M4 9.4l3 3L14 5.6' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 100% no-repeat;
}

.demo__preview-container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  position: relative;
}

.demo__preview-img,
.demo__preview-iframe {
  width: 100%;
  max-width: 380px;
  height: 550px;
  border-radius: calc(var(--radius) * 2.5);
  box-shadow: 0 24px 60px rgb(0 0 0 / 18%), 0 0 40px rgb(177 90 248 / 15%);
  border: 1px solid var(--border-strong);
  background: var(--panel-alt);
  transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease);
}

.demo__preview-img {
  height: auto;
  object-fit: contain;
}

.demo__preview-img:hover,
.demo__preview-iframe:hover {
  transform: translateY(-4px);
  box-shadow: 0 32px 70px rgb(0 0 0 / 22%), 0 0 50px rgb(177 90 248 / 25%);
}

.section--alt .chat {
  background: var(--bg);
}

.chat__bar {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.85rem 1.1rem;
  background: var(--grad);
  color: #fff;
}

.chat__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: rgb(255 255 255 / 22%);
  border-radius: 50%;
  font-size: 0.8rem;
  font-weight: 900;
  line-height: 1;
}

.chat__name {
  font-size: 0.9rem;
  font-weight: 800;
  line-height: 1.2;
}

.chat__status {
  display: block;
  font-size: 0.7rem;
  font-weight: 600;
  opacity: 0.85;
}

/* Fixed height so the card does not jump as messages are appended. */
.chat__log {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  height: 19rem;
  padding: 1.1rem;
  overflow-y: auto;
  scrollbar-width: thin;
}

.chat__msg {
  max-width: 84%;
  padding: 0.65rem 0.9rem;
  border-radius: calc(var(--radius) * 2);
  font-size: 0.9rem;
  line-height: 1.55;
}

.chat__msg--bot {
  align-self: flex-start;
  background: rgb(0 0 0 / 5%);
  border-bottom-left-radius: 4px;
  color: var(--text);
}

.chat__msg--user {
  align-self: flex-end;
  background: var(--grad);
  border-bottom-right-radius: 4px;
  color: #fff;
  font-weight: 500;
}

.chat__msg--lead {
  align-self: flex-start;
  width: 100%;
  max-width: 100%;
  background: var(--purple-dim);
  border: 1px dashed rgb(177 90 248 / 45%);
  color: #7c3aed;
  font-size: 0.8rem;
  font-weight: 700;
}

/* Typing indicator: three dots matching the hero entrance stagger. */
.chat__typing {
  display: flex;
  align-self: flex-start;
  gap: 4px;
  padding: 0.8rem 0.9rem;
  background: rgb(0 0 0 / 5%);
  border-radius: calc(var(--radius) * 2);
  border-bottom-left-radius: 4px;
}

.chat__typing span {
  width: 6px;
  height: 6px;
  background: var(--muted);
  border-radius: 50%;
  animation: blink 1.2s infinite;
}

.chat__typing span:nth-child(2) { animation-delay: 0.2s; }
.chat__typing span:nth-child(3) { animation-delay: 0.4s; }

@keyframes blink {
  0%, 60%, 100% { opacity: 0.25; }
  30% { opacity: 1; }
}

.chat__prompts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  padding: 0.9rem 1.1rem 1.1rem;
  border-top: 1px solid var(--border);
}

.chat__prompt {
  padding: 0.45rem 0.85rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--muted);
  font-family: var(--font);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.chat__prompt:hover:not(:disabled) {
  border-color: var(--purple);
  color: var(--purple);
}

.chat__prompt:disabled {
  opacity: 0.4;
  cursor: default;
}

/* -----------------------------------------------------------------------------
   Use cases: whole card is the link
-------------------------------------------------------------------------------*/
.usecase:hover {
  border-color: var(--purple);
}

/* auto pushes the link to the bottom of the card, so the "Explore" row sits
   on one line across the whole grid however long each description runs.
   align-self stops the inline-flex link stretching the full card width, which
   would make the whole row a click target instead of just the label. */
.usecase__more {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: 0.35rem;
  margin-top: auto;
  padding-top: 0.9rem;
  color: var(--purple);
  font-size: 0.8rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.usecase:hover .usecase__more svg {
  transform: translateX(3px);
}

.usecase__more svg {
  transition: transform 0.2s ease;
}

/* -----------------------------------------------------------------------------
   Pricing
-------------------------------------------------------------------------------*/
/* No align-items override here on purpose. Grid's default `stretch` is what
   makes all four plans the same height regardless of how many features each
   lists, which is the whole reason the CTAs can line up. */

/* The featured plan is lifted with a border and a wash rather than a scale
   transform, which would blur its text on non-retina screens. */
.plan--featured {
  border-color: var(--purple);
  background: linear-gradient(180deg, rgb(177 90 248 / 10%), transparent 55%), var(--panel-alt);
}

.section--alt .plan--featured {
  background: linear-gradient(180deg, rgb(177 90 248 / 10%), transparent 55%), var(--bg);
}

.plan__badge {
  align-self: flex-start;
  margin-bottom: 0.75rem;
  padding: 0.25rem 0.7rem;
  background: var(--grad);
  border-radius: 4px;
  color: #fff;
  font-size: 0.65rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  line-height: 1.6;
}

.plan__name {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}

.plan__price {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  margin: 0.5rem 0 0.25rem;
  font-size: 2rem;
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1.1;
}

.plan__period {
  color: var(--subtle);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0;
}

.plan__note {
  margin: 0 0 1.25rem;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

/* Exactly one auto margin per card, and it lives on .plan__cta below. A
   second one here would make the two items share the free space instead of
   the button taking all of it, and the CTAs would drift apart again. */
.plan__features {
  margin: 0 0 1.5rem;
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--border);
  list-style: none;
}

.plan__features li {
  position: relative;
  margin-bottom: 0.55rem;
  padding-left: 1.4rem;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.5;
}

.plan__features li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.55rem;
  width: 5px;
  height: 5px;
  background: var(--purple);
  border-radius: 50%;
}

/* margin-top:auto pins every CTA to the bottom edge, so buttons line up
   across cards even when the feature lists differ in length. */
.plan__cta {
  margin-top: auto;
  width: 100%;
}

.plans__footnote {
  margin: 1.75rem 0 0;
  color: var(--subtle);
  font-size: 0.875rem;
  text-align: center;
}

.plans__footnote a {
  color: var(--purple);
  font-weight: 700;
}

.plans__footnote a:hover {
  text-decoration: underline;
}

/* -----------------------------------------------------------------------------
   Testimonials
-------------------------------------------------------------------------------*/
/* Column layout is inherited from .card; this only resets the default
   <figure> margin so the card edges stay flush. */
.quote {
  margin: 0;
}

.quote__stars {
  margin-bottom: 0.9rem;
  color: var(--orange);
  font-size: 0.9rem;
  letter-spacing: 0.15em;
  line-height: 1;
}

.quote blockquote {
  margin: 0 0 1.25rem;
  color: var(--text);
  font-size: 0.975rem;
  line-height: 1.65;
}

.quote figcaption {
  margin-top: auto;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.85rem;
}

.quote figcaption strong {
  display: block;
  color: var(--text);
  font-weight: 700;
}

/* -----------------------------------------------------------------------------
   FAQ: native <details>, so it opens with JavaScript disabled and Google can
   read every answer in the initial HTML.
-------------------------------------------------------------------------------*/
.faq {
  max-width: 50rem;
  margin-inline: auto;
}

.faq details {
  margin-bottom: 0.75rem;
  background: var(--panel-alt);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) * 1.5);
}

.section--alt .faq details {
  background: var(--bg);
}

.faq details[open] {
  border-color: var(--border-strong);
}

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.05rem 1.3rem;
  font-size: 0.975rem;
  font-weight: 700;
  line-height: 1.45;
  cursor: pointer;
  /* Both are required: Safari uses the -webkit- pseudo-element, everything
     else honours list-style. */
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: '';
  flex: none;
  width: 24px;
  height: 24px;
  background: var(--grad);
  border-radius: 50%;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 8v8M8 12h8' stroke='%23000' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 8v8M8 12h8' stroke='%23000' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% no-repeat;
  transition: transform 0.25s var(--ease);
}

/* Rotating the plus into a minus keeps one mask instead of two. */
.faq details[open] summary::after {
  transform: rotate(45deg);
}

.faq__body {
  margin: 0;
  padding: 0 1.3rem 1.2rem;
  color: var(--muted);
  font-size: 0.925rem;
  line-height: 1.65;
}

.faq__body a {
  color: var(--purple);
  font-weight: 600;
}

.faq__body a:hover {
  text-decoration: underline;
}

/* -----------------------------------------------------------------------------
   Closing CTA
-------------------------------------------------------------------------------*/
.cta {
  position: relative;
  padding: clamp(2.75rem, 6vw, 4.25rem) clamp(1.5rem, 5vw, 3.5rem);
  background: var(--panel-alt);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) * 3);
  text-align: center;
  overflow: hidden;
}

/* Same violet bloom as the hero, so the page opens and closes on the same
   note. Sits behind the copy via a stacking context, not a z-index war. */
.cta::before {
  content: '';
  position: absolute;
  inset: -60% -10% auto;
  height: 160%;
  background: radial-gradient(closest-side, rgb(177 90 248 / 18%), transparent);
  pointer-events: none;
}

.cta > * {
  position: relative;
}

.cta__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 3.4vw, 2.5rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.025em;
  color: rgb(20 20 22);
  text-wrap: balance;
}

.cta__text {
  max-width: 34rem;
  margin: 1rem auto 0;
  color: var(--muted);
  text-wrap: pretty;
}

.cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
  margin-top: 2rem;
}

/* -----------------------------------------------------------------------------
   Footer
-------------------------------------------------------------------------------*/
.footer {
  border-top: 1px solid var(--border);
  padding-block: clamp(2.5rem, 5vw, 3.5rem) 1.5rem;
}

.footer__cols {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
  gap: 2rem clamp(1.5rem, 3vw, 3rem);
}

.footer__brand img {
  height: 34px;
  width: auto;
  border-radius: 8px;
}

.footer__tagline {
  margin: 1rem 0 0;
  max-width: 22rem;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.6;
}

.footer__contact {
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-size: 0.875rem;
}

.footer__contact a:hover {
  color: var(--text);
}

.footer__heading {
  margin: 0 0 0.9rem;
  color: var(--text);
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

.footer__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.55rem;
}

.footer__list a {
  color: var(--muted);
  font-size: 0.875rem;
}

.footer__list a:hover {
  color: var(--text);
}

/* Wide link block for the 10 landing pages, because a grid column each would make
   them unreadably narrow, so they run as a wrapping row instead. */
.footer__solutions {
  margin-top: 2.5rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border);
}

.footer__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem 1.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer__tags a {
  color: var(--subtle);
  font-size: 0.825rem;
}

.footer__tags a:hover {
  color: var(--text);
}

.footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 2rem;
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  color: var(--subtle);
  font-size: 0.825rem;
}

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}

.footer__legal a:hover {
  color: var(--text);
}

.footer__social {
  display: flex;
  gap: 0.6rem;
}

.footer__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--muted);
  transition: border-color 0.2s ease, color 0.2s ease;
}

.footer__social a:hover {
  border-color: var(--purple);
  color: var(--purple);
}

/* -----------------------------------------------------------------------------
   Scroll reveal: progressive enhancement. The `is-reveal` class is added by
   the script in index.html, so with JavaScript off nothing is ever hidden.
-------------------------------------------------------------------------------*/
@media (prefers-reduced-motion: no-preference) {
  .is-reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
  }

  .is-reveal.is-visible {
    opacity: 1;
    transform: none;
  }
}

/* -----------------------------------------------------------------------------
   Responsive: below-hero sections
-------------------------------------------------------------------------------*/
@media (max-width: 900px) {
  .demo {
    grid-template-columns: 1fr;
  }

  .demo__copy {
    max-width: 38rem;
  }

  .demo__preview-container {
    margin-top: 1rem;
  }

  .footer__cols {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  .footer__brand {
    grid-column: 1 / -1;
  }
}

@media (max-width: 560px) {
  .strip__inner {
    gap: 0.9rem;
  }

  .cta__actions .btn {
    flex: 1 1 100%;
  }

  .demo__preview-img,
  .demo__preview-iframe {
    max-width: 100%;
    height: 500px;
    border-radius: calc(var(--radius) * 2);
  }

  .footer__cols {
    grid-template-columns: 1fr;
  }
}
