/* ============================================================================
   DELHIVERY — WEBSITE REDESIGN CASE STUDY
   Four sections, screenshot-led, minimal.

   Written mobile-first for real: every rule outside a media query targets a
   ~390px canvas, and larger screens only ever ADD to it through min-width
   blocks. Nothing here is a desktop layout being folded back down.

   Layered on top of style.css, which already supplies the design tokens, the
   topbar/footer chrome, the optional-image fade-in and the .compare slider.

     1  Page tokens & gutter
     2  Chrome adjustments
     3  Primitives — ghost, phone, browser frame, plate
     4  Editorial furniture — eyebrow, headings, lede, caption
     5  Hero
     6  Challenge
     7  Solution
     8  Final experience
     9  Before / after
    10  Outcome, closing, next project
    11  TABLET         min-width: 768px
    12  DESKTOP        min-width: 1024px
    13  LARGE DESKTOP  min-width: 1440px
    14  Reduced motion
   ========================================================================= */

/* --------------------------------------------------------- 1 tokens & gutter */
:root {
  /* Delhivery-inspired palette, carried on the site's black canvas: red and
     black from the brand, warm white for type, warm light grey for copy. */
  --cs-red:    #b162fe;
  --cs-red-lo: rgba(177, 98, 254, .5);
  --cs-white:  #f7f5f2;   /* warm white */
  --cs-grey:   #a8a5a0;   /* warm light grey */
  --cs-dim:    #6e6b66;
  --cs-line:   rgba(247, 245, 242, .1);
  --cs-line-2: rgba(247, 245, 242, .18);
  --cs-panel:  #101011;

  --cs-space: 5.5rem;     /* 88px — mobile section rhythm */
  --cs-gap:   3.5rem;     /* 56px — gap between moments/steps */
  --cs-h1:    2.6rem;     /* 41.6px */
  --cs-h2:    1.95rem;    /* 31.2px */
  --cs-h3:    1.45rem;
  --cs-phone: 210px;
  --cs-r:     1.85rem;    /* phone radius */

  /* 22px of horizontal breathing room on mobile — style.css drops the global
     gutter to 18.4px under 640px, which is tighter than this page wants. */
  --gutter: 1.375rem;
}

/* ------------------------------------------------------- 2 chrome adjustments */
/* style.css makes the topbar transparent under 640px because the homepage hero
   sits behind it. Here it is a plain bar over a plain canvas, so it needs its
   background back. */
@media (max-width: 640px) {
  .topbar { background: var(--background); }
}

.cs-brand { display: flex; align-items: center; gap: .6rem; min-height: 44px; }
.cs-brand__arrow {
  width: 1.3rem; height: .6rem; color: var(--cs-grey);
  transition: transform .3s var(--ease), color .3s var(--ease);
}
.cs-brand:hover .cs-brand__arrow,
.cs-brand:focus-visible .cs-brand__arrow { transform: translateX(-5px); color: var(--cs-red); }

.cs-body { background: var(--background); }

/* ============================================================= 3 primitives */

/* Placeholder that shows through wherever a screenshot has not been supplied
   yet — script.js removes the <img> and this is what is left. */
.cs-ghost {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center;
  font-family: var(--font-cond); font-weight: 300;
  font-size: clamp(.72rem, 3.1vw, .95rem);
  letter-spacing: .18em; text-transform: uppercase; line-height: 1.7;
  color: rgba(247, 245, 242, .3);
  text-align: center;
  background:
    radial-gradient(130% 110% at 74% 8%, rgba(177, 98, 254, .16), transparent 64%),
    linear-gradient(158deg, #17171a, #0b0b0c 60%, #121214);
  pointer-events: none;
}

/* phone — one frame, reused sparingly and always at a size you can read */
.cs-phone {
  position: relative; flex: none;
  width: var(--cs-phone);
  aspect-ratio: 9 / 19.5;
  padding: .38rem;
  background: linear-gradient(158deg, #222226, #060607);
  border-radius: var(--cs-r);
  box-shadow: 0 30px 60px -28px rgba(0, 0, 0, .85), inset 0 0 0 1px rgba(255, 255, 255, .07);
}
.cs-phone::before {
  content: ""; position: absolute; z-index: 3;
  top: .44rem; left: 50%; transform: translateX(-50%);
  width: 30%; height: .32rem; border-radius: 1rem; background: #060607;
}
.cs-phone__screen {
  position: relative; width: 100%; height: 100%;
  border-radius: calc(var(--cs-r) - .38rem);
  overflow: hidden; background: #0b0b0c;
}
.cs-phone__screen picture { position: absolute; inset: 0; z-index: 1; }
.cs-phone__screen img { width: 100%; height: 100%; object-fit: cover; object-position: top; }

/* browser frame — used only where the chrome carries meaning (which URL, and
   whether we are looking at the archive or the live redesign) */
.cs-frame {
  border: 1px solid var(--cs-line);
  background: var(--cs-panel);
  border-radius: .55rem;
  overflow: hidden;
  box-shadow: 0 34px 64px -34px rgba(0, 0, 0, .9);
}
.cs-frame__bar {
  display: flex; align-items: center; gap: .38rem;
  padding: .5rem .7rem;
  border-bottom: 1px solid var(--cs-line);
}
/* :not() keeps the traffic-light dots from also matching the URL pill, which
   is a sibling span and would otherwise be collapsed to 5px by them */
.cs-frame__bar > span:not(.cs-frame__url) {
  width: 5px; height: 5px; border-radius: 50%; background: var(--cs-dim);
}
.cs-frame__url {
  margin-left: .45rem; padding: .22em .6em;
  border: 1px solid var(--cs-line);
  background: none;
  font-family: var(--font-cond); font-weight: 300;
  font-size: .6rem; letter-spacing: .08em; color: var(--cs-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cs-frame__shot { position: relative; aspect-ratio: 16 / 10; overflow: hidden; }
.cs-frame__shot picture { position: absolute; inset: 0; z-index: 1; }
.cs-frame__shot img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
/* The archived page is a long full-page scroll, so on a phone the frame goes
   portrait — at 346px wide that shows about 70% of the page's height, which is
   what makes the "information-heavy" point. Widened at 768px. */
.cs-frame__shot--tall { aspect-ratio: 3 / 4; }

/* plate — a bare bordered screenshot. Deliberately chrome-less so the
   solution and final sections do not repeat the browser mockup. */
.cs-plate {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border: 1px solid var(--cs-line);
  background: var(--cs-panel);
  box-shadow: 0 30px 58px -32px rgba(0, 0, 0, .85);
}
.cs-plate picture { position: absolute; inset: 0; z-index: 1; }
.cs-plate img { width: 100%; height: 100%; object-fit: cover; object-position: top; }

/* ================================================== 4 editorial furniture */
.cs-sec {
  padding-block: var(--cs-space);
  border-top: 1px solid var(--cs-line);
}
.cs-sec__head { margin-bottom: 2.75rem; }
/* --split becomes two columns at 1024px+ (see the desktop block); it stacks
   like any other head below that. */

.cs-eyebrow {
  display: flex; align-items: center; gap: .8rem;
  margin-bottom: 1.35rem;
  font-family: var(--font-cond); font-weight: 500;
  font-size: .68rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--cs-red);
}
.cs-eyebrow__rule { width: 1.6rem; height: 1px; background: var(--cs-red); flex: none; }

.cs-h1, .cs-h2, .cs-h3 {
  color: var(--cs-white);
  font-family: var(--font-cond); font-weight: 400;
  letter-spacing: -.012em;
  text-wrap: balance;
}
.cs-h1 { font-size: var(--cs-h1); line-height: 1.05; max-width: 15ch; }
.cs-h2 { font-size: var(--cs-h2); line-height: 1.12; max-width: 22ch; }
.cs-h3 { font-size: var(--cs-h3); line-height: 1.2;  max-width: 24ch; }

.cs-lede {
  margin-top: 1.35rem;
  font-size: 1rem; line-height: 1.7;
  color: var(--cs-grey);
  max-width: 48ch;
}

.cs-meta {
  display: grid; gap: 1.35rem;
  margin-top: 2.5rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--cs-line);
}
.cs-meta dt {
  font-family: var(--font-cond); font-weight: 500;
  font-size: .62rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--cs-dim);
  margin-bottom: .45rem;
}
.cs-meta dd {
  font-family: var(--font-cond); font-weight: 300;
  font-size: .95rem; letter-spacing: .02em; line-height: 1.5;
  color: var(--cs-white);
}

/* ================================================================== 5 hero */
.cs-hero { padding-block: 3rem var(--cs-space); }

.cs-hero__visual {
  position: relative;
  margin-top: 3.25rem;
  /* The cropped desktop slice is meant to run off the right edge of the
     screen. The visual breaks out of the shell to reach it and clips there
     with overflow — clip-path would only stop the paint, leaving the plate
     still contributing its full width to the document's scroll extent.
     padding-bottom leaves room for the phone's drop shadow inside the clip. */
  margin-right: calc(-1 * var(--gutter));
  padding-bottom: 2.5rem;
  overflow: hidden;
}

/* Mobile composition: the phone leads, a cropped slice of the desktop screen
   sits behind and runs off the right edge. Two screens, nothing tiny. */
.cs-hero__desktop {
  position: absolute; z-index: 1;
  top: 6%; left: 46%; width: 148%;
  opacity: .55;
}
.cs-hero__phone { position: relative; z-index: 2; width: 205px; }

/* the route line, behind both screens, threading the negative space */
.cs-route {
  position: absolute; inset: -10% -8%;
  z-index: 0; width: 116%; height: 120%;
  overflow: hidden;
}
.cs-route__line {
  stroke: var(--cs-red); stroke-width: 1.25; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  opacity: .55;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;   /* undrawn until .is-drawn lands */
}
.cs-route__dot {
  fill: var(--cs-red);
  opacity: 0;
  filter: drop-shadow(0 0 6px rgba(177, 98, 254, .9));
}
/* JS adds .is-drawn once the hero is in view and motion is allowed */
.cs-route.is-drawn .cs-route__line {
  animation: cs-draw 2.6s cubic-bezier(.4, 0, .2, 1) forwards;
}
.cs-route.is-drawn .cs-route__dot { animation: cs-dot-in .5s .15s ease forwards; }

@keyframes cs-draw   { to { stroke-dashoffset: 0; } }
@keyframes cs-dot-in { to { opacity: 1; } }

/* ============================================================= 6 challenge */
/* One screenshot, three observations, at every width — mobile stacks them,
   1024px+ moves the observations into the margins beside the screenshot.
   The archived capture is a long full-page scroll (1920x3673), so the frame
   is portrait on phones to show most of that density, and reverts to a wide
   4/3 window once the frame itself is big enough to read. */
.ch__full { margin-bottom: 3rem; }
.ch__full figcaption {
  margin-top: .9rem;
  font-family: var(--font-cond); font-weight: 300;
  font-size: .66rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--cs-dim);
}

.ch__notes { display: grid; gap: 2.5rem; }

/* a red hairline over each observation stands in for the leader line that
   bridges the gutter at desktop widths */
.ch__note { position: relative; padding-top: 1.35rem; }
.ch__note::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 2rem; height: 1px;
  background: var(--cs-red);
}

.ch__t {
  font-family: var(--font-cond); font-weight: 500;
  font-size: 1.15rem; line-height: 1.25; letter-spacing: .005em;
  color: var(--cs-white);
  margin-bottom: .55rem;
}
.ch__d { font-size: .95rem; line-height: 1.65; color: var(--cs-grey); max-width: 42ch; }

/* ============================================================== 7 solution */
.sol { display: grid; gap: var(--cs-gap); }
.sol__step { display: grid; gap: 1.5rem; }

.sol__num {
  font-family: var(--font-cond); font-weight: 400;
  font-size: .7rem; letter-spacing: .22em;
  color: var(--cs-red);
  margin-bottom: .8rem;
}
.sol__t {
  font-family: var(--font-cond); font-weight: 400;
  font-size: 1.85rem; line-height: 1.05; letter-spacing: -.01em;
  color: var(--cs-white);
  margin-bottom: .7rem;
}
.sol__d { font-size: .95rem; line-height: 1.7; color: var(--cs-grey); max-width: 40ch; }

/* The real captures for this section turn out to be moderate, near-native-size
   component screenshots (roughly 620-670px tall), not giant wide page
   captures — so a plain centred cover crop already keeps their content
   legible and in frame at every width. An earlier version of this rule
   guessed every screenshot would need an aggressive top-left-anchored zoom to
   compensate for being much wider than the plate; for a screenshot whose real
   ratio is already close to the plate's 16:10 (delhivery-desktop-tracking.png
   is 1001x618, ratio 1.62) that zoom just sliced the right edge off a
   centred card instead. --pos is an escape hatch: set it on a specific
   .sol__step if a future screenshot's meaningful content sits off-centre
   (e.g. --pos: left center for a nav panel pinned to one edge). */
.sol__visual .cs-plate img { object-position: var(--pos, center); }

/* ====================================================== 8 final experience */
.cs-sec--final .cs-sec__head { margin-bottom: 3.25rem; }

.fin { margin-bottom: 4.5rem; }
.fin__stage { position: relative; display: grid; gap: 1.5rem; }

.fin__cap {
  margin-top: 1.5rem;
  font-size: .9rem; line-height: 1.65;
  color: var(--cs-grey);
  max-width: 44ch;
}
.fin__tag {
  display: block;
  margin-bottom: .5rem;
  font-family: var(--font-cond); font-weight: 500;
  font-size: .62rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--cs-white);
}

/* Every plate in this section runs flush to the content edges, so the three
   moments share one left and right margin and read as a sequence. Nothing is
   absolutely positioned, offset or floated out into the gutter — that is what
   made the section feel scattered. */
.fin__plate { width: 100%; }

/* Moment 1 — the ONLY moment with a phone, because showing the same homepage
   on both screens is the point of it. Mobile stacks the desktop screen then
   the phone, centred under it. */
.fin--home .fin__desktop { width: 100%; }
.fin--home .fin__phone {
  width: 100%; max-width: 230px;
  justify-self: center;
}

/* Moment 2 — the navigation above a service page, both full width. Both slots
   are 2/1 because both supplied captures are 2704x1352 (ratio 2.000), so they
   land with no crop at all. These two must show the WHOLE screenshot — they
   are interface captures, not full-page scrolls that can be top-cropped — so
   they also carry data-fit-image, which re-reads the real ratio from the file
   at runtime. This CSS value is the fallback used while the ghost shows. */
.fin--services .fin__plate--nav,
.fin--services .fin__plate--svc { aspect-ratio: 2 / 1; }

/* ========================================================= 9 before / after */
.cs-ba { margin-top: 5rem; }
.cs-ba .cs-h3 { margin-bottom: 1.75rem; }

/* The stage matches the captures' own 16/9 at EVERY width, so the whole
   screenshot is visible and nothing is cropped.
   It used to go taller on small screens (16/13 here, 16/10 at 768) on the
   reasoning that a desktop capture squeezed to phone width is an unreadable
   strip. That was the wrong trade: object-fit is cover, so a taller box does
   not enlarge the image, it just crops 31% off the sides — you lost a third
   of the comparison and the halves no longer lined up with the real pages.
   A 16/9 shot can never be taller than width/1.78 anyway, so on a 390px phone
   this is ~195px tall. That is the honest size; the slider is what makes it
   readable, not the box.
   data-fit-image on the stage re-reads the true ratio from the loaded file,
   so a re-export at a different shape self-corrects. */
.cs-ba__figure .compare__stage { aspect-ratio: 16 / 9; border-color: var(--cs-line); }
.cs-ba__figure .compare__cap { color: var(--cs-dim); }
/* style.css's .compare__layer img has no object-position, so it defaults to
   centre. Both captures here are full-page scrolls (delhivery-old-desktop.png
   is 1920x3673, delhivery-desktop-home.png is 1920x6239) — centring would
   show an arbitrary strip from the middle of the page instead of the
   homepage fold the comparison is actually meant to show. */
.cs-ba__figure .compare__layer img { object-position: top; }

/* ============================================ 10 outcome, closing, next */
.cs-outcome {
  margin-top: 4rem;
  padding-left: 1.15rem;
  border-left: 2px solid var(--cs-red);
  font-family: var(--font-cond); font-weight: 300;
  font-size: 1.15rem; line-height: 1.55;
  color: var(--cs-white);
  max-width: 44ch;
}

.cs-closing {
  margin-top: 3.25rem;
  font-family: var(--font-cond); font-weight: 400;
  font-size: 1.55rem; line-height: 1.25; letter-spacing: -.01em;
  color: var(--cs-white);
  max-width: 24ch;
  text-wrap: balance;
}

.cs-next {
  margin-top: 4.5rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--cs-line);
}
.cs-next__label {
  font-family: var(--font-cond); font-weight: 500;
  font-size: .62rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--cs-dim);
  margin-bottom: .5rem;
}
.cs-next__link {
  display: flex; align-items: center; justify-content: space-between; gap: 1.25rem;
  min-height: 44px;   /* touch target */
  font-family: var(--font-cond); font-weight: 400;
  font-size: 1.5rem; letter-spacing: -.005em;
  color: var(--cs-white);
  transition: color .3s var(--ease);
}
.cs-next__link .arrow { width: 2.1rem; color: var(--cs-red); }
.cs-next__link:hover, .cs-next__link:focus-visible { color: var(--cs-red); }
.cs-next__link:hover .arrow { transform: translateX(6px); }

/* Reveals — the only motion outside the two set pieces — are entirely
   style.css's: `.js [data-reveal]` / `.js [data-reveal].is-in` there already
   carry the fade-and-rise, the --d delay hook and the no-JS safety of being
   scoped to .js. Redefining them here only shadowed them at lower specificity,
   so this page just uses data-reveal and data-reveal-delay in the markup. */


/* ============================================================================
   11  TABLET — min-width: 768px
   ========================================================================= */
@media (min-width: 768px) {
  :root {
    --gutter: 2.25rem;
    --cs-space: 7rem;
    --cs-gap: 5rem;
    --cs-h1: 3.6rem;
    --cs-h2: 2.6rem;
    --cs-h3: 1.85rem;
  }

  .cs-hero { padding-block: 4rem var(--cs-space); }
  .cs-hero__visual { margin-top: 4rem; }
  .cs-hero__desktop { top: 4%; left: 40%; width: 120%; opacity: .6; }
  .cs-hero__phone { width: 240px; }

  /* Role, Platforms, Completed Through — three short fields, one row. */
  .cs-meta { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2rem; }
  .cs-lede { font-size: 1.05rem; }

  /* Challenge — the frame is wide enough now to read as a desktop window, and
     the three observations sit in a row beneath it */
  .cs-frame__shot--tall { aspect-ratio: 4 / 3; }
  .ch__notes { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2.25rem; }

  /* Solution — copy and visual side by side, alternating */
  .sol__step {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    align-items: center; gap: 2.25rem;
  }
  .sol__step:nth-child(even) .sol__copy  { grid-column: 2; grid-row: 1; }
  .sol__step:nth-child(even) .sol__visual { grid-column: 1; grid-row: 1; }
  .sol__t { font-size: 2.4rem; }

  /* Moment 1 — desktop screen and phone side by side, bottoms aligned so the
     pair sits on one baseline instead of floating at different heights */
  .fin--home .fin__stage {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end; gap: 2rem;
  }
  .fin--home .fin__phone { width: 200px; max-width: none; justify-self: auto; }

  /* Moment 2 stays a stacked pair — that IS its composition, and it keeps the
     nav capture at full width where it needs no crop. */
  .fin--services .fin__stage { gap: 1.5rem; }

  .cs-outcome { font-size: 1.35rem; padding-left: 1.5rem; }
  .cs-closing { font-size: 2.1rem; }
  .cs-next__link { font-size: 2rem; justify-content: flex-start; }
}


/* ============================================================================
   12  DESKTOP — min-width: 1024px
   ========================================================================= */
@media (min-width: 1024px) {
  :root {
    /* style.css narrows the shell to 900px under `max-width: 1024px`, which
       overlaps this block at exactly 1024 and left the desktop layout ~100px
       narrower than it is designed for. Reclaim the site's wide shell from
       here up — a screenshot-led page needs the width. */
    --shell: 1480px;

    --gutter: 3rem;
    --cs-space: 9.5rem;
    --cs-gap: 8rem;
    --cs-h1: 4.6rem;
    --cs-h2: 3.2rem;
    --cs-h3: 2.2rem;
  }

  /* ---- hero: two columns, desktop screen leading ---------------------- */
  .cs-hero { padding-block: 5.5rem var(--cs-space); }
  /* the visual takes the larger share — this is a screenshot-led page, not a
     text column with a picture next to it */
  .cs-hero__inner {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    align-items: center;
    gap: 3.5rem;
  }
  /* both screens now sit inside their own column, so nothing needs to bleed
     off-screen and the clipping can be dropped — which lets the phone keep
     its full drop shadow */
  .cs-hero__visual {
    margin-top: 0;
    margin-right: 0;
    padding: 2.5rem 0 4rem 0;
    overflow: visible;
  }
  /* the desktop screen becomes the dominant object; the phone steps out to
     its lower left with a real gap, so the route line has somewhere to run */
  .cs-hero__desktop {
    position: relative; top: auto; left: auto;
    width: 88%; margin-left: 12%;
    opacity: 1;
  }
  /* sized so its top edge lands level with the desktop frame's while it hangs
     clear below it — the phone reads as standing in front, not floating */
  .cs-hero__phone {
    position: absolute; z-index: 2;
    left: 0; bottom: 0;
    width: 176px;
  }
  .cs-route { inset: -6% -4%; width: 108%; height: 112%; }

  /* ---- section heads: heading left, paragraph right ------------------- */
  /* the eyebrow spans both columns so the heading and its paragraph start on
     the same line beneath it */
  .cs-sec__head--split {
    display: grid;
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    column-gap: clamp(3rem, 6vw, 7rem);
    align-items: start;
  }
  .cs-sec__head--split .cs-eyebrow { grid-column: 1 / -1; }
  .cs-sec__head--split .cs-h2 { grid-column: 1; max-width: 20ch; }
  .cs-sec__head--split .cs-lede {
    grid-column: 2;
    margin-top: .55rem;   /* optically level with the heading's first line */
    max-width: 42ch;
  }

  /* ---- challenge: one large screenshot, observations in the margins ---- */
  .ch {
    display: grid;
    grid-template-columns:
      minmax(11rem, 1fr)
      minmax(0, 2.7fr)
      minmax(11rem, 1fr);
    grid-template-rows: auto 1fr auto;
    column-gap: var(--ch-bridge, 2.5rem);
    row-gap: 2.5rem;
    align-items: start;
  }
  .ch__full { grid-column: 2; grid-row: 1 / span 3; margin-bottom: 0; }

  /* the <ol> steps out of the way so its items become items of .ch's grid;
     role="list"/role="listitem" in the markup preserves the semantics */
  .ch__notes { display: contents; }

  .ch__note--1 { grid-column: 1; grid-row: 1; text-align: right; }
  .ch__note--2 { grid-column: 3; grid-row: 2; }
  .ch__note--3 { grid-column: 1; grid-row: 3; text-align: right; max-width: none; }
  .ch__note--1 .ch__d, .ch__note--3 .ch__d { margin-left: auto; }

  /* the mobile rule above the text becomes a leader line bridging the gutter
     to the screenshot */
  .ch__note { padding-top: 0; }
  .ch__note::before {
    content: ""; position: absolute; top: .6rem; left: auto;
    width: var(--ch-bridge, 2.5rem); height: 1px;
    background: var(--cs-red); opacity: .6;
  }
  .ch__note--1::before, .ch__note--3::before { right: calc(-1 * var(--ch-bridge, 2.5rem)); }
  .ch__note--2::before { left: calc(-1 * var(--ch-bridge, 2.5rem)); }

  .ch__t { font-size: 1.3rem; }
  .ch__d { max-width: none; }

  /* ---- solution: editorial three-part layout -------------------------- */
  .sol__step {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 3rem;
    align-items: center;
  }
  .sol__step:nth-child(odd)  .sol__copy   { grid-column: 1 / span 3; grid-row: 1; }
  .sol__step:nth-child(odd)  .sol__visual { grid-column: 5 / span 8; grid-row: 1; }
  .sol__step:nth-child(even) .sol__visual { grid-column: 1 / span 8; grid-row: 1; }
  .sol__step:nth-child(even) .sol__copy   { grid-column: 10 / span 3; grid-row: 1; }
  .sol__t { font-size: 3rem; }
  .sol__d { font-size: 1rem; }

  /* ---- final experience ---------------------------------------------- */
  .fin { margin-bottom: 8rem; }

  .fin--home .fin__stage { gap: 3rem; }
  .fin--home .fin__phone { width: 248px; }
  .fin--home .fin__cap { max-width: 52ch; }

  .fin--services .fin__stage { gap: 2rem; }

  .cs-ba { margin-top: 7rem; }

  .cs-outcome { font-size: 1.6rem; max-width: 40ch; }
  .cs-closing { font-size: 2.9rem; max-width: 22ch; margin-top: 4.5rem; }
  .cs-next { margin-top: 7rem; }
  .cs-next__link { font-size: 2.75rem; }
}


/* ============================================================================
   13  LARGE DESKTOP — min-width: 1440px
   ========================================================================= */
@media (min-width: 1440px) {
  :root {
    --gutter: 4rem;
    --cs-space: 11.5rem;
    --cs-gap: 10rem;
    --cs-h1: 5.6rem;
    --cs-h2: 3.9rem;
    --cs-h3: 2.6rem;
    --ch-bridge: 3.5rem;
  }

  .cs-hero__inner { grid-template-columns: minmax(0, 5fr) minmax(0, 8fr); gap: 5rem; }
  .cs-hero__phone { width: 214px; }

  .fin--home .fin__phone { width: 292px; }

  .sol__t { font-size: 3.6rem; }
  .cs-closing { font-size: 3.6rem; }
  .cs-next__link { font-size: 3.2rem; }
}


/* ============================================================================
   14  REDUCED MOTION
   Both set pieces degrade to their end state: the route line is simply drawn,
   the tracking point rests on it, and nothing translates or fades in. The
   before/after slider keeps working — it is an interaction, not an animation.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  .cs-route__line { stroke-dashoffset: 0; }
  .cs-route__dot { display: none; }
  .cs-route.is-drawn .cs-route__line,
  .cs-route.is-drawn .cs-route__dot { animation: none; }

  /* [data-reveal] is already forced visible with !important by style.css */
  .cs-next__link:hover .arrow { transform: none; }
  .cs-brand:hover .cs-brand__arrow { transform: none; }
}
