/* DTH Character Studio — landing page.
 * Matches the app's identity (apps/web/src/styles.css): dark warm-neutral grays,
 * the logo orange #fe5c01 as the only accent, Manrope. Dark-only by design —
 * the tools it serves (Daz, Houdini) have no light mode. */

@font-face {
  font-family: 'Manrope Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url('assets/manrope-latin-wght-normal.woff2') format('woff2-variations');
}

:root {
  /* Dark-only — also switches the UA's scrollbars/form controls to their dark
     variants (the guide pages had light scrollbars without this). */
  color-scheme: dark;
  --bg: #232323;
  --bg-raised: #2e2e2e;
  --card: #2f2f2f;
  --fg: #d4d4d4;
  --fg-bright: #f2efe9;
  --muted: #9b9b9b;
  --primary: #fe5c01;
  --primary-soft: #ff7a2e;
  --primary-deep: #ef4d00;
  --on-primary: #2b1200;
  --border: #484848;
  --radius: 12px;
  --topbar-h: 72px;
  --font: 'Manrope Variable', ui-sans-serif, system-ui, sans-serif;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background-color: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 16.5px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.container {
  /* Matches the guide's content area (sidebar + article, guide.css
     .guide-layout) so headers and sections share the same edges everywhere. */
  max-width: 1196px;
  margin-inline: auto;
  padding-inline: 24px;
}

h1,
h2,
h3 {
  color: var(--fg-bright);
  line-height: 1.15;
  text-wrap: balance;
  margin: 0;
}

p {
  margin: 0;
}

a {
  color: var(--primary);
  text-decoration-color: color-mix(in oklab, var(--primary) 45%, transparent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color 160ms ease;
}

a:hover {
  color: color-mix(in oklab, var(--primary) 80%, white);
}

:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
  border-radius: 4px;
}

.icon {
  width: 1.2em;
  height: 1.2em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}

/* ------------------------------------------------------------- Buttons */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border-radius: var(--radius);
  font-family: var(--font);
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color 160ms ease,
    border-color 160ms ease,
    box-shadow 160ms ease,
    transform 160ms ease;
}

.btn-primary {
  background: linear-gradient(180deg, var(--primary-soft), var(--primary-deep));
  color: var(--on-primary);
  border: 1px solid color-mix(in oklab, var(--primary) 60%, black);
  box-shadow:
    0 8px 28px -6px color-mix(in oklab, var(--primary) 45%, transparent),
    inset 0 1px 0 rgb(255 255 255 / 0.25);
}

.btn-primary:hover {
  background: linear-gradient(180deg, #ff8a45, var(--primary));
  color: var(--on-primary);
  transform: translateY(-1px);
  box-shadow:
    0 12px 34px -6px color-mix(in oklab, var(--primary) 55%, transparent),
    inset 0 1px 0 rgb(255 255 255 / 0.25);
}

.btn-compact {
  padding: 8px 16px;
  font-size: 0.9rem;
}

.btn-hero {
  /* Left padding = icon-text gap, so the icon sits centered between the
     button's left edge and the text (text stays put: 20+30+20 = 30+30+10). */
  padding: 16px 30px 16px 20px;
  gap: 20px;
  text-align: left;
}

.btn-hero .dl-icon .icon {
  width: 30px;
  height: 30px;
}

.dl-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.dl-label {
  font-size: 1.15rem;
  font-weight: 800;
  line-height: 1.25;
}

.dl-sub {
  font-size: 0.82rem;
  font-weight: 600;
  opacity: 0.78;
  line-height: 1.3;
}

.btn-secondary {
  padding: 12px 22px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  color: var(--fg-bright);
  font-size: 0.98rem;
}

.btn-secondary:hover {
  border-color: color-mix(in oklab, var(--primary) 60%, var(--border));
  color: var(--fg-bright);
  background: #353535;
}

.btn-ghost {
  padding: 10px 18px;
  color: var(--muted);
  font-size: 0.95rem;
  font-weight: 600;
  border: 1px solid transparent;
}

.btn-ghost:hover {
  color: var(--fg-bright);
}

.btn .arrow {
  transition: transform 160ms ease;
}

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

/* ------------------------------------------------------------- Topbar */

.topbar {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--topbar-h);
  z-index: 100;
  background: color-mix(in oklab, var(--bg) 86%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid color-mix(in oklab, var(--border) 70%, transparent);
  transform: translateY(-100%);
  opacity: 0;
  transition:
    transform 260ms ease,
    opacity 260ms ease;
}

.topbar.shown {
  transform: translateY(0);
  opacity: 1;
}

.topbar-inner {
  height: 100%;
  display: flex;
  align-items: center;
  gap: 40px;
}

.topbar-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 800;
  font-size: 0.95rem;
  color: var(--fg-bright);
  text-decoration: none;
  white-space: nowrap;
}

.topbar-brand:hover {
  color: var(--fg-bright);
}

/* Docs-style layout (à la octane.js): brand + text nav together on the LEFT,
   one right-aligned action cluster (search pill, divider, the Download CTA) —
   shared by the landing page and every guide page. The GitHub link lives in
   the footer only, on purpose. */
.topbar-nav {
  display: flex;
  gap: 40px;
}

.topbar-nav a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
}

/* The GitHub mark is a FILL glyph — the stroke-based .icon class would blank it. */
.gh-mark {
  width: 1.05em;
  height: 1.05em;
  fill: currentColor;
}

/* Inline nav icons (the Guide link's book) match the gh-mark's size. */
.topbar-nav .icon {
  width: 1.05em;
  height: 1.05em;
}

.topbar-nav a:hover {
  color: var(--fg-bright);
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-left: auto;
}

.topbar-sep {
  width: 1px;
  height: 22px;
  background: color-mix(in oklab, var(--border) 70%, transparent);
}

/* ------------------------------------------------------------- Search */

/* Topbar trigger; search.js (loaded by both pages) opens the modal below. */
.topbar-search {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px 7px 14px;
  border: 1px solid color-mix(in oklab, var(--border) 70%, transparent);
  border-radius: 999px;
  background: color-mix(in oklab, var(--bg-raised) 55%, transparent);
  color: var(--muted);
  font-family: var(--font);
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
  transition:
    color 160ms ease,
    border-color 160ms ease;
}

.topbar-search:hover {
  color: var(--fg-bright);
  border-color: var(--border);
}

.topbar-search kbd,
.search-box kbd {
  padding: 1px 6px;
  border: 1px solid color-mix(in oklab, var(--border) 70%, transparent);
  border-radius: 6px;
  background: var(--bg);
  font-family: var(--font);
  font-size: 0.72rem;
  color: var(--muted);
}

/* Inside the pill the hint chip rides along, stadium-shaped. */
.topbar-search kbd {
  border-radius: 999px;
  padding: 1px 8px;
}

/* Above the guide lightbox (200) — reachable via Ctrl+K while a screenshot is
   open. */
.guide-search {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: none;
  padding: min(14vh, 140px) 16px 16px;
  background: color-mix(in oklab, #000 62%, transparent);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.guide-search.open {
  display: block;
}

.search-panel {
  max-width: 620px;
  margin-inline: auto;
  overflow: hidden;
  border: 1px solid color-mix(in oklab, var(--border) 80%, transparent);
  border-radius: var(--radius);
  background: var(--bg);
  box-shadow: 0 24px 80px rgb(0 0 0 / 0.55);
}

.search-box {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid color-mix(in oklab, var(--border) 45%, transparent);
  color: var(--muted);
}

.search-box input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  color: var(--fg-bright);
  font-family: var(--font);
  font-size: 1.05rem;
}

.search-box input::placeholder {
  color: var(--muted);
}

/* Esc already clears the way out — the native clear ✕ would double up. */
.search-box input::-webkit-search-cancel-button {
  display: none;
}

.search-close {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}

.search-results {
  list-style: none;
  margin: 0;
  padding: 8px;
  max-height: min(56vh, 480px);
  overflow: auto;
}

.search-results:empty {
  padding: 0;
}

.search-results a {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border-radius: 8px;
  text-decoration: none;
}

.search-results li.active a,
.search-results a:hover {
  background: color-mix(in oklab, var(--primary) 12%, transparent);
}

.result-path {
  color: var(--fg-bright);
  font-weight: 600;
  font-size: 0.95rem;
}

.result-sep {
  color: var(--muted);
  font-weight: 400;
}

.result-excerpt {
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.search-results mark {
  background: color-mix(in oklab, var(--primary) 24%, transparent);
  color: var(--primary-soft);
  border-radius: 3px;
  padding: 0 1px;
}

.search-status {
  margin: 0;
  padding: 12px 16px;
  color: var(--muted);
  font-size: 0.88rem;
}

.search-status[hidden] {
  display: none;
}

@media (max-width: 720px) {
  .topbar-nav {
    display: none;
  }

  .topbar-inner {
    justify-content: space-between;
  }

  /* The text nav is gone — search shrinks to an icon-only round button; the
     divider goes too, leaving brand · search · Download. Tighter padding/gaps
     so that trio actually fits (the old topbar clipped Download at 400px). */
  .topbar-search span,
  .topbar-search kbd,
  .topbar-sep {
    display: none;
  }

  .topbar-search {
    padding: 8px;
    border-radius: 50%;
  }

  .topbar .container {
    padding-inline: 16px;
  }

  .topbar-inner,
  .topbar-actions {
    gap: 12px;
  }
}

@media (max-width: 480px) {
  /* Phone widths: the Download button stays, as its orange icon alone. */
  .topbar .btn-label {
    display: none;
  }
}

/* ------------------------------------------------------------- Hero */

.hero {
  position: relative;
  padding: clamp(56px, 10vh, 110px) 0 clamp(56px, 8vh, 96px);
  overflow: hidden;
  display: flex;
}

/* Desktop and up: the hero owns 85% of the viewport, and the guide teaser
   link gets extra clearance above it (see .hero .btn-ghost). On phones and
   tablets (and short screens) the content height wins and nothing changes. */
@media (min-width: 1024px) {
  .hero {
    min-height: 85vh;
    padding-bottom: 0;
  }
}

/* Soft orange glow behind the logo — pure decoration, painted cheap. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(520px 420px at 50% 150px, rgb(254 92 1 / 0.13), transparent 70%),
    radial-gradient(900px 500px at 50% -200px, rgb(255 255 255 / 0.04), transparent 70%);
  pointer-events: none;
}

.hero-inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%;
}

.download-block {
  margin-bottom: 28px;
}

.hero-logo {
  width: clamp(190px, 34vw, 300px);
  height: auto;
  filter: drop-shadow(0 18px 48px rgb(254 92 1 / 0.28));
}

.hero h1 {
  margin-top: 26px;
  font-size: clamp(2.1rem, 6vw, 3.3rem);
  font-weight: 800;
  letter-spacing: -0.02em;
}

.tagline {
  margin-top: 18px;
  max-width: 620px;
  font-size: clamp(1.05rem, 2.4vw, 1.25rem);
  color: var(--fg);
}

.tagline strong {
  color: var(--fg-bright);
}

.pipeline {
  margin-top: 18px;
  color: var(--muted);
  font-size: 0.95rem;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px 10px;
}

.pipeline-chips {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.chip {
  border: 1px solid var(--border);
  background: var(--bg-raised);
  border-radius: 999px;
  padding: 3px 12px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--fg);
  white-space: nowrap;
}

.chip-arrow {
  color: var(--primary);
  font-weight: 700;
}

.download-block {
  margin-top: 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* The `hidden` attribute must win over the display set by .btn etc. */
[hidden] {
  display: none !important;
}

/* Unsupported OS (mobile, Linux): main.js strips the href and adds this
   class — same big hero button, just gray and inert. */
.btn-primary.btn-disabled,
.btn-primary.btn-disabled:hover {
  background: var(--bg-raised);
  color: var(--muted);
  border: 1px solid var(--border);
  box-shadow: none;
  transform: none;
  cursor: default;
  pointer-events: none;
}

.dl-alt {
  margin-top: 14px;
  font-size: 0.88rem;
  color: var(--muted);
}

.dl-alt a {
  color: var(--muted);
}

.dl-alt a:hover {
  color: var(--fg-bright);
}

/* Mobile: compress the hero — the pipeline line (subtitle + tool chips) and
   the alt-download links go; title, tagline and the buttons carry it. Sits
   after the base rules above so it wins on source order. */
@media (max-width: 720px) {
  .pipeline,
  .dl-alt {
    display: none;
  }
}

.hero .btn-ghost {
  margin-top: 26px;
}

/* Book symbol on the guide buttons (hero teaser + features CTA) — outside
   the shine span, since background-clip: text would leave an SVG invisible. */
.btn-book {
  color: var(--primary);
}

@media (min-width: 1024px) {
  .hero .btn-ghost {
    margin-top: 30px;
  }
}

/* Shine: the text is painted with an orange gradient (primary plus lighter
   hues) clipped to the glyphs, and a bright band sweeps through it. The
   gradient starts and ends on the same color, so the linear loop is seamless.
   Goes on the element whose only background IS the text (the hero teaser
   link) or on a label span inside a surfaced button (the features CTA). */
.text-shine,
.text-shine:hover {
  background-image: linear-gradient(
    100deg,
    var(--primary) 0%,
    var(--primary-soft) 40%,
    #ffc9a1 50%,
    var(--primary-soft) 60%,
    var(--primary) 100%
  );
  background-size: 250% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: guide-shine 3s linear infinite;
}

@keyframes guide-shine {
  from {
    background-position: 250% 0;
  }
  to {
    background-position: 0 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .text-shine,
  .text-shine:hover,
  .cta-strip .btn-secondary {
    animation: none;
  }
}

/* ------------------------------------------------------------- Sections */

.section {
  padding: clamp(56px, 9vh, 96px) 0;
  scroll-margin-top: calc(var(--topbar-h) + 8px);
  border-top: 1px solid color-mix(in oklab, var(--border) 45%, transparent);
}

/* No divider between the hero and the first section — the teaser window
   floating in makes its own entrance. (Not :first-of-type — the hero itself
   is a <section>, so that would match the hero.) */
.hero + .section {
  border-top: 0;
}

.section h2 {
  font-size: clamp(1.6rem, 4vw, 2.2rem);
  font-weight: 800;
  letter-spacing: -0.015em;
}

/* Screenshot teasers atop the "why" and "about" sections: the top of a real
   app page, cropped to a fixed 2560/1130 band and fading out so the heading
   rides up into it. The mask keeps the upper half of the band fully opaque
   and fades out across the lower half, giving the heading a long ramp.

   No faked window chrome here, deliberately. A drawn titlebar (traffic dots on
   a 38px bar) sat badly with the 3D tilt below — the bar is the one part of the
   frame the eye reads as flat, so rotating it broke the illusion it existed to
   create — and its px-fixed dots were oversized against the teaser on a phone.
   The shot's own rounded frame + ring is the framing. */
.section-teaser {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  aspect-ratio: 2560 / 1130;
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  /* Depth + a faint brand-colored ambience so the dark app separates from the
     dark page. The mask also fades the glow toward the bottom — coherent with
     the image's own fade. */
  box-shadow:
    0 30px 80px -24px rgb(0 0 0 / 0.6),
    0 0 90px -24px color-mix(in oklab, var(--primary) 45%, transparent);
}

/* 1px ring on top of the shot, fading out with the mask like everything else. */
.section-teaser::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid color-mix(in oklab, var(--border) 90%, transparent);
  border-radius: inherit;
  pointer-events: none;
}

.section-teaser img {
  display: block;
  width: 100%;
  height: auto;
}

/* 3D entrance + parallax: the frame starts tilted back and flattens as it
   scrolls toward mid-viewport, while the shot INSIDE drifts a touch slower
   than the frame (classic depth cue). Both are scroll-driven CSS running on
   the compositor — no JS. The image starts pulled up 44px and gives back at
   most 38px across the whole traversal, so its top edge stays negative and the
   sliding shot can never open a gap at the top of the frame. Browsers without
   animation-timeline just show the flat, static framed shot. */
@supports (animation-timeline: view()) {
  .section-teaser {
    transform-origin: 50% 100%;
    view-timeline: --teaser;
    animation: teaser-flatten linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 45%;
  }

  .section-teaser img {
    margin-top: -44px;
    animation: teaser-parallax linear both;
    animation-timeline: --teaser;
    animation-range: cover 0% cover 100%;
  }
}

@keyframes teaser-flatten {
  from {
    transform: perspective(1400px) rotateX(9deg) scale(0.97);
  }

  to {
    transform: none;
  }
}

@keyframes teaser-parallax {
  from {
    transform: translateY(0);
  }

  to {
    transform: translateY(38px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .section-teaser,
  .section-teaser img {
    animation: none;
  }

  .section-teaser img {
    margin-top: 0;
  }
}

/* Pull the heading up into the teaser's faded zone so title and image blend. */
.section-teaser + h2 {
  position: relative;
  margin-top: -40px;
}

/* The About teaser holds more of its shot before fading (the why teaser
   starts its fade at 62%). */
#about .section-teaser {
  -webkit-mask-image: linear-gradient(to bottom, #000 75%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 75%, transparent 100%);
}

.section h2 em {
  font-style: normal;
  color: var(--primary);
}

.section-lead {
  margin-top: 18px;
  margin-inline: auto;
  max-width: 760px;
  color: var(--fg);
  text-align: center;
}

/* Cards */

.cards {
  /* Generous gap after the lead paragraph so the prose settles before the grid. */
  margin-top: 128px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px 24px;
  transition:
    border-color 160ms ease,
    transform 160ms ease;
}

.card:hover {
  border-color: color-mix(in oklab, var(--primary) 45%, var(--border));
  transform: translateY(-2px);
}

.card-icon {
  width: 30px;
  height: 30px;
  color: var(--primary);
  stroke-width: 1.7;
}

.card h3 {
  margin-top: 14px;
  font-size: 1.08rem;
  font-weight: 800;
}

.card p {
  margin-top: 8px;
  font-size: 0.94rem;
  color: color-mix(in oklab, var(--fg) 82%, var(--muted));
}

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

/* Features — the compact "why you'll want it" checklist. */

.features {
  margin: 64px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 32px 64px;
}

.features li {
  display: flex;
  gap: 12px;
  font-size: 0.97rem;
  color: color-mix(in oklab, var(--fg) 82%, var(--muted));
}

.features strong {
  color: var(--fg-bright);
}

.feature-check {
  width: 22px;
  height: 22px;
  color: var(--primary);
  flex-shrink: 0;
  margin-top: 1px;
  stroke-width: 2.5;
}

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

/* The guide call-out closing the features section. Its button border pulses
   toward orange in step with the label's shine: the sweep travels 250% of
   the box per 3s loop, so the bright band is actually over the text only
   during the ~67–93% stretch — the border peaks with it at 80%. */
.cta-strip .btn-secondary {
  animation: guide-border-pulse 3s ease-in-out infinite;
}


@keyframes guide-border-pulse {
  0%,
  60%,
  100% {
    border-color: var(--border);
  }
  80% {
    border-color: color-mix(in oklab, var(--primary-soft) 40%, var(--border));
  }
}

.cta-strip {
  margin-top: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 18px;
  background: color-mix(in oklab, var(--primary) 8%, var(--card));
  border: 1px solid color-mix(in oklab, var(--primary) 30%, var(--border));
  border-radius: var(--radius);
  padding: 22px 26px;
}

.cta-strip p {
  font-size: 1.02rem;
  color: var(--fg-bright);
}

/* Every section heading sits centered on the page axis — same axis as the
   hero and the card grids; About also centers its prose. */

.section h2 {
  text-align: center;
}

#about p {
  margin: 18px auto 0;
  max-width: 780px;
  text-align: center;
}

/* Resources */

.resources {
  margin-top: 64px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
}

.resource {
  display: block;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px;
  text-decoration: none;
  transition:
    border-color 160ms ease,
    transform 160ms ease;
}

.resource:hover {
  border-color: color-mix(in oklab, var(--primary) 55%, var(--border));
  transform: translateY(-2px);
}

.resource h3 {
  font-size: 0.99rem;
  font-weight: 800;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The symbol in front of each card title; the octocat variant carries
   .gh-mark for its fill, this class sizes and colors them all alike. */
.resource-icon {
  width: 17px;
  height: 17px;
  color: var(--primary);
}

.resource .ext {
  margin-left: auto;
  color: var(--muted);
  font-weight: 600;
  transition: color 160ms ease, transform 160ms ease;
}

.resource:hover .ext {
  color: var(--primary);
}

.resource p {
  margin-top: 8px;
  font-size: 0.87rem;
  color: var(--muted);
}

.resource.featured {
  border-color: color-mix(in oklab, var(--primary) 45%, var(--border));
  background: color-mix(in oklab, var(--primary) 7%, var(--card));
}

.resource.featured p {
  color: color-mix(in oklab, var(--fg) 80%, var(--muted));
}

@media (max-width: 900px) {
  .resources {
    grid-template-columns: repeat(2, 1fr);
  }
}

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

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

.footer {
  border-top: 1px solid color-mix(in oklab, var(--border) 45%, transparent);
  padding: 34px 0 44px;
}

.footer-inner {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 0.88rem;
  color: var(--muted);
}

.footer a {
  color: var(--muted);
}

.footer a:hover {
  color: var(--fg-bright);
}

.footer-sep {
  margin-inline: 6px;
}

/* GitHub link in the footer: octocat mark inline with the label. */
.footer .gh-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  vertical-align: bottom;
}

.footer-fine {
  font-size: 0.8rem;
  opacity: 0.75;
}

/* ------------------------------------------------------------- Motion */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .topbar,
  .btn,
  .card,
  .resource,
  .btn .arrow {
    transition: none;
  }
}
