﻿/* ==========================================================================
   VDPL Next — Home page

   Loaded only on the front page, by the page-specific rule in functions.php.
   Every class here is prefixed `h-` so nothing on this page can leak into a
   component other templates share.

   Consumes tokens and the `c-`/`l-` primitives. Declares no raw colours and
   no new spacing values.
   ========================================================================== */

/* ==========================================================================
   Hero

   Two columns on desktop, copy first on mobile. The section's own top padding
   is trimmed because the sticky header already sits over it, and a full
   `--section` there reads as a gap rather than as breathing room.
   ========================================================================== */

/* The bottom value is the *entire* gap between the proof stats and the logo
   wall, because .h-clients deliberately carries no top padding — it is
   evidence for the hero's claim, not a section of its own. At 7vw that gap
   measured 90px on a 1280 viewport, which read as the logo wall having come
   loose from the thing it supports. */
.h-hero { padding-block: clamp(48px, 6vw, 88px) clamp(32px, 3.4vw, 52px); }

.h-hero__grid {
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  align-items: center;
  gap: clamp(32px, 5vw, 72px);
}

.h-hero__copy { display: flex; flex-direction: column; gap: var(--s-5); }
.h-hero__copy .t-lead { max-width: 52ch; }

.h-hero__actions { margin-top: var(--s-2); gap: var(--s-3); }

/* The media column is given a fixed aspect ratio so the row height is settled
   before the image loads — the placeholder and a real photo occupy exactly the
   same box, so swapping one for the other shifts nothing. */
.h-hero__media {
  aspect-ratio: 4 / 3;
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--c-surface-2);
}
.h-hero__img { width: 100%; height: 100%; object-fit: cover; }
.h-hero__slot { display: grid; place-items: center; height: 100%; padding: var(--s-6); }
.h-hero__slot .c-placeholder { border: 0; background: none; max-width: 34ch; }

@media (max-width: 900px) {
  .h-hero__grid { grid-template-columns: 1fr; }
  .h-hero__media { aspect-ratio: 16 / 10; }
}

/* Below ~480px the two buttons' default padding no longer fits the viewport
   width side by side, so the row wraps and "See our work" drops beneath the
   primary action. Shrinking the buttons keeps them a pair rather than a
   stack — a wrapped hero CTA reads as broken, not as responsive. */
@media (max-width: 480px) {
  .h-hero__actions { flex-wrap: nowrap; gap: var(--s-2); }
  .h-hero__actions .c-btn {
    flex: 1 1 0;
    padding-inline: var(--s-4);
    font-size: var(--t-sm);
  }
}

/* ---- Proof stats --------------------------------------------------------
   A rule above rather than boxes around each figure: four bordered cards would
   make the numbers compete with the headline directly above them.

   Hairlines *between* the figures, though. With only a rule above and a 24px
   gap, four numbers spread across a 1265px row read as four unrelated facts
   that happened to land on the same line — the eye got no signal that they
   belong to one claim. Dividers turn the row into a single instrument panel,
   which is what it is. The gap goes to zero because the dividers now do the
   separating and a gap as well as a rule is a doubled signal. */

.h-proof {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin-top: clamp(28px, 3.5vw, 48px);
  padding-top: var(--s-6);
  border-top: 1px solid var(--c-line);
}

.h-proof__item {
  padding-inline: var(--s-6);
  border-inline-start: 1px solid var(--c-line-soft);
}
/* The first figure keeps the section's left edge — indenting it would break
   the alignment it shares with the headline above. */
.h-proof__item:first-child { padding-inline-start: 0; border-inline-start: 0; }

/* No size override. The stat figure is --t-d3 from the shared component, the
   same on every page that shows one. */

@media (max-width: 700px) {
  /* Two up. The divider is dropped on the wrapped row's first cell, which
     would otherwise draw a rule down the middle of nothing. */
  .h-proof { grid-template-columns: 1fr 1fr; row-gap: var(--s-5); }
  .h-proof__item:nth-child(odd) { padding-inline-start: 0; border-inline-start: 0; }
}

/* ==========================================================================
   Clients

   Sits tight under the hero deliberately: it is evidence for the claim made
   above it, not a section in its own right.
   ========================================================================== */

/* No top padding is right only while this section is transparent: it then
   continues the hero's ground and the logos read as evidence attached to the
   claim above them. It stops being right the moment the section paints, and
   the tint is not decided here - `.c-main > section:nth-of-type(even)` in
   components.css computes it from the rendered page, so whether this band has
   a ground of its own depends on how many sections above it rendered.

   Landing on an even count, it measured 1px from the band's top edge to the
   eyebrow against 65px below the logos: the heading sat welded to the seam.
   So the zero stays for the transparent case, and the tinted case pays for
   its own air. */
.h-clients { padding-block: 0 clamp(28px, 3vw, 48px); }

.c-main > section.h-clients:nth-of-type(even) {
  padding-top: clamp(20px, 2.5vw, 36px);
}

/* Label above the row.

   It was briefly moved beside the logos to save a line, which worked while
   there were five of them laid out statically. With eleven the row scrolls,
   and a fixed caption sitting next to a moving marquee reads as a column that
   failed to scroll rather than as a heading. It also cost width the marquee
   wants: the caption is only useful once, the logos need the whole line. */
/* The title now takes .t-eyebrow - the same accent-blue treatment every
   other section eyebrow on the site uses. It carried its own muted-gray
   styling instead, which read as an afterthought floating above the row
   rather than a label naming it. The tighter gap here pulls the row up
   under it so the two read as one unit rather than two stacked elements. */
.h-clients__in {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

.h-clients__row {
  display: flex;
  align-items: center;
  gap: var(--s-16);
  padding-inline-end: var(--s-16);
}

/* The static variant, for however many clients are below the marquee
   threshold. `space-between` used to stretch this - correct for exactly the
   width it was tuned against, but on a wide desktop viewport it spaced five
   132px logo boxes edge-to-edge with huge gaps between them, which read as a
   row that had come apart rather than a deliberate wide layout. Centred with
   a comfortable fixed gap holds together at five and still reads fine as more
   clients are added and it wraps to a second line. */
/* Left edge, not centred. Centring was tuned against the width it was written
   at; on a 1600px container it put the caption at x=64 and the first logo at
   x=406, so the label sat alone at the margin with the row floating in the
   middle of the section. The page has one left edge - every heading, every
   card grid - and this row is not the place to invent a second one. */
.h-clients__row--fill {
  justify-content: flex-start;
  gap: var(--s-10) var(--s-8);
  padding-inline-end: 0;
  flex-wrap: wrap;
}

/* Logos are normalised to one optical box and desaturated, so a client with a
   loud logo does not out-shout the rest of the row. Colour returns on hover.

   A *box*, not just a height. Every logo here is an SVG exported without
   width/height attributes, so it has no intrinsic aspect ratio for the browser
   to resolve `width: auto` against — Mahindra rendered several times the width
   of Shutiq off the same 30px height. Fixing both axes and letting
   `object-fit: contain` letterbox inside gives every client the same optical
   footprint regardless of how its file was exported. */
.h-clients__logo {
  height: 45px;
  width: 160px;
  object-fit: contain;
  object-position: center;
  filter: grayscale(1);
  /* .62 was set against logos that filled their box. Several of these are
     raster files with white margins baked in, so the mark inside is smaller
     than the box and .62 pushed it to the edge of legibility. */
  opacity: .74;

  /* Bitmap logos arrive on an opaque white ground, which draws a white
     rectangle on this section's tint. multiply drops white to the background
     and leaves the darks, so a JPEG/PNG logo behaves like a transparent one.
     Vector logos already have alpha and are unaffected. */
  mix-blend-mode: multiply;

  transition: filter var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.h-clients__item:hover .h-clients__logo { filter: none; opacity: 1; }

@media (max-width: 860px) {
  /* Caption back above the row: at this width `auto 1fr` leaves the logos
     about 60% of an already narrow screen. */
  .h-clients__in { grid-template-columns: 1fr; gap: var(--s-5); }
  .h-clients__title { max-width: none; }

  /* One line that scrolls, instead of three ragged ones.
     Wrapped, five logos became 20 / 184 / 20 / 184 / 20 - a 150px block ending
     in one logo alone on its own line, which reads as a layout that ran out
     rather than as a row of clients.

     The scroller bleeds into both gutters and pads itself back, so a logo is
     cut by the screen edge rather than stopping short of it: that cut is the
     only thing telling a thumb there is more to the right. The page itself
     must never scroll sideways, which is why the overflow lives on this
     element and not on an ancestor. */
  .h-clients__row--fill {
    flex-wrap: nowrap;
    gap: var(--s-8);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    /* Snapping measures from the scrollport edge, not from the padding, so on
       load it aligned the first logo to x=0 and ate the gutter the padding had
       just put there - the row started flush against the screen while the
       caption above it sat 20px in. */
    scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
  }
  .h-clients__row--fill::-webkit-scrollbar { display: none; }

  .h-clients__row--fill .h-clients__item {
    flex: none;
    scroll-snap-align: start;
  }
}

.h-clients__word {
  font-size: var(--t-lead);
  font-weight: var(--w-bold);
  letter-spacing: -.02em;
  color: var(--c-ink-3);
  white-space: nowrap;
  transition: color var(--dur) var(--ease);
}
.h-clients__item:hover .h-clients__word { color: var(--c-ink); }

/* The page-level overrides that used to sit here are gone. They existed to
   pull --t-d2 down from 56px and --t-lead down from 22px, which meant the home
   page silently disagreed with the scale every other page used. The scale
   itself now carries those sizes (44 and 18), so one number governs the site.
   Only the measure stays local, because it is about this page's column width
   rather than about type. */
.l-section .l-head .t-lead,
.l-section-sm .l-head .t-lead { max-width: 62ch; }

/* ==========================================================================
   Alternating section ground

   One alternate surface, never a second. Three near-identical greys is the
   clearest "assembled from a template" tell there is.
   ========================================================================== */

.h-alt { background: var(--c-surface-2); }

/* ==========================================================================
   Capability and industry cards
   ========================================================================== */

/* ---- Industry rows -----------------------------------------------------
   Twelve of these run down the home page, so the row has to survive being
   read twelve times. That rules out display type on the name: at --t-d4 the
   list out-shouted the six capability cards above it, which are the more
   important thing on the page.

   Four columns: icon, name, one sentence, arrow. The name column is fixed so
   the sentences share a left edge down the whole list - a ragged second
   column is what made this read as twelve unrelated rows rather than a list.
   The arrow is pinned right and moves on hover, which is enough to say "link"
   without borders or a button. No hover background: the row carries a
   border-top, so a tinted panel would have to either bleed past that rule or
   sit inset from it, and both read as a mistake. */
.h-ind {
  grid-template-columns: auto minmax(0, 19rem) minmax(0, 1fr) auto;
  gap: var(--s-3) var(--s-6);
  align-items: center;
  padding: var(--s-4) 0;
}

.h-ind__name {
  font-size: var(--t-lead);
  font-weight: var(--w-semi);
  letter-spacing: var(--t-lead-ls);
  color: var(--c-ink);
  transition: color var(--dur-fast) var(--ease);
}
.h-ind:hover .h-ind__name { color: var(--c-accent-text); }

/* Capped at a readable measure rather than allowed to fill the column: on a
   1440 monitor that column is 870px, which would put a 100-character sentence
   on one line. 64ch keeps it inside the range the eye tracks comfortably. */
.h-ind__lead {
  font-size: var(--t-sm);
  line-height: 1.5;
  color: var(--c-ink-3);
  max-width: 64ch;
}

.h-ind__go {
  display: inline-flex;
  color: var(--c-ink-3);
  transition: transform var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.h-ind:hover .h-ind__go { transform: translateX(4px); color: var(--c-accent-text); }

@media (prefers-reduced-motion: reduce) {
  .h-ind:hover .h-ind__go { transform: none; }
}

@media (max-width: 860px) {
  /* The arrow goes: on a phone the whole row is the tap target and a chevron
     at the end of a wrapped two-line row points at nothing. */
  .h-ind { grid-template-columns: auto minmax(0, 1fr); align-items: start; }
  .h-ind__lead { grid-column: 2; }
  .h-ind__go { display: none; }
}

/* Stretched link: the whole card is the click target, but the accessible name
   still comes from the heading link alone — so a screen reader announces one
   link per card, not the entire card body. */
.h-cap__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
/* ---- Capability cards ---------------------------------------------------
   Tightened from the shared .c-card defaults. Six of these run two rows deep,
   and at 32px padding with 16px gaps a card spent 317px presenting three lines
   of text — more chrome than content. The case study cards keep the 32px
   default, because their image bleeds to the card edge by negating exactly
   that value.

   The icon moves onto the heading's line. It was a 40px block plus a 16px gap
   above the title purely to label a card that the title already labels, and
   removing that row is most of the height saved. */
/* Stays a flex column: the CTA is pinned to the bottom with `margin-top: auto`,
   and a grid would leave that leftover space at the end of the track instead
   of handing it to the item. */
.h-cap {
  position: relative;
  gap: var(--s-3);
  padding: var(--s-6);
}

.h-cap__top {
  display: flex;
  align-items: center;
  gap: var(--s-4);
}

/* --t-d4, the same size every other card title in the site uses. It was a
   bespoke 18px to stop "Commerce & Content Platforms" wrapping while its
   neighbours stayed on one line — but d4 has since been narrowed to 20-24, so
   the clash is gone and this no longer needs its own size. A card title that
   does wrap is fine now: the description below it reserves a fixed three-line
   block, so the CTAs stay on a shared baseline either way. */
.h-cap__name {
  font-size: var(--t-d4);
  font-weight: var(--w-bold);
  letter-spacing: var(--t-d4-ls);
  line-height: var(--lh-snug);
  text-wrap: balance;
}

/* Exactly three lines: clamped at the top so a long lead cannot set the height
   of its whole row, and floored at the bottom so a short one still reserves
   the same block. The floor is what keeps the six "Read more" links on a
   shared baseline — without it a one-line card pulled its CTA up and the grid
   read as ragged even though the cards themselves were equal height.

   3 lines is spelled out as a calc rather than `3lh` because the descriptions
   inherit line-height from .t-body, and `lh` resolves against this element's
   own computed line-height in a way that is easy to break by restyling. */
/* Four lines, not three.

   Three was right when card copy was 14px. At the 16px body size the same
   column holds about 38 characters a line instead of 44, and the longest
   capability's *opening sentence* alone is 131 characters — which vdpl_first_
   sentences() always takes, by design, because a card showing a clipped first
   sentence still beats a card showing nothing. At three lines that one card
   clipped. The floor moves with the ceiling so the CTAs still share a
   baseline. */
.h-cap__desc {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(4 * var(--lh-body) * 1em);
}

@media (max-width: 760px) {
  /* The cards become a swipe carousel here (see .l-rail), roughly 225px of
     content width — about 32 characters a line, so the same text that fills
     three lines on desktop needs four or five. Clamping it would put the
     ellipsis back. In the carousel every card is in one row and stretches to
     the tallest anyway, so letting them run their natural length costs no
     alignment. */
  .h-cap__desc {
    -webkit-line-clamp: unset;
    line-clamp: unset;
    min-height: 0;
    overflow: visible;
  }
}

.h-cap__cta { margin-top: auto; padding-top: var(--s-1); }
.h-cap:hover .c-link__arrow { transform: translateX(4px); }

/* The card is the click target, so the heading link must not draw its own
   focus ring in a different place from the outline the card receives. */
.h-cap__link:focus-visible { outline: none; }
.h-cap:has(.h-cap__link:focus-visible) {
  outline: 3px solid var(--c-accent);
  outline-offset: 3px;
}

/* ==========================================================================
   Case study cards
   ========================================================================== */

.h-case { position: relative; gap: var(--s-3); }

/* 16/9 rather than 16/10. Until the case studies have featured images
   uploaded, this box renders as an empty placeholder — at 16/10 across three
   cards that was the largest single element in the section, and the emptiest.
   The shorter ratio also matches how the screenshots will actually be
   captured. */
.h-case__shot {
  display: block;
  margin: calc(var(--s-8) * -1) calc(var(--s-8) * -1) var(--s-2);
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: var(--c-surface-2);
}
.h-case__shot img { width: 100%; height: 100%; object-fit: cover; }
.h-case__shot .c-placeholder {
  height: 100%;
  border: 0;
  border-radius: 0;
}

.h-case__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.h-case__link:focus-visible { outline: none; }
.h-case:has(.h-case__link:focus-visible) {
  outline: 3px solid var(--c-accent);
  outline-offset: 3px;
}

.h-case__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-3);
  margin-top: auto;
  padding-top: var(--s-5);
  border-top: 1px solid var(--c-line-soft);
}
.h-case__stat { display: flex; flex-direction: column-reverse; gap: 2px; }
.h-case__statVal {
  font-size: var(--t-d4);
  font-weight: var(--w-bold);
  letter-spacing: -.02em;
  line-height: 1.1;
}
.h-case__statLabel { font-size: var(--t-xs); line-height: 1.3; }

/* ==========================================================================
   Process

   Heading left, steps right on desktop. The steps are a numbered list because
   they genuinely happen in order — the numbers are content, not decoration,
   which is why they are not generated by a CSS counter.
   ========================================================================== */

.h-proc {
  display: grid;
  grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
  gap: clamp(32px, 5vw, 80px);
  align-items: start;
}
.h-proc__head { position: sticky; top: 104px; }

.h-proc__list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-8) var(--s-10);
}
.h-proc__step { display: flex; flex-direction: column; gap: var(--s-2); }

.h-proc__num {
  font-size: var(--t-sm);
  font-weight: var(--w-bold);
  letter-spacing: .04em;
  color: var(--c-accent-text);
}

@media (max-width: 1000px) {
  .h-proc { grid-template-columns: 1fr; }
  .h-proc__head { position: static; }
}
@media (max-width: 620px) {
  .h-proc__list { grid-template-columns: 1fr; gap: 0; }

  /* Single column, the steps had nothing marking where one ended and the
     next began but a gap - four blocks of text reading as one continuous
     paragraph. The top rule is the same divider .cap-steps__item already
     uses on capability pages, so a numbered-steps list reads the same way
     everywhere on the site. */
  .h-proc__step {
    padding-block: var(--s-6);
    border-top: 1px solid var(--c-line);
  }
  .h-proc__step:first-child { border-top: none; padding-top: 0; }
}
