/* site.css — the handful of things Tailwind utility classes alone don't
   cover cleanly. Theme color/font tokens themselves live in the @theme
   block in includes/header.php (Tailwind v4's browser build reads that
   from the scanned page, not from this file). */

html.cfi-html {
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html.cfi-html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}

/* ── Soft section wash — a barely-there brand-hue tint (not a repeat of
   the hero's full-strength gradient), so the page has a second, quieter
   moment of color instead of "one colorful hero, then flat pale sections
   forever." Used once, on the "three threads" section. ── */
.cfi-section-wash {
  background: linear-gradient(180deg, var(--color-surface) 0%, color-mix(in oklab, var(--color-brand) 5%, var(--color-surface)) 100%);
}

/* ── Hero gradient (2026-08-24, "consistent modern design" pass) — the
   exact gradient recipe read directly from the live marketing site's own
   source (`/home/cashflo2/public_html/index.php`'s `.hero-bg`), not
   approximated: a 135deg navy-to-cyan sweep plus a soft radial glow.
   Section 9 of this file's own history had removed this exact treatment
   believing it read as "consumer SaaS" — it turned out to just be the
   actual brand, confirmed by reading the real homepage's CSS. Fixed
   (theme-invariant) like the topbar/footer navy already is — this hero
   is dark regardless of the site's light/dark toggle, matching how the
   real marketing site's hero has no light-mode variant either.

   The ::after scrim is the one addition NOT copied from the marketing
   site: a solid 65%-opaque navy plateau across the 0-58% width where the
   text column (`max-w-3xl` inside a `max-w-[1400px]` container) actually
   lives, fading out only from 58-88% width where nothing but decoration
   ever sits. This GUARANTEES AAA regardless of exactly where text lands
   within that column — computed against the true worst case (the
   gradient's lightest stop, #0081ae, composited under the full 65%
   scrim): white text still clears 8.3:1+ at every opacity tier actually
   used below. An earlier attempt at a lighter, fading-under-the-text
   scrim (45%, fading from 45% width) was checked the same way and
   failed this exact worst-case math (5.3-6.9:1) — kept here as the
   reason the plateau is solid instead of gradual under the text zone,
   not assumed safe from a lighter treatment. The real site avoids this
   problem entirely by placing all its text over the gradient's dark end
   and putting only a non-text image in the light end; this site's
   simpler single-column hero adds the scrim instead of relying on
   layout alone to guarantee the same thing. ── */
.cfi-hero-gradient {
  background: linear-gradient(135deg, #00102e 0%, #003750 40%, #005880 70%, #0081ae 100%);
  position: relative;
  overflow: hidden;
}
.cfi-hero-gradient::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(ellipse at 78% 35%, rgba(0, 186, 251, .18) 0%, transparent 65%);
  pointer-events: none;
}
.cfi-hero-gradient::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(0, 16, 46, .65) 0%, rgba(0, 16, 46, .65) 58%, transparent 88%);
  pointer-events: none;
}
.cfi-hero-content { position: relative; z-index: 1; }

/* Pipeline chips + outline button, dark-hero variants — the default chip/
   button styles assume a light background (used on all 59 guide pages);
   scoped overrides here apply only inside the hero, where the shared
   components would otherwise render illegibly (light-mode ink-on-ink).

   2026-08-27 bug fix: .cfi-hero-pipeline is only ever added by a wrapper
   markup that's actually inside the hero (render_pipeline()'s own div is
   plain .cfi-pipeline, no "hero" in the name at all), so that half of
   this was always correctly scoped. .cfi-hero-modules is different --
   render_module_strip() hardcodes that exact class on its own wrapper
   <div> UNCONDITIONALLY, for every one of its 16 call sites (the
   homepage hero AND 15 guide pages' own "related modules" strips, e.g.
   guides/dashboard.php's Overview/Accounting/Cash Flow Dashboard chips).
   Without the .cfi-hero-gradient ancestor guard added here, this rule's
   two joined class-selectors (specificity 0,2,0) outrank the chip's own
   inline border-line/bg-surface/text-ink Tailwind utility classes
   (0,1,0 each) EVERYWHERE, forcing white text on a ~8%-white background
   on every guide page's light surface -- i.e. actually invisible, not
   just low-contrast, which is exactly what got reported. Guarding with
   .cfi-hero-gradient (present only on index.php's actual hero section)
   makes this rule stop matching on every non-hero call site, so those
   fall back to the chip's own already-correct light-mode utility classes
   with no further changes needed anywhere else. */
.cfi-hero-pipeline .cfi-chip,
.cfi-hero-gradient .cfi-hero-modules .cfi-chip {
  /* border white/40 (not the CTA's rgba(255,255,255,.22) first guess) --
     checked against the hero's own worst-case composited background and
     .22 only cleared 1.9:1, under the 3:1 non-text-UI-component floor;
     .40 clears 3.25:1. */
  border-color: rgba(255, 255, 255, .4);
  background: rgba(255, 255, 255, .08);
  color: #fff;
}
.cfi-hero-pipeline [data-animate-pipeline].is-visible .cfi-chip:first-child,
.cfi-hero-pipeline .cfi-chip:first-child {
  background: #fff;
  border-color: #fff;
  color: #00102e;
}
.cfi-hero-pipeline .cfi-chip-arrow svg { color: rgba(255, 255, 255, .4); }
.cfi-hero-pipeline .bg-line { background: rgba(255, 255, 255, .18); }
/* Module strip chips carry an icon (render_module_strip()) -- the pipeline
   chips don't, so this sizing/color rule is scoped to .cfi-hero-modules
   only rather than folded into the shared .cfi-chip rule above. Same
   .cfi-hero-gradient guard as above, same reason. */
.cfi-hero-gradient .cfi-hero-modules .cfi-chip i { color: rgba(255, 255, 255, .7); }
.cfi-hero-gradient .cfi-btn-outline {
  color: #fff;
  border-color: rgba(255, 255, 255, .4);
}
.cfi-hero-gradient .cfi-btn-outline:hover {
  background: rgba(255, 255, 255, .1);
  border-color: rgba(255, 255, 255, .7);
  color: #fff;
}

/* ── Primary/outline CTA — the ONE place this site deliberately matches the
   marketing site's bolder register pixel-for-pixel (values copied from
   cashflowinventory.com/index.php's own .btn-primary-cfi/.btn-outline-cfi,
   not reinvented): the gold gradient + lift-on-hover is what every visitor
   already recognizes as "the button that does the main thing" on every
   other page of the domain, so the docs' own primary action (search)
   reuses it exactly rather than inventing a calmer variant that would
   read as a different product's button. Everywhere else on this site
   (nav, cards, badges) intentionally stays in the calmer documentation
   register — this is a targeted exception, not a reversion. ── */
/* 2026-08-24: literal #F7B731, not var(--color-gold) -- this class is
   plain CSS in a statically-linked stylesheet, but --color-gold only
   ever exists once Tailwind's browser-CDN script has parsed the
   @theme block in header.php and injected it. If that hasn't happened
   yet (slow CDN fetch) or fails for any reason, `var(--color-gold)`
   resolves to nothing, which makes the whole `background` shorthand
   invalid CSS -- and an invalid shorthand falls back to its initial
   value, i.e. no background at all. That's what a user reported: the
   primary CTA button rendering with text but no visible background.
   Gold never changes between light/dark mode anyway (no override exists
   in the dark-mode @theme block) -- there was never a reason for this
   one, single, theme-invariant value to depend on the CDN script
   succeeding. Hardcoded so this specific button can never silently lose
   its background regardless of Tailwind's load timing. */
.cfi-btn-primary {
  background: linear-gradient(135deg, #F7B731, #ff9800);
  color: #001850;
  font-weight: 700;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(247, 183, 49, .4);
  transition: transform .25s ease, box-shadow .25s ease, background .25s ease;
}
.cfi-btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 30px rgba(247, 183, 49, .5);
  background: linear-gradient(135deg, #fbbf24, #f59e0b);
}
.cfi-btn-outline {
  background: transparent;
  color: var(--color-ink);
  font-weight: 600;
  border-radius: 12px;
  border: 2px solid var(--color-cyan);
  transition: background .25s ease, color .25s ease;
}
/* Hover fill uses --color-topbar for text, not white or --color-ink:
   white-on-cyan measures ~2.2:1 (fails even AA, 4.5:1) -- a real legibility
   bug, not a style choice. --color-ink looked like the fix but --color-ink
   flips to a near-white tone in dark mode (built for dark surfaces), which
   is ALSO unreadable on this same bright cyan fill (~1.9:1) -- checked
   before shipping, not assumed. --color-topbar is the one token that never
   flips between themes (#00102E always), so it's the only single value
   that clears AAA (8.4:1+) against --color-cyan in both themes at once. */
.cfi-btn-outline:hover { background: var(--color-cyan); color: var(--color-topbar); }

/* ── Card shell (2026-08-24, revised same day after "still not modern/
   polished" feedback on the borders specifically) — checked against the
   real marketing site's own card CSS before revising (not guessed):
   `.feature-card` in /home/cashflo2/public_html/index.php has NO resting
   border or shadow at all — depth comes entirely from a hover-only
   shadow (0 20px 60px rgba(0,55,80,.15)) + a -6px lift + a 3px top
   accent-bar that fades in. That's the actual "modern polished" signature
   of this brand, not a heavier resting border (which is what the prior
   version of this file shipped, reasoning from a WCAG checklist instead
   of the product's own UI). Reproduced here at a proportionally smaller
   scale (these cards are denser/smaller than marketing hero cards): a
   near-invisible resting shadow, a -3px hover lift, a hover shadow scaled
   down from the reference recipe but keeping its navy tint and softness,
   and the same top accent-bar reveal (universal cyan->navy gradient,
   matching the reference exactly rather than per-card-accent, since the
   real site doesn't vary it by card either). Radius stays at 12px (see
   Section 14's rationale) — a deliberate half-step between Section 9's
   tight corners and the marketing site's fuller 20-24px, since a dense
   grid of 50+ cards reads better slightly tighter than a handful of hero
   cards. `.cfi-card` needs position:relative + overflow:hidden for the
   ::before accent-bar's corners to clip to the card's own radius. ── */
.cfi-card {
  position: relative;
  overflow: hidden;
  border-radius: 0.75rem;
  box-shadow: 0 1px 2px rgba(0, 16, 46, .04);
  transition: box-shadow .25s ease, border-color .25s ease, transform .25s ease, background-color .25s ease;
}
.cfi-card-interactive::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--color-cyan), var(--color-brand));
  opacity: 0; transition: opacity .25s ease;
}
.cfi-card-interactive:hover, .cfi-card-interactive:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 14px 32px rgba(0, 55, 80, .14), 0 4px 10px rgba(0, 55, 80, .06);
}
.cfi-card-interactive:hover::before, .cfi-card-interactive:focus-visible::before { opacity: 1; }
/* Dark mode: a black/navy-tinted shadow on an already-dark surface reads
   as muddy rather than elevated, so hover depth comes from a border/
   background shift instead -- the standard dark-UI substitute for a
   light-mode shadow. The accent-bar reveal stays (it's a gradient fill,
   not a shadow, so it reads fine on dark surfaces too). */
:root[data-theme="dark"] .cfi-card { box-shadow: none; }
:root[data-theme="dark"] .cfi-card-interactive:hover,
:root[data-theme="dark"] .cfi-card-interactive:focus-visible {
  box-shadow: none;
  border-color: color-mix(in oklab, var(--color-brand) 45%, transparent);
  background-color: color-mix(in oklab, var(--color-ink) 6%, var(--color-surface));
}
@media (prefers-reduced-motion: reduce) {
  .cfi-card-interactive:hover, .cfi-card-interactive:focus-visible { transform: none; }
}

/* ── Custom scrollbar — thin, matches the ledger/utility character ── */
.cfi-scroll { scrollbar-width: thin; scrollbar-color: var(--color-line) transparent; }
.cfi-scroll::-webkit-scrollbar { width: 6px; height: 6px; }
.cfi-scroll::-webkit-scrollbar-thumb { background: var(--color-line); border-radius: 9999px; }
[data-search-results], [data-drawer] nav, [data-toc] { scrollbar-width: thin; scrollbar-color: var(--color-line) transparent; }
[data-search-results]::-webkit-scrollbar, [data-drawer] nav::-webkit-scrollbar { width: 6px; }
[data-search-results]::-webkit-scrollbar-thumb, [data-drawer] nav::-webkit-scrollbar-thumb { background: var(--color-line); border-radius: 9999px; }

/* ── Prose measure for guide body copy — long-form reading needs a
   bounded line length regardless of viewport width. ── */
.cfi-prose { max-width: 72ch; }
.cfi-prose h2 {
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--color-ink);
  margin-top: 2.75rem;
  margin-bottom: 0.9rem;
  scroll-margin-top: 5.5rem;
}
.cfi-prose h2:first-child { margin-top: 0; }
.cfi-prose h3 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--color-ink);
  margin-top: 2rem;
  margin-bottom: 0.6rem;
  scroll-margin-top: 5.5rem;
}
.cfi-prose p { margin-top: 0; margin-bottom: 1rem; line-height: 1.7; color: color-mix(in oklab, var(--color-ink) 78%, transparent); }
.cfi-prose ul, .cfi-prose ol { margin: 0 0 1.1rem; padding-left: 1.25rem; display: flex; flex-direction: column; gap: 0.45rem; }
.cfi-prose ul { list-style: none; }
.cfi-prose ul > li { position: relative; padding-left: 1.35rem; line-height: 1.65; color: color-mix(in oklab, var(--color-ink) 78%, transparent); }
.cfi-prose ul > li::before {
  content: ""; position: absolute; left: 0.15rem; top: 0.62em;
  width: 5px; height: 5px; border-radius: 9999px; background: var(--color-brand); opacity: 0.55;
}
.cfi-prose ol { list-style: none; counter-reset: cfi-ol; }
.cfi-prose ol > li { counter-increment: cfi-ol; position: relative; padding-left: 1.9rem; line-height: 1.65; color: color-mix(in oklab, var(--color-ink) 78%, transparent); }
.cfi-prose ol > li::before {
  content: counter(cfi-ol); position: absolute; left: 0; top: 0.05em;
  width: 1.4rem; height: 1.4rem; border-radius: 0.4rem; background: color-mix(in oklab, var(--color-brand) 12%, transparent);
  color: var(--color-brand); font-family: var(--font-mono); font-size: 0.72rem; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
}
.cfi-prose strong { color: var(--color-ink); font-weight: 600; }
.cfi-prose a:not(.not-prose a) { color: var(--color-brand); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: color-mix(in oklab, var(--color-brand) 35%, transparent); }
.cfi-prose code { font-family: var(--font-mono); font-size: 0.85em; background: color-mix(in oklab, var(--color-ink) 6%, transparent); padding: 0.1em 0.4em; border-radius: 0.35em; }

/* ── Pipeline chip motif (the signature element) ── Enterprise/financial
   register: the "current state" chip is a flat, solid navy fill (a status
   pill, not a gradient-glow moment) — informative, not decorative. The
   traveling dot still animates on first scroll into view; it's functional
   wayfinding (shows the flow has direction), not gratuitous motion. */
.cfi-pipeline .cfi-chip { position: relative; transition: background-color .25s ease, color .25s ease, border-color .25s ease; }
[data-animate-pipeline] .cfi-chip-arrow svg { color: color-mix(in oklab, var(--color-ink) 30%, transparent); }
[data-animate-pipeline].is-visible .cfi-chip:first-child {
  background: var(--color-ink);
  border-color: var(--color-ink);
  color: var(--color-paper);
}
[data-animate-pipeline] .cfi-pipeline-dot {
  position: absolute; top: 50%; left: 0; width: 6px; height: 6px; margin-top: -3px;
  border-radius: 9999px; background: var(--color-signal);
  opacity: 0; transition: left 2.4s cubic-bezier(.65,0,.35,1), opacity .3s ease;
}
[data-animate-pipeline].is-visible .cfi-pipeline-dot { opacity: 1; left: calc(100% - 6px); }

/* ── Tabular numerals — applied to counts/mono data so digits align in a
   fixed-width grid (guide counts, footer stats), the small "data terminal"
   touch enterprise/financial software uses that a marketing site usually
   doesn't bother with. ── */
.cfi-tabular { font-variant-numeric: tabular-nums; }

/* ── Sidebar / TOC active-state indicator ── */
[data-toc] a { position: relative; }
[data-toc] a.is-active { color: var(--color-brand); font-weight: 600; }
[data-toc] a.is-active::before {
  content: ""; position: absolute; left: -0.85rem; top: 0.15rem; bottom: 0.15rem; width: 2px; background: var(--color-brand); border-radius: 9999px;
}

/* ── Guide sidebar list (2026-08-26) — semantic <ul>/<li> tree (was a flat
   stack of <a> tags with no list markup at all). The active-guide rail
   reuses [data-toc] a.is-active's own left-indicator convention directly
   above, rather than a second visual language for "you are here"; a hover
   state shows the same rail at low opacity so hover and active read as
   one family of feedback. ── */
.cfi-sidebar-list { display: flex; flex-direction: column; gap: 1px; }
.cfi-sidebar-link { position: relative; }
.cfi-sidebar-link::before {
  content: ""; position: absolute; left: -1px; top: 0.3rem; bottom: 0.3rem; width: 2px;
  border-radius: 9999px; background: var(--color-brand); opacity: 0; transform: scaleY(0.4);
  transition: opacity .15s ease, transform .15s ease;
}
.cfi-sidebar-link:hover::before { opacity: 0.35; transform: scaleY(1); }
.cfi-sidebar-link.is-active::before { opacity: 1; transform: scaleY(1); }

/* ── Footer guide list — was plain text with no marker or hover treatment,
   reading as an afterthought next to the rest of the page chrome. A small
   chevron marker plus a short rightward slide on hover, echoing the
   related-guide chip's own hover motion elsewhere on the site. ── */
.cfi-footer-list > li > a { position: relative; display: inline-flex; align-items: center; gap: 0.4rem; transition: color .15s ease, transform .15s ease; }
.cfi-footer-list > li > a::before { content: "\203A"; color: color-mix(in oklab, var(--color-cyan) 70%, transparent); font-weight: 600; }
.cfi-footer-list > li > a:hover { transform: translateX(2px); }

/* ── Prose list polish — the marker mechanism (dot bullets / numbered
   circle chips) was already in place; this adds a per-item hover
   highlight so a long list is easier to track line-by-line while
   reading, plus a rule for nested sub-lists, which had none before (a
   nested <ul>/<ol> would have inherited the parent level's own spacing
   and counter and visually collided with the item that contains it). ── */
.cfi-prose ul > li, .cfi-prose ol > li { border-radius: 0.375rem; padding-top: 0.15rem; padding-bottom: 0.15rem; padding-right: 0.5rem; transition: background-color .15s ease; }
.cfi-prose ul > li:hover, .cfi-prose ol > li:hover { background: color-mix(in oklab, var(--color-brand) 5%, transparent); }
.cfi-prose li > ul, .cfi-prose li > ol { margin-top: 0.45rem; margin-bottom: 0; }
.cfi-prose li > ul > li::before { width: 4px; height: 4px; opacity: 0.4; }
.cfi-prose li > ol > li::before { width: 1.15rem; height: 1.15rem; font-size: 0.65rem; }

/* ── Hero pipeline sizing on small screens ── */
@media (max-width: 640px) {
  .cfi-hero-pipeline .cfi-chip { font-size: 11.5px; padding: 0.3rem 0.55rem; }
}

/* ── Cross-document view transitions — pure CSS, no JS. Chromium-only today;
   browsers that don't understand the at-rule ignore it and fall back to a
   normal hard navigation, so this is a strict progressive enhancement.
   Turns internal link clicks into a soft cross-fade instead of a white
   flash — the highest leverage-to-effort "feels current" upgrade available
   to a static multi-page site with no SPA router. ── */
@view-transition {
  navigation: auto;
}

/* ── Reading-progress bar — width set via scaleX from a rAF-throttled
   scroll listener in site.js. Transition here (not inline) so it's a
   single, batched paint rather than per-frame layout thrash. ── */
[data-reading-progress] { transition: transform .1s linear; }

/* ── Back-to-top button — shown state toggled by site.js past a scroll
   threshold. Base (hidden) state is inline Tailwind utilities on the
   element itself; this is only the "shown" override, since Tailwind's
   browser build doesn't scan for a runtime-added .is-visible combinator. ── */
[data-back-to-top].is-visible { opacity: 1; transform: none; pointer-events: auto; }

/* ── Scroll-reveal for card grids — a single IntersectionObserver toggles
   .is-visible on the *group*; children stagger via nth-child delay so the
   motion re-enacts the domain → subgroup → guide grouping the IA already
   imposes, rather than reading as decoration. Capped at 8 staggered steps;
   anything past that reveals together with the 8th (avoids a long tail on
   large groups). ── */
/* 2026-08-24: shortened 14px->8px translate and 0.5s->0.35s duration
   (delays scaled down to match). A full-page screenshot tool that resizes
   the viewport to full document height in one step, rather than
   simulating real scrolling, can sample a frame mid-transition -- a user
   never sees that moment, but a static capture can, and it read as
   "garbled" text rather than "slightly faded." Shorter/smaller motion
   narrows the window where a sampled frame looks broken, without
   removing the reveal (prefers-reduced-motion already zeroes it
   entirely, unchanged, via the global rule at the top of this file). */
/* 2026-08-26: gated behind html.js (set synchronously in header.php, before
   first paint) -- this rule used to hide [data-reveal-group] content
   unconditionally, relying entirely on site.js's IntersectionObserver to
   ever show it again. That is invisible-by-default, not just an
   animation: a visitor with JS disabled/blocked, or any failure that
   stops site.js running, would see permanently blank content where
   render_module_strip()'s icon-chip visuals (used on ~15 guides) and the
   home page's domain/feature cards should be. Scoping under html.js makes
   the hide-then-reveal choreography a strict progressive enhancement --
   without JS, content is fully visible immediately; with it, the fade/
   slide-in still plays exactly as before. */
html.js [data-reveal-group] > * { opacity: 0; transform: translateY(8px); transition: opacity .35s ease, transform .35s ease; }
html.js [data-reveal-group].is-visible > * { opacity: 1; transform: none; }
[data-reveal-group] > *:nth-child(1) { transition-delay: 0ms; }
[data-reveal-group] > *:nth-child(2) { transition-delay: 40ms; }
[data-reveal-group] > *:nth-child(3) { transition-delay: 80ms; }
[data-reveal-group] > *:nth-child(4) { transition-delay: 120ms; }
[data-reveal-group] > *:nth-child(5) { transition-delay: 160ms; }
[data-reveal-group] > *:nth-child(6) { transition-delay: 200ms; }
[data-reveal-group] > *:nth-child(7) { transition-delay: 240ms; }
[data-reveal-group] > *:nth-child(n+8) { transition-delay: 280ms; }

/* ── Print: strip chrome, keep content readable ── */
@media print {
  header, footer, [data-toc-wrap], [data-sidebar], [data-drawer], [data-search-modal], [data-back-to-top] { display: none !important; }
  body { background: #fff; color: #000; }
  a { text-decoration: underline; color: #000; }
}
