/* ===========================================================================
   /services/website-design-development/ — page layer over the shared
   service-detail system. Ported from the final approved design
   "Tilth Service - Website Design Development & CRO.dc.html" per
   TILTH_WEBSITE_DESIGN_DEVELOPMENT_CRO_HANDOFF.md.

   Loaded AFTER assets/service-detail.css. Everything is scoped to .v3-cro so
   Performance Marketing, Growth Strategy, SEO and Affiliate are untouched —
   service-detail.css itself is not modified at all, which is what keeps those
   four routes byte-identical.

   Four things genuinely differ from the shared system and live here:
     1. this page's approved H1 and closing-CTA ladders, plus the hyphenation
        gate that switches the two H1 breaks off above 620px,
     2. a hero that is STACKED at every width (the shared file splits it at
        1440 and re-splits at 1700),
     3. the WHAT WE BUILD editorial index — this page has no six-item
        deliverables section, by design, and
     4. the CONVERSION PATH, this page's signature interaction.

   Selectors are .v3-service-detail.v3-cro / .v3-cro [hook] so they outrank the
   shared rules on SPECIFICITY, not source order — the shared file sets several
   of these inside larger min-width blocks that also match at wider widths, and
   relying on order there is the trap CLAUDE.md §6b warns about.

   Layout is CSS-first: every grid, gap and type step below is a media query,
   correct before any JS runs. Per the handoff, no responsive property on this
   page may live in an inline style — an inline declaration cannot be overridden
   by a media query, so the desktop treatment leaks into every smaller width.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   0. BODY MARGIN RESET — page-scoped, and load-bearing for the type ladder.

   brand-v3.css never resets the UA default `body{margin:8px}`, so every
   measured track in production is 16px narrower than the approved design's
   (whose helmet sets `body{margin:0}`). Measured: without this the H1 at 620
   overruns its 553px track and drives the fit safeguard down to 40.9px against
   the authored 43px, and 1200 sets five lines instead of four.

   Scoped to body.v3-cro so the Homepage, Services index and the four already
   approved service-detail routes keep their current rendering exactly.
   --------------------------------------------------------------------------- */
body.v3-cro { margin: 0; }

/* ---------------------------------------------------------------------------
   1. TYPE LADDERS — approved and frozen.

   H1   34 / 40 / 43 / 48 / 50 / 50 / 64 / 68 / 68 / 74 / 78 / 86
   CTA  24 / 31 / 34 / 34 / 40 / 40 / 40 / 40 / 40 / 40 / 52 / 52
   (at 390 / 480 / 620 / 700 / 768 / 900 / 1024 / 1180 / 1200 / 1280 / 1440 / 1700)

   Minimum H1 : closing-CTA ratio is 1.25x, at 768 and 900. Neither ladder may
   ever step down as the viewport widens. --h2/--h3/--body/--card/--pad/--sy are
   identical to the shared ladders and are left to cascade.
   --------------------------------------------------------------------------- */
.v3-service-detail.v3-cro { --h1: 34px; }
@media (min-width: 480px)  { .v3-service-detail.v3-cro { --h1: 40px; } }
@media (min-width: 620px)  { .v3-service-detail.v3-cro { --h1: 43px; } }
@media (min-width: 700px)  { .v3-service-detail.v3-cro { --h1: 48px; } }
@media (min-width: 768px)  { .v3-service-detail.v3-cro { --h1: 50px; } }
@media (min-width: 1024px) { .v3-service-detail.v3-cro { --h1: 64px; } }
@media (min-width: 1180px) { .v3-service-detail.v3-cro { --h1: 68px; } }
@media (min-width: 1280px) { .v3-service-detail.v3-cro { --h1: 74px; } }
@media (min-width: 1440px) { .v3-service-detail.v3-cro { --h1: 78px; } }
@media (min-width: 1700px) { .v3-service-detail.v3-cro { --h1: 86px; } }

.v3-cro [data-cta-h] { font-size: 24px; }
@media (min-width: 480px)  { .v3-cro [data-cta-h] { font-size: 31px; } }
@media (min-width: 620px)  { .v3-cro [data-cta-h] { font-size: 34px; } }
@media (min-width: 768px)  { .v3-cro [data-cta-h] { font-size: 40px; } }
/* stated so the ladder reads straight down; the shared 1024 step (46px) already
   loses to the specificity above */
@media (min-width: 1024px) { .v3-cro [data-cta-h] { font-size: 40px; } }
@media (min-width: 1440px) { .v3-cro [data-cta-h] { font-size: 52px; } }

/* Measure caps in px, never ch — a ch cap scales with font-size and makes a
   shrink-to-fit loop non-convergent. */
@media (min-width: 768px)  { .v3-cro [data-h2] { max-width: 620px; } }
@media (min-width: 1024px) { .v3-cro [data-h2] { max-width: 820px; } }
@media (min-width: 1440px) { .v3-cro [data-h2] { max-width: 900px; } }

/* ---------------------------------------------------------------------------
   2. H1 DISCRETIONARY BREAKS — gated, not greedy

   The H1 carries exactly two soft hyphens: Con&shy;version-focused and
   develop&shy;ment. Below 620 they are load-bearing — at 390 the track is 339px
   and neither CONVERSION- (~397px of ink at 34px) nor DEVELOPMENT. (~441px)
   fits, so without them the headline overruns and drives the fit safeguard to
   its floor.

   Above 620 both words fit on their own line, and left enabled develop&shy;ment
   fired INCORRECTLY at 1024 ("and develop-" / "ment."). Hyphenation is
   therefore switched off from 620 up. Do not ship the breaks ungated.
   --------------------------------------------------------------------------- */
.v3-cro [data-h1] { hyphens: manual; -webkit-hyphens: manual; }
@media (min-width: 620px) {
  .v3-cro [data-h1] { hyphens: none; -webkit-hyphens: none; }
}

/* ---------------------------------------------------------------------------
   3. HERO — STACKED AT ALL TWELVE WIDTHS

   There is no two-column hero breakpoint on this page. Order is always
   COPY -> GROWTH SYSTEM PANEL, the panel full width beneath the copy.

   service-detail.css splits [data-hero-grid] 1.55fr/.45fr at 1440 and re-splits
   it 1.45fr/.55fr at 1700. Both are cancelled here. This was measured, not
   preferred: a two-column test at 1440 forced the H1 from 78px down to 60.8px
   and it still overran by 61px; at 1700, 86px collapsed to 67.1px. One
   unconditional declaration is enough — at 0,2,0 it outranks both shared rules
   (0,1,0) at every width, whichever order the files load in.
   --------------------------------------------------------------------------- */
.v3-cro [data-hero-grid] { grid-template-columns: minmax(0, 1fr); }

/* ---------------------------------------------------------------------------
   4. PROBLEM — six editorial rows, never cards

   The shared file sets the head row but without justify-content, so the hint
   would not sit opposite the H2 at 1100+. Stated here rather than inline: an
   inline responsive property cannot be overridden by a media query, which is
   exactly the defect the approved design fixed in this block.
   --------------------------------------------------------------------------- */
@media (min-width: 1100px) {
  .v3-cro [data-prob-head] { justify-content: space-between; }
}

.v3-cro [data-prow] { grid-template-columns: minmax(0, 1fr); gap: 8px; }
.v3-cro [data-prow] [data-ptag] { justify-self: start; }
@media (min-width: 768px) {
  .v3-cro [data-prow] { grid-template-columns: 44px minmax(0, 1fr); gap: 0 20px; }
  .v3-cro [data-prow] [data-ptag] { grid-column: 2; margin-top: 10px; }
}
@media (min-width: 1200px) {
  .v3-cro [data-prow] { grid-template-columns: 44px minmax(0, 1fr) 190px; gap: 0 32px; align-items: baseline; }
  .v3-cro [data-prow] [data-ptag] { grid-column: 3; margin-top: 0; justify-self: end; text-align: right; }
}

/* ---------------------------------------------------------------------------
   5. WHAT WE BUILD — editorial numbered index, seven rows

   This page deliberately has no six-item deliverables section. A single-column
   ruled index means seven produces no orphan and no dead grid cell at any
   width, which is why it was chosen over a 4+3 matrix.
   --------------------------------------------------------------------------- */
.v3-cro [data-brow] { grid-template-columns: 38px minmax(0, 1fr); gap: 0 16px; align-items: baseline; }
.v3-cro [data-bname] { font-size: 22px; }
@media (min-width: 620px)  { .v3-cro [data-bname] { font-size: 27px; } }
@media (min-width: 768px)  {
  .v3-cro [data-brow] { grid-template-columns: 56px minmax(0, 1fr); gap: 0 24px; }
  .v3-cro [data-bname] { font-size: 33px; }
}
@media (min-width: 1024px) { .v3-cro [data-bname] { font-size: 40px; } }
@media (min-width: 1440px) {
  .v3-cro [data-brow] { grid-template-columns: 72px minmax(0, 1fr); }
  .v3-cro [data-bname] { font-size: 46px; }
}

/* PROOF — flat shared treatment, this page's own rhythm. The approved markup
   carries gap:40px inline, so the shared 900px column-gap never applies; these
   rules restate the design's margins only. */
.v3-cro [data-proof-grid] { margin-top: 48px; }
@media (min-width: 768px) { .v3-cro [data-proof-grid] { margin-top: 60px; } }

/* FAQ panel collapse contract. Without overflow:hidden a bad max-height spills
   over the next question instead of clipping. The closed state is applied by
   script, never authored, so all five answers are readable with JS off. */
.v3-cro [data-faq-ans] {
  overflow: hidden;
  transition: max-height .55s cubic-bezier(.16, 1, .3, 1), opacity .4s;
}

/* ===========================================================================
   6. SIGNATURE — CONVERSION PATH

   One continuous route, ENTRY -> UNDERSTANDING -> TRUST -> DECISION -> ACTION,
   with LEARNING as a post-action feedback destination (never a sixth stage).
   Four underlying conditions sit beneath the route, and the markers ON the path
   ARE the control — there is no tab bar, segmented control, lens row, slider or
   detached panel.

   Every state lives here, keyed off [data-cp][data-friction]. The JS sets one
   attribute and syncs aria; it never writes geometry, so a re-render cannot
   strand the diagram mid-state.
   =========================================================================== */
.v3-cro [data-cp-head] { grid-template-columns: minmax(0, 1fr); gap: 20px; }
.v3-cro [data-cp-foot] { grid-template-columns: minmax(0, 1fr); gap: 16px; }
.v3-cro [data-cp-desktop] { display: none; }
@media (min-width: 900px) {
  .v3-cro [data-cp-desktop] { display: block; }
  .v3-cro [data-cp-mobile] { display: none; }
}
@media (min-width: 1024px) {
  /* align-items belongs here, not in the inline style */
  .v3-cro [data-cp-head] { grid-template-columns: minmax(0, 1fr) auto; gap: 40px; align-items: end; }
  .v3-cro [data-cp-foot] { grid-template-columns: minmax(0, 1fr) 220px; gap: 48px; }
}

.v3-cro [data-cp] path,
.v3-cro [data-cp] circle,
.v3-cro [data-cp] text {
  transition: stroke-opacity .5s cubic-bezier(.16, 1, .3, 1), stroke-width .4s,
              fill-opacity .45s, fill .4s, stroke .4s;
}
.v3-cro [data-cp] :where([data-seg], [data-alt], [data-fb]) { fill: none; stroke: var(--stone); stroke-linecap: round; }
.v3-cro [data-cp] :where([data-seg]) { stroke-width: 2; stroke-opacity: .62; }
.v3-cro [data-cp] :where([data-alt]) { stroke-width: 2; stroke-opacity: 0; }
.v3-cro [data-cp] :where([data-fb])  { stroke-width: 1.5; stroke-opacity: .3; stroke-dasharray: 2 7; }
.v3-cro [data-cp] :where([data-fb="a"], [data-fb="b"]) { stroke-opacity: 0; }
.v3-cro [data-cp] :where([data-node]) { fill: var(--stone); fill-opacity: .85; }
.v3-cro [data-cp] :where(text) { fill: var(--stone); fill-opacity: .8; }
.v3-cro [data-cp] button { background: transparent; border: 0; padding: 0; cursor: pointer; transition: opacity .35s; }
.v3-cro [data-cp] button:focus-visible { outline: 2px solid var(--moss); outline-offset: 4px; }
/* dot border/background come from CSS, not inline, so the state rules can win */
.v3-cro [data-cp] [data-dot] { border: 1.5px solid var(--stone); background: var(--bg); transition: background .3s, border-color .3s, transform .3s; }
.v3-cro [data-cp] [data-mlab] { color: var(--stone); transition: color .3s; }
.v3-cro [data-cp] [data-mseg] { background: var(--stone); opacity: .55; transition: opacity .4s, background .4s; }
.v3-cro [data-cp] [data-mfb]  { background: repeating-linear-gradient(180deg, var(--stone) 0 3px, transparent 3px 9px); opacity: .5; transition: opacity .4s, background .4s; }
.v3-cro [data-cp] [data-mstage] { color: var(--stone); transition: color .4s, opacity .4s; }

/* exactly one readout visible per state */
.v3-cro [data-cp] [data-ro] { display: none; }
.v3-cro [data-cp][data-friction="none"] [data-ro="none"],
.v3-cro [data-cp][data-friction="positioning"] [data-ro="positioning"],
.v3-cro [data-cp][data-friction="ux"] [data-ro="ux"],
.v3-cro [data-cp][data-friction="performance"] [data-ro="performance"],
.v3-cro [data-cp][data-friction="measurement"] [data-ro="measurement"] { display: block; }

/* Stage emphasis — SPECIFICITY IS LOAD-BEARING. The blanket dim rule below is
   (0,4,0) once scoped; each emphasis selector must out-specify it. Do NOT
   refactor these into :where(), which contributes zero and silently loses,
   dimming every stage label with none emphasised. That was a real defect. */
.v3-cro [data-cp]:not([data-friction="none"]) [data-stage]  { fill-opacity: .32; }
.v3-cro [data-cp]:not([data-friction="none"]) [data-mstage] { opacity: .34; }

.v3-cro [data-cp][data-friction="positioning"] [data-stage="entry"],
.v3-cro [data-cp][data-friction="positioning"] [data-stage="understanding"],
.v3-cro [data-cp][data-friction="ux"] [data-stage="understanding"],
.v3-cro [data-cp][data-friction="ux"] [data-stage="trust"],
.v3-cro [data-cp][data-friction="ux"] [data-stage="decision"],
.v3-cro [data-cp][data-friction="performance"] [data-stage="decision"],
.v3-cro [data-cp][data-friction="performance"] [data-stage="action"],
.v3-cro [data-cp][data-friction="measurement"] [data-stage="action"],
.v3-cro [data-cp][data-friction="measurement"] [data-stage="learning"] { fill: var(--fg); fill-opacity: 1; }

.v3-cro [data-cp][data-friction="positioning"] [data-mstage="entry"],
.v3-cro [data-cp][data-friction="positioning"] [data-mstage="understanding"],
.v3-cro [data-cp][data-friction="ux"] [data-mstage="understanding"],
.v3-cro [data-cp][data-friction="ux"] [data-mstage="trust"],
.v3-cro [data-cp][data-friction="ux"] [data-mstage="decision"],
.v3-cro [data-cp][data-friction="performance"] [data-mstage="decision"],
.v3-cro [data-cp][data-friction="performance"] [data-mstage="action"],
.v3-cro [data-cp][data-friction="measurement"] [data-mstage="action"],
.v3-cro [data-cp][data-friction="measurement"] [data-mstage="learning"] { opacity: 1; color: var(--fg); }

/* POSITIONING — ENTRY -> UNDERSTANDING destabilises and never lands
   (data-alt="1" deliberately stops short of the UNDERSTANDING node) */
.v3-cro [data-cp][data-friction="positioning"] [data-seg="1"] { stroke-opacity: 0; }
.v3-cro [data-cp][data-friction="positioning"] [data-alt="1"] { stroke-opacity: .42; stroke-dasharray: 10 9; }
.v3-cro [data-cp][data-friction="positioning"] [data-mseg="1"] { opacity: .24; background: repeating-linear-gradient(180deg, var(--stone) 0 5px, transparent 5px 12px); }

/* UX — the route from understanding to a decision stops being direct */
.v3-cro [data-cp][data-friction="ux"] :where([data-seg="2"], [data-seg="3"]) { stroke-opacity: 0; }
.v3-cro [data-cp][data-friction="ux"] :where([data-alt="2"], [data-alt="3"]) { stroke-opacity: .4; stroke-dasharray: 12 8; }
.v3-cro [data-cp][data-friction="ux"] :where([data-mseg="2"], [data-mseg="3"]) { opacity: .24; background: repeating-linear-gradient(180deg, var(--stone) 0 5px, transparent 5px 12px); }

/* PERFORMANCE — movement toward the action is interrupted at the point of
   commitment; widest dash on the page */
.v3-cro [data-cp][data-friction="performance"] [data-seg="4"] { stroke-opacity: 0; }
.v3-cro [data-cp][data-friction="performance"] [data-alt="4"] { stroke-opacity: .44; stroke-dasharray: 22 16; }
.v3-cro [data-cp][data-friction="performance"] [data-mseg="4"] { opacity: .24; background: repeating-linear-gradient(180deg, var(--stone) 0 9px, transparent 9px 20px); }

/* MEASUREMENT — CRITICAL. The visitor path holds and ACTION is still reached;
   only the feedback connection breaks. Never apply [data-alt] or segment
   suppression here: bad measurement does not stop a visitor clicking a button,
   it stops the business learning where the others dropped. */
.v3-cro [data-cp][data-friction="measurement"] [data-fb="full"] { stroke-opacity: 0; }
.v3-cro [data-cp][data-friction="measurement"] :where([data-fb="a"], [data-fb="b"]) { stroke-opacity: .4; }
.v3-cro [data-cp][data-friction="measurement"] [data-fb="tick"] { stroke-opacity: .08; }
.v3-cro [data-cp][data-friction="measurement"] [data-node="learning"] { fill-opacity: .22; }
.v3-cro [data-cp][data-friction="measurement"] [data-mfb] { opacity: .2; background: repeating-linear-gradient(180deg, var(--stone) 0 4px, transparent 4px 16px); }

/* Selected marker = Moss (active inspection, never "success"). Unselected
   markers recede rather than vanish. Friction itself is only ever --stone,
   opacity, dash pattern and geometry — no red, no green, no good/bad coding. */
.v3-cro [data-cp]:not([data-friction="none"]) [data-mk] { opacity: .42; }
.v3-cro [data-cp][data-friction="positioning"] [data-mk="positioning"],
.v3-cro [data-cp][data-friction="ux"] [data-mk="ux"],
.v3-cro [data-cp][data-friction="performance"] [data-mk="performance"],
.v3-cro [data-cp][data-friction="measurement"] [data-mk="measurement"] { opacity: 1; }

.v3-cro [data-cp][data-friction="positioning"] [data-mk="positioning"] [data-dot],
.v3-cro [data-cp][data-friction="ux"] [data-mk="ux"] [data-dot],
.v3-cro [data-cp][data-friction="performance"] [data-mk="performance"] [data-dot],
.v3-cro [data-cp][data-friction="measurement"] [data-mk="measurement"] [data-dot] { background: var(--moss); border-color: var(--moss); transform: scale(1.2); }

.v3-cro [data-cp][data-friction="positioning"] [data-mk="positioning"] [data-mlab],
.v3-cro [data-cp][data-friction="ux"] [data-mk="ux"] [data-mlab],
.v3-cro [data-cp][data-friction="performance"] [data-mk="performance"] [data-mlab],
.v3-cro [data-cp][data-friction="measurement"] [data-mk="measurement"] [data-mlab] { color: var(--mosstext); }

/* The mobile controls carry inline background/color for the no-CSS baseline,
   and an inline style outranks any selector — so the selected state is the one
   place here that needs !important (CLAUDE.md §6b). */
.v3-cro [data-cp][data-friction="positioning"] [data-mmk="positioning"],
.v3-cro [data-cp][data-friction="ux"] [data-mmk="ux"],
.v3-cro [data-cp][data-friction="performance"] [data-mmk="performance"],
.v3-cro [data-cp][data-friction="measurement"] [data-mmk="measurement"] {
  background: var(--moss) !important;
  border-color: var(--moss) !important;
  color: #101310 !important;
  opacity: 1 !important;
}
.v3-cro [data-cp]:not([data-friction="none"]) [data-mmk] { opacity: .5; }
