/* ==========================================================================
   RAMYA · BORDERLESS LIGHT
   The design system for the public site. A rebuilt page loads this file and
   nothing else: not css/styles.css, not css/ramya-2026.css. The portal keeps
   its own dark stylesheet and never loads this one.

   THE RULES THIS FILE EXISTS TO ENFORCE
   1. No containers. Nothing here draws a card, a panel, a border or a shadow.
      There is exactly ONE solid object: the primary action pill, at most one
      to a screen (section 7). Because nothing else is solid, it is the most
      important thing on any screen it appears on.
   2. No dividers. Screens are separated by empty space and by the light
      moving, never by a line or a block of background.
   3. Light, not gradient. One light on screen at a time. It is a baked
      bitmap posed with transform and opacity (see tools/light/make_light.py
      for why it is not a CSS gradient). It always bleeds off the viewport.
   4. Emptiness is the layout. A screen holds one idea.
   5. Scroll motion touches transform and opacity only. There is no filter,
      no backdrop-filter and no box-shadow anywhere in this file.

   CONTENTS
    1. Fonts       2. Tokens      3. Ground       4. The light
    5. Screens     6. Type        7. Actions      8. Entrances
    9. The one pinned scene      10. Image slots     11. Quiet pieces
   12. Header    13. Menu       14. Footer      15. After the hero
   16. Reduced motion
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. FONTS
   Self hosted, variable, latin first. The Google Fonts stylesheet this
   replaces was a render blocking request to a second origin, then a font
   fetch from a third: two extra connections on 4G before any text could use
   its real face. Same origin files can be preloaded by a stable URL.

   Latin only. The rupee sign has two tiny files of its own, further down.
   -------------------------------------------------------------------------- */
@font-face{
  font-family:"Cormorant Garamond";font-style:normal;font-weight:300 700;font-display:swap;
  src:url("../assets/fonts/cormorant-garamond-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Cormorant Garamond";font-style:italic;font-weight:300 700;font-display:swap;
  src:url("../assets/fonts/cormorant-garamond-italic-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Inter";font-style:normal;font-weight:300 600;font-display:swap;
  src:url("../assets/fonts/inter-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* THE RUPEE SIGN. The copy is plain English; the one character it prints from
   outside basic latin is U+20B9, and that glyph lives in the latin-ext
   subsets: 33 KB of Cormorant and 84 KB of Inter, fetched by every page that
   shows a price. These two files hold that glyph and nothing else, 1.4 KB
   each, with the weight axis kept (tools/light/make_rupee_fonts.py). */
@font-face{
  font-family:"Cormorant Garamond";font-style:normal;font-weight:300 700;font-display:swap;
  src:url("../assets/fonts/cormorant-garamond-rupee.woff2") format("woff2");
  unicode-range:U+20B9;
}
@font-face{
  font-family:"Inter";font-style:normal;font-weight:300 600;font-display:swap;
  src:url("../assets/fonts/inter-rupee.woff2") format("woff2");
  unicode-range:U+20B9;
}

/* STAND INS. For the few hundred ms before a webfont lands, its text is set in
   a system face. Left alone that face is a different width, so when the real
   one arrives every centred line snaps sideways: a flash, and a layout shift.
   Each stand in below is a system face resized to occupy the space the webfont
   will. The numbers are measured, not eyeballed: the width of this site's own
   copy in the webfont divided by the same copy in the system face, taken in
   the browser so kerning counts (Cormorant is kerned hard; without it the
   figure is 0.8 too high), and the webfont's ascent and descent rescaled to
   match. The Inter to Arial figure reproduces next/font's published one.
   One number cannot fit every line: single headlines sit within about 2 of
   the mean, which at 80px is a few px of width. The tagline moves under 3.
   Georgia and Arial cover Apple and Windows. Android has neither, so local()
   fails there, the face is skipped, and the "Droid" pair is tried next. */
@font-face{font-family:"Cormorant Stand In";font-style:normal;src:local("Georgia");size-adjust:88.6%;ascent-override:104.3%;descent-override:32.4%;line-gap-override:0%;}
@font-face{font-family:"Cormorant Stand In";font-style:italic;src:local("Georgia Italic"),local("Georgia-Italic");size-adjust:78.8%;ascent-override:117.3%;descent-override:36.4%;line-gap-override:0%;}
@font-face{font-family:"Cormorant Stand In Droid";font-style:normal;src:local("Noto Serif");size-adjust:83.1%;ascent-override:111.2%;descent-override:34.5%;line-gap-override:0%;}
@font-face{font-family:"Cormorant Stand In Droid";font-style:italic;src:local("Noto Serif Italic"),local("NotoSerif-Italic");size-adjust:74.9%;ascent-override:123.4%;descent-override:38.3%;line-gap-override:0%;}
@font-face{font-family:"Inter Stand In";src:local("Arial");size-adjust:107%;ascent-override:90.6%;descent-override:22.6%;line-gap-override:0%;}
@font-face{font-family:"Inter Stand In Droid";src:local("Roboto");size-adjust:107.5%;ascent-override:90.1%;descent-override:22.5%;line-gap-override:0%;}
/* Capitals are their own case. Arial's are wide for its size and Inter's are
   not, so tracked uppercase (every action and label here) needs 99.5 where
   mixed case needs 107. With the mixed case figure the CTA moved 7.6px on
   swap. Roboto's proportions track Inter's in both cases, so it needs no twin. */
@font-face{font-family:"Inter Caps Stand In";src:local("Arial");size-adjust:99.5%;ascent-override:97.4%;descent-override:24.3%;line-gap-override:0%;}


/* --------------------------------------------------------------------------
   2. TOKENS
   -------------------------------------------------------------------------- */
:root{
  /* GROUND. Warm sand, never white. The brightest thing on any screen is the
     core of the light, and that is an off white, not #fff. */
  --sand:#ECE6DA;

  /* GOLD is light and accent. It is never a background block: nothing sits
     on a gold surface. The ONE solid gold thing allowed is the primary action
     pill (section 7), which is an object, not a surface. */
  --gold-rgb:168,133,76;
  --gold:rgb(var(--gold-rgb));
  --gold-solid:#B8965A;   /* the pill. Lighter than --gold so forest type clears AA on it */
  --on-gold:#16271F;      /* the pill's type: forest, a step deeper. 5.7:1 on --gold-solid */

  /* FOREST is type and small accents. It is never a fill either. */
  --forest-rgb:31,58,50;
  --forest:rgb(var(--forest-rgb));
  --ink:rgba(var(--forest-rgb),.94);        /* display type */
  --ink-quiet:rgba(var(--forest-rgb),.74);  /* body: muted forest. 4.9:1 on bare sand; .70 was 4.38, under AA */
  --ink-faint:rgba(var(--forest-rgb),.56);  /* incidental only: a scroll cue. Never for anything that must be read */

  --serif:"Cormorant Garamond","Cormorant Stand In","Cormorant Stand In Droid",Georgia,"Times New Roman",serif;
  --sans:"Inter","Inter Stand In","Inter Stand In Droid",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
  --sans-caps:"Inter","Inter Caps Stand In","Inter Stand In Droid",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;

  /* TYPE SCALE. There are no boxes to carry hierarchy, so the gap between
     display and body carries all of it. Display is 32 to 44 on a phone and
     48 to 80 on a desktop; body stays small and does not scale with it.
     Phone sizes are tied to vw so a line that fits at 390 fits at 320. */
  --d1:clamp(2rem,11.28vw,2.75rem);      /* 32 to 44 */
  --d2:clamp(2rem,9.75vw,2.375rem);      /* 32 to 38 */
  --body:.9375rem;                       /* 15 */
  --small:.8125rem;                      /* 13 */
  --label:.6875rem;                      /* 11 */

  --gutter:1.75rem;

  /* MOTION. Entrances are 400 to 600ms, ease out, 16 to 24px of travel. */
  --ease-out:cubic-bezier(.22,.61,.36,1);
  --enter:540ms;
  --travel:20px;
  --stagger:110ms;
  /* The light is slower than anything else on the page, always. */
  --ease-light:cubic-bezier(.4,0,.2,1);
  --light-shift:2200ms;
}
@media(min-width:30em){
  :root{
    --d1:clamp(2.75rem,1.61rem + 3.79vw,5rem);     /* 44 at 480, 80 at 1440 */
    --d2:clamp(2.375rem,1.59rem + 2.62vw,4rem);    /* 38 at 480, 64 at 1440 */
    --gutter:clamp(1.75rem,6vw,7rem);
  }
}
@media(min-width:60em){
  :root{--body:1rem;--small:.875rem;--label:.75rem;}
}


/* --------------------------------------------------------------------------
   3. GROUND
   The sand is painted on the root element so it reaches the canvas. The body
   stays transparent on purpose: the light sits at z-index -1, which is above
   the canvas and below every piece of content, so content floats IN the
   light and there is never a surface for it to sit ON.
   -------------------------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box;}
html{
  background:var(--sand);
  color-scheme:light;
  -webkit-text-size-adjust:100%;text-size-adjust:100%;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  scroll-behavior:smooth;
}
body{
  margin:0;
  font-family:var(--sans);font-size:var(--body);font-weight:400;line-height:1.75;
  color:var(--ink-quiet);
  overflow-x:hidden;overflow-x:clip;
}
h1,h2,h3,p{margin:0;font-weight:inherit;}
a{color:inherit;}
img{display:block;max-width:100%;}
::selection{background:rgba(var(--gold-rgb),.24);}
:focus-visible{outline:1px solid var(--gold);outline-offset:6px;}


/* --------------------------------------------------------------------------
   4. THE LIGHT
   One fixed light for the whole page. Screens do not own a light each: as a
   new screen reaches the middle of the viewport, js/light.js writes its name
   to <html data-light>, and the same light travels to the new pose. That is
   what "a shift in light" between sections means here, and it is why two
   lights can never be on screen together.

   FOUR ELEMENTS, ONE JOB EACH
   .ground       the sand, with a grain of about one level baked in. A
                 falloff this soft moves one 8 bit level at a time, and in the
                 tail those steps line up into long faint contour lines. The
                 grain gives them nothing clean to run along. It is a mask and
                 not a true dither; tools/light/make_light.py says why. Fixed,
                 opaque and promoted: rastered once, never again, and cheap to
                 composite because nothing is blended.
   .light        the fixed window. Sized in vw and vh, which do not change when
                 a phone's address bar collapses, so scrolling never relays
                 out or re rasters it.
   .light__pose  where the light is. transform and opacity, transitioned.
   .light__src   the bitmap. It drifts forever on a keyframe, transform only.
                 Pose and drift sit on different elements so neither one
                 fights the other for the transform property.

   A POSE IS SIX NUMBERS
   --lx --ly   where the centre of the light is, from the viewport centre
   --lr        the angle of the shaft
   --lsx --lsy its length and its width, as a multiple of 100vmax
   --lo        how strong it is
   The centre always sits beyond an edge of the viewport and the shaft points
   in, so the light enters the screen rather than sitting in it, and both of
   its soft edges are on screen: that is what makes it a shaft and not a glow
   in a corner.
   -------------------------------------------------------------------------- */
.ground{
  position:fixed;top:0;left:0;z-index:-2;
  width:100vw;height:100vh;
  background:var(--sand) url("../assets/light/ground.png?v=1") 0 0/128px 128px repeat;
  pointer-events:none;
  contain:strict;will-change:transform;
}
.light{
  position:fixed;top:0;left:0;z-index:-1;
  width:100vw;height:100vh;
  overflow:hidden;pointer-events:none;
  contain:strict;
}
.light__pose{
  position:absolute;top:50%;left:50%;width:0;height:0;
  transform:translate3d(var(--lx),var(--ly),0) rotate(var(--lr)) scale(var(--lsx),var(--lsy));
  opacity:var(--lo);
  transition:transform var(--light-shift) var(--ease-light),opacity var(--light-shift) var(--ease-light);
  will-change:transform,opacity;
}
.light__src{
  position:absolute;top:-50vmax;left:-50vmax;
  width:100vmax;height:100vmax;max-width:none;
  animation:light-in 2400ms var(--ease-out) backwards,light-drift 38s ease-in-out infinite alternate;
  will-change:transform,opacity;
}
@keyframes light-in{from{opacity:0;}to{opacity:1;}}
/* The drift starts from rest, which is also what reduced motion shows, and it
   starts at time zero: a drift that began late from anywhere but rest would
   make the light hop at the moment it began. */
@keyframes light-drift{
  from{transform:none;}
  to{transform:translate3d(2.6%,2%,0) rotate(2.4deg) scale(1.06);}
}

/* POSES. Portrait first.
   dawn  enters over the top right and falls to the lower left.
   noon  enters from above and falls straight down.
   dusk  enters over the top left and falls to the lower right.

   THE THREE ARE ONE ARC, and that is a rule, not a coincidence. A transition
   interpolates each number on its own, so the light really does visit every
   pose between two poses. All three waists sit on one line above the top
   edge, so the waist, the one bright narrow part, never crosses the screen on
   the way. And the angles run one way, -45 to -90 to -135, so dawn reaches
   dusk by swinging through vertical like the sun, and passes through noon
   exactly. Written as +45 instead of -135 dusk would look identical at rest
   (the bitmap is the same turned half way round) but the light would reach it
   by swinging through horizontal, a searchlight raking across the headline.
   A pose on another edge of the screen must not be reached by travelling. */
:root,:root[data-light="dawn"]{--lx:35vw;--ly:-58vh;--lr:-61deg;--lsx:1.9;--lsy:.87;--lo:.95;}
:root[data-light="noon"]{--lx:0vw;--ly:-58vh;--lr:-90deg;--lsx:1.75;--lsy:.95;--lo:.88;}
:root[data-light="dusk"]{--lx:-35vw;--ly:-58vh;--lr:-119deg;--lsx:1.9;--lsy:.87;--lo:.85;}
/* wane and ebb are dusk, failing. Same place, same angle, less of it: the light
   does not move through the old way, it goes out. Then noon brings it back. */
:root[data-light="wane"]{--lx:-35vw;--ly:-58vh;--lr:-119deg;--lsx:1.9;--lsy:.87;--lo:.5;}
:root[data-light="ebb"]{--lx:-35vw;--ly:-58vh;--lr:-119deg;--lsx:1.9;--lsy:.87;--lo:.2;}
@media(min-aspect-ratio:1/1){
  :root,:root[data-light="dawn"]{--lx:36.8vw;--ly:-60vh;--lr:-45.5deg;--lsx:2.19;--lsy:.83;--lo:.95;}
  :root[data-light="noon"]{--lx:0vw;--ly:-60vh;--lr:-90deg;--lsx:1.5;--lsy:1.1;--lo:.88;}
  :root[data-light="dusk"]{--lx:-36.8vw;--ly:-60vh;--lr:-134.5deg;--lsx:2.19;--lsy:.83;--lo:.85;}
  :root[data-light="wane"]{--lx:-36.8vw;--ly:-60vh;--lr:-134.5deg;--lsx:2.19;--lsy:.83;--lo:.5;}
  :root[data-light="ebb"]{--lx:-36.8vw;--ly:-60vh;--lr:-134.5deg;--lsx:2.19;--lsy:.83;--lo:.2;}
}


/* --------------------------------------------------------------------------
   5. SCREENS
   One idea per screen. A screen is at least one viewport tall and holds a
   single .idea, which is narrow on purpose: the emptiness is structural, not
   something each page has to remember to leave.

   svh, not vh. On a phone 100vh is the viewport with the address bar hidden,
   which is taller than what is visible on arrival, so the next screen would
   be pushed below a fold that the visitor cannot see yet, and the hero's own
   foot would be cut off. 100svh is exactly the visible area on arrival, so
   the hero owns the first viewport and nothing peeks in under it. It is also
   static, unlike dvh, so the screen does not resize mid scroll.
   -------------------------------------------------------------------------- */
.screen{
  position:relative;
  min-height:100vh;min-height:100svh;
  display:grid;align-content:center;justify-items:center;row-gap:2.75rem;
  padding:6.5rem var(--gutter);
  text-align:center;
}
.idea{width:100%;max-width:34rem;}
.idea>*+*{margin-top:1.5rem;}
.idea>*+.act{margin-top:2.25rem;}      /* the action is an object, and gets an object's room */
@media(min-width:60em){
  .idea{max-width:46rem;}
  .idea>*+*{margin-top:2rem;}
  .idea>*+.act{margin-top:2.75rem;}
}
/* A phone on its side is about 390 tall. Give the height back to the idea. */
@media(max-height:32em) and (orientation:landscape){
  .screen{padding-top:5rem;padding-bottom:3.5rem;}
}


/* --------------------------------------------------------------------------
   6. TYPE
   -------------------------------------------------------------------------- */
.display{
  font-family:var(--serif);font-weight:400;
  font-size:var(--d1);line-height:1.06;letter-spacing:-.012em;
  color:var(--ink);
}
.display--2{font-size:var(--d2);line-height:1.1;}
.display em{font-style:italic;color:var(--gold);}
/* A .line is one sentence on its own line. It wraps if it has to, and
   balances when the browser can. */
.display{text-wrap:balance;}
.display .line{display:block;}
/* .display--set is for a headline whose lines are known to fit: the frozen
   tagline. Its lines never wrap. --d1 is tied to vw on a phone, so a line
   that fits at 390 fits at 320 in its real face; what nowrap buys is that the
   wider FALLBACK face cannot wrap to a third line for the few hundred ms
   before the webfont lands, which would shove everything under it when it
   did. Above the fold that is a layout shift. Do not use it for a line that
   has not been measured at 320. */
.display--set .line{white-space:nowrap;}

.lede{max-width:24rem;margin-left:auto;margin-right:auto;color:var(--ink-quiet);text-wrap:balance;}
@media(min-width:60em){.lede{max-width:27rem;}}

.idea .label{color:var(--ink-quiet);}
.label{
  font-family:var(--sans-caps);
  font-size:var(--label);font-weight:500;letter-spacing:.22em;text-transform:uppercase;
  color:var(--ink-faint);
}


/* --------------------------------------------------------------------------
   7. ACTIONS
   THE PILL IS THE ONLY SOLID OBJECT IN THE ENTIRE LAYOUT. That is the point of
   it: with no cards, panels or fills anywhere else, the one deliberate solid
   thing on a screen is the most important thing on it. So:

     * .cta is the primary action. A solid gold pill, forest type. ONE PER
       SCREEN AT MOST. The header never carries one, because the hero does.
     * Nothing else may be solid. Not a second button, not the WhatsApp mark,
       not the cookie line, not a badge. If a screen needs a second action it
       is a .more: a line of forest type with a gold rule under it.

   No shadow and no border on the pill: a flat solid reads as an object on
   this ground without either. Hover and press move it (transform) and never
   repaint it.

   The gold here is --gold-solid, not --gold. Forest type on the brand gold is
   3.6:1, under AA at this size. On --gold-solid with --on-gold it is 5.7:1.
   -------------------------------------------------------------------------- */
.cta{
  display:inline-block;
  /* Small. On a 320 phone the type and the side padding give a little, so the
     pill stays under about two thirds of the screen; the height never does,
     because 48px is the tap target. */
  padding:calc((3rem - 1em) / 2) clamp(1.2rem,5.2vw,1.75rem);border-radius:999px;   /* always exactly 48px tall */
  background:var(--gold-solid);color:var(--on-gold);
  font-family:var(--sans-caps);
  font-size:clamp(.6875rem,3.1vw,.75rem);font-weight:600;line-height:1;letter-spacing:.14em;text-transform:uppercase;text-decoration:none;white-space:nowrap;
  -webkit-tap-highlight-color:transparent;
  transition:transform 320ms var(--ease-out);
}
@media(min-width:60em){.cta{padding-left:2rem;padding-right:2rem;font-size:.8125rem;}}
@media(hover:hover){.cta:hover{transform:translate3d(0,-2px,0);}}
.cta:active{transform:scale(.97);transition-duration:90ms;}
.cta:focus-visible{outline-offset:4px;}

.more{
  position:relative;display:inline-block;
  padding:1.05rem .25rem;
  font-family:var(--sans-caps);
  font-size:var(--small);font-weight:500;letter-spacing:.2em;text-transform:uppercase;text-decoration:none;
  color:var(--forest);
  -webkit-tap-highlight-color:transparent;
  transition:opacity 200ms var(--ease-out);
}
.more::after{
  content:"";position:absolute;left:.25rem;right:.25rem;bottom:.7rem;height:1px;
  background:var(--gold);
  transform-origin:0 50%;
  transition:transform 420ms var(--ease-out);
}
@media(hover:hover){
  .more:hover::after{transform:scaleX(.42);}
}
.more:active{opacity:.55;transition-duration:90ms;}

.quiet{
  font-size:var(--small);letter-spacing:.04em;text-decoration:none;color:var(--ink-quiet);
  transition:opacity 200ms var(--ease-out);
}
@media(hover:hover){.quiet:hover{opacity:.6;}}
.quiet:active{opacity:.5;}


/* --------------------------------------------------------------------------
   8. ENTRANCES
   Fade and rise, once. 540ms, ease out, 20px. transform and opacity only.

   .rise inside the first screen plays from CSS alone the moment the page
   renders, so the hero never waits on a script. Everywhere else js/light.js
   arms the element only if it is still below the fold when the script runs,
   and only by adding the .will-rise class itself: if the script never loads,
   nothing was ever hidden.
   -------------------------------------------------------------------------- */
/* backwards, not both. The end state is the element's own resting state, so
   nothing needs holding once the entrance is over, and an animation that is
   still "filling forwards" keeps its element promoted to a compositor layer
   for the life of the page. Measured: four hero layers that never went away. */
/* The headline (--i:0) starts with no delay at all. Its first visible frame IS
   the page's Largest Contentful Paint, so every ms of pause before it is a ms
   of LCP. Phase 1 held it back 160ms for the look of it and paid exactly that. */
.screen--first .rise{
  animation:rise var(--enter) var(--ease-out) backwards;
  animation-delay:calc(var(--i,0) * var(--stagger));
}
/* from .01, NOT from 0. It looks the same, and it is measured differently.
   Chrome does not count an element at opacity 0 as painted, and a compositor
   animation never repaints, so a headline that fades in from 0 is first seen
   by the LCP metric when the animation ENDS and the layer is dropped: first
   paint plus the full 540ms, for type that was readable 150ms in. Measured:
   the same page reported 1280ms and 1930ms depending only on whether some
   other repaint happened to land mid fade. From .01 it is counted at first
   paint, every time. */
@keyframes rise{
  from{opacity:.01;transform:translate3d(0,var(--travel),0);}
  to{opacity:1;transform:none;}
}
.will-rise{opacity:0;transform:translate3d(0,var(--travel),0);}
.will-rise.has-risen{
  opacity:1;transform:none;
  transition:opacity var(--enter) var(--ease-out),transform var(--enter) var(--ease-out);
  transition-delay:calc(var(--i,0) * var(--stagger));
}


/* --------------------------------------------------------------------------
   9. THE ONE PINNED SCENE
   The homepage is allowed exactly one, and this is it: the old way, said one
   line at a time in the same place, with the light failing as the lines pass
   and coming back at the turn.

   Nothing here follows the scroll frame by frame. The stage is pinned by
   position:sticky, which the compositor does by itself. Under it is a column
   of empty steps, each most of a screen tall; whichever step is crossing the
   middle of the viewport names the line (and the light), through the same
   IntersectionObserver the rest of the page already uses. A line arrives and
   leaves on a 600ms transition of opacity and transform. There is no scrub,
   no scroll listener and no library, so there is nothing to stutter.

   WITHOUT SCRIPT, OR WITH REDUCED MOTION, it is not pinned at all: .is-live is
   never added and the four lines are simply four lines, one under the other.
   Hidden lines are opacity 0 and NOT visibility:hidden, so a screen reader
   reads all four whatever the scroll position.
   -------------------------------------------------------------------------- */
.scene{position:relative;}
.scene__stage{
  display:grid;justify-items:center;row-gap:3.5rem;
  padding:6.5rem var(--gutter);
  text-align:center;
}
.scene__line{width:100%;max-width:34rem;}
.scene__steps{display:none;}
.scene.is-live .scene__stage{
  position:sticky;top:0;
  height:100vh;height:100svh;margin-bottom:-100vh;margin-bottom:-100svh;
  align-content:center;row-gap:0;padding-top:0;padding-bottom:0;
}
.scene.is-live .scene__line{
  grid-area:1/1;
  opacity:0;transform:translate3d(0,var(--travel),0);
  transition:opacity 600ms var(--ease-out),transform 600ms var(--ease-out);
}
.scene.is-live .scene__line.is-past{transform:translate3d(0,calc(var(--travel) * -1),0);}
.scene.is-live .scene__line.is-now{opacity:1;transform:none;}
.scene.is-live .scene__steps{display:block;}
.scene__steps span{display:block;height:80vh;height:80svh;}
@media(min-width:60em){.scene__line{max-width:52rem;}}


/* --------------------------------------------------------------------------
   10. IMAGE SLOTS
   Photography does not exist yet. A slot holds the place of one shot, at that
   shot's aspect ratio, and names it in data-shot so the shoot has a list.
   The layout is built as if the photographs were already there.

   An image never has an edge. The slot is feathered by a mask, so whatever is
   in it, a warm fill today and a photograph later, dissolves into the page and
   reads as something the light is falling on, not as a rectangle placed on a
   surface. Drop an <img> inside and it is feathered the same way.
   The mask is static and the slot is never animated by itself (its entrance
   is on the wrapper), so it is rastered once.
   -------------------------------------------------------------------------- */
.shot{
  display:block;width:min(62vw,18rem);aspect-ratio:4/5;margin:0 auto;
  background:rgba(var(--gold-rgb),.17);
  -webkit-mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 46%,rgba(0,0,0,0) 100%);
          mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 46%,rgba(0,0,0,0) 100%);
}
.shot--wide{aspect-ratio:3/2;width:min(82vw,26rem);}
/* On a phone a screen that already holds a list has no room for a shot as
   well; beside the list on a desktop it does. */
.shot--desk{display:none;}
@media(min-width:60em){.shot--desk{display:block;}}
.shot img{width:100%;height:100%;object-fit:cover;}
/* An idea with a shot. One column on a phone, shot first. Side by side on a
   desktop, and there the type is set left: centred type beside an image has
   nothing to be centred on. */
.split{display:grid;justify-items:center;row-gap:2.5rem;width:100%;max-width:34rem;}
@media(min-width:60em){
  .split{grid-template-columns:minmax(0,5fr) minmax(0,6fr);align-items:center;column-gap:5.5rem;max-width:66rem;text-align:left;}
  .split--flip>.shot{order:2;}
  .split .shot{width:min(100%,24rem);}
  .split .shot--wide{width:100%;}
  .split .idea{max-width:30rem;justify-self:start;}
  .split--flip .idea{justify-self:end;}
  .split .lede{margin-left:0;margin-right:0;}
  .split .more{margin-left:-.25rem;}     /* its type, not its padding, meets the column edge */
}


/* --------------------------------------------------------------------------
   11. QUIET PIECES
   What a screen may hold under its headline when one sentence is not the
   right shape: a list, a run of steps, a few quotations, the three lengths,
   a few questions. None of them has a box, a rule or a fill. They are set at
   body size on purpose: the only large thing on a screen is its headline.
   -------------------------------------------------------------------------- */
/* margin-bottom only. A plain margin:0 here comes later than .idea>*+* at the
   same weight and wipes the gap above the list: it sat flush on its headline. */
.list{list-style:none;margin-bottom:0;padding:0;display:grid;row-gap:.8rem;color:var(--ink);}
.note{color:var(--ink);}

.steps{list-style:none;margin:0;padding:0;display:grid;row-gap:1.75rem;width:100%;max-width:34rem;}
.steps li{display:grid;row-gap:.35rem;}
.steps .label{color:var(--ink-quiet);}     /* a marker has to be read, so it is not faint */
.steps b{font-weight:500;color:var(--ink);}
@media(min-width:60em){
  .steps{grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);column-gap:2.5rem;max-width:72rem;}
  /* one marker runs to two lines; hold two for all of them and sit each on the
     bottom, so the five titles share a baseline */
  .steps li{grid-template-rows:3.1em auto;}
  .steps .label{align-self:end;}
}

.quotes{display:grid;row-gap:3.25rem;width:100%;max-width:34rem;}
.quote{margin:0;}
.quote p{color:var(--ink);text-wrap:balance;}
.quote footer{margin-top:.9rem;font-size:var(--small);color:var(--ink-quiet);}
.stars{display:block;margin-bottom:.35rem;color:var(--gold);letter-spacing:.24em;}
@media(min-width:60em){
  .quotes{grid-template-columns:repeat(3,minmax(0,1fr));column-gap:4.5rem;max-width:72rem;align-items:start;}
}

.lengths{display:grid;row-gap:4.5rem;width:100%;max-width:72rem;}
.length{display:grid;justify-items:center;row-gap:.45rem;}
.length .label{color:var(--ink-quiet);}
/* min-height holds the line open before js/pricing.js writes the price into
   it, so a visitor landing on #membership never sees the screen jump. */
.length__price{font-family:var(--serif);font-size:var(--d2);line-height:1.15;min-height:1.15em;color:var(--ink);}
.length__term,.length__has{max-width:17rem;font-size:var(--small);color:var(--ink-quiet);min-height:1.75em;text-wrap:balance;}
.length__term .line{display:block;}
.length .act{margin-top:1.1rem;}
@media(min-width:60em){
  .lengths{grid-template-columns:repeat(3,minmax(0,1fr));column-gap:3rem;align-items:start;}
  /* the monthly column has no saving line; hold the row open so the three
     actions line up */
  .length__term,.length__has{min-height:3.5em;}
}

/* Questions are native <details>: they open without a script, and the answer
   appears at once. Height is never animated. */
.faq{display:grid;width:100%;max-width:34rem;text-align:left;}
.faq summary{
  display:flex;align-items:baseline;justify-content:space-between;column-gap:1.25rem;
  padding:.95rem 0;list-style:none;cursor:pointer;
  font-weight:500;color:var(--ink);
  -webkit-tap-highlight-color:transparent;
}
.faq summary::-webkit-details-marker{display:none;}
.faq summary::after{content:"+";flex:none;font-weight:400;color:var(--gold);transition:transform 300ms var(--ease-out);}
.faq details[open] summary::after{transform:rotate(45deg);}
.faq details p{padding:0 2rem 1.1rem 0;}


/* --------------------------------------------------------------------------
   12. HEADER
   Near invisible. A small wordmark and a few quiet words, with no bar, no
   pill, no fill and no button: the primary action lives in the hero, and a
   second pill up here would make two solid objects on the first screen.

   It is position:absolute, so it belongs to the top of the page and scrolls
   away with it. That is deliberate. A header that stays needs a surface
   behind it, or its words collide with the page passing underneath, and a
   surface is exactly what this system does not have. (The old one was a
   frosted pill with backdrop-filter, the main source of scroll jank.) It
   costs nothing per frame because it does nothing per frame.

   Under 60em the links fold into "Menu". The button ships with the hidden
   attribute and js/light.js reveals it, so without script there is no dead
   control; every link is also in the footer.
   -------------------------------------------------------------------------- */
.top{
  position:absolute;top:0;left:0;right:0;z-index:3;
  display:flex;align-items:center;justify-content:space-between;
  padding:1rem var(--gutter);
}
.top__mark{display:block;padding:.6rem 0;-webkit-tap-highlight-color:transparent;}
.top__mark img{height:30px;width:auto;}
.top__nav{display:none;}
.top__nav a,.top__menu{
  font-family:var(--sans);font-size:var(--small);font-weight:400;letter-spacing:.04em;line-height:1;
  color:var(--ink-quiet);text-decoration:none;
  transition:opacity 200ms var(--ease-out);
}
.top__menu{
  display:inline-block;margin:0 -.25rem 0 0;padding:1.1rem .25rem;
  background:none;border:0;border-radius:0;cursor:pointer;
  -webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent;
}
.top__menu[hidden]{display:none;}
@media(hover:hover){.top__nav a:hover,.top__menu:hover{opacity:.55;}}
.top__nav a:active,.top__menu:active{opacity:.45;}
/* TEST VARIANT, asked for before scrolling away is accepted: the same header,
   fixed, with no surface at all, fading once the hero has gone. It is switched
   on by ?header=float (js/light.js) so both can be judged on a real phone. */
html[data-header="float"] .top{position:fixed;transition:opacity 400ms var(--ease-out);}
html[data-header="float"].past-hero:not(.menu-open) .top{opacity:.5;}
@media(min-width:60em){
  .top{padding-top:1.5rem;}
  .top__mark img{height:34px;}
  .top__nav{display:flex;align-items:center;gap:2.5rem;}
  .top__nav a{padding:.75rem 0;}
  .top__menu{display:none;}
}


/* --------------------------------------------------------------------------
   13. MENU
   The menu is not a sheet laid over the page. When it opens, the page's own
   content steps out (main and the footer fade to nothing) and the links step
   in over the same ground and the same light, which swings to noon above
   them. So it is one more screen of this system, with no surface of its own.
   It may carry the screen's one pill.
   -------------------------------------------------------------------------- */
.menu{
  position:fixed;top:0;left:0;right:0;bottom:0;z-index:2;
  display:grid;align-content:center;justify-items:center;gap:2.5rem;
  padding:5.5rem var(--gutter) 2.5rem;
  text-align:center;
  overflow:auto;overscroll-behavior:contain;
  opacity:0;visibility:hidden;
  transition:opacity 360ms var(--ease-out),visibility 0s linear 360ms;
}
.menu__links{display:grid;gap:.15rem;}
.menu__links a{
  padding:.3rem 1rem;
  font-family:var(--serif);font-size:clamp(1.75rem,8.4vw,2.25rem);line-height:1.3;
  color:var(--ink);text-decoration:none;
  -webkit-tap-highlight-color:transparent;
}
.menu__links a:active{opacity:.5;}
main,.foot{transition:opacity 300ms var(--ease-out),visibility 0s linear 0s;}
html.menu-open{overflow:hidden;}
html.menu-open .top{position:fixed;}
html.menu-open .menu{opacity:1;visibility:visible;transition-delay:0s;}
html.menu-open main,html.menu-open .foot{opacity:0;visibility:hidden;transition-delay:0s,300ms;}
html.menu-open .wa,html.menu-open .consent{opacity:0;visibility:hidden;}
@media(min-width:60em){.menu{display:none;}}


/* --------------------------------------------------------------------------
   14. FOOTER
   The same restraint at the other end. No rule above it and no block under
   it: it is separated from the last screen by space alone. The disclaimer is
   legal text and is set verbatim, small, never cut.
   -------------------------------------------------------------------------- */
.foot{
  display:grid;justify-items:center;gap:2rem;
  padding:6rem var(--gutter) calc(5rem + var(--consent-h,0px));   /* 5rem clears the WhatsApp mark */
  text-align:center;
}
.foot__mark{display:block;padding:.5rem 0;}
.foot__mark img{height:26px;width:auto;}
.foot__nav{display:flex;flex-wrap:wrap;justify-content:center;gap:0 1.75rem;max-width:36rem;}
.foot__nav a,.foot__nav button{
  padding:.8rem 0;margin:0;
  font-family:var(--sans);font-size:var(--small);font-weight:400;letter-spacing:.04em;line-height:1.5;
  color:var(--ink-quiet);text-decoration:none;
  background:none;border:0;border-radius:0;cursor:pointer;
  -webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent;
  transition:opacity 200ms var(--ease-out);
}
.foot__nav button[hidden]{display:none;}
@media(hover:hover){.foot__nav a:hover,.foot__nav button:hover{opacity:.55;}}
.foot__note,.foot__copy{font-size:.75rem;line-height:1.75;color:var(--ink-quiet);}
.foot__note{max-width:30rem;text-wrap:balance;}
.foot__copy{letter-spacing:.04em;}
@media(min-width:60em){.foot__nav{max-width:none;gap:0 2.25rem;}}


/* --------------------------------------------------------------------------
   15. AFTER THE HERO
   The hero owns the first viewport, and that includes what floats over it.
   The WhatsApp mark and the cookie line both wait until the first screen has
   left the viewport (js/light.js sets html.past-hero from one
   IntersectionObserver). Neither is a solid object, because the pill is the
   only one: the mark is an outline with no disc behind it, and the cookie line
   is one line of type on a strip of the ground's own sand, which is not a box
   so much as the bottom of the page arriving early. Its top edge is feathered
   so type passing under it fades out instead of being cut.

   html.js is set by a one line inline script in the head. Without script
   there is no hero to wait for, so the mark is simply there, and the cookie
   line, which is nothing without script, is never created.
   -------------------------------------------------------------------------- */
.wa{
  position:fixed;z-index:4;
  right:max(.75rem,env(safe-area-inset-right));
  bottom:calc(.75rem + env(safe-area-inset-bottom) + var(--consent-h,0px));
  display:grid;place-items:center;width:48px;height:48px;
  color:var(--forest);
  -webkit-tap-highlight-color:transparent;
  transition:opacity 500ms var(--ease-out),transform 500ms var(--ease-out),visibility 0s linear 0s;
}
.wa svg{display:block;width:26px;height:26px;}
html.js .wa{opacity:0;visibility:hidden;transform:translate3d(0,10px,0);transition-delay:0s,0s,500ms;}
html.js.past-hero .wa{opacity:1;visibility:visible;transform:none;transition-delay:0s;}
.wa:active{opacity:.5;}

.consent{
  position:fixed;left:0;right:0;bottom:0;z-index:4;
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:0 .9rem;
  padding:.2rem 1rem calc(.2rem + env(safe-area-inset-bottom));
  background:var(--sand);
  font-size:.75rem;line-height:1.5;color:var(--ink-quiet);
  opacity:0;visibility:hidden;transform:translate3d(0,100%,0);
  transition:opacity 500ms var(--ease-out),transform 500ms var(--ease-out),visibility 0s linear 500ms;
}
.consent::before{
  content:"";position:absolute;left:0;right:0;bottom:100%;height:1.75rem;pointer-events:none;
  background:linear-gradient(to top,var(--sand),rgba(236,230,218,0));
}
.consent.is-shown{opacity:1;visibility:visible;transform:none;transition-delay:0s;}
.consent p{margin:0;white-space:nowrap;}
.consent__answers{display:flex;column-gap:.9rem;}
.consent a,.consent button{
  padding:.85rem 0;margin:0;
  font:inherit;color:var(--forest);
  background:none;border:0;border-radius:0;cursor:pointer;
  -webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent;
  text-decoration:underline;text-decoration-color:var(--gold);text-decoration-thickness:1px;text-underline-offset:.3em;
}
.consent a:active,.consent button:active{opacity:.5;}


/* --------------------------------------------------------------------------
   16. REDUCED MOTION
   Nothing travels. The light holds its pose and takes the next one without
   moving through the space between; content is simply there.
   -------------------------------------------------------------------------- */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto;}
  .light__pose{transition:none;}
  .light__src{animation:none;}
  .screen--first .rise{animation:none;}
  .will-rise,.will-rise.has-risen{opacity:1;transform:none;transition:none;}
  .cta,.more,.more::after,.quiet,.top__nav a,.top__menu,.foot__nav a,.foot__nav button{transition:none;}
  .menu,main,.foot,.wa,.consent,.top,.faq summary::after{transition:none;}
  html.js .wa,.consent{transform:none;}
}
