/* ==========================================================================
   Wealth Farming Advisory — site stylesheet
   ==========================================================================

   Tokens are lifted verbatim from brand/index.html, which extracted them from
   the client's physical business card. Do not eyeball new values — if a colour,
   spacing step or radius isn't here, it isn't in the system.

   Fonts are locked (confirmed 2026-08-07): Stardos Stencil display,
   Grand Hotel script, Poppins body.

   The logo is ICON ONLY. "Wealth Farming Advisory" is typeset, not artwork —
   the wordmark IS the type, which is why the display font is load-bearing.
   ========================================================================== */

:root {
  /* ---- palette: teal ---- */
  --teal-deepest: #000A10;
  --teal-core: #013F41;
  --teal-highlight: #2E7A76;

  /* ---- palette: gold ---- */
  --gold-shadow: #8E4719;
  --gold-core: #D1AC66;
  --gold-highlight: #FDD781;
  --tan-bar: #C4A666;

  /* ---- neutrals ---- */
  --paper: #F6F3EC;
  --ink: #14201F;

  /* ---- the locked metal recipe. For surfaces carrying NO text. ---- */
  --metal: linear-gradient(100deg, #8E4719 0%, #D1AC66 32%, #FDD781 50%, #D1AC66 68%, #8E4719 100%);
  /* Lit variant — same ramp, bronze ends dropped. For gold surfaces that carry
     dark text, or gold clipped INTO text. This is a measurement, not taste:
     ink #1A1204 on bronze #8E4719 is 2.7:1 and fails AA. */
  --metal-lit: linear-gradient(100deg, #D1AC66 0%, #FDD781 50%, #D1AC66 100%);

  /* ---- background ---- */
  /* The top stop was #2E7A76 and the visible top of the viewport measured
     rgb(29,100,98). Against that ground --gold-core is 3.21:1 and --on-teal-2
     is 4.44:1 — both below AA, on a background-attachment:fixed glow, so it is
     not just the hero: anything scrolled into the top band of the viewport sits
     on it. Measured from a real screenshot, not computed from the token.
     Darkened until gold text clears 4.5:1 at the lightest point anyone sees. */
  --bg-glow: radial-gradient(120% 140% at 50% -10%, #0B5250 0%, #013F41 40%, #000A10 100%);

  /* ---- surfaces on the teal ---- */
  --surf-recessed: rgba(0, 10, 16, .42);
  /* Recessed is a fill AND a shadow. The fill alone reads as a darker patch;
     the inset is what makes it read as cut INTO the surface. Documented as
     "rgba(0,10,16,.42) + inset shadow" since the first brand reference, but
     only the slider rail ever actually applied it. */
  --well: inset 0 3px 7px rgba(0, 10, 16, .62), inset 0 1px 1px rgba(0, 10, 16, .5), inset 0 -1px 0 rgba(255, 255, 255, .07);
  --surf-resting: rgba(255, 255, 255, .045);
  --surf-raised: rgba(255, 255, 255, .085);
  --edge-quiet: rgba(255, 255, 255, .10);
  --edge-gold: rgba(209, 172, 102, .28);
  --edge-gold-strong: rgba(209, 172, 102, .60);

  /* ---- text on teal ---- */
  --on-teal-1: #FFFFFF;
  --on-teal-2: rgba(255, 255, 255, .72);
  /* .62, not .50. At .50 this tops out at 5.32:1 even on pure black, so it
     could not clear AA anywhere on the glow — and it was carrying the three
     standing regulatory warnings, the footer legal band, award issuers and
     frame captions. Measured against the lightest ground actually on screen
     (rgb(7,75,74), sampled from a screenshot) .62 gives 4.87:1 with headroom,
     and still reads a clear step below --on-teal-2. */
  --on-teal-3: rgba(255, 255, 255, .62);
  --ink-on-gold: #1A1204;

  /* ---- functional signal only. Never decorative, never with white text. ---- */
  /* #FF8266 measured 4.08:1 against the lightest ground on screen — below AA
     for the one message a form has that the user MUST read. Lifted until it
     clears with headroom (4.63:1 lit, 9.33:1 deep). Still the only non-gold
     colour in the system, still functional-only. */
  --error: #FF9480;

  /* ---- spacing: 4px base. Only these values. ---- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
  --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px;

  /* ---- radius ---- */
  --r-xs: 4px; --r-sm: 8px; --r-md: 14px; --r-lg: 22px; --r-pill: 999px;

  /* ---- type ---- */
  --display: 'Stardos Stencil', Georgia, serif;
  --script: 'Grand Hotel', cursive;
  --body: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* ---- layout ---- */
  --wrap: 1140px;
  --nav-h: 64px;
}

/* ==========================================================================
   Reset / base
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--on-teal-1);
  background: var(--teal-deepest);
  background-image: var(--bg-glow);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg { max-width: 100%; height: auto; display: block; }

a { color: var(--gold-core); text-decoration: none; }
a:hover { color: var(--gold-highlight); }

/* Display headings are stencil CAPS, per brand/index.html .wf-h2. The tracking
   is POSITIVE: Stardos Stencil is built from broken letterforms, and negative
   tracking closes the breaks up until the face stops reading as a stencil. */
h1, h2, h3 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.12;
  margin: 0 0 var(--sp-4);
  text-transform: uppercase;
}

h1 { font-size: clamp(32px, 7vw, 60px); letter-spacing: .025em; }
h2 { font-size: clamp(26px, 4.5vw, 40px); letter-spacing: .025em; }
h3 { font-size: clamp(19px, 2.6vw, 24px); letter-spacing: .02em; }

p { margin: 0 0 var(--sp-4); color: var(--on-teal-2); }

.lede { font-size: 16px; color: var(--on-teal-2); max-width: 54ch; }

/* The sticky nav would otherwise eat the top of every anchored section. */
[id] { scroll-margin-top: calc(var(--nav-h) + 16px); }

:focus-visible {
  outline: 3px solid var(--gold-highlight);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--sp-5); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
[hidden] { display: none !important; }

/* CLIPPED TO NOTHING, NOT PARKED ABOVE THE PAGE.
   This used to sit at top:-100px and slide down on focus. That is the common
   recipe and it has a visible failure on Apple hardware: rubber-band overscroll
   exposes the area ABOVE the document, so dragging the page down revealed a
   gold "Skip to content" button floating over the browser chrome. Sambit hit it
   on desktop Safari; the same gesture does it on every iPhone.
   The fix is to give it no off-screen position to be dragged into view — a 1×1
   clipped box inside the document, exactly the .sr-only technique, which
   expands to its real size only on focus. Keyboard behaviour is unchanged and
   the element stays in the accessibility tree, which a display:none version
   would not. */
.skip-link {
  position: absolute; left: var(--sp-4); top: var(--sp-4); z-index: 200;
  background: var(--gold-core); color: var(--ink-on-gold);
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-sm); font-weight: 600;
  /* padding zeroed at rest too, or border-box leaves a 32×24 invisible box
     sitting over the nav that still takes clicks. pointer-events belt and
     braces on the same point. */
  width: 1px; height: 1px; padding: 0; overflow: hidden; pointer-events: none;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.skip-link:focus {
  width: auto; height: auto; padding: var(--sp-3) var(--sp-4);
  overflow: visible; pointer-events: auto;
  clip: auto; clip-path: none;
}

/* ==========================================================================
   Brand lockup — icon + typeset wordmark
   ========================================================================== */

.lockup { display: inline-flex; align-items: center; gap: var(--sp-3); }
/* In the bar the lockup IS the home link, and with the icon dropped on phones
   it collapsed to the height of its own 12.5px text — a 226x14 tap target for
   the most-tapped link on the site. The wordmark stays exactly the size it was;
   only the hit area grows. */
.nav a.lockup { min-height: 44px; }
/* Size by HEIGHT, never a square box. The mark's viewBox is 773x898 (0.861),
   so forcing it into 38x38 letterboxes it to 32.7px wide and leaves 2.6px of
   dead space on each side — which makes the gap to the wordmark read as ~14.6px
   instead of the --sp-3 token. Optical alignment beats a round number here. */
.lockup__icon { height: 38px; width: auto; aspect-ratio: 773 / 898; flex: 0 0 auto; color: var(--gold-core); }

/* External-file variant, for pages that do not inline the <symbol> (the
   /start/* stubs). An <img src="logo.svg"> CANNOT receive currentColor — the
   SVG renders as its own document and resolves currentColor to black, which is
   near-invisible on the teal nav. A CSS mask paints `background` through the
   artwork's alpha instead, so the mark stays fully themeable while the SVG
   stays an external, cacheable file rather than 15 KB inlined seven times. */
.lockup__icon--file {
  display: block;
  background: currentColor;
  -webkit-mask: url(/brand/assets/logo.svg) center / contain no-repeat;
  mask: url(/brand/assets/logo.svg) center / contain no-repeat;
}
/* One line, all stencil, wide tracking. The script face (Grand Hotel) is
   deliberately NOT used here: at ~13px a flowing script mushes, and stacking
   stencil + script + a line break puts three typographic ideas in a 38px box.
   The script belongs in the hero, where it has room to sing. */
.lockup__word {
  display: block;
  font-family: var(--display);
  font-size: 19px;
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: .14em;
  white-space: nowrap;
  color: var(--on-teal-1);
  /* letter-spacing adds trailing space after the final glyph; claw it back so
     the lockup's right edge is optically flush. */
  margin-right: -.14em;
}
.lockup__word em { font-style: normal; color: var(--gold-core); }

/* 23 characters on one line is wide, and it now shares the bar with a burger.
   Step the size and tracking down rather than wrapping, so the wordmark keeps
   its single-line silhouette all the way to 360px. */
@media (max-width: 900px) {
  .lockup__word { font-size: 17px; letter-spacing: .12em; margin-right: -.12em; }
}
@media (max-width: 560px) {
  .lockup__icon { height: 32px; }
  .lockup__word { font-size: 14px; letter-spacing: .09em; margin-right: -.09em; }
}
@media (max-width: 380px) {
  .lockup__icon { height: 28px; }
  .lockup__word { font-size: 12.5px; letter-spacing: .06em; margin-right: -.06em; }
}

/* ==========================================================================
   Navigation — sticky, "Start Now" always reachable
   ========================================================================== */

.nav {
  position: sticky; top: 0; z-index: 100;
  min-height: var(--nav-h);
  display: flex; align-items: center;
  background: rgba(0, 10, 16, .78);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--edge-quiet);
}
.nav__inner { display: flex; align-items: center; gap: var(--sp-5); width: 100%; }
.nav__links { display: flex; align-items: center; gap: var(--sp-5); margin-left: auto; }
.nav__link {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  color: var(--on-teal-2); font-size: 15px; font-weight: 500; white-space: nowrap;
}
.nav__link:hover, .nav__link[aria-current="page"] { color: var(--on-teal-1); }
/* Secondary errand, not an invitation — "Check status" is for a returning
   visitor and must not compete with the one metal button in the bar. */
.nav__link--quiet { font-size: 14px; color: var(--on-teal-3); }
.nav__link--quiet:hover { color: var(--on-teal-1); }
.nav__cta { margin-left: var(--sp-2); }

.nav__toggle {
  display: none; margin-left: auto; background: none; border: 0; cursor: pointer;
  padding: var(--sp-2); color: var(--on-teal-1);
  min-width: 44px; min-height: 44px;         /* was 40x40 — under the thumb minimum */
}
.nav__toggle svg { width: 24px; height: 24px; }
/* The burger becomes an X when the menu is open. Both glyphs ship; CSS picks
   one, so there is no icon swap to get wrong in JavaScript. */
.nav__x { display: none; }
.nav__toggle[aria-expanded="true"] .nav__burger { display: none; }
.nav__toggle[aria-expanded="true"] .nav__x { display: block; }

/* ---- dropdown groups ------------------------------------------------------
   <details> so a submenu opens with zero JavaScript, is keyboard- and
   screen-reader-correct without ARIA gymnastics, and keeps every sub-link in
   the DOM for crawlers even while collapsed. The shared name= attribute makes
   the groups an exclusive accordion. */
/* The panels resolve against .nav__links, NOT against their own trigger. A
   panel anchored to its trigger has no way to know how much room is left before
   the viewport edge, so the three-column Services panel overflowed to the right
   at every width below ~1300px. Hanging every panel from the right edge of the
   bar makes overflow structurally impossible. */
.nav__links { position: relative; }
.nav__group { position: static; }
.nav__summary { cursor: pointer; list-style: none; }
.nav__summary::-webkit-details-marker { display: none; }
.nav__summary[data-within="true"] { color: var(--on-teal-1); }
.nav__chev { width: 15px; height: 15px; transition: transform .2s ease; opacity: .7; }
.nav__group[open] .nav__chev { transform: rotate(180deg); }
.nav__group[open] > .nav__summary { color: var(--on-teal-1); }

.nav__panel {
  position: absolute; top: calc(100% + var(--sp-4)); z-index: 90;
  /* THE SAFE DEFAULT, and the no-JavaScript fallback: hang from the right edge
     of the bar, where overflow is structurally impossible at any width.
     assets/chrome.js then moves each panel under its OWN trigger and clamps it
     to the viewport. That split is deliberate — a panel anchored to its trigger
     cannot know in CSS how much room is left before the screen ends, so the
     stylesheet takes the option that can never crop and the script takes the
     one that looks right. */
  right: 0; left: auto;
  min-width: 260px;
  max-width: min(calc(100vw - var(--sp-6)), 1040px);
  /* Solid, not translucent. At .97 alpha a 60px stencil H1 still ghosted
     through the panel and made the link list hard to read. */
  background: var(--teal-deepest);
  background-image: linear-gradient(180deg, rgba(46,122,118,.10), rgba(46,122,118,0) 60%);
  border: 1px solid var(--edge-gold);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .55);
}
/* width:max-content is load-bearing. An absolutely-positioned panel is
   shrink-to-fit, and a shrink-to-fit box containing a WRAPPING flex container
   resolves its width to the widest single item — which collapsed the three
   Services columns into one tall stack. max-content asks for the unwrapped row;
   max-width:100% then lets it wrap only when the panel's own cap forces it. */
.nav__cols {
  display: flex; flex-wrap: wrap; gap: var(--sp-5) var(--sp-7);
  width: max-content; max-width: 100%; padding: 0;
}
.nav__col { min-width: 180px; }
.nav__col-head {
  display: block; margin-bottom: var(--sp-3); padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--edge-quiet);
  font-family: var(--display); font-size: 14px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--gold-core);
}
a.nav__col-head:hover { color: var(--gold-highlight); }
/* Insurance / Loans / Investments are group LABELS — there is no branch
   page behind them, so they must not offer the hover a link offers. */
.nav__col-head--label { cursor: default; }
.nav__panel ul { list-style: none; margin: 0; padding: 0; }
.nav__panel li { margin: 0; }
.nav__panel li a {
  display: block; padding: var(--sp-2) 0;
  font-size: 14.5px; color: var(--on-teal-2); white-space: nowrap;
}
.nav__panel li a:hover, .nav__panel li a[aria-current="page"] { color: var(--on-teal-1); }
.nav__badge {
  margin-left: var(--sp-2); padding: 1px var(--sp-2);
  border-radius: var(--r-pill); border: 1px solid var(--edge-gold);
  font-size: 11.5px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--gold-core); vertical-align: 1px;
}

/* 1000px, not the usual 900. Measured, not guessed: the lockup is a
   23-character stencil wordmark — it IS the logo, so it cannot shrink to an
   icon to buy bar space — and it alone runs 328px. Bar + links need 891px, and
   the wrap only offers that much at ~940px viewport. 1000px leaves ~60px of
   slack so a font fallback or a longer label cannot tip it into a wrap. */
@keyframes nav-fade-out { to { opacity: 0; } }

@media (max-width: 1000px) {
  .nav__toggle { display: block; }
  .nav__links {
    position: fixed; inset: var(--nav-h) 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--teal-deepest);
    border-bottom: 1px solid var(--edge-gold);
    padding: var(--sp-3) var(--sp-5) var(--sp-6);
    margin-left: 0;
    /* With submenus the menu can exceed the viewport, so it scrolls inside
       itself. Without this the last links and the CTA are simply unreachable. */
    max-height: calc(100dvh - var(--nav-h)); overflow-y: auto; overscroll-behavior: contain;
    /* A FADE AT THE BOTTOM EDGE, so a truncated list reads as truncated.
       The drawer has scrolled since submenus existed, but nothing said so: the
       last visible link simply ended at the screen edge, which looks identical
       to a list that stops there. With sixteen calculators under three headings
       the panel is ~1030px inside an ~780px drawer, so that guess is now wrong
       more often than right.

       A sticky pseudo-element rather than a background gradient, because
       backgrounds paint BEHIND text and the point is to fade the text itself.
       It is inert to pointers so it cannot eat a tap on the link beneath it. */
    transform: translateY(-120%);
    /* VISIBILITY, NOT JUST TRANSFORM. Sliding the drawer off-screen hides it
       from the eye and from nobody else: closed, it still reported
       visibility:visible with 41 focusable links at tabIndex 0. A keyboard user
       tabbing off the logo fell into the whole menu invisibly, and a screen
       reader announced every link on every page.
       visibility:hidden removes it from both the tab order and the
       accessibility tree. The 0s delay is what keeps the slide-out animating —
       without it the drawer vanishes instantly instead of sliding away. */
    visibility: hidden;
    transition: transform .22s ease, visibility 0s linear .22s;
  }
  .nav__links[data-open="true"] {
    transform: translateY(0);
    visibility: visible;
    transition: transform .22s ease, visibility 0s;
  }
  .nav__link { padding: var(--sp-4) 0; border-bottom: 1px solid var(--edge-quiet); font-size: 17px; }
  .nav__summary { display: flex; align-items: center; justify-content: space-between; }
  .nav__chev { width: 18px; height: 18px; }
  /* The column heads are real links to the overview pages, not captions — they
     were 31px, which is a menu control below the thumb minimum. Desktop keeps
     31px; a mouse does not need 44. */
  .nav__links::after {
    content: '';
    position: sticky; bottom: calc(var(--sp-6) * -1); z-index: 2;
    flex: 0 0 auto; height: 56px; margin-top: calc(56px * -1);
    pointer-events: none;
    background: linear-gradient(to bottom, transparent, var(--teal-deepest) 85%);
  }
  /* Where scroll-driven animation is supported the fade retires at the end of
     the scroll, so it only ever means "there is more". Everywhere else it stays
     put, which is a cosmetic imperfection rather than a wrong signal. */
  @supports (animation-timeline: scroll()) {
    .nav__links::after {
      animation: nav-fade-out linear both;
      animation-timeline: scroll(self block);
      animation-range: contain 85% contain 100%;
    }
  }

  .nav__col-head { display: flex; align-items: center; min-height: 44px; }

  /* Inline accordion, not a floating panel: on a phone the panel IS the menu. */
  .nav__panel {
    position: static; transform: none; min-width: 0;
    background: none; backdrop-filter: none; -webkit-backdrop-filter: none;
    border: 0; border-radius: 0; box-shadow: none;
    padding: var(--sp-2) 0 var(--sp-4);
  }
  /* .nav__cols also carries .wrap, and .wrap sets margin-inline:auto. On
     desktop the panel is wider than the columns need, so that resolves to
     roughly nothing. On a phone the panel is 342px and width:max-content makes
     the column 243px — so the auto margins split the 99px remainder and CENTRE
     the submenu, indenting it ~50px from a "Firm" heading that sits hard left.
     Both halves of .wrap have to be neutralised here, not just the margin. */
  .nav__cols { flex-direction: column; gap: var(--sp-5); width: 100%; margin-inline: 0; padding-inline: 0; }
  .nav__col { min-width: 0; }
  .nav__panel li a { padding: var(--sp-3) 0 var(--sp-3) var(--sp-4); white-space: normal; font-size: 16px; }
  .nav__link--quiet { font-size: 16px; }
  .nav__cta { margin: var(--sp-5) 0 0; }
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font-family: var(--body); font-size: 16px; font-weight: 600;
  padding: var(--sp-3) var(--sp-5);
  min-height: 46px;              /* thumb target — seminar audience is on phones */
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  cursor: pointer; text-align: center;
  transition: transform .12s ease, filter .18s ease;
  touch-action: manipulation;
}
.btn:active { transform: translateY(1px); }

/* Gold surfaces carrying dark text use --metal-lit, not --metal. See token note. */
.btn--primary { background: var(--metal-lit); color: var(--ink-on-gold); box-shadow: 0 6px 20px rgba(0,0,0,.28); }
.btn--primary:hover { filter: brightness(1.06); color: var(--ink-on-gold); }

.btn--ghost { background: var(--surf-resting); color: var(--on-teal-1); border-color: var(--edge-gold); }
.btn--ghost:hover { background: var(--surf-raised); color: var(--on-teal-1); }

.btn--block { width: 100%; }

/* Tertiary. For something that is not really an action — "Compare all plans",
   "More about investments". A third BUTTON would compete with the primary;
   a link admits it is a link. */
.btn--link {
  min-height: 44px; padding: var(--sp-2) 0; border: 0; background: none;
  color: var(--gold-core); font-weight: 600; gap: var(--sp-2);
}
.btn--link:hover { color: var(--gold-highlight); }

/* Disabled is a state we should almost never need — a control that cannot be
   used is usually a control that should not be rendered. Where a form genuinely
   must show one, aria-disabled is preferred over the attribute so the control
   stays focusable and a screen reader can still find and explain it. */
.btn:disabled, .btn[aria-disabled="true"] {
  filter: grayscale(.7); opacity: .55; cursor: not-allowed; box-shadow: none;
}
.btn:disabled:hover, .btn[aria-disabled="true"]:hover { filter: grayscale(.7); transform: none; }

/* THE FOCUS RING ON BUTTONS IS NOT THE GLOBAL ONE, DELIBERATELY.
   The global :focus-visible ring is --gold-highlight. .btn--primary is filled
   with --metal-lit, whose midpoint stop IS --gold-highlight — so the global
   ring was drawn in the button's own fill colour and disappeared against the
   middle of the most important control on the site. That is a 1.4.11 failure
   on the one element a keyboard user most needs to find.

   White ring, gold halo: the white clears 3:1 against both the gold fill and
   the teal ground, and the halo keeps it recognisably ours. */
.btn:focus-visible {
  outline: 3px solid var(--on-teal-1);
  outline-offset: 2px;
  box-shadow: 0 0 0 7px rgba(209, 172, 102, .45);
}

/* ==========================================================================
   FORM FIELD — the text input family

   The next build phase is a multi-step onboarding form with document upload,
   so getting one field right now is worth forty later. The field is a RECESSED
   well on the teal: label above, control, helper below, error replacing the
   helper rather than appearing under it — two lines of small print under one
   input is how people miss the one that matters.

   Focus here is GOLD, not the button's white: 2px gold against the dark
   recessed fill clears the 3:1 non-text requirement comfortably, and on a form
   the ring is surrounded by teal rather than gold, so there is no clash.
   ========================================================================== */
.ff { display: block; margin-bottom: var(--sp-5); }
.ff__label {
  display: block; margin-bottom: var(--sp-2);
  font-size: 14px; font-weight: 600; color: var(--on-teal-1);
}
/* The asterisk is decorative — "required" is carried by the required attribute
   and announced from there. A screen reader should not read "star". */
.ff__req { color: var(--gold-core); margin-left: 2px; }
.ff__input {
  display: block; width: 100%;
  min-height: 48px; padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  border: 1px solid var(--edge-quiet);
  background: var(--surf-recessed); box-shadow: var(--well);
  color: var(--on-teal-1);
  font-family: var(--body); font-size: 16px;   /* 16px — anything less zooms iOS on focus */
  transition: border-color .18s ease, box-shadow .18s ease;
}
.ff__input::placeholder { color: var(--on-teal-3); }
.ff__input:hover { border-color: var(--edge-gold); }
.ff__input:focus-visible {
  outline: none;
  border-color: var(--gold-core);
  box-shadow: 0 0 0 3px rgba(209, 172, 102, .30);
}
.ff__help { margin: var(--sp-2) 0 0; font-size: 13px; line-height: 1.5; color: var(--on-teal-3); }
.ff__err { margin: var(--sp-2) 0 0; font-size: 13px; line-height: 1.5; color: var(--error); font-weight: 500; }
/* Colour is never the only signal — the border changes AND the message names
   the problem in words. */
.ff--invalid .ff__input { border-color: var(--error); }
.ff--invalid .ff__input:focus-visible { box-shadow: 0 0 0 3px rgba(255, 130, 102, .30); }
.ff__input:disabled { opacity: .5; cursor: not-allowed; }

/* Upload well — the KYC case. A dashed edge reads as "drop here" without any
   JavaScript having to say so. */
.ff__drop {
  display: grid; place-items: center; gap: var(--sp-2);
  min-height: 120px; padding: var(--sp-5);
  border: 1px dashed var(--edge-gold); border-radius: var(--r-md);
  background: var(--surf-recessed); box-shadow: var(--well);
  color: var(--on-teal-2); text-align: center;
}
.ff__drop b { color: var(--on-teal-1); font-weight: 600; }
.ff__drop span { font-size: 13px; color: var(--on-teal-3); }

/* ==========================================================================
   Sections, cards, proof
   ========================================================================== */

.section { padding: var(--sp-9) 0; }
.section--tight { padding: var(--sp-7) 0; }

/* Band rhythm is 96px desktop / 48px mobile. 96px of air above and below every
   band on a 640px-tall handset costs a third of the viewport per section. */
@media (max-width: 720px) {
  .section { padding: var(--sp-7) 0; }
  .section--tight { padding: var(--sp-6) 0; }
}

.section-head { margin-bottom: var(--sp-7); }
.section-head h2 { margin-bottom: var(--sp-3); }
.section-head .lede { margin-bottom: 0; }

/* The SCRIPT eyebrow — Grand Hotel, used where the brand wants warmth. */
.eyebrow {
  font-family: var(--script);
  font-size: 22px;
  color: var(--gold-core);
  margin: 0 0 var(--sp-2);
}

/* MOTIF 03 (lead-in variant) — the UPPERCASE section eyebrow. Distinct from
   .eyebrow above: this one is a structural label, not a flourish, and it is the
   one that carries the gold seam lead-in rule. */
.kicker {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: 12px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--gold-highlight); margin: 0 0 var(--sp-3);
}
.kicker::before {
  content: ""; display: block; width: 44px; height: 2px; flex: none;
  background: linear-gradient(90deg, var(--gold-highlight), var(--gold-core) 45%, rgba(142,71,25,0));
}

.card {
  background: var(--surf-resting);
  border: 1px solid var(--edge-quiet);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
}
.card:hover { border-color: var(--edge-gold); background: var(--surf-raised); }

.grid { display: grid; gap: var(--sp-5); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

/* Inline proof — trust sits next to CTAs, never in an About backwater. */
.proof {
  display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); align-items: center;
  /* Resets its own list styling. It is usually a <ul>, and every page that used
     it was carrying style="list-style:none;margin:0;padding:0" to compensate —
     a workaround repeated per page is a missing rule here. */
  list-style: none; margin: 0; padding: 0;
}
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-pill);
  background: var(--surf-recessed);
  border: 1px solid var(--edge-gold);
  font-size: 13px; font-weight: 500; color: var(--on-teal-2);
}
/* At 360-390px a default chip runs ~165-200px, so three proof chips cost three
   rows and push the first real section most of a screen further down. Tightened
   just enough to pair them up. Proof chips are reassurance, not headlines. */
@media (max-width: 480px) {
  .proof { gap: var(--sp-2); }
  .proof .chip { font-size: 12px; padding: var(--sp-1) var(--sp-3); }
}

/* FLEX, NOT GRID, and the reason is worth keeping. The row was
   repeat(3, 1fr) — correct until the Connector figure came off and left two
   stats sitting in a three-column grid with a hole where the third used to be.
   repeat(auto-fit, minmax(200px, 1fr)) was the obvious fix and it does not
   work: measured, it produced tracks of 384px 384px 0px, collapsing the empty
   track without letting the survivors expand, so the row still ended two-thirds
   across with dead space after it.
   Flex has no phantom tracks. The items divide whatever is there, at any count,
   and nobody has to remember to update a column number when a stat is added or
   removed. */
.stats { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-6); }
.stats > * { flex: 1 1 100%; }
@media (min-width: 640px) { .stats > * { flex: 1 1 200px; } }

.stat {
  background: var(--surf-recessed); border: 1px solid rgba(255,255,255,.07);
  border-radius: var(--r-md); padding: var(--sp-5);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05);
  display: flex; flex-direction: column;
}
.stat__value {
  font-family: var(--display); font-size: clamp(30px, 5vw, 44px); font-weight: 700; line-height: 1;
  background: var(--metal-lit); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: var(--gold-core);
}
.stat__label { font-size: 14px; color: var(--on-teal-3); margin-top: var(--sp-2); }

/* ==========================================================================
   Motifs — the brand's decorative vocabulary, numbered as in brand/index.html.
   All of these animate transform/opacity ONLY, so they stay on the compositor:
   no layout, no paint. That matters on the mid-range Android these visitors are
   actually holding.
   ========================================================================== */

/* ---- MOTIF 02 — Furrow Field. ONE PER VIEWPORT. Two fields on one screen read
        as interference, not depth. It belongs in a page's hero. ---- */
.furrow { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
/* Each ring runs TWO independent animations:
     drift — the slow breathing scale/rise
     swell — an opacity pulse, phase-offset per ring so brightness travels
             outward from the centre. That outward travel is the point: it reads
             as growth radiating, which is the one place the farming metaphor is
             allowed to live at this scale.
   Irregular diameters (not an even series) stop it reading as a bullseye. */
.furrow i {
  position: absolute; left: 50%; top: 100%; width: var(--d); aspect-ratio: 1;
  border: 1px solid var(--ring, rgba(46,122,118,.45)); border-radius: 50%;
  transform: translate(-50%,-50%);
  animation:
    drift var(--dur, 30s) ease-in-out infinite alternate,
    swell var(--pulse, 14s) ease-in-out infinite;
  animation-delay: var(--delay, 0s), var(--delay, 0s);
}
.furrow i:nth-child(1) { --d:  620px; --dur: 30s; --delay:  0s;   --ring: rgba(46,122,118,.46); }
.furrow i:nth-child(2) { --d:  940px; --dur: 37s; --delay: -2.6s; --ring: rgba(46,122,118,.34); }
/* One ring picks up gold. It is the only warm note in the field, and it sits
   third so the eye finds it after the teal rather than instead of it. */
.furrow i:nth-child(3) { --d: 1260px; --dur: 43s; --delay: -5.2s; --ring: rgba(209,172,102,.20); }
.furrow i:nth-child(4) { --d: 1660px; --dur: 49s; --delay: -7.8s; --ring: rgba(46,122,118,.18); }
.furrow i:nth-child(5) { --d: 2120px; --dur: 56s; --delay: -10.4s; --ring: rgba(46,122,118,.11); }

@keyframes drift {
  from { transform: translate(-50%,-50%) scale(1); }
  to   { transform: translate(-50%,-53%) scale(1.045); }
}
/* Shallow on purpose: the field should feel alive, not blink. */
@keyframes swell {
  0%, 100% { opacity: .72; }
  50%      { opacity: 1; }
}

/* ---- MOTIF 03 — Seam Rule ---- */
.seam {
  border: 0; height: 2px; width: 180px; max-width: 60%; margin: var(--sp-5) 0;
  background: linear-gradient(90deg,
    transparent 0%, rgba(142,71,25,.7) 10%, var(--gold-core) 34%,
    var(--gold-highlight) 50%, var(--gold-core) 66%, rgba(142,71,25,.7) 90%, transparent 100%);
}

/* ---- MOTIF 04 — Corner Anchor. Bottom-right = the reading-exit corner. ---- */
.anchor { position: relative; overflow: hidden; }
/* Inset by --sp-4, NOT flush to the corner. Flush is what it used to be, and it
   was wrong on every card that carries it: the L is built from two straight
   bars, the cards are --r-lg (22px) rounded with overflow:hidden, so the arc
   sliced BOTH arms and they never met. At 8x it read as a rendering fault
   rather than an accent — 28 elements across .offer, .award, .pick, .card and
   .callout. 16px clears a 22px arc with room to spare (the corner point sits
   8.5px from the arc centre, well inside it), so the bracket now closes.
   Found in the 2026-08-10 design critique. */
.anchor::before, .anchor::after {
  content: ""; position: absolute; bottom: var(--sp-4); right: var(--sp-4); pointer-events: none;
  transition: width .32s cubic-bezier(.2,.7,.3,1), height .32s cubic-bezier(.2,.7,.3,1);
}
.anchor::before { width: 96px; height: 3px; background: linear-gradient(90deg, rgba(142,71,25,0), var(--gold-core) 55%, var(--gold-highlight)); }
.anchor::after  { width: 3px; height: 52px; background: linear-gradient(0deg, var(--gold-highlight), var(--gold-core) 45%, rgba(142,71,25,0)); }
.anchor:hover::before { width: 132px; }
.anchor:hover::after  { height: 70px; }

/* ---- MOTIF 05 — Coin Stack. Renders COMPLETE with no JS; the .go class only
        replays it as an entrance, so a failed script costs an animation, not
        the content. ---- */
.stack { display: flex; flex-direction: column; gap: 5px; margin-top: auto; padding-top: var(--sp-4); }
.stack span { display: block; height: 4px; border-radius: 2px; background: var(--metal-lit); transform-origin: left center; }
.stack span:nth-child(1) { width: 34%; }
.stack span:nth-child(2) { width: 54%; }
.stack span:nth-child(3) { width: 76%; }
.stack span:nth-child(4) { width: 100%; }
.stack.go span { animation: build .7s cubic-bezier(.2,.8,.25,1) both; }
.stack.go span:nth-child(1) { animation-delay: .30s; }
.stack.go span:nth-child(2) { animation-delay: .20s; }
.stack.go span:nth-child(3) { animation-delay: .10s; }
@keyframes build { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ---- MOTIF 06 — Sprout Tick, used as every list bullet on the site ---- */
.tick { width: 12px; height: 12px; flex: none; color: var(--gold-core); margin-top: 6px; }
.chip .tick { margin-top: 0; }

/* ---- MOTIF 07 — Harvest Bar. A two-part ratio, shown.

   The Coin Stack states growth as an IDEA — four fixed bars that mean "up".
   Nothing in the system could state a MEASURED share, so every calculator that
   produces one prints it as prose and buries it: /calculators/goal-sip/ already
   emits the row "Growth as a share of the total — 62%" between four rupee
   figures, where it is the most persuasive line on the page and the least
   visible. That row is the reason this exists.

   Deliberately NOT a chart. A donut or an axis implies a precision a
   30-year projection does not have, and it would be the first foreign object
   in a system with no chart vocabulary. A single filled track says "roughly
   this much of that" and claims nothing more.

   Deliberately NOT transitioned. The calculators recompute while a thumb is
   still dragging the slider; a width transition would leave the bar trailing
   the figure it is explaining, and a bar that disagrees with the number beside
   it is worse than no bar. Static IS the finished state — the width is an
   inline --share, so it renders complete with CSS animation unavailable, with
   JS blocked, and in a print stylesheet. ---- */
.harvest { display: block; }
.harvest__track {
  position: relative; height: 10px; border-radius: var(--r-pill); overflow: hidden;
  /* Recessed is a fill AND the inset. Without --well this is a flat darker
     stripe and the fill looks painted on top of the page rather than sitting
     in a cut track. */
  background: var(--surf-recessed); box-shadow: var(--well);
}
/* The metal is anchored to the RIGHT, and --share is the share of the ANSWER,
   not of the base. This is Corner logic applied to a bar: accent belongs at the
   reading-exit end, where it pushes the eye onward. It also keeps the one rule —
   gold marks the figure that matters. Left-anchored, the gold landed on "you
   would contribute" while the sentence was about growth, which is the accent
   marking the wrong half of its own explanation.

   Every use reads the same way round: the base sits in the recess on the left,
   the increment lands in metal on the right. Contributed / growth added. Cover
   held / additional cover needed. Principal / interest. The right-hand segment
   is always the answer. */
.harvest__fill {
  position: absolute; right: 0; top: 0; bottom: 0; width: var(--share, 50%);
  border-radius: var(--r-pill); background: var(--metal-lit);
}
/* The legend is the accessible copy of the bar: the bar itself is aria-hidden,
   so the two figures below are what a screen reader — and anyone who cannot
   separate gold from teal — actually receives. Colour is never the only signal. */
.harvest__legend {
  display: flex; justify-content: space-between; gap: var(--sp-4);
  margin-top: var(--sp-3); font-size: 13px; line-height: 1.4; color: var(--on-teal-3);
}
.harvest__legend > * { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.harvest__legend > :last-child { text-align: right; }
.harvest__legend b { font-size: 15px; font-weight: 600; color: var(--on-teal-2); }
/* The answer's figure is clipped out of the same ramp as the metal segment, on
   the same side as it, which is how you know which figure the gold belongs to.
   Same idiom as .stat__value and .offer__n — no new device, and no colour key
   floating beside the bar. */
.harvest__legend > :last-child b {
  background: var(--metal-lit); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: var(--gold-core);
}

/* ---- MOTIF 08 — Attest Rule. Words that are not ours.

   The system had no way to say "somebody else said this". A client review, a
   press line, a paragraph of prescribed wording — all of them landed in
   .callout, which is the component for a claim the PRACTICE is making. On a
   licensed distributor's site that is not a styling slip: a testimonial dressed
   as a callout reads as a promise by the firm, and prescribed wording
   paraphrased into house voice stops being the prescribed wording.

   Form: the Corner Anchor's vertical arm, moved to the reading-ENTRY edge and
   flipped so the taper is bright where the sentence starts. The anchor marks
   the exit corner and pushes toward the CTA; this marks the entry edge and
   holds you still. Same taper, same 3px, opposite end of the read.

   Use .fence, not this, for a standing warning — a warning attaches to a claim
   and has no speaker. This has a speaker, and the speaker must be named. ---- */
.attest { position: relative; max-width: 62ch; margin: var(--sp-6) 0; padding-left: var(--sp-5); }
.attest::before {
  content: ""; position: absolute; left: 0; top: 3px; bottom: 3px; width: 3px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--gold-highlight), var(--gold-core) 42%, rgba(142,71,25,0));
}
.attest p { margin: 0 0 var(--sp-3); font-size: 16.5px; line-height: 1.65; color: var(--on-teal-1); }
.attest p:last-of-type { margin-bottom: var(--sp-3); }
.attest__by { display: block; font-size: 13px; line-height: 1.5; font-style: normal; color: var(--on-teal-3); }
.attest__by b { font-weight: 600; color: var(--on-teal-2); }
/* The guard, and the reason this motif is worth having at all.

   An unattributed quotation is the exact failure this device exists to prevent,
   and it is invisible in review because it looks FINE — it just looks like the
   firm talking. So the missing attribution is made loud instead: the rule turns
   --error and the block names its own fault on the page. This is a lint rule
   with no build step behind it, which is what a no-build-step site can have.

   --error carries it because this is a functional signal, never decoration.
   Where :has() is unsupported the guard silently does not fire; that costs a
   warning, not a page. */
.attest:not(:has(.attest__by))::before { background: var(--error); }
.attest:not(:has(.attest__by))::after {
  content: "Unattributed — name the source, or do not quote it.";
  display: block; margin-top: var(--sp-3);
  font-size: 13px; font-weight: 600; letter-spacing: .02em; color: var(--error);
}

/* ---- MOTIF 09 — Fallow Plot. The honest hole.

   Half this site is waiting on the client: award certificates, the founder
   photograph, partner logo files, the Times of India clipping, the disclosures
   wording. Five missing-content states are documented and every one of them was
   solved separately — a monogram plate here, typeset names there, a stub note
   somewhere else. Three treatments for one problem is how a system starts
   drifting, and the fourth hole gets filled with something invented, which on a
   regulated site is the actual danger.

   So: one device that says "this is empty on purpose, and here is what belongs
   in it". Ploughed ground with nothing planted — the Furrow Field flattened
   from arcs into parallel lines, because a fallow plot has the furrows and not
   the crop. The hatch runs at 100deg, the same angle as the --metal ramp, so
   an empty plot is lit from the same direction as everything around it.

   Not aria-hidden and not pointer-events:none: unlike the rest of the family
   this one carries text, and the text is the entire point. A grey silhouette or
   a blurred placeholder would be a lie told in a lighter colour. ---- */
.fallow {
  display: grid; place-items: center; align-content: center; gap: var(--sp-2);
  min-height: 148px; padding: var(--sp-6); text-align: center;
  /* Dashed, not solid. A solid hairline is what a real card has; the broken one
     is the first thing that reads as "nothing here yet" from across a room. */
  border: 1px dashed var(--edge-quiet); border-radius: var(--r-lg);
  /* --surf-resting, not --edge-quiet, and 20px apart rather than 16. Drawn at
     edge strength the furrows ran straight through the 13px sentence and made
     the one thing anybody has to read the hardest thing in the box — on a
     mid-range phone held outdoors, by someone over fifty. A texture that
     competes with its own label is worse than a plain empty box. */
  background: repeating-linear-gradient(100deg,
    var(--surf-resting) 0 1px, transparent 1px 20px);
}
.fallow b { font-size: 14px; font-weight: 600; letter-spacing: .01em; color: var(--on-teal-2); }
/* The floor for this text is the disclosure floor, 13px, for the same reason:
   the sentence saying what is missing is the sentence somebody has to act on. */
.fallow span { max-width: 44ch; font-size: 13px; line-height: 1.55; color: var(--on-teal-3); }

/* ---- MOTIF 10 — Fence Line. Where our claim ends.

   The single most important thing this brand has to say about itself is that it
   is a distributor and a broker — not the insurer, not the lender, not the asset
   manager — "without ever sounding like it is dodging it". Nothing in the system
   carried that. The three standing warnings (market risk, subject matter of
   solicitation, lender discretion) ship as .footnote: 13px, --on-teal-3, no
   device at all, which makes the legally load-bearing sentence the quietest
   thing on the page. .footnote is a text style; this is the mark that says the
   text above it is the edge of what we are claiming.

   A fence, not a rule: the field boundary is where our ground stops and the
   provider's begins, which is precisely the sentence underneath it.

   --tan-bar, and this is its first use in the site — the token has been declared
   and documented as "flat gold for bars and dividers that must not shimmer"
   since the system was written, waiting for exactly this. The Seam Rule runs the
   full metal ramp because it is celebrating a transition. A warning must not
   glint; it is not the next thing to look at, it is the limit on the last thing.

   The mask fades in fast and out slow, so the line runs OFF to the right rather
   than resolving into a centred ornament. A boundary that stops tidily in the
   middle of the column reads as decoration, and this must never read as
   decoration. ---- */
.fence { position: relative; margin-top: var(--sp-5); padding-top: var(--sp-4); }
.fence::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  pointer-events: none;
  background: repeating-linear-gradient(90deg, var(--tan-bar) 0 9px, transparent 9px 18px);
  /* The #000 below is a mask ALPHA STENCIL, not a colour — nothing black is
     painted. The only colour this rule renders is --tan-bar above. Noted
     because a bare hex in this stylesheet should always make somebody look. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 10%, #000 58%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, #000 10%, #000 58%, transparent 100%);
}
.fence p { max-width: 72ch; margin: 0; font-size: 13px; line-height: 1.6; color: var(--on-teal-3); }
.fence p + p { margin-top: var(--sp-2); }
/* The clause naming who actually carries the risk is the one part that has to
   survive a skim, so it steps up one level of text — not to gold, which would
   claim it is the next thing to act on. */
.fence b { font-weight: 600; color: var(--on-teal-2); }

/* ==========================================================================
   Content-page furniture — the shapes every service and trust page is built
   from. Rendered by scripts/build-pages.mjs; nothing here is page-specific.
   ========================================================================== */

/* ---- page hero. Shorter than the homepage's: this visitor already chose a
        branch, so the job is to confirm they are in the right place and get
        out of the way, not to sell the firm again. ---- */
.page-hero { position: relative; overflow: hidden; padding: var(--sp-8) 0 var(--sp-7); }
.page-hero__inner { position: relative; z-index: 1; }
.page-hero h1 { margin-bottom: var(--sp-3); max-width: 18ch; }
.page-hero .eyebrow { font-size: clamp(19px, 3.4vw, 26px); margin-bottom: var(--sp-4); }
.page-hero .lede { font-size: 16.5px; margin-bottom: var(--sp-6); max-width: 58ch; }
.page-hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-5); }
@media (max-width: 520px) { .page-hero__actions .btn { width: 100%; } }
@media (max-width: 720px) { .page-hero { padding: var(--sp-6) 0 var(--sp-6); } }

/* ---- numbered offering cards. The number is a wayfinding device — it makes a
        long list feel finite rather than endless — so it is aria-hidden and the
        heading carries the meaning. ---- */
.offer-grid { display: grid; gap: var(--sp-5); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.offer {
  position: relative;
  background: var(--surf-resting);
  border: 1px solid var(--edge-quiet);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
}
.offer:hover { border-color: var(--edge-gold); background: var(--surf-raised); }
.offer__n {
  display: block; margin-bottom: var(--sp-3);
  font-family: var(--display); font-size: 13px; font-weight: 700; letter-spacing: .22em;
  background: var(--metal-lit); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: var(--gold-core);
}
.offer h3 { font-size: 21px; margin-bottom: var(--sp-3); }
.offer p { font-size: 15px; margin-bottom: var(--sp-4); }
.offer p:last-child { margin-bottom: 0; }

/* ---- tick lists. The sprout tick is the site's only bullet. ---- */
.ticks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.ticks li { display: flex; gap: var(--sp-2); font-size: 14px; line-height: 1.5; color: var(--on-teal-2); }

/* ---- numbered process. A real <ol>, so the order is in the markup and not
        only in the styling. ---- */
.steps { list-style: none; counter-reset: step; margin: 0; padding: 0;
  display: grid; gap: var(--sp-5); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.step { counter-increment: step; position: relative; padding-top: var(--sp-6); }
.step::before {
  content: counter(step, decimal-leading-zero);
  position: absolute; top: 0; left: 0;
  font-family: var(--display); font-size: 13px; font-weight: 700; letter-spacing: .22em;
  color: var(--gold-core);
}
.step::after {
  content: ""; position: absolute; top: 26px; left: 0; width: 28px; height: 2px;
  background: linear-gradient(90deg, var(--gold-core), rgba(142,71,25,0));
}
.step h3 { font-size: 19px; margin-bottom: var(--sp-2); }
.step p { font-size: 14.5px; margin: 0; }

/* ---- callout. Used for the paragraphs that exist to be believed rather than
        skimmed — the misselling note, the market-risk note. ---- */
.callout {
  background: var(--surf-recessed);
  border: 1px solid var(--edge-gold);
  border-radius: var(--r-lg);
  padding: var(--sp-7) var(--sp-6);
}
.callout h2 { font-size: clamp(23px, 3.4vw, 32px); margin-bottom: var(--sp-4); }
.callout p { max-width: 68ch; font-size: 15.5px; }
.callout p:last-of-type { margin-bottom: 0; }
.callout .btn { margin-top: var(--sp-5); }
@media (max-width: 480px) { .callout { padding: var(--sp-6) var(--sp-5); } }

/* ---- founder plate ---- */
.founder { display: grid; gap: var(--sp-6); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 860px) { .founder { grid-template-columns: 260px 1fr; gap: var(--sp-7); } }
/* TODO(client): a real photograph of Nikhil (4:5, >=560px wide) replaces this.
   Until one exists it is a deliberately obvious monogram plate — a placeholder
   that admits to being one, rather than a grey silhouette pretending. */
.portrait {
  max-width: 260px; aspect-ratio: 4 / 5; border-radius: var(--r-lg);
  background: var(--surf-recessed); border: 1px solid var(--edge-gold);
  display: grid; place-items: center; position: relative; overflow: hidden;
}
/* object-fit: cover, and the framing biased UP. The plate is 4:5 and the
   photograph is 7:9, so something has to be cropped; taking it off the bottom
   of the jacket rather than the top of the head is the only acceptable
   direction. */
.portrait img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 22%; display: block; }
.portrait b {
  font-family: var(--display); font-weight: 700; font-size: 64px; letter-spacing: .04em;
  background: var(--metal-lit); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: var(--gold-core);
}
.founder__name { margin-bottom: var(--sp-1); }
.founder__role { font-size: 14px; letter-spacing: .12em; text-transform: uppercase; color: var(--gold-core); margin-bottom: var(--sp-5); }
.founder p { max-width: 66ch; }

/* Registration numbers are the whole point of the block — monospace so a digit
   can be read off and checked without transcription errors. */
.creds { list-style: none; margin: var(--sp-5) 0 0; padding: 0; max-width: 260px; }
.creds li {
  padding: var(--sp-2) 0; border-top: 1px solid var(--edge-quiet);
  font-size: 12.5px; line-height: 1.5; color: var(--on-teal-2);
  font-variant-numeric: tabular-nums;
}

/* ---- forces / sector chips ----------------------------------------------
   A quieter chip than .chip: no gold hairline and no Sprout Tick, because this
   is an enumeration of organisations rather than a credential, and a row of
   nine gold-edged pills would out-shout the heading above them.

   PROMOTED FROM index.html's inline <style> on 2026-08-11. It had lived there
   while /for-soldiers/ and /corporate/ both render .forces — so on those two
   pages the list had always fallen back to a raw disc-bulleted <ul>. That is
   the failure mode a page-local style has the moment a second page uses the
   class, and it had been live the whole time. */
.forces { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-5) 0; padding: 0; list-style: none; }
.forces li {
  font-size: 12.5px; font-weight: 500; color: var(--on-teal-2);
  padding: var(--sp-1) var(--sp-3); border: 1px solid var(--edge-quiet); border-radius: var(--r-pill);
}

/* ---- awards ---- */
.awards { display: grid; gap: var(--sp-5); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.award {
  background: var(--surf-recessed); border: 1px solid var(--edge-gold);
  border-radius: var(--r-lg); padding: var(--sp-6);
}
/* The certificate slot. Sits below the attribution because the words are what
   a skim reads and the document is what a sceptic opens. min-height keeps the
   grid even whether a card holds a frame or an empty plot — a ragged row of
   awards reads as a ragged practice. */
.award__cert { margin-top: var(--sp-5); min-height: 148px; }
/* 1:1, not 4:3. The crossfade in one card holds a landscape stage photograph,
   a portrait trophy and a portrait composite, and `contain` never crops — so
   the box aspect only decides how much mat is wasted. A square is the least
   bad compromise across mixed evidence, and it stops a portrait certificate
   being letterboxed down to a stamp. */
.award__cert.frame .frame__box { aspect-ratio: 1 / 1; }

.award__year {
  font-family: var(--display); font-size: 13px; font-weight: 700; letter-spacing: .22em;
  color: var(--gold-core); margin: 0 0 var(--sp-3);
}
.award h3 { font-size: 19px; margin-bottom: var(--sp-3); }
.award__by { font-size: 13px; color: var(--on-teal-3); margin: 0; }

.prose p { max-width: 68ch; }
.section-cta { margin-top: var(--sp-7); }
.footnote { margin-top: var(--sp-5); font-size: 13px; line-height: 1.6; color: var(--on-teal-3); max-width: 72ch; }
.closer__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; margin-bottom: var(--sp-6); }
@media (max-width: 520px) { .closer__actions .btn { width: 100%; } }

/* ==========================================================================
   Partner panel — who sits behind the desk. Recurs on every service page:
   it is the trust signal a seminar attendee actually checks.
   ========================================================================== */

.partners { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }
@media (min-width: 620px) { .partners { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 920px) { .partners { grid-template-columns: repeat(6, 1fr); } }
/* TODO(client): partner logo files not supplied. Typeset names are the honest
   placeholder — a greyed box would read as a broken image. */
.partner {
  display: grid; place-items: center; text-align: center; min-height: 76px;
  padding: var(--sp-3); border: 1px solid var(--edge-quiet); border-radius: var(--r-md);
  background: var(--surf-recessed);
  font-size: 13px; font-weight: 600; letter-spacing: .01em; color: var(--on-teal-2);
}

/* ==========================================================================
   FAQ — every service page carries one, with matching FAQPage JSON-LD.
   Keep the DOM and the schema byte-identical: schema without visible content
   is a Google structured-data policy violation, not a shortcut.
   ========================================================================== */

/* ==========================================================================
   FAQ — a disclosure list, built on <details>
   ==========================================================================

   <details>/<summary>, NOT a div with role="button". The browser gives keyboard
   operation, the expanded/collapsed state, and the screen-reader semantics for
   free, and it all works before a line of JavaScript loads — which matters on a
   site with no build step and an audience on weak connections. It is also the
   idiom the mobile nav already uses, so the site has one disclosure pattern
   rather than two.

   EXCLUSIVE, via <details name="...">. One answer open at a time; opening
   another closes the previous one, which the browser does natively — the same
   attribute the mobile nav uses, and no JavaScript on either.

   I argued the other way first, on the grounds that an FAQ is reference
   material and someone might want two answers open to compare them. Sambit's
   call, and on a list this long he is right: six or seven questions all open at
   once turns a scannable index back into the wall of text this component
   replaced. The tradeoff to know about is that closing an item ABOVE the one
   you just opened shifts the page under you; that is inherent to the pattern
   rather than to this implementation.

   Browsers without support for the attribute simply allow several open, which
   is the old behaviour and not a broken one.

   SINGLE COLUMN, and it used to be two above 860px. With static text that was
   fine. With disclosures it is not: opening an item in the left column reflows
   the right one, and the thing you were reading jumps.

   THE ROW IS FULL WIDTH; THE ANSWER IS NOT. Capping the whole list at a reading
   measure left the rules and the plus signs stopping two-thirds across a wide
   screen, with the section heading running past them — it read as a column that
   had failed to fill rather than as a deliberate measure. The row now spans the
   wrap so the hairlines line up with everything else on the page, and only
   .faq__a carries the 65–75 character limit, which is where line length
   actually matters.

   COLLAPSED CONTENT IS STILL IN THE DOM, which is what keeps the FAQPage
   JSON-LD honest — Google requires the structured data to match the visible
   page, and explicitly permits FAQ answers behind an accordion. Hiding these
   with display:none in a script would have broken that.
   ========================================================================== */

.faq { display: grid; gap: 0; }

.faq__item { border-top: 1px solid var(--edge-quiet); }
.faq__item:last-child { border-bottom: 1px solid var(--edge-quiet); }

.faq__q {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-5);
  /* 44px minimum, and the padding is what gets it there — the question is a
     tap target on a phone before it is a heading. */
  min-height: 44px; padding: var(--sp-4) 0;
  cursor: pointer; list-style: none;
  transition: color .18s ease;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: var(--gold-highlight); }
.faq__item[open] > .faq__q { color: var(--on-teal-1); }

/* Questions stay in BODY type, sentence case — the one deliberate exception to
   the stencil-caps heading rule. Stencil caps at 17px is a display face doing a
   paragraph's job: it costs comprehension, and this block exists to be read
   (and quoted by answer engines). */
.q {
  font-family: var(--body); font-size: 16.5px; font-weight: 600;
  text-transform: none; letter-spacing: 0; line-height: 1.45;
  color: inherit; margin: 0;
}

/* THE MARKER IS DRAWN IN currentColor, deliberately.
   Its sibling project shipped a plus/minus whose colour was set independently
   of the circle behind it; when a light theme arrived the two resolved to the
   same value and an open row showed nothing at all. Inheriting the colour makes
   that class of bug impossible here — the glyph is whatever the text is, so it
   contrasts wherever the text does.
   Two bars, the vertical one rotated away on open: a plus becoming a minus. */
.faq__mark {
  position: relative; flex: none; width: 20px; height: 20px; margin-top: 2px;
}
.faq__mark::before, .faq__mark::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  background: currentColor; border-radius: 1px;
  transform: translate(-50%, -50%);
  transition: transform .2s ease-out, opacity .2s ease-out;
}
.faq__mark::before { width: 14px; height: 2px; }
.faq__mark::after { width: 2px; height: 14px; }
/* The vertical bar turns and fades rather than simply vanishing — one moving
   part, 200ms, ease-out, which is the whole animation budget this deserves. */
.faq__item[open] .faq__mark::after { transform: translate(-50%, -50%) rotate(90deg); opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .faq__mark::before, .faq__mark::after { transition: none; }
}

.faq__a { padding: 0 0 var(--sp-5); max-width: 68ch; }
.faq__a p { font-size: 14.5px; line-height: 1.7; margin: 0 0 var(--sp-3); }
.faq__a p:last-child { margin-bottom: 0; }
.faq__a a { border-bottom: 1px solid var(--edge-gold); }

/* ==========================================================================
   Closing CTA band — placement 3 of the five locked CTA positions.
   ========================================================================== */

.closer { text-align: center; }
.closer .seam { margin-inline: auto; }
.closer h2 { margin-bottom: var(--sp-5); }

/* ==========================================================================
   Placeholder-form banner
   Visible ONLY while a CTA still points at a /start/* stub. Driven by the
   data-form-status attribute forms.js writes, so it disappears by itself the
   moment a real ODIV Engage URL is registered — nothing to remember to remove.
   ========================================================================== */

.stub-note {
  background: var(--surf-recessed);
  border: 1px dashed var(--edge-gold-strong);
  border-radius: var(--r-md);
  padding: var(--sp-5);
  color: var(--on-teal-2);
  font-size: 15px;
}
.stub-note strong { color: var(--gold-highlight); }

/* Visibility, in three steps — and the order is load-bearing.
   The /start/* stub pages carry a .stub-note but have NO registry CTA at all
   (their actions are a tel: link and a back link), so a naive
   `hide by default, reveal on placeholder` pair would blank the one message
   those seven pages exist to deliver. Gating the HIDE on the page having a
   [data-form] element is what keeps both cases correct:
     no registry CTA on the page  -> always shown  (the stubs)
     registry CTAs, all live      -> hidden        (post-launch, automatically)
     registry CTAs, any placeholder -> shown       (today) */
.stub-note { display: block; margin-top: var(--sp-5); }
body:has([data-form]) .stub-note { display: none; }
body:has([data-form-status="placeholder"]) .stub-note { display: block; }

/* ==========================================================================
   Footer — legal entity + CIN, per PRD §2
   ========================================================================== */

.footer {
  border-top: 1px solid var(--edge-quiet);
  background: rgba(0, 10, 16, .55);
  padding: var(--sp-8) 0 var(--sp-7);
  margin-top: var(--sp-9);
}
/* FIVE columns now, not six — Contact moved up into the brand band, where a
   34-character email has room to sit on one line.

   That also halves the rag. The five lists run 6, 8, 8, 9 and 8 items, so the
   longest overhangs the shortest by three rows; with Contact's three-item list
   in the grid it was six, against a nine-item neighbour, and the bottom edge
   read as an accident. 170px minimum keeps them from collapsing to four. */
.footer__cols {
  display: grid;
  gap: var(--sp-6) var(--sp-5);
  align-items: start;
  /* TWO COLUMNS ON A PHONE, explicitly — not auto-fit.
     auto-fit at a 170px minimum resolves to a single column inside a 350px
     wrap, which stacked all five lists and made the footer 3,640px tall: nine
     hundred more pixels than the entire home page above it. Thirty-nine links
     at the 44px tap target this site holds itself to is simply a lot of
     height, and the only honest way to halve it without shrinking the targets
     or hiding links is to use the width that is there. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* TRACK COUNTS THAT DIVIDE SIX, not auto-fit.
   Four single columns plus the double-width Calculators column is always six
   track-units, so any track count that does not divide six leaves holes: at the
   five tracks auto-fit resolved to here, Calculators could not fit beside Firm
   and dropped to a second row with two empty cells behind it. 2, 3 and 6 all
   pack exactly, at every width. */
@media (min-width: 700px) {
  .footer__cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1000px) {
  .footer__cols { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* THE TALL-COLUMN PROBLEM, AND WHY IT IS SOLVED HERE RATHER THAN BY CUTTING LINKS.
   The five columns run 6, 9, 8, 8 and 17 links. A grid row is as tall as its
   tallest cell, so Calculators alone set the footer's height and the other four
   ended in roughly seven hundred pixels of nothing — the bottom edge read as a
   mistake rather than a margin.

   The column spans two tracks and flows into two sub-columns, so seventeen
   becomes 9 + 8 and the tallest column in the footer matches Loans at nine.
   Nothing is hidden and no link moves to another column. Measured at 1440:
   the column block goes from 937px to 507px, in one row instead of two.

   `span 2` does the right thing in every grid above: against the phone's
   two-track grid it resolves to the full width, which is what keeps the
   sub-columns at a readable ~165px instead of splitting a half-width cell into
   two 75px ones that would break "Loan interest recovery" mid-phrase. */
.footer__col--wide { grid-column: span 2; }
.footer__col--wide ul { columns: 2; column-gap: var(--sp-5); }
/* A link must not be split across the column break — on the phone these are
   44px flex rows, and half a tap target at the foot of a column is worse than
   an uneven column. */
.footer__col--wide li { break-inside: avoid; }
/* h3 AND h4: the last heading before the footer is usually the closer's h2, so
   the column labels have to be h3 — h2 -> h4 is a skipped level, which is both
   an audit failure and a real screen-reader outline bug. Styling both means the
   markup can use whichever level keeps that page's outline intact. */
.footer h4, .footer h3 {
  font-family: var(--body); font-weight: 600; font-size: 13px;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--on-teal-3); margin: 0 0 var(--sp-3);
}
.footer ul { list-style: none; margin: 0; padding: 0; }
/* Footer link lists are the classic phone mis-tap. 21px text rows sitting 8px
   apart become 33px rows sitting 12px apart — still compact, no longer a lottery. */
.footer li { margin-bottom: var(--sp-3); }
/* 6px of padding gives a 36px row, which is fine for a mouse. On a phone this
   site's own standard is 44 — the same thumb minimum the buttons carry, set for
   the same reason: the audience is on a phone, often outdoors, often over
   fifty. Thirty-three footer links at 36px was the largest single group of
   under-sized targets in the mobile audit, and it was the only one that turned
   out to be real. Desktop keeps the compact row. */
.footer li a { display: inline-block; padding: 6px 0; }
@media (max-width: 700px) {
  .footer li a { display: flex; align-items: center; min-height: 44px; padding: 4px 0; }
}
.footer a { color: var(--on-teal-2); font-size: 15px; }
.footer a:hover { color: var(--on-teal-1); }
.footer .lockup { margin-bottom: var(--sp-4); }
.footer__note { font-size: 13.5px; color: var(--on-teal-3); line-height: 1.6; }

/* Icons — Lucide, the set shadcn/ui ships. 20px is the resting size; they sit
   next to 15px body text and a 24px glyph overpowers it. */
.ico { width: 20px; height: 20px; flex: none; }
.ico--sm { width: 16px; height: 16px; }

.socials { display: flex; gap: var(--sp-2); margin-top: var(--sp-4); }
.social {
  display: grid; place-items: center;
  width: 44px; height: 44px;                 /* thumb target, not a 20px glyph */
  border-radius: var(--r-pill);
  border: 1px solid var(--edge-quiet);
  background: var(--surf-recessed);
  color: var(--on-teal-2);
  transition: color .18s ease, border-color .18s ease, background .18s ease;
}
.social:hover { color: var(--gold-highlight); border-color: var(--edge-gold-strong); background: var(--surf-raised); }

.contact-list { list-style: none; margin: 0; padding: 0; }
.contact-list li {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  margin-bottom: var(--sp-3); font-size: 15px; color: var(--on-teal-2); line-height: 1.5;
}
.contact-list .ico { color: var(--gold-core); margin-top: 2px; }
/* `overflow-wrap: anywhere` was here to stop the address being clipped at the
   old column's edge. It worked, and it produced "invest@wealthfa /
   rmingadvisory.c / om" — which does not read as a styling choice either.
   Given room in the brand band the address fits, so the rule can be the polite
   one: break at a normal opportunity if it must, never mid-word. */
.contact-list a, .contact-list span { overflow-wrap: break-word; }
.contact-list a { display: inline-block; padding: 2px 0; }
.contact-list li { min-width: 0; }

/* Its own band above the columns, not a cell inside them — a 328px wordmark
   forced the grid to give it two tracks, which opened a tall empty well beneath
   it and pushed the six link columns into a ragged 3-then-4 wrap. */
.footer__brand {
  display: grid;
  gap: var(--sp-6);
  padding-bottom: var(--sp-7); margin-bottom: var(--sp-7);
  border-bottom: 1px solid var(--edge-quiet);
}
@media (min-width: 780px) {
  /* The reach column is given a real minimum rather than a share of the row:
     the email is the widest single string in the footer and everything about
     this band exists so that it does not have to break. */
  .footer__brand { grid-template-columns: minmax(0, 1fr) minmax(300px, 380px); gap: var(--sp-7); }
}
.footer__brand .footer__note { margin: var(--sp-4) 0 0; }

.footer__reach-h {
  /* Same treatment as the column headings, so the band reads as part of the
     same system rather than as a panel bolted on. */
  font-family: var(--body); font-weight: 600; font-size: 13px;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--on-teal-3); margin: 0 0 var(--sp-3);
}
.socials { margin-top: var(--sp-5); }

.legal {
  margin-top: var(--sp-7); padding-top: var(--sp-5);
  border-top: 1px solid var(--edge-quiet);
  font-size: 12.5px; line-height: 1.7; color: var(--on-teal-3);
  display: flex; flex-direction: column; gap: var(--sp-4);
}
.legal strong { color: var(--on-teal-2); font-weight: 600; }

/* ⚠ A MEASURE, which this block did not have. At 12.5px across the full
   1092px wrap it ran to roughly 130 characters a line — about double what is
   comfortable, and the reason three ordinary paragraphs read as a wall of
   text. Everything else here is cosmetic next to this one rule. */
.legal p { margin: 0; max-width: 78ch; }

/* ⚠ font-size: inherit is NOT redundant. `.footer a` sets 15px, so every link
   in here rendered three points larger than the sentence around it — "see
   Disclosures" sat inside 12.5px text at 15px and looked like a mistake,
   because it was one. */
.legal a {
  font-size: inherit;
  color: var(--on-teal-2);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--edge-gold);
}
.legal a:hover { color: var(--on-teal-1); text-decoration-color: var(--gold-core); }

/* The separators are decoration between facts, so they are quieter than the
   facts and hidden from screen readers — read aloud, a run of interpuncts is
   noise between clauses that are already separate sentences to a listener. */
.legal__sep { color: var(--edge-gold); padding: 0 2px; }

/* The one paragraph a regulator cares about, marked as a different KIND of
   statement rather than as more small print. A hairline, not a box: this
   should be findable, not loud. */
.legal__reg {
  border-left: 2px solid var(--edge-gold);
  padding-left: var(--sp-4);
  color: var(--on-teal-2);
}

.legal__foot {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  align-items: baseline; justify-content: space-between;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--edge-quiet);
}
.legal__credit { color: var(--on-teal-3); }
.legal__credit a { text-decoration-color: var(--edge-quiet); }
.legal__credit a:hover { text-decoration-color: var(--gold-core); }

/* ==========================================================================
   Calculators
   ==========================================================================

   Sizing here is driven by one fact: the visitor is a seminar attendee on a
   mid-range Android, one-handed, quite possibly over fifty. Every control is
   at least 48px, the slider thumb is 32px, and the value itself is tappable
   so nobody has to land a slider on an exact rupee figure.
   ========================================================================== */

.page-hero--calc { padding-bottom: var(--sp-5); }

.calc { display: grid; gap: var(--sp-6); align-items: start; }
@media (min-width: 900px) {
  /* The answer stays on screen while the inputs are moved. On a phone it sits
     below them, which is correct there — you cannot see both at once anyway,
     and putting the result first would mean scrolling up to change anything. */
  .calc { grid-template-columns: minmax(0, 1fr) minmax(320px, 400px); gap: var(--sp-7); }
  .calc__out { position: sticky; top: calc(var(--nav-h) + var(--sp-4)); }
}

.calc__inputs { display: flex; flex-direction: column; gap: var(--sp-5); }

.field {
  background: var(--surf-resting);
  border: 1px solid var(--edge-quiet);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}
.field__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.field__label { font-size: 14.5px; color: var(--on-teal-2); }

/* The readout is the primary input, not a label. It is contenteditable, so it
   needs to look tappable and give obvious focus feedback. */
.field__val {
  font-family: var(--display); font-size: clamp(19px, 4.4vw, 24px); font-weight: 700;
  letter-spacing: .01em; white-space: nowrap;
  color: var(--gold-highlight);
  /* 8px vertical, not 4: at 4px the tappable readout measured 39px tall, under
     the 44px minimum. The negative margin cancels the padding so the extra
     height costs no layout. */
  padding: var(--sp-2) var(--sp-3); margin: calc(var(--sp-2) * -1) calc(var(--sp-3) * -1);
  border-radius: var(--r-sm);
  border-bottom: 1px dashed var(--edge-gold);
  cursor: text; font-variant-numeric: tabular-nums;
}
.field__val:hover { background: var(--surf-raised); }
.field__val:focus { outline: 2px solid var(--gold-highlight); outline-offset: 1px; background: var(--surf-recessed); }

.field__control { display: flex; align-items: center; gap: var(--sp-4); }
/* --on-teal-2, not -3. This sentence tells someone what a sensible answer looks
   like; it is guidance, not metadata. --on-teal-3 is white at 50% and tops out
   at 5.32:1 even on pure black, so it cannot clear AA anywhere on the glow —
   it is for text nobody has to read. */
.field__help { margin: var(--sp-3) 0 0; font-size: 12.5px; line-height: 1.5; color: var(--on-teal-2); }

/* 48px, and round. Press-and-hold is bound to these, so they get held down
   rather than tapped — a small target is genuinely painful here. */
.stepper {
  flex: 0 0 auto; width: 48px; height: 48px;
  display: grid; place-items: center;
  border-radius: 50%; cursor: pointer;
  background: var(--surf-recessed);
  border: 1px solid var(--edge-gold);
  color: var(--gold-core);
  touch-action: manipulation;         /* no double-tap-to-zoom delay */
  -webkit-user-select: none; user-select: none;
  transition: background .15s ease, border-color .15s ease, transform .1s ease;
}
.stepper:hover { background: var(--surf-raised); border-color: var(--edge-gold-strong); color: var(--gold-highlight); }
.stepper:active { transform: scale(.94); background: var(--surf-raised); }
.stepper svg { width: 22px; height: 22px; }

/* ---- slider ----
   Styled per-engine because there is no cross-browser shorthand for a range
   track and thumb. The thumb is 32px — well past the 24px most design systems
   use — because it is dragged with a thumb, not a mouse. */
.slider {
  flex: 1 1 auto; min-width: 0;
  -webkit-appearance: none; appearance: none;
  /* 44px of touch height for a 6px track — the extra is invisible padding that
     makes the drag forgiving. WebKit centres the track in the element box, so
     the thumb offset below is unaffected. */
  height: 44px; background: transparent; cursor: pointer;
  touch-action: pan-y;                /* let a vertical swipe still scroll */
}
/* ---- the track: a recessed brass rail ----------------------------------
   Inset shadow at the top edge so the slot reads as cut INTO the surface, with
   the thumb sitting proud of it. Without that the knob looks pasted on. */
.slider::-webkit-slider-runnable-track {
  height: 8px; border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--gold-shadow), var(--gold-core));
  box-shadow: inset 0 2px 3px rgba(0, 10, 16, .55), inset 0 -1px 0 rgba(255, 255, 255, .18);
}
.slider::-moz-range-track {
  height: 8px; border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--gold-shadow), var(--gold-core));
  box-shadow: inset 0 2px 3px rgba(0, 10, 16, .55), inset 0 -1px 0 rgba(255, 255, 255, .18);
}

/* ---- the knob ----------------------------------------------------------
   A turned brass knob rather than a flat disc. Four layers, because a thumb
   pseudo-element cannot have children — everything has to be backgrounds and
   shadows on the one box:

     1  specular highlight, up and left, where the light is
     2  a conic sweep — the brushed-metal banding that makes brass read as
        turned metal rather than a gold circle
     3  the radial body, bright at the crown falling to bronze at the rim,
        which is what gives it the dome
     4  inset shadows: dark at the bottom, light at the top, so the dome has
        a direction

   The dark bezel is not decoration. The knob sits on a gold track, and gold on
   gold is invisible — the bezel is what carries the 3:1 non-text contrast that
   makes the control findable. Same reason the button focus ring is white.

   Asked for by Sambit, 2026-08-10: "retro knobs ... golden or brass". */
.slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 34px; height: 34px; margin-top: -13px;
  border-radius: 50%;
  border: 2px solid rgba(0, 10, 16, .68);
  background:
    radial-gradient(circle at 34% 26%, rgba(255,255,255,.92) 0%, rgba(255,255,255,0) 42%),
    conic-gradient(from 210deg,
      rgba(0,0,0,.26) 0deg, rgba(255,255,255,.22) 78deg,
      rgba(0,0,0,.26) 180deg, rgba(255,255,255,.22) 258deg, rgba(0,0,0,.26) 360deg),
    radial-gradient(circle at 50% 44%, #FDD781 0%, #D1AC66 54%, #8E4719 100%);
  box-shadow:
    inset 0 -3px 6px rgba(0, 10, 16, .55),
    inset 0 2px 3px rgba(255, 255, 255, .45),
    0 3px 10px rgba(0, 0, 0, .5);
  transition: transform .12s ease, box-shadow .18s ease;
}
.slider::-moz-range-thumb {
  width: 34px; height: 34px; border-radius: 50%;
  border: 2px solid rgba(0, 10, 16, .68);
  background:
    radial-gradient(circle at 34% 26%, rgba(255,255,255,.92) 0%, rgba(255,255,255,0) 42%),
    conic-gradient(from 210deg,
      rgba(0,0,0,.26) 0deg, rgba(255,255,255,.22) 78deg,
      rgba(0,0,0,.26) 180deg, rgba(255,255,255,.22) 258deg, rgba(0,0,0,.26) 360deg),
    radial-gradient(circle at 50% 44%, #FDD781 0%, #D1AC66 54%, #8E4719 100%);
  box-shadow:
    inset 0 -3px 6px rgba(0, 10, 16, .55),
    inset 0 2px 3px rgba(255, 255, 255, .45),
    0 3px 10px rgba(0, 0, 0, .5);
}
/* Pressed: the knob sinks slightly. Grab feedback on a control people drag. */
.slider:active::-webkit-slider-thumb { transform: scale(.96); box-shadow: inset 0 -2px 5px rgba(0,10,16,.6), inset 0 2px 3px rgba(255,255,255,.35), 0 1px 4px rgba(0,0,0,.5); }
.slider:active::-moz-range-thumb { transform: scale(.96); }

/* White ring, not gold — a gold ring on a brass knob is the same mistake as a
   gold ring on the gold primary button. */
.slider:focus-visible::-webkit-slider-thumb {
  outline: 3px solid var(--on-teal-1); outline-offset: 2px;
  box-shadow: 0 0 0 7px rgba(209, 172, 102, .40), inset 0 -3px 6px rgba(0,10,16,.55), inset 0 2px 3px rgba(255,255,255,.45);
}
.slider:focus-visible::-moz-range-thumb {
  outline: 3px solid var(--on-teal-1); outline-offset: 2px;
  box-shadow: 0 0 0 7px rgba(209, 172, 102, .40);
}

/* ---- results ---- */
.results {
  background: var(--surf-recessed);
  border: 1px solid var(--edge-gold-strong);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
}
.results__label { font-size: 12px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--on-teal-3); margin: 0 0 var(--sp-3); }
/* Sized big and then shrunk by JS if a nine-digit corpus will not fit. A
   half-visible number is worse than a smaller one. */
.results__value {
  font-family: var(--display); font-size: clamp(34px, 8vw, 46px); font-weight: 700;
  line-height: 1.05; letter-spacing: .01em; white-space: nowrap;
  /* Overflow is hidden as the last line of defence. The script shrinks the type
     to fit first; this only matters if the script has not run yet, and a
     clipped number is still better than one spilling outside the card. */
  overflow: hidden;
  background: var(--metal-lit); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: var(--gold-core);
  margin: 0 0 var(--sp-2);
}
.results__sub { font-size: 14px; color: var(--on-teal-2); margin: 0 0 var(--sp-5); }
.results__rows { margin-bottom: var(--sp-5); }
.rrow {
  display: flex; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-3) 0; border-top: 1px solid var(--edge-quiet);
  font-size: 13.5px; color: var(--on-teal-2); line-height: 1.4;
}
/* The LABEL is the flexible item: it wraps and gives up width first, because a
   wrapped label is readable and a wrapped rupee figure is not.
   The VALUE keeps its natural width and stays on one line, so when the pair no
   longer fits, the ROW overflows — which is the signal wfa-calc.js measures
   before scaling the value down to fit. Squeezing the value here instead would
   hide the overflow and leave the script nothing to detect. */
.rrow span { flex: 1 1 auto; min-width: 0; }
.rrow b {
  flex: 0 0 auto;
  color: var(--on-teal-1); font-weight: 600; white-space: nowrap;
  font-variant-numeric: tabular-nums; text-align: right;
}
.rrow--strong { color: var(--on-teal-1); }
.rrow--strong b { color: var(--gold-highlight); }
.results__cta { margin-top: var(--sp-2); }
/* 13px, not 11.5px. On a calculator run by an AMFI-registered distributor the
   smallest type on the page should not be the sentence saying the number is not
   a guarantee — that reads as minimising it, whatever the intent. It is also
   the line most likely to matter later. */
.results__note { margin: var(--sp-3) 0 0; font-size: 13px; line-height: 1.5; color: var(--on-teal-2); text-align: center; }

/* ---- the mobile result peek ----------------------------------------------
   On desktop .calc__out is sticky, so the figure sits beside whichever control
   you are moving. A phone has no second column: the result card renders BELOW
   every input — 1,601px below the first slider on the retirement page, roughly
   two screens. So the defining interaction of a calculator, move a control and
   watch the number react, was unavailable at the default scroll position. You
   dragged, and as far as you could tell nothing happened.

   This pins the headline figure to the bottom of the viewport while the real
   card is off-screen, and withdraws the moment that card scrolls into view so
   the number is never on screen twice.

   Deliberately aria-hidden and not focusable: it duplicates a value already in
   the DOM, and an aria-live region firing on every drag of a slider would
   flood a screen reader with partial figures. Nothing is lost — the real card
   is a few tab stops further on, and it is the one that carries the note.
   Built for the 2026-08-10 design critique. */
.calc__peek { display: none; }

@media (max-width: 1000px) {
  .calc__peek {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
    padding: var(--sp-3) var(--sp-5);
    padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
    background: var(--teal-deepest);
    border-top: 1px solid var(--edge-gold);
    box-shadow: 0 -8px 24px rgba(0, 10, 16, .45);
    transform: translateY(100%);
    transition: transform .24s cubic-bezier(.2,.7,.3,1);
    pointer-events: none;
  }
  .calc__peek[data-show] { transform: translateY(0); }
  .calc__peek__label {
    font-size: 12px; font-weight: 600; letter-spacing: .16em;
    text-transform: uppercase; color: var(--on-teal-3);
  }
  .calc__peek__value {
    font-family: var(--display); font-size: 22px; line-height: 1;
    color: var(--gold-highlight); white-space: nowrap;
  }
  /* Without this the bar sits on top of the last field's ± buttons — the one
     control most likely to be in use while the bar is showing. */
  [data-calc] .calc__inputs { padding-bottom: 76px; }
}

@media (prefers-reduced-motion: reduce) {
  .calc__peek { transition: none; }
}

/* Before wfa-calc.js runs there is no answer to show. Say so, rather than
   leaving an em-dash that looks like a broken page. */
.calc:not([data-live]) .results__value::after {
  content: " "; }
.calc:not([data-live]) .results__sub::before {
  content: "Move a slider to see your result.";
}

.assumptions {
  margin-top: var(--sp-7); padding-top: var(--sp-6);
  border-top: 1px solid var(--edge-quiet);
}
.assumptions h2 { font-size: clamp(19px, 2.6vw, 23px); margin-bottom: var(--sp-4); }
.assumptions .ticks li { max-width: 78ch; }

.formula {
  margin-top: var(--sp-5); padding: var(--sp-4) var(--sp-5);
  background: var(--surf-recessed); border-left: 3px solid var(--gold-core);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; line-height: 1.7; color: var(--on-teal-2);
  overflow-x: auto;                   /* a long formula scrolls, the page does not */
}

/* Whole-card links on the calculator hub. */
.offer--link { display: flex; flex-direction: column; }
.offer--link .offer__go { margin-top: auto; padding-top: var(--sp-4); font-size: 13.5px; font-weight: 600; color: var(--gold-core); }
.offer--link:hover .offer__go { color: var(--gold-highlight); }

/* ==========================================================================
   Articles — /blog/ and /blog/<slug>/
   Server-rendered by api/, so this is the only place their styling lives.
   ========================================================================== */

.page-hero--article h1 { max-width: 24ch; }
.byline { font-size: 13.5px; color: var(--on-teal-3); margin: 0; }
.byline time { font-variant-numeric: tabular-nums; }

.article__cover {
  width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover;
  border-radius: var(--r-lg); border: 1px solid var(--edge-quiet);
  margin-bottom: var(--sp-6);
}

/* Body copy is set for READING, not for scanning like the marketing pages.
   66ch measure, larger type, generous leading — an article that is a chore to
   read does not build the trust it exists to build. */
.article { max-width: 68ch; font-size: 17px; line-height: 1.75; color: var(--on-teal-2); }
.article > * + * { margin-top: var(--sp-5); }
.article h2 { font-size: clamp(23px, 3.6vw, 30px); margin: var(--sp-8) 0 var(--sp-4); }
.article h3 { font-size: clamp(19px, 2.6vw, 23px); margin: var(--sp-7) 0 var(--sp-3); }
.article p { margin: 0 0 var(--sp-5); }
.article ul, .article ol { margin: 0 0 var(--sp-5); padding-left: 1.3em; }
.article li { margin-bottom: var(--sp-2); }
.article a { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.article strong { color: var(--on-teal-1); }
.article blockquote {
  margin: var(--sp-6) 0; padding: var(--sp-4) var(--sp-5);
  border-left: 3px solid var(--gold-core); background: var(--surf-recessed);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; font-style: normal;
}
.article blockquote p:last-child { margin-bottom: 0; }
.article table { width: 100%; border-collapse: collapse; font-size: 15px; display: block; overflow-x: auto; }
.article th, .article td { padding: var(--sp-3); border-bottom: 1px solid var(--edge-quiet); text-align: left; }
.article th { color: var(--on-teal-1); font-weight: 600; }
.article img { border-radius: var(--r-md); margin: var(--sp-5) 0; }
.article code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em;
  background: var(--surf-recessed); padding: 2px 6px; border-radius: var(--r-xs);
}

.pager { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); margin-top: var(--sp-7); }
.pager__count { font-size: 13.5px; color: var(--on-teal-3); }

/* ---- toggle ----
   For a boolean. It used to be a slider labelled "1 for yes, 0 for no", which
   is a defect rather than a compromise: nobody should have to drag a control to
   express yes. A real role="switch" button, so a screen reader announces a
   switch with a state rather than a slider with a number. */
.switch {
  display: flex; align-items: center; gap: var(--sp-4);
  width: 100%; min-height: 52px; padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-pill); cursor: pointer;
  background: var(--surf-recessed); border: 1px solid var(--edge-quiet);
  touch-action: manipulation; -webkit-user-select: none; user-select: none;
  transition: border-color .18s ease, background .18s ease;
}
.switch:hover { border-color: var(--edge-gold); background: var(--surf-raised); }
.switch__track {
  flex: 0 0 auto; position: relative; width: 60px; height: 34px;
  border-radius: var(--r-pill); background: var(--surf-recessed);
  border: 1px solid var(--edge-quiet); transition: background .2s ease, border-color .2s ease;
}
.switch__thumb {
  position: absolute; top: 3px; left: 3px; width: 26px; height: 26px;
  border-radius: 50%; background: var(--on-teal-3);
  transition: transform .2s cubic-bezier(.2,.8,.25,1), background .2s ease;
}
.switch[aria-checked="true"] .switch__track { background: var(--metal-lit); border-color: transparent; }
.switch[aria-checked="true"] .switch__thumb { transform: translateX(26px); background: var(--teal-deepest); }
/* Both words are always present so the control reads as a choice rather than as
   a state you have to infer from a colour — which also survives colour blindness. */
.switch__text { display: flex; gap: var(--sp-3); font-size: 15px; font-weight: 600; }
.switch__no, .switch__yes { color: var(--on-teal-3); transition: color .18s ease; }
.switch[aria-checked="false"] .switch__no { color: var(--on-teal-1); }
.switch[aria-checked="true"] .switch__yes { color: var(--gold-highlight); }
.field__val--static { border-bottom: 0; cursor: default; }
.field__val--static:hover { background: none; }

/* ---- choice ----
   A small discrete set. Every option visible and one tap away beats hunting
   for position 4 of 8 on a slider. */
.choices { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.choice {
  flex: 1 1 auto; min-width: 48px; min-height: 48px; padding: var(--sp-2);
  border-radius: var(--r-md); cursor: pointer;
  background: var(--surf-recessed); border: 1px solid var(--edge-quiet);
  color: var(--on-teal-2); font-family: var(--body); font-size: 16px; font-weight: 600;
  touch-action: manipulation;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.choice:hover { border-color: var(--edge-gold); color: var(--on-teal-1); }
.choice[aria-checked="true"] {
  background: var(--metal-lit); border-color: transparent;
  color: var(--ink-on-gold); box-shadow: 0 4px 14px rgba(0,0,0,.28);
}

/* "for each applicant" marker on the pre-form document checklist. A separate
   token rather than bold text, because the distinction between "one of these"
   and "one each" is the thing people get wrong and then have to redo. */
.both {
  display: inline-block; margin-left: var(--sp-1); padding: 1px var(--sp-2);
  border-radius: var(--r-pill); border: 1px solid var(--edge-gold);
  font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--gold-core); white-space: nowrap; vertical-align: 1px;
}

/* ==========================================================================
   MEDIA — evidence, photography, and waiting

   THE DECISION THAT SHAPES ALL OF THIS
     This practice has two completely different kinds of image and they must
     not share a component.

       EVIDENCE  — award certificates, the Times of India clipping, the ARN
                   registration. The visitor is checking whether we are real.
                   Every one must be legible and inspectable side by side.
       ATMOSPHERE — seminar photography. Many near-equivalent frames; no single
                   one carries information.

     Evidence never goes in a carousel. A carousel hides items 2..n behind an
     interaction, and hiding your credentials behind a swipe is the opposite of
     what they are for. Evidence gets .frame in a plain grid.
     Atmosphere gets .rail, where paging is exactly right.
   ========================================================================== */

/* ---- .frame — a document, shown as a document ----------------------------
   NEVER crops. object-fit: contain on a paper mat, because a certificate that
   has been cropped to fill a box looks like a stock photo of a certificate,
   and the entire reason it is on the page is that it is not one.

   The aspect box is fixed so the row cannot reflow as images arrive. This site
   has form on that: a single missing 404ing asset once pushed the homepage CLS
   to 0.262. Reserve the space, always. */
.frame {
  display: flex; flex-direction: column; margin: 0;
}
.frame__box {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden;
  border-radius: var(--r-md); border: 1px solid var(--edge-gold);
  background: var(--paper);
  /* The mat. A certificate sits on paper, not on the teal. */
  padding: var(--sp-3);
}
.frame--portrait .frame__box { aspect-ratio: 3 / 4; }
.frame--wide .frame__box { aspect-ratio: 16 / 9; }
.frame__box img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.frame figcaption {
  margin-top: var(--sp-3); font-size: 13.5px; line-height: 1.5; color: var(--on-teal-3);
}
.frame figcaption b { display: block; color: var(--on-teal-1); font-weight: 600; font-size: 14.5px; }
/* Zoom affordance only where a real full-size file exists. */
.frame__box a { position: absolute; inset: 0; }
.frame__box a::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  box-shadow: inset 0 0 0 0 var(--gold-core); transition: box-shadow .18s ease;
}
.frame__box a:hover::after, .frame__box a:focus-visible::after { box-shadow: inset 0 0 0 3px var(--gold-core); }

.frames { display: grid; gap: var(--sp-5); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* ---- .rail — scroll-snap carousel, no JavaScript -------------------------
   A real scroll container with snap points. It works with a thumb, a trackpad,
   a mouse wheel, and arrow keys once focused, on every browser, with the
   script blocked. A JS carousel would be more code and less reliable.

   Give it role="region", an aria-label and tabindex="0" so a keyboard user can
   reach and scroll it — a scrollable region that cannot receive focus is a
   genuine WCAG failure, not a nicety. */
/* ⚠ UNIFORM HEIGHT, NATURAL WIDTH — changed 2026-08-26, and it fixes a real
   bug rather than a preference.

   Every gallery item used to be forced into a 16:9 box and filled with
   object-fit: cover. The session photographs are 4:3, so a quarter of their
   height was cut; the portrait ones are 3:4, so roughly seventy per cent of
   theirs was. What sits in the top of a photograph of a man addressing a hall
   is his head, which is precisely what was being cut off.

   A horizontal rail cannot have both uniform width and honest aspect ratios —
   one of the two has to give. Uniform HEIGHT is the one that costs nothing:
   the row still lines up, the captions still align, and every picture keeps
   its own shape. Portraits come out narrow and tall, landscapes wide and
   short, which is what they are.

   The per-image ratio is written onto .frame__box by the renderer from the
   dimensions declared in pages.mjs, so a wrong number there shows up as a
   wrong shape rather than as a silent crop. */
.rail {
  display: flex; align-items: flex-start;
  gap: var(--sp-4);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-padding: var(--sp-5);
  padding-bottom: var(--sp-3);
  /* Hide the bar on desktop but keep the affordance on touch, where the bar is
     transient anyway. */
  scrollbar-width: thin; scrollbar-color: var(--edge-gold) transparent;
}
.rail > * { scroll-snap-align: start; flex: 0 0 auto; }
.rail img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: var(--r-md); }
/* fit-content, and the figure must NOT stretch its box. A .frame is a flex
   column, so the box would otherwise stretch to whatever the widest child is
   and the aspect-ratio would lose — which is exactly what happened first time:
   the portrait's box came out 288px wide instead of 225px and cropped again,
   because the CAPTION was setting the width. */
.rail figure { margin: 0; width: fit-content; max-width: 82vw; align-items: flex-start; }
.rail .frame__box {
  background: var(--surf-recessed); padding: 0;
  height: clamp(190px, 46vw, 300px); width: auto; flex: none;
}
/* width:0 with min-width:100% lets the caption FILL the width the picture
   established without contributing to it. Without the pair, a long sentence
   under a tall thin photograph makes the whole column as wide as the sentence. */
.rail figcaption { width: 0; min-width: 100%; }
@media (min-width: 760px) { .rail .frame__box { height: 300px; } }
.rail:focus-visible { outline: 2px solid var(--gold-core); outline-offset: 4px; }

/* ---- .crossfade — one moment, not a feature -----------------------------
   Stacked frames, staggered opacity. The FIRST child is opaque with no
   animation applied, so with CSS animation unavailable, JS blocked, or
   reduced-motion set, the component is simply that one photograph. Nothing is
   only comprehensible while moving.

   One per page, and never for anything the visitor has to read. */
.crossfade { position: relative; }
.crossfade img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-md); }
.crossfade img:first-child { position: relative; }
.crossfade img + img { opacity: 0; }

/* Inside a document frame the frame wins. .crossfade defaults to cover because
   atmosphere should fill its box, but a certificate inside a .frame__box must
   still be CONTAINED — cropping the evidence is the one thing the frame exists
   to prevent, and a cover-cropped certificate was exactly what shipped. */
.frame__box .crossfade img { object-fit: contain; border-radius: 0; }
/* The crossfade takes its height from its first child, which is the only one
   in flow. Inside a fixed-aspect frame that left the box sized to the first
   photograph and the rest of the mat empty below it — so every frame was
   top-anchored in a tall well of blank paper. Fill the box and let all of them
   be absolute, so `contain` centres each one in the same square. */
.frame__box .crossfade { height: 100%; }
.frame__box .crossfade img:first-child { position: absolute; }

/* ---- timing ---------------------------------------------------------------
   NEGATIVE delays, and keyframes that depend on how many frames there are.

   The first version used positive delays (0s, 6s, 12s) with a fade that ended
   at 33% of an 18s loop. Two things went wrong. Images two and three sat at
   their base opacity of 0 for the whole first cycle and then snapped in. And
   image one reached zero at 5.94s while image two did not begin until 6.00s,
   so for 60ms nothing was painted and the paper mat flashed through between
   every frame.

   A negative delay starts the animation already part-way through, so every
   frame is running from the first paint, and the fade-in of the next overlaps
   the fade-out of the current. There is no instant at which all frames are
   transparent.

   The overlap only works if the delays divide the loop by the FRAME COUNT,
   which is why the count is on the element rather than inferred. */
@media (prefers-reduced-motion: no-preference) {
  .crossfade img { animation-duration: 18s; animation-iteration-count: infinite; }

  .crossfade[data-frames="2"] img { animation-name: xfade2; }
  .crossfade[data-frames="2"] img:nth-child(1) { animation-delay: 0s; }
  .crossfade[data-frames="2"] img:nth-child(2) { animation-delay: -9s; }

  .crossfade[data-frames="3"] img { animation-name: xfade3; }
  .crossfade[data-frames="3"] img:nth-child(1) { animation-delay: 0s; }
  .crossfade[data-frames="3"] img:nth-child(2) { animation-delay: -12s; }
  .crossfade[data-frames="3"] img:nth-child(3) { animation-delay: -6s; }
}

/* Each frame holds, fades out, waits, then fades back in just as the previous
   one is fading out. The last stop returning to 1 is what makes the loop
   seamless rather than jumping at the wrap. */
@keyframes xfade2 {
  0%, 43% { opacity: 1; } 50%, 93% { opacity: 0; } 100% { opacity: 1; }
}
@keyframes xfade3 {
  0%, 30% { opacity: 1; } 36.7%, 96.7% { opacity: 0; } 100% { opacity: 1; }
}

/* ---- .badge-wall — the credential marks ---------------------------------
   Not chips: these are supplied artwork (AMFI, CFP, NISM), so they keep their
   own colour and get a quiet surface rather than a gold hairline that would
   compete with them. */
.badge-wall { display: flex; flex-wrap: wrap; gap: var(--sp-3); list-style: none; margin: 0; padding: 0; }
.badge-wall li {
  display: grid; place-items: center; min-height: 64px; padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--edge-quiet); border-radius: var(--r-sm); background: var(--surf-resting);
}
.badge-wall img { max-height: 34px; width: auto; display: block; }

/* ==========================================================================
   WAITING — what the site does before it can answer
   ========================================================================== */

/* ---- .skel — reserved space, not a spinner ------------------------------
   Preferred over a spinner wherever the SHAPE of the answer is already known,
   because it reserves the layout instead of collapsing it and then pushing
   everything down when content lands. */
.skel {
  border-radius: var(--r-sm);
  background: linear-gradient(100deg, var(--surf-resting) 8%, var(--surf-raised) 22%, var(--surf-resting) 36%);
  background-size: 240% 100%;
}
.skel--line { height: 12px; margin-bottom: var(--sp-3); }
.skel--line:nth-of-type(3n) { width: 72%; }
.skel--head { height: 22px; width: 46%; margin-bottom: var(--sp-4); }
.skel--card { height: 160px; }
@media (prefers-reduced-motion: no-preference) {
  .skel { animation: skel 1.6s linear infinite; }
}
@keyframes skel { from { background-position: 140% 0; } to { background-position: -40% 0; } }

/* ---- .loading — the coin stack, doing a second job ----------------------
   The site already owns a four-bar growth mark, so the loader is that mark
   cycling rather than a generic ring. Costs no new artwork and reads as ours.
   aria-hidden with a visually-hidden live message beside it: a screen reader
   should hear "Loading" once, not watch four bars. */
.loading { display: inline-flex; align-items: flex-end; gap: 3px; height: 18px; }
.loading i { display: block; width: 4px; border-radius: 2px; background: var(--metal-lit); height: 40%; }
@media (prefers-reduced-motion: no-preference) {
  .loading i { animation: loadbar 1s ease-in-out infinite; }
  .loading i:nth-child(2) { animation-delay: .12s; }
  .loading i:nth-child(3) { animation-delay: .24s; }
  .loading i:nth-child(4) { animation-delay: .36s; }
}
@keyframes loadbar { 0%, 100% { height: 40%; } 50% { height: 100%; } }

/* A control doing real work. The label stays — replacing it with a spinner
   loses the only thing telling the user what they clicked. */
.btn[aria-busy="true"] { pointer-events: none; }
.btn[aria-busy="true"] .loading { margin-left: var(--sp-2); }
/* On the primary the bars were --metal-lit on a --metal-lit fill: gold on gold,
   the same mistake as the focus ring. On a gold surface the loader is INK.
   Also drops the blanket opacity, which was dimming the label as well as the
   loader on the one control the user is waiting for. */
.btn--primary[aria-busy="true"] .loading i { background: var(--ink-on-gold); }
.btn--ghost[aria-busy="true"] .loading i,
.btn--link[aria-busy="true"] .loading i { background: var(--metal-lit); }


/* ==========================================================================
   CHOICE CONTROLS — checkbox, radio, select

   The calculators use .choice and .switch, which are BUTTONS driving a hidden
   value — right for a calculator, where every option is one tap and there is no
   submit. A real form is different: it posts, it validates, and it has to be
   restorable from the browser's own autofill. So these keep the NATIVE input
   and style around it. Never rebuild a checkbox out of divs.
   ========================================================================== */

.opt { display: flex; align-items: flex-start; gap: var(--sp-3); min-height: 44px; padding: var(--sp-2) 0; cursor: pointer; }
.opt input { position: absolute; opacity: 0; width: 0; height: 0; }
/* 22 on a mouse, 28 on a touch screen. The .opt ROW has always been 44px, so
   the target was never the problem -- but a 22px box under a fingertip is
   invisible while you are pressing it, and a visitor who cannot see the state
   change taps again and undoes it. Measured on a real handset, not a resized
   window. */
.opt__box {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 22px; height: 22px; margin-top: 1px;
  border: 1px solid var(--edge-quiet); border-radius: var(--r-xs);
  background: var(--surf-recessed); box-shadow: var(--well);
  transition: border-color .15s ease, background .15s ease;
}
.opt--radio .opt__box { border-radius: 50%; }
@media (hover: none), (max-width: 700px) {
  .opt__box { width: 28px; height: 28px; margin-top: 0; }
  .opt__box::after { transform: scale(1.18); }
}
.opt__box::after {
  content: ""; opacity: 0; transition: opacity .12s ease;
  width: 11px; height: 11px; border-radius: 2px; background: var(--ink-on-gold);
  /* A tick drawn with a clip-path rather than a glyph, so it cannot be
     affected by a missing font. */
  clip-path: polygon(14% 46%, 38% 70%, 86% 16%, 96% 28%, 38% 92%, 4% 58%);
}
.opt--radio .opt__box::after { width: 9px; height: 9px; border-radius: 50%; clip-path: none; }
.opt input:checked + .opt__box { background: var(--metal-lit); border-color: transparent; }
.opt input:checked + .opt__box::after { opacity: 1; }
.opt:hover .opt__box { border-color: var(--edge-gold); }
/* The ring goes on the BOX, because the real input is visually hidden — without
   this the control is keyboard-operable but the focus is invisible. */
.opt input:focus-visible + .opt__box {
  outline: 3px solid var(--on-teal-1); outline-offset: 2px;
  box-shadow: 0 0 0 6px rgba(209, 172, 102, .40);
}
.opt__text { font-size: 15px; line-height: 1.5; color: var(--on-teal-2); }
.opt__text b { display: block; color: var(--on-teal-1); font-weight: 600; }

/* Native select, restyled. The chevron is a background image because a
   pseudo-element on a <select> is unreliable across engines. */
.ff__select {
  display: block; width: 100%; min-height: 48px;
  padding: var(--sp-3) var(--sp-8) var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm); border: 1px solid var(--edge-quiet);
  box-shadow: var(--well);
  background-color: var(--surf-recessed);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23D1AC66' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--sp-4) center; background-size: 18px;
  color: var(--on-teal-1); font-family: var(--body); font-size: 16px;
  -webkit-appearance: none; appearance: none; cursor: pointer;
}
.ff__select:hover { border-color: var(--edge-gold); }
.ff__select:focus-visible { outline: none; border-color: var(--gold-core); box-shadow: 0 0 0 3px rgba(209, 172, 102, .30); }
/* The dropdown list itself is drawn by the OS and cannot be themed. On a dark
   page that means light option rows in some browsers — expected, not a bug. */
.ff__select option { color: var(--ink); }

/* ---- chip variants ------------------------------------------------------
   Three jobs, three treatments. Documented in the old brand reference and
   never built; built now.

     .chip          credential — gold hairline, repeatable, regulatory facts
     .chip--legal   attribution — neutral hairline, deliberately quieter
     .chip--award   the one place a continuous animation is allowed
   -------------------------------------------------------------------------- */
.chip--legal { border-color: var(--edge-quiet); color: var(--on-teal-3); }

/* Solid metal, slow sheen, ONE per viewport. It is the only continuously
   animating gold surface in the system; a second one turns the page into a
   slot machine. --metal-lit because it carries dark text. */
.chip--award {
  background: var(--metal-lit); background-size: 220% 100%;
  border-color: transparent; color: var(--ink-on-gold); font-weight: 600;
}
@media (prefers-reduced-motion: no-preference) {
  .chip--award { animation: sheen 7s ease-in-out infinite; }
}
@keyframes sheen { 0%, 100% { background-position: 0% 0; } 50% { background-position: 100% 0; } }

/* ==========================================================================
   The public-issue board — /investments/ipo/
   ==========================================================================

   A TABLE, not a grid of cards. Twenty-one issues carrying the same four facts
   each, and the reader's question is comparative: which closes first, which is
   in my range. Cards spend their space on frames around rows and push that scan
   across four screens; a table spends it on the rows.

   Two consequences of it being live data.

   First, it can be EMPTY, and empty has to be visibly different from broken.
   Both states render a .fallow plot rather than nothing, and the wording — not
   the styling — is what distinguishes them; see wfa-ipo.js.

   Second, everything on it is a DEADLINE. Status is its own column, the sort is
   by urgency, and the STATUS CHIPS RANK BY URGENCY TOO — solid metal on the
   last day to apply, gold hairline on merely open, quiet on not yet started.
   The label is "Last day", not "Closes today": the second reads as finished,
   which is the opposite of what it means, and it was the emphasis reading
   backwards rather than the emphasis being wrong.

   No colour is used as the only signal: every state carries its own words, so
   the board works in greyscale and for a red-green colourblind reader.
   ========================================================================== */

.ipo { display: grid; gap: var(--sp-7); }

.ipo__group-h {
  font-family: var(--display); font-size: 19px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--gold-core);
  margin: 0 0 var(--sp-2);
}
.ipo__group-sub { font-size: 14px; color: var(--on-teal-3); max-width: 62ch; margin: 0 0 var(--sp-4); }

/* THE BLEED. The row tint used to start and stop exactly where the text does,
   so a highlighted row looked cropped at both ends rather than highlighted.
   The table is pulled --sp-5 wider than its column on each side and the outer
   cells take that back as padding: text still lines up with the heading above
   it, and the tint now clears the first and last glyph. Capped to the wrap's
   own gutter so it can never introduce a horizontal scrollbar. */
.ipo__table {
  width: calc(100% + var(--sp-5) * 2);
  margin-inline: calc(var(--sp-5) * -1);
  border-collapse: collapse;
}

.ipo__table thead th {
  padding: 0 var(--sp-4) var(--sp-3);
  border-bottom: 1px solid var(--edge-gold);
  font-size: 12px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold-core); text-align: left; white-space: nowrap;
}
.ipo__table thead th:first-child { padding-left: var(--sp-5); }
.ipo__table thead th:last-child { padding-right: var(--sp-5); text-align: right; }

.ipo__table tbody tr { border-bottom: 1px solid var(--edge-quiet); }
/* A row is a surface, not a card: the tint appears on hover and on the rows
   that matter, so the table reads as a list at rest. */
.ipo__table tbody tr:hover { background: var(--surf-resting); }
.ipo__table tbody tr.is-closing { background: rgba(209, 172, 102, .07); }

.ipo__table td, .ipo__table tbody th {
  padding: var(--sp-4); text-align: left; vertical-align: middle; font-weight: 400;
}
.ipo__table tbody th:first-child { padding-left: var(--sp-5); }
.ipo__table td:last-child { padding-right: var(--sp-5); text-align: right; }

/* The name carries the row. Body face deliberately: a company name is DATA,
   arriving from the exchange half of it in shouty caps, and setting that in a
   broken-stroke display face makes the least editorial text look like the most. */
.ipo__c-name { min-width: 15rem; }
.ipo__c-name b { display: block; font-size: 15.5px; font-weight: 600; line-height: 1.35; color: var(--on-teal-1); }
.ipo__c-name span {
  display: block; margin-top: 3px; font-size: 12.5px; letter-spacing: .02em;
  color: var(--on-teal-3);
}

/* Tabular figures so the dates and the rupee columns line up digit for digit —
   the whole reason a table beats cards here is that the eye can run down them. */
.ipo__c-when, .ipo__c-price {
  font-variant-numeric: tabular-nums; font-size: 14.5px; color: var(--on-teal-2); white-space: nowrap;
}
.ipo__c-price { color: var(--on-teal-1); font-weight: 500; }

/* The NFO board's category column. Same two-line shape as the name cell: what
   the fund IS on the first line, the SEBI family it sits in on the second. Both
   are the regulator's own words — rewording them would be inventing a taxonomy
   on top of SEBI's. */
.ipo__c-cat b { display: block; font-size: 14.5px; font-weight: 500; color: var(--on-teal-2); }
.ipo__c-cat span { display: block; margin-top: 3px; font-size: 12.5px; color: var(--on-teal-3); }

/* The AMC name links out to the fund house — where the closing date AMFI does
   not publish actually lives. Underlined on a sub-line that is otherwise plain
   text, so it is visibly a link rather than colour alone. */
.ipo__c-name a { color: var(--gold-core); text-decoration: underline; text-underline-offset: 2px; }
.ipo__c-name a:hover { color: var(--gold-highlight); }

/* The status chips are .chip / .chip--award / .chip--legal, unchanged — the
   badge family the brand already documents, not a fourth thing. Buttons were
   the other candidate and are wrong here: .btn carries cursor:pointer and a
   46px thumb target, so every row would grow half an inch to advertise a click
   that does not exist.

   The sheen is switched OFF in this table, and that is the brand's own rule
   rather than an exception to it: .chip--award is documented as the only
   continuously animating gold surface and capped at ONE per viewport. Three
   last-day rows animating at once is the slot machine the cap exists to
   prevent. The solid metal still ranks it first; the movement is what had to go. */
.ipo__table .chip { white-space: nowrap; }
.ipo__table .chip--award { animation: none; }

/* Not a leftover paragraph after the table — a recessed reference panel, which
   is what --surf-recessed and --well are for. It reads as something inset into
   the page to be consulted, rather than as more prose to be read through. */
.ipo__legend {
  margin: var(--sp-5) 0 0;
  padding: var(--sp-5) var(--sp-6);
  background: var(--surf-recessed);
  box-shadow: var(--well);
  border-radius: var(--r-md);
}
.ipo__legend-h {
  display: block; margin: 0 0 var(--sp-4);
  font-size: 12px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--gold-core);
}
.ipo__legend dl {
  display: grid; grid-template-columns: 13.5rem 1fr; gap: var(--sp-3) var(--sp-5);
  margin: 0;
}
/* Gold, so the term reads as the term and the eye can find the row it wants
   without reading the definitions. */
.ipo__legend dt { font-size: 13.5px; font-weight: 600; color: var(--gold-core); }
.ipo__legend dd { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--on-teal-2); }

.ipo__meta { display: grid; gap: var(--sp-1); margin: 0; font-size: 13px; line-height: 1.55; color: var(--on-teal-3); }

/* Below 760px four columns cannot hold. The table becomes stacked rows rather
   than scrolling sideways — a horizontal scrollbar on a phone hides the price
   band, which is one of the two things anybody came for. Each value prints its
   own column head from data-label, so the stacked view is self-describing. */
@media (max-width: 760px) {
  .ipo__table, .ipo__table tbody, .ipo__table tr, .ipo__table td, .ipo__table tbody th { display: block; }
  .ipo__table thead { display: none; }
  .ipo__table tbody tr { position: relative; padding: var(--sp-4) var(--sp-5); }
  .ipo__table td, .ipo__table tbody th { padding: 0; }
  .ipo__table tbody th:first-child { padding-left: 0; }
  .ipo__table td:last-child { padding-right: 0; text-align: left; }
  .ipo__c-name { padding-right: 6.5rem !important; }
  .ipo__c-when, .ipo__c-price { margin-top: var(--sp-3); white-space: normal; }
  .ipo__c-when::before, .ipo__c-price::before {
    content: attr(data-label) " ";
    font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--on-teal-3);
  }
  /* Pinned to the row's top-right corner: the deadline is the second thing read
     after the name, and stacked it would sit fourth. */
  .ipo__c-status { position: absolute; top: var(--sp-4); right: var(--sp-5); }
  .ipo__legend { padding: var(--sp-5); }
  .ipo__legend dl { grid-template-columns: 1fr; gap: var(--sp-1); }
  .ipo__legend dd { margin-bottom: var(--sp-3); }
}

/* ==========================================================================
   The index strip — homepage
   ==========================================================================

   A marquee is a convention this audience reads instantly: a scrolling row of
   index levels is what a financial firm looks like. It is also the only
   continuously moving thing on the page below the fold, which is why it is the
   ONLY one down there — the furrow field and the animated mark both live in the
   hero, so no viewport ever carries three.

   DIRECTION IS NEVER COLOUR ALONE. Every quote carries an arrow and a signed
   number, so the strip reads correctly in greyscale and for a red-green
   colourblind visitor. There is no green in this system and none was added:
   up is gold, down is --error, and both are redundant with the glyph.

   prefers-reduced-motion does not slow the scroll, it STOPS it and hands the
   row back as something you swipe. An animation that only slows down is still
   an animation to somebody who asked for none.
   ========================================================================== */

/* DEBOSSED, not a panel sitting on the page. --surf-recessed is the fill and
   --well is what makes it read as cut INTO the surface rather than as a darker
   stripe — the token pair the brand documents for exactly this and which, until
   now, only the slider rail actually used. */
.ticker {
  position: relative; overflow: hidden;
  border-block: 1px solid var(--edge-quiet);
  background: var(--surf-recessed);
  box-shadow: var(--well);
  padding: var(--sp-4) 0;
}
/* Fade the ends so items enter and leave rather than being chopped by the
   viewport edge. */
.ticker::before, .ticker::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 64px; z-index: 2; pointer-events: none;
}
.ticker::before { left: 0; background: linear-gradient(90deg, var(--teal-deepest), transparent); }
.ticker::after { right: 0; background: linear-gradient(270deg, var(--teal-deepest), transparent); }

.ticker__track { display: flex; width: max-content; }
/* The track holds the row TWICE and travels exactly −50%, so the second copy
   lands where the first began and the loop has no seam. Any other distance
   shows a jump once a cycle. */
/* The distance and the duration are BOTH set by the script, in pixels and
   seconds, from the measured width of one set. A percentage cannot work here:
   -50% only lands seamlessly when exactly two sets exist, and the number of
   sets varies with how many quotes came back and how wide the viewport is.
   Duration is derived from distance so the speed is constant either way. */
@media (prefers-reduced-motion: no-preference) {
  .ticker__track { animation: tick var(--tick-dur, 30s) linear infinite; }
  .ticker:hover .ticker__track { animation-play-state: paused; }
}
@keyframes tick { from { transform: translateX(0); } to { transform: translateX(calc(var(--tick-dist, 50%) * -1)); } }

/* Reduced motion: no travel, and the row becomes swipeable instead so the
   quotes past the fold are still reachable. */
@media (prefers-reduced-motion: reduce) {
  .ticker { overflow-x: auto; }
  .ticker__track { animation: none; }
}

.ticker__item {
  display: inline-flex; align-items: baseline; gap: var(--sp-3);
  padding: 0 var(--sp-6); white-space: nowrap;
  border-right: 1px solid var(--edge-quiet);
  font-variant-numeric: tabular-nums;
}
.ticker__name {
  font-size: 12px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--on-teal-3);
}
.ticker__val { font-size: 15px; font-weight: 600; color: var(--on-teal-1); }
.ticker__chg { font-size: 13.5px; font-weight: 600; color: var(--on-teal-3); }
/* The absolute move, quieter than the percentage beside it — the percentage is
   what people compare, the number is what they verify. */
.ticker__abs { font-size: 12.5px; color: var(--on-teal-3); }
.ticker__chg.is-up { color: var(--gold-core); }
.ticker__chg.is-down { color: var(--error); }

/* The honest empty state. The strip is decoration on a marketing page, so a
   dead feed removes it rather than announcing an outage nobody asked about —
   the opposite of the IPO board, where the list IS the content and silence
   would be a lie. The standing "delayed levels, not a recommendation" line
   lives in the footer legal band with the other standing warnings, not under
   the strip, where it read as a caption on a decoration. */
.ticker[hidden] { display: none; }



/* ==========================================================================
   .install — "keep these on your phone"
   ==========================================================================

   A bar, pinned to the bottom of the calculators index, shown once and
   remembered when dismissed. It is the one interstitial on this site, and it
   earns that by appearing only where it can be acted on: Android after Chrome
   offers the install, iOS after twenty seconds with an instruction instead of
   a button.

   The close control is a real 44px target, not a 12px grey cross. A prompt
   that is hard to dismiss is the thing people close the tab over.
   ========================================================================== */

.install {
  position: fixed; inset: auto 0 0 0; z-index: 40;
  padding: var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom));
  /* An OPAQUE ground. --surf-raised is rgba(255,255,255,.085), which is a wash
     designed to sit on a card, not a background for something floating over
     the page — the first build let the calculator list read straight through
     the text. */
  background: #04181B;
  border-top: 1px solid var(--edge-gold);
  box-shadow: 0 -8px 30px -12px rgba(0, 0, 0, .6);
}
.install[hidden] { display: none; }
.install__in {
  display: flex; align-items: center; gap: var(--sp-4);
  max-width: 1092px; margin-inline: auto;
}
.install__ico { flex: none; color: var(--gold-core); display: grid; place-items: center; }
.install__text { margin: 0; flex: 1 1 auto; font-size: 13px; line-height: 1.45; color: var(--on-teal-2); }
.install__text b { display: block; color: var(--on-teal-1); font-size: 14px; }

/* Only the line the platform can honour. */
.install [data-install-when] { display: none; }
.install[data-install="button"] [data-install-when="button"],
.install[data-install="ios"]    [data-install-when="ios"] { display: inline; }
/* No install API on iOS, so no button that pretends there is one. */
.install[data-install="ios"] .install__go { display: none; }

.install__go { flex: none; white-space: nowrap; }
.install__x {
  flex: none;
  width: 44px; height: 44px;
  font-size: 22px; line-height: 1;
  color: var(--on-teal-3); background: none; border: 0; cursor: pointer;
  border-radius: var(--r-pill);
}
.install__x:hover { color: var(--on-teal-1); background: var(--surf-recessed); }
.install__x:focus-visible { outline: 2px solid var(--gold-highlight); outline-offset: 2px; }

@media (max-width: 560px) {
  .install__in { flex-wrap: wrap; }
  .install__text { flex: 1 1 100%; order: 1; }
  .install__ico { order: 0; }
  .install__x { order: 0; margin-left: auto; }
  .install__go { order: 2; width: 100%; }
}
