/* ═══════════════════════════════════════════════════════════════════════════
   NeverMiss Hawaii — the marketing site design system.
   Built 2026-08-14 on the 7-phase method (plans/2026-08-14-website-rebuild).

   THE STORY: "it runs when you don't." Every section answers one question,
   what happened while you weren't looking.

   THE RULES THIS FILE HOLDS TO (reference/anti-slop-design.md):
     · Playfair Display + Sora. Never Inter/Roboto/Geist.
     · Amber #e8a04a. Never indigo, violet or emerald.
     · A gradient must have a SOURCE. If you can move it anywhere and nothing
       looks wrong, it is a decorative wash, so delete it. The old body had
       exactly that and it is gone.
     · No glassmorphism, no colored glow, no reflections. Matte only.
     · Radius capped at 6px. Base unit 4px. Never 16px-on-everything.
     · No 3-4 identical icon-top cards. Asymmetry, varied weight.
     · No all-caps letter-spaced eyebrow labels. They were on lines 31/62/82
       of the old styles.css and they are the single most common AI tell.
     · transform / opacity / filter ONLY in animation. Nothing that lays out.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* DARK is the marketing voice. LIGHT is the working voice (the tool pages,
     projects/nevermiss-webchat/site/brand.css). That split is deliberate as of
     2026-08-14, not the accident reference/anti-slop-design.md flagged.
     To revert the whole site to light, see the block at the bottom of this file. */
  --bg:#100f0d;          /* near-black, warm */
  --surf:#1a1714;
  --surf2:#221f1b;
  --ink:#f5efe6;
  --mut:#b8a898;
  --faint:#7a6a58;
  --accent:#e8a04a;      /* amber. the only warm light in the room */
  --hover:#d97706;
  --line:rgba(232,160,74,.16);
  --rail:rgba(232,160,74,.44);

  /* 4px base unit, all the way up */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px;
  --s6:32px; --s7:48px; --s8:64px; --s9:96px; --s10:128px;

  --r:6px;               /* radius CAP, not a default */

  /* Phase 4 motion scale. No bounce, no elastic, no overshoot anywhere:
     overshoot reads playful, and we sell reliability to someone whose phone
     will not stop ringing. */
  --dur-fast:160ms;
  --dur-base:240ms;
  --dur-slow:400ms;
  --dur-cine:600ms;
  --ease-settle:cubic-bezier(.22,1,.36,1);
  --ease-exit:cubic-bezier(.4,0,1,1);
}

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}

body{
  background:var(--bg);
  color:var(--ink);
  font:16px/1.65 'Sora',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  position:relative;
  min-height:100vh;
}

/* Film grain. A brand signature (anti-slop-design.md names it), a texture
   rather than a gradient, so it survives the no-wash rule. One tiled SVG,
   about 400 bytes inline, composited once. */
body::after{
  content:'';position:fixed;inset:0;z-index:100;pointer-events:none;opacity:.04;
  background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

h1,h2,h3,.serif{font-family:'Playfair Display',Georgia,serif;font-weight:800;letter-spacing:-.02em;line-height:1.08}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}
::selection{background:var(--accent);color:#1a1410}

.wrap{max-width:1080px;margin:0 auto;padding:0 var(--s5);position:relative}
.narrow{max-width:760px}

/* ── Buttons ─────────────────────────────────────────────────────────────
   ONE primary CTA on the whole page, repeated verbatim. The old page put
   four competing CTAs above the fold, which is four choices, which is none. */
.btn{
  display:inline-flex;align-items:center;gap:var(--s2);
  font:600 16px/1 'Sora',sans-serif;
  padding:16px var(--s6);border-radius:var(--r);border:1px solid transparent;
  background:var(--accent);color:#171208;
  min-height:48px;                       /* thumb target */
  transition:background var(--dur-fast) var(--ease-settle),
             transform var(--dur-fast) var(--ease-settle);
  cursor:pointer;will-change:transform;
}
.btn:hover{background:var(--hover);transform:translateY(-1px)}
.btn:active{transform:scale(.98)}
.btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.btn-note{display:block;margin-top:var(--s3);font-size:14px;color:var(--faint)}

/* Secondary is a text link, never a second button. */
.tlink{
  display:inline-block;font-size:15px;color:var(--mut);
  border-bottom:1px solid transparent;padding-bottom:2px;
  transition:color var(--dur-fast),border-color var(--dur-fast);
}
.tlink:hover{color:var(--accent);border-bottom-color:var(--accent)}

/* ── Header ──────────────────────────────────────────────────────────── */
.nav{
  position:sticky;top:0;z-index:50;
  padding:var(--s4) 0;
  background:transparent;
  transition:background var(--dur-base),border-color var(--dur-base);
  border-bottom:1px solid transparent;
}
.nav.solid{background:rgba(16,15,13,.92);border-bottom-color:var(--line);
  -webkit-backdrop-filter:none;backdrop-filter:none}  /* never backdrop-filter: it cost real scroll jank in alice-platform */
.nav-in{max-width:1080px;margin:0 auto;padding:0 var(--s5);display:flex;align-items:center;justify-content:space-between;gap:var(--s5)}
.brand{display:flex;align-items:center;gap:10px}
.brand svg{width:30px;height:34px;flex:none}
.brand b{font-family:'Playfair Display',serif;font-weight:800;font-size:19px;letter-spacing:-.01em}
.brand span{color:var(--accent)}
.nav-links{display:flex;align-items:center;gap:var(--s6)}
.nav-links a{font-size:15px;color:var(--mut);transition:color var(--dur-fast)}
.nav-links a:hover,.nav-links a[aria-current]{color:var(--ink)}
.nav .btn{padding:10px var(--s5);font-size:14px;min-height:40px}

/* ═══ 1 · HERO ════════════════════════════════════════════════════════════
   Three real screens receding into the dark, lit by one warm lamp.
   No Three.js, no WebGL, no canvas. Three rectangles do not need a scene
   graph, and the library would have cost ~150KB to render them. */
.hero{position:relative;padding:var(--s8) 0 var(--s9);overflow:hidden}

/* Two columns from 1000px. Verified 2026-08-14: stacked, the centrepiece sat
   BELOW the fold at 1440x900, so the one idea the hero exists to deliver was
   invisible until you scrolled. Side by side puts the lit panels and the
   headline on screen together, which is the whole point, and it uses the right
   40% of the page that was dead space. */
@media (min-width:1000px){
  .hero .wrap{display:grid;grid-template-columns:1fr .92fr;gap:var(--s8);align-items:center}
  .hero .stage{margin-top:0;height:clamp(330px,26vw,390px);opacity:.82}
}

.hero h1{font-size:clamp(38px,5.6vw,62px);max-width:15ch}
.hero .sub{
  margin-top:var(--s5);max-width:52ch;
  font-size:clamp(17px,2vw,19px);line-height:1.6;color:var(--mut);
}
.hero .cta{margin-top:var(--s7)}
/* The locator sits BELOW the CTA as a fact. Above the H1 it would be the
   all-caps eyebrow badge, which is the tell we are avoiding. */
.hero .locator{margin-top:var(--s6);font-size:13px;color:var(--faint)}

/* The stage. perspective creates real Z, so the parallax differential between
   layers is genuine depth rather than three elements moving different amounts. */
.stage{
  position:relative;margin-top:var(--s8);
  height:clamp(300px,42vw,440px);
  perspective:1400px;perspective-origin:38% 40%;
}
.panels{
  position:absolute;inset:0;
  transform-style:preserve-3d;
  transform:rotateY(-6deg);          /* flat-on would kill the depth */
  transition:transform var(--dur-slow) var(--ease-settle);
}
.panel{
  position:absolute;top:0;left:2%;
  width:min(370px,84%);
  background:var(--surf);
  border-radius:var(--r);
  padding:var(--s5);
  /* One warm shadow, offset FROM the key light at upper-left. Not the
     0.1-opacity faint shadow that reads as generated. */
  box-shadow:22px 26px 60px -18px rgba(0,0,0,.75);
  will-change:transform;
}
/* No borders. The LIGHT defines the edges, which is what separates this
   from three cards sitting on a page. */
/* Falloff tuned by looking, 2026-08-14: .72/.45 on a #1a1714 surface read as
   two dark smudges rather than as depth. The panels must stay legible enough
   that you can tell they are a conversation. */
.panel.p1{transform:translate3d(0,var(--s6),0);        filter:brightness(.94)}
.panel.p2{transform:translate3d(12%,calc(var(--s7) * -1),-90px); filter:brightness(.84)}
.panel.p3{transform:translate3d(22%,calc(var(--s9) * -1.15),-180px); filter:brightness(.62)}

/* The key light. Masked to the panel group, so it has a SOURCE and a position.
   Move it and the scene breaks, which is the test that separates a light from
   a decorative wash. */
.stage::before{
  content:'';position:absolute;left:-24%;top:-38%;width:120%;height:150%;
  background:radial-gradient(closest-side,rgba(232,160,74,.19),rgba(232,160,74,.05) 52%,transparent 76%);
  pointer-events:none;z-index:0;
  opacity:0;transition:opacity var(--dur-cine) var(--ease-settle);
}
.stage.lit::before{opacity:1}

/* A message thread, rendered in CSS rather than shipped as a screenshot.
   Sharper at every DPI, a few hundred bytes instead of 300KB, and it depicts
   our own product behaviour rather than claiming to be a client's inbox. */
.thr{display:flex;flex-direction:column;gap:10px}
.thr .t{font-size:12px;color:var(--faint);letter-spacing:.01em}
.msg{max-width:78%;padding:10px var(--s4);border-radius:var(--r);font-size:14px;line-height:1.5}
.msg.them{background:var(--surf2);color:var(--mut);align-self:flex-start}
.msg.us{background:rgba(232,160,74,.14);color:var(--ink);align-self:flex-end}
.badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--accent)}
.badge::before{content:'';width:5px;height:5px;border-radius:50%;background:var(--accent)}


/* The rail was the signature for about an hour. Anu, 2026-08-14: "the rail
   isn't my favorite... using static backgrounds we could blend multiple images
   together as we're scrolling down the page." He was right, and the crossfade
   is the better signature because it carries the same night without asking
   anyone to read a diagram. The spine is gone; the night keeps its timestamps
   as a plain left column, because the TIMES were always the content and the
   line was only ever the device holding them.

   Kept in git at 78d0096 if we ever want it back. */

/* ═══ 2 · THE NIGHT ═══════════════════════════════════════════════════════
   The hero made a claim. This proves it with one night, timestamped.
   The rail is the spine of the whole page. */
.night{padding:var(--s8) 0 var(--s10)}
.night h2{font-size:clamp(28px,4vw,42px);max-width:18ch;margin-bottom:var(--s8)}

.rail{position:relative;padding-left:100px}
/* The rail draws itself downward as you scroll. scaleY on a composited
   pseudo-element: no layout, no paint, cheap at 60fps. */
.rail::before{
  content:none;
  transition:transform var(--dur-slow) linear;
}
.row{position:relative;padding-bottom:var(--s7)}
.row:last-child{padding-bottom:0}
.row .stamp{
  position:absolute;left:-100px;top:1px;width:64px;text-align:right;
  font-size:13px;color:var(--faint);font-variant-numeric:tabular-nums;
}

.row p{font-size:clamp(17px,2.2vw,20px);line-height:1.55;max-width:44ch}
.row .whisper{margin-top:var(--s2);font-size:15px;color:var(--faint)}

/* ═══ 3 · HOW IT WORKS ════════════════════════════════════════════════════
   Same rail, three moments alternating off it. Deliberately asymmetric.
   A 3-across grid here is exactly the skeleton we are avoiding. */
.works{padding:var(--s10) 0}
.works h2{font-size:clamp(28px,4vw,42px);max-width:16ch;margin-bottom:var(--s9)}
.beat{display:grid;gap:var(--s6);align-items:center;margin-bottom:var(--s9)}
.beat:last-of-type{margin-bottom:var(--s7)}
.beat p{font-size:clamp(17px,2.2vw,20px);line-height:1.55}
.beat .shot{border-radius:var(--r);overflow:hidden;background:var(--surf);
  box-shadow:18px 22px 50px -22px rgba(0,0,0,.7);
  transition:filter var(--dur-fast) var(--ease-settle);will-change:transform}
.beat .shot img{filter:brightness(.86) contrast(.97) saturate(.9);transition:filter var(--dur-base) var(--ease-settle)}
.beat .shot:hover img{filter:brightness(1) contrast(1) saturate(1)}   /* no lift, no scale, no glow */
@media (min-width:820px){
  .beat{grid-template-columns:1fr 1fr}
  /* Varied weight is what stops three rows reading as a grid. */
  .beat.b1{grid-template-columns:1fr 1.15fr}
  .beat.b2{grid-template-columns:1.35fr 1fr}
  .beat.b2 .txt{order:2}
  .beat.b3{grid-template-columns:1fr 1.05fr}
}

/* ═══ 4 · ANY PART ══════════════════════════════════════════════════════
   Rebuilt 2026-08-14. The first version was one sentence whose verb swapped on
   scroll. Anu: "it's just printing a bunch of things. I don't think I like this
   design." Both halves of that were right. The swap appended a span per scroll
   frame and removed each 260ms later, so scrolling faster than the removals
   could keep up stacked every verb on screen at once, and the idea was fragile
   even when it worked.

   This is a manifesto list instead. Every line visible, revealed in order, no
   state to get wrong. It still shows breadth without being a menu, because
   these are statements about what it does rather than options somebody picks
   from. Seven lines, exactly one about the phone: that ratio is the point, see
   the STORY BANK rule in offer-positioning.md. */
.any{padding:var(--s10) 0;position:relative}
.claims{list-style:none;margin:0;display:flex;flex-direction:column;gap:var(--s4)}
.claims li{
  font-family:'Playfair Display',Georgia,serif;font-weight:700;
  font-size:clamp(19px,2.2vw,27px);line-height:1.3;letter-spacing:-.015em;
  color:var(--faint);max-width:24ch;
}
/* The verb is the only amber on the line, so the eye runs down a column of
   accents and takes the whole range in at a glance. */
.claims li b{color:var(--mut);font-weight:800}
.any .foot{margin-top:var(--s9);font-size:17px;color:var(--mut);max-width:46ch}
@media (min-width:820px){ .claims li{max-width:26ch} }

/* ═══ 5 · PROOF ═══════════════════════════════════════════════════════════
   The strongest asset we own, currently buried in a paragraph on /about.
   Asymmetric on purpose: a uniform grid is the tell. */
.proof{padding:var(--s10) 0}
.proof h2{font-size:clamp(28px,4vw,42px);max-width:18ch;margin-bottom:var(--s5)}
.proof .lede{max-width:52ch;color:var(--mut);margin-bottom:var(--s9);font-size:17px}
.gal{display:grid;gap:var(--s6)}
.gal figure{border-radius:var(--r);overflow:hidden;background:var(--surf);
  box-shadow:16px 20px 46px -24px rgba(0,0,0,.7);will-change:transform}
.gal img{width:100%;height:auto;filter:brightness(.86) contrast(.97) saturate(.9);
  transition:filter var(--dur-base) var(--ease-settle)}
.gal figure:hover img{filter:brightness(1) contrast(1) saturate(1)}
.gal figcaption{padding:var(--s4) var(--s5) var(--s5);font-size:14px;color:var(--mut)}
.gal figcaption b{display:block;color:var(--ink);font-size:15px;font-weight:600;margin-bottom:3px}
@media (min-width:820px){
  .gal{grid-template-columns:repeat(6,1fr);align-items:start}
  /* Deliberate size + offset variation. This is the anti-slop point, so it is
     the thing that must survive any future edit. */
  .gal figure:nth-child(1){grid-column:span 4}
  .gal figure:nth-child(2){grid-column:span 2;margin-top:var(--s8)}
  .gal figure:nth-child(3){grid-column:span 3}
  .gal figure:nth-child(4){grid-column:span 3;margin-top:var(--s7)}
  .gal figure:nth-child(5){grid-column:span 2}
  .gal figure:nth-child(6){grid-column:span 4;margin-top:var(--s6)}
}
.proof .more{margin-top:var(--s8)}

/* ═══ 6 · COST + THE PROMISE ══════════════════════════════════════════════
   Combined on purpose. Price raises the objection, the guarantee answers it.
   Splitting them leaves the objection sitting on its own. */
.cost{padding:var(--s10) 0}
.cost h2{font-size:clamp(28px,4vw,42px);max-width:16ch;margin-bottom:var(--s8)}
.ledger{display:grid;gap:var(--s7)}
@media (min-width:820px){.ledger{grid-template-columns:1fr 1fr;gap:var(--s8)}}
.led-col h3{font-size:19px;font-weight:700;margin-bottom:var(--s5);color:var(--mut)}
.led-col.now h3{color:var(--faint)}
.led-col ul{list-style:none;display:flex;flex-direction:column;gap:var(--s4)}
.led-col li{font-size:17px;line-height:1.5;padding-left:var(--s5);position:relative}
.led-col li::before{content:'';position:absolute;left:0;top:11px;width:8px;height:1px;background:var(--faint)}
.led-col.us li::before{background:var(--accent)}
.led-col.now li{color:var(--faint)}

.price{margin-top:var(--s9);padding-top:var(--s7);border-top:1px solid var(--line)}
.price p{font-size:clamp(18px,2.4vw,22px);line-height:1.55;max-width:46ch;font-family:'Playfair Display',serif;font-weight:700}
.price .terms{margin-top:var(--s4);font-size:15px;color:var(--faint);font-family:'Sora',sans-serif;font-weight:400}
.promise{margin-top:var(--s7);padding:var(--s6);background:var(--surf);border-radius:var(--r);border-left:2px solid var(--accent)}
.promise p{font-size:17px;line-height:1.6;max-width:52ch;font-family:'Sora',sans-serif;font-weight:400}
.cost .cta{margin-top:var(--s8)}

/* ═══ 7 · CLOSE ═══════════════════════════════════════════════════════════
   The hero's key light returns at low intensity. Bookends the page. */
.close{position:relative;padding:var(--s10) 0 calc(var(--s10) + var(--s6));text-align:center;overflow:hidden}
.close::before{
  content:'';position:absolute;left:50%;top:-40%;width:760px;height:560px;
  transform:translateX(-50%);pointer-events:none;
  background:radial-gradient(closest-side,rgba(232,160,74,.13),transparent 75%);
}
.close h2{font-size:clamp(28px,4.4vw,46px);max-width:16ch;margin:0 auto var(--s5)}
.close p{max-width:44ch;margin:0 auto var(--s7);color:var(--mut);font-size:17px}
.close .locator{margin-top:var(--s6);font-size:13px;color:var(--faint)}

/* ── Footer ──────────────────────────────────────────────────────────── */
.foot{border-top:1px solid var(--line);padding:var(--s7) 0;font-size:14px;color:var(--faint)}
.foot-in{display:flex;flex-wrap:wrap;gap:var(--s5);align-items:center;justify-content:space-between}
.foot nav{display:flex;gap:var(--s5);flex-wrap:wrap}
.foot a{transition:color var(--dur-fast)}
.foot a:hover{color:var(--accent)}

/* ═══ /proof ══════════════════════════════════════════════════════════════
   Its own layout, not the home gallery repeated. Alternating case studies
   with the image oversized against the text, so the work is the loudest
   thing on the page. */
.phead{padding:var(--s9) 0 var(--s7)}
.phead h1{font-size:clamp(34px,5.4vw,58px);max-width:14ch}
.phead .sub{margin-top:var(--s5);max-width:56ch;font-size:clamp(17px,2vw,19px);line-height:1.6;color:var(--mut)}
.phead .locator{margin-top:var(--s5);font-size:13px;color:var(--faint)}

.cases{padding:var(--s7) 0 var(--s9)}
.case{display:grid;gap:var(--s6);align-items:center;padding:var(--s8) 0;border-top:1px solid var(--line)}
.case:first-child{border-top:0}
.case figure{border-radius:var(--r);overflow:hidden;background:var(--surf);
  box-shadow:18px 22px 52px -24px rgba(0,0,0,.72)}
.case figure img{filter:brightness(.96);transition:filter var(--dur-base) var(--ease-settle)}
.case figure:hover img{filter:none}
.case h2{font-size:clamp(24px,3.2vw,34px);margin-bottom:var(--s4)}
.case .body>p{font-size:17px;line-height:1.62;color:var(--mut);max-width:52ch}
.case ul{list-style:none;margin-top:var(--s5);display:flex;flex-direction:column;gap:10px}
.case li{position:relative;padding-left:var(--s5);font-size:15px;line-height:1.5;color:var(--mut)}
.case li::before{content:'';position:absolute;left:0;top:10px;width:8px;height:1px;background:var(--accent)}
@media (min-width:900px){
  /* Image gets the larger share. Alternating sides keeps it off a grid rhythm. */
  .case{grid-template-columns:1.25fr 1fr;gap:var(--s8)}
  .case.flip{grid-template-columns:1fr 1.25fr}
  .case.flip figure{order:2}
}

/* ── Reveal (Phase 4) ────────────────────────────────────────────────────
   IntersectionObserver adds .seen. Fires once. transform + opacity only,
   so nothing here can trigger layout. */
.rv{opacity:0;transform:translateY(16px);
  transition:opacity var(--dur-slow) var(--ease-settle),transform var(--dur-slow) var(--ease-settle)}
.rv.seen{opacity:1;transform:none}
.beat.b2 .txt.rv{transform:translateY(16px) translateX(24px)}
.beat.b1 .txt.rv,.beat.b3 .txt.rv{transform:translateY(16px) translateX(-24px)}
.beat.seen .txt.rv{transform:none}

/* ── Mobile ──────────────────────────────────────────────────────────────
   Parallax off. Panels become a static composition. The gallery keeps its
   size variation, because that variation is the whole anti-slop point and it
   must survive the breakpoint. */
@media (max-width:819px){
  .nav-links{display:none}
  .hero{padding:var(--s8) 0 var(--s9)}
  .hero h1{font-size:34px}
  .stage{height:340px;perspective:900px}
  .panel{width:86%;padding:var(--s4)}
  .panel.p2{transform:translate3d(10%,var(--s6),-60px)}
  .panel.p3{transform:translate3d(20%,calc(var(--s6)*2),-120px)}
  .btn{width:100%;justify-content:center}
  .nav .btn{width:auto}
  .rail{padding-left:var(--s6)}
  .row .stamp{position:static;display:block;margin-bottom:var(--s2)}
  
  .gal figure:nth-child(n){margin-top:0}
}

/* ── prefers-reduced-motion ──────────────────────────────────────────────
   Non-negotiable. The hero has depth motion and vestibular disorders are real. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important}
  .rv{opacity:1;transform:none}
  .rail::before{transform:scaleY(1)}
  .stage::before{opacity:1}
}

/* ── Reverting the whole site to LIGHT ───────────────────────────────────
   Anu forced the public site light on 2026-07-14 ("NeverMiss is LIGHT-ONLY",
   old styles.css line 9). The 2026-08-14 rebuild deliberately puts the
   MARKETING site back on dark while the tool pages stay light, because the
   hero's art direction is one warm lamp in a dark room and that idea does not
   survive on cream. If that call gets reversed, swap the six values below and
   nothing else in this file needs to change.

   :root{ --bg:#faf6f0; --surf:#ffffff; --surf2:#f3ece1;
          --ink:#2a2018; --mut:#6b5d4f; --faint:#9a8a78;
          --accent:#c2680a; --hover:#a85607;
          --line:rgba(150,95,35,.16); --rail:rgba(150,95,35,.30); }
   ...then drop the .panel box-shadow to a lighter value and re-check contrast
   on .msg.us, which is tuned for a dark ground. */

/* ═══════════════════════════════════════════════════════════════════════════
   ATMOSPHERE — added 2026-08-14 after Anu walked the first build.

   His note: "I like the static backgrounds and I think that's the best part of
   some of the websites I built." He is right, and the first build had none.
   It was flat #100f0d with text on it, which is why it read as empty rather
   than as restrained.

   Read off his own two sites, measured not guessed:
     teamlife.ai  .hero-bg  = THREE stacked radial-gradients over a base.
                              radial(55% 50% at 50% 0%, navy .34) +
                              radial(45% 45% at 82% 18%, gold .16) + linear base.
                              NO photograph in the hero at all.
     oly-life-808 .hero-veil = linear veil + two radials over a photo layer.

   So the technique is an AMBIENT FIELD: two or three big soft radials, placed
   at deliberate points, in brand colours, over a deep base. It is what gives
   depth without a single asset to download.

   This does NOT break the no-decorative-wash rule from the top of this file.
   The rule is that a gradient must have a source and a position. Each of these
   is a light in a specific place, tuned per section to sit behind that
   section's subject. Move one and the section visibly flattens.
   ═══════════════════════════════════════════════════════════════════════════ */

/* One fixed field behind everything. Fixed rather than per-section so the
   light stays put while content travels over it, which is what makes a page
   feel like one room instead of seven slides. */
body::before{
  content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:
    radial-gradient(58% 44% at 72% 4%,  rgba(232,160,74,.16), transparent 68%),
    radial-gradient(46% 38% at 12% 26%, rgba(120,86,140,.10), transparent 70%),
    radial-gradient(70% 50% at 50% 104%,rgba(232,160,74,.07), transparent 72%),
    var(--bg);
}
/* A cool counter-tone (that muted violet at 10%) is what stops the amber
   reading as a single flat wash. Warm light needs something cold to be warm
   against. It is far too dim to register as a colour, only as depth. */

/* Per-section ambient beats. Each one is a light placed behind that section's
   subject, so the page has high and low points instead of even lighting. */
.night::before,.works::before,.proof::before,.cost::before{
  content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;
}
.night::before{background:radial-gradient(50% 60% at 18% 30%,rgba(232,160,74,.09),transparent 70%)}
.works::before{background:radial-gradient(46% 52% at 14% 46%,rgba(232,160,74,.07),transparent 72%)}
.proof::before{background:radial-gradient(60% 50% at 80% 12%,rgba(232,160,74,.08),transparent 72%)}
.cost::before {background:radial-gradient(55% 55% at 22% 60%,rgba(232,160,74,.07),transparent 72%)}
.night,.works,.proof,.cost{position:relative}

/* Vignette. Pulls the eye to the middle and stops the page bleeding into the
   browser chrome. One of the cheapest premium cues there is. */
body{box-shadow:inset 0 0 240px 60px rgba(0,0,0,.55)}

/* ── The headline accent ─────────────────────────────────────────────────
   Straight off oly-life-808: "Wellness you can *feel*." Roman for the setup,
   ITALIC + accent colour for the word that carries the meaning. It costs
   nothing, it cannot be mistaken for a default, and it is the single loudest
   typographic move available to us. */
.hl-em{font-style:italic;color:var(--accent)}

/* ═══ PURPOSE-BUILT UI VISUALS ════════════════════════════════════════════
   Replaces the product screenshots, 2026-08-14, Anu: "All the pictures look
   distorted like they are the wrong size... I don't think we need to show
   exactly what the tools look like here."

   He was right on all counts, and there was a third problem he did not raise:
   those PNGs were screenshots of a whole browser window, so the actual UI was
   tiny and unreadable, AND they carried real client data (Team Life Academy,
   EPIC LIFE member counts, real task names and real people).

   These are composed in CSS instead. Fixed aspect so nothing can distort,
   legible at any size, in our own palette rather than the tools' light theme,
   a couple of hundred bytes each, and they contain no client's information.
   They show the SHAPE of the thing, which is all this section needs. */
.ui{
  position:relative;aspect-ratio:16/10;width:100%;
  background:linear-gradient(160deg,var(--surf) 0%,#151210 100%);
  border-radius:var(--r);overflow:hidden;
  box-shadow:18px 22px 52px -24px rgba(0,0,0,.75);
  padding:var(--s5);display:flex;flex-direction:column;gap:var(--s3);
}
/* One warm rake of light across each, from the same direction as the hero key,
   so every visual on the page is lit by the same lamp. */
.ui::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(70% 60% at 12% -10%,rgba(232,160,74,.13),transparent 66%);
}
.ui-top{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s2)}
.ui-top i{width:7px;height:7px;border-radius:50%;background:var(--faint);opacity:.5;font-style:normal}
.ui-top b{margin-left:auto;font:500 11px/1 'Sora',sans-serif;color:var(--faint);letter-spacing:.02em}

/* Kanban */
.ui-board{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s3);flex:1}
.ui-col{display:flex;flex-direction:column;gap:8px}
.ui-col em{font:600 10px/1 'Sora',sans-serif;color:var(--faint);font-style:normal;letter-spacing:.04em;text-transform:uppercase}
.ui-card{background:rgba(255,255,255,.045);border-radius:4px;height:26px;position:relative}
.ui-card::before{content:'';position:absolute;left:8px;top:8px;height:4px;width:58%;border-radius:2px;background:rgba(245,239,230,.20)}
.ui-card::after{content:'';position:absolute;left:8px;top:16px;height:3px;width:32%;border-radius:2px;background:rgba(245,239,230,.10)}
.ui-card.hot{background:rgba(232,160,74,.16)}
.ui-card.hot::before{background:rgba(232,160,74,.55)}

/* Rows */
.ui-rows{display:flex;flex-direction:column;gap:7px;flex:1;justify-content:center}
.ui-row{display:flex;align-items:center;gap:10px;height:24px}
.ui-row s{width:6px;height:6px;border-radius:50%;background:var(--accent);opacity:.6;text-decoration:none;flex:none}
.ui-row u{flex:1;height:5px;border-radius:3px;background:rgba(245,239,230,.14);text-decoration:none}
.ui-row:nth-child(2n) u{width:72%;flex:none}
.ui-row:nth-child(3n) u{width:86%;flex:none}
.ui-row q{width:34px;height:14px;border-radius:3px;background:rgba(232,160,74,.18);flex:none}
.ui-row q::before{content:''}

/* KPI tiles + a hand-drawn sparkline */
.ui-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s3)}
.ui-tile{background:rgba(255,255,255,.045);border-radius:4px;padding:10px}
.ui-tile b{display:block;font:800 20px/1 'Playfair Display',serif;color:var(--accent)}
.ui-tile span{display:block;margin-top:5px;height:3px;width:60%;border-radius:2px;background:rgba(245,239,230,.14)}
.ui-spark{flex:1;min-height:0}
.ui-spark svg{width:100%;height:100%;display:block}

@media (max-width:819px){ .ui{padding:var(--s4)} .ui-board{gap:var(--s2)} }

/* Portal: an avatar, a name line, and progress against a few steps. */
.ui-portal{display:flex;flex-direction:column;gap:var(--s3);flex:1;justify-content:center}
.ui-who{display:flex;align-items:center;gap:10px}
.ui-who i{width:26px;height:26px;border-radius:50%;background:rgba(232,160,74,.22);flex:none}
.ui-who u{height:5px;width:96px;border-radius:3px;background:rgba(245,239,230,.20);text-decoration:none}
.ui-bar{height:6px;border-radius:3px;background:rgba(245,239,230,.09);position:relative;overflow:hidden}
.ui-bar::after{content:'';position:absolute;left:0;top:0;bottom:0;width:var(--p,60%);background:var(--accent);opacity:.7;border-radius:3px}

/* Page: a website wireframe. Nav, a hero block, a content band. */
.ui-page{display:flex;flex-direction:column;gap:9px;flex:1}
.ui-page .pn{display:flex;gap:7px;align-items:center}
.ui-page .pn u{height:4px;width:26px;border-radius:2px;background:rgba(245,239,230,.16);text-decoration:none}
.ui-page .pn q{margin-left:auto;width:38px;height:13px;border-radius:3px;background:rgba(232,160,74,.32)}
.ui-page .ph{flex:1;border-radius:5px;background:linear-gradient(150deg,rgba(232,160,74,.16),rgba(245,239,230,.03));position:relative;min-height:0}
.ui-page .ph::before{content:'';position:absolute;left:14px;top:20px;height:7px;width:52%;border-radius:3px;background:rgba(245,239,230,.30)}
.ui-page .ph::after{content:'';position:absolute;left:14px;top:34px;height:5px;width:34%;border-radius:3px;background:rgba(245,239,230,.14)}
.ui-page .pg{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;height:34px}
.ui-page .pg i{border-radius:4px;background:rgba(255,255,255,.045)}

/* Agents: a stack of running jobs, one lit. */
.ui-agents{display:flex;flex-direction:column;gap:7px;flex:1;justify-content:center}
.ui-job{display:flex;align-items:center;gap:9px;height:22px;padding:0 9px;border-radius:4px;background:rgba(255,255,255,.04)}
.ui-job s{width:5px;height:5px;border-radius:50%;background:var(--faint);text-decoration:none;flex:none}
.ui-job u{height:4px;border-radius:2px;background:rgba(245,239,230,.16);text-decoration:none;flex:1}
.ui-job q{width:28px;height:4px;border-radius:2px;background:rgba(245,239,230,.08)}
.ui-job.live{background:rgba(232,160,74,.13)}
.ui-job.live s{background:var(--accent)}
.ui-job.live u{background:rgba(232,160,74,.42)}

/* On /proof the visual sits beside prose, so it must not tower over it. */
.case .ui{max-height:460px}

/* ═══════════════════════════════════════════════════════════════════════════
   THE CANVAS LAYER + TYPE IN MOTION   (2026-08-14, "stop protecting the
   performance budget")
   ═══════════════════════════════════════════════════════════════════════════ */

/* Sits inside .hero, under the content, over the CSS ambient field. Injected
   by atmos.js so a browser that never runs it simply gets the gradients, which
   already look finished on their own. */
.atmos{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  opacity:0;animation:atmos-in 1400ms var(--ease-settle) 300ms forwards;
}
@keyframes atmos-in{to{opacity:1}}
.hero .wrap{z-index:1}          /* content always above the dust */

/* ── Word-by-word mask reveal ────────────────────────────────────────────
   Each word rides in its own overflow box and rises from behind its baseline.
   The padding/margin pair is load-bearing: without it overflow:hidden slices
   the descenders off g, y and p, which is invisible in review and obvious on
   the live page. */
.w{display:inline-block;overflow:hidden;vertical-align:top;
   /* Pad every side the glyph can spill past, then pull it back with negative
      margin so spacing is unchanged. Bottom is for descenders, right is for
      italic overhang, left is for the odd swash. Getting this wrong is invisible
      in code and obvious on the page: it ate the f in "myself". */
   padding:.02em .14em .16em .06em;
   margin:-.02em -.14em -.16em -.06em}
.wi{display:inline-block;transform:translateY(115%);
    transition:transform 760ms cubic-bezier(.22,1,.36,1)}
.wsplit.seen .wi{transform:none}
/* Before the split runs, the heading must look normal. If the script fails or
   is slow, nobody sees a broken headline, they just see a headline. */
h1.tm:not(.wsplit) .wi,h2.tm:not(.wsplit) .wi{transform:none}

@media (prefers-reduced-motion:reduce){
  .atmos{display:none}
  .wi{transform:none!important;transition:none}
}

/* ═══ THE PHOTO LAYER — three options to pick between ═════════════════════
   Anu 2026-08-14: "you can choose something that can both relate to hawaii and
   our business model. Maybe you can show me 3 different examples/choices."

   Walk them with ?bg=a, ?bg=b, ?bg=c on the home page. Default is none.

     a  ISLAND AT NIGHT   the island is asleep and the lights are still on
     b  DEEP WATER, DUSK  calm, steady, it keeps going without you
     c  PALMS AT NIGHT    unmistakably Hawaii, most literal of the three

   All three are generated rather than stock, so there is no licence, no
   attribution and no chance of the same photo turning up on a competitor. The
   oly-life-808 recipe applies on top: a dark VEIL over the image so it is
   texture and never a picture competing with the headline. */
.hero-photo{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background-size:cover;background-position:center 62%;
  opacity:0;transition:opacity 1200ms var(--ease-settle) 200ms;
}
.hero-photo.on{opacity:1}
/* The veil. Without it the photo fights the type; with it the photo is only
   ever felt. Bottom stop is near-opaque so the section hands off to a flat
   ground instead of a hard edge. */
.hero-photo::after{
  content:'';position:absolute;inset:0;
  background:linear-gradient(to bottom,rgba(16,15,13,.62) 0%,rgba(16,15,13,.42) 34%,rgba(16,15,13,.96) 100%),
             linear-gradient(to right,rgba(16,15,13,.90) 0%,rgba(16,15,13,.20) 64%,rgba(16,15,13,.46) 100%);
}
body[data-bg="a"] .hero-photo{background-image:url("/assets/bg/a-island-night.jpg")}
body[data-bg="b"] .hero-photo{background-image:url("/assets/bg/b-water-dusk.jpg")}
body[data-bg="c"] .hero-photo{background-image:url("/assets/bg/c-palms-night.jpg")}

/* Real client sites on /services. Three public marketing sites we built, stacked
   with depth. Safe to show at full fidelity precisely BECAUSE they are public:
   the internal tool screenshots were not, which is why those are CSS visuals. */
.case figure.live{background:none;box-shadow:none;overflow:visible}
.live-stack{position:relative;padding:0 var(--s6) var(--s7) 0}
.live-stack img{
  height:auto;                 /* without this the height="688" attribute wins and they stretch */
  border-radius:var(--r);
  box-shadow:16px 20px 46px -22px rgba(0,0,0,.8);
  filter:brightness(.9) contrast(.97) saturate(.94);
  transition:filter var(--dur-base) var(--ease-settle),transform var(--dur-base) var(--ease-settle);
}
/* Two behind, one in front. Same lamp, same falloff as everything else. */
.live-stack img:nth-child(1){position:relative;z-index:2;width:82%}
.live-stack img:nth-child(2){position:absolute;right:0;bottom:-6%;width:56%;z-index:3;filter:brightness(.86) saturate(.94)}
.live-stack:hover img{filter:brightness(1) contrast(1) saturate(1)}
.live-stack:hover img:nth-child(1){transform:translateY(-3px)}
.live-note{margin-top:var(--s5);font-size:15px;color:var(--faint)}
.live-note a{color:var(--accent)}
@media (max-width:819px){
  .live-stack{padding:0 var(--s5) var(--s6) 0}
  .live-stack img:nth-child(2){top:18%}
}

/* Redacted product screenshots on /services. The blur is applied to the image
   FILE, never in CSS: a CSS filter can be turned off with devtools in two
   clicks, so the pixels themselves have to be gone. Source PNGs are kept out of
   the deploy (assets/work/src/ is excluded by .assetsignore + the dist sync). */
.case figure img{width:100%;height:auto}
.redact-note{margin-top:var(--s5);font-size:14px;color:var(--faint)}

/* ═══════════════════════════════════════════════════════════════════════════
   THE SCROLL — four photographs of Oahu, crossfading down the page.

   Anu, 2026-08-14: "the rail isn't my favorite... using static backgrounds we
   could blend multiple images together as we're scrolling down the page."
   This is that, and it takes over as the signature.

   The arc is the same night the copy describes: dusk over Honolulu, into the
   night, through the water, out to first light. The page IS the night, which
   is the only reason a photographic background earns its place here instead of
   being atmosphere for its own sake.

   PERFORMANCE. This is a full-viewport image layer, so it is the easiest thing
   on the page to make expensive:
     · position:fixed, so it never repaints on scroll, only its opacity changes.
     · opacity is the ONLY animated property. Not filter, not transform.
     · brightness and saturation are baked into the FILES, not applied in CSS,
       because a CSS filter on a full-viewport image composites every frame.
     · 205KB for all four, together, and only the first is eager.
   ═══════════════════════════════════════════════════════════════════════════ */
.scroll-bg{position:fixed;inset:0;z-index:-1;pointer-events:none;overflow:hidden}
.plate{
  position:absolute;inset:0;
  background-size:cover;background-position:center;
  opacity:0;transition:opacity 700ms linear;
  will-change:opacity;
}
.plate.on{opacity:1}

/* The veil. Straight off oly-life-808: a dark wash plus the brand light, so the
   photograph is texture you feel rather than a picture competing with the type.
   Without it, body copy over a photo is unreadable at any opacity worth having. */
.veil{
  position:absolute;inset:0;
  background:
    radial-gradient(58% 44% at 72% 8%, rgba(232,160,74,.14), transparent 66%),
    linear-gradient(to bottom, rgba(16,15,13,.62) 0%, rgba(16,15,13,.50) 40%, rgba(16,15,13,.74) 100%),
    linear-gradient(to right,  rgba(16,15,13,.82) 0%, rgba(16,15,13,.30) 60%, rgba(16,15,13,.52) 100%);
}

/* The fixed ambient field and the vignette both fought the photographs for the
   same job. The photographs do it better, so those come off. */
body::before{display:none}
body{box-shadow:none}

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


/* ── /contact spacing ────────────────────────────────────────────────────
   Anu: "spacing and lines don't match up properly." Three real faults, all
   measured rather than eyeballed:

   1. .fq carries margin-top:s6 with :first-child reset to 0. Inside .pair that
      rule fires per COLUMN, so "Your name" got 0 and "Email" got 32px, and the
      two fields in one row sat a third of an input apart.
   2. .pair itself is not a .fq, so it inherited no top margin and butted
      straight up against the field above it.
   3. .other ran the full 1032px wrap while every other thing in the form is
      620px, so the closing line and its rule overhung the column by 400px. */
.form .pair{margin-top:var(--s6)}
.form .pair .fq{margin-top:0}
.form .pair .fq .hint{min-height:0}
#form-wrap .other,.phead > .wrap > .other{max-width:620px}
