/* CreatiSoul proposal frame — layout, type, spacing, and interaction rules
   shared by every client deck. Client colour tokens live in a sibling file
   (e.g. imove.css) and are applied per-section via .imv / .imv-light so this
   file stays reusable across proposals. */

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  /* Wide content must scroll inside its own container (see .scroll-x below) —
     the page body itself must never gain a horizontal scrollbar. */
  overflow-x: hidden;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: var(--fs-body);
  line-height: 1.55;
}
img, svg, video { max-width: 100%; display: block; }
h1, h2, h3, p { margin: 0 0 var(--sp-4); }

/* ---------- Type scale (mobile -> desktop, fluid) ---------- */
:root {
  --fs-eyebrow: clamp(0.75rem, 0.72rem + 0.15vw, 0.8125rem);
  --fs-body:    clamp(1rem,    0.95rem + 0.2vw,  1.125rem);
  --fs-lead:    clamp(1.125rem, 1.02rem + 0.4vw, 1.375rem);
  --fs-h3:      clamp(1.375rem, 1.2rem + 0.8vw,  1.75rem);
  --fs-h2:      clamp(1.75rem,  1.4rem + 1.6vw,  2.5rem);
  --fs-h1:      clamp(2.25rem,  1.6rem + 3vw,    4rem);

  /* ---------- Spacing scale, 4px base ---------- */
  --sp-1: 0.25rem;  /* 4px */
  --sp-2: 0.5rem;   /* 8px */
  --sp-3: 0.75rem;  /* 12px */
  --sp-4: 1rem;     /* 16px */
  --sp-5: 1.25rem;  /* 20px */
  --sp-6: 1.5rem;   /* 24px */
  --sp-8: 2rem;     /* 32px */
  --sp-10: 2.5rem;  /* 40px */
  --sp-12: 3rem;    /* 48px */
  --sp-16: 4rem;    /* 64px */
  --sp-20: 5rem;    /* 80px */
  --sp-24: 6rem;    /* 96px */
}

.eyebrow {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  margin: 0 0 var(--sp-3);
}
h1, h2, h3 {
  font-family: "Bricolage Grotesque", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
h1 { font-size: var(--fs-h1); line-height: 1.08; font-weight: 700; }
h2 { font-size: var(--fs-h2); line-height: 1.15; font-weight: 700; }
h3 { font-size: var(--fs-h3); line-height: 1.25; font-weight: 600; }
.lead { font-size: var(--fs-lead); }

/* ---------- Layout primitives ---------- */
/* width:100% is load-bearing, not decorative: #cover (always) and every
   .section from 768px up (see the flex rules below) make .wrap a flex item
   on the column cross-axis. margin-inline:auto on a flex item with auto
   cross-axis margins means the item is NOT stretched — its width instead
   shrink-to-fits its content. That was invisible while body copy ran wide
   enough to fill the 72rem cap on its own, but once .wrap p/li below capped
   prose to a ~34em measure, .wrap's own box shrank to match the narrowest
   content and re-centred itself inside the section, indenting sections whose
   direct children were all short paragraphs (foundation/ahead/system) while
   sections with a grid (vision) or no long prose (cover) stayed full width —
   a 138px drift in the shared left edge. width:100% gives the flex item a
   definite cross size again, so it always fills the line up to max-width
   regardless of what its children measure, and margin-inline:auto goes back
   to doing only its intended job (centering the *container* once the
   viewport exceeds 72rem), never the accidental shrink-to-fit centering of
   individual sections. */
.wrap {
  width: 100%;
  max-width: 72rem;
  margin-inline: auto;
  padding-inline: clamp(1rem, 5vw, 4rem);
}

/* A full-width band. Mobile keeps its natural (content) height — forcing
   100svh on a phone just leaves dead space under short content and fights
   the collapsing address bar. Desktop gets the full-bleed slide treatment. */
.section {
  position: relative;
  width: 100%;
  padding-block: var(--sp-16) var(--sp-16);
}
@media (min-width: 768px) {
  .section {
    min-height: 100svh; /* svh, not vh — vh does not survive a collapsing mobile toolbar */
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: var(--sp-24);
  }
}

/* A visible seam where two sections in a row share the same tone.
   .imv / .imv-light alternate by hand, section by section, and there is no
   guarantee that alternation stays exact — an odd number of sections
   already makes exact alternation impossible for at least one join, and
   every future insertion has an even chance of creating another. Without
   this rule, two same-tone sections back to back read as one long run with
   a patch of empty space in the middle — a rendering gap, not a deliberate
   break. currentColor is the mechanism, not a hardcoded token: .imv sets
   color to --imv-bone and .imv-light sets it to --imv-t1 (imove.css), so
   this single rule draws correctly on both tones without knowing either
   value, and works unmodified in any future proposal that follows the same
   .imv/.imv-light convention this file already assumes (see the top-of-file
   comment). Kept faint (14% of the text colour) — a marker, not a rule
   heavy enough to compete with the section content itself. */
.imv + .imv,
.imv-light + .imv-light {
  border-block-start: 1px solid color-mix(in srgb, currentColor 14%, transparent);
}

/* Cover exception (deliberate): every OTHER section sizes to its content
   on mobile and only becomes 100svh from 768px up (rule above). A cover
   occupying a third of a phone screen reads as a broken page in the first
   two seconds, so #cover alone gets full-viewport height at every width —
   svh, not vh, so a collapsing mobile toolbar never leaves a sliver of the
   next section showing underneath it. */
#cover {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.cover-validity {
  font-size: var(--fs-body);
  opacity: 0.72;
  margin-top: var(--sp-2);
}

/* The cover's single scroll affordance. Pinned to the section's own box
   (.section is already position:relative), not the viewport, so it always
   sits at the bottom of the cover regardless of section height. */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: var(--sp-8);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  color: inherit;
  text-decoration: none;
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.75;
}
.scroll-cue svg { animation: scroll-cue-bob 1.8s ease-in-out infinite; }
@keyframes scroll-cue-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}
@media (prefers-reduced-motion: reduce) {
  .scroll-cue svg { animation: none; }
}

/* ---------- Sticky section-progress nav ---------- */
/* A slim sticky bar pinned to the viewport top, a thin fill that tracks
   scroll progress, and a dot rail marking each section (aria-current on the
   active one). Wired into index.html as a static list of #id anchors — it
   works with zero JS as a jump-nav; js/deck.js (this proposal only) adds the
   scroll-driven active dot and the fill's --progress value on top of that. */
.progress-nav {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  height: 56px;
  /* >= the dot's 18px invisible hit-area overhang (below), so the last dot's
     padding never pushes past the nav's own edge and out past the viewport. */
  padding-inline: var(--sp-6);
  background: rgba(13, 13, 13, 0.9);
  color: #fff; /* generic, not a client token — this chrome is dark for every
                  proposal regardless of the section palette underneath it */
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.progress-nav__track {
  position: relative;
  flex: 1;
  height: 2px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  overflow: hidden;
}
.progress-nav__fill {
  position: absolute;
  inset: 0;
  width: var(--progress, 0%);
  background: currentColor;
}
.progress-nav__list {
  display: flex;
  /* --sp-10 (40px), not --sp-2 (8px): each dot's invisible hit area is a
     44x44 square (see .progress-nav__dot below), but the flex layout only
     reserves the visible 8px dot for spacing purposes (the hit area's
     padding is cancelled by an equal negative margin). At the old 8px gap,
     dot centres sat 16px apart while each hit square reached 22px either
     side of its own centre — a 28px overlap into both neighbours, and since
     unpositioned siblings paint in DOM order, every dot but the last routed
     clicks to whichever section's dot came after it, not its own. 40px gap
     puts centres 48px apart (>=44, the sum of two 22px half-widths), so
     adjacent 44x44 hit squares no longer touch at all. Verified with
     document.elementFromPoint at each dot's own centre: every dot now
     resolves to its own href. */
  gap: var(--sp-10);
  list-style: none;
  margin: 0;
  padding: 0;
}
.progress-nav__dot {
  /* An <a href="#id">, not a <button> — it must jump to its section with no
     JS at all. An anchor is inline by default, so width/height need
     display:block to take effect; padding (offset by a matching negative
     margin) grows the hit area without growing the visible dot — 18px of
     padding on an 8px dot is a 44px square (the mobile-first tap-target floor),
     and the equal-and-opposite negative margin means the flex layout still
     reserves only the original 8px, so the dot rail's visible spacing is
     untouched. color:inherit is load-bearing: an anchor's UA-default link
     colour beats plain inheritance, so without it `background:currentColor`
     below resolves to browser link-blue instead of the nav's own white.
     The 44px hit area only avoids overlapping its neighbours because
     .progress-nav__list's gap was widened to match — see the comment there
     before changing either value independently. */
  display: block;
  box-sizing: content-box;
  width: 8px;
  height: 8px;
  padding: 18px;
  margin: -18px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
  background-clip: content-box;
  text-decoration: none;
  color: inherit;
}
.progress-nav__dot[aria-current="true"] { background: currentColor; background-clip: content-box; }

/* Below 720px the rail collapses to a bare progress strip — a five-item dot
   list is either too cramped to tap accurately or too wide to fit, so the
   dots and their gaps disappear and only the fill/track remain. This block
   must come AFTER the unconditional .progress-nav__list/__track rules above:
   equal specificity means source order decides, and an override placed
   before the rule it overrides loses. */
@media (max-width: 719.98px) {
  .progress-nav {
    height: 4px;
    padding-inline: 0;
    gap: 0;
    background: rgba(0, 0, 0, 0.16);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .progress-nav__list { display: none; }
  .progress-nav__track { height: 100%; border-radius: 0; }
}

/* ---------- Visibility utilities ---------- */
/* !important is load-bearing: a two-class selector like .btn.wide{display:block}
   out-specifies a one-class .mb{display:none} regardless of source order — the
   general specificity trap these utilities exist to guard against. These
   utilities must win no matter how specific the colliding rule is. */
.only-mb { display: block !important; }
.only-dk { display: none !important; }
@media (min-width: 768px) {
  .only-mb { display: none !important; }
  .only-dk { display: block !important; }
}

/* Wide content (tables, wide diagrams) scrolls inside its own box — the page
   body must never scroll horizontally. */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* A wide TABLE's scroller needs two things .scroll-x alone doesn't give it:
   a visible box (border) so the scrollable region reads as its own control,
   not an accidental clip, and — because table cells are not natively
   focusable — to be a labelled, focusable tab stop itself (tabindex="0",
   role="group", aria-label, set in the markup), so a keyboard user can
   reach columns that scroll off-screen. console.css defined this pattern
   first for the lead-console mockups; duplicated here rather than shared,
   because index.html never loads console.css and this file must keep
   working with zero client-specific dependencies (see the top-of-file
   comment). color-mix(...currentColor...) rather than a hardcoded hex is
   the same reusability move as the .imv+.imv hairline rule above — it reads
   correctly on both .imv and .imv-light without knowing which one it's in. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
  border-radius: 12px;
}
.table-scroll:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

/* ---------- Narrative components (shared across proposal sections) ---------- */

/* Prose measure. .wrap's 1152px content width is correct for layout —
   headings, the diagram and card/grid content should keep it — but left
   unconstrained it stretches single-column body copy (foundation, ahead) to
   100+ characters per line at desktop widths, which is exhausting to read
   and looks like a wall of text in a document asking for a large budget.
   34em is deliberate: at a ~0.5-em average character width, max-width:34em
   works out to roughly 68 characters per line AT ANY font-size a paragraph
   renders at, because both scale with the same em — so .lead's larger type
   and a plain <p>'s body type land in the same comfortable 60-75 band
   without two different rules. Grid-limited text (the timeline cards) is
   already narrower than 34em at the widths that matter and is unaffected. */
.wrap p, .wrap li {
  max-width: 34em;
}

/* The 34em cap above is for PROSE. Any component whose <li> is a layout box —
   a grid card, a flex row — rather than a paragraph of reading text must opt
   out here, or it silently truncates below the breakpoint where it stops
   being a grid column and becomes a full-width single-column item.
   .phase-plan__item/.checklist__item/.timeline__item all go multi-column
   only from 900px up; below that they ARE the single-column content and
   should fill .wrap like everything else does. The comments that introduced
   .phase-plan__item and .checklist__item reasoned "a 900px+ grid column
   never approaches 34em" — true, but that check only covers the >=900px
   state and never asks what happens below it, where the item's un-narrowed
   width (~738px at 820) is wider than the cap and the cap binds for real:
   measured live, phase-plan__item and checklist__item both ran 573/738px at
   820 before this rule existed, a 165px truncation invisible on
   .phase-plan__item's border-block-start (a gold rule that visibly stopped
   165px short of the card edge) and merely unmeasured on
   .timeline__item's border-inline-start (a vertical rule, so the same
   truncation carried no visible signal and had been shipping since before
   these two components existed). The check for a rule like this is to
   measure item width against container width AT EVERY WIDTH BELOW THE
   COMPONENT'S OWN BREAKPOINT, not to reason about the grid state alone.

   Specificity note, recorded because it bit the first draft of this very
   rule: `.phase-plan__item{max-width:none}` (one class, 0-1-0) LOSES to
   `.wrap li{max-width:34em}` (one class + one type, 0-1-1) regardless of
   source order — classes tie, and the type selector on the .wrap rule
   breaks the tie in its favour. Measuring after that first draft still
   showed 573/738 at 820, unchanged. Scoping under `.wrap` and keeping the
   `li` type here (0-2-1) unconditionally outranks `.wrap li`, so this wins
   without depending on being declared later in the file. */
.wrap li.phase-plan__item,
.wrap li.checklist__item,
.wrap li.timeline__item {
  max-width: none;
}

/* A pull-quote treatment for the one sentence a section is built around —
   used for the canonical ChatGPT-attribution line and its counterparts. */
.callout {
  font-size: var(--fs-lead);
  font-weight: 600;
  border-inline-start: 3px solid currentColor;
  padding-inline-start: var(--sp-5);
  margin-block: var(--sp-6);
}

/* "A day in their life" list: order matters (four outcomes, in sequence),
   hence <ol>, styled without numerals — a vertical rhythm, not a feature list. */
.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-8);
}
.timeline__item {
  padding-inline-start: var(--sp-6);
  border-inline-start: 2px solid currentColor;
}
.timeline__when {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  opacity: 0.6;
  margin: 0 0 var(--sp-2);
}
@media (min-width: 900px) {
  .timeline { grid-template-columns: repeat(2, 1fr); }
}

/* Five-node flow diagram: rendered as two complete inline <svg> alternates,
   not one flexed shape reflowed by CSS — a five-node horizontal flow does
   not compress readably onto a phone, so below 720px the vertical version
   shows and the horizontal version hides. Both carry the same five nodes and
   glosses; this is a breakpoint swap of one diagram, not two different ones. */
.diagram-wrap { margin-block: var(--sp-8) 0; }
.diagram-wide, .diagram-narrow {
  width: 100%;
  height: auto;
  display: block;
}
.diagram-narrow { display: none; }
@media (max-width: 719.98px) {
  .diagram-wide { display: none; }
  .diagram-narrow { display: block; }
}

/* Three-phase delivery plan: a horizontal row of cards from 900px up, one
   column (stacked cards) below it — same shape as .timeline above, reused
   here for sequential phases instead of a day-in-the-life sequence.
   .phase-plan (an <ol>) is the thing that needs the full .wrap width, and it
   is never subject to .wrap li's 34em prose cap because that selector only
   targets li/p, not ol. Each card IS an li, so it inherits the cap too — see
   the .phase-plan__item/.checklist__item/.timeline__item opt-out declared
   right after .wrap p, .wrap li above, and its comment, for why a card must
   NOT rely on the grid column alone staying under 34em: that only holds
   >=900px, and this component is single-column below it. */
.phase-plan {
  list-style: none;
  counter-reset: phase-step;
  margin: var(--sp-8) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-6);
}
@media (min-width: 900px) {
  .phase-plan {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--sp-8);
  }
}
.phase-plan__item {
  counter-increment: phase-step;
  border-block-start: 3px solid var(--imv-gold);
  padding-block-start: var(--sp-4);
}
.phase-plan__item .num-badge::before { content: counter(phase-step); }
.phase-plan__name { margin: var(--sp-3) 0 var(--sp-1); }
/* This label deliberately follows .phase-plan__name in the DOM, not the
   other way round, even though visually it reads like the eyebrow that
   precedes a heading elsewhere on this page. A window label ending in a bare
   digit ("Months 4–9") placed immediately BEFORE a heading beginning "Phase
   N — …" lets G6's count guard read the two as one string once tags are
   stripped — "4–9 Phase" matches the digit-then-"phases?" pattern and gets
   flagged as an unlisted count, even though nobody asserted "4–9 phases".
   Putting the heading first means a heading never ends in a bare digit, so
   the adjacency this guard watches for cannot occur. */
.phase-plan__window {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  opacity: 0.65;
  margin: 0 0 var(--sp-3);
}
.phase-plan__list {
  margin: 0;
  padding-inline-start: 1.1em;
  display: grid;
  gap: var(--sp-2);
}

/* The numbered circle shared by the phase cards above and the client-input
   checklist below. The digit is CSS-GENERATED (counter(), via the ::before
   rules beside each list), never literal text in the HTML. Reason: a literal
   "2" sitting in the source immediately before a paragraph that starts
   "Months 4–9" reads, after tag-stripping, as "2 Months 4–9" — G6's count
   guard reads that as an assertion of "2 months", which figures.json does
   not list, and fails the build on a number nobody actually asserted. Moving
   the digit into generated content removes it from the text guards scan
   entirely instead of trying to out-word a false positive. Gold is used here
   only as a BACKGROUND, never as text colour, so G4 does not apply — the
   circle's own text (the counter) is --imv-ink, not gold, on both surfaces. */
.num-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--imv-gold);
  color: var(--imv-ink);
  font-weight: 700;
  font-family: "Bricolage Grotesque", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* Client-input checklist: two columns from 900px, one below it — same
   breakpoint as .phase-plan and .timeline above, deliberately, so 820px
   (tablet) never drops into a narrowed multi-column read; only 1440 does.
   Same ol-not-li reasoning as .phase-plan above for the container — the grid
   container is the <ol>, never a bare <p>/<li>, so it is exempt from .wrap
   li's prose cap and can take the full .wrap width. Each ITEM is still a
   <li>, and needs the explicit opt-out declared beside .wrap p, .wrap li
   above — see that comment. */
.checklist {
  list-style: none;
  counter-reset: input-step;
  margin: var(--sp-8) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-5);
}
@media (min-width: 900px) {
  .checklist {
    grid-template-columns: repeat(2, 1fr);
    column-gap: var(--sp-8);
  }
}
.checklist__item {
  counter-increment: input-step;
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
}
.checklist__item .num-badge::before { content: counter(input-step); }
.checklist__item p { margin: 0; }

/* Closing sign-off line: extra breathing room above it and enough weight to
   read as an ending, nothing more — no card, no border, no rule. Deliberately
   unceremonious, so the only CSS this needed was space. */
.signoff {
  margin-block-start: var(--sp-8);
  font-weight: 600;
}

/* A vertical list of links out to the four console-mockup screens (section
   7). Deliberately single-column and short-line, not a grid card — but the
   .wrap li 34em prose cap DOES bind here, and is left binding on purpose:
   measured live at 1440, .screen-links (a grid, 1 column) renders 1024px
   wide and each .screen-links__item is clipped to the 34em (~612px) cap,
   not its natural 1024px row width. That's fine for this component because
   its content is short label + one-line gloss, not a paragraph the cap
   would visibly truncate — so no opt-out was added, unlike
   .phase-plan__item/.checklist__item/.timeline__item, which genuinely
   needed their full grid-column width. Tasks 7 and 8 add more mockup sets
   and can reuse this same list for their own screens; if either needs a
   *full-width* row (unlike this one), it must opt out the same way those
   three do — `.wrap li.your-class { max-width: none }`, scoped under
   `.wrap` and keeping the `li` type (0-2-1). Do not just add
   `.your-class { max-width: none }` on its own (0-1-0): that loses the
   specificity tie-break against `.wrap li` (0-1-1) and silently does
   nothing, regardless of source order. See the specificity note above
   .phase-plan__item/.checklist__item/.timeline__item for the full
   measurement that first caught this. */
.screen-links {
  list-style: none;
  margin: var(--sp-6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-3);
}
.screen-links__item a {
  display: block;
  padding-block: var(--sp-2);
  border-block-end: 1px solid currentColor;
  border-color: color-mix(in srgb, currentColor 20%, transparent);
  text-decoration: none;
  color: inherit;
}
.screen-links__item a:hover,
.screen-links__item a:focus-visible {
  color: var(--imv-gold-on-light); /* #8A6200 — not in G4's restricted hex
                                       list, so it is safe as text on any
                                       surface; #leads and #phases both
                                       render as .imv-light, where this is
                                       the correct hover colour outright.
                                       #tracking is .imv (dark) and
                                       needs the OTHER gold — imove.css adds
                                       a higher-specificity `.imv
                                       .screen-links__item a:hover` override
                                       for that surface rather than this
                                       shared rule trying to branch on
                                       surface itself. */
}
.screen-links__item strong { display: block; }
.screen-links__item span { opacity: 0.7; font-size: 0.9em; }

/* ---------- Buttons & form controls ---------- */
/* Mobile-first tap targets: 44-48px. Any form input at 16px so iOS does not
   zoom the page on focus. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding-inline: var(--sp-6);
  font-size: 1rem;
  font-weight: 600;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  text-decoration: none;
}
input, textarea, select, button {
  font-size: 16px;
  min-height: 44px;
}
