/* Vayu docs site -- same Catppuccin Mocha palette as the rest of this
   desktop (rmpc's theme, waybar, wofi, mako all already use it), so the
   site reads as part of the same thing it's documenting, not a
   generic GitHub-Pages-theme wrapper around it. Dark by default and
   only -- no light variant, matches the desktop itself. */

:root {
  /* One width for the whole site: the navbar pill, the hero card, the
     homepage sections and the docs layout all span exactly this, so their
     edges line up on every page. Containers add their own padding on top
     (calc below), keeping the visible edges at this width. */
  --site-width: 1480px;
  --base: #1e1e2e;
  --crust: #11111b;
  --mantle: #181825;
  --surface0: #313244;
  --text: #cdd6f4;
  --subtext: #a6adc8;
  --mauve: #cba6f7;
  --sky: #89dceb;
  --maroon: #eba0ac;
  --peach: #fab387;
  --green: #a6e3a1;
  --lavender: #b4befe;
  --rosewater: #f5e0dc;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  color: var(--text);
  font-family: -apple-system, "Segoe UI", Roboto, "Inter", sans-serif;
  line-height: 1.6;
}
/* Deep charcoal-to-amethyst page background, plus a very faint grain --
   a single tiled SVG feTurbulence noise filter baked in as a data URI
   (no external image request, no font/asset dependency, consistent
   with the rest of this site's "no external loading" approach), kept
   subtle (2% opacity) so it reads as texture, not visible static. */
/* scroll-behavior: smooth -- direct feedback: anchor-link jumps (nav
   -> #features etc.) should transition, not snap instantly. Applies
   site-wide, to every same-page hash link. */
html { scroll-behavior: smooth; }
body {
  background-color: var(--crust);
  background-image:
    radial-gradient(ellipse 80% 60% at 20% -10%, rgba(203, 166, 247, 0.10), transparent 60%),
    radial-gradient(ellipse 70% 50% at 100% 20%, rgba(137, 220, 235, 0.06), transparent 55%),
    linear-gradient(180deg, #14121c 0%, var(--crust) 45%, #17111f 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
  /* Deliberately NOT background-attachment: fixed -- direct bug
     report: a visible white flash when jumping to #features. A fixed
     background forces the browser to repaint this whole (expensive:
     three gradients + a tiled SVG noise filter) background on every
     scroll position change instead of letting it composite/scroll
     with the page like normal content -- exactly the kind of thing
     that flashes/stutters during a large, fast scroll jump. The
     visual difference from a scrolling background is imperceptible
     here (one continuous gradient, not an obviously "moving" pattern)
     so there's no real tradeoff, just a real fix. */
}
/* Real smooth cross-page transitions (nav -> Architecture/Keybindings/
   Changelog etc., actual different HTML documents, not same-page
   anchors) -- the Cross-Document View Transitions API. One CSS
   at-rule, no JS: browsers that support it (recent Chrome/Edge) cross-
   fade between pages instead of an instant hard-cut; everywhere else
   this is simply ignored and navigation works exactly as it already
   did, zero risk of a broken experience on unsupported browsers. */
@view-transition {
  navigation: auto;
}

a { color: var(--mauve); text-decoration: none; }
a:hover { text-decoration: underline; }

code {
  font-family: "JetBrainsMono Nerd Font", "Fira Code", monospace;
  background: var(--mantle);
  color: var(--rosewater);
  padding: 0.15em 0.4em;
  border-radius: 4px;
  font-size: 0.9em;
}

/* --- Top nav --- */
/* Floating pill nav, no hard border-bottom -- same rounded-module,
   no-hairline language waybar already uses on the real desktop
   (waybar/style.css's own #workspaces/module pills), so the site's
   header reads like it belongs to what it documents instead of a
   generic docs-site topbar. Separation from the page comes from its
   own shadow + a touch of vertical air, not a line. */
.topnav-wrap {
  position: sticky;
  top: 0;
  z-index: 10;
  padding: 1rem 1.5rem 0;
  background: linear-gradient(to bottom, var(--crust) 60%, transparent);
}
.topnav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: var(--site-width);
  margin: 0 auto;
  padding: 0.65rem 1.25rem;
  border-radius: 999px;
  background: rgba(24, 24, 37, 0.85);
  border: 1px solid rgba(203, 166, 247, 0.14);
  backdrop-filter: blur(10px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}
/* Text-only wordmark, no icon -- direct feedback against the emoji
   ("put some good logo... or just drop that logo and lets use the
   text only"). Echoes the hero H1's own serif + gradient treatment at
   nav scale instead of plain bold sans, so it still reads as a real
   mark rather than just another nav link. text-decoration explicitly
   reset -- it was inheriting the site-wide `a:hover { underline }`
   rule, a real reported bug, not wanted on a wordmark. */
.topnav .brand {
  font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
  font-size: 1.3rem;
  font-weight: 500;
  background: linear-gradient(135deg, var(--mauve), var(--text));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.topnav .brand:hover { text-decoration: none; }
/* The splash logo (splash/logo.svg), still and small; the hero animates
   it. Strokes are thicker than the original's so the hairlines survive
   at this size. */
.topnav .brand { display: inline-flex; align-items: center; }
.topnav .brand-logo { display: block; height: 1.9rem; width: auto; overflow: visible; }
.topnav .navlinks a {
  margin-left: 0.35rem;
  color: var(--subtext);
  font-size: 0.9rem;
  padding: 0.4rem 0.75rem;
  border-radius: 999px;
  transition: background 0.15s ease, color 0.15s ease;
}
.topnav .navlinks a:hover { color: var(--mauve); background: var(--mantle); text-decoration: none; }

/* --- Hero: left = name/headline, right = image ---
   Direct follow-up feedback: the scroll-pin from the previous pass felt
   sluggish, and tying page scroll to the screenshot gallery at all was
   the wrong call -- "let the scroll behave normally... if a user want
   to see the screenshots then he can just do something there" (their
   own words). Page scroll is now completely untouched by any of this
   -- .hero is a normal, static-flow section, nothing pinned, nothing
   reading scrollY. The screenshot gallery (assets/carousel.js) is
   fully self-contained instead: autoplay on a timer, pauses on hover,
   and the dots (already built for the old version) are real click
   targets to jump straight to a screenshot -- the "do something there"
   the feedback asked for. */
/* min-height + flex-centering, not just relying on .hero-card's own
   72vh floor -- reported directly, with a screenshot: the next
   section's own heading ("What's in it") peeked up at the very bottom
   edge on a real 1080p viewport, not a clean first screen. The gap was
   the sticky nav (.topnav-wrap) above -- it occupies real space in
   normal flow (position: sticky, not fixed/pulled out of flow), so the
   hero's own content has less room below it than a plain 100vh
   assumption would give. `calc(100vh - 6rem)` reserves that space back
   (6rem is a deliberately generous estimate of the real nav height, not
   a precise measurement -- overshooting here just centers the card
   with a little extra breathing room top/bottom, undershooting is the
   actual bug being fixed, so the safe direction to round is up).
   `display:flex; align-items:center` lets .hero-card's own 72vh
   min-height center within whatever space that leaves, rather than
   needing the two numbers to match exactly. */
.hero {
  position: relative;
  max-width: calc(var(--site-width) + 4rem); /* + its 2rem side padding */
  margin: 0 auto;
  padding: 3rem 2rem 5rem;
  min-height: calc(100vh - 6rem);
  display: flex;
  align-items: center;
}
/* Ambient glow behind the whole card -- two large, low-opacity blurred
   color blobs (Mauve/Sky), sitting behind the card via z-index. */
.hero-glow {
  position: absolute;
  inset: -12% -8% auto -8%;
  height: 130%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(circle at 20% 25%, rgba(203, 166, 247, 0.20), transparent 50%),
    radial-gradient(circle at 88% 55%, rgba(137, 220, 235, 0.13), transparent 50%);
  filter: blur(10px);
}

/* The main glassmorphism card -- everything (nav-adjacent brand feel
   aside) lives inside one large frosted panel floating on the page's
   own dark gradient background, per direct request for a "premium
   Glassmorphism-style" homepage: real background blur, a soft inner
   highlight, a deep outer shadow, and the whole thing sitting on the
   ambient glow above so light appears to spill out from behind it. */
.hero-card {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 4rem;
  /* width: 100% -- real regression from making .hero a flex container
     above: .hero-card was relying on plain block-element behavior
     (fill the parent's width) for this the whole time, never an
     explicit rule of its own. As a flex item on .hero's row axis, its
     default sizing is shrink-to-fit-content instead, which is exactly
     what narrowed it -- confirmed directly, reported back immediately
     once seen live. min-height alone controls the OTHER axis (already
     correct); this is the one that was actually missing. */
  width: 100%;
  min-height: 72vh;
  padding: 3.5rem;
  border-radius: 28px;
  background: linear-gradient(155deg, rgba(203, 166, 247, 0.09), rgba(24, 24, 37, 0.6) 45%);
  backdrop-filter: blur(28px);
  -webkit-backdrop-filter: blur(28px);
  border: 1px solid rgba(203, 166, 247, 0.18);
  box-shadow:
    0 40px 100px rgba(0, 0, 0, 0.55),
    0 0 120px rgba(203, 166, 247, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  animation: hero-rise 0.7s ease both;
}
.hero-text {
  flex: 1 1 440px;
  min-width: 300px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.hero-image {
  position: relative;
  flex: 0 1 420px;
  min-width: 260px;
  display: flex;
  flex-direction: column;
  align-items: center;
  perspective: 1400px;
}
/* Extra, tighter glow specifically behind the image stack -- "intense,
   diffused backlighting spilling from behind the layered stack",
   distinct from the card's own broader ambient glow above. */
.hero-image::before {
  content: "";
  position: absolute;
  inset: -15%;
  z-index: 0;
  background: radial-gradient(circle at 60% 45%, rgba(203, 166, 247, 0.4), transparent 62%);
  filter: blur(30px);
  pointer-events: none;
}
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-card { animation: none; }
  html { scroll-behavior: auto; }
}

.slide-stack {
  position: relative;
  z-index: 1;
  width: 100%;
  aspect-ratio: 4 / 3;
}
.slide-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Hints the browser to promote these to their own compositor layers
     ahead of time, not at the moment the first transition starts --
     the usual cause of a visible stutter/flash on a *first* animation
     specifically, since layer promotion itself has a one-time cost. */
  will-change: transform, opacity;
  border-radius: 16px;
  border: 1px solid rgba(203, 166, 247, 0.22);
  box-shadow: 0 25px 70px rgba(0, 0, 0, 0.6);
  /* Slower, gentler than a typical UI transition on purpose -- direct
     feedback that the previous 0.5s snap felt rushed. A longer
     duration paired with an eased-out-both-ends curve (rather than
     the sharper deceleration curve before) reads as a smooth,
     deliberate glide instead of a UI element "jumping" into place.
     Only transform + opacity -- both composite-only properties every
     browser can animate on the GPU without repainting. `filter`
     (brightness, used here before for the "dimmer further back" look)
     is NOT compositor-only -- animating it forces a real repaint on
     every frame, which is exactly the kind of thing that shows up as
     a visible flash/stutter, a real direct bug report. Dropped it
     entirely and leaned on lower opacity alone for the same "further
     back reads as dimmer" cue instead. */
  transition: transform 0.85s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.85s ease;
}
/* Real layered card-deck depth via a data-offset attribute
   (assets/carousel.js) -- 0 is the active/front card at full size;
   1 and 2 peek out progressively smaller and rotated behind it and to
   the side (the "edges and glimpses of stacked panels" look), dimmed
   via opacity alone now, not filter; anything further back is
   invisible rather than cluttering the stack with all 5 at once. */
.slide-img[data-offset="0"] { transform: translate(0, 0) scale(1) rotate(0deg); z-index: 5; opacity: 1; }
.slide-img[data-offset="1"] { transform: translate(34px, -22px) scale(0.92) rotate(3deg); z-index: 4; opacity: 0.45; }
.slide-img[data-offset="2"] { transform: translate(58px, -38px) scale(0.85) rotate(5deg); z-index: 3; opacity: 0.2; }
.slide-img[data-offset="3"], .slide-img[data-offset="4"], .slide-img[data-offset="5"] {
  transform: translate(70px, -46px) scale(0.8) rotate(6deg);
  z-index: 2;
  opacity: 0;
  pointer-events: none;
}

.slide-dots { position: relative; z-index: 1; display: flex; gap: 0.5rem; justify-content: center; margin-top: 1.5rem; }
/* Real <button> elements (assets/carousel.js), not decorative spans --
   click jumps straight to that screenshot. Reset the default button
   chrome, keep only the dot's own look. */
.slide-dot {
  appearance: none;
  border: none;
  padding: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--surface0);
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
}
.slide-dot:hover { background: var(--subtext); }
.slide-dot.active { background: var(--mauve); transform: scale(1.4); }

/* Elegant serif for the wordmark specifically (body text stays sans) --
   no external font request, a cross-platform serif stack instead, kept
   in the same "no extra asset loading" spirit as the rest of this
   site. Gradient goes purple-to-silver (Mauve -> the same Clean Silver
   #cdd6f4 used as this whole theme's base text color), not purple-to-
   lavender as before -- closer to an actual brushed-metal/silver look. */
.hero-text h1 {
  font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
  font-weight: 500;
  font-size: 5.5rem;
  line-height: 1;
  margin: 0 0 1.25rem;
  background: linear-gradient(135deg, var(--mauve), var(--text));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Latest released version, filled in live by assets/version.js (a real
   GitHub API fetch, not hand-typed -- never goes stale after a
   release). A small pill, not gradient/serif text like the title
   itself -- it's metadata sitting next to the wordmark, not part of
   it, and needs its own solid (non-transparent) color since it lives
   outside the h1's background-clip. Sits empty (zero visual weight)
   until the fetch resolves, rather than a "vX.Y.Z" placeholder that
   might briefly show something wrong. */
/* The boot splash logo in the hero h1, animated the way
   splash/make-frames.py renders it for Plymouth (same timings and
   easings): letters write in 0.9 s each, 0.15 s apart; the wind line
   sweeps out from 0.9 s for 1.2 s; from 2.2 s a soft light runs along it
   every 2.4 s. pathLength="1" on every path, so dashes are fractions. */
.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;
}
.vayu-logo {
  display: inline-block;
  width: 17rem;
  height: auto;
  vertical-align: bottom;
  overflow: visible;
}
.vayu-logo + .version-badge { vertical-align: bottom; margin-bottom: 0.4rem; }
.vayu-logo .draw {
  stroke-dasharray: 1 2;
  stroke-dashoffset: 1;
  animation: vayu-draw 0.9s cubic-bezier(.6, 0, .3, 1) var(--d, 0s) forwards;
}
/* The Y is two paths (arms, then stem) so the stroke reads as one line:
   arms are ~74% of its length, so they get that share of the 0.9 s. */
.vayu-logo .y-arms { animation-duration: 0.66s; animation-timing-function: cubic-bezier(.6, 0, .6, 1); }
.vayu-logo .y-stem { animation-duration: 0.3s; animation-timing-function: cubic-bezier(.3, 0, .3, 1); }
.vayu-logo .wind { animation: vayu-draw 1.2s cubic-bezier(.5, 0, .2, 1) 0.9s forwards; }
@keyframes vayu-draw { to { stroke-dashoffset: 0; } }
.vayu-logo .glow {
  stroke-dasharray: 0.12 2;
  stroke-dashoffset: 0.12;
  opacity: 0;
  animation:
    vayu-glow-run 2.4s cubic-bezier(.45, 0, .25, 1) 2.2s infinite,
    vayu-glow-fade 2.4s linear 2.2s infinite;
}
@keyframes vayu-glow-run { from { stroke-dashoffset: 0.12; } to { stroke-dashoffset: -1; } }
@keyframes vayu-glow-fade { 0% { opacity: 0; } 15%, 85% { opacity: 0.9; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .vayu-logo .draw { animation: none; stroke-dashoffset: 0; }
  .vayu-logo .glow { animation: none; }
}

.version-badge {
  display: inline-block;
  /* h1 has line-height: 1 and a huge font-size -- "middle" would sink
     the badge to the visual center of the whole glyph height, "text-
     bottom" keeps it sitting near the baseline instead, closer to
     where a small superscript-like tag actually reads well next to
     giant text. */
  vertical-align: text-bottom;
  margin-left: 0.75rem;
  padding: 0.2rem 0.65rem;
  border-radius: 999px;
  border: 1px solid rgba(203, 166, 247, 0.28);
  background: rgba(203, 166, 247, 0.08);
  font-family: -apple-system, "Segoe UI", Roboto, "Inter", sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--mauve);
}
/* The opening of the Taittiriya Upanishad (1.1.1), a salutation to Vayu,
   the wind the project is named after. System Devanagari fonts only (Noto
   on Linux/Android, Kohinoor on macOS/iOS, Nirmala UI on Windows), no
   web font to load. The translation is only in the title (hover). */
.shlok { margin: -0.25rem 0 1.25rem; }
.shlok blockquote {
  margin: 0;
  font-family: "Noto Serif Devanagari", "Tiro Devanagari Sanskrit", "Kohinoor Devanagari", "Nirmala UI", "Noto Sans Devanagari", serif;
  font-size: 1.2rem;
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: var(--mauve);
}

.hero-text .tagline {
  font-size: 1.2rem;
  color: var(--subtext);
  margin: 0 0 1rem;
  max-width: 40ch;
}
.hero-text .tagline strong { color: var(--text); }

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.78rem;
  color: var(--subtext);
  margin: 0 0 2rem;
  font-weight: 600;
}

.cta-row { display: flex; gap: 1rem; margin-top: 0.5rem; flex-wrap: wrap; }
.btn {
  display: inline-block;
  padding: 0.7rem 1.4rem;
  border-radius: 10px;
  font-weight: 600;
  font-size: 0.95rem;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}
.btn:hover { text-decoration: none; }
.btn-primary {
  background: var(--mauve);
  color: var(--crust);
  box-shadow: 0 8px 24px rgba(203, 166, 247, 0.35);
}
.btn-primary:hover { background: var(--lavender); box-shadow: 0 10px 30px rgba(180, 190, 254, 0.45); }
/* Ghost button gets its own frosted-glass treatment (a real
   backdrop-filter blur, not just a transparent background) so it
   reads as another glass surface, not a plain outlined link. */
.btn-ghost {
  background: rgba(255, 255, 255, 0.04);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--text);
  border: 1px solid rgba(203, 166, 247, 0.25);
}
.btn-ghost:hover { border-color: var(--mauve); color: var(--mauve); background: rgba(203, 166, 247, 0.08); }

/* Stable/Nightly channel toggle, assets/channel.js -- a segmented
   control above the install snippet, same visual family as .btn (same
   radius/weight/transition timing) but styled as tabs rather than two
   separate buttons, since only one is ever "the" command showing below
   at a time. */
.channel-toggle {
  position: relative;
  display: inline-flex;
  gap: 0.65rem;
  padding: 0.3rem;
  margin: 1rem 0;
  background: var(--mantle);
  border: 1px solid var(--surface0);
  border-radius: 12px;
}
/* The sliding fill itself -- a separate absolutely-positioned layer
   rather than a background on the active button, so switching channels
   animates as one pill moving, not an instant color swap. Positioned/
   sized in px by assets/channel.js (measured via offsetLeft/offsetWidth,
   not a fixed 50% -- "Stable" and "Nightly" are different widths, more so
   once their live version text loads). */
.channel-thumb {
  position: absolute;
  top: 0.3rem;
  bottom: 0.3rem;
  /* left: 0, not 0.3rem -- assets/channel.js's translateX(btn.offsetLeft)
     already measures from the container's padding edge (offsetLeft
     bakes that 0.3rem padding in), so a nonzero left here double-counted
     it: the thumb landed ~0.3rem further right than the real button in
     both states -- overlapping into the other tab when Stable (the
     first, smaller offsetLeft) was active, and leaving a matching gap
     before Nightly's own left edge when Nightly was active. */
  left: 0;
  width: 0;
  border-radius: 8px;
  background: var(--mauve);
  box-shadow: 0 6px 18px rgba(203, 166, 247, 0.3);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), width 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 0;
  pointer-events: none;
}
.channel-btn {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 1.1rem;
  /* A real border/fill at rest, not just transparent -- an inactive
     tab needs to read as "a button, just not the selected one", not
     as plain floating text next to an actual button. Only the color
     of this border/fill changes between states; shape/size never
     does, so the two tabs stay visually identical as controls. */
  border: 1px solid var(--surface0);
  border-radius: 8px;
  /* appearance: none -- without it, Chromium on Linux/GTK themes can
     keep painting a native button bezel (ignoring border: none) since
     the element's appearance is still "auto", not actually reset to a
     plain box; this is what made these render as default system
     buttons instead of the styled segmented control. */
  appearance: none;
  -webkit-appearance: none;
  background: rgba(255, 255, 255, 0.03);
  color: var(--subtext);
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.channel-btn:hover { color: var(--text); border-color: var(--subtext); }
.channel-btn:focus-visible {
  outline: 2px solid var(--mauve);
  outline-offset: 2px;
}
/* Active tab's border/fill fade out since .channel-thumb (same shape,
   same radius) sits directly under it and supplies the mauve fill --
   this just keeps the active tab's own border from doubling up with
   the thumb's edge underneath it. Text color plays against that
   mauve fill, same accent as .btn-primary, delivered by the thumb
   layer instead of the button's own background. */
.channel-btn.active {
  color: var(--crust);
  border-color: transparent;
  background: transparent;
}
.channel-btn.active:hover { color: var(--crust); border-color: transparent; }
.channel-icon {
  font-size: 0.95em;
  line-height: 1;
}
.channel-version {
  font-family: "JetBrainsMono Nerd Font", "Fira Code", monospace;
  font-size: 0.75rem;
  font-weight: 500;
  opacity: 0.7;
}
.channel-btn.active .channel-version { opacity: 0.85; }
.channel-desc {
  color: var(--subtext);
  font-size: 0.9rem;
  margin: 0.85rem 0 1.5rem;
}
/* Quick cross-fade instead of an instant text jump when
   assets/channel.js swaps the command/description on a channel
   switch -- toggled briefly, not a permanent state. */
.channel-desc,
.install-snippet {
  transition: opacity 0.15s ease;
}
.channel-desc.fade,
.install-snippet.fade {
  opacity: 0;
}

.install-snippet {
  background: var(--mantle);
  border: 1px solid var(--surface0);
  border-radius: 8px;
  padding: 1rem 1.25rem;
  margin: 0;
}
/* overflow-x lives on the code element, not the pre itself -- the copy
   button (assets/copy.js) is an absolutely-positioned child of <pre>,
   and a scrolling *pre* drags its own absolutely-positioned children
   along with the scrolled content (confirmed live: the button visibly
   slid toward the middle on horizontal scroll). Scrolling only the
   inner code element keeps <pre> itself static, so the button --
   positioned relative to <pre>, not <code> -- never moves. */
.install-snippet code {
  display: block;
  overflow-x: auto;
  background: none;
  padding: 0;
  color: var(--green);
  font-size: 0.88rem;
}

/* Copy-to-clipboard button, added by assets/copy.js to every <pre> --
   pre needs position:relative (set inline by the script) for this
   absolute positioning to anchor to the code block itself, not the
   page. Hidden until hover/focus on the block, avoiding a permanent
   floating button on every single code snippet on a page. */
pre { position: relative; }
.copy-btn {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  background: var(--surface0);
  color: var(--subtext);
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 0.3rem 0.65rem;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease;
}
pre:hover .copy-btn, pre:focus-within .copy-btn, .copy-btn:focus {
  opacity: 1;
}
.copy-btn:hover { background: var(--mauve); color: var(--crust); }
.copy-btn.copied { background: var(--green); color: var(--crust); opacity: 1; }

/* --- Feature grid --- */
main { max-width: calc(var(--site-width) + 5rem); margin: 0 auto; padding: 0 2.5rem; }
/* Direct bug report: clicking "Features" in the nav scrolled past the
   section's own title, since the sticky .topnav-wrap sits on top of
   whatever the browser's native anchor-jump lands on. scroll-margin-top
   tells that jump to leave real space above the target instead --
   applies to any future id'd section too, not just these two. */
main section[id] { scroll-margin-top: 6rem; }
section h2 {
  font-size: 1.6rem;
  margin: 4rem 0 1.75rem;
  color: var(--text);
}
.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
}
.feature-card {
  position: relative;
  background: var(--mantle);
  border: 1px solid var(--surface0);
  border-radius: 12px;
  padding: 1.6rem 1.6rem 1.5rem;
  overflow: hidden;
  transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}
/* Soft accent glow in the corner instead of a hard left stripe --
   each card still reads as "its own color" at a glance, just as a
   quiet wash rather than a ruled line. */
.feature-card::before {
  content: "";
  position: absolute;
  top: -40%;
  right: -30%;
  width: 140px;
  height: 140px;
  background: var(--accent, var(--mauve));
  opacity: 0.12;
  border-radius: 50%;
  filter: blur(10px);
  pointer-events: none;
}
.feature-card:hover {
  transform: translateY(-3px);
  border-color: var(--accent, var(--mauve));
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);
}
.feature-card .feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 9px;
  background: color-mix(in srgb, var(--accent, var(--mauve)) 16%, transparent);
  color: var(--accent, var(--mauve));
  font-size: 1.15rem;
  margin-bottom: 0.9rem;
}
.feature-card h3 { margin: 0 0 0.5rem; color: var(--text); font-size: 1.05rem; position: relative; }
.feature-card p { margin: 0; color: var(--subtext); font-size: 0.92rem; position: relative; }

/* --- Install section (below hero, full walkthrough pointer) --- */
.install p { color: var(--subtext); max-width: 68ch; }

/* --- Docs links --- */
.docs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
}
.doc-card {
  display: block;
  background: var(--mantle);
  border: 1px solid var(--surface0);
  border-radius: 8px;
  padding: 1.4rem 1.5rem;
  color: var(--text);
}
.doc-card:hover { border-color: var(--mauve); text-decoration: none; }
.doc-card h3 { margin: 0 0 0.5rem; color: var(--mauve); font-size: 1.05rem; }
.doc-card p { margin: 0; color: var(--subtext); font-size: 0.9rem; }

footer {
  text-align: center;
  padding: 3rem 2.5rem 4rem;
  color: var(--subtext);
  font-size: 0.9rem;
  margin-top: 3rem;
  border-top: 1px solid var(--surface0);
}

/* Menu button: only shown below 1024px (below). Three bars that fold into
   an X while the menu is open. */
.nav-toggle {
  display: none;
  appearance: none;
  -webkit-appearance: none;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 1px solid rgba(203, 166, 247, 0.18);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--text);
  cursor: pointer;
  align-items: center;
  justify-content: center;
}
.nav-toggle:focus-visible { outline: 2px solid var(--mauve); outline-offset: 2px; }
.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  display: block;
  width: 1.05rem;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before,
.nav-toggle-bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after { top: 6px; }
.topnav.open .nav-toggle-bars { background: transparent; }
.topnav.open .nav-toggle-bars::before { transform: translateY(6px) rotate(45deg); background: var(--text); }
.topnav.open .nav-toggle-bars::after { transform: translateY(-6px) rotate(-45deg); background: var(--text); }

/* --- Phones and narrow tablets ---
   Everything stacks into one column with 1rem page gutters. Nothing may
   be wider than the screen: the grids' minimum column width is capped
   at 100% (min()), the hero's two halves drop their desktop min-widths,
   and the screenshot deck's peeking cards fan out by a few px instead of
   up to 70px (which pushed the page sideways on a 360px phone). */
/* The eight nav links need ~740px next to the logo: below 1024px they'd
   wrap onto a second row ("GitHub" alone under the rest), so from there
   down they fold behind the menu button instead. */
.topnav .navlinks { display: flex; flex-wrap: nowrap; }
.topnav .navlinks a { white-space: nowrap; }
@media (max-width: 1024px) {
  .topnav { flex-wrap: wrap; border-radius: 22px; padding: 0.45rem 0.5rem 0.45rem 1rem; }
  .topnav .navlinks { flex-wrap: wrap; gap: 0.15rem; }
  .topnav .navlinks a { margin-left: 0; }
  /* With JS (nav.js adds .has-toggle) the links fold behind the button. */
  .topnav.has-toggle .nav-toggle { display: inline-flex; }
  .topnav.has-toggle .navlinks { display: none; width: 100%; flex-direction: column; padding: 0.5rem 0 0.35rem; }
  .topnav.has-toggle.open .navlinks { display: flex; }
  .topnav.has-toggle .navlinks a { padding: 0.7rem 0.85rem; font-size: 1rem; border-radius: 14px; color: var(--text); }
}

@media (max-width: 860px) {
  .topnav-wrap { padding: 0.6rem 0.75rem 0; }
  .topnav .brand-logo { height: 1.6rem; }

  /* The glow behind the card bleeds past the screen edge here: clip it
     (clip, not hidden, so nothing becomes a scroll container). */
  .hero { min-height: 0; padding: 1rem 1rem 2.5rem; overflow-x: clip; }
  .hero-card {
    flex-direction: column;
    align-items: stretch;
    min-height: 0;
    padding: 1.75rem 1.25rem 1.5rem;
    gap: 2rem;
    border-radius: 20px;
  }
  .hero-text, .hero-image { flex: none; width: 100%; min-width: 0; }
  .hero-text h1 { font-size: 2.8rem; margin-bottom: 1rem; }
  .vayu-logo { width: 12rem; }
  .version-badge { font-size: 0.8rem; margin-left: 0.5rem; }
  .shlok blockquote { font-size: 1.05rem; }
  .hero-text .tagline { font-size: 1.05rem; }
  .eyebrow { margin-bottom: 1.5rem; }
  .cta-row { gap: 0.75rem; }
  .cta-row .btn { flex: 1 1 10rem; text-align: center; }
  .hero-image::before { inset: -6%; }
  .slide-img[data-offset="1"] { transform: translate(12px, -10px) scale(0.94) rotate(2deg); }
  .slide-img[data-offset="2"] { transform: translate(20px, -16px) scale(0.88) rotate(3deg); }
  .slide-img[data-offset="3"], .slide-img[data-offset="4"], .slide-img[data-offset="5"] {
    transform: translate(24px, -18px) scale(0.85) rotate(3deg);
  }
  .slide-dot { width: 10px; height: 10px; }
  .slide-dots { gap: 0.75rem; }

  main { padding: 0 1rem; }
  main section[id] { scroll-margin-top: 5rem; }
  section h2 { font-size: 1.4rem; margin: 2.75rem 0 1.25rem; }
  .feature-grid, .docs-grid { gap: 0.9rem; }
  .feature-card { padding: 1.3rem 1.25rem; }
  .doc-card { padding: 1.2rem 1.25rem; }

  .channel-toggle { display: flex; width: 100%; max-width: 26rem; gap: 0.4rem; }
  .channel-btn { flex: 1 1 0; justify-content: center; padding: 0.55rem 0.5rem; }
  .channel-btn { min-width: 0; flex-wrap: wrap; column-gap: 0.4rem; row-gap: 0; }
  .channel-version { white-space: nowrap; }
  .install-snippet { padding: 0.85rem 1rem; }
  .install-snippet code { font-size: 0.8rem; }

  footer { padding: 2rem 1rem 2.5rem; margin-top: 2rem; }
}

/* Smallest phones: the channel tabs drop their icons to fit. */
@media (max-width: 380px) {
  .channel-icon { display: none; }
  .channel-btn { padding: 0.55rem 0.35rem; }
}

/* Grids never ask for a column wider than the screen. */
.feature-grid { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.docs-grid { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }

/* Touch screens have no hover: the copy button is always shown there, and
   code gets room on the right so the button doesn't sit on the text. */
@media (hover: none) {
  .copy-btn { opacity: 1; }
  pre:has(.copy-btn) > code { padding-right: 4rem; }
}
