/* custom.css — only what Tailwind can't do.
   Tailwind utilities come from the CDN; theme tokens are configured in includes/header.php.
   Color values use CSS variables so we can flip them all at once via the .dark class on <html>.

   Design language: bold creative-agency (inspired by the "Lettery" theme) rendered in the
   Resulve brand — cream + near-black + Resulve amber, oversized Space Grotesk display type,
   and scroll-driven motion. No build step, no framework: Tailwind CDN + Alpine + vanilla JS. */

/* ───────── Fonts ─────────
   Matches the Lettery reference: Outfit (geometric display) + Inter for body. */
:root {
  --font-display: 'Outfit', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body:    'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --btn-radius:   9999px;
}

/* ───────── Theme variables (light) ─────────
   Resulve's own brand palette — warm stone + burnt amber (kept by request). Only the fonts,
   layout and motion follow the Lettery reference; the colors stay Resulve. */
:root {
  --color-bg:           #fafaf9;
  --color-bg-warm:      #f5f5f4;
  --color-ink:          #1c1917;
  --color-ink-muted:    #57534e;
  --color-ink-soft:     #78716c;
  --color-hair:         #e7e5e4;
  --color-accent:       #b45309;
  --color-accent-hover: #92400e;
  --color-accent-bright: #f59e0b;  /* brighter Resulve amber — for fills & dark surfaces */
  --color-code-bg:      #1c1917;   /* code-block background, kept dark in both modes */
  --color-code-fg:      #fafaf9;
  --color-inline-code-bg: #f5f5f4;
  --color-card-bg:      #ffffff;   /* white surfaces (form inputs, preview pane) */
}

/* ───────── Theme variables (dark) — toggled via .dark on <html> ───────── */
:root.dark {
  --color-bg:           #0c0a09;
  --color-bg-warm:      #1c1917;
  --color-ink:          #fafaf9;
  --color-ink-muted:    #a8a29e;
  --color-ink-soft:     #78716c;
  --color-hair:         #292524;
  --color-accent:       #f59e0b;
  --color-accent-hover: #fbbf24;
  --color-accent-bright: #fbbf24;
  --color-code-bg:      #292524;
  --color-code-fg:      #fafaf9;
  --color-inline-code-bg: #292524;
  --color-card-bg:      #1c1917;
}

/* Smooth fade on theme switch. */
html { transition: background-color 0.15s ease, color 0.15s ease; }

/* Hide Alpine-managed elements until Alpine boots, to prevent a flash. */
[x-cloak] { display: none !important; }

/* ───────── Display typography ─────────
   Repointing Tailwind's `serif` family to Space Grotesk (see header.php config) turns every
   existing `font-serif` heading into the agency display face with zero markup churn. Here we
   give those headings weight + tight tracking so they read as a bold grotesk, not a light one. */
.font-serif        { font-weight: 500; letter-spacing: -0.01em; }
main h1, .h-display { letter-spacing: -0.03em; line-height: 1.0; font-weight: 700; }
main h2            { letter-spacing: -0.02em; }

/* Oversized hero / section display type. */
.h-display {
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 8vw, 6.25rem);
}
.h-section {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.02;
  font-size: clamp(2rem, 4.5vw, 3.5rem);
}

/* Eyebrow label with a leading accent tick — the small agency section marker. */
.eyebrow-tick { display: inline-flex; align-items: center; gap: 0.6rem; }
.eyebrow-tick::before {
  content: ""; width: 1.75rem; height: 2px; background: var(--color-accent); display: inline-block;
}

/* ───────── Buttons ─────────
   Pill-shaped, lifting CTAs. Scoped to the actual button pattern (inline-block link / <button>
   with horizontal padding) so text-only "Learn more →" links and cards are untouched. */
main a.inline-block[class*="px-"],
main button[class*="px-"],
footer a.inline-block[class*="px-"],
.btn-pill {
  border-radius: var(--btn-radius);
  font-weight: 600;
  transition: transform 0.2s ease, background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
  will-change: transform;
}
main a.inline-block[class*="px-"]:hover,
main button[class*="px-"]:hover,
footer a.inline-block[class*="px-"]:hover,
.btn-pill:hover { transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
  main a.inline-block[class*="px-"]:hover,
  main button[class*="px-"]:hover,
  footer a.inline-block[class*="px-"]:hover,
  .btn-pill:hover { transform: none; }
}

/* ───────── Scroll reveal ─────────
   Progressive enhancement: elements are only hidden once JS adds `.reveal-ready` to <html>
   (see js/script.js), so with JS disabled every section is fully visible. */
html.reveal-ready [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
html.reveal-ready [data-reveal].is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  html.reveal-ready [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* ───────── Marquee ─────────
   Seamless horizontal scroll. The track holds the content twice; translating it -50% loops
   invisibly. Pauses on hover and for reduced-motion users. */
.marquee { overflow: hidden; white-space: nowrap; }
.marquee__track {
  display: inline-flex; align-items: center;
  animation: marquee-scroll 34s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__item { display: inline-flex; align-items: center; }
.marquee__dot { color: var(--color-accent); margin: 0 1.25rem; font-size: 0.6em; }
@keyframes marquee-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .marquee__track { animation: none; } }

/* ───────── Dark band ─────────
   The alternating full-contrast section that gives the site its agency rhythm. */
.band-dark { background: var(--color-ink); color: var(--color-bg); }
.band-dark .band-muted { color: color-mix(in srgb, var(--color-bg) 70%, transparent); }

/* ───────── Preloader ─────────
   Full-screen overlay with a counting number, matching Lettery's "Loading • Please wait"
   intro. JS removes .is-loading from <html> on window load; CSS handles the fade-out.
   Only active once JS adds .js-preload, so no-JS visitors never see a stuck screen. */
.preloader {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-ink); color: var(--color-bg);
  opacity: 0; visibility: hidden; transition: opacity 0.6s ease, visibility 0s linear 0.6s;
}
html.js-preload.is-loading .preloader { opacity: 1; visibility: visible; transition: none; }
.preloader__count { font-family: var(--font-display); font-weight: 700; font-size: clamp(3rem, 12vw, 8rem); letter-spacing: -0.04em; line-height: 1; }
html.is-loading body { overflow: hidden; }
@media (prefers-reduced-motion: reduce) { .preloader { transition: none; } }

/* ───────── Custom cursor ─────────
   A dot + a lagging ring that grows over interactive elements. Enabled only by JS on
   fine-pointer devices (.has-cursor on <html>); touch and reduced-motion keep the native cursor. */
.cursor-dot, .cursor-ring {
  position: fixed; top: 0; left: 0; z-index: 300; pointer-events: none;
  border-radius: 9999px; transform: translate3d(-50%, -50%, 0); opacity: 0;
}
.cursor-dot  { width: 7px; height: 7px; background: var(--color-accent); }
.cursor-ring { width: 34px; height: 34px; border: 1.5px solid var(--color-accent);
  transition: width 0.25s ease, height 0.25s ease, background-color 0.25s ease, opacity 0.3s ease; }
html.has-cursor .cursor-dot, html.has-cursor .cursor-ring { opacity: 1; }
html.has-cursor.cursor-hover .cursor-ring { width: 58px; height: 58px; background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
html.has-cursor * { cursor: none !important; }

/* ───────── Word-by-word heading reveal ─────────
   JS wraps each word of [data-words] in <span class="word"><span>…</span></span>. Words rise
   into place with a stagger when the heading scrolls in. No-JS / reduced-motion = plain text. */
.words-split .word { display: inline-block; overflow: hidden; vertical-align: top; padding-bottom: 0.14em; margin-bottom: -0.14em; }
.words-split .word > span { display: inline-block; transform: translateY(115%); transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1); transition-delay: var(--word-delay, 0s); }
.words-split.is-visible .word > span { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .words-split .word > span { transform: none; transition: none; }
}

/* ───────── Scale-on-scroll media ─────────
   Wrap media in <figure class="scale-media" data-scale>. The inner img/box starts slightly
   enlarged and settles to 1× as it enters view — Lettery's mil-scale-img reveal. */
.scale-media { overflow: hidden; }
.scale-media > * { transform: scale(1.18); transition: transform 1.1s cubic-bezier(0.16, 1, 0.3, 1); will-change: transform; display: block; }
.scale-media.is-visible > * { transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
  .scale-media > * { transform: none; transition: none; }
}

/* ───────── Editorial body typography (Parsedown-rendered Markdown) ─────────
   Wrap rendered HTML in <div class="prose">. Headings use the display face to match the site;
   colors driven by CSS variables so they swap with the theme. */
.prose                  { color: var(--color-ink); max-width: 38rem; }
.prose > * + *          { margin-top: 1.25em; }
.prose h2               { font-family: var(--font-display); font-weight: 700; letter-spacing: -0.02em; font-size: 1.9rem; line-height: 1.15; margin-top: 2.5em; margin-bottom: 0.5em; color: var(--color-ink); }
.prose h3               { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.01em; font-size: 1.5rem; line-height: 1.3; margin-top: 2em; margin-bottom: 0.5em; color: var(--color-ink); }
.prose h4               { font-family: var(--font-display); font-weight: 600; font-size: 1.25rem; line-height: 1.4; margin-top: 1.5em; margin-bottom: 0.5em; color: var(--color-ink); }
.prose p                { line-height: 1.75; }
.prose a                { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
.prose a:hover          { color: var(--color-accent-hover); }
/* Lists: Tailwind's Preflight resets `list-style` to none on ul/ol, which hides bullets and
   numbers in rendered Markdown. Restore them explicitly, or every list looks like plain
   indented text. `list-style-position: outside` keeps wrapped lines aligned under the text. */
.prose ul, .prose ol    { padding-left: 1.5rem; list-style-position: outside; }
.prose ul               { list-style-type: disc; }
.prose ol               { list-style-type: decimal; }
.prose ul ul            { list-style-type: circle; margin-top: 0.5em; }
.prose ol ol            { list-style-type: lower-alpha; margin-top: 0.5em; }
.prose li::marker       { color: var(--color-accent); }
.prose ol > li::marker  { font-weight: 600; }
.prose ul > li,
.prose ol > li          { line-height: 1.7; margin-top: 0.5em; padding-left: 0.25rem; }

/* Tables: Preflight also strips table borders/spacing. Markdown tables are common in posts,
   so give them real styling. `display:block` + `overflow-x:auto` lets a wide table scroll
   inside itself on mobile instead of forcing the whole page to scroll sideways. */
.prose table {
  display: block;
  width: max-content;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: 0.95em;
  margin-top: 1.75em;
  margin-bottom: 1.75em;
}
.prose thead           { background: var(--color-bg-warm); }
.prose th              { font-weight: 600; text-align: left; color: var(--color-ink); }
.prose th, .prose td   { border: 1px solid var(--color-hair); padding: 0.6rem 0.9rem; vertical-align: top; }
.prose tbody tr:nth-child(even) { background: color-mix(in srgb, var(--color-bg-warm) 45%, transparent); }
/* First column often holds the row label; make it read as one. */
.prose tbody td:first-child { font-weight: 600; color: var(--color-ink); }
.prose blockquote       { border-left: 3px solid var(--color-accent); padding-left: 1.25rem; font-style: italic; color: var(--color-ink-muted); }
.prose code             { background: var(--color-inline-code-bg); padding: 1px 6px; border-radius: 3px; font-size: 0.9em; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.prose pre              { background: var(--color-code-bg); color: var(--color-code-fg); padding: 1rem; border-radius: 6px; overflow-x: auto; font-size: 0.875rem; line-height: 1.5; }
.prose pre code         { background: transparent; padding: 0; color: inherit; font-size: 1em; }
.prose strong           { font-weight: 600; color: var(--color-ink); }
.prose em               { font-style: italic; }
.prose hr               { border: 0; border-top: 1px solid var(--color-hair); margin: 2em 0; }
.prose img              { max-width: 100%; height: auto; border-radius: 4px; }

/* Respect users who prefer reduced motion. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ───────── Card hover treatment ─────────
   The editorial card pattern (services index, work grid, "more work" / "related" strips,
   homepage featured grid). Subtle lift on hover. */
a.group.border-t-2 {
  transition: transform 0.2s ease, border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
  will-change: transform;
}
a.group.border-t-2:hover { transform: translateY(-3px); }
@media (prefers-reduced-motion: reduce) {
  a.group.border-t-2:hover { transform: none; }
}

/* Image-zoom hook: wrap a card's hero image in <div class="card-image"> and the image scales
   subtly on hover. (Inert until real hero images exist.) */
.card-image          { overflow: hidden; border-radius: 4px; }
.card-image img      { transition: transform 0.4s ease; display: block; width: 100%; height: auto; }
a:hover .card-image img { transform: scale(1.04); }
@media (prefers-reduced-motion: reduce) {
  a:hover .card-image img { transform: none; }
}

/* ───────── Inline data charts ─────────
   Native bar charts for case studies. Built from theme tokens rather than exported as PNGs,
   so they follow light/dark mode, stay sharp at any zoom, and keep their labels as real
   selectable text (a flat image of a chart is invisible to search and screen readers). */
.chart {
  margin: 2.5rem 0;
  padding: 1.5rem 1.5rem 1.25rem;
  border: 1px solid var(--color-hair);
  border-radius: 1rem;
  background: var(--color-bg-warm);
}
.chart__title { font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; color: var(--color-ink); margin: 0; letter-spacing: -0.01em; }
.chart__sub   { font-size: 0.85rem; color: var(--color-ink-soft); margin: 0.25rem 0 0; }
.chart__bars  { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 1rem; align-items: end; min-height: 210px; margin-top: 1.75rem; }
.chart__col   { display: flex; flex-direction: column; justify-content: flex-end; height: 100%; text-align: center; }
.chart__val   { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.05rem, 3vw, 1.6rem); color: var(--color-ink); line-height: 1; margin-bottom: 0.5rem; }
.chart__fill  { border-radius: 6px 6px 0 0; background: var(--color-hair); min-height: 6px; }
.chart__fill--mid  { background: color-mix(in srgb, var(--color-accent) 55%, transparent); }
.chart__fill--peak { background: var(--color-accent); }
.chart__label { font-size: 0.9rem; color: var(--color-ink); margin-top: 0.6rem; font-weight: 600; }
.chart__note  { font-size: 0.75rem; color: var(--color-ink-soft); margin-top: 0.15rem; line-height: 1.35; }
.chart__axis  { border-top: 1px solid var(--color-hair); margin-top: 0; }
/* Two-bar comparison variant gets a delta badge between the columns. */
.chart__delta { font-family: var(--font-display); font-weight: 700; color: var(--color-accent); font-size: clamp(1.1rem, 3.5vw, 1.75rem); align-self: center; }
@media (max-width: 30rem) {
  .chart__bars { min-height: 170px; gap: 0.5rem; }
  .chart__note { display: none; }
}

/* ───────── Client logo strip ─────────
   An auto-scrolling monochrome logo row (like the Lettery reference). Logos read as a
   calm grey set and bloom to full colour on hover; the whole strip pauses on hover. */
/* Static, selectable logo row. Replaced the marquee here because a moving logo is a poor
   click target and this section now carries the testimonial + case-study CTA. */
.client-row {
  display: flex; flex-wrap: wrap; align-items: stretch;
  gap: 0.75rem; justify-content: flex-start;
}
@media (max-width: 40rem) { .client-row { gap: 0.5rem; justify-content: center; } }

/* Each logo sits on its own pale chip. The earlier treatment flattened every mark to a
   silhouette with brightness(0), which only works for logos that are a single shape on
   transparency. A mark carrying a white-filled badge (eHost) turned into a black blob.
   A chip keeps every logo on a light surface in BOTH themes, so marks can be shown in
   their own colours and nothing has to be filtered into a blob to stay legible. */
.client-logo {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0.85rem 1.35rem; border-radius: 0.75rem;
  background: #fff; border: 1px solid var(--color-hair);
}
:root.dark .client-logo { background: #f5f4f2; border-color: transparent; }
.client-logo--ondark,
:root.dark .client-logo--ondark { background: #292524; border-color: #292524; }
.client-logo img {
  filter: grayscale(1); opacity: 0.55;
  transition: filter 0.3s ease, opacity 0.3s ease;
}
.client-logo span {
  filter: grayscale(1); opacity: 0.6; color: #1c1917;
  transition: filter 0.3s ease, opacity 0.3s ease;
}

@media (prefers-reduced-motion: reduce) {
  .client-logo img, .client-logo span { transition: none; }
}

/* ───────── Testimonial cards ─────────
   A speech bubble tailing down to the client's logo. The card is a flex column and the
   bubble takes the free space, so every tail and avatar in a row lines up even when the
   quotes are different lengths. */
/* Rail: scrolls sideways so the list can grow without reflowing into a second row.
   Padded with the page gutter so the first card lines up with the heading above it
   while cards can still bleed to the viewport edge as you scroll. */
.tcard-rail {
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  /* Scrollbar hidden: with few cards the near-full-width thumb reads as a stray rule
     under the section. Drag (script.js), touch and keyboard still scroll the rail. */
  scrollbar-width: none;
  /* Headroom for the hover lift: overflow-y clips at the padding edge, so without
     this the bubble's translateY(-4px) cuts off its top border. */
  padding-top: 6px;
}
.tcard-rail::-webkit-scrollbar { display: none; }
.tcard-rail:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 4px; }
/* Pointer drag (added by script.js). The grab cursor only makes sense on a mouse. */
.tcard-rail.is-draggable { cursor: grab; }
.tcard-rail.is-dragging  { cursor: grabbing; scroll-snap-type: none; }
/* A drag must not fire the link underneath it. */
.tcard-rail.is-dragging a { pointer-events: none; }

.tcard-track {
  display: flex; align-items: stretch; gap: 1.5rem;
  /* 64rem matches Tailwind's `page` maxWidth in includes/header.php, so the first card
     starts flush with the heading rather than at the raw viewport edge. */
  padding: 0 max(1.5rem, calc((100vw - 64rem) / 2 + 1.5rem));
  width: max-content;
}
@media (max-width: 40rem) { .tcard-track { gap: 1rem; padding: 0 1.25rem; } }

.tcard {
  display: flex; flex-direction: column;
  flex: 0 0 auto; width: 21rem; max-width: 82vw;
  scroll-snap-align: start;
}
.tcard__bubble {
  position: relative; flex: 1 1 auto;
  background: var(--color-card-bg);
  border: 1px solid var(--color-hair);
  border-radius: 0.5rem;
  padding: 2.25rem 2rem 2rem;
  text-align: center;
  display: flex; flex-direction: column; align-items: center;
}
/* Tail: two stacked triangles so the 1px border reads on the pointer too. */
.tcard__bubble::before,
.tcard__bubble::after {
  content: ""; position: absolute; left: 50%; width: 0; height: 0;
  border-left: 13px solid transparent; border-right: 13px solid transparent;
}
.tcard__bubble::before {
  bottom: -13px; margin-left: -13px;
  border-top: 13px solid var(--color-hair);
}
.tcard__bubble::after {
  bottom: -12px; margin-left: -13px;
  border-top: 13px solid var(--color-card-bg);
}
.tcard__mark {
  font-family: var(--font-display); font-size: 2.5rem; line-height: 1;
  color: var(--color-accent-bright); margin-bottom: 0.75rem;
}
.tcard__quote {
  margin: 0; border: 0; padding: 0; font-style: normal;
  font-size: 0.975rem; line-height: 1.7; color: var(--color-ink-muted);
  max-width: 34rem;
}
.tcard__quote::before { content: "\201C"; }
.tcard__quote::after  { content: "\201D"; }
.tcard__cta {
  display: inline-flex; align-items: center; gap: 0.45rem; margin-top: 1.5rem;
  font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--color-accent); transition: gap 0.2s ease;
}
.tcard__cta:hover { gap: 0.8rem; color: var(--color-accent-hover); }

.tcard__person {
  display: flex; flex-direction: column; align-items: center;
  padding-top: 2rem; text-align: center;
}
.tcard__avatar {
  display: flex; align-items: center; justify-content: center;
  width: 4.5rem; height: 4.5rem; border-radius: 50%;
  background: #f0efed; overflow: hidden; padding: 0.85rem;
  margin-bottom: 1rem;
}
.tcard__avatar img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* Light wordmarks (white on transparency) need a dark chip in BOTH themes, since the
   default chip is pale in both. Set from the logo's own measured luminance. */
.tcard__avatar--ondark { background: #292524; }
.tcard__avatar--text {
  font-family: var(--font-display); font-weight: 600; font-size: 1.1rem;
  color: #57534e; letter-spacing: 0.04em;
}
.tcard__name { font-weight: 600; color: var(--color-ink); font-size: 1.05rem; }
.tcard__role { margin-top: 0.2rem; font-size: 0.9rem; color: var(--color-ink-soft); }
.tcard__stars {
  margin-top: 0.6rem; color: var(--color-accent-bright);
  font-size: 0.95rem; letter-spacing: 0.15em;
}

/* Highlight on hover only, drawn from the palette rather than a flat block of yellow:
   an accent-tinted surface, an accent border and a small lift. The tint is mixed from
   --color-accent so it tracks whichever theme is active instead of hardcoding amber.
   :focus-within carries the same state for keyboard users, and .is-near does it for
   touch, where there is no hover at all (set by script.js on the centred card). */
.tcard__bubble { transition: background 0.25s ease, border-color 0.25s ease, transform 0.25s ease; }
.tcard:hover .tcard__bubble,
.tcard:focus-within .tcard__bubble,
.tcard.is-near .tcard__bubble {
  background: color-mix(in srgb, var(--color-accent) 10%, var(--color-card-bg));
  border-color: var(--color-accent);
  transform: translateY(-4px);
}
.tcard:hover .tcard__bubble::before,
.tcard:focus-within .tcard__bubble::before,
.tcard.is-near .tcard__bubble::before { border-top-color: var(--color-accent); }
.tcard:hover .tcard__bubble::after,
.tcard:focus-within .tcard__bubble::after,
.tcard.is-near .tcard__bubble::after {
  border-top-color: color-mix(in srgb, var(--color-accent) 10%, var(--color-card-bg));
}
.tcard:hover .tcard__quote,
.tcard:focus-within .tcard__quote,
.tcard.is-near .tcard__quote { color: var(--color-ink); }
@media (prefers-reduced-motion: reduce) {
  .tcard__bubble { transition: none; }
  .tcard:hover .tcard__bubble,
  .tcard:focus-within .tcard__bubble,
  .tcard.is-near .tcard__bubble { transform: none; }
}

.marquee__track--slow { animation-duration: 48s; }

/* ───────── Services nav flyout ─────────
   Desktop only: the grouped services panel opens on hover or keyboard focus
   (focus-within covers tabbing through the panel's links). On mobile the
   services sub-list renders inline inside the nav dropdown instead. */
@media (min-width: 768px) {
  .nav-svc:hover .nav-svc__panel,
  .nav-svc:focus-within .nav-svc__panel { display: block; }
}

/* Flyout card — uses the elevated-surface token so it reads as a raised card in
   both themes (white on cream, warm charcoal on near-black) instead of blending
   into the page background. Shadows barely register on dark backgrounds, so the
   dark theme leans on surface contrast + border and a deeper shadow. */
.nav-svc__card {
  background: var(--color-card-bg);
  border: 1px solid var(--color-hair);
  box-shadow: 0 20px 45px -18px rgb(28 25 23 / 0.25);
}
:root.dark .nav-svc__card { box-shadow: 0 24px 50px -16px rgb(0 0 0 / 0.7); }

/* ───────── Band-aware controls ─────────
   .band-dark inverts with the theme (dark band on the light site, light band on
   the dark site), so nothing inside it may hardcode white/black. These classes
   derive every color from the band's own foreground (--band-fg). */
.band-dark { --band-fg: var(--color-bg); }
.band-field {
  background: color-mix(in srgb, var(--band-fg) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--band-fg) 24%, transparent);
  color: var(--band-fg);
}
.band-field::placeholder { color: color-mix(in srgb, var(--band-fg) 50%, transparent); }
.band-field:focus { border-color: var(--color-accent-bright); outline: none; }
.band-hair { border-color: color-mix(in srgb, var(--band-fg) 14%, transparent); }
.band-surface { background: color-mix(in srgb, var(--band-fg) 7%, transparent); }
.band-outline-btn {
  border: 1px solid color-mix(in srgb, var(--band-fg) 35%, transparent);
  color: var(--band-fg);
  transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.band-outline-btn:hover { background: var(--band-fg); color: var(--color-ink); border-color: var(--band-fg); }

/* Accent-filled buttons: the dark theme's accent is bright amber, where white
   text fails contrast — flip the label to near-black there. */
:root.dark .bg-accent.text-white { color: #1c1917; }

/* ───────── Lifting cards ─────────
   Soft-surface cards (no border — the tinted background does the separation)
   that lift on hover. Shadow is theme-tuned; motion respects reduced-motion. */
.card-lift {
  background: var(--color-bg-warm);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.card-lift:hover {
  transform: translateY(-5px);
  box-shadow: 0 16px 32px -14px rgb(28 25 23 / 0.2);
}
:root.dark .card-lift:hover { box-shadow: 0 16px 32px -14px rgb(0 0 0 / 0.65); }
@media (prefers-reduced-motion: reduce) {
  .card-lift { transition: none; }
  .card-lift:hover { transform: none; box-shadow: none; }
}

/* Hero with the orb column: cap the display type slightly so "Marketing,"
   never overflows its (narrower) grid column. */
@media (min-width: 768px) {
  .hero-grid .h-display { font-size: clamp(2.75rem, 6.2vw, 5.25rem); }
}

/* ───────── Sticky header surface ─────────
   Tailwind's opacity modifiers (bg-paper/85) silently no-op on colors defined
   as plain CSS variables, which left the header fully transparent — dark nav
   text became invisible whenever a dark band scrolled underneath. Solid paper
   fallback; translucent + blur where supported. */
.site-header { background: var(--color-bg); }
@supports ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .site-header {
    background: color-mix(in srgb, var(--color-bg) 84%, transparent);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
  }
}

/* Soft accent border (same fix — border-accent/40 was a silent no-op). */
.accent-hair { border-color: color-mix(in srgb, var(--color-accent) 40%, transparent); }

/* Orb canvas: fade out entirely over the hero's text column so shards and glow
   never overlap the copy — the animation lives right of the text, full stop. */
#orb-canvas {
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, transparent 40%, #000 64%);
  mask-image: linear-gradient(90deg, transparent 0%, transparent 40%, #000 64%);
}

/* Preloader orb — the pulsating brand circle shown while the page loads
   (replaces the counting number; .preloader__count CSS kept for easy revert). */
.preloader__orb {
  width: clamp(6rem, 18vw, 9rem);
  height: clamp(6rem, 18vw, 9rem);
  border-radius: 9999px;
  background: radial-gradient(circle,
    rgba(196, 105, 42, 0.95) 0%,
    rgba(196, 105, 42, 0.65) 55%,
    rgba(196, 105, 42, 0.25) 80%,
    rgba(196, 105, 42, 0) 100%);
  animation: preloader-pulse 1.1s ease-in-out infinite;
}
@keyframes preloader-pulse {
  0%, 100% { transform: scale(0.9); opacity: 0.8; }
  50%      { transform: scale(1.08); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .preloader__orb { animation: none; } }
