/**
 * Geekflare V3 Design System — tokens, dark mode, base rules
 *
 * Ported from geekflare-website's packages/gf-v3/src/styles/gf-v3.css,
 * which is the live source of truth (per project decision — the Guides
 * handoff doc's own :root block still shows an older teal secondary;
 * gf-v3 deliberately moved to "orange is the only color", secondary is a
 * neutral ink. That decision is carried through here, not the teal one.
 *
 * Unlike geekflare-website (a Next.js app migrating page-by-page, so its
 * tokens are scoped to a `.gf-v3` wrapper class to avoid clashing with
 * old-design pages in the same document), this theme has no such
 * collision risk — the current design doesn't use CSS custom properties
 * at all. Tokens live on :root directly.
 *
 * @package Main
 * @since 1.0.0
 */

:root {
  /* ---------- Surfaces & ink ---------- */
  --gf-bg: #ffffff;
  --gf-text: #1b1a19;
  --gf-panel: #ffffff;
  --gf-sunk: #f7f5f4;
  --gf-line: rgba(27, 26, 25, 0.09);
  --gf-line-2: rgba(27, 26, 25, 0.16);
  --gf-dim: #6a6562;
  --gf-quiet: #6f6a67;

  /* ---------- Brand (orange is the only hue) ---------- */
  --gf-brand: #ff4a00;
  --gf-brand-hover: #e64300;
  --gf-accent-text: #c23700;
  --gf-accent-soft: rgba(255, 74, 0, 0.09);
  --gf-accent-wash: rgba(255, 74, 0, 0.05);
  --gf-link: var(--gf-accent-text);

  /* ---------- "Secondary" — neutral ink, not a second hue ---------- */
  --gf-2: #2b2927;
  --gf-2-text: #1b1a19;
  --gf-2-soft: rgba(27, 26, 25, 0.06);
  --gf-2-on: #ffffff;

  /* ---------- Status (muted, sit beside the brand orange) ---------- */
  --gf-ok: #22a060;
  --gf-warn: #c8861a;
  --gf-bad: #d1463a;
  /* Pros/cons list markers (article body) — aliased to the status colors
     rather than a new hue, per "orange is the only accent" — gf-v3 itself
     has no pros/cons pattern to source these from. */
  --gf-pro: var(--gf-ok);
  --gf-con: var(--gf-bad);

  /* ---------- Typography ---------- */
  --gf-display: 'Familjen Grotesk', system-ui, sans-serif;
  --gf-sans: 'IBM Plex Sans', system-ui, sans-serif;
  --gf-serif: 'IBM Plex Serif', Georgia, serif;
  --gf-mono: 'IBM Plex Mono', ui-monospace, monospace;
  --gf-track-1: -0.035em;
  --gf-track-2: -0.028em;

  /* ---------- Layout ---------- */
  --gf-max: 1200px;
  --gf-r: 12px;
  --gf-r-sm: 8px;
  --gf-hair: 1px solid var(--gf-line);
  /* Used as a mask, so the stroke colour here is irrelevant — the element
     paints it with a token. Matches lucide's ChevronDown. */
  --gf-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");

  /* ---------- Shadow & wash ---------- */
  --gf-shadow: 0 1px 2px rgba(27, 26, 25, 0.04), 0 14px 34px -18px rgba(27, 26, 25, 0.16);
  --gf-shadow-lg: 0 2px 4px rgba(27, 26, 25, 0.05), 0 34px 70px -30px rgba(27, 26, 25, 0.26);
  --gf-wash:
    radial-gradient(90% 62% at 2% -14%, rgba(27, 26, 25, 0.04), transparent 62%),
    linear-gradient(180deg, rgba(27, 26, 25, 0.015), transparent 46%);

  --gf-logo-filter: none;

  color-scheme: light;
}

/* Dark tokens: the attribute handles an explicit choice, the media query
   handles the system default. Both are required. */
html[data-theme="dark"] {
  --gf-bg: #0d0c0c;
  --gf-text: #f7f5f4;
  --gf-panel: #171615;
  --gf-sunk: #121110;
  --gf-line: rgba(247, 245, 244, 0.1);
  --gf-line-2: rgba(247, 245, 244, 0.18);
  --gf-dim: #a8a29f;
  --gf-quiet: #8d8784;
  --gf-accent-text: #ff8258;
  --gf-accent-soft: rgba(255, 74, 0, 0.16);
  --gf-accent-wash: rgba(255, 74, 0, 0.1);
  --gf-2: #e7e3e0;
  --gf-2-text: #f7f5f4;
  --gf-2-soft: rgba(247, 245, 244, 0.1);
  --gf-2-on: #0d0c0c;
  --gf-ok: #3ecb84;
  --gf-bad: #ff6b5c;
  --gf-shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 18px 38px -22px rgba(0, 0, 0, 0.75);
  --gf-shadow-lg: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 40px 80px -34px rgba(0, 0, 0, 0.9);
  --gf-wash: radial-gradient(90% 62% at 2% -14%, rgba(247, 245, 244, 0.05), transparent 62%);
  --gf-logo-filter: grayscale(1) invert(1) brightness(1.6);
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) {
    --gf-bg: #0d0c0c;
    --gf-text: #f7f5f4;
    --gf-panel: #171615;
    --gf-sunk: #121110;
    --gf-line: rgba(247, 245, 244, 0.1);
    --gf-line-2: rgba(247, 245, 244, 0.18);
    --gf-dim: #a8a29f;
    --gf-quiet: #8d8784;
    --gf-accent-text: #ff8258;
    --gf-accent-soft: rgba(255, 74, 0, 0.16);
    --gf-accent-wash: rgba(255, 74, 0, 0.1);
    --gf-2: #e7e3e0;
    --gf-2-text: #f7f5f4;
    --gf-2-soft: rgba(247, 245, 244, 0.1);
    --gf-2-on: #0d0c0c;
    --gf-ok: #3ecb84;
    --gf-bad: #ff6b5c;
    --gf-shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 18px 38px -22px rgba(0, 0, 0, 0.75);
    --gf-shadow-lg: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 40px 80px -34px rgba(0, 0, 0, 0.9);
    --gf-wash: radial-gradient(90% 62% at 2% -14%, rgba(247, 245, 244, 0.05), transparent 62%);
    --gf-logo-filter: grayscale(1) invert(1) brightness(1.6);
    color-scheme: dark;
  }
}

/* The window scrollbar follows <html>, not a wrapper — set color-scheme there too. */
html[data-theme="dark"] { color-scheme: dark; }
html[data-theme="light"] { color-scheme: light; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) { color-scheme: dark; }
}

/* Scope the new design's base rules to a wrapper so pages not yet
   migrated (still using the old gray/Gilroy design) are unaffected —
   remove this scoping once every page has been rebuilt.

   Specificity note: the old theme's dist/style.css is deliberately
   printed last (see main_print_styles_last()) so IT wins ties against
   everything else — but it also has page-specific rules like
   `body.archive:not(.single-post-page), body.home {
   background-color:#fcfcff }` (WordPress adds `.archive` to every
   category/tag/author page). `:not()`'s argument counts toward
   specificity, so that selector carries 2 classes' worth — more than a
   bare `.gf-v3` (1 class), and even more than the previous `html
   body.gf-v3` fix (element bump only helps against 1-class selectors).
   `html body.gf-v3.gf-v3` combines both an element and a class bump to
   get strictly ahead of any single old-theme selector we've hit so far,
   regardless of load order. */
html body.gf-v3.gf-v3 {
  background: var(--gf-bg);
  color: var(--gf-text);
  font-family: var(--gf-sans);
  -webkit-font-smoothing: antialiased;
}

/* The old theme paints a decorative cream/pink gradient and a hero photo
   behind #main-content on both the home template AND any archive page
   (dist/style.css: `.home #main-content{background:linear-gradient(...)}`
   plus `.archive:not(.single-post-page) #main-content{...}` — the second
   one is what leaks onto category.php, since WordPress adds `.archive` to
   every category page's body class) and the matching `:before` hero
   photo. `.archive:not(.single-post-page)` carries 2 classes worth of
   specificity (`:not()`'s argument counts), so `body.gf-v3` alone (1
   class) isn't enough to beat it — `.gf-v3.gf-v3` (doubled selector, a
   standard specificity bump, still matches the same single class) gets
   there regardless of load order. */
body.gf-v3.gf-v3 #main-content {
  background: none;
}
body.gf-v3.gf-v3 #main-content::before {
  content: none;
}
/* Base link colour, deliberately kept at the lowest possible weight.
   `.gf-v3 a` is two selectors, so it used to out-specify every component
   that paints its own colour through a single class — nav links, the
   header CTA, the spotlight and CTA-band buttons all silently fell back
   to inherit, which is why white-on-orange buttons rendered black.
   :where() contributes no specificity, so this now sits level with the
   bare `a` rule in dist/style.css (which says the same thing) and any
   .gf-* component beats it without having to fight. text-decoration has
   no such competition, so it keeps its weight. */
:where(.gf-v3) a { color: inherit; }
:where(.gf-v3) a:hover { color: var(--gf-accent-text); }
.gf-v3 a { text-decoration: none; }
.gf-v3 ::selection { background: rgba(255, 74, 0, 0.2); }
.gf-v3 :focus-visible { outline: 2px solid var(--gf-brand); outline-offset: 2px; }

.gf-wash {
  background-image: var(--gf-wash);
  background-repeat: no-repeat;
  background-size: 100% 760px;
}
/* Shorter falloff on pages with less hero real estate before content
   starts (category vs. home) — same tint, per the design spec. */
.gf-wash--sm {
  background-size: 100% 560px;
}

/* Hero: copy/search on the left, a fixed-width spotlight card on the
   right — stacks on narrow viewports since there's no room for both. */
.gf-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
  gap: 48px;
  align-items: start;
}
@media (max-width: 760px) {
  .gf-hero-grid {
    grid-template-columns: 1fr;
  }
}

/* Header logo, matched to geekflare-website's own size (Logo in
   packages/gf-v3/src/layout/site-header.tsx: h-[34px] sm:h-[38px]). */
.gf-site-logo {
  height: 34px;
  width: auto;
}
@media (min-width: 640px) {
  .gf-site-logo {
    height: 38px;
  }
}

/* Header: transparent and borderless at the top of the page, solid once
   scrolled — same treatment as geekflare-website's SiteHeader (`solid`
   state there is scrolled || menu open; this site has no mega menu, so
   just scroll). Toggled by initHeaderScroll() in gf-v3-theme.js. */
.gf-site-header {
  background: transparent;
  border-bottom: 1px solid transparent;
  backdrop-filter: none;
  transition: background-color 0.2s, border-color 0.2s, backdrop-filter 0.2s;
}
.gf-site-header.is-scrolled {
  background: color-mix(in srgb, var(--gf-bg) 82%, transparent);
  border-bottom: 1px solid var(--gf-line);
  backdrop-filter: blur(16px);
}
/* While a dropdown or the drawer is open the header goes fully opaque —
   the drawer is a fixed panel hanging off the header, and a backdrop
   filter on an ancestor would clip it. */
.gf-site-header.is-open {
  background: var(--gf-bg);
  border-bottom: 1px solid var(--gf-line);
  backdrop-filter: none;
}

.gf-site-header {
  position: sticky;
  top: 0;
  z-index: 60;
}
.gf-site-header__bar {
  max-width: var(--gf-max);
  margin: 0 auto;
  padding: 0 28px;
  height: 64px;
  display: flex;
  align-items: center;
  gap: 28px;
}
.gf-site-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}
.gf-theme-toggle {
  width: 34px;
  height: 34px;
  border-radius: var(--gf-r-sm);
  display: grid;
  place-items: center;
  cursor: pointer;
  background: transparent;
  border: var(--gf-hair);
  color: var(--gf-dim);
  flex: none;
  transition: border-color 0.15s, color 0.15s;
}
.gf-theme-toggle:hover {
  border-color: var(--gf-line-2);
  color: var(--gf-text);
}
.gf-header-cta {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 15px;
  border-radius: var(--gf-r-sm);
  background: var(--gf-brand);
  color: #fff;
  font-family: var(--gf-sans);
  font-weight: 600;
  font-size: 13.5px;
  white-space: nowrap;
  flex: none;
  transition: background-color 0.15s;
}
.gf-header-cta:hover {
  background: var(--gf-brand-hover);
  color: #fff;
}

/* ---------- Primary navigation ---------- */
/* Mirrors geekflare-website's SiteHeader so the two sites read as one
   product: 36px trigger boxes, panels on --gf-panel with a hairline and
   the large shadow, mono column headings. The breakpoint is theirs too
   (--breakpoint-nav, 1080px). */
.gf-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.gf-nav-link {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  border-radius: var(--gf-r-sm);
  font-family: var(--gf-sans);
  font-size: 14px;
  font-weight: 400;
  line-height: 1;
  white-space: nowrap;
  color: var(--gf-dim);
  background: none;
  border: 0;
  cursor: pointer;
  transition: color 0.15s;
}
.gf-nav-link:hover,
.gf-nav-dropdown.is-open > .gf-nav-trigger {
  color: var(--gf-text);
}
.gf-nav-link.is-active {
  color: var(--gf-accent-text);
}
.gf-nav-chevron {
  transition: transform 0.2s;
}
.gf-nav-dropdown.is-open .gf-nav-chevron,
.gf-mnav-row.is-open .gf-nav-chevron {
  transform: rotate(180deg);
}

.gf-nav-dropdown {
  position: relative;
}
.gf-nav-panel {
  position: absolute;
  top: 100%;
  left: 0;
  /* Invisible bridge so the pointer can cross the gap into the panel. */
  padding-top: 14px;
}
.gf-nav-panel[hidden] {
  display: none;
}
.gf-nav-panel__inner {
  border-radius: var(--gf-r);
  border: var(--gf-hair);
  background: var(--gf-panel);
  box-shadow: var(--gf-shadow-lg);
  overflow: hidden;
}
.gf-nav-panel--compact .gf-nav-panel__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  width: 520px;
  padding: 12px;
}
/* The mega panel spans the content column, not the trigger. Dropping the
   containing block off the trigger lets it resolve against the sticky
   header instead, so it can stretch edge to edge and then centre itself
   on --gf-max — the same shape as geekflare-website's inset-x-0 panel. */
.gf-nav-dropdown:has(.gf-nav-panel--mega) {
  position: static;
}
.gf-nav-panel--mega {
  left: 0;
  right: 0;
  padding-left: 28px;
  padding-right: 28px;
}
.gf-nav-panel--mega .gf-nav-panel__inner {
  /* The content column is --gf-max minus its own 28px gutters, so the
     panel has to subtract them to line up with it at every width. */
  max-width: calc(var(--gf-max) - 56px);
  margin: 0 auto;
}
.gf-nav-panel--mega .gf-nav-panel__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  padding: 12px;
}
.gf-nav-group__head {
  font-family: var(--gf-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gf-quiet);
  padding: 8px 12px 6px;
}
.gf-nav-item {
  display: block;
  border-radius: var(--gf-r-sm);
  padding: 10px 12px;
  color: var(--gf-text);
  transition: background-color 0.15s;
}
.gf-nav-item:hover {
  background: var(--gf-sunk);
  color: var(--gf-text);
}
.gf-nav-item__label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--gf-sans);
  font-size: 14px;
  font-weight: 600;
  color: var(--gf-text);
}
.gf-nav-item__ext {
  flex: none;
  color: var(--gf-quiet);
}
.gf-nav-item__desc {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  line-height: 1.375;
  color: var(--gf-dim);
}

/* ---------- Mobile drawer ---------- */
.gf-nav-burger {
  display: none;
  place-items: center;
  width: 36px;
  height: 36px;
  margin-right: -6px;
  border: 0;
  border-radius: var(--gf-r-sm);
  background: none;
  color: var(--gf-text);
  cursor: pointer;
  flex: none;
}
.gf-nav-burger__close,
.gf-nav-burger[aria-expanded="true"] .gf-nav-burger__open {
  display: none;
}
.gf-nav-burger[aria-expanded="true"] .gf-nav-burger__close {
  display: block;
}
.gf-mobile-nav {
  position: fixed;
  inset: 64px 0 0;
  overflow-y: auto;
  border-top: var(--gf-hair);
  background: var(--gf-bg);
}
.gf-mobile-nav[hidden] {
  display: none;
}
.gf-mobile-nav__inner {
  max-width: var(--gf-max);
  margin: 0 auto;
  padding: 12px 20px 32px;
  display: flex;
  flex-direction: column;
}
.gf-mnav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 16px 0;
  border-bottom: var(--gf-hair);
  font-family: var(--gf-sans);
  font-size: 16px;
  font-weight: 600;
  color: var(--gf-text);
  text-align: left;
  background: none;
  border-left: 0;
  border-right: 0;
  border-top: 0;
}
.gf-mnav-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 16px 0;
  font-family: var(--gf-sans);
  font-size: 16px;
  font-weight: 600;
  color: var(--gf-text);
  background: none;
  border: 0;
  cursor: pointer;
}
.gf-mnav-toggle .gf-nav-chevron {
  color: var(--gf-quiet);
}
.gf-mnav-row[data-gf-mnav-accordion] {
  display: block;
  padding: 0;
}
.gf-mnav-panel {
  margin: 0 -12px;
  padding-bottom: 12px;
}
.gf-mnav-panel[hidden] {
  display: none;
}

@media (max-width: 1079px) {
  .gf-nav {
    display: none;
  }
  .gf-nav-burger {
    display: grid;
  }
  .gf-site-header__bar {
    padding: 0 20px;
    gap: 12px;
  }
}
@media (min-width: 1080px) {
  .gf-mobile-nav {
    display: none;
  }
}
@media (max-width: 420px) {
  .gf-header-cta:not(.gf-mobile-nav__cta) {
    display: none;
  }
}
.gf-mobile-nav__cta {
  margin-top: 24px;
  justify-content: center;
  height: 44px;
}

/* ---------- Custom tooltips (replaces the browser's native title tooltip) ---------- */
.gf-tooltip { position: relative; }
.gf-tooltip::after {
  content: attr(data-gf-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  white-space: nowrap;
  background: var(--gf-text);
  color: var(--gf-bg);
  font-family: var(--gf-sans);
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  padding: 6px 9px;
  border-radius: 6px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease, visibility 0s linear 0.12s;
  z-index: 50;
}
.gf-tooltip:hover::after,
.gf-tooltip:focus-visible::after {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition: opacity 0.12s ease, transform 0.12s ease, visibility 0s linear 0s;
}

/* ---------- Select control ---------- */
/* geekflare-website's SelectFieldV3 is a JS listbox, so its option list is
   its own markup. This stays a native <select> — it keeps working without
   JS, submits the form on change and gets the OS picker on touch — and
   borrows the trigger's look: hairline on --gf-panel, 8px radius, a
   --gf-quiet chevron replacing the platform arrow, --gf-2 focus ring.
   Scaled down from their 46px form field to suit an inline toolbar
   control. The dropped-open list is drawn by the OS and can't be styled. */
.gf-select {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.gf-select > select {
  appearance: none;
  -webkit-appearance: none;
  height: 36px;
  padding: 0 34px 0 12px;
  border-radius: var(--gf-r-sm);
  border: var(--gf-hair);
  background: var(--gf-panel);
  color: var(--gf-text);
  font-family: var(--gf-sans);
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.gf-select > select:hover {
  border-color: var(--gf-line-2);
}
.gf-select > select:focus {
  outline: none;
  border-color: var(--gf-2);
  box-shadow: 0 0 0 1px var(--gf-2);
}
/* Masked rather than a background-image SVG, so the chevron can take a
   token colour and follow the theme. */
.gf-select::after {
  content: "";
  position: absolute;
  right: 11px;
  width: 16px;
  height: 16px;
  pointer-events: none;
  background: var(--gf-quiet);
  -webkit-mask: var(--gf-chevron) center / contain no-repeat;
  mask: var(--gf-chevron) center / contain no-repeat;
}

/* Without JS the select can't submit on change, so a button appears. */
.gf-select__go {
  height: 36px;
  margin-left: 8px;
  padding: 0 12px;
  border-radius: var(--gf-r-sm);
  border: var(--gf-hair);
  background: var(--gf-panel);
  color: var(--gf-text);
  font-family: var(--gf-sans);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}

/* ---------- Closing CTA band ---------- */
/* geekflare-website's CtaBlock. The panel's base and both glow stops are
   literal values there, not tokens, so it renders the same in light and
   dark — only the band around it follows the theme. */
.gf-cta-band {
  border-top: var(--gf-hair);
  background: var(--gf-bg);
  padding-block: 64px;
}
.gf-cta-band__inner {
  max-width: var(--gf-max);
  margin: 0 auto;
  padding-inline: 28px;
}
.gf-cta-panel {
  overflow: hidden;
  border-radius: var(--gf-r);
  background-color: #131211;
  background-image:
    radial-gradient(80% 120% at 88% -10%, rgba(255, 74, 0, 0.42), transparent 58%),
    radial-gradient(60% 100% at 4% 108%, rgba(255, 255, 255, 0.06), transparent 60%);
  padding: clamp(32px, 5vw, 64px);
  /* The panel is dark whichever theme is on, so any native control or
     scrollbar inside it should be too. */
  color-scheme: dark;
}
.gf-cta-panel__title {
  margin: 0;
  max-width: 18ch;
  font-family: var(--gf-display);
  font-weight: 700;
  font-size: clamp(30px, 4.6vw, 54px);
  line-height: 1.02;
  letter-spacing: -0.035em;
  color: #fff;
  text-wrap: balance;
}
.gf-cta-panel__lead {
  margin: 18px 0 0;
  max-width: 46ch;
  font-family: var(--gf-sans);
  font-size: 17px;
  line-height: 1.52;
  color: rgba(255, 255, 255, 0.78);
}
.gf-cta-panel__actions {
  margin-top: 30px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.gf-cta-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 48px;
  padding: 0 21px;
  border-radius: var(--gf-r-sm);
  font-family: var(--gf-sans);
  font-weight: 600;
  font-size: 15px;
  white-space: nowrap;
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}
.gf-cta-btn--primary {
  background: var(--gf-brand);
  color: #fff;
}
.gf-cta-btn--primary:hover {
  background: var(--gf-brand-hover);
  color: #fff;
}
.gf-cta-btn--on-dark {
  border: 1px solid rgba(255, 255, 255, 0.3);
  color: #fff;
}
.gf-cta-btn--on-dark:hover {
  border-color: rgba(255, 255, 255, 0.6);
  color: #fff;
}
@media (min-width: 768px) {
  .gf-cta-band {
    padding-block: 78px;
  }
}

/* ---------- Ambient page texture ---------- */
/* Site-wide version of the hero dot field: fixed to the viewport, masked
   so it only ever fills the gutters outside the content column, and faded
   down the page. Two masks composited — horizontal (gutters only, keyed
   off --gf-max so it lines up with the content) and vertical (fade out) —
   the same approach geekflare-website uses for its hero backdrop.
   Below --gf-max there are no gutters, so it vanishes on its own. */
.gf-page-texture {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: radial-gradient(var(--gf-quiet) 1.2px, transparent 1.2px);
  background-size: 16px 16px;
  opacity: 0.17;
  -webkit-mask-image:
    linear-gradient(
      to right,
      #000 0,
      #000 calc(50% - var(--gf-max) / 2),
      transparent calc(50% - var(--gf-max) / 2),
      transparent calc(50% + var(--gf-max) / 2),
      #000 calc(50% + var(--gf-max) / 2),
      #000 100%
    ),
    linear-gradient(to bottom, #000 0%, #000 45%, transparent 92%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(
      to right,
      #000 0,
      #000 calc(50% - var(--gf-max) / 2),
      transparent calc(50% - var(--gf-max) / 2),
      transparent calc(50% + var(--gf-max) / 2),
      #000 calc(50% + var(--gf-max) / 2),
      #000 100%
    ),
    linear-gradient(to bottom, #000 0%, #000 45%, transparent 92%);
  mask-composite: intersect;
}
/* Everything else stacks above it. */
.gf-v3 #page {
  position: relative;
  z-index: 1;
}
@media (prefers-reduced-transparency: reduce) {
  .gf-page-texture { display: none; }
}

/* Hero background: a faint dot-grid confined to the left/right margins,
   fading out toward the bottom, leaving the headline/search area clean —
   a lightweight CSS stand-in for geekflare-website's WebGL "Pixel Blast"
   hero backdrop (packages/gf-v3/src/ui/hero-background.tsx) without
   pulling in three.js for a content-heavy, performance-sensitive site.
   Same edge-masking idea (its MASK constant), just static. */
.gf-hero-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: radial-gradient(var(--gf-quiet) 1.2px, transparent 1.2px);
  background-size: 16px 16px;
  opacity: 0.3;
  -webkit-mask-image:
    linear-gradient(to bottom, #000 0%, #000 70%, transparent 100%),
    linear-gradient(to right, #000 0%, transparent 22%, transparent 78%, #000 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom, #000 0%, #000 70%, transparent 100%),
    linear-gradient(to right, #000 0%, transparent 22%, transparent 78%, #000 100%);
  mask-composite: intersect;
}

/* Sponsor logos (Bright Data, Sparkian, Murf AI, ...) ship in their own
   fixed brand colors, some of which vanish on a dark page background —
   a small always-light chip keeps every logo legible in both themes. */
.gf-logo-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 52px;
  padding: 0 22px;
  border-radius: var(--gf-r-sm);
  background: #ffffff;
  border: 1px solid rgba(27, 26, 25, 0.08);
}

/* Sponsored spotlight CTA — orange, with a slow glow pulse and a sheen
   that sweeps across every few seconds to pull the eye. Both stop for
   anyone who's asked the OS to reduce motion. */
.gf-cta-glow {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 16px;
  border-radius: var(--gf-r-sm);
  background: var(--gf-brand);
  color: #fff;
  font-family: var(--gf-sans);
  font-weight: 600;
  font-size: 13.5px;
  animation: gf-cta-pulse 2.8s ease-in-out infinite;
}
.gf-cta-glow:hover {
  background: var(--gf-brand-hover);
  color: #fff;
}
.gf-cta-glow::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.45), transparent);
  transform: skewX(-18deg);
  animation: gf-cta-sheen 3.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes gf-cta-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 74, 0, 0.5); }
  55% { box-shadow: 0 0 0 9px rgba(255, 74, 0, 0); }
}
@keyframes gf-cta-sheen {
  0%, 62% { left: -60%; }
  100% { left: 120%; }
}
@media (prefers-reduced-motion: reduce) {
  .gf-cta-glow { animation: none; }
  .gf-cta-glow::after { display: none; }
}

/* Footer status dot — same idea as geekflare-website's animate-gf-pulse. */
@keyframes gf-pulse-dot {
  0%, 100% { opacity: 0.3; transform: scale(0.9); }
  50% { opacity: 1; transform: scale(1); }
}
.gf-pulse-dot {
  animation: gf-pulse-dot 2.6s ease-in-out infinite;
}

/* Logo swap follows the *effective* theme, in CSS so it never flashes. */
.gf-logo-dark { display: none; }
html[data-theme="dark"] .gf-logo-light { display: none; }
html[data-theme="dark"] .gf-logo-dark { display: block; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) .gf-logo-light { display: none; }
  html:not([data-theme="light"]) .gf-logo-dark { display: block; }
}

/* ---------- Social icons ---------- */
/* Neutral at rest, brand colour on hover. X has no one colour that works
   on both themes — its mark is black on light, white on dark — so it
   resolves from --gf-text rather than a literal. */
.gf-social {
  color: var(--gf-dim);
  transition: color 0.15s, border-color 0.15s;
}
.gf-social:hover,
.gf-social:focus-visible {
  color: var(--gf-social, var(--gf-text));
}
.gf-social[data-gf-social="linkedin"] { --gf-social: #0a66c2; }
.gf-social[data-gf-social="facebook"] { --gf-social: #1877f2; }
.gf-social[data-gf-social="x"] { --gf-social: var(--gf-text); }

/* ---------- Partners band ---------- */
.gf-partners {
  border-top: var(--gf-hair);
}
.gf-partners__inner {
  max-width: var(--gf-max);
  margin: 0 auto;
  padding: 40px 28px 44px;
}
.gf-partners__label {
  font-family: var(--gf-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gf-quiet);
  margin-bottom: 16px;
}
.gf-partners__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 16px;
}
.gf-partners__card {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 22px 20px;
  border-radius: var(--gf-r);
  border: var(--gf-hair);
  background: var(--gf-panel);
  transition: border-color 0.15s;
}
a.gf-partners__card:hover {
  border-color: var(--gf-line-2);
}
.gf-partners__logo {
  height: 42px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  display: block;
}

/* ---------- Search field ---------- */
/* Used on the search results page and the 404 — the only two places the
   design needs a standalone field. Shape follows the header CTA and the
   select control: hairline on --gf-panel, --gf-r-sm, --gf-2 focus ring. */
.gf-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 520px;
}
.gf-search__icon {
  position: absolute;
  left: 14px;
  color: var(--gf-quiet);
  pointer-events: none;
}
.gf-search input[type="search"] {
  flex: 1 1 auto;
  min-width: 0;
  height: 46px;
  padding: 0 14px 0 40px;
  border-radius: var(--gf-r-sm);
  border: var(--gf-hair);
  background: var(--gf-panel);
  color: var(--gf-text);
  font-family: var(--gf-sans);
  font-size: 14.5px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.gf-search input[type="search"]::placeholder {
  color: var(--gf-quiet);
}
.gf-search input[type="search"]:hover {
  border-color: var(--gf-line-2);
}
.gf-search input[type="search"]:focus {
  outline: none;
  border-color: var(--gf-2);
  box-shadow: 0 0 0 1px var(--gf-2);
}
.gf-search button {
  flex: none;
  height: 46px;
  padding: 0 20px;
  border: 0;
  border-radius: var(--gf-r-sm);
  /* Neutral, not brand orange: the page's own CTA band carries the
     orange, and two oranges on one screen compete. Matches the homepage
     hero's search button. */
  background: var(--gf-text);
  color: var(--gf-bg);
  font-family: var(--gf-sans);
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: opacity 0.15s;
}
.gf-search button:hover {
  opacity: 0.88;
}

/* Footer social marks — same brand-hover component as the article byline. */
.gf-social[data-gf-social="twitter"] { --gf-social: var(--gf-text); }
.gf-social[data-gf-social="youtube"] { --gf-social: #ff0000; }
