/* ==== 00-base.css ==== */
/* Mendoza Marketing — site stylesheet.
   Structure taken from /root/dronegodmax-src/site/css/site.css (the house
   generator's shape: tokens, buttons, glass nav, footer, reveal); the palette,
   the type and the motion contract are Drew's and are set out below.

   =====================================================================
   COLOUR — Drew's own hex values, from his README. One accent, never two.
   =====================================================================
   --bg      #050A0A   the canvas
   --panel   #1A1F1F   every raised surface
   --line    #2E3535   hairlines, never type
   --ink     #DDE4E1   body and display type — a refined cool grey,
                       not the README's #f0ffff (the owner, 2026-09-23:
                       "boutique premium readability"); the white stays in
                       his logo lettering only
   --green   #2de8b5   THE accent: links, rules, icons, the primary button
   --green-2 #7FF2D1   hover and glow only — never a second accent
   --green-line        --green at 35% alpha: hairlines where a block is
                       green-led (the service cards); a border, never type
   --muted   #93A3A3   computed here, not given: the one grey allowed to carry
                       words. Chosen as the lightest grey that still reads as
                       secondary while clearing AAA on both the canvas and a
                       panel (7.60 : 1 and 6.36 : 1 — see the table).

   CONTRAST — every pair this stylesheet actually sets type in, computed with
   the WCAG 2.x relative-luminance formula (docs/contrast.py in the sibling
   repos does the same arithmetic; these numbers were computed the same way).
   AA large text needs 3.0, AA body 4.5, AAA body 7.0.

     foreground        background      ratio     verdict
     --ink    #DDE4E1  --bg   #050A0A  15.42:1   AAA   body, display
     --ink    #DDE4E1  --panel#1A1F1F  12.91:1   AAA   card body
     --green  #2de8b5  --bg   #050A0A  12.64:1   AAA   links, eyebrows, figures
     --green  #2de8b5  --panel#1A1F1F  10.58:1   AAA   ghost button label
     --green-2#7FF2D1  --bg   #050A0A  14.73:1   AAA   hover state
     --green-2#7FF2D1  --panel#1A1F1F  12.32:1   AAA   hover state on a card
     --bg     #050A0A  --green#2de8b5  12.64:1   AAA   .btn label on the fill
     --bg     #050A0A  --green-2       14.73:1   AAA   .btn label, hovered
     --muted  #93A3A3  --bg   #050A0A   7.60:1   AAA   leads, footer links
     --muted  #93A3A3  --panel#1A1F1F   6.36:1   AA    muted text on a card

   Two pairs carry no type and are exempt by definition:
     --line   #2E3535  on --bg  1.59:1  hairlines only
     --panel  #1A1F1F  on --bg  1.19:1  surface only

   =====================================================================
   TYPE — three faces, each with one job (Google Fonts link in build.py)
   =====================================================================
   Space Grotesk 500/700  display. The bold-geometric register of Drew's own
                          references (EllesmereUI, Midnight Marketing). Not
                          DroneGodMax's condensed Bebas: this site must not
                          read as a Max clone.
   Inter 400/600          body.
   JetBrains Mono 400/600 labels, prices, dashboard figures — the techy voice
                          the client dashboards already speak in.

   =====================================================================
   MOTION — the house contract
   =====================================================================
   Everything hidden for effect is hidden under html.motion only, and carries
   a 2.8s self-reveal: a page whose script never arrives, or arrives broken,
   still shows all of its words. prefers-reduced-motion and no-JS both render
   the page complete. Nothing here is load-bearing.

   Breakpoints are mobile-first: the base rules are the phone, and 640 / 960 /
   1240 add to them in that order. 1240 is --max, so the last one is the point
   at which the page stops growing.
*/

:root{
  --bg:#050A0A; --panel:#1A1F1F; --line:#2E3535; --ink:#DDE4E1;
  --green:#2de8b5; --green-2:#7FF2D1;
  --green-line:rgba(45,232,181,.35); /* hairlines on green-led blocks (the services) */
  --muted:#93A3A3;                 /* 7.60:1 on --bg, 6.36:1 on --panel */
  --radius:14px; --radius-s:10px;
  --font-display:"Space Grotesk","Inter",system-ui,-apple-system,sans-serif;
  --font-body:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --font-mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --max:1240px; --nav-h:72px;
  --ease:cubic-bezier(.16,1,.3,1);
}

/* ------------------------------------------------------------------ base */
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font-body);
  font-size:16px;line-height:1.65;-webkit-font-smoothing:antialiased;
  overflow-x:hidden}
img,video,svg{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
h1,h2,h3,h4{margin:0;font-family:var(--font-display);font-weight:700;line-height:1.05;
  letter-spacing:-.02em}
h1{font-size:clamp(2.6rem,10vw,5.4rem)}
h2{font-size:clamp(2rem,6vw,3.4rem)}
h3{font-size:clamp(1.15rem,3.4vw,1.4rem);letter-spacing:-.01em;line-height:1.25}
h4{font-size:1rem}
p{margin:0 0 1em}
ul{margin:0;padding:0}
:focus-visible{outline:2px solid var(--green);outline-offset:3px;border-radius:4px}
::selection{background:var(--green);color:var(--bg)}

.wrap{width:min(var(--max),calc(100% - 2rem));margin-inline:auto}
@media (min-width:640px){.wrap{width:min(var(--max),calc(100% - 3rem))}}
section{padding:clamp(3.5rem,9vw,7rem) 0;position:relative}
.muted{color:var(--muted)}
.lead{font-size:1.06rem;color:var(--muted);max-width:60ch}
@media (min-width:960px){.lead{font-size:1.15rem}}

/* The two-tone headline: line one ink, line two green. Drew's references
   (Ellesmere's "Your entire interface. / One addon.") do exactly this, and it
   is the one place the accent is allowed to be this large. */
.h-two span{color:var(--green);display:block}

/* Mono is a voice, not a font-family swap: labels, prices and figures. */
.mono{font-family:var(--font-mono);font-weight:600;font-variant-numeric:tabular-nums;
  letter-spacing:.02em}
.eyebrow{display:inline-flex;align-items:center;gap:.6rem;font-family:var(--font-mono);
  font-size:.75rem;font-weight:600;letter-spacing:.18em;text-transform:uppercase;
  color:var(--green);margin-bottom:1rem}
.eyebrow::before{content:"";width:20px;height:2px;background:var(--green)}

.skip{position:absolute;left:-999px;top:0;z-index:1000;background:var(--green);
  color:var(--bg);padding:.6rem 1rem;font-weight:600}
.skip:focus{left:1rem;top:1rem}

/* --------------------------------------------------------------- buttons */
/* Two buttons and no third. The primary is the green fill with black type —
   12.64:1, and the only large area of accent on the page. The ghost is a
   PANEL fill with a hairline and green type: never transparent, because a
   transparent button over a photograph or a film is a button that disappears
   exactly when it is needed. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  padding:.85rem 1.5rem;border-radius:999px;border:1px solid transparent;
  font-family:var(--font-body);font-weight:600;font-size:.95rem;line-height:1;
  cursor:pointer;white-space:nowrap;
  transition:background .2s,border-color .2s,color .2s,transform .2s}
.btn:hover{transform:translateY(-2px)}
.btn{background:var(--green);color:var(--bg)}
.btn:hover{background:var(--green-2)}
.btn-ghost{background:var(--panel);border-color:var(--line);color:var(--green)}
.btn-ghost:hover{background:var(--panel);border-color:var(--green);color:var(--green-2)}
.btn-sm{padding:.6rem 1.05rem;font-size:.85rem}
.btn svg{width:18px;height:18px}
.btn-row{display:flex;gap:.7rem;flex-wrap:wrap}

/* ------------------------------------------------------------------- nav */
/* Glass: the blur sits on a pseudo-element, not on .nav itself — backdrop-filter
   on the header would make it the containing block for the dropdown and the
   phone panel and clip both to 72px (the bug DGM's stylesheet records). */
.nav{position:fixed;inset:0 0 auto 0;z-index:100;height:var(--nav-h);
  border-bottom:1px solid transparent;transition:border-color .3s}
.nav::before{content:"";position:absolute;inset:0;z-index:-1;
  background:rgba(5,10,10,.78);backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);opacity:0;transition:opacity .3s;
  pointer-events:none}
.nav.scrolled::before,.nav.open::before{opacity:1}
.nav.scrolled,.nav.open{border-color:var(--line)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;
  height:100%;gap:1rem}
/* The lockup Drew sent is square and stacked — mark over the name over the
   three words — so it is given most of the height of the bar instead of the
   44px a horizontal lockup would take, and it is the only image in the nav. */
/* The header lockup: his mark, then MENDOZA over MARKETING, both cut from his
   transparent PNG so they sit straight on the glass — no box, no radius. The
   mark is 15% taller than the words block (the owner, 2026-09-23), and the
   words 15% smaller than the first cut. On a phone: 46/34px, leaving room for
   the hamburger; on a desktop 53/39px inside the 72px bar. */
.brand{display:flex;align-items:center;gap:.4em;flex:0 0 auto;font-size:14px;perspective:320px}
.brand img{width:auto}
.brand-mark{height:46px}
.brand-words{height:34px}
@media (min-width:960px){.brand-mark{height:53px}.brand-words{height:39px}}

.nav-links{display:none;list-style:none;align-items:center;gap:.15rem}
.nav-links>li{position:relative}
.nav-links a,.nav-links button{display:flex;align-items:center;gap:.35rem;
  padding:.55rem .75rem;border-radius:var(--radius-s);font-family:inherit;
  font-size:.92rem;font-weight:700;color:var(--ink);background:none;border:0;
  cursor:pointer;transition:color .2s,background .2s}
/* The top level is --ink, bold — the same near-white the submenu is set in
   (Drew, 2026-10-03: "change the main menu items to bold white … the white
   in the submenu is closer to the color I want"). Rule 0 still holds: --ink,
   never #f0ffff. 15.42:1 on the canvas. */
.nav-links a:hover,.nav-links button:hover{color:var(--ink);background:rgba(240,255,255,.08)}
.nav-links a.active{color:var(--green)}
.nav-links .has-menu>button svg{width:12px;height:12px;opacity:.75;transition:transform .2s}
.nav-links .has-menu.open>button svg{transform:rotate(180deg)}
.submenu{list-style:none;padding:.35rem;margin:0}
.submenu a{display:block;padding:.55rem .75rem;border-radius:9px;color:var(--ink);
  font-weight:600}
.submenu a small{display:block;margin-top:.1rem;font-family:var(--font-mono);
  font-size:.72rem;font-weight:400;color:var(--muted)}
.submenu a:hover{background:rgba(240,255,255,.08);color:var(--green)}
/* The quote pill is a link inside .nav-links, so the nav's own link rule
   (higher specificity than .btn) would strip its fill. Put it back here rather
   than weakening the nav rule: this is the one green object in the bar. */
.nav-cta{margin-left:.4rem}
.nav-links .nav-cta .btn{background:var(--green);color:var(--bg);
  padding:.6rem 1.05rem;border-radius:999px;font-size:.88rem;justify-content:center}
.nav-links .nav-cta .btn:hover{background:var(--green-2);color:var(--bg)}
.nav-toggle{display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:var(--radius-s);border:1px solid var(--line);
  background:var(--panel);color:var(--ink);cursor:pointer}
.nav-toggle svg{width:22px;height:22px}

/* The phone menu: the links become a full-height sheet under the bar. */
@media (max-width:959px){
  .nav-links{position:fixed;top:var(--nav-h);left:0;right:0;bottom:0;
    height:calc(100dvh - var(--nav-h));flex-direction:column;align-items:stretch;
    gap:.1rem;padding:1rem 1.25rem 2.5rem;overflow:auto;
    background:rgba(5,10,10,.94);backdrop-filter:blur(14px);
    -webkit-backdrop-filter:blur(14px)}
  .nav.open .nav-links{display:flex}
  .nav-links a,.nav-links button{width:100%;justify-content:space-between;
    font-size:1.08rem;padding:.8rem .5rem;color:var(--ink)}
  .submenu{display:none;padding:0 0 .5rem .9rem}
  .has-menu.open .submenu{display:block}
  .submenu a{font-size:.98rem;color:var(--muted)}
  .nav-cta{margin:1.2rem 0 0;width:100%}
  .nav-cta .btn{width:100%}
}
@media (min-width:960px){
  .nav-links{display:flex}
  .nav-toggle{display:none}
  .submenu{position:absolute;top:calc(100% + 8px);left:0;min-width:250px;
    background:rgba(26,31,31,.96);backdrop-filter:blur(12px);
    -webkit-backdrop-filter:blur(12px);border:1px solid var(--line);
    border-radius:var(--radius);box-shadow:0 24px 60px -24px rgba(0,0,0,.9);
    opacity:0;visibility:hidden;transform:translateY(6px);transition:all .18s}
  .has-menu:hover .submenu,.has-menu.open .submenu,
  .has-menu:focus-within .submenu{opacity:1;visibility:visible;transform:none}
}

/* ------------------------------------------------------------- page hero */
.page-hero{position:relative;isolation:isolate;overflow:hidden;
  padding:calc(var(--nav-h) + 3.5rem) 0 3.5rem;display:flex;align-items:flex-end}
@media (min-width:960px){.page-hero{min-height:62svh;
  padding:calc(var(--nav-h) + 5rem) 0 4.5rem}}
.hero-media{position:absolute;inset:0;z-index:-2}
.hero-media img,.hero-media video{width:100%;height:100%;object-fit:cover}
.hero-shade{position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(5,10,10,.72),rgba(5,10,10,.35) 45%,var(--bg))}
.hero-inner{max-width:900px}
.hero-inner .lead{margin:1.2rem 0 2rem}
.crumbs{display:flex;gap:.5rem;font-family:var(--font-mono);font-size:.74rem;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-bottom:1rem}
.crumbs a:hover{color:var(--green)}

/* ----------------------------------------------------------- panels/grid */
.grid{display:grid;gap:1.2rem}
@media (min-width:640px){.grid-2,.grid-3{grid-template-columns:repeat(2,1fr)}}
@media (min-width:960px){.grid-3{grid-template-columns:repeat(3,1fr)}}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  padding:1.4rem}

/* ------------------------------------------------------------ price card */
.price{display:flex;flex-direction:column;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--radius);padding:1.6rem;
  transition:border-color .25s,transform .25s}
.price:hover{border-color:var(--green);transform:translateY(-3px)}
.price.featured{border-color:var(--green)}
.price .tier{font-family:var(--font-mono);font-size:.72rem;font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--green)}
.price h3{margin:.5rem 0 .3rem}
.price .amount{font-family:var(--font-mono);font-weight:600;font-size:2rem;
  line-height:1.15;color:var(--ink);margin:.4rem 0 1rem;font-variant-numeric:tabular-nums}
.price .amount small{display:block;font-family:var(--font-body);font-weight:400;
  font-size:.85rem;color:var(--muted);margin-top:.3rem}
.price .amount .qual{font-size:.8rem;color:var(--muted);margin-right:.35rem}
.price ul{list-style:none;display:grid;gap:.55rem;margin:0 0 1.4rem;
  font-size:.94rem;color:var(--muted)}
.price li{display:flex;gap:.55rem}
.price li::before{content:"";flex:0 0 auto;width:7px;height:7px;margin-top:.55rem;
  border-radius:2px;background:var(--green)}
.price .btn{margin-top:auto}
.price .badge{align-self:flex-start;font-family:var(--font-mono);font-size:.68rem;
  font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--bg);
  background:var(--green);border-radius:999px;padding:.25rem .6rem;margin-bottom:.8rem}

/* ------------------------------------------------------------------- cta */
.cta-wrap{padding-bottom:clamp(4rem,9vw,7rem)}
/* Drew, 2026-10-15: "Increase spacing between the sentences and text
   elements. The current layout feels slightly cramped." More room above and
   below the words, a beat between the heading and the sentence, a looser
   line, and the buttons set further off. */
.cta{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  padding:clamp(2.6rem,7vw,4.6rem) clamp(1.4rem,6vw,3.5rem);text-align:center}
.cta h2{margin-bottom:1.1rem}
.cta p{color:var(--muted);max-width:54ch;margin:0 auto;line-height:1.75}
.cta .btn-row{justify-content:center;margin-top:2.3rem;gap:.9rem}

/* ----------------------------------------------------------------- swipe */
.swipe{position:relative}
.swipe-rail{display:flex;gap:1rem;overflow-x:auto;scroll-snap-type:x mandatory;
  scrollbar-width:none;padding-bottom:.5rem}
.swipe-rail::-webkit-scrollbar{display:none}
.swipe-slide{flex:0 0 86%;scroll-snap-align:center}
@media (min-width:640px){.swipe-slide{flex-basis:48%}}
@media (min-width:960px){.swipe-slide{flex-basis:32%}}
.swipe-arw{position:absolute;top:40%;display:none;width:44px;height:44px;
  border-radius:50%;border:1px solid var(--line);background:var(--panel);
  color:var(--green);cursor:pointer;align-items:center;justify-content:center}
.swipe-arw svg{width:20px;height:20px}
.swipe-arw.prev{left:-18px}.swipe-arw.next{right:-18px}
@media (min-width:960px){.swipe-arw{display:inline-flex}}
.swipe-dots{display:flex;gap:.4rem;justify-content:center;margin-top:1rem}
.swipe-dot{width:8px;height:8px;padding:0;border:0;border-radius:50%;
  background:var(--line);cursor:pointer}
.swipe-dot.is-on{background:var(--green)}
.swipe-hint{text-align:center;font-family:var(--font-mono);font-size:.72rem;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted);margin-top:.6rem}
@media (min-width:960px){.swipe-hint{display:none}}

/* ---------------------------------------------------------------- footer */
/* Max's footer shape, in Drew's colours: brand + tagline + socials, then
   Services, Explore and Contact, then the Green Flash line across the bottom. */
footer{border-top:1px solid var(--line);background:var(--panel);
  padding:3.5rem 0 2rem}
.foot-grid{display:grid;gap:2.2rem}
@media (min-width:640px){.foot-grid{grid-template-columns:repeat(2,1fr)}}
@media (min-width:960px){.foot-grid{grid-template-columns:1.6fr 1fr 1fr 1fr}}
.foot-brand img{height:150px;width:auto}
.foot-brand p{margin-top:1rem;max-width:38ch;font-size:.95rem;color:var(--muted)}
.foot-tag{font-family:var(--font-mono);font-size:.8rem;letter-spacing:.1em;
  color:var(--green);margin-top:.8rem}
.foot-grid h4{font-family:var(--font-mono);font-size:.72rem;font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--green);margin-bottom:1rem}
.foot-grid ul{list-style:none;display:grid;gap:.55rem;min-width:0}
.foot-grid ul a,.foot-grid ul span{font-size:.95rem;color:var(--muted);
  overflow-wrap:anywhere}
.foot-grid ul a:hover{color:var(--green)}
.socials{display:flex;gap:.55rem;margin-top:1.2rem}
.socials a,.socials span{display:grid;place-items:center;width:40px;height:40px;
  border-radius:50%;border:1px solid var(--line);color:var(--ink);
  transition:background .2s,color .2s,border-color .2s}
.socials a:hover{background:var(--green);color:var(--bg);border-color:var(--green)}
/* A social account Drew has not given us yet is drawn, not linked: the shape
   of the footer is right and the dead link is not there to click. */
.socials span{color:var(--muted);opacity:.45}
.socials svg{width:18px;height:18px}
.foot-bottom{display:flex;flex-wrap:wrap;gap:.8rem;justify-content:space-between;
  margin-top:2.8rem;padding-top:1.4rem;border-top:1px solid var(--line);
  font-size:.85rem;color:var(--muted)}
.foot-bottom a{color:var(--green)}
.foot-bottom a:hover{color:var(--green-2)}

/* ---------------------------------------------------------------- 404 */
.nf{min-height:70svh;display:flex;align-items:center}
.nf .mono{color:var(--green);font-size:.8rem;letter-spacing:.2em}

/* -------------------------------------------------------------- lightbox */
.lb{position:fixed;inset:0;z-index:300;display:none;place-items:center;
  background:rgba(5,10,10,.94);padding:1.2rem}
.lb.on{display:grid}
.lb-inner{position:relative;width:min(1100px,100%);max-height:82svh}
.lb-inner img,.lb-inner video,.lb-inner iframe{width:100%;max-height:82svh;
  border:0;border-radius:var(--radius);object-fit:contain}
.lb-close{position:absolute;top:-52px;right:0;width:40px;height:40px;border-radius:50%;
  border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer;
  display:grid;place-items:center}
.lb-close svg{width:20px;height:20px}
.lb-cap{margin-top:.8rem;text-align:center;color:var(--muted);font-size:.9rem}

/* ---------------------------------------------------------------- reveal */
/* Hidden only under html.motion — the class the inline head script adds when
   the browser runs JavaScript and has not asked for reduced motion. The
   animation is the bail-out: at 2.8s the element reveals itself whether or not
   the observer ever ran. .in wins over it the moment the observer fires. */
html.motion .rv{opacity:0;transform:translateY(22px);
  transition:opacity .8s var(--ease),transform .8s var(--ease);
  animation:mmSelf 0s linear 2.8s forwards}
html.motion .rv.rv.in{opacity:1;transform:none;animation:none}
.rv.in{opacity:1;transform:none;animation:none}
.rv-d1{transition-delay:.1s}.rv-d2{transition-delay:.2s}.rv-d3{transition-delay:.3s}
@keyframes mmSelf{to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .rv{opacity:1;transform:none;transition:none;animation:none}
  .btn:hover,.price:hover{transform:none}
}

/* ------------------------------------------------------- feature icons */
/* The small icon on a feature box (the Included trio, the beats): art.py's
   24px drawing in a 40px round well, green on the well, the hairline in the
   green at 35% like the service cards. One size everywhere it appears. */
.f-ico{display:inline-grid;place-items:center;width:40px;height:40px;
  border-radius:12px;background:var(--well);border:1px solid var(--green-line);
  color:var(--green);margin-bottom:1rem}
.f-ico svg{width:24px;height:24px}

/* ==== 10-home.css ==== */
/* Mendoza Marketing — the Home page.

   This file styles Home and nothing else: the hero, the client strip, the
   module grid, the results band, the reels, the beats, the About teaser and
   the twin cards. The tokens, the type, the buttons, the nav, the footer and
   the reveal contract are 00-base.css's and are not restated here.

   site/css/site.css is concatenated from site/css/parts/*.css in name order,
   so this file lands after the base and before the inner pages. It may
   therefore lean on every token the base declares, and it must not redefine
   any of them.

   =====================================================================
   CONTRAST — the pairs this file introduces (CLAUDE.md rule 9: add a pair,
   add the number). Computed with the WCAG 2.x relative-luminance formula,
   the same arithmetic as the table at the top of 00-base.css.

   --band #070d0d is the recessed band the strip, the results, the beats and
   the twin cards sit on: one notch darker than the canvas, which is what
   separates them without drawing a line. --well #0b1413 is the inside of a
   demo well and of an icon chip — a hole in a panel rather than a surface.

     foreground        background      ratio     verdict
     --ink    #DDE4E1  --band #070d0d  15.16:1   AAA   headings, figures
     --green  #2de8b5  --band #070d0d  12.42:1   AAA   eyebrows, deltas
     --green-2#7FF2D1  --band #070d0d  14.48:1   AAA   hover
     --muted  #93A3A3  --band #070d0d   7.47:1   AAA   labels, leads
     --ink    #DDE4E1  --well #0b1413  14.47:1   AAA   the Meta counter
     --green  #2de8b5  --well #0b1413  11.86:1   AAA   the card icons, the
                                                       leads figure
     --ink    #DDE4E1  --band #070d0d  15.16:1   AAA   the strip's client name
     --muted  #93A3A3  --well #0b1413   7.13:1   AAA   a well's own label

   Two pairs carry no type and are exempt by definition:
     --band   #070d0d  on --bg    #050A0A  1.02:1  surface only
     --well   #0b1413  on --panel #1A1F1F  1.12:1  surface only

   =====================================================================
   MOTION — every animated thing on this page is step 5's, and every one of
   them is animated ON TOP of a finished element: the bars are drawn at their
   widths here, the chart holds all ninety of its points, the figures are
   printed, the films have their posters. Nothing below hides anything from a
   visitor with no script. The only hiding on the page is .rv, which is the
   base's contract and bails out after 2.8s.
*/

:root{
  --band:#070d0d;                 /* 19.07:1 with --ink — see the table */
  --well:#0b1413;                 /* the inside of a well or an icon chip */
}

/* A band is the same page, one notch back. Hairlines top and bottom so two
   bands in a row still read as two. */
.band{background:var(--band);border-block:1px solid var(--line)}

/* --------------------------------------------------------------- the hero */
/* Taller than an inner page's hero — this is the one full-height thing on the
   site — and the words sit on the floor of it, over the film's darkest part.
   svh rather than vh so a phone's collapsing address bar does not push the
   buttons off the bottom. */
.home-hero{position:relative;isolation:isolate;overflow:hidden;
  min-height:88svh;display:flex;align-items:flex-end;
  padding:calc(var(--nav-h) + 3rem) 0 3.5rem}
@media (min-width:960px){.home-hero{min-height:92svh;
  padding:calc(var(--nav-h) + 5rem) 0 4rem}}
.home-hero .hero-media{position:absolute;inset:0;z-index:-2}
.home-hero .hero-media video{width:100%;height:100%;object-fit:cover;
  filter:saturate(.85)}
/* The shade is what makes white type legible on a film nobody has graded for
   it: dark at the top so the glass nav has something to sit on, dark again at
   the bottom where the words are, and resolved into the canvas so the band
   below begins without a seam. */
.home-hero .hero-shade{position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(5,10,10,.66),rgba(5,10,10,.5) 45%,
    rgba(5,10,10,.82) 78%,var(--bg))}
.home-hero .hero-inner{max-width:980px}
/* Bigger than an inner page's h1: Drew's references set the home headline as
   large as the canvas allows, and this is the one place on the site that
   does it. */
.home-hero h1{font-size:clamp(3rem,7.4vw,6.6rem);margin-top:.15em}
.home-hero .lead{margin:1.3rem 0 0;max-width:46ch}
.home-hero .btn-row{margin-top:1.8rem}

/* ------------------------------------------------------- the client strip */
.strip{padding:1.6rem 0}
.strip .wrap{display:flex;align-items:center;justify-content:space-between;
  gap:1.2rem 2rem;flex-wrap:wrap}
.strip-k{font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted)}
.strip-logos{list-style:none;display:flex;align-items:center;
  gap:1rem;flex-wrap:wrap;margin:0;padding:0}
@media (min-width:640px){.strip-logos{gap:1.6rem}}
/* Monochrome by filter, not by a second set of files: the client's own logo
   is what is on disk, and the strip's job is to be a strip. brightness lifts
   the darker marks so grey does not become black on black. */
.chip{width:48px;height:48px;border-radius:50%;overflow:hidden;
  background:var(--panel);border:1px solid var(--line);
  filter:grayscale(1) brightness(1.12);opacity:.75;
  transition:filter .25s,opacity .25s,box-shadow .25s}
@media (min-width:640px){.chip{width:64px;height:64px}}
.chip img{width:100%;height:100%;object-fit:cover}
/* One chip is left in colour so the row reads as logos rather than as discs.
   Hover lights any of them, with no script involved. */
.chip.is-lit,.chip:hover{filter:none;opacity:1}
.chip.is-lit{box-shadow:0 0 0 2px var(--green)}
/* The lit chip's name. --ink rather than --muted so it reads as the answer to
   the ring, and a fixed slot on a wide screen so the row does not shuffle
   when a long name ("Zen Zone Property Maintenance") follows a short one.
   Below 960 the strip wraps and the name takes its own line. */
.strip-name{color:var(--ink);transition:opacity .22s linear}
@media (min-width:960px){.strip-name{flex:0 0 19rem;text-align:right}}
html.motion .strip-name.is-swap{opacity:0}

/* ------------------------------------------------------- a section heading */
.sec-head{display:flex;align-items:flex-end;justify-content:space-between;
  gap:1.2rem 2rem;flex-wrap:wrap;margin-bottom:2.4rem}
.sec-head .lead{max-width:36ch;margin:0}
.sec-head .btn{flex:0 0 auto}

/* --------------------------------------------------------- the module grid */
.svc-grid{display:grid;gap:1rem}
@media (min-width:640px){.svc-grid{grid-template-columns:repeat(2,1fr)}}
@media (min-width:960px){.svc-grid{grid-template-columns:repeat(3,1fr)}}
.svc{display:flex;flex-direction:column;min-height:250px;
  background:var(--panel);border:1px solid var(--green-line);
  border-radius:var(--radius);padding:1.5rem;
  transition:border-color .25s,transform .25s,background .25s}
.svc:hover{border-color:var(--green);transform:translateY(-3px);
  background:color-mix(in srgb,var(--panel) 94%,var(--green))}
/* The services read green (the owner, 2026-09-23: "make services text/elements
   green, keep visual hierarchy strong"): the name, the price, the icon and the
   hairlines are the accent; the one-line description stays --muted so the
   order on the card is name > price > description, not a wall of one colour.
   The hairlines are the green at 35% so six cards together do not shout. */
.svc h3{color:var(--green);font-weight:700}
.svc-top{display:flex;align-items:flex-start;justify-content:space-between;
  gap:1rem;margin-bottom:1.2rem}
/* The icon sits in a well of its own — the same hole the demo is cut into
   lower down, at 44px. The drawing inside it is art.py's, at exactly this
   size, so no stroke is ever scaled. */
.ico{flex:0 0 auto;display:grid;place-items:center;width:48px;height:48px;
  border-radius:13px;background:var(--well);border:1px solid var(--green-line);
  color:var(--green);transition:border-color .25s,color .25s}
/* 44px, exactly the size art.py draws them: one SVG unit to one CSS pixel, so
   a 1.6px stroke is 1.6px and nothing lands on a half pixel. The chip is the
   drawing plus its margin, not a box the drawing is squeezed into. */
.ico svg{width:44px;height:44px}
.svc:hover .ico{border-color:var(--green);color:var(--green-2)}
.svc-price{font-size:.78rem;letter-spacing:.04em;color:var(--green);
  text-align:right;padding-top:.35rem}
.svc p{color:var(--muted);font-size:.95rem;margin:.6rem 0 1.4rem}
/* ------------------------------------------------- the demo wells (the six)
   The stage each card's demo runs on, and — because home.py draws the demo's
   FINISHED state into it — a small picture of the service with no script at
   all. One height for all six, one radius, one green hairline, never dashed:
   six wells that do not match read as six unfinished boxes.

   120px so a reel poster fills it and reads, a counter can be set large, and
   the leads line has room for a shape. Everything inside is absolutely
   positioned, so a demo can move what is in a well and can never reflow the
   card. Positions are percentages of the well, so the phone's shorter well
   needs no second set of numbers. */
.svc-demo{position:relative;margin-top:auto;height:120px;border-radius:12px;
  background:var(--well);border:1px solid var(--green-line);overflow:hidden}
@media (max-width:519px){.svc-demo{height:106px}}
.svc:hover .svc-demo{border-color:var(--green)}

/* The website card: the page that has drawn itself. A header rule, two lines
   of copy that span the well, and the button the cursor presses. */
.wire{position:absolute;left:14px;height:6px;border-radius:3px;
  background:var(--green);display:block}
.wire.w1{top:15%;height:11px;border-radius:5px;width:64%}
.wire.w2{top:40%;width:calc(100% - 28px);opacity:.5}
.wire.w3{top:56%;width:52%;opacity:.32}
.wf-btn{position:absolute;right:14px;bottom:13%;width:62px;height:19px;
  border-radius:5px;background:var(--green);opacity:.9}

/* The Meta card: the campaign's reach, set large and centred over its label.
   The figure is the first Results tile's, printed here by the server, and the
   demo only ever replaces it with another formatting of the same number — it
   is never emptied, blanked or faded out. */
.mm-num{position:absolute;left:8px;right:8px;top:50%;transform:translateY(-50%);
  text-align:center;font-size:clamp(1.55rem,4.2vw,2rem);line-height:1;
  font-weight:600;letter-spacing:.01em;color:var(--ink);
  font-variant-numeric:tabular-nums}
.mm-cap{position:absolute;left:10px;right:10px;bottom:11px;text-align:center;
  font-size:.62rem;letter-spacing:.15em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.mm-cap-b{text-align:left;right:auto;max-width:60%}
.mm-up{position:absolute;right:14px;top:50%;margin-top:-1.2em;font-style:normal;
  font-size:1rem;color:var(--green)}

/* The social card: the reel at the well's full width and height. Portrait
   posters cropped to the well rather than a phone-shaped strip in the middle
   of it — the demo cross-fades between the three. object-position sits a
   little above centre, which is where the subject of a vertical reel is. */
.mm-reel{position:absolute;inset:0;opacity:0}
.mm-reel.is-on{opacity:1}
/* The social well is the one tall well: a reel is portrait, so it gets the
   height to be shown whole — the poster in a 9:16 frame over a blurred,
   darkened copy of itself filling the well edge to edge. */
.svc-demo[data-live="social"]{height:236px}
@media (max-width:519px){.svc-demo[data-live="social"]{height:220px}}
.mm-reel-bg{position:absolute;inset:0;width:100%;height:100%;max-width:none;
  object-fit:cover;filter:blur(16px) brightness(.45) saturate(1.1);transform:scale(1.18)}
.mm-reel-img{position:absolute;top:10px;bottom:10px;left:50%;height:calc(100% - 20px);
  width:auto;aspect-ratio:9/16;max-width:none;transform:translateX(-50%);object-fit:cover;
  border-radius:10px;border:1px solid var(--green-line);box-shadow:0 8px 24px rgba(0,0,0,.45)}

/* The logo card: a mark on its construction grid. The guides are hairlines
   in the card's own --green-line, the ring and the disc are the accent, and
   the monogram is knocked out of the disc in the well's own colour — one
   accent, and the M is a hole in it rather than a second colour. */
.mm-logo{position:absolute;inset:0;width:100%;height:100%}
.mm-lg-guides{fill:none;stroke:var(--green-line);stroke-width:.8}
.mm-lg-disc{fill:var(--green)}
.mm-lg-ring{fill:none;stroke:var(--green);stroke-width:2.4;stroke-linecap:round}
.mm-lg-m,.mm-lg-ko{fill:none;stroke-width:6;stroke-linecap:round;
  stroke-linejoin:round}
.mm-lg-m{stroke:var(--green)}
.mm-lg-ko{stroke:var(--well)}

/* The drone card: a horizon with marks on it, the shadow, and the quad above
   them. The marks are what makes a slide visible — a line sliding along itself
   is a line standing still. */
.horizon{position:absolute;left:0;right:0;top:74%;height:1px;
  background:var(--green-line)}
.ground{position:absolute;left:0;right:0;top:74%;height:9px;display:block}
.tick{position:absolute;top:0;width:1px;height:7px;background:var(--green);
  opacity:.34}
.mm-shadow{position:absolute;left:14px;top:74%;margin-top:-2px;width:46px;
  height:4px;border-radius:50%;background:var(--green);opacity:.3;
  filter:blur(1px)}
.quad{position:absolute;left:14px;top:44%;width:46px;height:26px;
  transform:translateY(-50%);color:var(--green)}

/* The lead-conversion card: the daily-leads line, edge to edge, and the Leads
   Center figure at the end of it. The points are results.leads_points(),
   thinned; the stroke does not stretch with the box. */
.mm-chart{position:absolute;inset:0;width:100%;height:100%}
.mm-area{fill:var(--green);opacity:.13}
.mm-line{fill:none;stroke:var(--green);stroke-width:2;
  stroke-linejoin:round;stroke-linecap:round}
/* The figure sits on its own patch of the well: the line's own peak runs
   under it at the right-hand end, and a green number crossed by a green
   stroke is not a number anybody can read. */
.mm-fig{position:absolute;right:10px;top:9px;font-size:1.15rem;
  font-weight:600;color:var(--green);font-variant-numeric:tabular-nums;
  background:var(--well);border-radius:5px;padding:1px 5px;line-height:1.15}

/* ------------------------------------------------------------- the results */
.stats{display:grid;gap:1rem}
@media (min-width:560px){.stats{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1100px){.stats{grid-template-columns:repeat(4,1fr)}}
.stat{background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);padding:1.3rem;display:grid;
  align-content:start;gap:.15rem;min-width:0}
.stat-k{font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted)}
/* The figure. Tabular mono, so a count-up cannot make the tile change width
   as it runs. */
.stat-v{font-size:clamp(1.7rem,4.4vw,2.1rem);line-height:1.1;color:var(--ink);
  margin:.55rem 0 .1rem;overflow-wrap:anywhere}
.stat-up{font-size:.8rem;color:var(--green)}
.stat-note{font-size:.86rem;color:var(--muted);margin-top:.35rem}

.panels{display:grid;gap:1rem;margin-top:1rem}
@media (min-width:960px){.panels{grid-template-columns:1fr 1fr}}
.panels .panel{padding:1.3rem;min-width:0}
.panel-k{font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);margin-bottom:1rem}

/* What the row is showing: his real mix, or an example one (site.js). The
   name is the accent, the rest --muted — both already in the table. */
.mix-k{font-size:.74rem;letter-spacing:.06em;color:var(--muted);
  margin:-.45rem 0 .9rem}
.mix-k b{color:var(--green);font-weight:600;transition:opacity .25s linear}

/* One bar: the label, the track, the figure. The track is the full row width
   at every viewport, so four bars read as one chart rather than four. */
.bar{display:grid;grid-template-columns:4.6rem 1fr 3.2rem;align-items:center;
  gap:.75rem;margin:.7rem 0;font-size:.86rem;color:var(--muted)}
.bar-k{overflow:hidden;text-overflow:ellipsis}
.bar-track{height:8px;border-radius:4px;background:var(--well);
  border:1px solid var(--line);overflow:hidden}
.bar-track b{display:block;height:100%;background:var(--green);
  border-radius:4px;transition:width .9s var(--ease)}
/* A bar with a real but tiny number still has to be visible: 1.7K against
   365K is half a percent, and half a percent of a track is nothing at all. */
.bar-track b[style]:not([style*=":0%"]){min-width:3px}
.bar-v{text-align:right;color:var(--ink);font-size:.82rem}

.chart-box{height:120px}
.chart{width:100%;height:100%;display:block}
.chart-line{fill:none;stroke:var(--green);stroke-width:2;
  stroke-linejoin:round;stroke-linecap:round}
.chart-fill{fill:rgba(45,232,181,.12);stroke:none}

/* --------------------------------------------------------------- the reels */
/* The reels take the section's full width (Drew, 2026-10-03: "rework the
   sizing/layout so the videos fill the section better … there is too much
   unused space"). On a wide screen they are three across the whole
   container, each as tall as the screen can show at once — a frame's width
   is capped at 9/16 of 78svh, so a short laptop screen never has to scroll
   to see the bottom of a reel. On a phone they become a rail you swipe,
   each frame most of the screen wide with the next one showing at the edge,
   which is also how the row grows when more reels arrive. */
.reels-sec .reels{display:grid;grid-auto-flow:column;grid-auto-columns:74%;gap:.9rem;
  overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;
  --bleed:1rem;margin-inline:calc(var(--bleed) * -1);padding-inline:var(--bleed);
  scroll-padding-inline:var(--bleed);max-width:none}
/* The rail runs to the screen's edges: the bleed is .wrap's own gutter. */
@media (min-width:640px){.reels-sec .reels{--bleed:1.5rem}}
.reels-sec .reels::-webkit-scrollbar{display:none}
@media (min-width:760px){
  .reels-sec .reels{grid-auto-flow:row;grid-template-columns:repeat(3,1fr);gap:1.5rem;
    overflow:visible;margin-inline:0;padding-inline:0}
}
.reels-aside{display:grid;gap:1rem;justify-items:start}
.phone{position:relative;aspect-ratio:9/16;border-radius:26px;overflow:hidden;
  background:#000;border:1px solid var(--line);scroll-snap-align:start;
  width:100%;max-width:calc(78svh * 9 / 16);justify-self:center}
.phone video{width:100%;height:100%;object-fit:cover}
/* The glyph is decoration on a poster, not a control: it is aria-hidden and
   it does not take the pointer, because until step 5 lands there is nothing
   for a click to do. */
.play{position:absolute;right:12px;bottom:12px;width:40px;height:40px;
  border-radius:50%;display:grid;place-items:center;pointer-events:none;
  background:var(--green);color:var(--bg)}
.play svg{width:18px;height:18px;margin-left:2px}

/* --------------------------------------------------------------- the beats */
.beats{list-style:none;display:grid;gap:1.4rem;margin:0;padding:0}
@media (min-width:640px){.beats{grid-template-columns:repeat(3,1fr);gap:1rem}}
.beat{border-top:2px solid var(--green);padding-top:1.2rem}
.beat .f-ico{margin-bottom:.8rem}
.beat-k{font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted)}
.beat h3{margin-top:.55rem;font-size:1.12rem}

/* --------------------------------------------------------------- the about */
.about{display:grid;gap:2rem;align-items:center}
@media (min-width:860px){.about{grid-template-columns:1fr 1.4fr;gap:2.8rem}}
/* His headshot is a tall portrait; left at its own ratio it is a metre of
   photograph next to two lines of text. The frame is 4:5 and the crop is
   taken from the top, which is where his face is. */
.about-pic{max-width:420px}
.about-pic picture{display:block}
/* height:auto is not optional here: the width/height attributes pic() writes
   are presentational hints, so an <img> given only a width keeps the file's
   own pixel height and the photograph comes out three times too tall. */
.about-pic img{width:100%;height:auto;aspect-ratio:4/5;object-fit:cover;
  object-position:50% 18%;border-radius:var(--radius);
  border:1px solid var(--line);display:block}
.about-txt .lead{margin:1.2rem 0 1.6rem}

/* ---------------------------------------------------------- the twin cards */
.twin{display:grid;gap:1rem}
@media (min-width:640px){.twin{grid-template-columns:1fr 1fr}}
.tw{display:flex;flex-direction:column;align-items:flex-start;
  background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);padding:1.6rem;
  transition:border-color .25s}
.tw:hover{border-color:var(--green)}
.tw p{color:var(--muted);font-size:.95rem;margin:.7rem 0 1.4rem;max-width:34ch}
.tw h3{font-size:1.25rem}
.tw .btn{margin-top:auto}

/* -------------------------------------------------------- the service mark */
/* art.service_mark(): Drew's lockup with the service named under it, until
   the vector arrives and the name can be set into the mark itself. It is used
   by the service pages rather than by Home; these are the defaults so the
   markup is never naked, and 20-pages.css — which lands after this file — is
   free to say otherwise. */
.mark{display:inline-grid;justify-items:center;gap:.7rem}
.mark img{width:clamp(120px,22vw,180px);height:auto;border-radius:10px}
.mark-cap{font-size:.74rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--green)}

@media (prefers-reduced-motion:reduce){
  .svc:hover,.tw:hover{transform:none}
  .bar-track b{transition:none}
}

/* ==== 15-motion.css ==== */
/* Mendoza Marketing — the motion states.

   Step 5's stylesheet, and only step 5's: every rule in this file describes a
   state that site.js puts an element into, or styles a node site.js builds.
   Nothing here lays out a page, and nothing here is a second copy of
   10-home.css's geometry — the wells, the wheels and the tracks are already
   sized there and this file moves what is in them.

   site/css/site.css concatenates site/css/parts/*.css in name order, so this
   lands after 10-home.css and may lean on every token the base declares.

   =====================================================================
   THE CONTRACT (CLAUDE.md rule 4)
   Every selector in this file starts at `html.motion`. With no script, or
   with prefers-reduced-motion asked for, the class is not on the document,
   not one of these rules matches, and the page is exactly the server's HTML:
   the wires at their widths, the quad parked, the figures printed, the bars
   drawn, the chart whole. Nothing below hides anything from anybody, and
   nothing below supplies a word or a figure the page does not already hold.

   =====================================================================
   CONTRAST (CLAUDE.md rule 9)
   This file introduces no new colour pair. Everything it sets type in is
   already computed in the two tables above it:

     --bg     #050A0A  on --green #2de8b5  12.64:1  AAA  the "Sound on" chip

   The wells' own type — the Meta counter in --ink, the leads figure in
   --green, a well's label in --muted, all on --well — is set
   in 10-home.css with the rest of the wells' geometry, and is in that file's
   table: the server prints those words and this file only moves them.

   The wheels of a rolling figure are the figure's own colour on the figure's
   own surface — they are the same glyphs, moved — so they add no pair either.
*/

/* =====================================================================
   THE LIVE TILES — three seconds of the service, off scroll, on a loop

   One verb per card, no score, nothing that unlocks. The well already HOLDS
   the demo's finished state — home.py draws it there — so site.js arms it,
   plays it, holds the end for a beat and plays it again while the card is on
   screen. Nothing triggers on hover, on a tap or on focus: the card is a link
   and one tap is Learn more (CLAUDE.md rule 4).

   Which is why this section is nearly empty. The run is arithmetic: site.js
   writes the transforms and opacities inline, frame by frame, and strips them
   again when the card leaves the viewport. There is no CSS state to hide
   anything in, and therefore nothing for a missing or broken script to leave
   hidden — a stronger reading of the motion contract than the file's own
   header promises. What is left below are the two things that only make sense
   while a script is running them: the cursor the website card's demo moves,
   and the pen drawing the mark on the logo card. .mm-run is the class site.js
   puts on a card whose demo is live.
   ===================================================================== */

/* The website card's cursor: a ring with a dot in it, so it reads as a pointer
   rather than as a bullet. Built by site.js, positioned by it on every frame,
   and drawn from the well's top-left corner — hence the margins. */
html.motion .mm-cursor{position:absolute;left:0;top:0;width:12px;height:12px;
  margin:-6px 0 0 -6px;border-radius:50%;border:1.7px solid var(--ink);
  background:rgba(240,255,255,.22);opacity:0;pointer-events:none}

/* The social card's reels cross-fade rather than cut. */
html.motion .mm-reel{transition:opacity .62s var(--ease)}

/* The logo card's pen, its handle and its anchor points — built by site.js
   inside the mark's own SVG, so they share its coordinates, and only there
   while a run is drawing. The pen is --ink, the one thing in the well that is
   not the mark; anchors are hollow squares, the way every vector tool draws
   them. */
html.motion .mm-lg-pen{fill:var(--ink);opacity:0;pointer-events:none}
html.motion .mm-lg-pen .slit{fill:var(--well)}
html.motion .mm-lg-handle{stroke:var(--ink);stroke-width:.8;opacity:0}
html.motion .mm-lg-handle circle{fill:var(--well);stroke:var(--ink);stroke-width:.8}
html.motion .mm-lg-anchor{fill:var(--well);stroke:var(--ink);stroke-width:.9;opacity:0}

/* =====================================================================
   THE RESULTS BAND — his dashboards, arriving and leaving

   The plan: the four figures count up on entry, the bar row fills, the
   daily-leads line draws itself on scroll. Open Air Gallery's Live is the
   pattern and the reason it runs BOTH ways — its owner asked to "see motion
   as he scrolls up and down from them", and a figure that only ever animates
   once is a thing you miss by arriving from the wrong direction.

   These are the only numbers on this site that are allowed to move
   (CLAUDE.md rule 4): they are Drew's own dashboard figures, they are in the
   server HTML as text, and every one of them is still that text underneath —
   the wheels are laid over the real characters, which stay in the DOM,
   carry the box, are what a screen reader reads and what a visitor selects.
   ===================================================================== */

/* --- the figures as mechanical counters (OAG's Live, ported) ----------
   The window is one line tall plus a sixth of an em of air at each end,
   pulled back by an equal negative margin so the line box is unchanged. Each
   wheel is a grid of ten cells one window high, so the roll is a translateY
   of a whole number of windows and the digit lands exactly where the text's
   digit was. The ghost goes transparent rather than hidden: it is still the
   figure, it is still selectable, and it is still what sizes the box — which
   is why a figure rolling cannot shift a pixel of the page. */
html.motion .roll{position:relative;display:inline-block;font-variant-numeric:tabular-nums}
html.motion .roll-g{color:transparent}
html.motion .roll-w{position:absolute;inset:0;display:flex;align-items:stretch;overflow:hidden}
html.motion .roll-x{flex:none;display:flex;align-items:center}
html.motion .roll-d{flex:none;position:relative;overflow:hidden;height:calc(100% + .34em);margin:-.17em 0}
html.motion .roll-c{display:grid;grid-auto-rows:100%;height:100%;
  transform:translateY(calc(var(--d,0) * -100%));
  transition:transform var(--rt,.6s) cubic-bezier(.22,.78,.26,1) var(--rd,0s)}
html.motion .roll-c>span{display:flex;align-items:center;justify-content:center}
/* .in is added on the way in and taken away on the way out: one declaration
   is the whole of both directions. */
html.motion .roll.in .roll-c{--d:var(--v)}

/* --- the bar row -----------------------------------------------------
   The widths are the server's inline styles and are never touched: the bar
   is scaled from its left edge instead, so there is one value in the page
   and it is the one results.py computed.

   Nothing here needs the house 2.8s bail-out, because nothing here hides a
   bar the script is not already holding: .mm-live is put on a row by the
   script, one row at a time, at the moment it is taken over and after the
   observer that will reveal it exists. A page with no script, a script that
   died before this module, a browser with no IntersectionObserver, or a bar
   row this module does not recognise all keep their bars drawn — there is no
   state to fall out of. */
html.motion .bar.mm-live .bar-track b{transform-origin:left center;
  transform:scaleX(0);transition:transform .9s var(--ease),width 1s var(--ease)}
html.motion .bar.mm-live.in .bar-track b{transform:none}

/* --- the mixes ------------------------------------------------------
   Home's row moves between content mixes (site.js sets the widths inline and
   puts them back). While an EXAMPLE mix is showing, the figures beside the
   bars are his real ones and would be lying about the example, so they fade
   out; the mix line says "example mix" for the same reason. The real state
   is the server's, with every figure on it. */
html.motion .bar-v{transition:opacity .35s linear}
html.motion [data-mixes].is-example .bar-v{opacity:0}
html.motion .mix-k b.is-swap{opacity:0}

/* --- the daily-leads line --------------------------------------------
   The dash is set by the script off the path's own length — nothing here
   hides the line, because a stylesheet cannot measure it. If the script is
   not there the polyline is simply drawn, which is how the server sent it. */
html.motion .chart-line{transition:stroke-dashoffset 1.8s cubic-bezier(.35,.1,.25,1)}
/* The area under the line waits for the line: shaded ground where the stroke
   has not reached yet reads as a chart that is ahead of itself. The delay is
   on the way in only and the script sets it, because a fill that lingers on
   the way out is the same mistake in reverse. */
html.motion .chart-fill{transition:opacity .6s var(--ease)}

/* =====================================================================
   THE REELS — playing while you are looking at them

   The posters are the server's render and stay it: with no script, reduced
   motion or a metered connection these three films are three stills and
   nothing is fetched. Under html.motion the script arms a clip as it
   approaches, plays it muted while it is on screen and pauses it the moment
   it is not — a film running behind the visitor's back is battery spent on
   something nobody is watching.

   Sound is off, always, until somebody asks for it. 10-home.css draws the
   play glyph as decoration with pointer-events:none, because until this
   file there was nothing for a click on it to do; the script upgrades the
   glyph to a real button (role, tabindex, aria-pressed) on the reels it has
   taken over, and .mm-sound is it saying so. One reel at a time.
   ===================================================================== */
html.motion .phone.mm-sound .play{pointer-events:auto;cursor:pointer;
  transition:transform .2s var(--ease),background .2s linear}
html.motion .phone.mm-sound .play:hover{transform:scale(1.08)}
html.motion .phone.is-snd .play{background:var(--green-2)}
/* The state, small and in the corner: the one label on this page that is not
   a figure of Drew's, and it is there because sound being on is a thing a
   visitor has to be able to see. --bg on --green, 12.64:1. */
html.motion .mm-snd{position:absolute;left:12px;bottom:14px;
  background:var(--green);color:var(--bg);border-radius:4px;
  padding:.1rem .4rem;font-size:.6rem;letter-spacing:.12em;
  text-transform:uppercase;pointer-events:none}

/* =====================================================================
   THE REVEALS — twelve pixels, and nothing else

   The observer is the base's (site.js's first IIFE) and stays exactly as it
   is: .rv takes .in once, on the way in, and the stylesheet's 2.8s self
   reveal is still the bail-out for a script that never arrives. This is only
   the distance. Twenty-two pixels was the scaffold's guess; with six live
   tiles, four rolling figures, a bar row, a drawing chart, three films and a
   walking light on the page, the sections themselves should arrive quietly —
   a rise and a fade, no scale, no blur, no stagger beyond the .rv-d* delays
   the markup already asks for.
   ===================================================================== */
html.motion .rv{transform:translateY(12px)}

/* The mark turns like a coin, slowly, on the spot, turning to the left — its
   right edge recedes, its left edge comes forward (positive rotateY). One
   turn every nine seconds, linear so it never seems to catch. Only the mark
   moves; the words beside it hold still. Static under reduced motion and
   with no script, like everything else in this file. */
@keyframes brand-coin{from{transform:rotateY(0)}to{transform:rotateY(360deg)}}
html.motion .brand-mark{animation:brand-coin 9s linear infinite;transform-style:preserve-3d;
  backface-visibility:visible;will-change:transform}

/* ==== 16-flight.css ==== */
/* Mendoza Marketing — THE FLIGHT PATH.

   His little quad lifts off the Drones card and flies the page: it rides the
   scroll down a spline that is drawn through the page's own furniture (the
   demo well it was parked in, the gaps between the sections, the four figures
   on the Results band, the three reels, the mark in the footer), it casts a
   shadow on the panels it crosses, and it lands on the Mendoza mark. An
   aerial survey of the page, which is the business.

   This file is the states; site/js/flight.js is the flying. Nothing here
   lays out a page and nothing here is a second copy of 10-home.css: the quad,
   the shadow and the layer they live on are absolutely positioned over the
   document and cannot move a pixel that is in flow.

   =====================================================================
   THE CONTRACT (CLAUDE.md rule 4)

   One selector in this file is NOT under html.motion, and it is the rule
   that makes the whole thing decoration:

       .flight{display:none}

   The layer is server HTML (home.py puts it at the end of Home's <main>) so
   that flight.js never has to build the aircraft it is flying — but with no
   script, or with prefers-reduced-motion asked for, `html.motion` is not on
   the document, that one rule is all that matches, and the layer is not in
   the render at all. The document a crawler, a reader and a reduced-motion
   visitor get is the document they got before this file existed. Everything
   below the line turns it back on.

   =====================================================================
   CONTRAST (CLAUDE.md rule 9)

   No new pair. Nothing in this file sets type: the quad is `currentColor`
   over --green (the same stroke the drone card's own quad is drawn in), the
   shadow is a --green gradient at a fraction of its alpha, and .overflown is
   a rim and a lift on a tile whose text has not changed.
*/

/* The rule the contract above is about. It is deliberately not under
   html.motion: without that class this layer must not be in the render. */
.flight{display:none}

/* --------------------------------------------------------------- the layer
   Absolutely positioned against the initial containing block — nothing
   between it and <html> is positioned — so its origin is the document's
   origin and flight.js can think in document coordinates and write one
   translate. Its height is set by the script to the height of the body's own
   in-flow content, so the layer's bottom edge is exactly the document's and
   it cannot add a pixel of scroll. `contain: layout paint` keeps its two
   moving children off everybody else's layout and clipped to the page.

   --fq is how much of the 64px hit box the drawing fills: 56px at desktop,
   40px on a phone. The BOX does not change with it, so the aircraft is the
   same size to a finger at every width, and the script only has to read one
   number to know how big to draw. */
html.motion .flight{display:block;position:absolute;top:0;left:0;width:100%;
  height:0;pointer-events:none;contain:layout paint;z-index:4;
  perspective:640px;--fq:.875}
@media (max-width:559.98px){html.motion .flight{--fq:.625}}

/* --------------------------------------------------------------- the quad
   art.quad('flight-quad'), the same drawing as the Drones card's. The box is
   64x64 whatever the drawing is scaled to, so the hit area is the 64px the
   plan asks for and `margin` puts the element's CENTRE on the translate the
   script writes. pointer-events:all so the empty part of the box takes the
   pointer too — a 1.6px stroke is not something you can grab.

   touch-action:none is on THIS element and nothing else: a finger on the
   quad flies it, and a finger anywhere else on the page scrolls the page. */
html.motion .flight-quad{position:absolute;top:0;left:0;
  width:64px;height:64px;margin:-32px 0 0 -32px;
  color:var(--green);pointer-events:all;touch-action:none;cursor:grab;
  will-change:transform;transform:translate3d(-999px,-999px,0);
  /* A 1.6px stroke crossing a photograph of a shop full of fairy lights is
     a 1.6px stroke nobody can see. One pixel of --bg under it separates the
     aircraft from whatever it is over without putting a second colour on the
     page. It is on the 64px aircraft and nowhere else — there is no filter
     anywhere on the page itself. */
  filter:drop-shadow(0 1px 2px rgba(5,10,10,.92))}
html.motion .flight.held .flight-quad{cursor:grabbing}

/* The props. quad() has no animation of its own, so the four rotor discs are
   spun here: a disc seen from above does not turn, it thins and fills, which
   is what a prop at speed actually looks like. transform-box:fill-box is
   what makes each ellipse turn about its own centre rather than the SVG's. */
html.motion .flight-quad ellipse{transform-box:fill-box;transform-origin:50% 50%;
  animation:flightProp .19s linear infinite}
@keyframes flightProp{0%,100%{transform:scaleX(1)}50%{transform:scaleX(.5)}}
/* Down: the props wind down rather than stop dead, and the aircraft settles
   the last two percent onto the mark. The scale is written by the script
   (it composes with the translate); this is the rest of the landing. */
html.motion .flight.landed .flight-quad ellipse{animation-duration:1.1s}
html.motion .flight.landed .flight-quad{cursor:default}

/* ------------------------------------------------------------- the shadow
   The ground the quad is over, not a blur of the quad: a radial gradient
   ellipse whose SCALE is the altitude. There is no filter on it — a
   `filter: blur()` on an element that moves every frame is a full-page
   raster on every one of those frames, and the plan says transform only. */
html.motion .flight-shade{position:absolute;top:0;left:0;width:56px;height:15px;
  margin:-7.5px 0 0 -28px;border-radius:50%;pointer-events:none;
  background:radial-gradient(closest-side,rgba(45,232,181,.5),rgba(45,232,181,0) 100%);
  will-change:transform,opacity;transform:translate3d(-999px,-999px,0)}

/* ------------------------------------------------------- flown over
   A tile the quad has just crossed: a rim of the accent and four percent of
   lift, for 900 ms. transform and box-shadow only, so nothing in flow moves.

   The lift is written as `translate` rather than as `transform` on purpose.
   .stat and .phone are both .rv, and .rv's reveal owns `transform`; using the
   independent translate property means the light can never fight the reveal,
   and .fl-lit — which flight.js puts on the tiles it has taken over — can
   add the two new transitions to .rv's two without replacing them.

   The rolling figures are not touched: this is a class on the tile, and the
   wheels inside it are the results module's business. */
html.motion .fl-lit.fl-lit{
  transition:opacity .8s var(--ease),transform .8s var(--ease),
             translate .34s var(--ease),box-shadow .34s var(--ease)}
html.motion .fl-lit.overflown{translate:0 -4%;
  box-shadow:0 0 0 1px var(--green),0 16px 38px -18px rgba(45,232,181,.55)}

/* ==== 20-pages.css ==== */
/* ==========================================================================
   The inner pages — the six services, /pricing, /work, /results, /about.
   Owned by pages.py; 00-base.css owns the tokens, the nav, the footer, the
   buttons, .panel, .price, .swipe and the reveal contract, and nothing here
   redefines any of them.

   Everything is built to the Home mockup the owner reviewed
   (docs/mockup/home.html): two-tone headlines, a mono eyebrow over every
   section, panel cards on hairlines, one green, and every figure and price in
   JetBrains Mono. Where this file draws a dashboard it uses the same class
   names and the same data- attributes Home's does, so step 5's counters, bars
   and chart bind to both pages with one selector.

   New text/background pairs this file introduces, computed the same way as the
   table at the top of 00-base.css:
     --muted  #93A3A3  on .band-alt #070D0D   7.44:1   AAA   captions, bullets
     --green  #2de8b5  on .band-alt #070D0D  12.38:1   AAA   eyebrows, figures
     --ink    #DDE4E1  on .band-alt #070D0D  15.16:1   AAA   headings
     --muted  #93A3A3  on .logo-shot #0B1413  6.94:1   AA    (carries no type)
   ========================================================================== */

/* The two-tone headline, without the .h-two class.
   page_hero() puts the class on the SECTION, and `.h-two span` would then also
   catch the <span>s inside the breadcrumb — which every inner page has and
   Home does not. Scoping to the direct child of the heading gets line two
   green and leaves the crumbs alone. */
.page-hero .hero-inner > h1 > span,
.sec-head h2 > span,
.bio-copy h2 > span { color: var(--green); display: block; }

/* An in-page anchor must not land under the fixed bar. */
section[id] { scroll-margin-top: calc(var(--nav-h) + 1rem); }

/* The starting price, in mono, straight under the hero lead. */
.hero-price {
  display: inline-block; color: var(--green); font-size: .82rem;
  letter-spacing: .14em; text-transform: uppercase;
  border: 1px solid var(--line); border-radius: 999px;
  padding: .42rem .9rem; margin-bottom: 1.4rem;
}

/* The alternate band: a shade darker than the canvas, ruled top and bottom.
   The mockup uses it to separate one section from the next without a second
   colour. */
.band-alt {
  background: #070D0D;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}

/* ------------------------------------------------------------ section head */
.sec-head {
  display: flex; flex-wrap: wrap; gap: 1.2rem 2rem;
  align-items: flex-end; justify-content: space-between;
  margin-bottom: clamp(1.8rem, 4vw, 2.6rem);
}
.sec-head h2 { max-width: 20ch; }
.sec-aside { max-width: 42ch; }
.sec-note { color: var(--muted); font-size: .95rem; margin: 0; }

/* ------------------------------------------------- his copy + his breakdown */
.split { display: grid; gap: 1.6rem; align-items: start; }
@media (min-width: 960px) { .split { grid-template-columns: 1.35fr 1fr; gap: 3rem; } }
.prose p { font-size: 1.04rem; color: var(--muted); max-width: 62ch; }
.prose p:last-child { margin-bottom: 0; }
@media (min-width: 960px) { .prose p { font-size: 1.08rem; } }

.bd-h {
  font-family: var(--font-mono); font-size: .74rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--green);
  margin-bottom: 1rem;
}
.bd ul { list-style: none; display: grid; gap: .7rem; font-size: .95rem; color: var(--muted); }
.bd li { display: flex; gap: .6rem; }
.bd li::before {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-top: .55rem;
  border-radius: 2px; background: var(--green);
}
/* His own price sentence. Mono, because it carries the figure. */
.bd-price { font-family: var(--font-mono); font-size: .88rem; color: var(--ink); }

/* ------------------------------------------------- the sky behind a hero
   Drew, 2026-10-10: "a subtle moving background behind 'Professional Web
   Design' … slowly moving clouds, wind lines, or something drone-related.
   Nothing too distracting." His reference is the drifting cloud field on
   netoriouslabs.com/contact; this is that idea in his own colours. Three
   tiled layers — far clouds in --ink, near clouds in --green, a few wind
   lines in --green-2 (a glow, which is what that colour is for) — each
   faint enough that the hero's type keeps its contrast: the brightest the
   ground under a word can get is the canvas plus 6% of a cloud, #0f1514,
   which leaves --ink at 14.6:1 and --muted at 7.2:1.

   The drawing is here and shows with no script at all; the drift is under
   html.motion only, so no-JS and reduced motion get the same still sky
   (CLAUDE.md rule 4). It moves right to left, the far bank slowest. The mask
   fades it out at the bottom so the hero meets the next section cleanly. */
.hero-sky{position:absolute;inset:0;z-index:-2;pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg,#000 55%,transparent);
          mask-image:linear-gradient(180deg,#000 55%,transparent)}
.hero-sky i{position:absolute;inset:0;display:block;background-repeat:repeat}
.sky-far{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='340'%3E%3Cg fill='%23DDE4E1'%3E%3Ccircle cx='76' cy='88' r='20'/%3E%3Ccircle cx='106' cy='74' r='29'/%3E%3Ccircle cx='140' cy='86' r='21'/%3E%3Crect x='54' y='84' width='112' height='24' rx='12'/%3E%3Ccircle cx='393' cy='58' r='16'/%3E%3Ccircle cx='417' cy='47' r='23'/%3E%3Ccircle cx='444' cy='57' r='17'/%3E%3Crect x='375' y='55' width='90' height='19' rx='10'/%3E%3Ccircle cx='651' cy='148' r='23'/%3E%3Ccircle cx='685' cy='132' r='33'/%3E%3Ccircle cx='724' cy='145' r='24'/%3E%3Crect x='626' y='143' width='129' height='28' rx='14'/%3E%3Ccircle cx='229' cy='248' r='18'/%3E%3Ccircle cx='256' cy='236' r='26'/%3E%3Ccircle cx='287' cy='246' r='19'/%3E%3Crect x='210' y='245' width='101' height='22' rx='11'/%3E%3Ccircle cx='534' cy='288' r='15'/%3E%3Ccircle cx='557' cy='278' r='22'/%3E%3Ccircle cx='582' cy='287' r='16'/%3E%3Crect x='518' y='286' width='84' height='18' rx='9'/%3E%3Ccircle cx='813' cy='298' r='16'/%3E%3Ccircle cx='837' cy='287' r='23'/%3E%3Ccircle cx='864' cy='297' r='17'/%3E%3Crect x='795' y='295' width='90' height='19' rx='10'/%3E%3C/g%3E%3C/svg%3E");opacity:.035;background-size:900px 340px}
.sky-near{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1300' height='420'%3E%3Cg fill='%232de8b5'%3E%3Ccircle cx='95' cy='206' r='38'/%3E%3Ccircle cx='152' cy='180' r='55'/%3E%3Ccircle cx='217' cy='202' r='40'/%3E%3Crect x='54' y='199' width='213' height='46' rx='23'/%3E%3Ccircle cx='649' cy='117' r='30'/%3E%3Ccircle cx='694' cy='96' r='44'/%3E%3Ccircle cx='745' cy='114' r='32'/%3E%3Crect x='616' y='111' width='168' height='36' rx='18'/%3E%3Ccircle cx='1040' cy='296' r='35'/%3E%3Ccircle cx='1093' cy='272' r='51'/%3E%3Ccircle cx='1152' cy='293' r='37'/%3E%3Crect x='1002' y='290' width='196' height='42' rx='21'/%3E%3C/g%3E%3C/svg%3E");opacity:.05;background-size:1300px 420px;
  background-position:-200px 60px}
.sky-wind{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1100' height='380'%3E%3Cdefs%3E%3ClinearGradient id='g'%3E%3Cstop offset='0' stop-color='%237FF2D1' stop-opacity='0'/%3E%3Cstop offset='.5' stop-color='%237FF2D1'/%3E%3Cstop offset='1' stop-color='%237FF2D1' stop-opacity='0'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='url%28%23g%29' stroke-width='1.2' stroke-linecap='round' fill='none'%3E%3Cpath d='M60 70h260'/%3E%3Cpath d='M420 150c80 0 120-10 210-10'/%3E%3Cpath d='M780 60h200'/%3E%3Cpath d='M140 260c70 0 110 8 230 8'/%3E%3Cpath d='M640 320h240'/%3E%3C/g%3E%3C/svg%3E");opacity:.22;background-size:1100px 380px;
  background-position:0 40px}
html.motion .sky-far{animation:skyFar 160s linear infinite}
html.motion .sky-near{animation:skyNear 95s linear infinite}
html.motion .sky-wind{animation:skyWind 26s linear infinite}
@keyframes skyFar{to{background-position:-900px 0}}
@keyframes skyNear{from{background-position:-200px 60px}to{background-position:-1500px 60px}}
@keyframes skyWind{to{background-position:-1100px 40px}}

/* -------------------------------------------------------- the Included trio */
/* Max's feature boxes, in Drew's colours (Drew, 2026-10-10: "make the boxes
   similar to Max's website, with a subtle hover/floating animation"): the
   icon chip on a soft green tint, the heading, two or three lines of what
   the visitor gets. On hover the box lifts and a soft light follows the
   pointer (site.js sets --mx/--my); with no pointer it simply lifts. */
.inc-card { position: relative; overflow: hidden; padding: 1.7rem;
  transition: transform .35s var(--ease), border-color .3s, box-shadow .35s; }
.inc-card::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit; opacity: 0; transition: opacity .3s;
  background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 0%),
    rgba(45,232,181,.09), transparent 62%); }
.inc-card:hover { transform: translateY(-6px); border-color: var(--green-line);
  box-shadow: 0 30px 60px -30px rgba(0,0,0,.9); }
.inc-card:hover::after { opacity: 1; }
.inc-card .f-ico { width: 48px; height: 48px; border-radius: 14px;
  background: linear-gradient(135deg, rgba(45,232,181,.16), rgba(45,232,181,.04));
  transition: transform .35s var(--ease); }
.inc-card:hover .f-ico { transform: translateY(-2px) rotate(-4deg); }
.inc-card h3 { color: var(--ink); font-size: 1.15rem; }
.inc-card p { margin: .55rem 0 0; color: var(--muted); font-size: .95rem; line-height: 1.6; }
@media (prefers-reduced-motion: reduce) {
  .inc-card:hover, .inc-card:hover .f-ico { transform: none; }
}

/* A button under a block of prose (/websites: "View our work"). */
.split .btn-row { margin-top: 1.6rem; }

/* ---------------------------------------------------------------- prices */
.grid-4 { grid-template-columns: 1fr; }
@media (min-width: 640px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .grid-4 { grid-template-columns: repeat(4, 1fr); } }
/* One card should not stretch the width of the page. */
.prices-1 { max-width: 420px; }
/* A package's features: his title in --ink, the detail in --muted. */
.price li b { color: var(--ink); font-weight: 600; }
/* A package with its own graphic is laid out wider, the graphic on top. */
.prices-1:has(.has-art) { max-width: 520px; }
.price-art { margin: -1.6rem -1.6rem 1.4rem; aspect-ratio: 16 / 10; overflow: hidden;
  border-bottom: 1px solid var(--line); background: var(--well, #0b1413); }
.price-art img { width: 100%; height: 100%; object-fit: cover; }
/* "Commission or hourly" is a sentence, not a figure, and does not want the
   2rem tabular size the amounts are set in. */
.prices-words .amount { font-size: 1.3rem; line-height: 1.35; }
/* The three social tiers are priced and their contents are not written down
   anywhere we hold. Marked rather than invented — CLAUDE.md rule 7. */
.tbc { font-style: italic; }
.price li:has(.tbc)::before { background: var(--line); }

/* /pricing: one block per service, stacked. */
.pg-group + .pg-group { margin-top: clamp(3rem, 7vw, 4.5rem); }
.pg-group .sec-head { margin-bottom: 1.6rem; }
.pg-group .sec-head h2 { font-size: clamp(1.7rem, 4.4vw, 2.4rem); }

/* -------------------------------------------------------- the drone rail */
.clip-card { margin: 0; }
.clip-card video {
  width: 100%; aspect-ratio: 16 / 9; background: #000;
  border: 1px solid var(--line); border-radius: var(--radius);
}
.clip-card figcaption {
  display: flex; flex-direction: column; gap: .2rem;
  margin-top: .8rem; font-size: .95rem; color: var(--muted);
}
.clip-card figcaption .mono {
  font-size: .7rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--green);
}

/* ------------------------------------------------------------------ /work */
/* Each client logo arrived as a finished square with its own background
   baked in — two of them white, two of them near-black — so the tile is the
   file itself, edge to edge, rather than a plate colour that would be wrong
   for half of them. */
.logo-shot {
  aspect-ratio: 1; overflow: hidden; background: #0B1413;
  border: 1px solid var(--line); border-radius: var(--radius-s);
  margin-bottom: 1.2rem;
}
.logo-shot picture, .logo-shot img { width: 100%; height: 100%; }
.logo-shot img { object-fit: cover; }
.logo-work { color: var(--green); font-size: .72rem; letter-spacing: .14em;
  text-transform: uppercase; margin: .5rem 0 0; }
.logo-note, .site-card p:last-child { color: var(--muted); font-size: .92rem; margin: .7rem 0 0; }
.site-card h3 { color: var(--ink); }

/* The Isle de Grande mark is a transparent PNG drawn in navy and red: on
   #050A0A it is very nearly invisible. A brand asset is shown on the ground it
   was drawn for, so the case plate is light. The second render arrived on
   white already and sits on the same plate seamlessly. */
.case-shot { margin: 0; }
.case-shot picture, .case-shot > img {
  display: block; background: var(--ink); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; padding: clamp(1rem, 3vw, 2rem);
}
.case-shot img { width: 100%; height: auto; }
.case-shot figcaption {
  margin-top: .7rem; font-family: var(--font-mono); font-size: .74rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}

/* The reels are 1080x1920 and they are advertising: they carry sound, so they
   sit behind a poster and a play control and start only when asked. */
.reels { max-width: 900px; }
.reel { margin: 0; }
.phone {
  aspect-ratio: 9 / 16; border-radius: 22px; overflow: hidden;
  border: 1px solid var(--line); background: #000;
}
.phone video { width: 100%; height: 100%; object-fit: cover; }
.reel figcaption { display: grid; gap: .2rem; margin-top: .8rem; }
.reel figcaption b { font-weight: 600; }
.reel figcaption .mono { font-size: .72rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); }

/* --------------------------------------------------------------- /results */
/* The dashboards, rebuilt in Drew's own colours rather than screenshotted.
   Every figure is in the markup as text: the page is complete and identical
   with no script and under prefers-reduced-motion, and data-count / data-bars
   / data-chart are only step 5's hooks to animate what is already there.
   INTEGRATOR: Home draws the same tiles; if 10-home.css grows its own .stat
   rules, keep one copy of these, not two. */
.stat { display: flex; flex-direction: column; gap: .35rem; }
.stat-label {
  font-family: var(--font-mono); font-size: .7rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.stat-num {
  font-family: var(--font-mono); font-weight: 600; font-variant-numeric: tabular-nums;
  font-size: clamp(1.7rem, 4.4vw, 2.1rem); line-height: 1.1; color: var(--ink);
}
.stat-delta { font-style: normal; font-family: var(--font-mono); font-size: .8rem; color: var(--green); }
.stat-note { font-size: .82rem; color: var(--muted); }
/* A one-tile group is still a grid, and a lone auto-sized implicit column
   does not stretch to its track — the tile came out a third of the width of
   the chart beside it, with its label wrapping. Name the column. */
.stats-1 { grid-template-columns: minmax(0, 1fr); }
.stats-1, .stats-2 { align-content: start; }
.figures { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 640px) { .figures { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.fig-cap { color: var(--muted); font-size: .92rem; margin: 1.1rem 0 0; max-width: 72ch; }
.figures { margin-top: 1.2rem; align-items: start; }
.figure { padding: 1.4rem; }
.fig-h {
  font-family: var(--font-mono); font-size: .7rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  margin-bottom: 1.1rem;
}

.bar {
  display: grid; grid-template-columns: 6.6rem minmax(0, 1fr) 3.4rem;
  align-items: center; gap: .7rem; margin: .7rem 0;
}
.bar-name { font-size: .82rem; color: var(--muted); overflow-wrap: anywhere; }
.bar-track { height: 8px; background: #0B1413; border: 1px solid var(--line);
  border-radius: 5px; overflow: hidden; }
.bar-track b { display: block; height: 100%; background: var(--green); border-radius: 4px; }
.bar-val { font-family: var(--font-mono); font-size: .82rem; color: var(--ink);
  text-align: right; font-variant-numeric: tabular-nums; }

.chart { height: 130px; color: var(--green); }
.chart svg { width: 100%; height: 100%; display: block; }
.chart-line { fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.chart-fill { fill: rgba(45, 232, 181, .12); stroke: none; }

/* ----------------------------------------------------------------- /about */
.bio { display: grid; gap: 2rem; align-items: center; }
@media (min-width: 960px) { .bio { grid-template-columns: 1fr 1.4fr; gap: 3.2rem; } }
.bio-shot { margin: 0; }
.bio-shot picture, .bio-shot > img {
  display: block; border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden;
}
.bio-shot img { width: 100%; height: auto; }
.bio-shot figcaption {
  margin-top: .7rem; font-family: var(--font-mono); font-size: .74rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.bio-copy h2 { margin: 1.6rem 0 0; font-size: clamp(1.8rem, 4.6vw, 2.6rem); }
.bio-copy .lead { margin: 1rem 0 1.6rem; }
.spec { list-style: none; display: grid; gap: .55rem; margin: 0; font-size: 1rem; }
.spec li { display: flex; gap: .6rem; color: var(--ink); }
.spec li::before {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-top: .6rem;
  border-radius: 2px; background: var(--green);
}
.photos figure { margin: 0; }
.photos picture, .photos > figure > img {
  display: block; border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden;
}
/* Three phone photographs of three different shapes, cropped to him. */
.photos img { width: 100%; height: 100%; aspect-ratio: 4 / 5; object-fit: cover;
  object-position: 50% 28%; }
/* The autumn frame is a full-figure phone photograph: he is a quarter of it,
   the rest is leaves, and the file is grainy at full size. Scaling in about a
   third, about where he is standing, makes it a portrait of him and throws the
   noisiest part of the frame away. */
.ph-drew-suit picture { display: block; overflow: hidden; }
.ph-drew-suit img { transform: scale(1.35); transform-origin: 61% 42%; }

.beat { border-top: 2px solid var(--green); padding-top: 1.2rem; }
.beat .f-ico { margin-bottom: .8rem; }
.beat .mono { font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); }
.beat h3 { margin-top: .6rem; }

/* ------------------------------------------------ /meta-ads: campaign results
   Drew, 2026-10-11: "previous campaign results … 1,996 incoming leads over 90
   days … visually engaging with subtle animated statistics or charts". The
   headline is the largest number on the page and the accent; it rolls up on
   the results module's wheels (data-count), the 90 days fill in under it, and
   the daily chart beside it draws itself (data-chart). All of it is in the
   server HTML at its finished state. */
.cr { display: grid; gap: 1rem; }
@media (min-width: 960px) { .cr { grid-template-columns: 1fr 1.25fr; } }
.cr-big { padding: 1.8rem; display: flex; flex-direction: column; gap: .3rem; }
.cr-num { font-family: var(--font-mono); font-weight: 600; color: var(--green);
  font-size: clamp(3.4rem, 9vw, 5.2rem); line-height: 1; margin: .5rem 0 .2rem;
  font-variant-numeric: tabular-nums; }
.cr-when { color: var(--ink); font-size: .95rem; }
.cr-span { height: 6px; border-radius: 3px; background: var(--well, #0b1413);
  border: 1px solid var(--line); overflow: hidden; margin-top: 1.4rem; }
.cr-span i { display: block; height: 100%; background: var(--green);
  border-radius: 3px; transform-origin: left center; }
.cr-months { display: flex; justify-content: space-between; margin-top: .45rem;
  font-size: .7rem; letter-spacing: .08em; color: var(--muted); }
.cr-sub { display: flex; align-items: baseline; gap: .7rem; margin-top: auto;
  padding-top: 1.4rem; border-top: 1px solid var(--line); }
.cr-big .cr-sub { margin-top: 1.6rem; }
.cr-sub b { font-size: 1.8rem; color: var(--ink); font-variant-numeric: tabular-nums; }
.cr-sub span { color: var(--muted); font-size: .9rem; }
/* The 90 days fill in as the panel arrives — under html.motion only, and with
   the house 2.8s bail-out, so a page whose script never runs shows it full. */
html.motion .cr-big .cr-span i { transform: scaleX(0);
  transition: transform 1.8s cubic-bezier(.35,.1,.25,1) .3s;
  animation: mmSelf 0s 2.8s forwards; }
html.motion .cr-big.in .cr-span i { transform: none; }

/* ------------------------------------------------ /meta-ads: campaign preview
   The mock campaign (campaign.py). Four dials, the map, the answers. With no
   script the dials are disabled and drawn at their defaults, and the answers
   are those defaults' answers — dimmed controls, complete panel. */
.cp { padding: 1.6rem; }
@media (min-width: 960px) { .cp { padding: 2rem; } }
.cp-dials { display: grid; gap: 1.1rem 1.6rem; }
@media (min-width: 640px) { .cp-dials { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .cp-dials { grid-template-columns: repeat(4, 1fr); } }
.cp-dial { display: grid; gap: .55rem; }
.cp-dial-k { display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; font-family: var(--font-mono); font-size: .72rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); }
.cp-dial-k output { font-size: 1rem; letter-spacing: 0; text-transform: none;
  color: var(--ink); font-weight: 600; }
.cp-dial input[type=range] { width: 100%; accent-color: var(--green); height: 28px;
  cursor: pointer; }
.cp-dial select { width: 100%; padding: .55rem .7rem; border-radius: var(--radius-s);
  background: var(--well, #0b1413); color: var(--ink); border: 1px solid var(--line);
  font: inherit; font-size: .95rem; }
.cp-dial :disabled { opacity: .55; cursor: default; }
.cp-body { display: grid; gap: 1.4rem; margin-top: 1.8rem; }
@media (min-width: 960px) { .cp-body { grid-template-columns: 1fr 1fr; gap: 2rem;
  align-items: start; } }
.cp-mapbox { position: relative; aspect-ratio: 1; border-radius: 12px; overflow: hidden;
  background: var(--well, #0b1413); border: 1px solid var(--green-line); }
.cp-map { display: block; width: 100%; height: 100%; }
.cp-water { fill: rgba(45,232,181,.05); stroke: var(--green-line); stroke-width: 1px;
  vector-effect: non-scaling-stroke; }
.cp-river { fill: none; stroke: var(--green-line); stroke-width: 2.5px;
  vector-effect: non-scaling-stroke; }
.cp-sea { font-family: var(--font-mono); font-size: 2.3px; letter-spacing: .12em;
  text-transform: uppercase; fill: var(--muted); opacity: .75; }
.cp-place circle { fill: var(--muted); opacity: .55; }
.cp-place text { font-family: var(--font-body); font-size: 2.7px; fill: var(--muted); }
.cp-place.is-in circle { fill: var(--green); opacity: 1; }
.cp-place.is-in text { fill: var(--ink); }
.cp-area { fill: rgba(45,232,181,.12); stroke: var(--green); stroke-width: 2px;
  vector-effect: non-scaling-stroke; }
.cp-pin { fill: var(--green); stroke: var(--well, #0b1413); stroke-width: .6; }
/* The zoom and the re-centring ease; the ring follows the dial as it is
   dragged (the r transition is a progressive nicety where CSS r animates). */
html.motion .cp-world, html.motion .cp-mark { transition: transform .5s var(--ease); }
html.motion .cp-area { transition: r .35s var(--ease), cx .5s var(--ease), cy .5s var(--ease); }
html.motion .cp-place circle, html.motion .cp-place text { transition: fill .3s, opacity .3s; }
.cp-mapk { position: absolute; left: .8rem; bottom: .6rem; font-size: .66rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.cp-out { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .8rem; }
.cp-fig { display: flex; flex-direction: column; gap: .3rem; padding: 1rem 1.1rem;
  border-radius: 12px; background: var(--well, #0b1413); border: 1px solid var(--line); }
.cp-fig-main { grid-column: 1 / -1; border-color: var(--green-line); }
.cp-k { font-family: var(--font-mono); font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); display: flex; flex-wrap: wrap;
  gap: .4rem; align-items: center; }
/* "projection" — on every estimated figure, every time, so neither can be
   read as one of his results. */
.cp-k i { font-style: normal; letter-spacing: .06em; color: var(--green);
  border: 1px solid var(--green-line); border-radius: 999px; padding: .05rem .45rem;
  font-size: .62rem; }
.cp-fig b { font-size: 1.45rem; color: var(--ink); font-variant-numeric: tabular-nums;
  line-height: 1.15; overflow-wrap: anywhere; }
.cp-fig-main b { font-size: 2.4rem; color: var(--green); }
.cp-n { font-size: .82rem; color: var(--muted); }
.cp-cap { grid-column: 1 / -1; margin: 0; font-size: .88rem; color: var(--green); }
.cp-cap[hidden] { display: none; }
.cp-small { margin: 1.4rem 0 0; font-size: .8rem; color: var(--muted); max-width: 90ch; }
.cr .chart { height: 230px; }
.cp-map.is-wide .cp-place:not(.is-major):not(.is-centre) text { display: none; }
.cp-place.is-centre text { fill: var(--green); font-weight: 600; }
/* The two projections sit side by side; people in range takes its own row. */
.cp-fig-wide { grid-column: 1 / -1; }
@media (max-width: 519px) { .cp-out { grid-template-columns: minmax(0, 1fr); } }
/* A phone's map is a third the width of a desktop's: the names grow to stay
   readable (the marks are counter-scaled, so this is their screen size). */
@media (max-width: 639px) {
  .cp-place text { font-size: 4.2px; }
  .cp-place circle { r: 1.5px; }
  .cp-sea { font-size: 3.4px; }
}

/* ------------------------------------------------ the pen behind a hero
   /logo (build.SKETCH): a mark on its construction grid, drawn on a loop.
   Faint, on the right where the hero has no words; on a phone it sits behind
   the words, fainter still. The finished drawing is the static render; the
   cycle below is html.motion only (CLAUDE.md rule 4). The brightest ground
   it can put under a word is the canvas plus 14% of the green on the disc,
   #0b2a24 — --ink 12.4:1, --muted 6.1:1 on it — and on a phone the whole
   layer is at a third of that. */
.hero-sketch { position: absolute; z-index: -2; pointer-events: none;
  top: 50%; right: -12vw; width: min(78vw, 400px); aspect-ratio: 1;
  transform: translateY(-46%); opacity: .35; }
@media (min-width: 960px) { .hero-sketch { right: max(2vw, calc(50vw - 640px));
  width: min(46vw, 600px); opacity: .7; } }
.hero-sketch svg { width: 100%; height: 100%; display: block; overflow: visible; }
.sk-grid { fill: none; stroke: rgba(45,232,181,.07); stroke-width: 1; }
.sk-guides { fill: none; stroke: rgba(45,232,181,.16); stroke-width: 1; }
.sk-fill { fill: rgba(45,232,181,.14); }
.sk-ring, .sk-m { fill: none; stroke: var(--green); stroke-linecap: butt;
  stroke-linejoin: round; stroke-dasharray: 1 1; }
.sk-ring { stroke-width: 6; opacity: .55; }
.sk-m { stroke-width: 22; opacity: .5; }
.sk-handle { fill: var(--bg); stroke: var(--ink); stroke-width: 1.5; opacity: 0; }
.sk-anchors { fill: var(--bg); stroke: var(--ink); stroke-width: 1.5; opacity: 0; }
.sk-pen { fill: var(--ink); opacity: 0; }
.sk-pen .sk-slit { fill: var(--bg); }

html.motion .sk-guides { animation: skGuide 14s linear infinite; }
html.motion .sk-ring { animation: skRing 14s linear infinite; }
html.motion .sk-m { animation: skM 14s linear infinite; }
html.motion .sk-fill { transform-box: fill-box; transform-origin: center;
  animation: skFill 14s var(--ease) infinite; }
html.motion .sk-handle { animation: skHandle 14s linear infinite; }
html.motion .sk-anchors { animation: skAnchors 14s linear infinite; }
html.motion .sk-pen-ring { animation: skPenRing 14s linear infinite; }
html.motion .sk-pen-m { animation: skPenM 14s linear infinite; }
@keyframes skGuide { 0% { opacity: 0 } 6%, 90% { opacity: 1 } 97%, 100% { opacity: 0 } }
@keyframes skRing { 0%, 8% { stroke-dashoffset: 1; opacity: 0 }
  8.5% { opacity: .55 } 30%, 90% { stroke-dashoffset: 0; opacity: .55 }
  97%, 100% { stroke-dashoffset: 0; opacity: 0 } }
@keyframes skM { 0%, 32% { stroke-dashoffset: 1; opacity: 0 }
  32.5% { opacity: .5 } 50%, 90% { stroke-dashoffset: 0; opacity: .5 }
  97%, 100% { stroke-dashoffset: 0; opacity: 0 } }
@keyframes skFill { 0%, 52% { transform: scale(0); opacity: 0 }
  64%, 90% { transform: scale(1); opacity: 1 } 97%, 100% { transform: scale(1); opacity: 0 } }
@keyframes skHandle { 0%, 8% { opacity: 0 } 10%, 28% { opacity: .8 } 31%, 100% { opacity: 0 } }
@keyframes skAnchors { 0%, 8% { opacity: 0 } 11%, 58% { opacity: .85 } 64%, 100% { opacity: 0 } }
@keyframes skPenRing { 0%, 7% { opacity: 0 } 8.5%, 29.5% { opacity: .9 } 31%, 100% { opacity: 0 } }
@keyframes skPenM { 0%, 31.5% { opacity: 0 } 32.5%, 49.5% { opacity: .9 } 51.5%, 100% { opacity: 0 } }

/* The feature icons redraw themselves on hover (Drew, 2026-10-12: "subtle
   hover animations to the existing icons"): art.feature() gives every stroke
   pathLength="1", so one dash rule draws any of them from nothing, and the
   chip lifts and tilts as before. Motion only; a filled dot simply stays. */
@keyframes icoDraw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
html.motion .inc-card:hover .f-ico svg [pathLength] { stroke-dasharray: 1 1;
  animation: icoDraw .9s var(--ease) both; }
html.motion .inc-card:hover .f-ico { box-shadow: 0 0 0 4px rgba(45,232,181,.08),
  0 10px 24px -10px rgba(45,232,181,.45); border-color: var(--green); }
html.motion .inc-card .f-ico { transition: transform .35s var(--ease),
  box-shadow .35s var(--ease), border-color .3s; }

/* ------------------------------------------------ /logo: before & after
   Two logos on the same light plate (a client's mark is drawn for white),
   the arrow between them. The plate is --ink, a surface here, never type.
   Under motion the "after" arrives a beat behind the "before". */
.ba-list { display: grid; gap: 1.6rem; max-width: 920px; }
.ba { display: grid; gap: 1rem; align-items: center; }
@media (min-width: 760px) { .ba { grid-template-columns: 1fr auto 1fr; gap: 1.4rem; } }
.ba-side { margin: 0; display: grid; gap: .7rem; }
.ba-k { font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted); }
.ba-after .ba-k { color: var(--green); }
.ba-plate { aspect-ratio: 1; border-radius: var(--radius); background: var(--ink);
  border: 1px solid var(--line); display: grid; place-items: center; padding: 9%;
  overflow: hidden; }
.ba-after .ba-plate { border-color: var(--green); box-shadow: 0 0 0 4px rgba(45,232,181,.08); }
.ba-plate img { width: 100%; height: 100%; object-fit: contain; }
.ba-arrow { justify-self: center; display: grid; place-items: center; width: 52px;
  height: 52px; border-radius: 50%; color: var(--bg); background: var(--green); }
.ba-arrow svg { width: 22px; height: 22px; }
@media (max-width: 759px) { .ba-arrow { transform: rotate(90deg); } }
.ba-cap { grid-column: 1 / -1; margin: .2rem 0 0; color: var(--muted); }
.ba-cap b { color: var(--ink); font-weight: 600; }
html.motion .ba.rv .ba-after { opacity: 0; transform: translateX(14px);
  transition: opacity .7s var(--ease) .45s, transform .7s var(--ease) .45s;
  animation: mmSelf 0s 2.8s forwards; }
html.motion .ba.rv.in .ba-after { opacity: 1; transform: none; }

/* ------------------------------------------------ /logo: build the brand
   Max's "a logo is the start of a brand", in Drew's colours: the sentence on
   the left, the website package's own card on the right. */
.tie { display: grid; gap: 1.6rem; align-items: center; }
@media (min-width: 960px) { .tie { grid-template-columns: 1fr 1fr; gap: 3rem; } }
.tie-words .lead { margin: 0 0 1rem; max-width: 46ch; }
.tie-also { margin: 1.2rem 0 0; color: var(--muted); font-size: .95rem; }
.tie-also a { color: var(--green); }
.tie-card { padding: 1.8rem; border-color: var(--green-line);
  transition: transform .35s var(--ease), border-color .3s, box-shadow .35s; }
.tie-card:hover { transform: translateY(-5px); border-color: var(--green);
  box-shadow: 0 30px 60px -30px rgba(0,0,0,.9); }
.tie-top { display: flex; justify-content: space-between; align-items: flex-start;
  margin-bottom: 1.1rem; }
.tie-price { color: var(--green); font-size: .9rem; padding-top: .4rem; }
.tie-card h3 { color: var(--green); font-size: 1.35rem; margin-bottom: .9rem; }
.tie-card ul { list-style: none; display: grid; gap: .55rem; margin: 0 0 1.5rem;
  padding: 0; color: var(--muted); font-size: .94rem; }
.tie-card li { display: flex; gap: .6rem; }
.tie-card li::before { content: ""; flex: 0 0 auto; width: 7px; height: 7px;
  margin-top: .55rem; border-radius: 2px; background: var(--green); }
.tie-card li b { color: var(--ink); font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .tie-card:hover { transform: none; } }

/* ==== 22-services.css ==== */
/* =====================================================================
   /social, /drones, /lead-conversion and /work — Drew's notes of 2026-10-15.

   Contrast (rule 9), the new pairs only:
     --ink    #DDE4E1 on .pk-far   #0F1717  14.3:1  AAA  (hero words over the far ridge)
     --muted  #93A3A3 on .pk-mid   #0B1212   7.1:1  AAA
     --ink    #DDE4E1 on .kd .stat #050A0A  15.4:1  AAA
     --bg     #050A0A on .hub-booked #2de8b5 11.5:1  AAA  (status chip)
     --ink    #DDE4E1 on .film-shade at 80% black over the poster  ≥ 12:1
   Every moving thing below is under html.motion (script on, no reduced
   motion); without it each one is drawn where its loop begins (rule 4).
   ===================================================================== */

/* ------------------------------------------------ /social: hero backdrop */
.hero-social { position: absolute; inset: 0; z-index: -2; pointer-events: none; }
.so-i { position: absolute; fill: none; stroke: var(--green); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round; opacity: .22; }
@media (max-width: 959px) { .hero-social { opacity: .55; } }
html.motion .so-i { animation: soRise 16s ease-in-out infinite; animation-delay: var(--d); }
@keyframes soRise {
  0% { transform: translateY(26px) scale(.9); opacity: 0 }
  18% { opacity: .26 }
  50% { transform: translateY(-14px) rotate(-6deg) scale(1); opacity: .3 }
  82% { opacity: .2 }
  100% { transform: translateY(-56px) rotate(4deg) scale(.95); opacity: 0 } }

/* His two results as bubbles. Under the buttons on a phone and a laptop;
   on a wide screen they float in the empty right of the hero. */
.hero-proof { list-style: none; margin: 1.6rem 0 0; padding: 0; display: flex;
  flex-wrap: wrap; gap: .7rem; }
.hero-proof li { display: inline-flex; align-items: center; gap: .6rem;
  padding: .6rem 1rem; border-radius: 999px; font-size: .8rem; color: var(--ink);
  background: rgba(26,31,31,.82); border: 1px solid var(--green-line);
  box-shadow: 0 14px 30px -18px rgba(45,232,181,.45); backdrop-filter: blur(6px); }
.hero-proof li i { width: .55rem; height: .55rem; border-radius: 50%; flex: none;
  background: var(--green); box-shadow: 0 0 0 4px rgba(45,232,181,.18); }
@media (min-width: 1200px) {
  .has-social .hero-inner { position: relative; }
  .has-social .hero-proof { position: absolute; left: calc(100% + 1rem); top: 18%;
    flex-direction: column; align-items: flex-start; gap: 1.4rem; margin: 0; width: 300px; }
  .has-social .hero-proof li:nth-child(2) { margin-left: 2.4rem; }
  html.motion .has-social .hero-proof li { animation: soBob 6s ease-in-out infinite; }
  html.motion .has-social .hero-proof li:nth-child(2) { animation-delay: -3s; }
}
@keyframes soBob { 50% { transform: translateY(-8px); } }

/* ------------------------------------------------ /drones: the mountains */
.hero-peaks { position: absolute; inset: 0; z-index: -2; pointer-events: none; overflow: hidden;
  background: radial-gradient(ellipse 70% 60% at 72% 18%, rgba(45,232,181,.07), transparent 70%); }
.pk { position: absolute; left: -10%; width: 120%; max-width: none; bottom: 0; height: 58%; }
.pk path { stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.pk-far path { fill: #0F1717; stroke: rgba(45,232,181,.28); }
.pk-mid { height: 44%; }
.pk-mid path { fill: #0B1212; stroke: rgba(45,232,181,.14); }
.pk-near { height: 30%; }
.pk-near path { fill: #070D0D; stroke: rgba(45,232,181,.08); }
.pk-haze { position: absolute; left: 0; right: 0; bottom: 0; height: 55%;
  background: linear-gradient(180deg, transparent, rgba(5,10,10,.55) 60%, var(--bg)); }
html.motion .pk-far { animation: pkDrift 70s ease-in-out infinite alternate; }
html.motion .pk-mid { animation: pkDrift 48s ease-in-out infinite alternate-reverse; }
html.motion .pk-near { animation: pkDrift 34s ease-in-out infinite alternate; }
@keyframes pkDrift { from { transform: translateX(-3.5%); } to { transform: translateX(3.5%); } }

.pk-drone { position: absolute; color: var(--green); width: 46px; opacity: .8;
  filter: drop-shadow(0 6px 10px rgba(45,232,181,.25)); }
.pk-drone svg { width: 100%; height: auto; display: block; }
.pk-d1 { left: 58%; top: 20%; }
.pk-d2 { left: 80%; top: 36%; width: 36px; opacity: .6; }
.pk-d3 { left: 70%; top: 10%; width: 26px; opacity: .45; }
@media (max-width: 959px) {
  .pk-d1 { left: 68%; top: 16%; width: 38px; } .pk-d2 { left: 84%; top: 30%; width: 28px; }
  .pk-d3 { left: 46%; top: 9%; width: 22px; }
}
html.motion .pk-d1 { animation: pkFly1 26s ease-in-out infinite; }
html.motion .pk-d2 { animation: pkFly2 32s ease-in-out infinite; }
html.motion .pk-d3 { animation: pkFly3 40s ease-in-out infinite; }
@keyframes pkFly1 {
  0%, 100% { transform: translate(0, 0) rotate(0) }
  20% { transform: translate(14vw, -3vh) rotate(6deg) }
  40% { transform: translate(22vw, 6vh) rotate(2deg) }
  60% { transform: translate(8vw, 10vh) rotate(-6deg) }
  80% { transform: translate(-6vw, 3vh) rotate(-3deg) } }
@keyframes pkFly2 {
  0%, 100% { transform: translate(0, 0) rotate(0) }
  25% { transform: translate(-16vw, -6vh) rotate(-6deg) }
  55% { transform: translate(-26vw, 4vh) rotate(-2deg) }
  80% { transform: translate(-8vw, 8vh) rotate(5deg) } }
@keyframes pkFly3 {
  0%, 100% { transform: translate(0, 0) }
  30% { transform: translate(10vw, 4vh) }
  65% { transform: translate(-8vw, 2vh) } }

/* ------------------------------------------- /lead-conversion: growth */
.hero-growth { position: absolute; z-index: -2; pointer-events: none;
  top: 50%; right: -10vw; width: min(80vw, 420px); aspect-ratio: 560 / 420;
  transform: translateY(-40%); opacity: .35; }
@media (min-width: 960px) { .hero-growth { right: max(2vw, calc(50vw - 640px));
  width: min(44vw, 560px); opacity: .75; } }
.hero-growth svg { width: 100%; height: 100%; display: block; overflow: visible; }
.gr-axis { fill: none; stroke: rgba(45,232,181,.18); stroke-width: 1.5; }
.gr-bar { fill: rgba(45,232,181,.1); stroke: rgba(45,232,181,.32); stroke-width: 1.5; }
.gr-line { fill: none; stroke: var(--green); stroke-width: 4; stroke-linecap: round;
  stroke-linejoin: round; opacity: .6; stroke-dasharray: 1 1; }
.gr-head { fill: none; stroke: var(--green); stroke-width: 4; stroke-linecap: round;
  stroke-linejoin: round; opacity: .6; }
.gr-f { position: absolute; color: var(--green); font-size: clamp(1rem, 2vw, 1.6rem);
  font-weight: 600; opacity: .22; }
html.motion .gr-bar { transform-box: fill-box; transform-origin: 50% 100%;
  animation: grBar 12s var(--ease) infinite; animation-delay: calc(var(--i) * .25s); }
html.motion .gr-line { animation: grLine 12s ease-in-out infinite; }
html.motion .gr-head { animation: grHead 12s ease-in-out infinite; }
html.motion .gr-f { animation: grRise 11s ease-in-out infinite; animation-delay: var(--d); }
@keyframes grBar { 0% { transform: scaleY(0) } 18%, 88% { transform: scaleY(1) }
  100% { transform: scaleY(0) } }
@keyframes grLine { 0%, 18% { stroke-dashoffset: 1; opacity: 0 } 19% { opacity: .6 }
  42%, 88% { stroke-dashoffset: 0; opacity: .6 } 100% { stroke-dashoffset: 0; opacity: 0 } }
@keyframes grHead { 0%, 40% { opacity: 0 } 46%, 88% { opacity: .6 } 100% { opacity: 0 } }
@keyframes grRise { 0% { transform: translateY(30px); opacity: 0 } 25% { opacity: .26 }
  75% { opacity: .2 } 100% { transform: translateY(-60px); opacity: 0 } }

/* ------------------------------------------------ /social: Kelly's board */
.kd-links { display: flex; flex-wrap: wrap; gap: .6rem; }
.kd-link { display: inline-flex; align-items: center; gap: .5rem; padding: .55rem .95rem;
  border: 1px solid var(--green-line); border-radius: 999px; color: var(--ink);
  font-size: .88rem; font-weight: 600; transition: border-color .25s, background .25s, transform .25s var(--ease); }
.kd-link svg { width: 18px; height: 18px; color: var(--green); }
.kd-link:hover { border-color: var(--green); background: rgba(45,232,181,.08); transform: translateY(-2px); }
.kd { margin-top: 2rem; padding: clamp(1.1rem, 3vw, 1.8rem); display: grid; gap: 1.2rem; }
.kd-top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
  gap: .6rem; padding-bottom: 1rem; border-bottom: 1px solid var(--line); }
.kd-live { display: inline-flex; align-items: center; gap: .6rem; font-weight: 600; color: var(--ink); }
.kd-live i { width: .6rem; height: .6rem; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0 0 rgba(45,232,181,.5); }
html.motion .kd-live i { animation: kdPulse 2.4s ease-out infinite; }
@keyframes kdPulse { 70% { box-shadow: 0 0 0 10px rgba(45,232,181,0) } }
.kd-src { font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.kd .stats { margin: 0; }
@media (min-width: 760px) { .kd .kd-stats { grid-template-columns: repeat(3, 1fr); } }
.kd .stat { background: var(--bg); }
.kd .figure { background: var(--bg); }
.kd-hls { display: grid; gap: 1rem; }
@media (min-width: 900px) { .kd-hls { grid-template-columns: repeat(3, 1fr); } }
.kd-hl { position: relative; display: flex; flex-direction: column; gap: .45rem;
  padding: 1.3rem; border: 1px solid var(--line); border-radius: var(--radius-s);
  background: linear-gradient(160deg, rgba(45,232,181,.06), transparent 60%); color: inherit;
  transition: transform .35s var(--ease), border-color .3s; }
.kd-hl:hover { transform: translateY(-4px); border-color: var(--green-line); }
.kd-k { font-size: .7rem; letter-spacing: .16em; text-transform: uppercase; color: var(--green); }
.kd-big { font-family: var(--font-display); font-size: clamp(1.5rem, 3vw, 2rem); line-height: 1.1;
  color: var(--ink); }
.kd-big.mono { font-family: var(--font-mono); font-weight: 600; }
.kd-hl p { margin: 0; color: var(--muted); font-size: .94rem; }
.kd-pkg { border-color: var(--green-line); }
.kd-go { margin-top: auto; padding-top: .4rem; display: inline-flex; align-items: center; gap: .4rem;
  color: var(--green); font-weight: 600; font-size: .9rem; }
.kd-go svg { width: 16px; height: 16px; transition: transform .25s var(--ease); }
.kd-pkg:hover .kd-go svg { transform: translateX(4px); }
.kd .fig-cap { margin: 0; }

/* ---------------------------------------------------- /drones: arsenal */
.ars { gap: 1.4rem; }
.ars-card { overflow: hidden; padding: 0; display: flex; flex-direction: column;
  transition: transform .4s var(--ease), border-color .3s; }
.ars-card:hover { transform: translateY(-6px); border-color: var(--green-line); }
.ars-stage { position: relative; aspect-ratio: 16 / 10; display: grid; place-items: center;
  background: radial-gradient(ellipse 60% 70% at 50% 45%, rgba(45,232,181,.1), transparent 70%),
    linear-gradient(180deg, #0B1212, var(--bg)); border-bottom: 1px solid var(--line); }
.ars-stage picture, .ars-stage > img { width: 100%; height: 100%; }
.ars-stage img { object-fit: cover; }
.drone-art { width: min(62%, 300px); height: auto; color: var(--ink); position: relative; z-index: 1; }
.dr-guard { stroke: var(--green); opacity: .55; }
.dr-disc { stroke-dasharray: 3 7; opacity: .35; }
.dr-frame { stroke: var(--muted); }
.dr-body { fill: var(--panel); stroke: var(--ink); }
.dr-lens { fill: var(--green); stroke: none; }
.dr-prop { stroke: var(--ink); opacity: .75; }
.dr-prop circle { fill: var(--panel); }
.ars-shadow { position: absolute; left: 50%; bottom: 9%; width: 46%; height: 7%; z-index: 0;
  transform: translateX(-50%); border-radius: 50%;
  background: radial-gradient(closest-side, rgba(0,0,0,.75), transparent); }
html.motion .drone-art { animation: arsHover 5s ease-in-out infinite; }
html.motion .ars-card + .ars-card .drone-art,
html.motion .ars-card + .ars-card .ars-shadow { animation-delay: -2.5s; }
html.motion .ars-shadow { animation: arsShadow 5s ease-in-out infinite; }
html.motion .dr-prop { transform-box: fill-box; transform-origin: center;
  animation: arsSpin .5s linear infinite; }
html.motion .ars-card:hover .drone-art { animation-duration: 2.6s; }
@keyframes arsHover { 0%, 100% { transform: translateY(4px) rotate(-1.5deg) }
  50% { transform: translateY(-10px) rotate(1.5deg) } }
@keyframes arsShadow { 0%, 100% { transform: translateX(-50%) scale(1); opacity: 1 }
  50% { transform: translateX(-50%) scale(.8); opacity: .6 } }
@keyframes arsSpin { to { transform: rotate(calc(360deg * var(--s, 1))); } }
.ars-body { padding: 1.5rem 1.6rem 1.7rem; display: grid; gap: .55rem; }
.ars-body h3 { font-size: 1.6rem; color: var(--ink); }
.ars-body p { margin: 0; color: var(--muted); }
.ars-chips { list-style: none; padding: 0; margin: .5rem 0 0; display: flex; flex-wrap: wrap; gap: .45rem; }
.ars-chips li { font-size: .72rem; letter-spacing: .06em; padding: .35rem .7rem;
  border: 1px solid var(--green-line); border-radius: 999px; color: var(--green); }

/* ----------------------------------------------------- /drones: the film */
.film { margin: 0 auto; max-width: 1120px; }
.film-frame { position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden;
  border-radius: var(--radius); border: 1px solid var(--line); background: #000;
  box-shadow: 0 40px 80px -40px rgba(0,0,0,.9), 0 0 0 1px rgba(45,232,181,.06); color: var(--ink); }
.film-frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.2s var(--ease), filter .6s; filter: saturate(.92); }
.film-frame:hover img { transform: scale(1.03); filter: saturate(1.05); }
.film-shade { position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,.8)); }
.film-play { position: absolute; left: 50%; top: 50%; width: clamp(64px, 9vw, 92px); aspect-ratio: 1;
  transform: translate(-50%, -50%); border-radius: 50%; display: grid; place-items: center;
  background: var(--green); color: var(--bg); box-shadow: 0 0 0 10px rgba(45,232,181,.18);
  transition: transform .3s var(--ease), box-shadow .3s; }
.film-play svg { width: 40%; height: 40%; margin-left: 6%; }
.film-frame:hover .film-play, .film-frame:focus-visible .film-play {
  transform: translate(-50%, -50%) scale(1.08); box-shadow: 0 0 0 16px rgba(45,232,181,.14); }
html.motion .film-play::after { content: ""; position: absolute; inset: -10px; border-radius: 50%;
  border: 2px solid rgba(45,232,181,.5); animation: filmRing 2.6s ease-out infinite; }
@keyframes filmRing { from { transform: scale(.85); opacity: 1 } to { transform: scale(1.5); opacity: 0 } }
.film-meta { position: absolute; left: clamp(1rem, 3vw, 2rem); bottom: clamp(.9rem, 2.6vw, 1.6rem);
  right: 1rem; display: grid; gap: .25rem; text-align: left; }
.film-meta b { font-family: var(--font-display); font-size: clamp(1.05rem, 2.6vw, 1.6rem); }
.film-meta .mono { font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--green); }
.film-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.film-frame.is-playing { cursor: default; }

/* ------------------------------------------- /lead-conversion: the hub */
.hub { display: grid; gap: 1.2rem; align-items: center; margin-top: 1rem; }
@media (min-width: 900px) { .hub { grid-template-columns: 230px 110px 1fr; gap: 0; } }
.hub-srcs { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .55rem; }
@media (min-width: 900px) { .hub-srcs { flex-direction: column; } }
.hub-src { display: flex; align-items: center; gap: .65rem; padding: .6rem .85rem;
  border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--bg);
  color: var(--ink); font-size: .92rem; font-weight: 600; }
.hub-ico { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  color: var(--green); background: rgba(45,232,181,.1); flex: none; }
.hub-ico svg { width: 17px; height: 17px; }
.hub-src > i { margin-left: auto; width: .45rem; height: .45rem; border-radius: 50%;
  background: var(--green-line); }
html.motion .hub-src > i { animation: hubPing 5s ease-in-out infinite;
  animation-delay: calc(var(--i) * 1s); }
@keyframes hubPing { 0%, 14%, 100% { background: var(--green-line); box-shadow: none }
  6% { background: var(--green); box-shadow: 0 0 0 5px rgba(45,232,181,.2) } }
.hub-flow { display: flex; justify-content: center; gap: .5rem; height: 40px; }
.hub-flow i { width: 2px; height: 100%;
  background: repeating-linear-gradient(180deg, var(--green) 0 6px, transparent 6px 12px); opacity: .5; }
@media (min-width: 900px) {
  .hub-flow { flex-direction: column; height: auto; gap: 1.6rem; padding: 0 .9rem; }
  .hub-flow i { width: 100%; height: 2px;
    background: repeating-linear-gradient(90deg, var(--green) 0 8px, transparent 8px 16px); }
}
html.motion .hub-flow i { animation: hubFlowY 1s linear infinite; background-size: 100% 24px; }
@media (min-width: 900px) { html.motion .hub-flow i { animation-name: hubFlowX; background-size: 32px 100%; } }
@keyframes hubFlowY { to { background-position: 0 24px; } }
@keyframes hubFlowX { to { background-position: 32px 0; } }
.hub-dash { padding: 1.2rem; background: var(--bg); }
.hub-top { display: flex; justify-content: space-between; align-items: center;
  padding-bottom: .8rem; margin-bottom: .6rem; border-bottom: 1px solid var(--line); }
.hub-top b { color: var(--ink); }
.hub-tag { font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: .2rem .55rem; }
.hub-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.hub-row { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: .8rem;
  padding: .7rem .8rem; border-radius: var(--radius-s); background: var(--panel); }
.hub-dot { width: .5rem; height: .5rem; border-radius: 50%; background: var(--green); }
.hub-what { display: grid; min-width: 0; }
.hub-what b { color: var(--ink); font-size: .94rem; }
.hub-what .mono { color: var(--muted); font-size: .72rem; letter-spacing: .08em; }
.hub-st { font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; padding: .28rem .6rem;
  border-radius: 999px; border: 1px solid var(--green-line); color: var(--green); white-space: nowrap; }
.hub-contacted { border-color: var(--line); color: var(--muted); }
.hub-booked { background: var(--green); border-color: var(--green); color: var(--bg); }
html.motion .hub-row { animation: hubIn 11s var(--ease) infinite;
  animation-delay: calc(var(--i) * .7s); }
@keyframes hubIn { 0% { opacity: 0; transform: translateX(-14px) }
  6%, 86% { opacity: 1; transform: none } 94%, 100% { opacity: 0; transform: translateX(10px) } }

/* --------------------------------------- /lead-conversion: the quote */
.lq { display: grid; gap: 1.6rem; padding: clamp(1.4rem, 4vw, 2.6rem);
  border-color: var(--green-line); }
@media (min-width: 900px) { .lq { grid-template-columns: 1.1fr 1fr; gap: 3rem; align-items: center; } }
.lq-words { display: grid; gap: .9rem; justify-items: start; }
.lq-words .tier { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--green); }
.lq-amount { font-size: clamp(1.5rem, 3.4vw, 2.2rem); font-weight: 600; color: var(--green); }
.lq-words p { margin: 0; color: var(--muted); max-width: 46ch; }
.lq-words .btn { margin-top: .6rem; }
.lq-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }
.lq-list li { display: flex; align-items: center; gap: .8rem; padding: .95rem 1.1rem;
  border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--bg);
  color: var(--ink); font-weight: 600; }
.lq-list svg { width: 22px; height: 22px; flex: none; color: var(--bg); background: var(--green);
  border-radius: 50%; padding: 4px; }

/* -------------------------------------------------- /work: the websites */
.ss-gallery { overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none;
  margin-inline: calc(var(--bleed, 1rem) * -1); padding: .4rem var(--bleed, 1rem) 1rem; }
@media (min-width: 640px) { .ss-gallery { --bleed: 1.5rem; } }
.ss-gallery::-webkit-scrollbar { display: none; }
.ss-track { display: flex; width: max-content; }
.ss { flex: none; width: min(82vw, 460px); margin-right: 1.2rem; scroll-snap-align: start;
  display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); overflow: hidden; color: inherit;
  transition: transform .4s var(--ease), border-color .3s, box-shadow .4s; }
.ss:hover, .ss:focus-visible { transform: translateY(-6px); border-color: var(--green-line);
  box-shadow: 0 30px 60px -30px rgba(0,0,0,.9); }
.ss-bar { display: flex; align-items: center; gap: .35rem; padding: .6rem .8rem;
  background: #111717; border-bottom: 1px solid var(--line); }
.ss-bar i { width: .55rem; height: .55rem; border-radius: 50%; background: var(--line); }
.ss-bar .mono { margin-left: .6rem; flex: 1; font-size: .7rem; color: var(--muted);
  background: var(--bg); border-radius: 999px; padding: .25rem .7rem; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.ss-img { display: block; aspect-ratio: 16 / 10; overflow: hidden; background: var(--bg); }
.ss-img picture, .ss-img img { display: block; width: 100%; height: 100%; }
.ss-img img { object-fit: cover; object-position: top; transition: transform .8s var(--ease); }
.ss:hover .ss-img img { transform: scale(1.04); }
.ss-cap { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: .2rem .8rem;
  padding: 1rem 1.1rem 1.1rem; }
.ss-cap b { color: var(--ink); font-size: 1.05rem; }
.ss-cap .mono { grid-column: 1; font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--green); }
.ss-cap svg { grid-column: 2; grid-row: 1 / span 2; width: 20px; height: 20px; color: var(--green);
  transition: transform .25s var(--ease); }
.ss:hover .ss-cap svg { transform: translateX(4px); }
/* The marquee (site.js adds .is-marquee on a wide screen under motion). */
.ss-gallery.is-marquee { overflow: hidden; scroll-snap-type: none;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); }
.is-marquee .ss-track { animation: ssScroll 70s linear infinite; }
.is-marquee:hover .ss-track, .is-marquee:focus-within .ss-track { animation-play-state: paused; }
@keyframes ssScroll { to { transform: translateX(-50%); } }

/* The logo plates: a mark drawn for white sits on a light plate. */
.logo-shot.is-light { background: var(--ink); }
.logo-shot.is-light img, .logo-shot.is-contain img { object-fit: contain; padding: 12%; }

/* ==== 30-contact.css ==== */
/* Mendoza Marketing — the /contact page.

   This file styles the quote form and the "or reach out" column and nothing
   else. The tokens, the type, the buttons, the panel, the nav, the footer and
   the reveal contract are 00-base.css's and are not restated here; --band and
   --well are declared by 10-home.css, which lands before this file, and are
   written with their literal fallback so this part still renders alone.

   site/css/site.css is concatenated from site/css/parts/*.css in name order,
   so this is the last part in and may lean on every token above it.

   =====================================================================
   CONTRAST — every pair this file sets type in (CLAUDE.md rule 9). All six
   are already computed in the tables above; the numbers are repeated rather
   than assumed, because this file is where a reviewer looks for the form.

     foreground        background      ratio     verdict
     --ink    #DDE4E1  --panel#1A1F1F  12.91:1   AAA   the form card's words
     --green  #2de8b5  --panel#1A1F1F  10.58:1   AAA   eyebrows, the card icons
     --muted  #93A3A3  --panel#1A1F1F   6.36:1   AA    field labels, hints,
                                                       the note under the button
     --ink    #DDE4E1  --well #0b1413  14.47:1   AAA   what the visitor types
     --muted  #93A3A3  --well #0b1413   7.13:1   AAA   placeholders, the caret
     --green  #2de8b5  --well #0b1413  11.86:1   AAA   the focus ring's edge
     --bg     #050A0A  --green#2de8b5  12.64:1   AAA   "Send the brief"

   The form has no red. CLAUDE.md rule 1 allows one accent and this page is
   not the exception: a failed send is said in --ink on the same surface with
   a white edge down its left side, which reads as "stop" without introducing
   a second colour to the site.

   =====================================================================
   MOTION — the two columns are .rv, which is the base's contract (hidden only
   under html.motion, self-revealing after 2.8s). Nothing else on this page
   moves, and nothing at all is hidden from a visitor with no script: the
   whole form, both selects and every option are in the server HTML.
*/

/* --------------------------------------------------------------- the hero */
/* The two-tone headline. page_hero() puts `cls` on the <section>, not on the
   <h1>, so the base's `.h-two span` would also catch the two <span>s in the
   breadcrumb and drop them down the page in green. The rule is therefore
   restated here against this hero's own heading. */
.contact-hero h1 span{color:var(--green);display:block}
.contact-hero h1{font-size:clamp(2.3rem,6.4vw,4.2rem)}
/* The base's 62svh floor is there so a photograph has room to be one. This
   hero has no photograph, so it is as tall as its words and the floor never
   binds; what would be left is a stack of black between the headline and the
   only thing the page is for. The hero keeps its top padding — the words need
   to clear the nav — and gives the bottom back. */
@media (min-width:960px){
  .page-hero.contact-hero{min-height:0;padding-bottom:2.5rem}
}

/* --------------------------------------------------------------- the grid */
.contact-sec{padding-top:clamp(2rem,4vw,2.5rem)}
.contact-grid{display:grid;gap:1.2rem;align-items:start}
@media (min-width:960px){
  .contact-grid{grid-template-columns:7fr 5fr;gap:1.6rem}
}
/* Both columns are grid items: without this a long email address or a wide
   select stretches the track instead of wrapping inside it. */
.contact-grid>*{min-width:0}

/* --------------------------------------------------------------- the form */
.quote{padding:1.3rem}
@media (min-width:640px){.quote{padding:2rem}}
.quote h2{font-size:clamp(1.5rem,3.6vw,2rem)}

.form{display:grid;gap:1rem;margin-top:1.5rem}
.form .row{display:grid;grid-template-columns:1fr;gap:1rem}
@media (min-width:560px){.form .row{grid-template-columns:1fr 1fr}}
.form .row>*{min-width:0}

.field{display:grid;gap:.4rem;min-width:0}
.field label{font-family:var(--font-mono);font-size:.7rem;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}
.field input,.field textarea,.field select{
  width:100%;padding:.8rem .9rem;border-radius:var(--radius-s);
  border:1px solid var(--line);background:var(--well,#0b1413);color:var(--ink);
  font-family:var(--font-body);font-size:1rem;line-height:1.45;
  transition:border-color .2s,box-shadow .2s}
.field input::placeholder,.field textarea::placeholder{color:var(--muted);opacity:1}
.field input:focus,.field textarea:focus,.field select:focus{
  outline:none;border-color:var(--green);box-shadow:0 0 0 3px rgba(45,232,181,.18)}
.field textarea{min-height:140px;resize:vertical}
/* The dropdown itself is the platform's, and on a dark page the platform
   paints it light unless it is told otherwise. */
.field option{background:var(--panel);color:var(--ink)}
.field .hint{font-size:.82rem;line-height:1.5;color:var(--muted)}

/* The caret is drawn rather than left to the platform: appearance:none takes
   the native arrow away, and a select with no arrow does not look like one. */
.sel{position:relative;display:block}
.sel select{appearance:none;-webkit-appearance:none;padding-right:2.4rem;
  cursor:pointer}
.sel::after{content:"";position:absolute;right:1rem;top:50%;
  width:8px;height:8px;margin-top:-6px;pointer-events:none;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:rotate(45deg)}
.sel:focus-within::after{border-color:var(--green)}

/* The honeypot. Off the page rather than display:none, because a bot that
   reads the stylesheet skips a hidden field and fills a visible one; a human
   never reaches it (tabindex -1, aria-hidden) and a screen reader is not told
   about it. Its name is Formspree's own: _gotcha. */
.gotcha{position:absolute;left:-9999px;top:auto;width:1px;height:1px;
  overflow:hidden}

.form .btn-row{margin-top:.4rem}
.form-status{display:none;padding:.9rem 1.1rem;border:1px solid var(--line);
  border-left:3px solid var(--green);border-radius:var(--radius-s);
  background:var(--well,#0b1413);color:var(--ink);font-size:.95rem;
  overflow-wrap:anywhere}
.form-status.on{display:block}
.form-status.is-bad{border-left-color:var(--ink)}
.form-status a{color:var(--green);text-decoration:underline;
  text-underline-offset:2px}
.form-status a:hover{color:var(--green-2)}
.form-note{margin:0;font-size:.85rem;line-height:1.6;color:var(--muted);
  overflow-wrap:anywhere}
.form-note a{color:var(--green)}
.form-note a:hover{color:var(--green-2)}

/* ------------------------------------------------------------ the sidebar */
.contact-side{display:grid;gap:.8rem}
.contact-side .eyebrow{margin-bottom:.2rem}
.side-card{display:flex;gap:.9rem;align-items:flex-start;min-width:0;
  padding:1.1rem 1.2rem;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);transition:border-color .2s}
a.side-card:hover,a.side-card:focus-visible{border-color:var(--green)}
.side-card .ic{flex:0 0 auto;display:grid;place-items:center;width:40px;height:40px;
  border-radius:var(--radius-s);border:1px solid var(--line);
  background:var(--well,#0b1413);color:var(--green)}
.side-card .ic svg{width:19px;height:19px}
.side-card>div{min-width:0}
.side-card b{display:block;margin-bottom:.15rem;font-size:.95rem;color:var(--ink)}
.side-card span{display:block;font-size:.9rem;line-height:1.55;color:var(--muted);
  overflow-wrap:anywhere}
a.side-card:hover b{color:var(--green)}

/* The two accounts Drew has not sent us yet: drawn, not linked, exactly as the
   footer does it. The base already styles .socials; only the spacing inside a
   card is this file's business. */
.side-social .socials{margin-top:.8rem}

/* The one card that is an offer rather than a detail, so it is a column with a
   button at the bottom of it instead of an icon beside a line of text. */
.side-card.book{display:flex;flex-direction:column;align-items:flex-start;
  padding:1.4rem 1.2rem}
.side-card.book h3{margin-top:.1rem;font-size:1.12rem}
.side-card.book p{margin:.6rem 0 1.2rem;font-size:.92rem;color:var(--muted);
  max-width:32ch}
.side-card.book .btn{margin-top:auto}

@media (prefers-reduced-motion:reduce){
  .field input,.field textarea,.field select,.side-card{transition:none}
}
