/* Good Roots — shared tokens and base patterns across pages.
   Page-specific layout stays in each page's own <style> block. */

:root {
  --ink: #1f2a1f;
  --paper: #faf7f0;
  --green: #2f4d2f;
  --green-light: #4a6b3f;
  --soil: #6b4a2f;
  --line: #ddd6c4;
  --surface: #ffffff;
  --text-soft: #55584f;
  --text-desc: #444444;
  --muted: #666358;
  --max-width: 960px;
}

/* No dark-mode variant yet — deliberate: this is a pre-launch brochure
   site, not yet through a formal brand-identity pass (see next-steps.md).
   Add a prefers-color-scheme block when that pass happens, not before. */

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  color: var(--ink);
  background: var(--paper);
  line-height: 1.6;
}

a { color: var(--green); text-decoration: underline; text-decoration-color: var(--line); }
a:hover { color: var(--green-light); }

header.site-header {
  padding: 4rem 1.5rem 3rem;
  text-align: center;
  border-bottom: 1px solid var(--line);
}

header.site-header .wordmark {
  font-size: 0.85rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--soil);
  margin-bottom: 1.5rem;
}

header.site-header .wordmark a {
  color: inherit;
  text-decoration: none;
}

header.site-header h1 {
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  margin: 0 0 0.75rem;
  font-weight: 500;
}

header.site-header p.tagline {
  font-size: 1.15rem;
  color: var(--green-light);
  max-width: 40ch;
  margin: 0 auto;
}

header.site-header .header-phone {
  /* inline-block, not the anchor's default inline: vertical margins
     (the gap above) are ignored on inline boxes, so the phone line
     would otherwise sit flush under the tagline. */
  display: inline-block;
  margin: 1rem 0 0;
  font-size: 0.9rem;
}

footer.site-footer {
  text-align: center;
  padding: 3rem 1.5rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.9rem;
}

footer.site-footer .disclaimer {
  font-size: 0.8rem;
}

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

/* Shared CTA link pattern: a text link with a generous invisible hit area
   (44px minimum) via padding + a compensating negative margin, so the
   visual position doesn't shift. */
.cta {
  display: inline-block;
  text-decoration: none;
  color: var(--green);
  font-weight: 600;
  padding: 0.6rem 0.25rem;
  margin: -0.6rem -0.25rem 0;
  min-height: 44px;
  box-sizing: border-box;
}

.cta::after { content: " →"; }

/* Editorial placeholder for real business facts that don't exist yet
   (a testimonial, say). Deliberately reads as unfinished — italic,
   muted, dashed underline — so it's never mistaken for real content
   even if a page ships before the placeholder is replaced. The phone
   number was its one use until the real number landed; the pattern
   stays for the next missing fact, alongside deploy.py's [Add ...]
   check. */
.placeholder-text {
  font-style: italic;
  color: var(--muted);
  border-bottom: 1px dashed var(--muted);
  display: inline-block;
}

/* ==========================================================================
   Tier waterfall — shared detail-page pattern (products.html, coaching.html)
   A single continuous scroll: each tier reveals in place, alternating media
   and content sides, with an optional desktop progress rail. Pair with
   waterfall.js.
   ========================================================================== */

.back-link {
  text-align: center;
  margin: 0;
  padding: 1.25rem 1.5rem 0;
  font-size: 0.85rem;
}

.page-intro {
  max-width: 60ch;
  margin: 2rem auto 0;
  padding: 0 1.5rem;
  text-align: center;
  font-size: 1.05rem;
  color: var(--text-desc);
}

/* ---- progress rail (desktop only) ---- */

.tier-rail {
  position: fixed;
  top: 50%;
  /* Positioned from the same centering math as the content column
     (var(--max-width), 960px) rather than a fixed offset, so clearance is
     guaranteed by arithmetic instead of an estimate of rendered text
     width: rail-right-edge = content-left-edge − 16px, always, at any
     viewport ≥ the breakpoint below. 186px = this rail's own 170px width
     + that 16px clearance budget. Re-derive both the breakpoint and this
     186px if --max-width, the rail's width, or its longest label change.
     170px (down from an earlier 200px) is only possible because the
     labels dropped their redundant "01/02/03 " prefix — the dot position
     already shows order. A viewport below the breakpoint genuinely can't
     fit readable text labels in the available margin at this content
     width; a dots-only compact rail would be the next step to cover
     960-1399px, not attempted here. */
  left: calc((100vw - var(--max-width)) / 2 - 186px);
  width: 170px;
  transform: translateY(-50%);
  display: none;
  flex-direction: column;
  /* deliberately no align-items override: flex's default "stretch" is
     what gives every link below a full 200px hit/click width */
  gap: 1.75rem;
  z-index: 10;
}

.tier-rail::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--line);
}

.tier-rail::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--green);
  transform: scaleY(var(--rail-progress, 0));
  transform-origin: top;
  transition: transform 0.15s linear;
}

.tier-rail a {
  position: relative;
  padding-left: 1.5rem;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--muted);
  transition: color 0.2s ease;
  /* Truncation backstop: the rail's 200px width is sized generously for
     every current label, but this guarantees an unexpectedly long label
     (a longer tier name added later, a wider font on some platform) clips
     with an ellipsis instead of overflowing into the content column. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tier-rail a::before {
  content: "";
  position: absolute;
  left: 1px;
  top: 0.35em;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--muted);
  transition: background 0.2s ease, border-color 0.2s ease;
}

.tier-rail a.is-active {
  color: var(--green);
  font-weight: 600;
}

.tier-rail a.is-active::before {
  background: var(--green);
  border-color: var(--green);
}

/* 1400px is the smallest viewport where the calc() above still leaves the
   rail's own left edge at least 32px from the browser edge (a comfortable
   floor, not a razor margin): (1400-960)/2 - 186 = 34px. Below this width
   the rail is hidden outright rather than left to creep toward 0. */
@media (min-width: 1400px) {
  .tier-rail { display: flex; }
}

/* ---- tier sections ---- */

.tiers {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 3rem 1.5rem 2rem;
}

.tier {
  padding: 4rem 0;
  border-bottom: 1px solid var(--line);
}

.tier:last-child { border-bottom: none; }

.tier-inner {
  display: flex;
  align-items: center;
  gap: 3rem;
}

/* ---- reveal: the shared scroll-entrance pattern ----
   Generalized from what was originally a .tier-inner-only rule, now that
   the homepage's sections use it too. Any element carrying class="reveal"
   fades and rises into place once reveal.js adds "is-visible" as it
   scrolls into view.

   Hidden-until-revealed only applies once reveal.js has actually run
   (signaled by the "js" class landing on <html> before paint — see the
   inline script in <head>). No JS at all, a blocked/failed script, or a
   runtime error elsewhere on the page all leave .reveal elements at their
   default visible state instead of hiding page content with no way back. */
.reveal {
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.js .reveal {
  opacity: 0;
  transform: translateY(24px);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.tier:nth-child(even) .tier-inner {
  flex-direction: row-reverse;
}

.tier-media,
.tier-content {
  flex: 1 1 0;
  min-width: 0;
}

.tier-media.placeholder {
  aspect-ratio: 4 / 3;
  border-radius: 8px;
  border: 1px dashed var(--soil);
  background:
    repeating-linear-gradient(135deg, rgba(107, 74, 47, 0.08) 0 14px, rgba(107, 74, 47, 0.03) 14px 28px),
    var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1.5rem;
}

.tier-media.placeholder span {
  font-size: 0.8rem;
  color: var(--muted);
  max-width: 22ch;
  line-height: 1.5;
}

/* .has-photo: a real photo, as opposed to .placeholder's dashed empty box.
   Deliberately does NOT use aspect-ratio to size the box — it stretches to
   match whatever height .tier-content's text naturally takes up (its flex
   sibling), via align-self: stretch, so a tall/slender photo reads as a
   full-height banner running the section's actual height rather than
   dictating an oversized one. object-fit: cover crops to fill. The img
   itself is position: absolute/inset so it never contributes its own
   intrinsic size to that stretch measurement (an earlier aspect-ratio +
   height:auto attempt lost to the img's width/height attributes and blew
   the section out to the photo's raw 1800px height instead). */
.tier-media.has-photo {
  position: relative;
  align-self: stretch;
  overflow: hidden;
  border-radius: 8px;
  min-height: 260px;
}

.tier-media.has-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tier-kicker {
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--soil);
  margin: 0 0 0.5rem;
}

.tier-content h2 {
  font-size: clamp(1.5rem, 3vw, 2rem);
  margin: 0 0 0.5rem;
  font-weight: 500;
}

.tier-price {
  font-size: 1.15rem;
  color: var(--soil);
  margin: 0 0 1rem;
}

.tier-price .price-note {
  display: block;
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 0.2rem;
}

.tier-lede {
  font-size: 1.05rem;
  margin: 0 0 1.5rem;
}

.tier-includes {
  margin: 0 0 1.5rem;
  padding-left: 1.2rem;
}

.tier-includes li {
  margin-bottom: 0.5rem;
  font-size: 0.95rem;
}

.tier-configs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.75rem;
  margin: 0 0 1.5rem;
}

.config-card {
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: 6px;
  padding: 0.9rem 1rem;
}

.config-card h3 {
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--soil);
}

.config-card p {
  margin: 0 0 0.3rem;
  font-size: 0.85rem;
  color: var(--text-desc);
}

.config-card p:last-child { margin-bottom: 0; }

.config-card strong { color: var(--ink); }

.tier-excludes {
  font-size: 0.85rem;
  color: var(--muted);
  margin: 0 0 1.5rem;
}

/* The line directly before each tier's CTA — deliberately warm and
   forward-looking, so the last thing read before "Ask about..." is an
   invitation, not the exclusion clause above it. */
.tier-invite {
  font-size: 1rem;
  font-style: italic;
  color: var(--green-light);
  margin: 0 0 1rem;
}

/* ---- compare: a compact cross-tier strip above the waterfall, so
   choosing between tiers doesn't require scrolling the whole page and
   holding each one in working memory ---- */

.compare {
  max-width: var(--max-width);
  /* Top margin matches .page-intro's own top gap (2rem, see above) —
     .page-intro's bottom margin is 0 because on coaching.html it's
     followed straight by .tiers' 3rem top padding. products.html is the
     only page that puts .compare in between, so without this the two
     zero margins meet and the intro paragraph runs flush into the cards. */
  margin: 2rem auto 3rem;
  padding: 0 1.5rem;
}

.compare-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
}

.compare-col {
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: 6px;
  padding: 1.25rem;
  text-align: center;
}

.compare-col h2 {
  font-size: 1.05rem;
  font-weight: 500;
  margin: 0 0 0.5rem;
}

.compare-price {
  font-size: 1rem;
  color: var(--soil);
  margin: 0 0 0.5rem;
}

.compare-note {
  font-size: 0.85rem;
  color: var(--text-desc);
  margin: 0 0 1rem;
}

/* ---- closing note: a page-ending block after the last tier, e.g.
   coaching.html's "what this isn't" / build-credit copy ---- */

.tier-note {
  max-width: 60ch;
  margin: 0 auto;
  padding: 3rem 1.5rem 4rem;
  text-align: center;
}

.tier-note h2 {
  font-size: 1.3rem;
  font-weight: 500;
  margin: 0 0 1rem;
}

.tier-note ul {
  text-align: left;
  display: inline-block;
  margin: 0 0 1.5rem;
  padding-left: 1.2rem;
  color: var(--text-desc);
  font-size: 0.95rem;
}

.tier-note ul li { margin-bottom: 0.5rem; }

.tier-note .credit {
  font-size: 0.95rem;
  color: var(--text-soft);
}

@media (max-width: 720px) {
  .tier-inner,
  .tier:nth-child(even) .tier-inner {
    flex-direction: column;
    align-items: stretch;
    gap: 1.75rem;
  }

  /* .has-photo's height normally comes from stretching to match
     .tier-content's row-mode height (see the rule itself) — that sibling
     relationship doesn't exist once the columns stack, so fall back to a
     fixed portrait ratio instead.

     `flex: 0 0 auto` is load-bearing, not tidiness: .tier-media's shared
     `flex: 1 1 0` makes flex-basis 0 the *main* axis once this becomes a
     column, which resolves to a definite 0 height, and `min-height: 0`
     below removes the automatic content minimum that would otherwise
     floor it. Between them the box collapsed to height: 0 and the photo
     was invisible (clipped by `overflow: hidden`) on every viewport
     ≤720px. With the basis back to `auto`, `aspect-ratio` has an
     indefinite height to resolve against and applies as intended. */
  .tier-media.has-photo {
    flex: 0 0 auto;
    align-self: auto;
    aspect-ratio: 3 / 4;
    min-height: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    transition: none;
    opacity: 1;
    transform: none;
  }
  .tier-rail::after {
    transition: none;
  }
}
