/* ==========================================================================
   Lumos Design Studio — brand layer
   Loaded last. The template's geometry is untouched; this file only enforces
   brand-bible rules the template has no opinion about:
     · Anton for short display headings, uppercase only, never below 18px
     · Inter for sentence-length headings and all body copy
     · red as punctuation only, and never two red elements in one viewport
     · the mark is artwork — placed, never re-typeset
   ========================================================================== */

/* ---------- display headings -----------------------------------------------
   These were Anton. Sahil moved them to Inter 600 on 2026-09-09, which retires
   Anton from the page — the wordmark and hero lockup are vector artwork with the
   letterforms already outlined, so the brand's display face still shows there.
   The @font-face stays declared in fonts.css and costs nothing until something
   asks for it, so putting a heading back in Anton is a one-line change.
   -------------------------------------------------------------------------- */
.projects__title,
.services__title,
.testimonials__title,
.faq__title,
.contact__title {
  font-family: var(--font-ui);
  font-weight: 600;
  text-transform: none;
  letter-spacing: -0.035em;      /* Inter at display size wants more than the -0.02em body value */
  line-height: .95;
}

/* Sentence-length headings — same face, lighter weight. */
.why__title,
.about__title,
.team__title,
.blog__title,
.case__title { font-weight: 500; letter-spacing: var(--tracking); }

/* Counters stay in the text face: Anton has no tabular figures and the
   numerals jitter while the count animates. */
.stat__value,
.rating__score { font-family: var(--font-ui); font-weight: 700;
                 font-variant-numeric: tabular-nums; }

/* ---------- the red rule -------------------------------------------------
   Red is punctuation, never decoration. Exactly two red marks exist on this
   page and they are ~9,000px apart, so no viewport ever holds both.
   Verified programmatically, not by eye.
   -------------------------------------------------------------------------- */
.stop { color: var(--c-red); }

/* ---------- the mark -----------------------------------------------------
   Placed as artwork at a fixed height. Never re-typeset, never recoloured
   beyond the two approved lockups (ink and reverse).
   -------------------------------------------------------------------------- */
.site-header .wordmark { display: flex; align-items: center; height: 100%; }
.site-header .wordmark img { height: 13px; width: auto; }

.outro .wordmark img { height: 18px; width: auto; }

/* The mark standing in for a portrait: the studio is one person and the
   template's photo slots would otherwise need faces we do not have. */
.mark-slot {
  display: grid; place-items: center;
  width: 100%; height: 100%;
  background: var(--c-ink);
}
.mark-slot img { width: 46%; height: auto; }
.member__photo .mark-slot img { width: 38%; }

/* ---------- client wordmarks ---------------------------------------------
   Set as type rather than as logo files. We have worked on these products;
   we do not hold their marks, and reproducing a client's logo implies an
   endorsement none of them has given.
   -------------------------------------------------------------------------- */
.client-name {
  font-family: var(--font-ui);
  font-size: var(--fs-md); font-weight: 500;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--c-ink); opacity: .55; white-space: nowrap;
}

/* ---------- header ---------------------------------------------------------
   The template's header is an ink bar. The reference has it on the page ground
   with ink text and the compact L|D|S mark, which also lets the hero below
   round all four corners instead of butting into a dark strip.
   -------------------------------------------------------------------------- */
/* Flush to the top and the full width, so nothing scrolls through the gap the
   template left above and beside the bar. */
.site-header {
  inset: 0 0 auto 0;
  margin-inline: 0;
  height: 64px;
  background: var(--c-off);
  color: var(--c-ink);
  border-radius: 0;
  padding-inline: 40px;
}
.hero { margin-top: 64px; }
.site-header a { color: var(--c-ink); }
.site-header .wordmark img { height: 26px; }
.menu-toggle span { background: var(--c-ink); }

@media (max-width: 809px) { .site-header { padding-inline: 20px; } }

/* ---------- hero -----------------------------------------------------------
   The client's own footage: black ink diffusing on a light ground, played as
   shot. An earlier pass inverted it to white-on-black to keep the template's
   dark hero; the reference makes clear the ink should read as ink.

   The clip needed re-cutting rather than just un-filtering. As shot it opens on
   ~1.4s of blank light grey with nothing in frame, and by ten seconds has
   filled to a near-solid black rectangle — neither is the look. It is now the
   1.4s–7s stretch, where ink is present and the ground is still light, run
   forward then reversed so the loop returns through the same tones instead of
   cutting from black back to grey. Mean luminance holds between 42 and 133
   across the whole 11.2s loop.
   -------------------------------------------------------------------------- */
.hero {
  border-radius: var(--r-lg);
  align-content: stretch;
  grid-template-rows: auto 1fr auto;
}
.hero__media { background: var(--c-grey); }
.hero__media video { width: 100%; height: 100%; object-fit: cover; }

/* Two scrims, not one. The lower band carries the lockup and card and needs
   real weight; the middle stays clear so the hero still reads as a light image.
   ::after is the constant part. */
.hero__media::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(10,10,10,.26) 0%, rgba(10,10,10,.07) 26%, rgba(10,10,10,0) 42%),
    linear-gradient(180deg, rgba(10,10,10,0) 52%, rgba(10,10,10,.32) 72%, rgba(10,10,10,.88) 100%);
}

/* The upper band is the hard case: it carries white copy over footage that
   OPENS near-white and only darkens as the ink floods in. Measured white-on-
   substrate contrast for the lede is 2.2:1 at t=0 and still 3.2:1 at t=4 —
   under AA — but 17:1 once the frame has filled. A fixed scrim heavy enough
   for second one would read as a dark header strip for the rest of the shot.
   So it is timed to the footage: heavy while the frame is pale, gone by the
   time the ink has done the job. Duration tracks the video, which now plays
   once, and `forwards` holds the cleared state on the frozen last frame. */
.hero__media::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(10,10,10,.52) 0%, rgba(10,10,10,.34) 22%, rgba(10,10,10,0) 46%);
  animation: hero-scrim-clear 7s cubic-bezier(.33,0,.5,1) forwards;
}
@keyframes hero-scrim-clear { to { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  /* Nothing to time against, so hold a middle weight: readable over the pale
     opening frame without sitting on the filled one like a header strip. */
  .hero__media::before { animation: none; opacity: .55; }
}

/* The template's registration marks are white, which vanished the moment the
   footage stopped being uniformly dark. Difference blending keeps them legible
   against both the pale ground and the ink. */
.hero__pluses i { mix-blend-mode: difference; }

/* ---------- hero, upper band -----------------------------------------------
   Lede left, capabilities right, per the reference.
   -------------------------------------------------------------------------- */
/* The right column is the founder card's width and the padding matches
   .hero__inner's, so the capabilities list and the card share a left edge —
   measured rather than eyeballed, see the note on --hero-card-w in base.css. */
.hero__top {
  grid-row: 1;
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--hero-card-w, 360px);
  align-items: start; gap: 40px;
  padding: 44px 40px 0;          /* matches .hero__inner's padding-inline */
}
.hero__top .hero__lede {
  max-width: 560px; margin: 0;
  font-size: var(--fs-lg); font-weight: 500; line-height: 1.3;
  color: rgba(255,255,255,.72);
}
.hero__top .hero__lede strong { color: var(--c-white); font-weight: 500; }

.hero__services {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 14px;
  justify-items: start; text-align: left;   /* shared left edge, block sits right */
}
.hero__services li {
  font-size: 18px; font-weight: 600;          /* Sahil: Inter semibold, 18px */
  letter-spacing: var(--tracking); color: var(--c-white);
}

@media (max-width: 1199px) {
  .hero__top { grid-template-columns: 1fr; gap: 26px; }
}
@media (max-width: 809px) {
  .hero__top { padding: 28px 20px 0; }
  .hero__services { display: none; }   /* the capabilities section covers this below the fold */
}

/* ---------- the located-in hanger -------------------------------------------
   The tag the reference site carries, moved to the right. Theirs bleeds off the
   screen edge; Sahil's second call was to line it up instead, so the
   capabilities list, this tag and the founder card share one column — same left
   edge, same right edge, all three.

   That alignment is not three matching numbers: the width comes from
   `--hero-card-w` and the right offset from the same 40px `.hero__inner` uses,
   so the column holds if either changes.

   It lives in the hero's middle grid row — the empty band between the copy and
   the card — so "below the copy, above the card" is structural rather than a
   set of magic offsets. Radius on all four corners now: it no longer touches an
   edge, so it reads as a card in the column rather than a tab on the side.
   -------------------------------------------------------------------------- */
.hero__hanger {
  grid-row: 2;
  justify-self: end; align-self: end;
  position: relative;
  width: var(--hero-card-w);          /* the founder card's width, one variable */
  margin-right: 40px;                 /* matches .hero__inner's padding-inline */
  margin-bottom: 20px;
  padding: 14px 24px 15px 22px;
  background: rgba(255, 255, 255, .10);
  backdrop-filter: blur(16px) saturate(120%);
  -webkit-backdrop-filter: blur(16px) saturate(120%);
  border-radius: 14px;
  color: #FFFFFF;
}

/* White at 10% is the right resting treatment, but the take OPENS on a pale
   grey frame: composited from the real footage, white type on it measures
   1.42:1 for the first second and a half, then 14-16:1 once the ink has
   flooded in. Same problem the hero lede had, so the same answer — a backing
   timed to the footage rather than a fixed one, clearing on the same 7s curve.
   After that the hanger is exactly the white-10% over ink that Sahil asked for;
   this only carries the opening. */
.hero__hanger::before {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit;
  background: rgba(10, 10, 10, .55);
  animation: hanger-settle 7s cubic-bezier(.33,0,.5,1) forwards;
}
@keyframes hanger-settle { to { opacity: 0; } }

/* The backing is absolutely positioned, so it would paint over static children.
   The two lines are positioned too, which puts them back on top of it. */
.hero__hanger > * { position: relative; }

@media (prefers-reduced-motion: reduce) {
  /* Nothing to time against — hold a middle weight, as the lede scrim does. */
  .hero__hanger::before { animation: none; opacity: .55; }
}
/* Both lines are pure white per his spec, so the hierarchy is carried by size
   and weight rather than by dropping the opacity of the label. */
.hanger__label {
  margin: 0; font-size: 12px; font-weight: 500;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: #FFFFFF;
}
.hanger__place {
  margin: 4px 0 0; font-size: 20px; font-weight: 600;
  letter-spacing: -0.02em; color: #FFFFFF;
}

@media (max-width: 1199px) {
  .hero__hanger { margin-bottom: 16px; }
}
@media (max-width: 809px) {
  /* The capabilities list is hidden here, but the card is still in an `auto`
     track on the right — measured at [82..362] in a 390px window. So the column
     survives: same width, same 20px offset, and the two edges still line up. */
  .hero__hanger {
    margin: 0 20px 14px; padding: 11px 18px 12px 16px;
    max-width: calc(100% - 40px);
  }
  .hanger__place { font-size: 17px; }
}

/* ---------- hero, lower band ------------------------------------------------
   The lockup carries the block at reference scale — placed artwork at a fixed
   ratio, never re-typeset, never restretched.
   -------------------------------------------------------------------------- */
/* base.css declares three tracks — a leftover from the template's three-item
   hero. With two children the empty third track still contributes its 40px gap,
   which held the card 40px clear of the padding edge and broke the alignment
   with the band above. Two tracks, and the two right edges agree. */
.hero__inner { grid-row: 3; padding-inline: 40px; grid-template-columns: 1fr auto; }
.hero__lockup { width: clamp(190px, 25vw, 400px); height: auto; }

@media (max-width: 809px) {
  .hero__inner { padding-inline: 20px; }
  .hero__lockup { width: 58vw; margin-top: 18px; }   /* clear the lede above it */
}

/* ---------- work still ----------------------------------------------------
   The template's showreel frame. No reel exists, so the frame holds a still
   from the work and the play control is removed rather than left inert.

   The frame is `display:grid; place-items:center`, which the source's <video>
   escaped by being absolutely positioned. A plain <img> does not, so it sat
   centred at its natural size with the frame's ground showing around it —
   absolute positioning restores the full bleed.
   -------------------------------------------------------------------------- */
.showreel__still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* The caption moves off the centre of the image and into the corner, over a
   scrim, so it stays legible against the pale areas of the artwork. */
.showreel__frame { place-items: end start; }
.showreel__ui {
  justify-items: start; text-align: left;
  padding: 0 40px 40px; color: var(--c-white);
  /* The scrim below is a ::after on the frame, so it paints after this block.
     Lift the caption above it. */
  z-index: 2;
}
.showreel__frame::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 260px;
  background: linear-gradient(rgba(10,10,10,0), rgba(10,10,10,.82));
  pointer-events: none;
}
.showreel__ui .showreel__title { font-weight: 500; }
.showreel__ui .showreel__meta { color: rgba(255,255,255,.62); }

@media (max-width: 809px) { .showreel__ui { padding: 0 20px 28px; } }

/* ---------- engagement panel ---------------------------------------------
   The template's pricing table. We publish no rate card, so the panel keeps
   its layout and carries engagement models instead of figures.
   -------------------------------------------------------------------------- */
.plan__model {
  font-family: var(--font-ui);
  font-size: var(--fs-xl); font-weight: 500;
  letter-spacing: var(--tracking); margin: 0 0 6px;
}

/* ---------- footer mark --------------------------------------------------- */
.colophon { font-variant-numeric: tabular-nums; }

/* ---------- project grid --------------------------------------------------
   The template carries six cards in two columns. We have three shipped case
   studies and will not pad the grid with work that does not exist, so the row
   is a 3-up instead.

   The cards are **square** because the posters are: 1254x1254 compositions with
   type running to the edges, where any crop eats the copy. A square frame on a
   square source crops nothing at all. Height comes from aspect-ratio rather
   than a fixed px so it holds at every width. */
.project-grid { grid-template-columns: repeat(3, 1fr); }
.project-card { height: auto; aspect-ratio: 1 / 1; }
@media (max-width: 809px) { .project-grid { grid-template-columns: 1fr; } }

/* Two testimonials, both real and both attributed. The template's third slot
   is left out rather than filled with a quote nobody gave. */
.quotes { grid-template-columns: repeat(2, 1fr); }
/* Sahil asked for a white card ground. The hairline stays: on #F5F5F5 a white
   fill alone is only a 4% step and the card edge would go soft. */
.quote { background: #FFFFFF; }
@media (max-width: 1199px) { .quotes { grid-template-columns: 1fr; } }

/* One member. The grid collapses to a single card beside the aside copy
   instead of stretching one portrait across four columns. */
.team__grid { grid-template-columns: repeat(4, 1fr); }
.team__grid .member { grid-column: span 1; }


/* ---------- mobile hero ---------------------------------------------------
   The mark is centred at 34% of the hero, which clears the copy at desktop but
   not once the lede wraps to five lines on a phone. Lift it and let the hero
   take the full viewport.
   -------------------------------------------------------------------------- */
@media (max-width: 809px) {
  .hero { min-height: calc(100vh - 60px); }
  .hero__pluses { display: none; }
}


/* ---------- clients --------------------------------------------------------
   Rebuilt from Sahil's OurClients.svg, which is pure vector — the six marks are
   extracted paths, not the 220dpi raster crops they replace.

   Every measurement below is his, read straight off that file's 1200x228
   artboard: cards 185x135 on a 4px gap with a 14px radius, and each mark's
   width as a share of its card. Taking the sizes from the source beats
   re-balancing them by eye, which is what the previous pass had to do.
   -------------------------------------------------------------------------- */

.clients__head {
  display: grid;
  /* His label sits left and the date at ~25% across, not hard right where the
     template put it. 1fr/3fr lands within a pixel of the artboard. */
  grid-template-columns: 1fr 3fr;
  gap: 24px;
  font-size: var(--fs-base); font-weight: 500;
  margin-bottom: 40px;
}
.clients__label,
.clients__span { margin: 0; }
.clients__label { display: flex; align-items: center; gap: 10px; }

/* The filled plus, 18px circle with 2x8 bars — drawn rather than shipped as a
   seventh file. */
.plus {
  position: relative; flex: 0 0 18px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--c-ink);
}
.plus::before,
.plus::after { content: ""; position: absolute; background: var(--c-white); }
.plus::before { left: 8px; top: 5px; width: 2px; height: 8px; }
.plus::after  { left: 5px; top: 8px; width: 8px; height: 2px; }

.client-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px;
}
.client-card {
  display: grid; place-items: center;
  aspect-ratio: 185 / 135;               /* his card proportion */
  background: var(--c-white);
  border-radius: 14px;
}
.client-logo { display: block; height: auto; }

/* Widths as a share of the card, taken from the artboard. Equal heights would
   flatten the optical balance he set — the circular Jio mark and the wide
   NOWFLOATS wordmark are deliberately very different sizes. */
.client-logo--jio   { width: 30.7%; }
.client-logo--tata  { width: 65.3%; }
.client-logo--icici { width: 78.8%; }
.client-logo--adi   { width: 44.5%; }
.client-logo--now   { width: 76.1%; }
.client-logo--pow   { width: 45.0%; }

@media (max-width: 1199px) { .client-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 809px) {
  .client-grid { grid-template-columns: repeat(2, 1fr); }
  .clients__head { grid-template-columns: 1fr auto; gap: 16px; margin-bottom: 28px; }
}

/* ---------- testimonial portraits ------------------------------------------
   Real people, from the deck. The footer becomes a row rather than a stack.
   -------------------------------------------------------------------------- */
.quote footer { display: flex; align-items: center; gap: 14px; }
.quote footer > span { display: grid; gap: 2px; }
.quote__face {
  width: 52px; height: 52px; flex: 0 0 52px;
  border-radius: 50%; object-fit: cover;
}

/* ---------- team card ------------------------------------------------------
   A real photograph now, so the mark no longer stands in for one.
   -------------------------------------------------------------------------- */
/* ---------- the founder card ------------------------------------------------
   Rebuilt 2026-09-10 to Sahil's reference. The template's card was 360x145 —
   aspect 2.48, a flat strip with a thumbnail. His comp is 1.49, which means a
   much larger photograph, larger type, and the pill dropped to the bottom
   right with real space above it. Proportions are taken off the comp:
   the photo is 34% of the card's width at aspect 0.64, and the type block
   runs top / middle / bottom rather than bunching at the top.
   -------------------------------------------------------------------------- */
.hero .team-card {
  /* 2026-09-10: Sahil set the card to 280x180 with the frame around the
     portrait no wider than 16px. Everything below follows from those three
     numbers — the photo column is what is left of the height after the frame
     (180 - 32 = 148), and the type had to come down with it. */
  width: var(--hero-card-w);
  height: 180px;
  grid-template-columns: 96px 1fr;
  gap: 14px;
  padding: 16px;
  border-radius: 18px;
  align-items: stretch;
}
.hero .team-card__photo { aspect-ratio: auto; height: 100%; border-radius: 12px; }
.hero .team-card__body {
  padding: 0;
  display: grid; grid-template-rows: auto auto 1fr auto; gap: 0;
  align-content: stretch;
}
.hero .team-card__role { font-size: 13px; font-weight: 600; line-height: 1.15; margin: 0; }
.hero .team-card__at   { font-size: 11px; font-weight: 500; color: #6B6B6B; margin: 3px 0 0; }
.hero .team-card__name {
  font-size: 17px; font-weight: 600; line-height: 1.05;
  letter-spacing: -0.03em; color: var(--c-ink);
  align-self: center; margin: 0;
}
.hero .team-card .pill {
  justify-self: start;          /* Sahil: flush with the copy, not the card edge */
  align-self: end;
  font-size: 12px; padding: 9px 9px 9px 14px; gap: 8px;
}
.hero .team-card .pill::after { width: 6px; height: 6px; }

/* The studio portrait is pre-cropped to 1/1.28 and the card frame is 0.64, so
   cover trims a little from the top and bottom; 50% 30% keeps the eyes high. */
.team-card__photo img { object-position: 50% 30%; }


/* The template set the hero lede at 55% white against dark footage. This
   footage carries large pale passages, so the second sentence needs more
   weight to hold against them. */
.hero__lede { color: rgba(255,255,255,.74); }


/* ---------- mobile overflow -------------------------------------------------
   Two pages scrolled sideways at 390px: the home page to 727px, the JioBook
   case study to 549px. Same cause both times — a single unbreakable word set
   at display size becomes the min-content width of its grid track, the track
   refuses to shrink below it, and the document inherits that as its scroll
   width. The culprits were "Experiences." at 144px and "Consistency" at 85px.

   Fixed in two places: the tracks are told they may shrink, and the type is
   scaled so it does not need to clip when they do.
   -------------------------------------------------------------------------- */

/* A grid item defaults to min-width:auto, i.e. min-content. These tracks hold
   display type, so they must be allowed to go narrower than their longest word. */
.testimonials__head > *,
.stats__grid > *,
.impact__grid > *,
.why__stats > * { min-width: 0; }

@media (max-width: 809px) {
  /* The template scales .projects__title and .services__title down for mobile
     but never .testimonials__title, which inherits the same 144px display size. */
  .testimonials__title { font-size: 12vw; }

  /* Counters. On the JioBook case study two of the three impact figures are
     words rather than numerals, so 85px sets a 500px floor on the column. */
  .impact__v                { font-size: 42px; }
  .stats__grid .stat__value { font-size: 54px; }
  .why__stats .stat__value  { font-size: 44px; }
  .rating__score            { font-size: 38px; }

  /* 26px in a 390px column is roughly four words a line. */
  .quote p { font-size: 20px; }
  .why__title { font-size: 30px; }
}



/* ---------- the loading screen ----------------------------------------------
   Rebuilt 2026-09-10 after Sahil called the first pass bouncy. It was: the lift
   used the site's overdamped spring sampled into a 19-point `linear()` curve,
   and piecewise-linear segments across a front-loaded curve read as a stutter.

   So this time the reference was actually read rather than guessed at. Its
   loader tweens `top` and `height` on GSAP's **Power4.easeInOut**, with
   Power4.easeOut on the way in — smooth, symmetric, no spring anywhere. The
   equivalents here are cubic-beziers, and nothing is sampled.

   The other thing the reference does, which the flat slide missed entirely: the
   panel leaves with a **curved trailing edge**. Its device, copied here because
   it is the whole character of the move — an ellipse 150% wide and 750% tall
   with a 50% radius, pulled up so only its top arc shows through a strip with
   `overflow: hidden`. Growing the strip's height bulges the curve out; the
   panel then lifts away and the strip collapses with it.
   -------------------------------------------------------------------------- */
.loader {
  position: fixed; inset: 0; z-index: 9999;
  background: var(--c-ink); color: var(--c-white);
  display: grid; place-items: center;
  will-change: transform;
  animation: loader-failsafe 0s linear 6s forwards;
}

/* Every greeting occupies the same cell and crossfades. Swapping the text of a
   single node reflows it mid-fade, which is its own small jolt. */
/* Nothing paints here until the face is final. The first greeting is in the
   markup with `is-on`, so without this it renders in the fallback and re-draws
   when Inter lands — measured at 245px then 213px on a cold cache, which is
   exactly the flash. js/ui.js adds `is-typeset` as soon as the font is ready;
   the animation is the no-JS fallback so the panel is never blank for long. */
.loader__stage {
  position: relative; display: grid; place-items: center;
  opacity: 0;
  animation: loader-typeset 0s linear 1.1s forwards;
}
.loader.is-typeset .loader__stage { opacity: 1; }
@keyframes loader-typeset { to { opacity: 1; } }
.loader__row {
  grid-area: 1 / 1;
  display: flex; align-items: center; gap: .38em; white-space: nowrap;
  /* Inter only. The earlier stack listed CJK and Devanagari fallbacks for
     greetings in scripts the shipped Inter subsets do not cover — which meant
     the typeface visibly changed from word to word. The greetings are now all
     latin / latin-ext, so one face carries the whole sequence. */
  font-family: 'Inter', system-ui, sans-serif;
  font-size: clamp(34px, 6vw, 76px); font-weight: 600; letter-spacing: -0.04em;
  opacity: 0;
  /* Asymmetric on purpose. A symmetric crossfade leaves two greetings at half
     opacity on top of each other, which reads as a smear rather than a change;
     the outgoing one drops quickly and the incoming one comes up gently, and
     js/ui.js offsets the two so they barely overlap. */
  transition: opacity .12s ease;
}
.loader__row.is-on { opacity: 1; transition: opacity .2s ease; }
/* The one red on this view, and nothing else competes with it. */
.loader__row i {
  flex: none; width: .15em; height: .15em; border-radius: 50%;
  background: var(--c-red);
}

.loader__curve {
  position: absolute; left: 0; top: 100%; width: 100%; height: 0;
  overflow: hidden; will-change: height;
  transition: height .45s cubic-bezier(.165,.84,.44,1);   /* Power4.easeOut */
}
.loader__curve span {
  position: absolute; left: 50%; width: 150%; height: 750%;
  border-radius: 50%; background: var(--c-ink);
  transform: translate(-50%, -86.666%);
}
.loader.is-bulging .loader__curve { height: 9vh; }

.loader.is-leaving {
  transform: translateY(-100%);
  transition: transform 1.05s cubic-bezier(.86,0,.07,1);  /* Power4.easeInOut */
}
.loader.is-leaving .loader__curve {
  height: 0;
  transition: height 1.05s cubic-bezier(.86,0,.07,1);
}
.loader.is-leaving .loader__stage { opacity: 0; transition: opacity .32s ease; }

@keyframes loader-failsafe { to { transform: translateY(-101%); visibility: hidden; } }

html.is-loading, html.is-loading body { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  /* No entrance worth the wait if motion is unwelcome — the page is simply there. */
  .loader { display: none; }
  html.is-loading, html.is-loading body { overflow: auto; }
}

/* ---------- the confirmation toast -----------------------------------------
   Feedback for an e-mail click when the operating system has no mail client to
   hand off to. Sits above the page but below the loading screen, and is inert
   to the pointer so it can never swallow a click meant for the page.
   -------------------------------------------------------------------------- */
.toast {
  position: fixed; z-index: 200;
  left: 50%; bottom: 28px;
  transform: translate(-50%, 14px);
  padding: 13px 20px;
  background: var(--c-ink); color: var(--c-white);
  border-radius: var(--r-xl);
  font-size: var(--fs-sm); font-weight: 500; letter-spacing: var(--tracking);
  white-space: nowrap;
  box-shadow: 0 10px 30px rgba(10, 10, 10, .28);
  opacity: 0; pointer-events: none;
  transition: opacity .22s ease, transform .32s cubic-bezier(.16,1,.3,1);
}
.toast.is-up { opacity: 1; transform: translate(-50%, 0); }

@media (max-width: 809px) {
  .toast {
    bottom: 20px; max-width: calc(100vw - 40px);
    white-space: normal; text-align: center;
  }
}
@media (prefers-reduced-motion: reduce) {
  .toast { transition-duration: .01ms; }
}

/* ---------- in-page scrolling ------------------------------------------------
   Sahil asked for a scroll effect on the nav rather than a jump. `scroll-padding-top`
   is the other half of it: the header is fixed and 64px tall, so without it every
   anchor lands with its heading tucked underneath. 8px more for air.
   -------------------------------------------------------------------------- */
html {
  scroll-behavior: smooth;
  scroll-padding-top: 72px;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* ---------- the drop-panel menu is gone ------------------------------------
   It was a mobile-only ink panel under the header. The full-screen curtain
   below replaced it at every width (2026-09-09), so `.site-header nav.is-open`
   is no longer produced by the JS and its rules have been removed rather than
   left to rot. `.site-header nav` stays `display: contents` at desktop and
   `display: none` below 810px, both from base.css.
   -------------------------------------------------------------------------- */

/* ---------- the full-screen menu --------------------------------------------
   Sahil asked for the reference site's overlay. Its numbers, read off the live
   page: links are Inter 600 at 60px on a 72px line with -3.6px tracking
   (-0.06em), ink on #F5F5F5 — the same ground as our header, so the two read as
   one curtain rather than a panel sitting on a bar. The curtain stops short of
   the bottom of the window, leaving a sliver of the page visible underneath;
   the reference opens to ~83vh and so does this.

   The header keeps a higher stacking order than the curtain so the wordmark and
   the X stay live while it is open.
   -------------------------------------------------------------------------- */
.site-header { z-index: 100; }

.site-menu {
  position: fixed; inset: 0 0 auto 0; z-index: 90;
  height: min(83vh, 940px);
  background: var(--c-off); color: var(--c-ink);
  display: grid; grid-template-rows: 1fr auto;
  padding: 112px 40px 36px;
  transform: translateY(-101%);
  visibility: hidden;
  transition: transform .72s cubic-bezier(.16,1,.3,1), visibility 0s linear .72s;
}
.nav-open .site-menu {
  transform: none; visibility: visible;
  transition: transform .72s cubic-bezier(.16,1,.3,1), visibility 0s;
}

.site-menu__nav {
  display: grid; align-content: center; justify-items: center;
  gap: 0;                                  /* rows butt together, as in the reference */
}
.site-menu__nav a {
  font-size: clamp(34px, 5.4vw, 60px);
  font-weight: 600; line-height: 1.2;      /* 72/60 */
  letter-spacing: -0.06em;
  color: var(--c-ink);
  /* each link rides in behind the curtain, one after another */
  transform: translateY(90px); opacity: 0;
  transition: transform .9s cubic-bezier(.16,1,.3,1) calc(var(--i) * 60ms + 120ms),
              opacity   .5s ease                     calc(var(--i) * 60ms + 120ms),
              color .25s ease;
  /* the same overdamped spring the rest of the site settles on
     (stiffness 121, damping 27, mass 0.3), sampled into a linear() curve.
     Browsers without linear() keep the cubic-bezier above. */
  transition-timing-function:
    linear(0, .2286, .4381, .5911, .7026, .7841, .8434, .8868, .9184, .9414,
           .9583, .9705, .9795, .986, .9908, .9943, .9968, .9987, 1),
    ease, ease;
}
.nav-open .site-menu__nav a { transform: none; opacity: 1; }
.site-menu__nav a:hover { color: var(--c-muted); }

.site-menu__foot {
  display: grid; grid-template-columns: 1fr auto; gap: 32px; align-items: end;
  border-top: 1px solid var(--c-rule); padding-top: 26px;
  opacity: 0; transition: opacity .5s ease .34s;
}
.nav-open .site-menu__foot { opacity: 1; }

.site-menu__mail { margin: 0; }
.site-menu__mail a {
  font-size: clamp(20px, 2.4vw, 30px); font-weight: 600;
  letter-spacing: -0.035em; color: var(--c-ink);
  border-bottom: 1.5px solid var(--c-ink); padding-bottom: 4px;
}
.site-menu__work {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 6px; justify-items: end;
  font-size: var(--fs-sm); font-weight: 500;
}
.site-menu__work a { color: #6B6B6B; }
.site-menu__work a:hover { color: var(--c-ink); }

/* the two bars cross into an X while the curtain is down */
.menu-toggle span { transition: transform .25s ease, opacity .25s ease; }
.nav-open .menu-toggle span:first-child { transform: translateY(6px) rotate(45deg); }
.nav-open .menu-toggle span:last-child  { transform: translateY(-6px) rotate(-45deg); }

/* the page behind must not scroll while the curtain is down */
.nav-open, .nav-open body { overflow: hidden; }

/* With the curtain down, the header carries only the wordmark and the X — the
   inline nav would otherwise sit on top of the same links set 60px high. It is
   `display: contents`, so the anchors are hidden individually; `visibility`
   also takes them out of the tab order, which the focus trap depends on. */
.site-header nav a { transition: opacity .3s ease; }
.nav-open .site-header nav a { opacity: 0; visibility: hidden; }

/* The toggle is now the primary navigation control at every width, but its
   artwork is only 34x14. Pad the hit area out to 44px and pull the margin back
   by the same amount, so the target is comfortable and nothing moves. */
.menu-toggle { padding: 15px 5px; margin: -15px -5px; box-sizing: content-box; }

@media (max-width: 809px) {
  .site-menu { height: min(88vh, 900px); padding: 96px 20px 28px; }
  .site-menu__foot { grid-template-columns: 1fr; gap: 22px; align-items: start; }
  .site-menu__work { justify-items: start; }

  /* Tap targets. At 34px the nav rows come out ~41px on the reference's 1.2
     line, just under the 44px floor, and the project links are 17px. Open the
     leading rather than padding the rows, so the block keeps its even rhythm. */
  .site-menu__nav a { line-height: 1.45; }
  .site-menu__work a,
  .site-menu__mail a { display: inline-flex; align-items: center; min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  .site-menu, .site-menu__nav a, .site-menu__foot { transition-duration: .01ms; }
  .site-menu__nav a { transition-delay: 0s; }
}

/* ---------- form status -----------------------------------------------------
   The form talks back now instead of silently reloading the page.

   The obvious move is a red error message, but this sits in the same panel as
   the closing statement's red period — the one sanctioned red on the page — and
   the bible allows only one red element per view. So the error is carried by
   weight and wording rather than colour. That also satisfies the accessibility
   rule against signalling state by colour alone: the message says what is wrong.
   -------------------------------------------------------------------------- */
.form__status {
  margin: 14px 0 0;
  font-size: var(--fs-sm); font-weight: 500;
  color: var(--c-muted-local);
  min-height: 1.2em;
}
.form__status[data-kind="ok"]    { color: currentColor; opacity: .85; }
.form__status[data-kind="error"] { color: currentColor; font-weight: 600; opacity: 1; }


/* ---------- why choose us ---------------------------------------------------
   Rebuilt to Sahil's reference: label and two-tone heading across the top, then
   a portrait beside a lede and a bento of figures.

   The reference's numbers are the template's invented ones (50+, 95%). These are
   the two real attributed figures the section already carried.
   -------------------------------------------------------------------------- */
.why__head { display: grid; grid-template-columns: 300px 1fr; gap: 40px; align-items: start; }
.why__label { display: flex; align-items: center; gap: 10px; font-size: var(--fs-base); font-weight: 500; margin: 0; }
.why__title {
  margin: 0; max-width: 900px;
  font-size: var(--fs-h2); font-weight: 600; line-height: 1.06;
  letter-spacing: -0.03em;
}
.why__title span { color: var(--c-muted-local); }

.why__body {
  display: grid; grid-template-columns: 360px 1fr; gap: 64px;
  margin-top: 96px; align-items: start;
}
.why__figure { margin: 0; border-radius: var(--r-md); overflow: hidden; aspect-ratio: 360 / 620; }
/* The desk photograph is 941x1672 (0.563) against a 0.581 box, so on desktop
   it is very nearly the whole frame. The mobile box is landscape (16/11) and
   takes a band out of a tall image: 26% down is the one that keeps his head
   and the screen both in shot. */
.why__figure img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 34%; }

.why__panel { display: grid; gap: 56px; }
.why__lede {
  margin: 0; max-width: 620px; justify-self: end;
  font-size: var(--fs-lg); font-weight: 500; line-height: 1.35;
  color: var(--c-muted-local);
}
.why__lede strong { color: var(--c-ink); font-weight: 600; }

.why__bento {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
  max-width: 620px; justify-self: end;
}
.bento { background: var(--c-white); border-radius: var(--r-md); padding: 26px; }
.bento--fig { display: flex; align-items: flex-start; justify-content: space-between; min-height: 132px; }
.bento__value {
  font-size: 54px; font-weight: 600; line-height: 1;
  letter-spacing: -0.04em; font-variant-numeric: tabular-nums;
}
.bento__index { font-size: 10px; font-weight: 500; color: var(--c-muted-local); }
.bento__label { margin: 0 0 8px; font-size: var(--fs-md); font-weight: 600; }
.bento__text  { margin: 0; font-size: var(--fs-sm); font-weight: 500; color: #6B6B6B; }
.bento--tall  { grid-row: span 2; display: grid; align-content: start; }
.bento--wide  { grid-column: 2; grid-row: 1; }
.bento--fig:first-child { grid-column: 1; grid-row: 2; }
.bento--note:nth-child(2) { grid-column: 1; grid-row: 3; }
.bento--tall  { grid-column: 2; grid-row: 2 / span 2; }

.bento__marks { list-style: none; display: flex; align-items: center; gap: 22px; margin: auto 0 0; padding: 26px 0 0; }
.bento__marks img { height: 15px; width: auto; opacity: .5; }
.bento__marks li:first-child img { height: 26px; }

@media (max-width: 1199px) {
  .why__head, .why__body { grid-template-columns: 1fr; gap: 26px; }
  .why__body { margin-top: 56px; }
  .why__figure { aspect-ratio: 16 / 11; }
  .why__figure img { object-position: 50% 26%; }
  .why__lede, .why__bento { justify-self: stretch; max-width: none; }
}
@media (max-width: 809px) {
  .why__bento { grid-template-columns: 1fr; }
  .bento--fig, .bento--note, .bento--tall, .bento--wide { grid-column: 1; grid-row: auto; }
  .why__title { font-size: 30px; }
}


/* ---------- FAQ ------------------------------------------------------------
   Sahil's reference: the title and lede sit in a left column, the questions as
   white cards on the right, each with a filled plus. The first one opens by
   default so the pattern is legible without a click.
   -------------------------------------------------------------------------- */
.faq__grid { display: grid; grid-template-columns: 1fr 1.35fr; gap: 80px; align-items: start; }
.faq__head { display: block; }
.faq__title { margin: 0; font-size: clamp(64px, 9vw, 132px); }
.faq__lede  { margin: 26px 0 0; max-width: 380px; font-size: var(--fs-md);
              font-weight: 500; color: #6B6B6B; }

.faq__list { margin: 0; border-top: 0; display: grid; gap: 14px; }
.faq__item {
  background: var(--c-white); border-radius: var(--r-md);
  border-bottom: 0; padding: 4px 30px;
}
.faq__item summary {
  padding: 26px 0; gap: 28px; align-items: center;
  font-size: var(--fs-lg); font-weight: 500; letter-spacing: var(--tracking);
}
.faq__item summary::-webkit-details-marker { display: none; }

/* sections-2.css draws its own "+" as summary::after. The card now carries a
   real filled button, so the text marker has to go or every row shows two. */
.faq__item summary::after { content: none; }

/* the question fills the row so the button is pushed to the right edge */
.faq__item summary > span { flex: 1 1 auto; }

/* the filled plus, rotating into a minus when the card is open */
.faq__plus {
  position: relative; flex: 0 0 26px;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--c-ink);
}
.faq__plus::before,
.faq__plus::after {
  content: ""; position: absolute; background: var(--c-white);
  transition: transform .25s ease, opacity .25s ease;
}
.faq__plus::before { left: 12px; top: 7px;  width: 2px; height: 12px; }
.faq__plus::after  { left: 7px;  top: 12px; width: 12px; height: 2px; }
.faq__item[open] .faq__plus::before { transform: rotate(90deg); opacity: 0; }

.faq__item p { margin: 0; padding: 0 0 28px; max-width: 62ch;
               font-size: var(--fs-sm); font-weight: 500; color: #6B6B6B; }

@media (max-width: 1199px) { .faq__grid { grid-template-columns: 1fr; gap: 40px; } }
@media (max-width: 809px)  {
  .faq__item { padding-inline: 20px; }
  .faq__item summary { font-size: var(--fs-md); gap: 16px; }
}

/* ---------- contact --------------------------------------------------------
   The reference pairs the heading with a form card. Sahil asked for the address
   alone, so the address becomes the thing you land on — set large, as a link,
   where the form used to be.
   -------------------------------------------------------------------------- */
.contact { position: relative; overflow: hidden; padding: 104px 40px 112px; }
.contact__media { position: absolute; inset: 0; }
.contact__media video { width: 100%; height: 100%; object-fit: cover; opacity: .5; }
.contact__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(10,10,10,.72), rgba(10,10,10,.92));
}
.contact__inner { position: relative; }

.contact__head { max-width: 760px; }
.contact__title { margin: 0; font-size: clamp(56px, 8.6vw, 128px); }
.contact__lede {
  margin: 22px 0 0; max-width: 520px;
  font-size: var(--fs-lg); font-weight: 500; color: rgba(255,255,255,.66);
}

.contact__mailto { margin: 56px 0 0; }
.contact__mailto a {
  display: inline-block;
  font-size: clamp(24px, 3.4vw, 46px); font-weight: 500;
  letter-spacing: -0.03em; color: var(--c-white);
  border-bottom: 1px solid rgba(255,255,255,.3);
  padding-bottom: 6px;
  transition: border-color .3s ease;
}
.contact__mailto a:hover { border-bottom-color: var(--c-white); }

.contact__points {
  display: grid; grid-template-columns: repeat(2, minmax(0, 320px));
  gap: 40px; margin-top: 72px;
  border-top: 1px solid rgba(255,255,255,.16); padding-top: 34px;
}
.point__title { margin: 0 0 10px; font-size: var(--fs-md); font-weight: 600; }
.point__text  { margin: 0; font-size: var(--fs-sm); font-weight: 500; color: rgba(255,255,255,.6); }

/* The founder card was removed from Let's Talk on 2026-09-09 at Sahil's
   request, so the section ends on the two points. `.team-card` still styles
   the hero card; only the contact-specific override is gone. */

@media (max-width: 809px) {
  .contact { padding: 64px 20px 72px; }
  .contact__points { grid-template-columns: 1fr; gap: 26px; margin-top: 48px; }
  .contact__mailto a { border-bottom-width: 1px; word-break: break-word; }
}
