/* Humble Crew Auto Spa — the phone pass
   Loaded LAST on every page, deliberately: several of the rules below have to
   beat selectors like `.lp main > section + section` (0,1,3) that live in
   landing.css, and matching specificity from a later sheet is the only way to
   do that without an arms race of !important.

   The brief was "there is too much text on a phone". The measurement said
   otherwise: a service page carries 458–930 words but runs 8–15 screens. The
   problem was never the copy, it was the page height per word — so nothing
   here deletes a sentence. It compresses the space between them. */
@media (max-width:760px){

  /* ══ 手机端 hero:给视频一个真正的窗口 ═══════════════════════════════
     原来的做法是把桌面版整块搬到手机上:视频铺满整个 stage 当背景,眉标、
     大标题、评分、两个按钮、一整段介绍卡、两个认证 logo 全部压在它上面。
     结果是一屏被文字占满、视频只剩几条缝 —— Jason 2026-09-10 的原话是
     "特别拥挤看不见视频"。

     改成:视频不再是背景,而是一块 56svh 的**画面**,上面只落一样东西 ——
     标题,而且是落在它下缘本来就有的暗场上。其余信息退到视频下方的实地,
     不跟画面抢。首屏于是变成:导航 → 一整块干净的车间画面 → 标题 → 评分 →
     打电话。

     实现上用 display:contents 把 .hero__grid / .hero__left / .hero__right
     这三层容器在手机端"取消",让里面的元素直接成为 stage 网格的行 ——
     不动一行 HTML,桌面端一个像素都不变。                              */
  .hero{height:auto}
  .hero__stage{position:relative;height:auto;min-height:0;overflow:hidden;
    display:grid;grid-template-columns:100%;grid-auto-rows:auto;row-gap:0;
    padding:var(--sp-3) 0 var(--sp-6)}
  .hero__grid,.hero__left,.hero__right{display:contents}

  /* 每一行自己管左右留白 —— stage 的横向 padding 已经交给视频做满幅 */
  .hero__nav,.hero__h1,.rating,.hero__btns,.hero__card,.creds,.hero__rail,.hero__trio{
    padding-inline:clamp(var(--sp-5),5vw,var(--sp-6))}

  .hero__nav{grid-row:1;grid-column:1}

  /* 字标第二行:桌面导航条塞不下第三行,手机端字标下面有空,把店名补全。 */
  .brand__txt i.brand__sub{display:block;margin-top:4px;
    font-family:var(--font-mono);font-style:normal;font-size:10.5px;font-weight:600;
    letter-spacing:.30em;text-transform:uppercase;color:var(--ink-muted);line-height:1}

  /* 眉标带 —— 导航那条 1px 分隔线和画面上沿之间原来是 16px 的死空隙。
     把桌面那行眉标搬进来正好填满它:三个词左中右,中间的渐变短线 flex:1
     自己撑开,于是这条带子是满幅的,不再是"一段空的暗色"。 */
  .hero__trio{display:flex;grid-row:2;grid-column:1;align-items:center;
    gap:8px;margin:0;padding-block:11px 12px}
  .hero__trio span{font-size:clamp(8.5px,2.55vw,10.5px);letter-spacing:.11em;
    white-space:nowrap;color:var(--ink-subtle)}
  .hero__trio i{flex:1 1 auto;width:auto;min-width:10px;opacity:.7}

  /* 画面本体:满幅、56svh,和标题共用同一个网格格子 */
  .hero__media{position:relative;inset:auto;grid-row:3;grid-column:1;
    height:calc(60svh / var(--z));min-height:360px;overflow:hidden}
  /* 顶部压暗是给"masthead 压在画面上"用的 —— 手机端导航已经挪到画面上方,
     这层纯属白白糟蹋画面,直接撤掉(上一版留了 26%,等于话说了一半)。 */
  .hero__topfade{display:none}
  /* 底部压暗只需要托住标题那 117px。原来 62% 高、从 y=454 起就是 ≥.74 的黑,
     等于为了没有字的地方也压掉约 140px 画面。收到 40%,并且**不**收到
     var(--bg):收到纯黑等于画面没有下边缘,窗口又化回背景 —— 那正是这次要
     摆脱的东西。对比度由标题自己的投影补回来(它是纯白字,不是渐变填色,
     .sweep 上不能这么干)。 */
  .hero__basefade{height:40%;background:linear-gradient(180deg,transparent 0%,
    rgba(0,0,0,.30) 30%,rgba(0,0,0,.76) 64%,rgba(0,0,0,.92) 100%)}
  /* 品牌色罩在手机端要收:site.css 的手机分支把 saturate 抬回 .94,画面自己
     的彩度和这层 mix-blend-mode:color 的品红罩正面打架 —— 实测源片那块车漆
     是 R55/G72/B102(蓝),渲染出来是 R170/G112/B148(品红),通道顺序整个翻了。
     一个做漆面保护的店,hero 里看不出车漆是什么颜色,是硬伤。 */
  .hero__media::after{opacity:.16}
  .hero__video{filter:saturate(.82) contrast(1.06) brightness(.98)}
  .hero__rules{display:none}     /* 6 条竖线在 390px 宽上只剩噪点 */

  .hero__h1{grid-row:3;grid-column:1;align-self:end;z-index:3;
    margin:0;padding-bottom:clamp(18px,4.2vw,26px);
    font-size:clamp(30px,8.8vw,46px);line-height:.96;
    text-shadow:0 2px 20px rgba(0,0,0,.9)}

  /* 384 条五星是这家店最硬的资产,原来继承桌面那套 16.5px + 大字距,在 390px
     上折成两行,读起来像设置面板的说明文字。压成一行。 */
  .rating{grid-row:4;grid-column:1;margin-top:18px;
    display:flex;align-items:baseline;gap:10px;flex-wrap:nowrap}
  .rating__stars{font-size:15px;letter-spacing:.14em}
  .rating__txt{font-size:13px;letter-spacing:.10em}
  .rating__txt b{font-size:19px}
  /* 手机上唯一要发生的事是打电话。原来两个 342x54 的框一样大,等于把
     "Get in touch" 和电话号码摆成同等分量;而且第二个框的下沿正好落在 844
     的折线上 —— 既没被切到"下面还有",又不是一屏收住,是最含糊的那种。
     第二动作降成文字链:少一个框,不少一个字,折线改落在正文第一行上。 */
  .hero__btns{grid-row:5;grid-column:1;margin-top:20px;
    display:flex;flex-direction:column;gap:0;width:100%}
  .hero__btns .btn{width:100%}
  .hero__btns .btn:first-child{background:var(--brand-sweep);border-color:transparent;color:#fff}
  .hero__btns .btn--ghost{min-height:44px;border:0;background:none;padding:0;
    justify-content:flex-start;font-size:14.5px;
    text-decoration:underline;text-underline-offset:6px;
    text-decoration-color:rgba(255,0,128,.5)}
  /* 注意:这里只能清上下内边距。写 padding:0 会把上面那条统一的
     padding-inline 一起清掉,正文就顶到屏幕边、右侧还会被切掉。 */
  /* 一条发丝线就够说明"换了一个层次"了 —— 这是站内本来就有的手法(导航下沿、
     地纹竖线)。不然画面以下就是六个左对齐、间距几乎一样的同级块。 */
  .hero__card{grid-row:6;grid-column:1;margin-top:var(--sp-6);
    padding-block:var(--sp-6) 0;
    background:none;border:0;border-top:1px solid var(--line);
    backdrop-filter:none;box-shadow:none}
  /* 渐变高亮保持和桌面一致 —— 上一版在这里把后四个还原成白字,Jason 要求撤销。 */
  .hero__lede{font-size:17px;line-height:1.55}
  .creds{grid-row:7;grid-column:1;margin-top:var(--sp-6);
    display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-4)}
  .creds__label{text-align:left;font-size:13px;line-height:1.7}
  /* 底栏在手机端整条撤掉:跑马灯和下面那一屏的服务区一字不差地重复,而
     "Scroll to fast-forward" 说的是首访时的入场锁 —— 那个锁现在只在桌面跑
     (见 js/reel.js),手机端没有可被"加速"的东西了。省下 74px。
     (前一版这里写的是"手机端根本没有那个机制",是错的:reel.js 的 hold
      本来带 touchmove 分支,手机端一样会锁。所以才改成只在桌面锁。) */
  .hero__rail{display:none}


  /* ── 手机端文案精简(Jason 2026-09-10)──────────────────────
     手机上一屏放不下几行,几个大段正文摞在一起就是一堵墙。这里把每段里
     可删的整句/整从句藏掉,只留主干。桌面端一个字都不改。

     用的是同一份内容的响应式呈现:被藏的句子仍在 DOM 里、仍是客户自己的原话,
     不是给爬虫看一套给人看另一套。所有精简都只做"删除连续的一段",
     没有新增或改写任何一个词。
     刻意没动:客户评价(截断别人的证言不合适)和 blog 全文。 */
  .mo-cut{display:none}

  /* section rhythm — the run-on gap between stacked sections was 84px on top
     of each section's own 56px, which reads as an empty screen on a phone */
  .lp main > section{padding-block:clamp(40px,7vw,56px)}
  .lp main > section + section{padding-top:clamp(52px,10vw,72px)}

  /* the portfolio reads perfectly well two-up at this size, and stacking it
     cost four full screens on the ceramic page alone */
  .work__grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .work__grid figcaption{font-size:15.5px;line-height:1.45;margin-top:10px}
  .wcard__media{aspect-ratio:4/3}

  /* package plates: the picture does not need to be as tall as the copy */
  .pkg__plates{gap:var(--sp-5)}
  .plate{padding-top:var(--sp-5)}
  .plate__media{aspect-ratio:2/1}

  .reasons__grid,.steps__list,.proc__list{gap:var(--sp-5)}

  /* ── the counter, on a phone ──────────────────────────────────────────
     Same idea, tighter: the surface has to reach full strength sooner
     because the headline wraps to four lines here and pushes the .em words
     up to ~215px from the section top. .proof's bottom padding is only 64px
     on a phone, so the lead-in has to fit inside that. */
  .quote{--lead-in:52px}
  .quote__plate{background:linear-gradient(180deg,
    rgba(0,0,0,0)     0,
    rgba(0,0,0,.42)   26px,
    rgba(0,0,0,.90)   var(--lead-in),
    rgba(0,0,0,.975)  124px,
    rgba(0,0,0,.975)  calc(100% - var(--tail)),
    rgba(0,0,0,0)     100%)}

  /* six hairlines across 390px is noise, not structure — the same call
     .hero__rules already makes above. The plate keeps its grain instead. */
  .quote__rules{display:none}

  .quote__inner{padding-top:var(--sp-6)}
  .quote__lead{padding-top:var(--sp-6)}
  /* "TELL US ABOUT THE / CAR." left a four-word orphan; balance the block */
  .quote__h2{text-wrap:balance}
  .quote__col{padding-top:var(--sp-7)}
  /* the phone number is the one thing a phone user actually wants, so it
     stays above the form rather than under it */
  .quote__tel{font-size:clamp(28px,8.2vw,34px)}

  /* the quote form — 174px per field is a desktop rhythm on a phone */
  .qrow{gap:var(--sp-4)}
  .quote__form{gap:var(--sp-4)}
  .quote__facts{gap:var(--sp-5);margin-top:var(--sp-7);padding-top:var(--sp-5)}
  /* 44px minimum touch target for the service chips */
  .chip span{min-height:44px}

  /* The journal index is 24 rows. Stacked full-width — picture over title over
     a full dek — it ran 15.6 screens, which is not an index, it is a queue.
     A square thumbnail beside the text and a two-line excerpt keeps every post
     scannable and the whole list browsable. Nothing is removed: the excerpt is
     clamped visually and the full post is one tap away. */
  .lp main > section.jr{padding-block:calc(var(--sp-5) + 118px) clamp(40px,7vw,56px)}
  .lp main > section.gal{padding-block:calc(var(--sp-5) + 118px) clamp(40px,7vw,56px)}
  .lp main > section.bk{padding-block:calc(var(--sp-5) + 108px) clamp(40px,7vw,56px)}
  .jr__link{grid-template-columns:minmax(0,1fr) 96px;gap:var(--sp-4);
    align-items:start;padding-block:var(--sp-5)}
  .jr__thumb{order:0;aspect-ratio:1;border-radius:10px}
  .jr__meta{font-size:13.5px;gap:var(--sp-3)}
  .jr__title{font-size:20.5px;line-height:1.14;margin-top:10px}
  .jr__dek{margin-top:8px;font-size:17px;line-height:1.5;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .post__body{padding-block:clamp(40px,7vw,56px)}
  .post__col h2{margin-top:var(--sp-6);padding-top:var(--sp-5)}
  .post__fig,.post__gal{margin-block:var(--sp-5)}
  .post__gal{gap:8px}
}

/* ── 手机端闪黑屏的处理（2026-09-15）───────────────────────────────────
   症状:滑到页脚附近偶尔整屏闪一下黑。headless Chrome 26 帧复现不出来 ——
   这是真机(尤其 iOS Safari)的合成层问题,不是布局问题。

   针对三个已知成因,全部只在手机端生效:

   1) .ground 是 position:fixed 的整屏底层,上面还挂着一个**无限循环**的
      grain 动画。固定层 + 持续动画 = iOS 上最典型的闪屏来源。
      grain 的 opacity 只有 .028,手机上根本看不见,停掉零视觉代价。
   2) mix-blend-mode + filter:blur(120px) 会强制离屏合成,是 iOS 黑帧的常客。
      .hero__bloom 在手机端本来就被压得很小,直接去掉混合模式。
   3) 给 .ground 一个稳定的合成层(translateZ + backface-visibility),
      让它不要在滚动中被反复光栅化。 */
@media (max-width:760px){
  /* ── 手机端不要 .ground 这个固定层（2026-09-15 定稿）──────────────
     症状:滑动时背景闪黑,慢滑能看到"彩色背景变黑、文字还在"。
     iOS Safari 和 iOS Chrome(同为 WebKit)都复现 —— 是 WebKit 对
     position:fixed 整屏层的合成/重绘问题,不是布局问题。

     前两轮我试过「钉住这层」(translateZ)和「压小这层」(blur+缩尺寸),
     都没解决,而且 blur 很可能让它更糟(滤镜强制离屏渲染)。

     换思路:**这层在手机上根本没有存在的必要。**
     body 自己就是 background:var(--bg) 的纯黑,.ground 在手机上只是
     额外画了一层会闪的东西。直接不渲染它 ——
     · 闪黑从根上消失:没有固定层,就没有可丢的固定层
     · 顺带解决你说的那两块文字读不清(洋红糊屏的就是它里面的 drift)
     · 品牌渐变一点没丢:首屏 .hero__bloom、.em 渐变字、按钮扫光都在,
       那些是各自区块内的 absolute 元素,不是固定层
     桌面完全不受影响,这条只在 ≤760px 生效。 */
  .ground{display:none}

  /* ④ mix-blend-mode + 120px 模糊会强制离屏合成,是 iOS 黑帧的常客 */
  .hero__bloom{mix-blend-mode:normal;filter:blur(60px);opacity:.26}
}

/* ══ founder + footer: a surface of their own, on a phone ═══════════════════
   Client's iPhone screenshots: white and pink copy sitting on a bright
   magenta wash in "THE MAN BEHIND THE MISSION" and in the footer.

   MEASURED CAUSE (not guessed — each layer was switched off in turn at 390px,
   headless Chrome with SwiftShader so the WebGL layer actually renders):
       ground as shipped ........... bg under .foot__col a = rgb(223,6,149)
       .ground__light off .......... rgb(4,4,4)      ← this is the one
       .ground__drift off .......... rgb(216,5,151)  ← contributes ~7/255
   So the wash was js/raking.js — the fixed full-viewport WebGL beam whose
   ramp runs to #ff0080 and #ffd9ec — NOT .ground__drift, whose radials only
   ever reach rgba(255,0,128,.14) and cannot paint anything near that value.
   Worst measured line was "Call us" (--accent #ff0080) at 1.09:1 on the
   beam's core, i.e. literally invisible. Behind it: the .em word "Jesse" at
   3.76:1, five wrapped lines of .mission__p at 4.11-4.43:1, and in the
   footer one .foot__col h3 plus three .foot__col a at 3.88-4.37:1.

   THE FIX IS NOT "turn the light down". It is the same move .quote__plate
   already makes for the quote section (see site.css ~409): give the copy a
   full-bleed surface of its OWN, so the ground under a glyph is a value we
   chose rather than wherever the beam happens to be this second. Measured
   with the ground pinned at #ffd9ec — the hottest colour the shader can
   produce — the numbers below do not move, which is the whole point.

   And because the plate carries its own brand light, these two sections keep
   the pink→magenta→violet language even now that .ground is display:none on
   mobile: the light here is a per-section position:absolute element, not a
   fixed layer, so it costs nothing on the iOS compositing side.

   Geometry (390px, measured): .stack__pin is 1337px tall — 64px of empty
   padding, rail at 64-97, portrait 121-375, body copy 399-1273, 64px of
   empty padding. So both fades land in empty space and no edge is visible.
   .foot is 978px — 33px of padding, then content to 954.

   Measured, 390x844, dpr 3, median pixel of each line box (the brightest
   pixel in a line box is the glyph, not the ground). Icon-only links — the
   three white social discs, which are graphics at 3.77:1 against the #ff0080
   glyph and so pass the 3:1 non-text rule — are excluded from the counts.

                        beam build   .ground:none   + this block   ground back on
     founder worst        1.09:1        5.55:1         5.32:1         4.99:1
     footer  worst        3.88:1        6.84:1         6.56:1         6.17:1
     lines below AA      11 of 74      0 of 74        0 of 74        0 of 74
     brightest bg        rgb(248,       rgb(0,0,0)    rgb(67,0,34)   rgb(91,20,55)
      under any text       8,131)       flat black    brand light    still AA

   Column 2 is today's build: killing .ground took the wash out, but it also
   took every trace of the brand light out of these two sections — they are
   now flat #000. Column 3 is what this block is for: the light comes back as
   the section's own paint. Column 4 is the insurance — if .ground ever
   returns on mobile, the copy does not go back to 1.09:1.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width:760px){

  /* ── the founder ──────────────────────────────────────────────────────
     Two stacked backgrounds. The lower one is the surface: nothing at the
     section edge, full strength by the time the rail arrives, and back to
     nothing inside the bottom padding. The upper one is the brand light,
     painted here so it is a known quantity — strongest across the top, where
     the only text is white 15.5px mono and the portrait covers most of it,
     and down to a violet floor by the time the lead paragraph starts (36%).
     That floor is the binding constraint: the .em word "Jesse" and the two
     --accent links live on it, and #ff0080 needs the ground under it to stay
     below ~L 0.0118 to hold 4.5:1. It measures rgb(20,0,25) → 5.32:1. */
  .stack__pin{position:relative}
  .stack__pin::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
    background:
      linear-gradient(180deg,
        rgba(255,0,128,0)     0,
        rgba(255,0,128,.30)  44px,
        rgba(255,0,128,.24) 104px,
        rgba(186,0,178,.18)  20%,
        rgba(146,0,180,.15)  29%,
        rgba(138,0,176,.142) 36%,
        rgba(138,0,176,.138) 84%,
        rgba(170,0,192,.20) calc(100% - 30px),
        rgba(170,0,192,0)   100%),
      linear-gradient(180deg,
        rgba(0,0,0,0)     0,
        rgba(0,0,0,.62)  34px,
        rgba(0,0,0,.952) 96px,
        rgba(0,0,0,.952) calc(100% - 64px),
        rgba(0,0,0,.66)  calc(100% - 26px),
        rgba(0,0,0,0)    100%)}
  /* the plate is z-index 0 inside .stack__pin, so the content needs a layer.
     .mission is the only child; site.css gives it overflow-x:clip and no
     position, so this is additive. */
  .mission{position:relative;z-index:1}

  /* ── the footer ───────────────────────────────────────────────────────
     Same construction, shorter lead-in: .foot only has 32px of top padding
     before the wordmark, and it already draws a 1px top border, so the ramp
     is allowed to read as "the footer is a darker surface" — which is what a
     footer is. No bottom fade: that edge is the end of the document.
     left/right:0 is the padding box of .foot, which at 390px is the viewport,
     so the plate is full-bleed and has no side edges to give itself away. */
  .foot::before{content:"";position:absolute;left:0;right:0;top:0;bottom:0;
    z-index:0;pointer-events:none;
    background:
      linear-gradient(180deg,
        rgba(255,0,128,0)     0,
        rgba(255,0,128,.26)  26px,
        rgba(255,0,128,.20)  48px,
        rgba(168,0,182,.16)   9%,
        rgba(138,0,176,.15)  16%,
        rgba(138,0,176,.142) 70%,
        rgba(168,0,192,.22) 100%),
      linear-gradient(180deg,
        rgba(0,0,0,0)     0,
        rgba(0,0,0,.66)  20px,
        rgba(0,0,0,.955) 56px,
        rgba(0,0,0,.955) 100%)}
  .foot > *{position:relative;z-index:1}
}

/* ── 页脚下方那条黑带（2026-09-15）───────────────────────────────────
   页脚正好结束在文档底部(实测 13302 = 13302,之后 0 高度),所以那条黑带
   不是某个元素,是 **iOS Safari 的回弹/工具栏区域** —— 它画的是画布背景,
   而画布背景取自 html 的 background(纯黑)。页脚 plate 到文档边缘就停,
   于是出现「紫 → 硬切纯黑」。

   把画布底色设成页脚底部的实测色 rgb(37,0,42),接缝就消失了。
   只在手机端改;桌面的 .ground 会盖住画布,设不设都看不见。 */
@media (max-width:760px){
  html{background:rgb(37,0,42)}
  /* ⚠️ body 必须用**背景图**画黑,不能用背景色。
     iOS Safari 底部回弹区(拉过文档末尾那一下)取的是 **body 的背景色**,
     不是 html 的 —— 这是 CSS 背景传播在 WebKit 上的老毛病。
     写 background:#000 的话,再怎么设 html 都没用,回弹出来就是纯黑
     (2026-09-15 实机反复验证)。
     用 background-image 画黑、background-color 留 transparent:
       · body 盒子里照样是纯黑,页面观感不变
       · 回弹区落回 html 的 rgb(37,0,42),和页脚底色连成一片 */
  body{background-color:transparent;
       background-image:linear-gradient(#000,#000)}
}

/* ══════════════════════════════════════════════════════════════════════════
   THE COUNTER, REBUILT FOR A PHONE   +   brand light for the two sections
   that went flat when .ground was switched off    (2026-09-15, mobile only)
   ══════════════════════════════════════════════════════════════════════════

   Client, on his iPhone: "不觉得太黑了么啥也没有 …… 尤其是那个 get quote form."

   MEASURED CAUSE, not guessed. With .ground:none, 43 of the 44 line boxes
   in .quote measure their ground at exactly rgb(0,0,0) — the 44th is the
   submit label, which has the button's own fill under it. .quote__plate is
   still in the DOM and still painting, but every stop in it is a BLACK with
   an alpha, and black over black is a no-op. The section that was designed
   as "the page settles onto one machined surface" became the absence of one.

   Two separate problems, two separate fixes:

     1. NO SURFACE. Repaint .quote__plate with the brand ramp instead of
        black-on-black. Identical move to .stack__pin::before and
        .foot::before above, and for the identical reason: a section-local
        position:absolute plate is not a fixed layer, so it costs nothing on
        the iOS compositing side that killed .ground. No filter, no
        backdrop-filter, no animation, nothing fixed.
     2. NO FORM. Seven stacked fields — six 84px text slots and a 130px
        message — plus the chips and the submit row: 1140px of form, and
        every field of it is label-over-void. On a 844px screen that is one
        field per third of a screen with nothing between them, which is
        what the client's 5.jpeg and 6.jpeg actually show.
        The label is 15.5px Rajdhani at .16em — the same
        optical weight as the 17.5px answer beneath it — and the input
        itself is one 1px rule at white-42% plus a 5.5% wash, which on flat
        black is invisible. The client is not wrong that there is "nothing
        there": there is nothing there. Fields get a body; see ── the slots.

   Not one word of copy is added, removed or rewritten.

   MEASURED RESULT (390×844, dpr 3, headless with WebGL actually enabled —
   --disable-gpu makes getContext('webgl2') return null and every colour
   read is then fiction):
                              before          after
     .quote height            2331px          1829px   (−502, −21%)
     .quote__form height      1140px           810px   (−330)
     one field                  84px            63px
     .quote__facts             366px           281px
     worst contrast in       5.84:1 on       5.53:1 on
       .quote                rgb(0,0,0)      rgb(25,0,26)
     line boxes below AA      0 of 44         0 of 42
     ground under any        rgb(0,0,0)      rgb(19,0,24) → rgb(67,0,35)
       .quote glyph          flat black      brand light
   .svc and .proof: worst contrast unchanged (9.72:1 and 4.83:1, both
   pre-existing and both on their own opaque card faces), 0 failures before
   and after, ground rgb(0,0,0) → rgb(19,0,25)/rgb(19,0,24).
   Desktop: computed geometry, padding, background, radius and document
   height are byte-identical at 761 / 900 / 1440 / 1920px.

   SCOPE. Everything that touches the form is prefixed .quote__form or
   .quote on purpose: .fld is ALSO the promo-modal field on all 75 pages
   (`.promo__form .fld`, see contact-us.html:228), so a bare `.fld` rule
   here would silently redesign a dialog nobody asked about. .quote,
   .quote__col, .qrow, .fset, .qfoot, .qreq, .chip and .svc are index-only;
   .proof/.feed also appear on the four service landing pages and were
   checked there.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width:760px){

  /* ── the counter's surface ───────────────────────────────────────────
     Stops are in PX from the plate's top, not %, because the form below
     shortens by 330px in this block and a %-stop would slide the ramp up
     under the .em words. The plate starts --lead-in above the section,
     inside .proof's 64px of empty bottom padding, so the ramp-in is never
     seen against content.

     The binding constraint is the .em ramp in .quote__sub: its darkest stop
     is #ff0080 (L .2282), which needs the ground under it no lighter than
     L .0118 to hold 4.5:1. Those four words sit 257–343px below the section
     top = 313–399px down the plate, where the floor is rgba(150,0,180,.146)
     → measured rgb(22,0,26) to rgb(25,0,26), and they come out at
     5.53–6.19:1. The hot part of the ramp is spent by 240px, above them.

     --tail drops 96→64px. It is BOTH the section's bottom padding and the
     length of the plate's fade-out, and 96px of dead black between this
     plate and .foot::before's own pink leading edge is the seam in the
     client's 8.jpeg. */
  .quote{--lead-in:56px;--tail:64px}
  .quote__plate{background:
    /* light caught on the leading edge — the section's one bright moment,
       and it lands on empty padding and a white mono label, nothing else */
    radial-gradient(135% 190px at 24% 56px,rgba(255,0,128,.26),transparent 74%),
    radial-gradient(95% 165px at 88% 80px,rgba(138,0,176,.32),transparent 76%),
    /* the floor. This is what stops the section reading as a void: between
       the leading edge and the facts strip it never returns to #000. */
    linear-gradient(180deg,
      rgba(255,0,128,0)     0,
      rgba(255,0,128,.19)  56px,
      rgba(255,0,128,.165) 120px,
      rgba(196,0,172,.152) 240px,
      rgba(150,0,180,.146) 380px,
      rgba(138,0,176,.140) 560px,
      rgba(138,0,176,.138) calc(100% - 210px),
      rgba(152,0,188,.128) calc(100% - 120px),
      rgba(152,0,188,.085) 100%),
    /* the darkener the floor rides on */
    linear-gradient(180deg,
      rgba(0,0,0,0)     0,
      rgba(0,0,0,.44)  28px,
      rgba(0,0,0,.90)  56px,
      rgba(0,0,0,.968) 150px,
      rgba(0,0,0,.968) 100%)}

  /* ── the lead ────────────────────────────────────────────────────────
     26px made the page's one conversion headline smaller than .hero__h1
     (34.3px here). 29px, paid for by bringing the paragraph's leading in
     from 1.65 to 1.5: the whole lead column measures 522px against 536px
     before, so the headline grows and the block still ends up shorter. */
  .quote__h2{font-size:29px;line-height:1.02}
  .quote__sub{margin-top:var(--sp-5);font-size:19px;line-height:1.5}
  .quote__lead{padding-bottom:30px}
  .quote__reach{padding-top:30px}
  .quote__reach dt{margin-bottom:10px}
  .quote__reach dd{gap:2px}
  /* 44px touch targets without moving the type a pixel. The phone number is
     a 32px line box on a 32px font — it was a 32px tap target for the one
     thing a phone user is most likely to do in this section. */
  .quote__tel,
  .quote__mail{display:inline-flex;align-items:center;min-height:44px}

  /* ── the panel ───────────────────────────────────────────────────────
     site.css argues — correctly, for desktop — that a card on the plate is
     "just another rectangle". That argument assumes a plate you can see.
     On a phone the form has to be an OBJECT you can aim at: one raised
     face, lighter than the plate under it, with the brand lit along its
     leading edge. The counter, made literal.
     Measured in the 13px gutter beside the fields, so this is the face
     itself and not a slot: rgb(32,14,37) at the top, rgb(25,7,30) by the
     message field, against a plate of rgb(19,0,24) on both sides of it. It
     separates on value alone, so the border can stay a hairline. */
  .quote__col{padding:0 13px 20px;margin-top:2px;
    border:1px solid rgba(255,255,255,.14);border-radius:8px;
    background:linear-gradient(180deg,
      rgba(255,255,255,.075) 0,
      rgba(255,255,255,.030) 170px,
      rgba(255,255,255,.022) 100%);
    box-shadow:0 30px 60px -44px rgba(0,0,0,.95)}
  /* the seam hairline becomes the panel's lit edge. This overrides the
     max-width:960 branch in site.css, which lies the seam down into a
     full-width rule with a hard left end — on a panel that reads as a
     scratch across the top. */
  .quote__col::before{left:0;right:0;top:0;bottom:auto;width:auto;height:2px;
    border-radius:8px 8px 0 0;
    background:linear-gradient(90deg,rgba(255,0,128,0) 0,rgba(255,77,163,.95) 30%,
               rgba(199,125,255,.80) 72%,rgba(138,0,176,0) 100%)}
  .qreq{margin:0;padding:15px 0 14px;font-size:11.5px;letter-spacing:.14em}

  /* ── the slots ───────────────────────────────────────────────────────
     The single biggest change. The label stops being a heading stacked
     above a void and becomes a stamp inside the slot it names — the same
     mono spec-label language the site already speaks, now applied to the
     object it describes instead of floating above it. The field becomes a
     thing with edges you can see and hit: measured 63px tall, filled,
     bordered, 4px radius (automotive, not app-store — --radius is 2px).
     84px → 63px per field, and the label/answer hierarchy inverts. The
     slot fills measure rgb(38,21,43)–rgb(50,33,55), and the 11px labels on
     them 14.91–16.40:1.
     16.5px on the input is deliberate: under 16px, iOS zooms on focus.
     Checked in the browser: in none of the 7 fields does the label's box
     reach the input's text origin or its right edge. */
  .quote__form{gap:12px}
  .quote__form .fld{position:relative;display:block;gap:0}
  .quote__form .fld > span{position:absolute;z-index:2;left:13px;top:9px;
    pointer-events:none;font-size:11px;line-height:1;letter-spacing:.135em;
    color:var(--ink)}
  .quote__form .fld input,
  .quote__form .fld textarea{display:block;width:100%;box-sizing:border-box;
    font-size:16.5px;padding:26px 12px 9px;
    border:1px solid rgba(255,255,255,.17);border-radius:4px;
    background:linear-gradient(180deg,rgba(255,255,255,.085),rgba(255,255,255,.045));
    transition:border-color var(--dur-fast) var(--ease),
               background var(--dur-fast) var(--ease),
               box-shadow var(--dur-fast) var(--ease)}
  .quote__form .fld textarea{min-height:104px;padding-bottom:11px}
  /* the placeholder is the one string a contrast sweep never sees, because
     it is not a text node — so it was measured off the screenshot by hand.
     site.css's white @.5 measures 5.14:1 on the new filled slot (it passes;
     it did NOT fail), .64 measures 7.63:1. This is legibility of the one
     piece of guidance in the form, not a fix for a violation. */
  .quote__form .fld input::placeholder,
  .quote__form .fld textarea::placeholder{opacity:.64}
  .quote__form .fld input:hover,
  .quote__form .fld textarea:hover{border-color:rgba(255,255,255,.30);
    background:linear-gradient(180deg,rgba(255,255,255,.11),rgba(255,255,255,.06))}
  .quote__form .fld input:focus,
  .quote__form .fld textarea:focus{border-color:var(--accent-quiet);
    background:linear-gradient(180deg,rgba(255,0,128,.19),rgba(255,0,128,.08));
    box-shadow:0 0 0 1px var(--accent-quiet),0 14px 30px -20px var(--glow)}

  /* First/last is the one pair short enough to sit side by side in 312px of
     inner width; email, phone and the vehicle line all carry values that
     need the full measure. Colour keeps a short slot because the answer is
     short — the field says so before the label does. */
  .quote__form .qrow{gap:12px}
  .quote__form .qrow:nth-of-type(1){grid-template-columns:1fr 1fr}
  .quote__form .fld--sm{width:58%}

  /* ── services ────────────────────────────────────────────────────────
     The question fits on one line at 12px and was wrapping at 15.5px. The
     chips stretch to fill their row so the block has a right edge —
     ragged-right pills on a dark ground is what makes this read as debris
     in the client's 6.jpeg.
     The :checked and :hover rules are restated because the base rule below
     is (0,2,1), the same as site.css's `.chip input:checked + span`, and
     this sheet loads last — without them the base background would win
     over the checked one and the chips would stop showing their state. */
  .quote__form .fset{margin-top:6px}
  .quote__form .fset legend{font-size:12px;letter-spacing:.13em;margin-bottom:12px}
  .quote__form .chips{gap:8px}
  .quote__form .chip{flex:1 1 auto}
  .quote__form .chip span{width:100%;min-height:44px;padding:0 12px;gap:9px;
    font-size:14.5px;border-color:rgba(255,255,255,.28);
    background:rgba(255,255,255,.035)}
  .quote__form .chip:hover span{border-color:var(--line-strong)}
  .quote__form .chip input:checked + span{color:#fff;border-color:transparent;
    background:var(--brand-sweep-fill)}

  /* ── submit ──────────────────────────────────────────────────────────
     Full width, and the 48-hour promise stops being a second paragraph.
     .qnote does not go as small as the field labels: js/site.js:152 reuses
     it as the form's status channel ("Sending…", the success line, and the
     .is-bad failure message), so it has to stay comfortably readable.
     Measured on the panel face: 19.35:1 at rest, and 6.83:1 with .is-bad
     applied and an error string in place (#ff5fa2 on rgb(25,6,29)). */
  .qfoot{flex-direction:column;align-items:stretch;gap:12px;margin-top:20px}
  .qfoot .btn{width:100%}
  .qnote{font-size:12.5px;letter-spacing:.13em;text-align:center}

  /* ── the facts strip ─────────────────────────────────────────────────
     Same three cells, same words, 366px → 281px: the label drops to the
     11.5px the rest of this section now uses and the leading comes in. */
  .quote__facts{gap:20px;margin-top:32px;padding-top:20px}
  .quote__facts dt{font-size:11.5px;letter-spacing:.14em;margin-bottom:7px}
  .quote__facts dd{font-size:16px;line-height:1.5}


  /* ══ brand light for the two sections that are still flat black ═══════
     Only these two. The hero has its video and .hero__bloom; the founder
     and the footer got their plates earlier today; .tesla has .tesla__glow;
     the service cards carry their own imagery on an opaque --surface face.
     What is left is 2303px of .svc above the cards and 4809px of .proof —
     5.7 phone screens of nothing — and that is the run the client scrolled
     through on his way to the form. Both floors are tuned to land on the
     same value the founder and footer plates already hold, so the page
     reads as one material rather than three treatments. Sampled in the
     left and right gutters at 390px: .svc rgb(19,0,25), .proof rgb(19,0,24),
     .stack__pin rgb(19,0,25), .foot rgb(20,0,26). */

  /* services. .stack__over already lays an opaque black under the top of
     this section so it can cover the pinned founder; that background is
     untouched — this is a separate layer painted over it. Every word in
     .svc__head is white (21:1) and every card body sits on an opaque
     background:var(--surface), so nothing here rides on the plate. */
  .svc::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
    background:linear-gradient(180deg,
      rgba(255,0,128,.155) 0,
      rgba(186,0,178,.150) 120px,
      rgba(146,0,180,.144) 320px,
      rgba(138,0,176,.140) 46%,
      rgba(138,0,176,.132) 100%)}

  /* the review / youtube / instagram feeds. One ramp over 4809px is a wash
     nobody reads as light, so the floor is flat and each .feed gets its own
     leading edge — the same idea as .feed__line, which already draws a lit
     hairline down the rail of each one.
     The constraint here is .feed__big ("5.0"): it is --brand-type through
     background-clip:text, so it carries the same #ff0080 floor as .em. It
     sits ~150px below each feed's top, inside this ramp, which is why the
     leading edge is held to .16 — measured rgb(37,0,40) under it, and "5.0"
     lands at 5.92:1 against 6.57:1 on the old flat black. */
  .proof::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
    background:linear-gradient(180deg,
      rgba(168,0,182,.150) 0,
      rgba(138,0,176,.140) 260px,
      rgba(138,0,176,.136) calc(100% - 180px),
      rgba(138,0,176,.060) 100%)}
  .proof > *{position:relative;z-index:1}
  .feed{position:relative}
  /* left/right:-24px is exactly .proof's mobile padding, so this reaches
     the viewport edges and stops there. It never produces a rect wider than
     innerWidth — html{overflow-x:clip} would hide that, not fix it. */
  .feed::before{content:"";position:absolute;z-index:0;pointer-events:none;
    left:-24px;right:-24px;top:-30px;height:360px;
    background:linear-gradient(180deg,
      rgba(255,0,128,.16) 0,
      rgba(186,0,178,.10) 120px,
      rgba(138,0,176,.04) 240px,
      rgba(138,0,176,0)   100%)}
  .feed > *{position:relative;z-index:1}
}

/* ── iOS 安全区（2026-09-15）─────────────────────────────────────────
   症状:手机上 "TERMS AND CONDITIONS" 那行下面有一条纯黑带。
   查明它**不在文档里** —— 页面到 terms 之后只剩 24px,而且被页脚的 plate
   铺满了。那条黑带是 iOS 的安全区(home indicator 那条),
   viewport 没有 viewport-fit=cover 时,Safari 把页面从安全区缩进来,
   露出的是画布底色。

   两步:①viewport 加 viewport-fit=cover(已在 HTML 里改),让页面铺满整块屏;
        ②页脚底部补上安全区高度,否则最后一行会被 home indicator 压住。
   .foot::before 是 inset:0,会跟着一起铺下去,所以紫色一直延伸到屏幕最底。 */
@media (max-width:760px){
  .foot{padding-bottom:calc(24px + env(safe-area-inset-bottom,0px))}
}
