@font-face{font-family:Manrope;src:url('../fonts/manrope-var.woff2') format('woff2-variations');font-weight:600 800;font-style:normal;font-display:swap}@font-face{font-family:'Manrope Fallback';src:local('Arial'),local('Helvetica'),local('Liberation Sans');size-adjust:104.1%;ascent-override:102.4%;descent-override:28.8%;line-gap-override:0%}@font-face{font-family:Inter;src:url('../fonts/inter-var.woff2') format('woff2-variations');font-weight:400 700;font-style:normal;font-display:swap}@font-face{font-family:'Inter Fallback';src:local('Arial'),local('Helvetica'),local('Liberation Sans');size-adjust:104.6%;ascent-override:92.6%;descent-override:23.1%;line-gap-override:0%}@font-face{font-family:'IBM Plex Mono';src:url('../fonts/plexmono-600.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap}@font-face{font-family:'IBM Plex Mono Fallback';src:local('Courier New'),local('Liberation Mono');size-adjust:99.5%;ascent-override:103.0%;descent-override:27.6%;line-gap-override:0%}:root{--o-50:#FBF4E9;--o-100:#F5E7C8;--o-200:#EAD3A0;--o-300:#E9B457;--o-400:#EE9B00;--o-500:#CA6702;--o-600:#BB3E03;--o-700:#AE2012;--o-800:#9B2226;--c-100:#DDE3E5;--c-300:#A6B2B7;--c-500:#47565E;--c-600:#3A474E;--c-700:#2B363C;--ink:#001219;--ink-2:#05262F;--paper:#F8F4EA;--sheet:#FFFDF8;--desk:#DCCFB6;--band:#EFE6D4;--band-sand:#F1E8D2;--band-clay:#F0E8DB;--band-linen:#ECE6D8;--band-taupe:#EAE3D6;--line:#ECE4D4;--line-2:#DCD2BE;--line-warm:#F0DFC2;--text-strong:#14202A;--text-body:#33424A;--text-mut:#5E6B70;--text-soft:#9AA4A7;--shadow-rgb:4,20,26;--shadow-brand-rgb:150,46,12;--tint-1:color-mix(in srgb, var(--o-600) 5%,  transparent);--tint-2:color-mix(in srgb, var(--o-600) 12%, transparent);--tint-3:color-mix(in srgb, var(--o-600) 28%, transparent);--r-sm:10px;--r-md:14px;--r-lg:18px;--r-btn:14px;--ease:cubic-bezier(.22, 1, .36, 1);--brand:var(--o-700);--brand-soft:var(--o-300);--route-sky:var(--c-700);--route-teal:var(--c-600);--o-btn:#E85D04;--o-btn-hover:#C94B02;--icon-mono:var(--o-600);--wedge-top:44%;--wedge-bot:31%}html{-webkit-text-size-adjust:100%;text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:6rem}body{font-family:Inter,'Inter Fallback',system-ui,-apple-system,'Segoe UI',sans-serif;background-color:var(--desk);font-feature-settings:"cv05" 1,"cv11" 1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;overflow-x:hidden}body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;background:linear-gradient(135deg,color-mix(in srgb,var(--c-500) 9%,transparent) 0,transparent 40%,transparent 60%,color-mix(in srgb,var(--o-600) 9%,transparent) 100%);background-size:100% 100%}@media (min-width:1360px){body::before{background-image:linear-gradient(135deg,color-mix(in srgb,var(--c-500) 7%,transparent) 0,transparent 40%,transparent 60%,color-mix(in srgb,var(--o-600) 8%,transparent) 100%),linear-gradient(180deg,rgba(90,74,50,.13) 0,transparent 26%,transparent 74%,rgba(90,74,50,.1) 100%),linear-gradient(rgba(220,207,182,.72),rgba(220,207,182,.72)),url(../img/desk.webp);background-size:100% 100%,100% 100%,100% 100%,cover;background-position:center,center,center,center;background-repeat:no-repeat,no-repeat,no-repeat,no-repeat}.page-sheet{box-shadow:0 0 0 1px rgba(var(--shadow-rgb),.09),0 2px 6px rgba(var(--shadow-rgb),.1),0 40px 90px -55px rgba(var(--shadow-rgb),.5)}}::selection{background:color-mix(in srgb,var(--o-600) 18%,transparent)}:focus-visible{outline:2px solid var(--c-600);outline-offset:3px;border-radius:6px}.panel-dark :focus-visible,footer :focus-visible{outline-color:var(--c-300)}#mobile-menu>div,header nav{display:none}.font-display{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;letter-spacing:-.015em}.font-mono{font-family:'IBM Plex Mono','IBM Plex Mono Fallback',monospace;font-variant-numeric:tabular-nums}.tel-num{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;font-variant-numeric:tabular-nums;letter-spacing:-.01em}.font-mono .text-sm{font-family:inherit;font-size:.68em;font-weight:600;letter-spacing:0}dd,li,p,td{overflow-wrap:break-word}.brand-ribbon{height:3px;background-color:var(--o-600)}.section{margin-top:3.25rem}@media (min-width:768px){.section{margin-top:4.5rem}}.sec-title{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;font-weight:800;letter-spacing:-.015em;line-height:1.16;font-size:clamp(1.6875rem, 1.15rem + 2vw, 2.375rem);text-wrap:balance}.sec-lead{font-size:.9375rem;line-height:1.75;color:var(--text-mut);text-wrap:pretty;max-width:68ch}.eyebrow{display:inline-flex;align-items:center;gap:.625rem;font-size:.75rem;font-weight:700;line-height:1;text-transform:uppercase;letter-spacing:.09em;color:var(--text-mut)}.eyebrow::before{content:"";width:20px;height:2px;border-radius:999px;background-color:var(--o-600)}.eyebrow-cool::before{background-color:var(--c-700)}.eyebrow-light{color:var(--text-soft)}.eyebrow-light::before{background-color:var(--o-500)}.eyebrow-sky{color:var(--text-soft)}.eyebrow-sky::before{background-color:var(--c-500)}.eyebrow-teal{color:var(--text-soft)}.eyebrow-teal::before{background-color:var(--c-500)}.page-sheet .card.p-8{padding-top:2rem;padding-bottom:2rem}.page-sheet .panel-dark.relative{padding-top:2.25rem;padding-bottom:2.25rem}.page-sheet section>.text-center.max-w-2xl{margin-bottom:2rem}.marquee span{font-size:1.0625rem;letter-spacing:.01em}.marquee-track>div{gap:2.75rem;padding-right:2.75rem}section:has(> .marquee)>p:first-child{font-size:10px;margin-bottom:1.125rem}.page-sheet{max-width:1300px;margin:0 auto;padding:0;display:flow-root;background:var(--paper);box-shadow:0 0 0 1px rgba(var(--shadow-rgb),.07),0 50px 90px -50px rgba(var(--shadow-rgb),.4)}.page-sheet>:not(.panel){padding-left:2rem;padding-right:2rem}.page-sheet>.panel{margin-left:2rem;margin-right:2rem}.page-sheet>.hd{padding-left:0;padding-right:0}.page-sheet>:first-child:not(.hd){margin-top:2rem}.page-sheet>:last-child{padding-bottom:2rem}.ok-ink{color:var(--c-600)}.ok-bg{background-color:#f5ecdc}.ok-dot{background-color:#ebdcc2}.ok-line{border-color:#e3d3b6}.page-sheet>.band{background:var(--band);padding-top:1.75rem;padding-bottom:1.75rem}.page-sheet>.band-sand{background:var(--band-sand)}.page-sheet>.band-clay{background:var(--band-clay)}.page-sheet>.band-linen{background:var(--band-linen)}.page-sheet>.band-taupe{background:var(--band-taupe)}body>footer{margin-top:0!important}.hd{position:relative;overflow:hidden;display:flex;align-items:center;width:100%;min-height:560px;background:#1e1710;box-shadow:0 26px 52px -40px rgba(var(--shadow-rgb),.34),0 -18px 40px -34px rgba(var(--shadow-rgb),.14)}.hd::after,.hd::before{content:"";position:absolute;left:0;right:0;height:56px;z-index:4;pointer-events:none}.hd::before{top:0;background:linear-gradient(to bottom,rgba(var(--shadow-rgb),.13),transparent)}.hd::after{bottom:0;background:linear-gradient(to top,rgba(var(--shadow-rgb),.13),transparent)}.hd-media{position:absolute;inset:0;z-index:0}.hd-media picture{display:block;width:100%;height:100%}.hero-shot{width:100%;height:100%;object-fit:cover;object-position:right}.hd-wedge{position:absolute;inset:0;z-index:1;background:linear-gradient(100deg,rgba(30,23,16,.95) 0,rgba(33,25,17,.91) 60%,rgba(38,29,19,.86) 100%);clip-path:polygon(0 0,var(--wedge-top) 0,var(--wedge-bot) 100%,0 100%)}.hd-seam{position:absolute;inset:0;z-index:2;pointer-events:none;background:linear-gradient(180deg,var(--o-400) 0,var(--o-500) 45%,#6e3a12 100%);clip-path:polygon(var(--wedge-top) 0,calc(var(--wedge-top) + .5%) 0,calc(var(--wedge-bot) + .5%) 100%,var(--wedge-bot) 100%)}.hd-text{position:relative;z-index:3;padding:2.75rem 1.5rem}@media (min-width:768px){.hd-text{padding:3rem 3.25rem}}@media (min-width:1024px){.hd{min-height:620px}.hd-text{padding:3rem 1rem 3rem 3rem;max-width:27rem}}@media (min-width:1024px) and (max-width:1279px){:root{--wedge-top:51%;--wedge-bot:39%}.hd-text{padding-left:3rem;padding-right:1rem;max-width:25.5rem}.hd-title{font-size:2.5rem}}.hd-eyebrow{margin:0 0 1.1rem;font-size:.75rem;font-weight:700;letter-spacing:.05em;color:#b9a991}.hd-title{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;font-weight:800;color:#fbf7f0;font-size:2.125rem;line-height:1.07;letter-spacing:-.032em;margin:0}@media (min-width:768px){.hd-title{font-size:2.75rem}}.hd-title span{color:#a08a6e}.hd-lead{margin:1.25rem 0 0;font-size:.9375rem;line-height:1.6;color:#cbbfaf;max-width:19rem}.hd-cta{margin-top:1.75rem;display:flex;flex-wrap:wrap;gap:.7rem}.hd .btn-outline{background:0 0;border-color:rgba(255,255,255,.4);color:#fff}@media (max-width:1023px){.page-sheet>:not(.panel){padding-left:1.5rem;padding-right:1.5rem}.page-sheet>.band{padding-top:1.5rem;padding-bottom:1.5rem}.page-sheet>.panel{margin-left:1.5rem;margin-right:1.5rem}.hd{display:block;min-height:0}.hd-media{position:relative;height:260px}.hd-wedge{top:0;height:260px;bottom:auto;clip-path:none;background:linear-gradient(172deg,rgba(30,23,16,0) 26%,rgba(30,23,16,.3) 46%,rgba(30,23,16,.68) 66%,rgba(30,23,16,.9) 84%,#1e1710 100%)}.hd-seam{display:none}.hd-text{padding:1.5rem 1.5rem 2rem;max-width:none}}.card{background:#fff;border:1px solid var(--line);border-radius:var(--r-md);box-shadow:0 1px 2px rgba(var(--shadow-rgb),.04)}.card-hover{transition:transform .25s var(--ease),box-shadow .25s ease,border-color .25s ease}.card-hover:hover{transform:translateY(-3px);border-color:var(--line-2);box-shadow:0 1px 2px rgba(var(--shadow-rgb),.05),0 16px 32px -20px rgba(var(--shadow-rgb),.28)}.panel{border-radius:var(--r-lg)}.panel-dark{background:var(--ink);border-radius:var(--r-lg);box-shadow:0 20px 44px -34px rgba(var(--shadow-rgb),.7)}.panel-light{background-color:var(--sheet);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:0 1px 2px rgba(var(--shadow-rgb),.04),0 18px 40px -32px rgba(var(--shadow-rgb),.22)}.blueprint{background-image:radial-gradient(58% 76% at 90% 8%,color-mix(in srgb,var(--c-500) 6%,transparent),transparent 62%),radial-gradient(56% 70% at 5% 95%,color-mix(in srgb,var(--o-600) 8%,transparent),transparent 60%)}.panel-warm{background-color:var(--o-50);border:1px solid var(--line-warm);border-radius:var(--r-lg);box-shadow:0 1px 2px rgba(var(--shadow-brand-rgb),.04),0 18px 40px -32px rgba(var(--shadow-brand-rgb),.2)}.glow-warm{background-image:radial-gradient(circle at top right,var(--o-200),transparent 62%)}.hero-panel--about,.hero-panel--fleet,.hero-panel--services{position:relative;overflow:hidden}.hero-panel--services{background-color:var(--o-50);border-color:var(--line-warm);background-image:radial-gradient(60% 80% at 92% 6%,color-mix(in srgb,var(--o-500) 18%,transparent),transparent 62%),radial-gradient(56% 70% at 5% 95%,color-mix(in srgb,var(--o-600) 11%,transparent),transparent 60%)}.hero-panel--about{background-color:var(--ink);border-color:var(--ink-2);box-shadow:0 1px 2px rgba(var(--shadow-rgb),.1),0 20px 44px -34px rgba(var(--shadow-rgb),.7);background-image:radial-gradient(60% 80% at 92% 6%,color-mix(in srgb,var(--c-500) 22%,transparent),transparent 62%),radial-gradient(56% 70% at 5% 95%,color-mix(in srgb,var(--o-500) 16%,transparent),transparent 60%)}.hero-panel--about .hero-copy .text-slate-900,.hero-panel--about .hero-copy h1.sec-title{color:#fff}.hero-panel--about .hero-copy .sec-lead{color:color-mix(in srgb,#fff 74%,var(--c-100))}.hero-panel--about .hero-copy .eyebrow{color:var(--o-300)}.hero-panel--about .hero-copy .eyebrow::before{background-color:var(--o-400)}.hero-panel--about .hero-copy .btn-outline{background-color:transparent;border-color:rgba(255,255,255,.28);color:#fff}.hero-panel--about .hero-copy .btn-outline:hover{border-color:#fff;box-shadow:0 1px 2px rgba(0,0,0,.3)}.hero-watermark{position:absolute;right:-1.75rem;bottom:-2.25rem;width:17.5rem;height:17.5rem;color:var(--ink);opacity:.1;pointer-events:none;user-select:none}.hero-watermark svg{width:100%;height:100%;display:block}.hero-panel--services .hero-watermark{width:24rem;height:24rem;right:-2.5rem;bottom:-3rem}.hero-panel--about .hero-watermark{color:#fff;opacity:.12}@media (max-width:640px){.marquee span{font-size:.9375rem}.marquee-track>div{gap:2rem;padding-right:2rem}.page-sheet>:not(.panel){padding-left:1.25rem;padding-right:1.25rem}.page-sheet>.panel{margin-left:1rem;margin-right:1rem}.hero-watermark{width:10.5rem;height:10.5rem;right:-1rem;bottom:-1.25rem;opacity:.09}.hero-panel--services .hero-watermark{width:13rem;height:13rem;right:-1.25rem;bottom:-1.5rem}}@media (min-width:1024px){.hero-panel--about,.hero-panel--fleet,.hero-panel--services{min-height:34.5rem;display:flex;flex-direction:column;justify-content:center}}.hero-panel--about h1.sec-title,.hero-panel--fleet h1.sec-title,.hero-panel--services h1.sec-title{font-size:2.25rem;line-height:1.05;letter-spacing:-.02em}@media (min-width:768px){.hero-panel--about h1.sec-title,.hero-panel--fleet h1.sec-title,.hero-panel--services h1.sec-title{font-size:3.25rem}}.hero-chip{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem .875rem;border-radius:9999px;background:#fff;border:1px solid var(--line);font-size:.8125rem;font-weight:700;color:var(--text-body);transition:border-color .2s,background-color .2s,transform .15s}.hero-chip::before{content:"";width:7px;height:7px;border-radius:999px;background:var(--a);flex:none}.hero-chip:hover{border-color:var(--a);background:color-mix(in srgb,var(--a) 12%,#fff);transform:translateY(-1px)}.about-badge{background:var(--sheet)}.about-badge .badge-year{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;letter-spacing:-.02em}.btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--r-btn);font-weight:600;font-size:.875rem;letter-spacing:-.005em;padding:.75rem 1.375rem;white-space:nowrap;border:1px solid transparent;transition:background-color .18s ease,color .18s ease,border-color .18s ease,box-shadow .18s ease,transform .18s var(--ease),filter .18s ease}.btn-primary{background:linear-gradient(155deg,var(--o-400) 0,var(--o-btn) 55%,var(--o-600) 100%);background-origin:border-box;color:#fff;border-color:transparent;box-shadow:0 1px 1px rgba(var(--shadow-rgb),.1),0 16px 28px -16px rgba(var(--shadow-brand-rgb),.6)}.btn-primary:focus-visible,.btn-primary:hover{filter:brightness(.92);box-shadow:0 2px 3px rgba(var(--shadow-rgb),.14),0 20px 34px -16px rgba(var(--shadow-brand-rgb),.68);transform:translateY(-1px)}.btn-ink{background:linear-gradient(155deg,var(--ink-2) 0,var(--ink) 70%);background-origin:border-box;color:#fff;border-color:transparent;box-shadow:0 1px 1px rgba(var(--shadow-rgb),.14),0 16px 28px -18px rgba(var(--shadow-rgb),.55)}.btn-ink:focus-visible,.btn-ink:hover{filter:brightness(1.2);box-shadow:0 2px 3px rgba(var(--shadow-rgb),.18),0 20px 32px -16px rgba(var(--shadow-rgb),.6);transform:translateY(-1px)}.btn-outline{background-color:#fff;border-color:var(--line-2);color:var(--text-strong);box-shadow:0 1px 2px rgba(var(--shadow-rgb),.05);position:relative;overflow:hidden;isolation:isolate}.btn-outline:focus-visible,.btn-outline:hover{border-color:var(--text-strong);box-shadow:0 6px 16px -10px rgba(var(--shadow-rgb),.3);transform:translateY(-1px)}.btn-sm{padding:.5rem 1rem;border-radius:9999px}.btn-compact{padding:.5625rem .9375rem;font-size:.8125rem}.btn-go svg{flex-shrink:0;transition:transform .18s var(--ease)}.btn-go:focus-visible svg,.btn-go:hover svg{transform:translateX(3px)}@media (prefers-reduced-motion:reduce){body::before{animation:none;transform:none}.btn-go svg{transition:none}}.btn:active{transform:translateY(1px);transition-duration:.05s}.btn-ink,.btn-primary{position:relative;overflow:hidden;isolation:isolate}.btn-ink::after,.btn-primary::after{content:'';position:absolute;top:0;left:-35%;width:35%;height:100%;background:linear-gradient(115deg,transparent,rgba(255,255,255,.45) 45%,transparent 80%);transform:skewX(-18deg);pointer-events:none;opacity:0}@media (hover:hover) and (pointer:fine){.btn-ink:hover::after,.btn-primary:hover::after{opacity:1;animation:btn-shine .85s var(--ease)}}.btn-ink:focus-visible::after,.btn-primary:focus-visible::after{opacity:1;animation:btn-shine .85s var(--ease)}@keyframes btn-shine{from{left:-35%}to{left:135%}}.btn-outline::after{content:'';position:absolute;inset:0;background:var(--tint-2);transform:scaleX(0);transform-origin:left;transition:transform .32s var(--ease);z-index:-1}.btn-outline:focus-visible::after,.btn-outline:hover::after{transform:scaleX(1)}.acc-brand{--a:var(--o-700);--a-2:var(--o-500);--a-bg:var(--tint-2)}.acc-sky{--a:var(--c-700);--a-2:var(--c-500);--a-bg:color-mix(in srgb, var(--c-700) 9%, transparent)}.acc-teal{--a:var(--c-600);--a-2:var(--c-300);--a-bg:color-mix(in srgb, var(--c-600) 9%, transparent)}.acc-ink{--a:#45525A;--a-2:#5E6B70;--a-bg:color-mix(in srgb, #45525A 8%, transparent)}.svc-icon{background:#fff;border:1px solid var(--line);color:var(--a);transition:transform .3s var(--ease)}.group:hover .svc-icon{transform:scale(1.08)}.svc-link{color:var(--a)}.svc-link svg{transition:transform .25s var(--ease)}.group:hover .svc-link svg{transform:translateX(4px)}.svc-modal{position:fixed;inset:0;z-index:90;display:none}.svc-modal.is-visible{display:block}.svc-modal-backdrop{position:absolute;inset:0;background:rgba(15,23,32,.6);opacity:0;transition:opacity .28s var(--ease)}.svc-modal.is-open .svc-modal-backdrop{opacity:1}.svc-modal-dialog{--m-pad-t:2rem;--m-pad-x:1.75rem;position:relative;z-index:1;width:calc(100% - 2.5rem);max-width:640px;max-height:88vh;overflow-y:auto;margin:6vh auto 0;background:#fff;border-radius:1.75rem;box-shadow:0 30px 70px -28px rgba(var(--shadow-rgb),.55);padding:var(--m-pad-t) var(--m-pad-x) 1.75rem;opacity:0;transform:translateY(22px) scale(.96);transition:opacity .3s var(--ease),transform .32s var(--ease)}.svc-modal.is-open .svc-modal-dialog{opacity:1;transform:translateY(0) scale(1)}.svc-modal-close{position:absolute;top:.5rem;right:.5rem;width:2.25rem;height:2.25rem;z-index:2;display:flex;align-items:center;justify-content:center;border-radius:999px;background:#fff;border:1px solid var(--line);color:var(--text-body);box-shadow:0 2px 10px rgba(var(--shadow-rgb),.16);cursor:pointer;transition:background-color .2s,color .2s}.svc-modal-close:hover{background:var(--tint-3,#e2e8f0);color:var(--text-strong)}.svc-modal-body .ph{--edge-gap:.5rem;border-radius:1.25rem;min-height:140px;margin:calc(var(--edge-gap) - var(--m-pad-t)) calc(var(--edge-gap) - var(--m-pad-x)) 0 0;width:calc(100% + var(--m-pad-x) - var(--edge-gap));-webkit-mask-image:radial-gradient(circle at calc(100% - 1.125rem) 1.125rem,transparent 1.625rem,#000 calc(1.625rem + 1px));mask-image:radial-gradient(circle at calc(100% - 1.125rem) 1.125rem,transparent 1.625rem,#000 calc(1.625rem + 1px))}.svc-modal-body .sec-title{font-size:1.375rem}.svc-modal-body .sec-lead{font-size:.875rem;line-height:1.6}.svc-modal-body .card table{font-size:.8125rem}body.modal-open{overflow:hidden}@media (max-width:640px){.svc-modal-dialog{width:100%;max-height:100vh;height:100%;margin:0;border-radius:0;padding:1.5rem 1.25rem 2rem}.svc-modal-body .ph{margin:-1rem -1rem 0 0;width:calc(100% + 1rem);-webkit-mask-image:radial-gradient(circle at calc(100% - 1.375rem) 1.125rem,transparent 1.625rem,#000 calc(1.625rem + 1px));mask-image:radial-gradient(circle at calc(100% - 1.375rem) 1.125rem,transparent 1.625rem,#000 calc(1.625rem + 1px))}}@media (prefers-reduced-motion:reduce){.svc-modal-backdrop,.svc-modal-dialog{transition:none}.svc-modal.is-visible .svc-modal-backdrop{opacity:1}.svc-modal.is-visible .svc-modal-dialog{opacity:1;transform:none}}.counter{font-variant-numeric:tabular-nums}#stats .font-display{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;font-variant-numeric:tabular-nums;letter-spacing:-.02em}#stats p.text-xs{letter-spacing:.06em}.stat-rule{display:block;width:2rem;height:2px;border-radius:999px;margin:0 auto 1rem;background-color:var(--o-600);transform:scaleX(1);transition:transform .7s var(--ease) .1s}.reveal:not(.visible) .stat-rule{transform:scaleX(.2)}.route-steps{position:relative}.route-steps>*{position:relative;z-index:1}@media (min-width:1024px){.route-steps::before{content:"";position:absolute;z-index:0;left:8%;right:8%;top:3.5rem;height:2px;background-image:repeating-linear-gradient(90deg,var(--line-2) 0 9px,transparent 9px 19px)}}h2,h3,h4{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;letter-spacing:-.01em}details.acc{position:relative;transition:background-color .3s,border-color .3s;transition:transform .22s var(--ease),box-shadow .22s ease,border-color .22s ease;transform-origin:center}details.acc>summary{list-style:none;cursor:pointer;transition:background-color .25s}details.acc>summary::-webkit-details-marker{display:none}details.acc>summary:hover{background:var(--tint-1)}details.acc:not([open]):hover{transform:scale(1.014);border-color:var(--line-2);box-shadow:0 1px 2px rgba(var(--shadow-rgb),.05),0 18px 34px -22px rgba(var(--shadow-rgb),.3)}details.acc>summary>span:first-child{transition:transform .25s var(--ease)}details.acc>summary:hover>span:first-child{transform:translateX(3px)}details.acc>summary:focus-visible{outline:2px solid var(--o-600);outline-offset:-4px;border-radius:var(--r-md)}details.acc>summary:focus:not(:focus-visible){outline:0}details.acc-dark>summary:focus-visible{outline-color:var(--o-300)}details.acc::before{content:"";position:absolute;left:5px;top:1.125rem;bottom:1.125rem;width:3px;border-radius:999px;background-color:var(--o-600);opacity:0;transform:translateX(-3px);transition:opacity .3s ease,transform .3s var(--ease)}details.acc.is-open::before{opacity:1;transform:none}details.acc.is-open{border-color:var(--line-warm);background:#fffdfa}details.acc>summary .font-mono{transition:color .3s}details.acc.is-open>summary .font-mono{color:var(--o-700)}.acc-plus{position:relative;display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border-radius:9999px;flex:none;border:1px solid var(--line-2);color:var(--text-mut);font-size:0;transition:transform .42s var(--ease),background-color .25s,border-color .25s,color .25s}.acc-plus::after,.acc-plus::before{content:"";position:absolute;top:50%;left:50%;width:10px;height:2px;border-radius:999px;background:currentColor}.acc-plus::before{transform:translate(-50%,-50%)}.acc-plus::after{transform:translate(-50%,-50%) rotate(90deg)}details.acc>summary:hover .acc-plus{border-color:var(--tint-3);color:var(--o-700)}details.acc.is-open .acc-plus{transform:rotate(45deg);background:var(--tint-2);border-color:transparent;color:var(--o-700)}details.acc-dark .acc-plus{border-color:rgba(255,255,255,.18);color:var(--text-soft)}details.acc-dark.is-open .acc-plus{background:color-mix(in srgb,var(--o-600) 18%,transparent);border-color:transparent;color:var(--o-300)}.acc-body{overflow:hidden;transition:height .34s cubic-bezier(.4,0,.2,1)}.acc-body>*{position:relative;opacity:0;transform:translateY(-6px);transition:opacity .3s ease .08s,transform .4s var(--ease) .08s}details.acc.is-open .acc-body>*{opacity:1;transform:none}.acc-body>::before{content:"";position:absolute;left:1rem;right:1rem;top:0;height:2px;border-radius:999px;background:var(--line-warm);transform:scaleX(0);transform-origin:left center;transition:transform .34s var(--ease) .06s}details.acc.is-open .acc-body>::before{transform:scaleX(1)}.acc-body p,.acc-body ul{max-width:64ch}#faq details.acc>summary,#tral details.acc>summary,#vacancies details.acc>summary{padding:1.25rem 1.5rem}#faq details.acc>summary>span:not(.acc-plus),#tral details.acc>summary>span:first-child>span:last-child,#vacancies details.acc>summary>span:not(.acc-plus){font-size:.9375rem}#faq details.acc>summary>.acc-plus,#tral details.acc>summary>.acc-plus,#vacancies details.acc>summary>.acc-plus{font-size:0}#faq .acc-body>div,#tral .acc-body>div,#vacancies .acc-body>div{padding:0 1.5rem 1.375rem}#faq .acc-body>::before,#tral .acc-body>::before,#vacancies .acc-body>::before{left:1.5rem;right:1.5rem}#faq .acc-body>div{max-width:68ch}#tral div:has(> details.acc){display:block;max-width:48rem;margin-inline:auto}#tral div:has(> details.acc)>details.acc{margin-bottom:.75rem}#tral div:has(> details.acc)>details.acc:last-child{margin-bottom:0}#tral .mt-14>h3{max-width:48rem;margin-inline:auto}#shpunt .items-center{align-items:flex-start}.cargo-grid{background-image:radial-gradient(60% 80% at 88% 10%,color-mix(in srgb,var(--c-500) 18%,transparent),transparent 62%),radial-gradient(58% 72% at 6% 94%,color-mix(in srgb,var(--o-600) 14%,transparent),transparent 60%)}.glow-cool{background-image:radial-gradient(circle at top right,var(--c-500),transparent 58%),radial-gradient(circle at bottom left,var(--o-500),transparent 52%)}.glow-cool-b{background-image:radial-gradient(circle at bottom right,var(--c-500),transparent 58%),radial-gradient(circle at top left,var(--o-500),transparent 52%)}.step-num{font-family:Manrope,'Manrope Fallback',sans-serif;font-weight:800;line-height:.8;user-select:none;color:#f2ede6;letter-spacing:-.03em}@supports (background-clip:text) or (-webkit-background-clip:text){.step-num-warm{background-image:linear-gradient(160deg,var(--o-200),var(--o-100) 55%,rgba(253,246,240,0) 92%);-webkit-background-clip:text;background-clip:text;color:transparent}}.ph{position:relative;overflow:hidden;background-color:var(--tint-1);background-size:cover;background-position:center;background-repeat:no-repeat}.ph::after,.ph::before{content:"";position:absolute;inset:0;background-image:inherit;background-position:center;background-repeat:no-repeat}.ph::before{background-size:cover;filter:blur(13px) saturate(.85) brightness(.94);transform:scale(1.14)}.ph::after{background-size:contain}.ph:has(> picture,> img)::after{content:none}.ph>img,.ph>picture,.ph>picture>img{position:relative;z-index:1;display:block;width:100%;height:100%;object-fit:contain}.ph>picture{position:absolute;inset:0}.ph-fit{aspect-ratio:var(--ar,16 / 9);width:100%;min-height:0;content-visibility:auto}.cta-strip{display:flex;flex-wrap:wrap;align-items:center;gap:1.25rem 1.75rem;padding:1.5rem}.cta-strip-icon{width:3.25rem;height:3.25rem;flex-shrink:0;border-radius:1rem;display:flex;align-items:center;justify-content:center;background:var(--tint-1);border:1px solid var(--line);color:var(--o-700)}.cta-strip-icon svg{width:1.5rem;height:1.5rem}.cta-strip-text{flex:1 1 18rem;min-width:0}.cta-strip-text .sec-title{font-size:1.375rem}.cta-strip-text .sec-lead{font-size:.875rem}.cta-strip-actions{display:flex;flex-wrap:wrap;gap:.625rem;flex-shrink:0}@media (min-width:1024px){.cta-strip{padding:1.75rem 2rem;flex-wrap:nowrap}}.form-card{position:relative;overflow:hidden}.form-card::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--o-700),#f5762b 55%,transparent)}.form-head{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:.75rem 1rem}.form-badge{display:inline-flex;align-items:center;gap:.375rem;flex-shrink:0;padding:.3125rem .625rem;border-radius:9999px;background:var(--tint-1);border:1px solid var(--line);font-size:.6875rem;font-weight:700;color:var(--text-mut);white-space:nowrap}.form-badge svg{width:.875rem;height:.875rem;flex-shrink:0}.form-opt{font-weight:500;color:var(--text-mut)}.sec-divider{height:2rem;border:0;margin:0;background:0 0}.card-foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.75rem 1.25rem;padding:.875rem 1rem .875rem 1.25rem;background-color:var(--tint-1)}.card-foot p{flex:1 1 15rem;margin:0;font-size:.8125rem;line-height:1.6;color:var(--text-mut)}.w-3\.5{width:.875rem}.h-3\.5{height:.875rem}.text-red-700{color:#b91c1c}.bg-red-50{background-color:#fef2f2}.border-red-100{border-color:#fee2e2}.split-shot{aspect-ratio:var(--ar,16 / 10)}.split-shot::before{filter:blur(18px) saturate(.75) brightness(.92);transform:scale(1.18)}@media (min-width:1024px){.split-shot{aspect-ratio:auto;height:100%}}.ph-tral{background-image:url(../img/hero.webp)}.ph-evak1{background-image:url(../img/evakuator-1.webp)}.ph-manip{background-image:url(../img/manipulyator.webp)}.ph-evak2{background-image:url(../img/avtopark-2.webp)}.ph-manip2{background-image:url(../img/tral.webp)}.ph-emkosti{background-image:url(../img/rabota-tral-emkosti.webp)}.ph-drobilka{background-image:url(../img/rabota-tral-drobilka.webp)}.ph-katok{background-image:url(../img/rabota-tral-jcb-katok.webp)}.ph-2jcb{background-image:url(../img/rabota-tral-2jcb.webp)}.ph-gusenica{background-image:url(../img/rabota-tral-gusenica.webp)}.works-grid{display:grid;grid-template-columns:1fr;gap:1.25rem}@media (min-width:768px){.works-grid{grid-template-columns:1fr 1fr}.works-grid>.work-wide{grid-column:1/-1}}.work-card{overflow:hidden;margin:0}.work-shot{position:relative;overflow:hidden;aspect-ratio:var(--ar,16 / 9);background-color:var(--tint-1)}.work-shot::before{content:"";position:absolute;inset:0;background-image:var(--src);background-size:cover;background-position:center;filter:blur(13px) saturate(.85) brightness(.94);transform:scale(1.18)}.work-shot img{position:relative;display:block;width:100%;height:100%;object-fit:contain}form button,form input,form textarea{font-family:inherit}form input::placeholder,form textarea::placeholder{color:var(--text-soft)}form input:focus,form textarea:focus{box-shadow:0 0 0 3px rgba(var(--shadow-rgb),.06)}.cselect{position:relative}.cselect-btn{width:100%;display:flex;align-items:center;justify-content:space-between;gap:.5rem;background:#faf6ec;border:1px solid var(--line-2);border-radius:var(--r-sm);padding:.8125rem 1rem;font-size:.875rem;color:var(--text-body);text-align:left;font-family:inherit;transition:border-color .2s,background-color .2s,box-shadow .2s}.cselect-btn:hover{border-color:color-mix(in srgb,var(--line-2) 70%,var(--text-mut))}.cselect.open .cselect-btn{border-color:var(--text-strong);background:#fff;box-shadow:0 0 0 3px rgba(var(--shadow-rgb),.06)}.cselect-chev{width:1rem;height:1rem;color:var(--text-soft);flex:none;transition:transform .3s var(--ease)}.cselect.open .cselect-chev{transform:rotate(180deg)}.cselect-list{position:absolute;z-index:40;left:0;right:0;top:calc(100% + .5rem);background:#fff;border:1px solid var(--line);border-radius:14px;padding:.375rem;box-shadow:0 18px 40px -18px rgba(var(--shadow-rgb),.32),0 2px 6px rgba(var(--shadow-rgb),.06);max-height:17rem;overflow:auto;scrollbar-width:thin;scrollbar-color:var(--line-2) transparent;opacity:0;visibility:hidden;transform:translateY(-6px);transform-origin:top center;transition:opacity .18s ease,transform .22s var(--ease),visibility .22s}.cselect-list::-webkit-scrollbar{width:6px}.cselect-list::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:999px}.cselect.open .cselect-list{opacity:1;visibility:visible;transform:translateY(0)}.cselect-list li{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.625rem .75rem;border-radius:8px;font-size:.875rem;color:var(--text-body);cursor:pointer;transition:background-color .15s,color .15s}.cselect-list li.is-active{background:var(--tint-1);color:var(--text-strong)}.cselect-list li[aria-selected=true]{color:var(--text-strong);font-weight:700}.cselect-list li[aria-selected=true]::after{content:"✓";color:var(--o-700);font-weight:700;font-size:.8125rem}.card table thead th{font-size:.6875rem;text-transform:uppercase;letter-spacing:.07em;color:var(--text-mut)}.card table tbody tr{transition:background-color .18s}.card table tbody tr:hover{background-color:var(--tint-1)}.js .reveal{opacity:0;transform:translateY(14px);transition:opacity .5s ease,transform .5s var(--ease)}.js .reveal.visible{opacity:1;transform:translateY(0)}.js .reveal-up{opacity:0;transform:translateY(18px);transition:opacity .55s ease,transform .55s var(--ease)}.js .reveal-up.visible{opacity:1;transform:translateY(0)}@media (prefers-reduced-motion:reduce){.js .reveal,.js .reveal-up{opacity:1;transform:none;transition:none}}@keyframes heroIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}.hero-in{opacity:0;animation:heroIn .55s var(--ease) forwards}.nav-dropdown{visibility:hidden;opacity:0;transform:translateY(8px);transition:opacity .2s ease,transform .28s var(--ease),visibility .28s}.group:focus-within .nav-dropdown,.group:hover .nav-dropdown{visibility:visible;opacity:1;transform:translateY(0)}.nav-dropdown a{opacity:0;transform:translateY(5px);transition:opacity .25s ease,transform .3s var(--ease),background-color .2s}.group:focus-within .nav-dropdown a,.group:hover .nav-dropdown a{opacity:1;transform:none}.m-sub{max-height:0;overflow:hidden;transition:max-height .32s var(--ease)}.m-sub-icon{transition:transform .3s var(--ease)}footer ul a{background-image:linear-gradient(currentColor,currentColor);background-size:0 1px;background-position:0 100%;background-repeat:no-repeat;transition:background-size .3s var(--ease),color .2s ease}footer ul a:hover{background-size:100% 1px}footer h4{letter-spacing:-.005em}#to-top{transform:scale(.8);transition:opacity .3s ease,transform .35s var(--ease),background-color .2s}#to-top:not(.opacity-0){transform:scale(1)}#to-top:hover{transform:scale(1.08)}.marquee{overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}.marquee-track{display:flex;width:max-content;animation:34s linear infinite marquee}.marquee-track .font-display{letter-spacing:.045em}@keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}.freight-layer{will-change:transform}.tab-island{position:relative;width:100%;max-width:30rem;display:flex;align-items:stretch;gap:4px;background:var(--sheet);border:1px solid var(--line);border-radius:9999px;padding:6px;box-sizing:border-box}.tab-indicator{position:absolute;top:6px;left:6px;width:calc((100% - 12px - 12px)/ 4);height:calc(100% - 12px);border-radius:9999px;background:linear-gradient(155deg,var(--o-300) 0,var(--o-btn) 48%,var(--o-700) 100%);box-shadow:inset 0 1px 0 rgba(255,255,255,.28),inset 0 -8px 12px -8px rgba(var(--shadow-brand-rgb),.35),0 1px 1px rgba(var(--shadow-rgb),.1),0 10px 22px -9px rgba(var(--shadow-brand-rgb),.65);transition:transform .4s var(--ease);will-change:transform;z-index:1}.tab-island:has(:where(.tab-btn:nth-child(2)[aria-current=page])) .tab-indicator{transform:translateX(0)}.tab-island:has(:where(.tab-btn:nth-child(3)[aria-current=page])) .tab-indicator{transform:translateX(calc(100% + 4px))}.tab-island:has(:where(.tab-btn:nth-child(4)[aria-current=page])) .tab-indicator{transform:translateX(calc(200% + 8px))}.tab-island:has(:where(.tab-btn:nth-child(5)[aria-current=page])) .tab-indicator{transform:translateX(calc(300% + 12px))}html[data-tab-from="0"] .tab-island .tab-indicator{transform:translateX(0)}html[data-tab-from="1"] .tab-island .tab-indicator{transform:translateX(calc(100% + 4px))}html[data-tab-from="2"] .tab-island .tab-indicator{transform:translateX(calc(200% + 8px))}html[data-tab-from="3"] .tab-island .tab-indicator{transform:translateX(calc(300% + 12px))}html[data-tab-from] .tab-island .tab-indicator{transition:none}.tab-btn{position:relative;z-index:2;flex:1 1 0;min-width:0;display:flex;align-items:center;justify-content:center;text-align:center;border-radius:9999px;padding:.5rem .75rem;font-size:.875rem;font-weight:600;letter-spacing:-.005em;color:var(--text-mut);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;transition:color .25s,transform .12s}.tab-btn:hover{color:var(--text-body)}.tab-btn:active{transform:scale(.95)}.tab-btn:focus-visible{outline:2px solid var(--o-btn);outline-offset:2px}.tab-btn[aria-current=page]{color:#fff;cursor:default}.tab-btn[aria-current=page]:hover{color:#fff}.tabpanel[hidden]{display:none}.tabpanel>section:first-child{margin-top:2rem}.tabpanel>section#hero:first-child{margin-top:0}@keyframes panelIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}.tabpanel.is-entering{animation:panelIn .45s var(--ease) both}@media (prefers-reduced-motion:no-preference){.tab-indicator{animation:tabHere .3s var(--ease)}@keyframes tabHere{from{opacity:.6}to{opacity:1}}}#fleet-modes .panel-dark,#shpunt .panel-dark{background:var(--sheet);border:1px solid var(--line);box-shadow:0 1px 2px rgba(var(--shadow-rgb),.04),0 18px 40px -32px rgba(var(--shadow-rgb),.22);--c-300:#2B363C}#fleet-modes .cargo-grid,#shpunt .cargo-grid{background-image:radial-gradient(58% 76% at 90% 8%,color-mix(in srgb,var(--c-500) 7%,transparent),transparent 62%),radial-gradient(56% 70% at 5% 95%,color-mix(in srgb,var(--o-600) 9%,transparent),transparent 60%)}#fleet-modes .text-white,#shpunt .text-white{color:var(--text-strong)}#fleet-modes .text-slate-400,#shpunt .text-slate-400{color:var(--text-mut)}#shpunt .text-slate-300{color:var(--text-body)}#fleet-modes .eyebrow-sky,#shpunt .eyebrow-sky{color:var(--text-mut)}#fleet-modes .eyebrow-sky::before,#shpunt .eyebrow-sky::before{background-color:var(--c-700)}#shpunt details.acc-dark{background:#fff;border-color:var(--line)}#shpunt details.acc-dark.is-open{background:#fffdfa;border-color:var(--line-warm)}#shpunt details.acc-dark>summary:hover{background:var(--tint-1)}#shpunt details.acc-dark:not([open]):hover{transform:scale(1.014)}#shpunt details.acc-dark .acc-plus{border-color:var(--line-2);color:var(--text-mut)}#shpunt details.acc-dark>summary:hover .acc-plus{border-color:var(--tint-3);color:var(--o-700)}#shpunt details.acc-dark.is-open .acc-plus{background:var(--tint-2);border-color:transparent;color:var(--o-700)}#shpunt .acc-body li::marker{color:var(--line-2)}#about .panel-dark{background:var(--o-50);border:1px solid var(--line-warm);box-shadow:0 1px 2px rgba(var(--shadow-brand-rgb),.04),0 18px 40px -32px rgba(var(--shadow-brand-rgb),.2);--c-300:#2B363C}#about .glow-cool{background-image:radial-gradient(circle at top right,var(--o-200),transparent 62%)}#about .text-white{color:var(--text-strong)}#about .text-slate-300{color:var(--text-body)}#about .eyebrow-light{color:#93693a}#about .eyebrow-light::before{background-color:var(--o-600)}@media (prefers-reduced-motion:reduce){#to-top,.acc-body,.acc-body>*,.acc-body>::before,.acc-plus,.btn,.btn-ink::after,.btn-outline::after,.btn-primary::after,.card table tbody tr,.cselect-list,.cselect-list li,.nav-dropdown,.nav-dropdown a,.stat-rule,.svc-icon,.tab-btn,.tab-indicator,details.acc,details.acc::before,footer ul a{transition:none}.btn-ink:focus-visible::after,.btn-ink:hover::after,.btn-primary:focus-visible::after,.btn-primary:hover::after{animation:none}.btn-outline:focus-visible::after,.btn-outline:hover::after{transform:scaleX(1)}.reveal,.reveal-up{opacity:1!important;transform:none!important;transition:none}.hero-in{animation:none;opacity:1}.marquee-track{animation:none}.freight-layer{transform:none!important}#hero .animate-pulse{animation:none}#shpunt details.acc-dark:not([open]):hover,#to-top:hover,.acc-body>::before,.btn:hover,.card-hover:hover,.group:hover .svc-icon,.stat-rule,details.acc.is-open::before,details.acc:not([open]):hover{transform:none}.tab-indicator,.tabpanel.is-entering{animation:none}}#site-loader{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:#fff;opacity:0;visibility:hidden;pointer-events:none;transition:opacity .28s,visibility .28s}#site-loader.is-shown{opacity:1;visibility:visible;pointer-events:auto}#site-loader.is-hidden{opacity:0;visibility:hidden;pointer-events:none}#site-loader .loader{width:fit-content;height:fit-content;display:flex;align-items:center;justify-content:center}#site-loader .truckWrapper{width:200px;height:100px;display:flex;flex-direction:column;position:relative;align-items:center;justify-content:flex-end;overflow-x:hidden}#site-loader .truckBody{width:130px;height:fit-content;margin-bottom:6px;animation:1s linear infinite truckMotion}@keyframes truckMotion{0%,100%{transform:translateY(0)}50%{transform:translateY(3px)}}#site-loader .truckTires{width:108px;height:fit-content;display:flex;align-items:center;justify-content:space-between;padding:0 10px 0 15px;position:absolute;bottom:0}#site-loader .truckTires svg{width:24px}#site-loader .truckTires svg:first-child{margin-left:-10px}#site-loader .truckTires svg:last-child{margin-right:-11px}#site-loader .road{width:100%;height:1.5px;background-color:#282828;position:relative;bottom:0;align-self:flex-end;border-radius:3px}#site-loader .road::before{content:"";position:absolute;width:20px;height:100%;background-color:#282828;right:-50%;border-radius:3px;animation:1.4s linear infinite truckRoad;border-left:10px solid #fff}#site-loader .road::after{content:"";position:absolute;width:10px;height:100%;background-color:#282828;right:-65%;border-radius:3px;animation:1.4s linear infinite truckRoad;border-left:4px solid #fff}#site-loader .lampPost{position:absolute;bottom:0;right:-90%;height:90px;animation:1.4s linear infinite truckRoad}@keyframes truckRoad{0%{transform:translateX(0)}100%{transform:translateX(-350px)}}.route-map{display:block;width:100%;max-width:27rem;height:auto;margin-inline:auto;overflow:visible}.route-pin-ring{transform-origin:462px 60px;animation:2.2s ease-out infinite routePing}@keyframes routePing{0%{transform:scale(.6);opacity:.55}100%,70%{transform:scale(1.9);opacity:0}}.route-label{font-family:'IBM Plex Mono','IBM Plex Mono Fallback',monospace;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;fill:var(--o-600)}@media (prefers-reduced-motion:reduce){#site-loader .lampPost,#site-loader .road::after,#site-loader .road::before,#site-loader .truckBody{animation:none}.route-pin-ring{animation:none;opacity:0}}.svc-card{--a:var(--o-700)}.svc-card .svc-icon{width:3.25rem;height:3.25rem;border-radius:var(--r-md);background:linear-gradient(150deg,var(--o-50) 0,color-mix(in srgb,var(--o-300) 26%,#fff) 100%);border-color:color-mix(in srgb,var(--o-500) 22%,transparent);color:var(--icon-mono);box-shadow:inset 0 1px 0 #fff,0 1px 2px rgba(var(--shadow-brand-rgb),.06)}.svc-card .svc-icon svg{width:1.75rem;height:1.75rem}.svc-card:hover .svc-icon{background:linear-gradient(150deg,var(--o-100) 0,color-mix(in srgb,var(--o-400) 26%,#fff) 100%);border-color:color-mix(in srgb,var(--o-500) 38%,transparent)}.hero-facts{display:flex;flex-wrap:wrap;gap:0 2.25rem}.hero-facts>div{padding-right:2.25rem;border-right:1px solid var(--line-2)}.hero-facts>div:last-child{border-right:0;padding-right:0}.hero-facts dt{font-family:Manrope,'Manrope Fallback',Inter,sans-serif;font-weight:800;font-size:1.5rem;letter-spacing:-.02em;color:var(--text-strong)}.hero-facts dd{margin:.25rem 0 0;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--text-mut)}@media (max-width:640px){.hero-facts{gap:0 1.25rem}.hero-facts>div{padding-right:1.25rem}.ph-gusenica{background-image:url(../img/rabota-tral-gusenica-sm.webp)}.ph-emkosti{background-image:url(../img/rabota-tral-emkosti-sm.webp)}.ph-drobilka{background-image:url(../img/rabota-tral-drobilka-sm.webp)}.ph-katok{background-image:url(../img/rabota-tral-jcb-katok-sm.webp)}.ph-2jcb{background-image:url(../img/rabota-tral-2jcb-sm.webp)}.ph-tral{background-image:url(../img/hero-sm.webp)}.ph::before,.work-shot::before{filter:none;transform:none;background-image:none;background-color:var(--tint-1)}.section{margin-top:3.25rem}.cta-strip{padding:1.25rem;gap:1rem}.cta-strip-actions{width:100%}.cta-strip-actions .btn{flex:1 1 100%;justify-content:center}.cta-strip-text .sec-title{font-size:1.25rem}.form-badge{font-size:.625rem}.marquee-track{animation-duration:52s}}@media (max-width:1023px),(hover:none){.freight-layer{will-change:auto;transform:none!important}}.map-embed{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;flex:1 1 auto;width:100%;height:100%;min-height:12rem;padding:1.5rem;border:0;cursor:pointer;text-align:center;color:var(--text-mut);background-color:var(--tint-1);background-image:linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px);background-size:24px 24px;z-index:1;transition:background-color .2s,opacity .3s}.map-embed:hover{background-color:#efece7}.map-embed:focus-visible{outline:2px solid var(--o-700);outline-offset:-3px}.map-embed-pin{width:1.875rem;height:1.875rem;color:var(--o-700)}.map-embed-text{font-size:.9375rem;font-weight:700;line-height:1.45;color:var(--text-strong)}.map-embed-hint{font-size:.75rem;font-weight:600}.map-embed.is-loading{cursor:default}.map-embed.is-loading:hover{background-color:var(--tint-1)}.map-embed--dark.is-loading:hover{background-color:rgba(255,255,255,.04)}.map-embed.is-loading .map-embed-hint{animation:1.6s ease-in-out infinite map-hint}@keyframes map-hint{0%,100%{opacity:.45}50%{opacity:1}}.map-embed.is-done{opacity:0;pointer-events:none}@media (prefers-reduced-motion:reduce){.map-embed{transition:none}.map-embed.is-loading .map-embed-hint{animation:none;opacity:.7}}.map-embed--dark{color:#a9a29a;background-color:rgba(255,255,255,.04);background-image:linear-gradient(rgba(255,255,255,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.07) 1px,transparent 1px)}.map-embed--dark:hover{background-color:rgba(255,255,255,.08)}.map-embed--dark .map-embed-text{color:#fff}.hp-field{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.link-accent{color:var(--o-700);font-weight:600;text-decoration:underline;text-decoration-color:var(--o-300);text-underline-offset:2px;text-decoration-thickness:1px;transition:text-decoration-color .2s}.link-accent:hover{text-decoration-color:var(--o-600)}.js .ph-defer:not(.is-near){background-image:none!important}.js .ph-defer::after,.js .ph-defer::before{opacity:0;transition:opacity .5s}.js .ph-defer.is-loaded::after,.js .ph-defer.is-loaded::before{opacity:1}@media (prefers-reduced-motion:reduce){.js .ph-defer::after,.js .ph-defer::before{transition:none}}@media (pointer:coarse){.btn{min-height:44px}.btn-compact,.btn-sm{min-height:42px}#mobile-menu a,#mobile-menu button{min-height:44px;display:flex;align-items:center}.hero-chip{min-height:38px}.svc-modal-close{min-width:44px;min-height:44px}}

/* ===================================================================
   v34 — ЭМБЛЕМЫ МАРОК + СИСТЕМА ГЛУБИНЫ
   Блок дописан в конец файла, поэтому переопределяет всё, что выше.
   =================================================================== */

/* -------------------------------------------------------------------
   1. ПЕРЕСВЕДЁННАЯ ПАЛИТРА ФОНОВЫХ ПОЛОС

   Тон и насыщенность оставлены почти исходные (хрома 31 → 29,
   тон 43° → 41°) — песочный характер листа никуда не делся.
   Работает другой рычаг: светлота. Было 88.4 при 94.5 у листа —
   шаг настолько мелкий, что глаз списывал разницу на краску,
   а не на глубину, и полоса читалась «жёлтой заливкой». Стало 86.1:
   шаг от листа вырос с 0.095 до 0.148 по яркости, и та же самая
   теплота теперь читается как тень, упавшая на тот же лист.
   ------------------------------------------------------------------- */
:root {
    --band: #E9E0D0;
    --band-sand: #EAE1CD;
    --band-clay: #E9DFD0;
    --band-linen: #E7E1D4;
    --band-taupe: #E3DDD3;

    /* Граница карточки, лежащей на полосе. Обычная --line (#ECE4D4)
       на фоне полосы почти сливается — карточка выглядела наклеенной. */
    --line-band: #D4CBB9;

    /* Тень для тёплых поверхностей. Основная --shadow-rgb холодная
       (4,20,26) — на песочном фоне она даёт грязно-серый ореол.
       Здесь тёплый нейтральный коричневый той же светлоты. */
    --shadow-warm-rgb: 82, 64, 44;
}

/* -------------------------------------------------------------------
   2. ПОЛОСА = ВЫЕМКА В ЛИСТЕ (второй план)
   Волосяная линия сверху и снизу + мягкая внутренняя тень по краям.
   Тени внутренние, поэтому лэйаут не смещается ни на пиксель.
   ------------------------------------------------------------------- */
.page-sheet > .band {
    box-shadow:
        inset 0 1px 0 0 rgba(var(--shadow-warm-rgb), .15),
        inset 0 -1px 0 0 rgba(var(--shadow-warm-rgb), .12),
        inset 0 12px 16px -13px rgba(var(--shadow-warm-rgb), .38),
        inset 0 -12px 16px -13px rgba(var(--shadow-warm-rgb), .24);
}

/* Полоса потемнела, поэтому приглушённый текст, лежащий прямо на ней
   (.sec-lead, .eyebrow), надо подтянуть следом. На исходной полосе
   --text-mut давал 4.51:1 — впритык к порогу AA; на новой было бы
   4.24 и порог бы не прошёл. Переопределяем саму переменную на
   полосе: 4.64:1. Внутри белых карточек это тоже подействует,
   но там контраст только вырастет — с 5.59 до 6.03. */
.page-sheet > .band {
    --text-mut: #586569;
}

/* Чуть больше воздуха: карточки не должны упираться в края выемки,
   иначе полоса читается не как поверхность, а как рамка. */
@media (min-width: 1024px) {
    .page-sheet > .band {
        padding-top: 2.25rem;
        padding-bottom: 2.25rem;
    }
}

/* -------------------------------------------------------------------
   3. КАРТОЧКА НА ПОЛОСЕ = ПЕРВЫЙ ПЛАН
   Три слоя тени: контактная (1px), ближняя (форма) и дальняя (отрыв).
   Всё тёплое, потому что и подложка тёплая.
   ------------------------------------------------------------------- */
.page-sheet > .band .card {
    border-color: var(--line-band);
    box-shadow:
        0 1px 1px rgba(var(--shadow-warm-rgb), .06),
        0 3px 6px -2px rgba(var(--shadow-warm-rgb), .08),
        0 16px 28px -14px rgba(var(--shadow-warm-rgb), .18);
}

.page-sheet > .band .card-hover:hover {
    border-color: var(--line-2);
    box-shadow:
        0 1px 1px rgba(var(--shadow-warm-rgb), .07),
        0 6px 12px -4px rgba(var(--shadow-warm-rgb), .10),
        0 30px 50px -22px rgba(var(--shadow-warm-rgb), .26);
}

/* Аккордеоны в FAQ стоят стопкой по семь штук. Отдельно стоящей
   карточке отрыв идёт на пользу, а стопке — нет: семь одинаковых
   ореолов подряд создают грязь. Им тень короче и мягче. */
.page-sheet > .band details.acc.card {
    box-shadow:
        0 1px 1px rgba(var(--shadow-warm-rgb), .05),
        0 2px 4px -2px rgba(var(--shadow-warm-rgb), .07),
        0 9px 18px -11px rgba(var(--shadow-warm-rgb), .15);
}

/* -------------------------------------------------------------------
   4. КАРТОЧКА НА ЧИСТОМ ЛИСТЕ
   Лист сам по себе — первый план, поэтому тень здесь тише, чем
   на полосе: разница в силе тени и создаёт ощущение двух планов.
   ------------------------------------------------------------------- */
.card {
    box-shadow:
        0 1px 1px rgba(var(--shadow-rgb), .04),
        0 2px 4px -2px rgba(var(--shadow-rgb), .05);
}

/* -------------------------------------------------------------------
   5. МАРКИ: ЭМБЛЕМЫ ВМЕСТО НАДПИСЕЙ
   Файлы кладутся в /assets/img/brands/. Если файла нет или он не
   загрузился — атп3.js снимает <img> и на его месте остаётся
   текстовое начертание, так что строка не разваливается никогда.
   ------------------------------------------------------------------- */
.brand-row {
    display: flex;
    align-items: center;
    gap: 3.25rem;
    padding-right: 3.25rem;
    margin: 0;
    list-style: none;
}

.brand-logo {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    /* --s — индивидуальная оптическая подгонка: круглая эмблема при
       одинаковой высоте выглядит крупнее, чем вытянутый логотип. */
    height: calc(2.15rem * var(--s, 1));
    /* Резерв ширины, чтобы лента не дёргалась, пока файлы грузятся. */
    min-width: 5.25rem;
}

.brand-logo img {
    display: block;
    width: auto;
    height: 100%;
    max-width: 9.5rem;
    object-fit: contain;
    /* Обесцвечиваем: полтора десятка фирменных цветов подряд
       перетянули бы на себя всё внимание с заголовка. */
    filter: grayscale(1) opacity(.52);
    transition: filter .35s var(--ease);
}

.marquee:hover .brand-logo img {
    filter: grayscale(1) opacity(.66);
}

.brand-logo:hover img {
    filter: grayscale(0) opacity(1);
}

/* Текстовый запасной вариант. По умолчанию скрыт; boot.js вешает
   .is-txt на плашку, если файл эмблемы не загрузился. Через :has()
   не делаем: в браузере без его поддержки правило отвалится целиком
   и текст полез бы поверх логотипов. */
.brand-logo-txt {
    display: none;
    font-family: Manrope, 'Manrope Fallback', system-ui, sans-serif;
    font-size: 1.0625rem;
    font-weight: 800;
    letter-spacing: .045em;
    line-height: 1;
    white-space: nowrap;
    text-transform: uppercase;
    color: var(--text-soft);
}

.brand-logo.is-txt {
    min-width: 0;
}

.brand-logo.is-txt .brand-logo-txt {
    display: block;
}

/* Подпись и сноска под лентой. Селектор повторяет старое правило
   section:has(> .marquee) > p:first-child один в один по весу, но идёт
   ниже по файлу — иначе прежний margin-bottom перебил бы новый. */
section:has(> .marquee) > p.brands-caption {
    text-align: center;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .18em;
    color: var(--text-mut);
    margin-bottom: 1.375rem;
}

.brands-note {
    max-width: 38rem;
    margin: 1.25rem auto 0;
    text-align: center;
    font-size: .8125rem;
    line-height: 1.6;
    color: var(--text-soft);
}

@media (max-width: 640px) {
    .brand-row {
        gap: 2.25rem;
        padding-right: 2.25rem;
    }

    .brand-logo {
        height: calc(1.75rem * var(--s, 1));
        min-width: 4rem;
    }

    .brand-logo img {
        max-width: 7rem;
    }

    .brand-logo-txt {
        font-size: .9375rem;
    }

    .brands-note {
        font-size: .75rem;
    }
}

/* На тач-экранах ховера нет — держим логотипы чуть контрастнее. */
@media (hover: none) {
    .brand-logo img {
        filter: grayscale(1) opacity(.62);
    }
}

/* Марок стало больше, поэтому увеличиваем период прохода — иначе
   лента поедет заметно быстрее прежнего при той же длительности. */
.marquee-track {
    animation-duration: 48s;
}

@media (max-width: 640px) {
    .marquee-track {
        animation-duration: 68s;
    }
}


/* ===================================================================
   v35 — СТАТИЧНЫЙ ФОН, ПЕРЕСВЕДЁННЫЕ ШАПКИ «УСЛУГ» И «АВТОПАРКА»
   Блок дописан в конец файла, поэтому переопределяет всё, что выше.
   =================================================================== */

/* -------------------------------------------------------------------
   1. ФОН САЙТА: ТА ЖЕ ПОДЛОЖКА, НО РОВНАЯ И СВЕТЛЕЕ

   Подложка остаётся прежней: на экранах шире 1360 px это по-прежнему
   фотография тягача с тралом (desk.webp) под тёплой заливкой, ниже —
   плоский тон --desk. Ничего не удаляется.

   Снимаются только два слоя, из-за которых поле «играло»: диагональный
   цветовой градиент (холодный угол сверху слева, тёплый снизу справа)
   и вертикальная виньетка сверху и снизу. Без них фон читается как
   одна ровная поверхность.

   Светлее — за счёт плотности заливки поверх фото: было 72 %, стало
   80 %. Средняя яркость подложки поднимается с L* 74.1 до 77.0.
   Плоский тон --desk поднят на столько же: L* 83.7 → 86.6.
   ------------------------------------------------------------------- */
:root {
    --desk: #E3D8C2;
}

body::before {
    background: none;
}

@media (min-width: 1360px) {
    body::before {
        background-image:
            linear-gradient(rgba(220, 207, 182, .8), rgba(220, 207, 182, .8)),
            url(../img/desk.webp);
        background-size: 100% 100%, cover;
        background-position: center, center;
        background-repeat: no-repeat, no-repeat;
    }
}

/* -------------------------------------------------------------------
   4. ПОИМЁННЫЙ СОСТАВ ДЕЖУРНОЙ ГРУППЫ

   Было: таблица на всю ширину, одиннадцать строк, высота ~530 px —
   почти вдвое выше двух карточек над ней, и набрана она другим
   языком (шапка, разлиновка, подвал). Стало: та же карточка, что и
   у соседей (белый фон, p-6, заголовок, лид), а машины — двумя
   колонками по одной строке на борт. Высота ~330 px, вровень с
   соседями.

   grid-auto-flow:column раскладывает список по колонкам, а не по
   строкам: борта 1–6 слева, 7–11 справа. Число строк задано жёстко —
   при добавлении машины поправьте repeat(6, auto). */
.duty-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: 1fr;
    column-gap: 2.5rem;
}

.duty-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .125rem .625rem;
    padding: .5rem 0;
    border-bottom: 1px solid var(--line);
    font-size: .8125rem;
    line-height: 1.55;
}

.duty-list li:last-child {
    border-bottom: 0;
}

.duty-list li > span:first-child {
    flex: 0 0 auto;
    font-family: 'IBM Plex Mono', 'IBM Plex Mono Fallback', monospace;
    font-weight: 600;
    color: var(--text-strong);
}

.duty-list li > span:last-child {
    flex: 1 1 12rem;
    min-width: 0;
    color: var(--text-mut);
}

@media (min-width: 768px) {
    .duty-list {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: repeat(6, auto);
        grid-auto-flow: column;
    }

    .duty-list li:nth-child(6) {
        border-bottom: 0;
    }
}


/* ===================================================================
   v38 — ШАПКИ РАЗДЕЛОВ

   Услуги — вариант Г: светлая панель, чертёжная сетка, фирменная
   линия по верхней кромке, карточка с цифрами справа. Рисунка в углу
   нет вовсе.

   О нас — вариант В: тёмная панель с приглушённой фотографией внутри
   и тёплым отсветом сверху справа.
   =================================================================== */

/* --------------------- УСЛУГИ (Г) --------------------- */
.hero-panel--services {
    position: relative;
    overflow: hidden;
    /* Панель набирает высоту содержимым: групповое правило выше
       подпирает все три шапки min-height в 34.5rem, из-за чего блок
       стоял на 552 px при собственных ~430 и текст плавал в пустоте. */
    min-height: 0;
    background-color: #FCF8F0;
    border: 1px solid #E3D6BA;
    background-image:
        radial-gradient(76% 92% at 98% 0%, #F5E7CB 0%, transparent 62%),
        linear-gradient(172deg, #FFFDF8 0%, #F8F1E3 62%, #F2E8D6 100%);
    box-shadow:
        0 1px 2px rgba(90, 74, 50, .10),
        0 8px 18px -8px rgba(90, 74, 50, .16),
        0 44px 70px -36px rgba(90, 74, 50, .44);
}

/* Сетка гаснет маской к дальнему углу: сплошная читается как обои,
   затухающая — как фактура чертёжного листа. */
.hero-panel--services::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        linear-gradient(rgba(120, 96, 58, .10) 1px, transparent 1px),
        linear-gradient(90deg, rgba(120, 96, 58, .10) 1px, transparent 1px);
    background-size: 32px 32px;
    -webkit-mask-image: radial-gradient(120% 100% at 100% 0, #000 6%, rgba(0, 0, 0, .35) 48%, transparent 82%);
    mask-image: radial-gradient(120% 100% at 100% 0, #000 6%, rgba(0, 0, 0, .35) 48%, transparent 82%);
}

/* Тот же приём, что у .form-card */
.hero-panel--services::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    z-index: 2;
    pointer-events: none;
    background: linear-gradient(90deg, var(--o-700) 0%, var(--o-btn) 30%, var(--o-400) 54%, rgba(238, 155, 0, 0) 100%);
}

/* Псевдоэлементы позиционированы и без этого легли бы поверх текста */
.hero-panel--services > * {
    position: relative;
    z-index: 1;
}

.hero-badge {
    background: var(--sheet);
}

.hero-badge .badge-num {
    font-family: Manrope, 'Manrope Fallback', Inter, sans-serif;
    letter-spacing: -.02em;
    line-height: 1;
}

/* --------------------- О НАС ---------------------
   Шапка возвращена к исходной: тёмная панель с водяным знаком-графиком,
   свет с двух сторон — холодная сталь сверху справа, тёплый янтарь снизу
   слева. Меняется только сила градиента.

   Было: c-500 на 22 % и o-500 на 16 %, оба пятна узкие. На почти чёрном
   фоне такие проценты дают разницу в пару пунктов яркости — градиент
   есть в коде, но глазом не читается.

   Стало: пятна шире, ядро плотнее (52 % и 38 %), плюс диагональная
   подложка, которая задаёт вертикальную глубину. Верхний правый угол
   уходит в сталь #283C45, нижний левый — в янтарную охру #513615,
   середина остаётся почти чёрной. Структура света та же, просто теперь
   её видно.
   ------------------------------------------------- */
.hero-panel--about {
    background-color: var(--ink);
    border-color: var(--ink-2);
    box-shadow:
        0 1px 2px rgba(var(--shadow-rgb), .1),
        0 24px 52px -34px rgba(var(--shadow-rgb), .72);
    background-image:
        radial-gradient(78% 96% at 94% 2%,
            color-mix(in srgb, var(--c-500) 52%, transparent) 0%,
            color-mix(in srgb, var(--c-500) 20%, transparent) 38%,
            transparent 68%),
        radial-gradient(70% 88% at 2% 100%,
            color-mix(in srgb, var(--o-500) 38%, transparent) 0%,
            color-mix(in srgb, var(--o-600) 14%, transparent) 40%,
            transparent 68%),
        linear-gradient(158deg, #06202A 0%, var(--ink) 52%, #071820 100%);
}

@media (max-width: 1023px) {
    .hero-panel--services::before {
        background-size: 26px 26px;
    }
}

/* --------------------- АВТОПАРК (К5-2) ---------------------
   Заголовок во всю ширину, под ним линейка, ниже три колонки:
   лид с кнопками, цифры, кадр. Колонки растягиваются на общую
   высоту, поэтому обрываются на одной линии.

   ВАЖНО про отступы. Tailwind здесь собран заранее и содержит
   только те классы, что были в исходной вёрстке: py-10, py-14,
   py-16, md:py-16, md:py-20. Классов py-12 и md:py-12 в сборке
   НЕТ — они молча не применяются, и панель остаётся с нулевым
   вертикальным отступом. Поэтому отступы шапки заданы здесь,
   а не утилитами. */
.hero-panel--fleet {
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    padding: 2.5rem 1.5rem;
    /* Групповое правило выше ставит min-height:34.5rem всем трём шапкам.
       Из-за него разворот растягивался на 348 px при собственных 210,
       колонки распирало, а между числами возникали просветы по 43 px.
       Панель должна набирать высоту содержимым. */
    min-height: 0;
    background-color: #FCF8F0;
    border: 1px solid #E3D6BA;
    background-image:
        radial-gradient(76% 92% at 98% 0%, #F5E7CB 0%, transparent 62%),
        linear-gradient(172deg, #FFFDF8 0%, #F8F1E3 62%, #F2E8D6 100%);
    box-shadow:
        0 1px 2px rgba(90, 74, 50, .10),
        0 8px 18px -8px rgba(90, 74, 50, .16),
        0 44px 70px -36px rgba(90, 74, 50, .44);
}

.hero-panel--fleet::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    z-index: 2;
    pointer-events: none;
    background: linear-gradient(90deg, var(--o-700) 0%, var(--o-btn) 28%, var(--o-400) 52%, rgba(238, 155, 0, 0) 100%);
}

.hero-panel--fleet > * {
    position: relative;
    z-index: 1;
}

.hero-head .sec-title {
    margin-top: .75rem;
}

.hero-split {
    display: grid;
    grid-template-columns: 1fr;
    gap: 2rem;
    /* gap работает между элементами сетки, но не отделяет её от
       заголовка выше — на узком экране лид иначе липнет к h1 */
    margin-top: 1.75rem;
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: 2.75rem;
}

.hero-nums {
    margin: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0 .875rem;
}

.hero-nums > div {
    border-top: 2px solid var(--line-2);
    padding-top: .625rem;
}

.hero-nums dt {
    font-family: Manrope, 'Manrope Fallback', Inter, sans-serif;
    font-weight: 800;
    font-size: 1.25rem;
    letter-spacing: -.02em;
    color: var(--text-strong);
}

.hero-nums dd {
    margin: .125rem 0 0;
    font-size: .625rem;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-mut);
}

.hero-shot {
    margin: 0;
    border-radius: 14px;
    overflow: hidden;
    background: var(--band);
    aspect-ratio: 16 / 10;
    box-shadow:
        0 1px 3px rgba(90, 74, 50, .16),
        0 18px 32px -20px rgba(90, 74, 50, .5);
}

.hero-shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

@media (min-width: 768px) {
    .hero-panel--fleet {
        padding: 3.5rem;
    }
}

@media (min-width: 1024px) {
    .hero-head {
        padding-bottom: 1.75rem;
        border-bottom: 1px solid var(--line);
    }

    /* Панель — колонка, разворот тянется на весь остаток высоты,
       поэтому кадр заполняет её до низа и воздуха не остаётся. */
    .hero-split {
        flex: 1;
        grid-template-columns: 1fr 13rem 20rem;
        gap: 0;
        align-items: stretch;
        margin-top: 0;
        padding-top: 1.75rem;
    }

    .hero-lead {
        padding-right: 2.25rem;
    }

    /* Кнопки идут обычным потоком за текстом. Прижимать их к низу
       колонки (margin-top:auto) пробовали — вставали заподлицо
       с кадром и читались как проваленные. Просвет фиксированный. */
    .hero-lead .hero-actions {
        margin-top: 3rem;
    }

    .hero-nums {
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        gap: 1.125rem;
        border-left: 1px solid var(--line);
        padding: 0 2.25rem;
    }

    .hero-nums > div {
        border-top: 1px solid var(--line);
        padding-top: 1.125rem;
    }

    .hero-nums > div:first-child {
        border-top: 0;
        padding-top: 0;
    }

    .hero-nums dt {
        font-size: 1.625rem;
    }

    .hero-nums dd {
        font-size: .6875rem;
        letter-spacing: .06em;
    }

    /* Кадр вынут из расчёта высоты. Иначе картинка 520×520 при ширине
       колонки 320 навязывала развороту свои 320 px, и панель раздувалась
       до 582 при собственных 216. Теперь высоту задают текст и цифры,
       а снимок просто заполняет колонку. */
    .hero-shot-col {
        position: relative;
        min-height: 14rem;
    }

    .hero-shot {
        position: absolute;
        inset: 0;
        aspect-ratio: auto;
    }
}

/* Карточка тянется на высоту текстовой колонки, содержимое внутри
   центрируется — колонки заканчиваются на одной линии. */
@media (min-width: 1024px) {
    .hero-panel--services > .grid {
        align-items: stretch;
    }

    .hero-badge {
        display: flex;
        flex-direction: column;
        justify-content: center;
    }
}

/* --------------------- УСЛУГИ: воздух ---------------------
   Блок читался сжатым: при 408 px высоты на содержимое приходилось
   248, а внутренние просветы шли 17 / 20 / 32 — первые два глаз не
   различает. Раздвигаем и рамку, и ритм внутри. */
.hero-panel--services {
    padding-top: 4rem;
    padding-bottom: 4rem;
}

.hero-panel--services .hero-copy .sec-lead {
    margin-top: 1.75rem;
}

.hero-panel--services .hero-actions {
    margin-top: 2.5rem;
}

.hero-panel--services .hero-badge {
    padding: 2.5rem;
}

@media (min-width: 768px) {
    .hero-panel--services {
        padding-top: 6rem;
        padding-bottom: 6rem;
    }
}

/* -------------------------------------------------------------------
   СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ (форма заявки)

   Галочка, а не строка мелким шрифтом под кнопкой: по 152-ФЗ согласие
   должно быть конкретным и осознанным, а молчание согласием не считается.
   Поэтому input стоит перед кнопкой отправки и имеет required —
   форма не уйдёт, пока посетитель сам не отметит.

   Своя разметка вместо системного чекбокса: у него не задать размер и
   цвет, а в 11-м кегле стандартный квадрат выглядит инородно. Реальный
   input оставлен на месте и скрыт визуально — фокус с клавиатуры,
   озвучивание экранным диктором и :checked работают как обычно.
   ------------------------------------------------------------------- */
.consent {
    position: relative;   /* якорь для скрытого input: без него он уедет
                             к ближайшему позиционированному предку (.form-card),
                             и браузер при фокусе с клавиатуры прокрутит
                             страницу к верху карточки вместо самой галочки */
    display: flex;
    align-items: flex-start;
    gap: .625rem;
    cursor: pointer;
    font-size: .75rem;
    line-height: 1.55;
    color: var(--text-mut);
}

.consent input {
    position: absolute;
    opacity: 0;
    width: 1.125rem;
    height: 1.125rem;
    margin: 0;
}

/* Рисованный квадрат. flex:none — иначе при длинном тексте ужимается */
.consent-box {
    position: relative;
    flex: none;
    width: 1.125rem;
    height: 1.125rem;
    margin-top: .0625rem;
    border: 1.5px solid var(--line-2);
    border-radius: 5px;
    background: #fff;
    transition: background-color .18s, border-color .18s;
}

.consent-box::after {
    content: "";
    position: absolute;
    left: 5px;
    top: 1px;
    width: 5px;
    height: 9px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg) scale(.5);
    opacity: 0;
    transition: opacity .16s, transform .2s var(--ease);
}

.consent:hover .consent-box {
    border-color: color-mix(in srgb, var(--line-2) 60%, var(--text-mut));
}

.consent input:checked + .consent-box {
    background: var(--o-700);
    border-color: var(--o-700);
}

.consent input:checked + .consent-box::after {
    opacity: 1;
    transform: rotate(45deg) scale(1);
}

/* Обводка фокуса: своя, потому что настоящий input не виден */
.consent input:focus-visible + .consent-box {
    outline: 2px solid var(--c-600);
    outline-offset: 2px;
}

/* Браузер подсвечивает незаполненное обязательное поле после попытки
   отправки. :user-invalid, а не :invalid — иначе квадрат красный
   с первой секунды, ещё до того как посетитель что-то сделал. */
.consent input:user-invalid + .consent-box {
    border-color: #b91c1c;
}

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

/* -------------------------------------------------------------------
   ПРАВОВЫЕ СТРАНИЦЫ (политика обработки персональных данных)

   Длинный сплошной текст — единственное место на сайте, где нужна
   обычная типографика документа. Стили заданы по тегам внутри .legal,
   чтобы разметка осталась семантической: правки в тексте не требуют
   раскладывать классы по каждому абзацу.
   ------------------------------------------------------------------- */
.legal {
    max-width: 46rem;
    margin-inline: auto;
    color: var(--text-body);
    font-size: .9375rem;
    line-height: 1.75;
}

.legal h2 {
    margin: 2.75rem 0 .875rem;
    font-size: 1.1875rem;
    font-weight: 800;
    line-height: 1.3;
    color: var(--text-strong);
    scroll-margin-top: 6rem;
}

.legal h2:first-child {
    margin-top: 0;
}

/* Номер раздела моноширинным, как подписи в остальном интерфейсе */
.legal h2 > span {
    font-family: 'IBM Plex Mono', 'IBM Plex Mono Fallback', monospace;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--o-700);
    margin-right: .5rem;
}

.legal p {
    margin: 0 0 .875rem;
    text-wrap: pretty;
}

.legal ul {
    margin: 0 0 .875rem;
    padding: 0;
    list-style: none;
}

.legal li {
    position: relative;
    padding-left: 1.125rem;
    margin-bottom: .4375rem;
}

.legal li::before {
    content: "";
    position: absolute;
    left: 0;
    top: .6875rem;
    width: 6px;
    height: 2px;
    border-radius: 999px;
    background: var(--o-500);
}

.legal strong {
    color: var(--text-strong);
    font-weight: 700;
}

/* Карточка с реквизитами оператора и датой редакции */
.legal-meta {
    display: grid;
    gap: .625rem 1.5rem;
    margin-bottom: 2.5rem;
    padding: 1.375rem 1.5rem;
    font-size: .875rem;
}

@media (min-width: 640px) {
    .legal-meta {
        grid-template-columns: 11rem 1fr;
    }
}

.legal-meta dt {
    color: var(--text-mut);
}

.legal-meta dd {
    margin: 0;
    color: var(--text-strong);
    font-weight: 600;
}

/* Незаполненный реквизит виден и в вёрстке, и на странице: пропустить
   такое при публикации сложнее, чем аккуратный прочерк. */
.legal-todo {
    background: #fff4d6;
    border-bottom: 2px solid #e0a800;
    padding: 0 .25rem;
    font-weight: 700;
    color: #7a4b00;
}

/* Строка про политику под галочкой. Ознакомление с политикой — это
   информирование, а не согласие: своей отметки оно не требует, поэтому
   идёт обычным текстом. Отступ выровнен по тексту галочки (ширина
   квадрата 1.125rem + зазор .625rem), чтобы обе строки читались
   как один блок, а не как случайная подпись под кнопкой. */
.consent-note {
    margin: -.25rem 0 0 1.75rem;
    font-size: .6875rem;
    line-height: 1.55;
    color: var(--text-soft);
}

/* -------------------------------------------------------------------
   v35 — ТОН ТЁПЛЫХ ПОДЛОЖЕК

   Тёплый ряд идёт от песочного к кирпичному: o-100 стоит на 87°,
   o-400 на 71°, o-600 уже на 39°, o-700 на 30°. Это нормальная
   шкала — светлые ступени жёлтые, тёмные красные.

   Ошибка была в том, откуда брались светлые подложки. Все мягкие
   поверхности (--tint-1/2/3) подмешивались из o-600, то есть из
   красного конца шкалы. На 5–12 % поверх белого от него остаётся
   только тон, и плитка под иконкой выходила на 47° — розовой,
   рядом с синими плитками соседних карточек это читалось как брак,
   а не как акцент. Лист сайта при этом стоит на 88°.

   Подмешиваем из o-500 (54°) — ступенью выше по той же шкале.
   Фирменная теплота сохраняется, розовый уходит: плитка встаёт
   на 68°, подвал карточки и подложка кадра — на 68°.
   Один токен, ничего нового в палитру не добавлено.
   ------------------------------------------------------------------- */
:root {
    --tint-1: color-mix(in srgb, var(--o-500) 5%,  transparent);
    --tint-2: color-mix(in srgb, var(--o-500) 12%, transparent);
    --tint-3: color-mix(in srgb, var(--o-500) 28%, transparent);
}

/* -------------------------------------------------------------------
   v36 — ОДИН ЦВЕТ ДЛЯ ВСЕХ ПОЛОС

   Пять оттенков полосы (sand / clay / linen / taupe и базовый) стояли
   в пределах dE 0.5–1.8 друг от друга — разница, которую глаз не берёт
   даже при прямом сравнении, а на разных страницах и подавно. Вместо
   пяти почти одинаковых значений оставляем одно — песочное, с полосы
   статистики на главной.

   Побочно закрывается дыра по контрасту: на самой тёмной полосе
   (taupe, автопарк) приглушённый текст давал 4.47 — ниже порога AA.
   На песочной 4.64.

   Токены-модификаторы оставлены и указывают на --band: разметку
   трогать не нужно, а если понадобится вернуть вариативность —
   достаточно переопределить нужный токен здесь.
   ------------------------------------------------------------------- */
:root {
    --band:       #EAE1CD;
    --band-sand:  var(--band);
    --band-clay:  var(--band);
    --band-linen: var(--band);
    --band-taupe: var(--band);
}

/* -------------------------------------------------------------------
   ЦИФРЫ-СЧЁТЧИКИ: без вспышки настоящего значения перед отсчётом

   Разметка содержит настоящее число (28, 60, 40) — так его видит
   поисковик и человек с выключенным JS. Раньше скрипт обнулял его
   уже после первой отрисовки, и на страницах, где блок попадает
   на первый экран (автопарк), читалось: «40 → 0 → отсчёт → 40».
   Со стороны это выглядело сбоем, а не анимацией.

   Теперь при работающем JS число ждёт своей очереди невидимым и
   проявляется вместе с началом отсчёта. Класс js ставится в <head>
   до первого кадра (boot.js), поэтому промежуточного состояния,
   когда правило ещё не применилось, не существует.

   Подстраховки на случай, если atp3.js не доехал: класс is-counted
   через 5 с ставит boot.js, а он лежит отдельным файлом в <head>.
   Если и его нет — нет и класса js, и правило просто не работает.

   min-width скрипт выставляет в ch по длине конечного значения:
   при tabular-nums цифры одинаковой ширины, поэтому «+» и « т»
   рядом с числом стоят на месте, пока оно набирает разряды.
   ------------------------------------------------------------------- */
.counter {
    display: inline-block;
    font-variant-numeric: tabular-nums;
}

.js .counter:not(.is-counted) {
    opacity: 0;
}

.js .counter {
    transition: opacity .28s ease;
}

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

/* -------------------------------------------------------------------
   КАРТА: подложка под фотографию

   Заглушка умеет показывать снимок карты вместо клетчатого поля.
   Снимок подставляется переменной, а не правится в CSS, — чтобы на
   главной и в «О нас» можно было поставить разные кадры:

       <button type="button"
               class="map-embed map-embed--dark map-embed--photo"
               style="--map-poster:url(/assets/img/map-office.webp)"
               data-map="…">

   Слой со снимком стоит первым в списке background-image, клетка —
   под ним. Если файла на сервере нет, браузер молча пропускает слой
   и остаётся прежняя клетка: заглушка не ломается никогда.

   Класс --photo добавляет затемнение поверх снимка (::after с
   отрицательным z-index — рисуется над фоном, но под текстом) и
   выбеливает подписи. Без снимка его ставить не нужно: затемнение
   ляжет на пустую клетку.
   ------------------------------------------------------------------- */
.map-embed {
    background-image:
        var(--map-poster, none),
        linear-gradient(var(--line) 1px, transparent 1px),
        linear-gradient(90deg, var(--line) 1px, transparent 1px);
    background-size: cover, 24px 24px, 24px 24px;
    background-position: center, 0 0, 0 0;
    background-repeat: no-repeat, repeat, repeat;
}

.map-embed--dark {
    background-image:
        var(--map-poster, none),
        linear-gradient(rgba(255, 255, 255, .07) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .07) 1px, transparent 1px);
}

.map-embed--photo::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg, rgba(10, 8, 6, .25) 0, rgba(10, 8, 6, .58) 100%);
    transition: opacity .2s;
}

.map-embed--photo:hover::after {
    opacity: .82;
}

.map-embed--photo .map-embed-text {
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
}

.map-embed--photo .map-embed-hint {
    color: rgba(255, 255, 255, .86);
}

.map-embed--photo .map-embed-pin {
    color: #fff;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .55));
}

@media (prefers-reduced-motion: reduce) {
    .map-embed--photo::after {
        transition: none;
    }
}

/* Таблицы на «Автопарке»: на узком экране боковые поля ячеек в 20 px
   съедали треть строки, и текст переносился по одному слову. */
@media (max-width: 640px) {
    .card table th,
    .card table td {
        padding-left: .875rem;
        padding-right: .875rem;
    }
}

/* Плашка марки без загруженной эмблемы не должна держать ширину:
   пока файлов нет, резерв в 5.25rem превращался в пустоту внутри ленты.
   Класс is-txt ставит boot.js по событию error — но событие приходит
   только когда браузер реально пробует загрузить файл. У картинок
   в ленте убрана ленивая загрузка именно поэтому: за пределами экрана
   загрузка не начиналась, ошибки не было, и плашка оставалась пустой. */
.brand-logo:not(.is-txt) img[src$=".svg"]:not([data-ok]) {
    min-width: 0;
}

/* Фоновые заглушки для трёх снимков, которые раньше лежали в папке
   без применения: тот же приём, что у остальных .ph-* — фон рисуется
   сразу, <img> подгружается по мере приближения к экрану. */


/* -------------------------------------------------------------------
   КАРТА: подложка под фотографию

   Заглушка умеет показывать снимок карты вместо клетчатого поля.
   Снимок подставляется переменной, а не правится в CSS, — чтобы на
   главной и в «О нас» можно было поставить разные кадры:

       <button type="button"
               class="map-embed map-embed--dark map-embed--photo"
               style="--map-poster:url(/assets/img/map-office.webp)"
               data-map="…">

   Слой со снимком стоит первым в списке background-image, клетка —
   под ним. Если файла на сервере нет, браузер молча пропускает слой
   и остаётся прежняя клетка: заглушка не ломается никогда.

   Класс --photo добавляет затемнение поверх снимка (::after с
   отрицательным z-index — рисуется над фоном, но под текстом) и
   выбеливает подписи. Без снимка его ставить не нужно: затемнение
   ляжет на пустую клетку.
   ------------------------------------------------------------------- */
.map-embed {
    background-image:
        var(--map-poster, none),
        linear-gradient(var(--line) 1px, transparent 1px),
        linear-gradient(90deg, var(--line) 1px, transparent 1px);
    background-size: cover, 24px 24px, 24px 24px;
    background-position: center, 0 0, 0 0;
    background-repeat: no-repeat, repeat, repeat;
}

.map-embed--dark {
    background-image:
        var(--map-poster, none),
        linear-gradient(rgba(255, 255, 255, .07) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .07) 1px, transparent 1px);
}

.map-embed--photo::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg, rgba(10, 8, 6, .25) 0, rgba(10, 8, 6, .58) 100%);
    transition: opacity .2s;
}

.map-embed--photo:hover::after {
    opacity: .82;
}

.map-embed--photo .map-embed-text {
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
}

.map-embed--photo .map-embed-hint {
    color: rgba(255, 255, 255, .86);
}

.map-embed--photo .map-embed-pin {
    color: #fff;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .55));
}

@media (prefers-reduced-motion: reduce) {
    .map-embed--photo::after {
        transition: none;
    }
}
/* Таблицы на «Автопарке»: на узком экране боковые поля ячеек в 20 px
   съедали треть строки, и текст переносился по одному слову. */
@media (max-width: 640px) {
    .card table th,
    .card table td {
        padding-left: .875rem;
        padding-right: .875rem;
    }
}

/* Плашка марки без загруженной эмблемы не должна держать ширину:
   пока файлов нет, резерв в 5.25rem превращался в пустоту внутри ленты.
   Класс is-txt ставит boot.js по событию error — но событие приходит
   только когда браузер реально пробует загрузить файл. У картинок
   в ленте убрана ленивая загрузка именно поэтому: за пределами экрана
   загрузка не начиналась, ошибки не было, и плашка оставалась пустой. */
.brand-logo:not(.is-txt) img[src$=".svg"]:not([data-ok]) {
    min-width: 0;
}
/* Фоновые заглушки для трёх снимков, которые раньше лежали в папке
   без применения: тот же приём, что у остальных .ph-* — фон рисуется
   сразу, <img> подгружается по мере приближения к экрану. */



/* -------------------------------------------------------------------
   v39 — ШАПКИ РАЗДЕЛОВ НА ПОЛОСАХ

   После сведения полос к одному песочному тону заголовок и лид
   лежали прямо на подложке, а карточки под ними — белые. Полоса
   читалась как незакрашенный кусок листа, а не как отдельный
   раздел: белое внизу, ничем не поддержанный текст сверху.

   Заворачиваем шапку в такую же белую плашку, что и карточки.
   Раздел получает верх и низ из одного материала, а песочный тон
   остаётся тем, чем задумывался, — полем вокруг.

   Селектор бьёт только по прямым потомкам полосы, поэтому шапки
   в обычных разделах, где фон и так белый, не затронуты. Полоса
   статистики на главной шапки не имеет и правило не ловит.
   ------------------------------------------------------------------- */
.page-sheet > .band > .text-center.max-w-2xl {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: 1.5rem 1.25rem;
    box-shadow: 0 1px 2px rgba(var(--shadow-rgb), .04);
}

@media (min-width: 768px) {
    .page-sheet > .band > .text-center.max-w-2xl {
        padding: 2rem 2.5rem;
    }
}

/* -------------------------------------------------------------------
   v40 — ШАПКА «АВТОПАРКА» БЕЗ СНИМКА

   Кадр с манипулятором из шапки убран: разгрузка контейнера на
   стройке не иллюстрирует заголовок про сорок собственных машин.
   Пока не снят подходящий кадр, разворот идёт в две колонки —
   текст и цифры. Третью колонку вернуть просто: снимок кладётся
   обратно в .hero-shot-col, а это правило удаляется.

   Обе колонки прижаты кверху: без снимка тянуть их по высоте
   нечем, и растянутые цифры провисали бы в пустоте.
   ------------------------------------------------------------------- */
@media (min-width: 1024px) {
    .hero-split:not(:has(.hero-shot-col)) {
        grid-template-columns: 1fr 14rem;
        align-items: start;
    }

    .hero-split:not(:has(.hero-shot-col)) .hero-lead {
        padding-right: 3.5rem;
    }
}

/* -------------------------------------------------------------------
   v41 — ОКНА УСЛУГ: БЕЗ КАДРА И БЕЗ ПОЛОСЫ ПРОКРУТКИ

   Кадр из шапки окна убран в скрипте: он повторял фотографию,
   которая и так стоит в карточке услуги на странице, а в узком
   окне занимал треть первого экрана.

   Полосу прокрутки прячем, но саму прокрутку оставляем: колесом,
   пальцем и клавишами окно листается по-прежнему. Заодно
   overscroll-behavior не даёт странице за окном уезжать вслед
   за прокруткой, когда список дошёл до конца.
   ------------------------------------------------------------------- */
.svc-modal-dialog {
    scrollbar-width: none;
    -ms-overflow-style: none;
    overscroll-behavior: contain;
}

.svc-modal-dialog::-webkit-scrollbar {
    width: 0;
    height: 0;
}

.svc-modal-body > .eyebrow:first-child {
    margin-top: .25rem;
}

/* -------------------------------------------------------------------
   v42 — РИСУНОК В ШАПКЕ «АВТОПАРКА»

   После снятия фотографии правый низ панели опустел. Ставим туда
   тот же приём, что уже работает на «О нас» и «Услугах»: контурный
   водяной знак цветом текста, почти прозрачный. Он заполняет угол,
   не споря с цифрами поверх, и не претендует на роль фотографии —
   когда появится снимок площадки, знак останется фоном или уйдёт.

   Рисунок шире стандартного квадрата, поэтому пропорции и размеры
   заданы отдельно: ангар с воротами и сцепка перед ними.
   ------------------------------------------------------------------- */
.hero-watermark--fleet {
    /* Правило .hero-panel--fleet > * ставит всем прямым детям панели
       position: relative — оно идёт ниже базового .hero-watermark и
       перебивало его absolute. Знак вываливался в поток и вставал
       над заголовком. Возвращаем абсолютное позиционирование здесь,
       ниже обоих правил.

       Место выбрано между кнопками и колонкой с цифрами: раньше знак
       стоял под текстом и просвечивал сквозь абзац. Показываем его
       только на широких экранах — на узких свободного места нет. */
    position: absolute;
    z-index: 0;
    width: 11.5rem;
    height: 8rem;
    right: 21rem;
    bottom: 2.5rem;
    opacity: .13;
}

@media (min-width: 1280px) {
    .hero-watermark--fleet {
        width: 14rem;
        height: 9.8rem;
        right: 24rem;
        bottom: 3rem;
    }
}

/* -------------------------------------------------------------------
   v43 — ПЛАВНОСТЬ МЕЛКОЙ АНИМАЦИИ

   Аккордеон в «Частых вопросах» дёргался, и виновата была не сама
   анимация высоты, а её соседи. При каждом открытии одновременно
   менялись: высота тела, тень карточки, её масштаб, рамка, фон,
   поворот плюсика, прозрачность и сдвиг содержимого. Высота и тень —
   самые дорогие: первая заставляет браузер пересчитывать раскладку
   на каждом кадре, вторая — заново размывать тень поверх неё.

   Тень из перехода убрана: она меняется мгновенно, на глаз это не
   читается, потому что одновременно едет высота. Телу добавлен
   contain — пересчёт раскладки замыкается внутри карточки и не
   расходится на всю страницу, где таких карточек семь подряд.
   will-change включается только на время анимации, по тому же
   признаку data-busy, который ставит скрипт.
   ------------------------------------------------------------------- */
details.acc {
    transition: transform .22s var(--ease), border-color .22s ease;
}

.acc-body {
    contain: layout paint;
}



.acc-body > * {
    transition: opacity .26s ease .04s, transform .3s var(--ease) .04s;
}

/* Наведение на закрытый вопрос масштабировало карточку вместе с
   текстом — буквы перерисовывались целиком. Оставляем сдвиг рамки
   и подсветку, масштаб убираем: на стопке одинаковых карточек он
   всё равно читался как дрожание. */
details.acc:not([open]):hover {
    transform: none;
}

/* -------------------------------------------------------------------
   Полоска под вкладками при переходе между страницами.

   К переезду полоски примешивалась вспышка: на каждой загрузке
   поверх сдвига проигрывалась анимация прозрачности с 60 до 100.
   Два эффекта разом читались как рывок. Прозрачность убрана,
   осталось одно движение.
   ------------------------------------------------------------------- */
.tab-indicator {
    animation: none;
    backface-visibility: hidden;
}

/* -------------------------------------------------------------------
   v44 — ПОЧЕМУ АККОРДЕОН ВСЁ ЕЩЁ ДЁРГАЛСЯ

   Прошлая правка сняла нагрузку с самой карточки, но не с того, что
   лежит ниже. Анимация высоты двигает всю страницу под собой, а под
   «Частыми вопросами» стоят фотоблоки с размытой подложкой. Размытие
   в 26 и 38 пикселей браузер пересчитывает на каждом кадре, пока
   слой едет: отсюда и ощущение двадцати кадров в секунду.

   Радиусы уменьшены до 13 и 18 — подложка по-прежнему читается как
   мягкое пятно цвета, а стоит втрое дешевле. Плюс каждой карточке
   аккордеона дан contain: paint, чтобы её перерисовка не выходила
   за собственные границы, и отдельным карточкам ниже — пропуск
   отрисовки, пока они за экраном.
   ------------------------------------------------------------------- */
details.acc {
    contain: paint;
}

.acc-body {
    transition: height .26s cubic-bezier(.32, .72, 0, 1);
}

/* content-visibility отсюда убран: при изменении высоты страницы карточки
   на границе экрана то входили в отрисовку, то выходили, и каждый переход
   стоил полной раскладки своего поддерева. Экономия обернулась расходом. */

/* -------------------------------------------------------------------
   Полоска под вкладками: движение заметнее

   Было ровное скольжение за 0.4 с, которое на глаз почти сливалось
   с перезагрузкой страницы. Кривая с перелётом — полоска чуть
   проскакивает цель и возвращается — читается как живое движение,
   а не как перерисовка.
   ------------------------------------------------------------------- */
.tab-indicator {
    transition: transform .52s cubic-bezier(.34, 1.42, .64, 1);
}

.tab-btn {
    transition: color .28s ease, transform .12s;
}

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

/* -------------------------------------------------------------------
   v45 — АККОРДЕОН БЕЗ АНИМАЦИИ ВЫСОТЫ

   Два захода на оптимизацию не помогли, и причина в самом приёме.
   Анимация высоты — единственное свойство здесь, которое заставляет
   браузер пересчитывать раскладку всей страницы на каждом кадре:
   раскрывающийся ответ двигает вниз всё, что под ним, вместе с
   фотоблоками, тенями и фоном листа. На слабой машине это сорок
   кадров работы там, где должно быть ноль.

   Поэтому высоту больше не анимируем: ответ раскрывается сразу, а
   едет только его содержимое — прозрачность и сдвиг на шесть
   пикселей. Оба свойства считает видеокарта, раскладка не трогается
   вообще, и провалиться ниже шестидесяти кадров тут физически нечему.

   Класс is-open ставится через два кадра после open, иначе браузер
   не успевает заметить начальное состояние и анимация не запускается.
   ------------------------------------------------------------------- */
.acc-body {
    height: auto;
    transition: none;
    will-change: auto;
    contain: none;
}

.acc-body > * {
    transition: opacity .22s ease, transform .28s cubic-bezier(.32, .72, 0, 1);
}

/* -------------------------------------------------------------------
   v46 — ЦВЕТ ТЕКСТА ВКЛАДОК ЕДЕТ ВМЕСТЕ С ПОЛОСКОЙ

   При переходе на другую страницу подпись новой вкладки становилась
   белой мгновенно — ещё до того, как под неё приезжала оранжевая
   полоска. Секунду держалась картинка «оранжевая кнопка с тёмным
   текстом»: полоска ещё на старой вкладке, а белым уже подсвечена
   новая.

   Пока на странице висит метка data-tab-from, то есть пока полоска
   стоит на прежнем месте, прежняя вкладка остаётся белой, а новая —
   приглушённой. Метку снимает скрипт в момент старта переезда, и
   оба цвета переходят вместе с ней, с той же длительностью.
   ------------------------------------------------------------------- */
html[data-tab-from] .tab-island .tab-btn[aria-current="page"] {
    color: var(--text-mut);
}

html[data-tab-from="0"] .tab-island .tab-btn:nth-child(2),
html[data-tab-from="1"] .tab-island .tab-btn:nth-child(3),
html[data-tab-from="2"] .tab-island .tab-btn:nth-child(4),
html[data-tab-from="3"] .tab-island .tab-btn:nth-child(5) {
    color: #fff;
}

.tab-btn {
    transition: color .42s ease .04s, transform .12s;
}

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

/* -------------------------------------------------------------------
   v47 — ПЛАВНОЕ РАСКРЫТИЕ, НАЙДЕННАЯ ПРИЧИНА

   Мгновенное открытие смотрелось грубо, поэтому анимация высоты
   возвращается. Но теперь понятно, что её тормозило.

   У листа страницы стояла тень с радиусом размытия 90 пикселей.
   Лист высотой в несколько тысяч пикселей, и при каждом изменении
   высоты страницы браузер заново размывает эту тень по всему
   периметру. Анимация высоты меняет её шестьдесят раз в секунду —
   отсюда и двадцать кадров вместо шестидесяти. Сама анимация была
   ни при чём, дорого обходилась перерисовка вокруг неё.

   Радиус уменьшен вдвое насовсем: на глаз тень та же, мягкая
   подложка под краем листа. А на время раскрытия скрипт вешает на
   страницу метку acc-animating, и размытый слой снимается совсем.
   Триста миллисекунд без него не видно — тень живёт у краёв листа,
   далеко от вопроса, по которому кликнули.
   ------------------------------------------------------------------- */
.acc-body {
    height: 0;
    transition: height .3s cubic-bezier(.32, .72, 0, 1);
    contain: none;
}

details.acc[open] .acc-body {
    height: auto;
}

html.acc-animating .page-sheet {
    box-shadow: 0 0 0 1px rgba(var(--shadow-rgb), .07);
}

/* Пока идёт раскрытие, гасим и наведение на соседние карточки:
   лишние перерисовки в те же кадры. */
html.acc-animating details.acc {
    transition: none;
}

/* -------------------------------------------------------------------
   v48 — ВИДИМОСТЬ ЧЕРТЕЖА

   Знак не показывался вовсе: в разметке стоял класс lg:block, а в
   этой сборке Tailwind правил для брейкпоинта lg нет — есть только
   sm и md. Элемент оставался display:none на любой ширине.
   Управление видимостью переносим сюда, чтобы не зависеть от того,
   какие утилиты попали в сборку.
   ------------------------------------------------------------------- */
.hero-watermark--fleet {
    display: none;
}

@media (min-width: 1024px) {
    .hero-watermark--fleet {
        display: block;
    }
}

/* -------------------------------------------------------------------
   v49 — ФОНОВЫЙ СЛОЙ В ОТДЕЛЬНЫЙ КОМПОЗИТНЫЙ СЛОЙ

   Под всей страницей лежит body::before — прибитый к окну слой из
   трёх наложенных градиентов с color-mix. Он неподвижен, но браузер
   не знает об этом и перерисовывает его всякий раз, когда меняется
   раскладка страницы. При анимации высоты это шестьдесят полных
   перерисовок окна в секунду поверх всего остального.

   translateZ выносит слой на видеокарту: он растрируется один раз и
   дальше только показывается. Раскладка его больше не трогает.
   ------------------------------------------------------------------- */
body::before {
    transform: translateZ(0);
    will-change: transform;
}

/* На время раскрытия ответа замораживаем переходы у соседей: пока едет
   высота, страница под аккордеоном двигается, элементы пересекают край
   экрана, и наблюдатель появления навешивает им свои анимации. Три-четыре
   таких запуска в те же кадры — и раскрытие спотыкается. */
html.acc-animating .reveal,
html.acc-animating .reveal-up,
html.acc-animating .work-card,
html.acc-animating .card {
    transition: none !important;
}

/* -------------------------------------------------------------------
   v50 — ДЕЛО БЫЛО НЕ В КАДРАХ

   Замер на живом сайте показал шестьдесят кадров в секунду, худший
   кадр 17 мс, ноль затыков. Анимация шла ровно — и всё равно
   выглядела рваной. Причин оказалось две, и обе про восприятие.

   Первая: время одно, а расстояние разное. Короткий ответ проезжал
   80 пикселей, длинный — 400, оба за треть секунды. У длинного
   скорость впятеро выше, шаг между соседними кадрами впятеро
   крупнее, и глаз читает крупный шаг как рывки. Теперь длительность
   считает скрипт от высоты ответа: 220 мс плюс по 0,62 мс на
   пиксель, в границах от 300 до 560 мс. Скорость выходит одинаковой
   у всех вопросов.

   Вторая: кривая. cubic-bezier(.32,.72,0,1) — резкое торможение:
   движение выстреливает в первые сто миллисекунд, а последние
   пиксели доползают. Со стороны «дёрнулось и застряло». Ставим
   ровную кривую, где разгон и торможение примерно равны.

   Заодно текст внутри перестаёт ехать отдельно от рамки. Он
   двигался на шесть пикселей вверх в те же кадры, что и высота, —
   две разные скорости на одном тексте мылят картинку. Осталось
   только проявление.
   ------------------------------------------------------------------- */
.acc-body {
    transition-timing-function: cubic-bezier(.37, .16, .27, 1);
}

.acc-body > * {
    transform: none;
    transition: opacity .3s ease .06s;
}

details.acc.is-open .acc-body > * {
    transform: none;
}

/* -------------------------------------------------------------------
   v51 — ЯКОРЬ ПРОКРУТКИ ПРОТИВ АНИМАЦИИ ВЫСОТЫ

   Замер показал ровные шестьдесят кадров и ноль затыков, а движение
   всё равно читается рваным. Осталась одна причина, которая как раз
   так себя и ведёт: браузер помогает нам и тем самым мешает.

   Chrome умеет удерживать место чтения. Когда над видимой областью
   что-то вырастает, он подкручивает прокрутку на ту же величину,
   чтобы текст под пальцем не убегал. Механизм называется scroll
   anchoring и обычно незаменим — например, когда сверху догружается
   картинка.

   Но во время раскрытия ответа высота растёт шестьдесят раз в
   секунду, и браузер шестьдесят раз подкручивает прокрутку вслед.
   Кадры при этом не теряются — каждый нарисован вовремя. Просто в
   каждом из них страница едет сама, поверх нашей анимации, и две
   независимые скорости складываются в дрожание. Отсюда и ощущение
   низкого фпс при честных шестидесяти, и одинаковый эффект на
   длинных и коротких ответах.

   Внутри аккордеона якорь выключаем: место чтения тут держит сам
   заголовок вопроса, он неподвижен. На остальной странице механизм
   продолжает работать.
   ------------------------------------------------------------------- */
details.acc,
.acc-body,
.acc-body > *,
#faq,
#faq * {
    overflow-anchor: none;
}

/* -------------------------------------------------------------------
   v52 — ОБВОДКА ФОКУСА ТОЛЬКО ДЛЯ КЛАВИАТУРЫ

   При клике мышью по вопросу вокруг заголовка вспыхивала оранжевая
   рамка и держалась, пока не кликнешь мимо. Так браузер помечает
   элемент, получивший фокус: у <summary> он считает фокус «видимым»
   даже после щелчка мышью, потому что это интерактивный виджет, а
   не поле ввода.

   Обводка нужна тем, кто ходит по сайту с клавиатуры, — им без неё
   не понять, где они находятся. Поэтому не убираем её совсем, а
   гасим только тогда, когда фокус пришёл от мыши или пальца: скрипт
   помечает такой случай признаком data-ptr и снимает его при первом
   же нажатии клавиши.
   ------------------------------------------------------------------- */
details.acc[data-ptr] > summary:focus-visible,
.tab-btn[data-ptr]:focus-visible,
.btn[data-ptr]:focus-visible {
    outline: 0;
}

/* -------------------------------------------------------------------
   v53 — ОБЯЗАТЕЛЬНЫЕ ПОЛЯ ЗАЯВКИ

   Звёздочка помечает три поля: имя, телефон и согласие. Остальное
   человек заполняет по желанию, и теперь это видно до отправки, а не
   после отказа формы.

   Для читалок экрана звёздочки скрыты — там обязательность и так
   объявлена атрибутом required у самого поля, а лишний символ в
   подписи читался бы как «имя звёздочка».
   ------------------------------------------------------------------- */
.req {
    color: var(--o-700);
    margin-left: .15em;
    font-weight: 700;
}

.consent .req {
    margin-left: .1em;
}

/* -------------------------------------------------------------------
   v54 — КНОПКИ В ШАПКЕ ГЛАВНОЙ В ОДНУ СТРОКУ

   Две кнопки не помещались рядом и переносились одна под другую:
   колонка с текстом узкая, а надписи длинные. От планшета и шире
   запрещаем перенос и слегка ужимаем поля — обе встают в строку.
   На телефоне перенос остаётся, там иначе никак.
   ------------------------------------------------------------------- */
@media (min-width: 768px) {
    .hd-cta {
        flex-wrap: nowrap;
    }

    .hd-cta .btn {
        white-space: nowrap;
        padding-left: 1.15rem;
        padding-right: 1.15rem;
    }
}

/* Тема обращения до выбора — приглушённая подпись-заглушка, как
   placeholder у обычного поля. */
.cselect-placeholder {
    color: var(--text-mut);
}

/* -------------------------------------------------------------------
   v55 — ТРИ МЕЛОЧИ, КОТОРЫЕ ПОРТИЛИ КАРТИНКУ

   1. ГАЛОЧКА СОГЛАСИЯ

   Птичка рисовалась двумя границами прямоугольника 5x9, повёрнутого
   на 45 градусов, и позиционировалась жёстко — left 5px, top 1px.
   При повороте центр тяжести фигуры смещается, поэтому птичка сидела
   в квадрате криво, ближе к левому нижнему углу.

   Теперь она привязана к центру и сдвинута вверх на ту величину, на
   которую поворот утягивает её вниз. Квадрат чуть крупнее — 20 пикселей
   вместо 18: на 18 птичка упиралась в границы.

   2. АКЦЕНТНАЯ ПОЛОСКА У ОТКРЫТОГО ВОПРОСА

   Стояла в пяти пикселях от края, внутри скруглённого угла карточки, и
   читалась как случайно приставленная палка. Прижимаем её к самому краю,
   скругляем только справа — получается язычок, выходящий из грани
   карточки, а не предмет рядом с ней. Плоский цвет заменён на градиент
   сверху вниз, от светлого к тёмному.

   3. КРЕСТИК

   Полоски центровались процентами: в кружке 28 пикселей полоска 10 —
   это 9 пикселей отступа с каждой стороны, дробное значение. Браузер
   рисовал их между пикселями, отсюда размытость и кривизна, особенно
   заметная при повороте на 45 градусов.

   Задаём отступы целыми числами: полоска 12 в кружке 28 — ровно 8 слева
   и 13 сверху. Границы попадают на пиксельную сетку, крестик становится
   чётким.
   ------------------------------------------------------------------- */

.consent-box {
    width: 1.25rem;
    height: 1.25rem;
    border-radius: 6px;
}

.consent-box::after {
    left: 50%;
    top: 50%;
    width: 6px;
    height: 10px;
    border-width: 0 2px 2px 0;
    border-radius: 1px;
    transform: translate(-50%, -58%) rotate(45deg) scale(.4);
    transform-origin: center;
}

.consent input:checked + .consent-box::after {
    transform: translate(-50%, -58%) rotate(45deg) scale(1);
}

details.acc::before {
    left: 0;
    top: .75rem;
    bottom: .75rem;
    width: 4px;
    border-radius: 0 999px 999px 0;
    background-color: transparent;
    background-image: linear-gradient(180deg, var(--o-500) 0%, var(--o-700) 100%);
    transform: scaleY(.4);
    transform-origin: center;
    transition: opacity .26s ease, transform .3s var(--ease);
}

details.acc.is-open::before {
    transform: scaleY(1);
}

.acc-plus::after,
.acc-plus::before {
    top: 13px;
    left: 8px;
    width: 12px;
    height: 2px;
}

.acc-plus::before {
    transform: none;
}

.acc-plus::after {
    transform: rotate(90deg);
}

/* -------------------------------------------------------------------
   v55 — ГАЛОЧКА СОГЛАСИЯ

   Была нарисована двумя границами уголка, повёрнутого на 45 градусов,
   и подогнана по месту сдвигом на минус 58 процентов по высоте. Такой
   уголок ровным не бывает: у него разная толщина линий на сгибе, а
   подгонка процентами разъезжается при другом масштабе экрана.

   Заменяем на настоящий контур из SVG — он центрируется сам и
   остаётся чистым при любом увеличении.

   Полоску слева и знак раскрытия в вопросах трогать не стали: они
   возвращены к прежнему виду по просьбе — тому, что был после
   разбора с кадрами.
   ------------------------------------------------------------------- */
.consent-box::after {
    content: none;
}

.consent input:checked + .consent-box {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E");
    background-size: 12px 12px;
    background-position: center;
    background-repeat: no-repeat;
}

/* -------------------------------------------------------------------
   v58 — РАСКРЫТИЕ БЕЗ ПЕРЕСЧЁТА СТРАНИЦЫ

   Замер показал: стиль и раскладка почти бесплатны, всё время уходит
   в отрисовку. Причина не в сложности картинки, а в её площади:
   пока едет высота, содержимое под ответом сдвигается вниз, и браузер
   каждый кадр закрашивает почти всё окно заново.

   Это давняя и известная беда — анимировать высоту нельзя, сколько ни
   оптимизируй. Лечится приёмом FLIP, описанным Полом Льюисом: вместо
   того чтобы менять раскладку понемногу каждый кадр, её меняют один
   раз, целиком, а потом сдвигают всё обратно трансформом и отпускают.
   Трансформ и прозрачность — единственные два свойства, которые
   браузер считает на видеокарте, без раскладки и перекраски.

   Как это работает у нас. По клику ответ мгновенно получает свою
   настоящую высоту — раскладка пересчитывается ровно один раз. Затем
   всё, что лежит ниже по документу, поднимается трансформом на ту же
   величину, как будто ничего не открывалось. И уже отсюда отпускается
   вниз: карточки съезжают на свои места, а текст ответа выезжает
   из-под заголовка. Ни раскладки, ни перекраски в этих кадрах нет —
   только перемещение готовых слоёв.

   При закрытии наоборот: сначала едем трансформом, а раскладку
   схлопываем в последнем кадре, когда движение уже закончилось.

   will-change ставится на время движения и снимается сразу после,
   чтобы слои не висели в памяти.
   ------------------------------------------------------------------- */
.acc-body {
    overflow: hidden;
    height: 0;
    transition: none;
    will-change: auto;
}

details.acc[open] .acc-body {
    height: auto;
}

.acc-inner {
    will-change: auto;
}

/* Содержимое больше не проявляется по частям: оно едет одним блоком
   вместе с высотой, поэтому прежние переходы у детей отключаем. */
.acc-body > *,
.acc-inner > * {
    opacity: 1;
    transform: none;
    transition: none;
}

/* -------------------------------------------------------------------
   v58 — РАСКРЫТИЕ БЕЗ РАСКЛАДКИ И БЕЗ ПЕРЕКРАСКИ

   Замер показал: стиль 0,1 мс, раскладка 1 мс, всё остальное время —
   отрисовка. Видеоускорение при этом включено. Значит, дело не в
   железе и не в сложности правил, а в самом приёме: анимация высоты
   заставляет браузер каждый кадр заново закрашивать всё, что ниже
   ответа.

   Это известная беда, и у неё известное лечение. В руководстве
   Chrome по производительности сказано прямо: ширину и высоту
   анимировать нельзя, они требуют раскладки и отрисовки на каждом
   кадре; надо переводить эффект на transform, который целиком
   считает видеокарта.

   Приём называется FLIP, по первым буквам четырёх шагов:

     First   — запоминаем, где всё лежит сейчас;
     Last    — мгновенно применяем конечное состояние;
     Invert  — трансформами возвращаем всё туда, где было;
     Play    — отпускаем трансформы в ноль.

   Раскладка при этом случается ровно один раз, между вторым и
   третьим шагом, а дальше едут только трансформы: ни пересчёта
   координат, ни перекраски. Содержимое ответа выезжает из-под
   заголовка, а всё, что ниже, одновременно съезжает вниз.

   Высоту больше не анимируем совсем — она меняется мгновенно, но
   этого не видно, потому что глаз следит за движением трансформов.
   ------------------------------------------------------------------- */
.acc-body {
    transition: none;
}

.acc-inner {
    display: block;
}

/* Пока едет анимация, содержимое не должно выезжать за карточку. */
.acc-body {
    overflow: hidden;
}

/* -------------------------------------------------------------------
   v59 — МЕЛОЧИ ОТКРЫТОЙ КАРТОЧКИ

   1. Рамка вокруг заголовка. Chrome считает <summary> клавиатурным
   виджетом и после щелчка мышью оставляет на нём рамку фокуса. Прежний
   приём с признаком data-ptr срабатывал не всегда, поэтому теперь
   скрипт просто снимает фокус, если клик пришёл от указателя: у клика
   мышью свойство detail больше нуля, у нажатия Enter — ноль. Для
   клавиатуры рамка остаётся, там без неё не сориентироваться.

   2. Черта над ответом. Раньше она рисовалась над каждым абзацем, а
   после появления внутренней обёртки осталась одна, зато прижалась к
   самому верху и повисла отдельной палкой. Отодвигаем её от заголовка
   и подгоняем отступы по краям к отступам текста.

   3. Полоска слева у открытой карточки не должна выходить за
   скруглённый угол — прижимаем её к внутреннему полю.
   ------------------------------------------------------------------- */
.acc-inner {
    position: relative;
    padding-top: .35rem;
}

.acc-body > ::before {
    left: 1.5rem;
    right: 1.5rem;
    height: 1px;
    opacity: .7;
}

details.acc::before {
    left: 8px;
    top: 1.25rem;
    bottom: 1.25rem;
}

/* -------------------------------------------------------------------
   v60 — ОТКРЫТАЯ КАРТОЧКА ВОПРОСА, ЧИСТОВИК

   Три правки, все с запасом по весу правил, чтобы наверняка перекрыть
   всё, что накопилось выше.

   1. РАМКА ФОКУСА. Больше не полагаемся на :focus-visible: у элемента
   <summary> Chrome считает фокус видимым и после щелчка мышью, отсюда
   и вспышки при работе указателем. Теперь рамка появляется только
   тогда, когда человек действительно взялся за клавиатуру: скрипт
   ставит признак kbd по нажатию Tab, Enter, пробела или стрелок и
   снимает его при первом же касании мыши. Поведение перестаёт зависеть
   от догадок браузера.

   2. БОКОВАЯ ПОЛОСКА. Была прижата к нулю по горизонтали, вылезала за
   скруглённый угол и упиралась в рамку фокуса. Убираем внутрь на десять
   пикселей, скругляем с обеих сторон, даём переход цвета сверху вниз и
   держим ровно по высоте карточки.

   3. ЧЕРТА НАД ОТВЕТОМ убрана совсем. Внутри карточки, у которой уже
   есть рамка и своя заливка, лишняя линия только дробила текст. Заодно
   исчез отступ, из-за которого ответ казался сдвинутым вниз.
   ------------------------------------------------------------------- */

/* 1. Рамка — только для клавиатуры */
details.acc > summary:focus,
details.acc > summary:focus-visible {
    outline: 0;
}

html.kbd details.acc > summary:focus-visible {
    outline: 2px solid var(--o-600);
    outline-offset: -4px;
    border-radius: var(--r-md);
}

/* 2. Полоска */
details.acc::before,
details.acc.is-open::before {
    left: 10px;
    top: .75rem;
    bottom: .75rem;
    width: 3px;
    border-radius: 999px;
    background-color: transparent;
    background-image: linear-gradient(180deg,
            var(--o-500) 0%,
            var(--o-600) 50%,
            var(--o-700) 100%);
}

details.acc::before {
    opacity: 0;
    transform: scaleY(.3);
    transform-origin: center;
    transition: opacity .3s ease, transform .38s cubic-bezier(.22, .61, .36, 1);
}

details.acc.is-open::before {
    opacity: 1;
    transform: scaleY(1);
}

/* 3. Никаких черт внутри карточки */
.acc-body > ::before,
.acc-inner::before {
    content: none;
}

.acc-inner {
    padding-top: 0;
}

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

/* -------------------------------------------------------------------
   v62 — ВИЗУАЛ СТРОК: ЗНАЧОК, ОТСТУПЫ, МЕТКА

   1. ЗНАЧОК. Плюс собирался из двух палочек, которые надо совмещать
   по пикселю, и поворачивался на 45 градусов — при дробном масштабе
   экрана обе ошибки видны сразу, отсюда «кривые крестики». Ставим
   вместо него шеврон одним контуром из SVG: он центрируется сам,
   поворачивается на 180 градусов, а на такой поворот размытия по
   краям не бывает. Так делают почти все — шеврон в конце строки
   называют самым узнаваемым значком и в разборах Mobbin, и у
   LogRocket.

   2. РАЗМЕР СТРОКИ. Было по 1,5 rem со всех сторон — на глаз строка
   получалась выше, чем нужно, особенно в списке вакансий, где
   заголовки короткие. Уменьшаем до 1,05 rem по вертикали и держим
   одинаково в обоих списках: разметка у них общая, значит и правило
   одно.

   3. ОТСТУП ОТВЕТА. После того как убрали черту-разделитель, текст
   прилип к заголовку. Опускаем его на полсантиметра и выравниваем
   боковые поля с полями заголовка, чтобы левый край текста и левый
   край вопроса стояли на одной линии.

   4. МЕТКА СЛЕВА. Полоса во всю высоту карточки спорила со
   скруглением угла и тянулась вдоль всего ответа без всякой цели.
   Заменяем короткой меткой у самого вопроса: она отмечает строку, а
   не обводит блок.
   ------------------------------------------------------------------- */

/* 1. Шеврон */
.acc-plus {
    background-color: transparent;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23586569' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5l4 4 4-4'/%3E%3C/svg%3E");
    background-size: 13px 13px;
    background-position: center;
    background-repeat: no-repeat;
    transition: transform .34s cubic-bezier(.22, .61, .36, 1), background-color .3s ease, border-color .3s ease;
}

.acc-plus::before,
.acc-plus::after {
    content: none;
}

details.acc.is-open .acc-plus {
    transform: rotate(180deg);
    background-color: var(--tint-1);
    border-color: var(--tint-3);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23B4470E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5l4 4 4-4'/%3E%3C/svg%3E");
}

/* 2. Строка ниже ростом */
details.acc > summary {
    padding: 1.05rem 1.15rem;
    align-items: center;
}

@media (min-width: 768px) {
    details.acc > summary {
        padding: 1.05rem 1.35rem;
    }
}

/* 3. Отступы ответа под заголовок */
details.acc .acc-body > .acc-inner > div,
details.acc .acc-body > div {
    padding: .55rem 1.15rem 1.15rem;
}

@media (min-width: 768px) {

    details.acc .acc-body > .acc-inner > div,
    details.acc .acc-body > div {
        padding: .55rem 1.35rem 1.3rem;
    }
}

/* 4. Короткая метка вместо полосы */
details.acc::before {
    left: 0;
    top: 1.15rem;
    bottom: auto;
    height: 1.35rem;
    width: 3px;
    border-radius: 0 999px 999px 0;
    background-color: transparent;
    background-image: linear-gradient(180deg, var(--o-500), var(--o-700));
    opacity: 0;
    transform: scaleY(.4);
    transform-origin: center;
    transition: opacity .28s ease, transform .34s cubic-bezier(.22, .61, .36, 1);
}

details.acc.is-open::before {
    opacity: 1;
    transform: scaleY(1);
}

@media (prefers-reduced-motion: reduce) {

    .acc-plus,
    details.acc::before {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v63 — ПЛЮС ВМЕСТО ШЕВРОНА, БЕЗ ПОЛОСКИ, БЕЗ НАЛОЖЕНИЯ

   1. ЗНАЧОК. Возвращаем плюс — он нравился больше, ломала его не
   форма, а сборка: две палочки сажались жёсткими отступами в
   пикселях, центр уезжал, и поворот на 45 градусов эту ошибку
   показывал во всей красе. Теперь плюс нарисован одним контуром в
   SVG и центрируется браузером сам. Поворот на 45 градусов даёт
   ровный крестик, потому что поворачивать нечего — фигура одна.

   2. ПОЛОСКА СЛЕВА убрана совсем. Я переделывал её четыре раза, и
   каждый раз она за что-нибудь цеплялась: то за скруглённый угол, то
   за рамку фокуса, то за тень. Открытую строку и без неё видно —
   заливка карточки становится тёплой, значок краснеет. Добавляем
   третий признак: сам вопрос тоже становится оранжевым. Три сигнала
   вместо четырёх, и ни один не может съехать.

   3. НАЛОЖЕНИЕ. При раскрытии соседние карточки на миг оказывались
   поверх фона растущей — они едут на собственных слоях, а слой
   всегда рисуется выше обычного содержимого. Скрипт теперь поднимает
   раскрывающуюся карточку над соседями на время движения, и они
   уезжают вниз из-под неё, а не по ней.
   ------------------------------------------------------------------- */

/* 1. Плюс одним контуром */
.acc-plus {
    background-color: transparent;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23586569' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M8 3.5v9M3.5 8h9'/%3E%3C/svg%3E");
    background-size: 12px 12px;
    background-position: center;
    background-repeat: no-repeat;
    transition: transform .34s cubic-bezier(.22, .61, .36, 1), background-color .3s ease, border-color .3s ease;
}

details.acc.is-open .acc-plus {
    transform: rotate(45deg);
    background-color: var(--tint-1);
    border-color: var(--tint-3);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23B4470E' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M8 3.5v9M3.5 8h9'/%3E%3C/svg%3E");
}

/* 2. Полоски нет, вопрос открытой строки краснеет */
details.acc::before {
    content: none;
}

details.acc.is-open > summary > span:first-child {
    color: var(--o-700);
    transition: color .3s ease;
}

/* 3. Растущая карточка — поверх соседей */
details.acc {
    position: relative;
}

/* -------------------------------------------------------------------
   v64 — ВОЗВРАТ К ПРОСТОМУ РАСКРЫТИЮ И ЛИНИЯ НА МЕСТЕ

   Затея с трансформами провалилась не по скорости, а по поведению:
   закрытие оставалось мгновенным, а текст на старте выезжал из-под
   заголовка, и это читалось как обрезка. Возвращаемся к анимации
   высоты — она едет в обе стороны одинаково и ничего не режет.

   Линия слева возвращается такой, какой была: во всю высоту карточки,
   с переходом цвета сверху вниз, скруглённая, отступя от края.

   Значок в открытом состоянии больше не заливается фоном: с заливкой
   кружок читался крупнее, чем закрытый, хотя размер не менялся.
   Остаётся поворот и цвет.
   ------------------------------------------------------------------- */
.acc-body {
    overflow: hidden;
    height: 0;
    transition: height .42s cubic-bezier(.22, .61, .36, 1);
}

details.acc[open] .acc-body {
    height: auto;
}

/* Линия */
details.acc::before {
    content: "";
    position: absolute;
    left: 9px;
    top: .9rem;
    bottom: .9rem;
    width: 3px;
    border-radius: 999px;
    background-color: transparent;
    background-image: linear-gradient(180deg,
            var(--o-500) 0%,
            var(--o-600) 55%,
            var(--o-700) 100%);
    opacity: 0;
    transform: scaleY(.35);
    transform-origin: center;
    transition: opacity .3s ease, transform .42s cubic-bezier(.22, .61, .36, 1);
}

details.acc.is-open::before {
    opacity: 1;
    transform: scaleY(1);
}

/* Значок: тот же размер в обоих состояниях */
details.acc.is-open .acc-plus {
    background-color: transparent;
    border-color: var(--tint-3);
}

@media (prefers-reduced-motion: reduce) {

    .acc-body,
    details.acc::before {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v65 — СПИСКИ ВОПРОСОВ И ВАКАНСИЙ, СОБРАНО ЗАНОВО

   Основание для переделки — замер на машине заказчика: расчёт стилей
   0,1 мс, раскладка 1 мс, отрисовка 27 мс. Тормозила краска, и вот
   что про неё пишут.

   Chrome for Developers, статья про режим непрерывной отрисовки:
   сочетание box-shadow и border-radius на одном элементе даёт очень
   дорогую отрисовку, потому что Chrome не умеет её оптимизировать;
   отключение любого из двух свойств заметно сокращает время. Там же
   на web.dev, в разборе стоимости свойств: комбинации стоят дороже
   суммы частей, и пример приводится ровно этот — тень плюс радиус.

   У нас было именно так. Семь карточек вопросов, каждая с радиусом и
   трёхслойной тенью; под ними лист страницы, тоже с радиусом и тенью
   размытием в девяносто пикселей на четыре тысячи пикселей высоты.
   Раскрытие двигает всё это вниз, и каждый кадр перерисовывается
   самая дорогая из возможных комбинаций.

   Поэтому список собран заново по другому принципу — тому, который
   рекомендуют обзоры аккордеонов 2026 года: структура задаётся
   отступами и тонкими линиями, а не коробками с тенями.

     — один контейнер с рамкой и скруглением, без тени;
     — строки внутри плоские: ни радиуса, ни тени, только волосяная
       линия между соседями;
     — открытая строка отмечается заливкой, оранжевым заголовком и
       короткой линией слева — всё плоское, всё дешёвое;
     — на время раскрытия тени снимаются и с листа страницы, и с
       остальных карточек, которые попали в кадр.

   Тени и радиусы никуда не делись со страницы — они остались там,
   где ничего не движется. Убраны ровно у того, что перерисовывается
   при каждом кадре анимации.
   ------------------------------------------------------------------- */
.qa {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    overflow: hidden;
}

.qa > details.acc {
    margin: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    transform: none;
    transition: background-color .28s ease;
}

.qa > details.acc + details.acc {
    border-top: 1px solid var(--line);
}

.qa > details.acc.is-open {
    background: var(--tint-1);
}

.qa > details.acc:not(.is-open):hover {
    background: rgba(var(--shadow-rgb), .022);
}

/* Заголовок строки */
.qa > details.acc > summary {
    padding: 1.05rem 1.1rem 1.05rem 1.35rem;
    align-items: center;
    gap: 1rem;
    background: transparent;
}

.qa > details.acc > summary:hover {
    background: transparent;
}

.qa > details.acc > summary > span:first-child {
    transition: color .28s ease;
}

.qa > details.acc > summary:hover > span:first-child {
    transform: none;
}

.qa > details.acc.is-open > summary > span:first-child {
    color: var(--o-700);
}

/* Короткая линия слева — плоская, без тени и радиуса */
.qa > details.acc::before {
    content: "";
    position: absolute;
    left: 0;
    top: 1.15rem;
    bottom: auto;
    height: 1.25rem;
    width: 3px;
    border-radius: 0;
    background: var(--o-600);
    opacity: 0;
    transform: none;
    transition: opacity .26s ease;
}

.qa > details.acc.is-open::before {
    opacity: 1;
}

/* Тело ответа */
.qa .acc-body > .acc-inner > div,
.qa .acc-body > div:not(.acc-inner) {
    padding: 0 1.35rem 1.2rem;
}

.acc-inner {
    padding: 0;
    display: block;
}

/* На время движения снимаем тени со всего, что попадает в кадр */
html.acc-animating .page-sheet,
html.acc-animating .card,
html.acc-animating .panel,
html.acc-animating .work-card {
    box-shadow: none;
}

@media (min-width: 768px) {
    .qa > details.acc > summary {
        padding: 1.1rem 1.25rem 1.1rem 1.5rem;
    }

    .qa .acc-body > .acc-inner > div,
    .qa .acc-body > div:not(.acc-inner) {
        padding: 0 1.5rem 1.35rem;
    }

    .qa > details.acc::before {
        top: 1.2rem;
    }
}

/* -------------------------------------------------------------------
   v66 — ТОЛЬКО TRANSFORM И OPACITY, В ОБЕ СТОРОНЫ

   Возвращаемся к тому, на что указывали статьи, и доводим до конца.

   web.dev, руководство по высокопроизводительной анимации: браузер
   рисует в три этапа — раскладка, отрисовка, склейка слоёв. Анимация
   ширины и высоты запускает первые два на каждом кадре; анимация
   transform и opacity идёт целиком на склейке, минуя раскладку и
   отрисовку, — именно поэтому она держит шестьдесят кадров.

   Прошлый заход провалился не из-за приёма, а из-за того, что я
   применил его наполовину: закрытие оставалось мгновенным, потому
   что содержимое <details> исчезает в тот же миг, когда снимается
   признак open. Решение — не снимать его до конца анимации.

   Как теперь устроено закрытие:
     меряем, где лежат соседи сейчас и где окажутся после схлопывания;
     карточка остаётся открытой, соседи трансформом уезжают на новое
     место, ответ гаснет по прозрачности;
     и только когда движение закончилось, признак open снимается —
     раскладка встаёт ровно туда, где всё уже нарисовано.

   Открытие — то же самое наоборот: раскладка применяется сразу,
   соседи трансформом возвращаются назад и оттуда едут к нулю.

   Высота не анимируется нигде. За кадр меняются только transform у
   соседей и opacity у ответа — обе величины считает видеокарта.
   ------------------------------------------------------------------- */
.acc-body {
    transition: none;
    height: auto;
    overflow: hidden;
}

details.acc:not([open]) .acc-body {
    height: 0;
}

/* Раскрывающаяся строка на время движения поверх соседей: они едут на
   собственных слоях, а слой рисуется выше обычного содержимого. */
.qa > details.acc {
    position: relative;
}

/* z-index у открытой строки снят: он поднимал её над соседями, и текст
   ответа просвечивал сквозь карточки, которые в этот момент едут вниз.
   Теперь ответ срезается clip-path и наружу не выходит вовсе. */

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

/* -------------------------------------------------------------------
   v67 — СРЕЗ ВМЕСТО ПРОЗРАЧНОСТИ

   Прошлый заход дал нужные кадры, но два артефакта: текст ответа
   просвечивал сквозь соседние строки, а внизу контейнера на время
   движения зияла пустота.

   Причина одна: тело ответа сразу получало полную высоту и просто
   гасло прозрачностью, а соседи в это время ехали поверх него. Плюс
   контейнер уже вырос до конечного размера, тогда как строки внутри
   ещё не доехали.

   Лечим срезом. Motion в разборе производительности и тир-листе
   2025 года относит clip-path к свойствам, которые современные
   браузеры считают на композиторе, — Chromium обещал это ещё в 2021
   и с тех пор добавил. Значит, срез можно анимировать наравне с
   трансформом.

   Ответ раскрывается срезом сверху вниз: текст стоит на месте, его
   просто становится видно всё больше, и за границы он не выходит —
   значит, ни просвечивать, ни налезать не может. Контейнер в это же
   время срезается снизу ровно на ту высоту, на которую ещё не
   доехали строки, — пустоты нет.

   Скругление контейнера скрипт берёт из вычисленных стилей и
   подставляет в срез, иначе на время анимации углы стали бы
   прямыми.
   ------------------------------------------------------------------- */
.acc-body {
    overflow: hidden;
}

details.acc:not([open]) .acc-body {
    height: 0;
}

@media (prefers-reduced-motion: reduce) {

    .acc-body,
    .qa {
        transition: none !important;
        clip-path: none !important;
    }
}

/* -------------------------------------------------------------------
   v68 — МАСШТАБ С КОМПЕНСАЦИЕЙ: КАНОНИЧЕСКИЙ ПРИЁМ

   Срез пришлось снять. Chrome for Developers в статье про раскрытие
   и схлопывание пишет прямо: анимировать clip и clip-path следует с
   осторожностью, а ширину и высоту — не следует вовсе; вместо этого
   предлагается приём с масштабированием. Причина в том, что срез
   считается не всегда на композиторе, и тогда он идёт по главному
   потоку, пока трансформы соседей идут по композиторному. Две
   анимации расходятся, и в кадре видно, как строка ниже наезжает на
   ещё не открывшийся текст — ровно то, что вы поймали.

   Приём из той же статьи: тело ответа сжимается по вертикали от нуля
   до единицы, а его внутренняя обёртка одновременно растягивается в
   обратную величину. Масштабы гасят друг друга, поэтому буквы не
   плывут — они стоят на месте, их просто становится видно больше.

   Обратная величина нелинейна, поэтому кривую нельзя отдать
   браузеру: она рассчитывается заранее, двадцатью пятью ключевыми
   кадрами, и для тела берётся прямая, для обёртки — обратная. Тем же
   набором кадров едут и соседние блоки. Все три анимации запускаются
   одним вызовом Web Animations с линейной интерполяцией между
   кадрами, то есть буквально по одному расписанию — расходиться им
   нечем.

   Ни высоты, ни среза: за кадр меняется только transform.
   ------------------------------------------------------------------- */
.acc-body {
    overflow: hidden;
    height: auto;
    transition: none;
}

details.acc:not([open]) .acc-body {
    height: 0;
}

@media (prefers-reduced-motion: reduce) {

    .acc-body,
    .acc-inner {
        transform: none !important;
    }
}

/* -------------------------------------------------------------------
   v69 — КАК ДЕЛАЮТ БИБЛИОТЕКИ

   Хватит изобретать. Посмотрел, как эту задачу решают те, кто делает
   готовые компоненты для всего мира.

   Radix Primitives отдаёт наружу переменную с измеренной высотой
   содержимого — --radix-accordion-content-height — и предлагает
   анимировать именно высоту. shadcn/ui в своей сборке аккордеона
   объявляет ровно два ключевых кадра: от нуля до этой переменной и
   обратно, длительность 0.2 s ease-out. В обсуждении Spartan NG
   отдельно решили отказаться от трюка с сеткой и перейти на тот же
   приём — «для единообразия и соответствия устоявшимся образцам».

   То есть индустрия анимирует высоту. И делает это коротко: двести
   миллисекунд. Короткое движение прощает то, чего не прощает долгое, —
   при двухсот миллисекундах даже просевшие кадры глаз не считает.

   У нас так же: скрипт измеряет высоту ответа, кладёт её в
   переменную, ставит признак состояния, а два ключевых кадра делают
   остальное. Длительность считается от высоты — от 180 до 320 мс.

   Ни масштабов, ни срезов, ни трансформов у соседей. Значит, нет и
   артефактов: ни призрачного текста, ни наложений, ни расхождения
   двух анимаций — анимация ровно одна.

   Заодно остаётся в силе вчерашняя чистка: у всего, что движется,
   нет сочетания тени со скруглением, которое Chrome не умеет
   оптимизировать. Именно это делает короткую анимацию высоты
   приемлемой по цене.
   ------------------------------------------------------------------- */
.acc-body {
    overflow: hidden;
    transform: none;
    transition: none;
    clip-path: none;
}

details.acc:not([open]) .acc-body {
    height: 0;
}

details.acc[open] .acc-body {
    height: auto;
}

.acc-body[data-state="open"] {
    animation: accDown var(--acc-dur, .2s) cubic-bezier(.22, .61, .36, 1);
}

.acc-body[data-state="closed"] {
    animation: accUp var(--acc-dur, .2s) cubic-bezier(.22, .61, .36, 1);
}

@keyframes accDown {
    from {
        height: 0;
    }

    to {
        height: var(--acc-h);
    }
}

@keyframes accUp {
    from {
        height: var(--acc-h);
    }

    to {
        height: 0;
    }
}

/* Текст ответа слегка проявляется — единственное, что едет помимо высоты,
   и оба свойства считает видеокарта. */
.acc-body[data-state="open"] > * {
    animation: accFade var(--acc-dur, .2s) ease-out;
}

@keyframes accFade {
    from {
        opacity: 0;
        transform: translateY(-4px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {

    .acc-body,
    .acc-body[data-state="open"],
    .acc-body[data-state="closed"],
    .acc-body[data-state="open"] > * {
        animation: none;
    }
}

/* -------------------------------------------------------------------
   v70 — КАДР ПРИ ЗАКРЫТИИ И ЗНАЧОК ПО ОБРАЗЦАМ

   1. ПРОСКОК НА ОДИН КАДР. Нашёлся: у схлопывающей анимации не было
   указано, что конечное состояние надо удержать. Анимация доезжала до
   нуля, отпускала высоту — а признак open ещё стоял, значит высота
   возвращалась к auto, и ответ на один кадр вспыхивал целиком. Только
   потом таймер снимал признак. Добавляем удержание конечного кадра,
   а снятие признака переводим с таймера на событие конца анимации:
   теперь порядок гарантирован браузером, а не совпадением по времени.
   Таймер остаётся страховкой, если событие не придёт.

   2. ЗНАЧОК. Отказываюсь от поворота на 45 градусов совсем. Диагональ
   при дробном масштабе экрана всегда мылит, и выигрыша в понятности
   она не даёт.

   Беру то, что используют библиотеки. shadcn в наборе аккордеонов
   держит вариант с плюсом и минусом и описывает его как более
   заметный, чем шеврон; там индикаторы переключаются плавно. AgnosticUI
   называет этот же приём классическим указателем состояния: плюс
   превращается в минус.

   Собран он двумя полосками, но не так, как было у нас: раньше их
   сажали дробными процентами и центр уезжал. Теперь обе заданы
   чётными размерами и целыми отступами от середины — 12 на 2 пикселя,
   сдвиг на 6 и 1. Попасть между пикселями невозможно арифметически.
   При открытии вертикальная полоска сжимается по ширине в ноль, и
   плюс становится минусом. Обе полоски всегда строго горизонтальны
   или вертикальны — размывать по краям нечего.
   ------------------------------------------------------------------- */

/* 1. Удержание конечного кадра */
.acc-body[data-state="open"] {
    animation-fill-mode: forwards;
}

.acc-body[data-state="closed"] {
    animation-fill-mode: forwards;
}

/* 2. Плюс и минус двумя полосками, целыми пикселями */
.acc-plus {
    background-image: none;
    transform: none;
}

.acc-plus::before,
.acc-plus::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 2px;
    margin: -1px 0 0 -6px;
    border-radius: 1px;
    background: currentColor;
    transform: none;
    transition: transform .26s cubic-bezier(.22, .61, .36, 1);
}

.acc-plus::after {
    transform: rotate(90deg);
}

details.acc.is-open .acc-plus {
    transform: none;
    background-image: none;
}

details.acc.is-open .acc-plus::after {
    transform: rotate(90deg) scaleX(0);
}

@media (prefers-reduced-motion: reduce) {

    .acc-plus::before,
    .acc-plus::after {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v71 — ТОНЬШЕ ЗНАЧОК, ЛИНИЯ ПО ОБРАЗЦАМ

   1. ТОЛЩИНА ПОЛОСОК. Было два пикселя. На экране с масштабом 125
   процентов два пикселя разметки — это два с половиной настоящих, то
   есть полоска всегда ложится на половину пикселя и слегка мылится.
   Ставим 1,6 px: при том же масштабе это ровно два настоящих пикселя,
   край выходит чистым. Заодно стало тоньше, как вы и просили. Отступ
   от середины тоже кратен: 0,8 px даёт ровно один настоящий.

   2. ЛИНИЯ СЛЕВА. Подсмотрел, как это делают в подборках готовых
   аккордеонов. Приём везде один и тот же: полоса во всю высоту
   заголовка, прижатая к левому краю, и рост через scaleY от нуля к
   единице с началом отсчёта у края — в примерах Subframe это
   `transform: scaleY(0)` с переходом по transform, в одном наборе
   начало сверху, в другом снизу.

   Беру этот приём с двумя уточнениями под наш случай. Во-первых,
   линия тянется на всю карточку, а не только на заголовок: у нас
   заголовок и ответ — одна строка списка, и линия связывает их.
   Во-вторых, рост идёт сверху вниз, вслед за раскрытием ответа, а не
   от середины: движение линии и движение содержимого читаются как
   одно.

   Толщина два пикселя, край прямой и заигрывает с рамкой контейнера,
   как в образцах. Скругления нет намеренно: именно оно в прошлых
   попытках спорило с углом карточки.
   ------------------------------------------------------------------- */

/* 1. Полоски значка */
.acc-plus::before,
.acc-plus::after {
    width: 11.2px;
    height: 1.6px;
    margin: -0.8px 0 0 -5.6px;
    border-radius: .8px;
}

/* 2. Линия открытой строки */
.qa > details.acc::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    height: auto;
    width: 2px;
    border-radius: 0;
    background: linear-gradient(180deg, var(--o-500), var(--o-700));
    opacity: 1;
    transform: scaleY(0);
    transform-origin: top center;
    transition: transform .34s cubic-bezier(.22, .61, .36, 1);
}

.qa > details.acc.is-open::before {
    transform: scaleY(1);
}

@media (prefers-reduced-motion: reduce) {
    .qa > details.acc::before {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v72 — ПОЧЕМУ ВАКАНСИИ ГЛАДКИЕ, А ВОПРОСЫ НЕТ

   Наблюдение заказчика: в вакансиях просадок нет, в вопросах есть.
   Код у них общий, значит дело не в коде, а в том, где они стоят.

   Вакансии раскрываются внутри всплывающего окна. Окно прибито к
   экрану, вне потока документа, поэтому его рост не двигает страницу
   и перерисовывать нечего — только сам список.

   Вопросы стоят в середине страницы, и под ними лежит ещё двести
   тридцать семь элементов: полоса с призывом, панель контактов с
   фотографией карты, тёмная панель вакансий, подвал. Каждый кадр
   раскрытия сдвигает всё это, и всё это перерисовывается.

   Убрать сдвиг нельзя — на этом стоит сама идея аккордеона. Но можно
   сделать перерисовку дешевле. На время движения:

     — снимаем фотофактуру с фона страницы: на широких экранах под
       листом лежит текстура, которая при каждом изменении высоты
       перекладывается заново;
     — снимаем снимок карты с кнопки: это отдельная картинка в тысячу
       пикселей шириной, и она попадает в кадр;
     — разделам ниже даём contain: paint — браузер получает право
       считать их отрисовку замкнутой в своих границах и не проверять
       выход за края.

   Всё это только на те двести-триста миллисекунд, пока идёт
   раскрытие, и возвращается сразу после. На глаз подмена не видна:
   текстура фона и снимок карты в этот момент едут вместе со
   страницей.
   ------------------------------------------------------------------- */
html.acc-animating body,
html.acc-animating .map-embed--photo {
    background-image: none;
}

html.acc-animating .page-sheet > section,
html.acc-animating .page-sheet > .cta-strip,
html.acc-animating body > footer {
    contain: paint;
}

html.acc-animating .panel,
html.acc-animating .panel-warm,
html.acc-animating .panel-dark,
html.acc-animating .cta-strip {
    box-shadow: none;
}

/* -------------------------------------------------------------------
   v74 — ВОПРОСЫ ПЕРЕЕХАЛИ НА ОТДЕЛЬНУЮ СТРАНИЦУ

   Выбран вариант с отдельной страницей. Причина просадки уходит сама:
   на /faq под списком вопросов только подвал, поэтому раскрытие почти
   ничего не сдвигает и перерисовывать нечего. Аккордеон остаётся
   привычным, окно и накладка больше не нужны.

   Заодно у каждого вопроса появляется свой адрес, а разметка FAQPage
   теперь лежит на странице, которая целиком про вопросы, — для
   поисковика это честнее, чем прятать её внутри страницы о компании.
   ------------------------------------------------------------------- */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* -------------------------------------------------------------------
   v75 — ПОТОЛОК РАСКРЫТИЯ И ПРОКРУТКА ВНУТРИ

   Идея заказчика, и она бьёт в саму причину. Страница дёргалась
   потому, что раскрытие двигало вниз всё, что под ним, а двигало тем
   сильнее, чем длиннее ответ. Самый длинный у нас — полторы тысячи
   знаков, это почти пятьсот пикселей сдвига.

   Теперь у блока есть потолок: он раскрывается не больше чем на
   300 пикселей, а на невысоких экранах — не больше чем на 42 процента
   высоты окна, чтобы вопрос и ответ помещались вместе. Что не влезло,
   листается внутри блока.

   Сдвиг страницы стал вдвое меньше и, главное, одинаковым для всех
   вопросов: короткий ответ двигает на свою высоту, длинный — ровно на
   потолок, не больше. Перерисовывать приходится меньше, и разброса
   между строками больше нет.

   Прокрутка появляется только когда она нужна: скрипт сравнивает
   высоту содержимого с потолком и ставит признак. Внизу блока
   появляется мягкое затемнение — по нему видно, что текст
   продолжается. Прокрутка колеса внутри блока не перескакивает на
   страницу, когда текст закончился.
   ------------------------------------------------------------------- */
.acc-body.acc-scroll {
    max-height: var(--acc-cap);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--line-warm) transparent;
    -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 2.25rem), transparent 100%);
    mask-image: linear-gradient(180deg, #000 calc(100% - 2.25rem), transparent 100%);
}

.acc-body.acc-scroll::-webkit-scrollbar {
    width: 6px;
}

.acc-body.acc-scroll::-webkit-scrollbar-thumb {
    background: var(--line-warm);
    border-radius: 999px;
}

/* Запас снизу, чтобы последняя строка не упиралась в край и не
   пряталась под затемнением. */
.qa .acc-body.acc-scroll > div {
    padding-bottom: 2.5rem;
}

/* Пояснение к правке: переменная --acc-h живёт только на время
   ключевых кадров и снимается сразу после них. Если привязать потолок
   к ней, он исчезал бы в момент окончания анимации, и блок прыжком
   разворачивался на всю высоту. Поэтому потолок держит отдельная
   переменная --acc-cap: её скрипт ставит при раскрытии и снимает
   только при закрытии. */

/* -------------------------------------------------------------------
   v76 — РАЗМЫТАЯ ПОДЛОЖКА ЯНДЕКС-КАРТЫ

   Снимок карты на кнопке размыт намеренно: до клика он работает как
   намёк на карту, а не как карта. Улицы и подписи всё равно нечитаемы
   в таком размере, зато размытие снимает главную странность прежнего
   варианта — попытку выдать картинку за рабочую карту.

   Подпись правообладателя вынесена текстом в угол кнопки: на размытом
   снимке та, что была вшита в кадр, читаться перестала.
   ------------------------------------------------------------------- */
.map-embed-attr {
    position: absolute;
    right: .6rem;
    bottom: .5rem;
    font-size: .625rem;
    letter-spacing: .02em;
    color: rgba(255, 255, 255, .78);
    text-shadow: 0 1px 2px rgba(0, 0, 0, .55);
    pointer-events: none;
}

.map-embed:not(.map-embed--dark) .map-embed-attr {
    color: rgba(255, 255, 255, .85);
}

/* -------------------------------------------------------------------
   v77 — ФИЛЬТР ВОПРОСОВ ПО ТЕМАМ

   Вопросов стало двадцать один, и список вырос на треть экрана. Обзоры
   аккордеонов советуют разбивать на темы начиная с двенадцати — как
   раз наш случай.

   Сделано фильтром, а не отдельной страницей: все двадцать один вопрос
   остаются в разметке, просто часть скрыта стилями. Это важно для
   поиска — Google с 2016 года подтверждает, что содержимое, скрытое
   ради удобства и доступное по клику, получает полный вес. Отдельная
   страница такой гарантии не даёт: там пришлось бы делить вес между
   двумя адресами.

   По умолчанию открыты пять частых вопросов — те, что были на сайте
   изначально. Остальные доступны в один клик по теме: стоимость и
   оплата, техника и тралы, сроки и маршрут, документы. Кнопка «Все»
   разворачивает полный список.

   Скрытие сделано через display:none у неподходящих строк, потому что
   так не остаётся ни высоты, ни отступов — список действительно
   занимает меньше места, а не просто прячет содержимое.
   ------------------------------------------------------------------- */
.qa-filter {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    max-width: 48rem;
    margin: 0 auto 1rem;
}

.qa-chip {
    font: inherit;
    font-size: .8125rem;
    font-weight: 600;
    line-height: 1;
    padding: .5rem .85rem;
    border-radius: 999px;
    border: 1px solid var(--line);
    background: #fff;
    color: var(--text-mut);
    cursor: pointer;
    transition: background-color .2s ease, color .2s ease, border-color .2s ease;
}

.qa-chip:hover {
    color: var(--text-strong);
    border-color: var(--line-warm);
}

.qa-chip[aria-pressed="true"] {
    background: var(--o-700);
    border-color: var(--o-700);
    color: #fff;
}

html:not(.kbd) .qa-chip:focus-visible {
    outline: 0;
}

html.kbd .qa-chip:focus-visible {
    outline: 2px solid var(--o-600);
    outline-offset: 2px;
}

/* Показываем только выбранную тему */
.qa[data-filter="top"] > details.acc:not([data-top]),
.qa[data-filter="price"] > details.acc:not([data-cat="price"]),
.qa[data-filter="tech"] > details.acc:not([data-cat="tech"]),
.qa[data-filter="time"] > details.acc:not([data-cat="time"]),
.qa[data-filter="docs"] > details.acc:not([data-cat="docs"]) {
    display: none;
}

/* Скругление достаётся первой и последней видимой строке */
.qa > details.acc {
    border-radius: 0;
}

@media (max-width: 640px) {
    .qa-filter {
        gap: .4rem;
    }

    .qa-chip {
        font-size: .75rem;
        padding: .45rem .7rem;
    }
}

/* -------------------------------------------------------------------
   v78 — ТЕМЫ БЕЗ КРАСНОГО, ЗАГОЛОВКИ И ПЕРЕКЛЮЧЕНИЕ

   1. ЦВЕТ КНОПОК. Оранжевая заливка выбранной темы спорила с главной
   кнопкой страницы: на одном экране оказывались два одинаково громких
   пятна, и взгляд не понимал, куда идти. Выбранная тема теперь
   тёмная, цветом текста заголовков. Оранжевый остаётся только за
   действиями — позвонить, оставить заявку.

   2. ЗАГОЛОВКИ. Вопросы стали h3 вместо span. Для человека ничего не
   меняется — размер и начертание те же. Для поиска меняется многое:
   двадцать один заголовок третьего уровня внутри раздела с h2 задают
   структуру страницы, по которой поисковик понимает, что здесь
   собраны ответы, а не просто абзацы.

   3. ПЕРЕКЛЮЧЕНИЕ ТЕМ. Строки не появляются рывком, а проявляются
   волной: каждая следующая на пятнадцать миллисекунд позже соседней.
   Едут прозрачность и сдвиг на шесть пикселей — обе величины считает
   видеокарта, раскладка не трогается. Задержка задаётся переменной,
   которую скрипт проставляет при смене темы.
   ------------------------------------------------------------------- */

/* 1. Тёмная выбранная тема вместо оранжевой */
.qa-chip[aria-pressed="true"] {
    background: var(--ink);
    border-color: var(--ink);
    color: #fff;
}

.qa-chip[aria-pressed="true"]:hover {
    color: #fff;
    border-color: var(--ink);
}

/* 2. Заголовок вопроса ведёт себя как прежняя строка */
.acc-q {
    margin: 0;
    font-size: .9375rem;
    line-height: 1.45;
    letter-spacing: normal;
}

/* 3. Волна при переключении темы */
.qa[data-switching] > details.acc {
    animation: qaIn .34s cubic-bezier(.22, .61, .36, 1) both;
    animation-delay: calc(var(--i, 0) * 15ms);
}

@keyframes qaIn {
    from {
        opacity: 0;
        transform: translateY(6px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .qa[data-switching] > details.acc {
        animation: none;
    }
}

/* -------------------------------------------------------------------
   v79 — ВВОДНЫЙ АБЗАЦ ПОД СПИСКОМ

   Абзац переехал из-под заголовка вниз, под вопросы. Заодно потерял
   белую плашку: он попадал под правило, которое оформляет шапки
   разделов на песочных полосах, и читался как второй заголовок —
   лишняя ступень перед списком.

   Теперь это сноска: приглушённый текст под списком, без подложки.
   Для поиска значения не изменилось — слова остались на странице и
   рядом с вопросами. Для человека стало чище: сразу после заголовка
   идут темы и вопросы, а пояснение ждёт того, кто дочитал.
   ------------------------------------------------------------------- */
.qa-note {
    max-width: 48rem;
    margin: 1.25rem auto 0;
    text-align: center;
    font-size: .8125rem;
    line-height: 1.7;
    color: var(--text-mut);
}

@media (max-width: 640px) {
    .qa-note {
        text-align: left;
    }
}

/* Первая фраза сноски — акцентом: она короткая и работает как вывод,
   остальное поясняет. */
.qa-note strong {
    color: var(--text-strong);
    font-weight: 700;
}

/* -------------------------------------------------------------------
   v80 — ПЛЮС И МИНУС: ЧЕСТНАЯ ЦЕНТРОВКА

   Прежняя сборка сажала обе полоски отрицательными полями в дробных
   пикселях: минус 0,8 по вертикали и минус 5,6 по горизонтали. Каждая
   полоска считалась от своего края, поэтому их центры не совпадали
   между собой, а при масштабе экрана 125 процентов обе ещё и попадали
   между пикселями. Отсюда и ощущение кривизны.

   Теперь центр у обеих полосок один и тот же по построению: обе стоят
   в середине кружка и сдвигаются ровно на половину собственного
   размера через transform. Половина от 12 и от 2 — это 6 и 1, целые
   числа при любом масштабе; а главное, сдвиг считается от одной и той
   же точки, поэтому перекрестие симметрично всегда.

   Рамка кружка переведена с border на внутреннюю тень. Граница
   съедала по пикселю с каждой стороны, из-за чего внутренняя область
   становилась 26 пикселей вместо 28, и середина приходилась на 13 —
   нечётное число. С внутренней тенью область остаётся 28, середина —
   ровно 14.

   Полоски чуть длиннее и толще прежних: 12 на 2 вместо 11,2 на 1,6.
   Дробные размеры были попыткой попасть в пиксельную сетку конкретного
   экрана; целые работают на всех.
   ------------------------------------------------------------------- */
.acc-plus {
    box-sizing: border-box;
    width: 1.75rem;
    height: 1.75rem;
    border: 0;
    box-shadow: inset 0 0 0 1px var(--line-2);
    background-color: transparent;
    background-image: none;
    border-radius: 999px;
    transform: none;
    transition: box-shadow .26s ease, color .26s ease;
}

details.acc:hover .acc-plus {
    box-shadow: inset 0 0 0 1px var(--tint-3);
    color: var(--o-700);
}

details.acc.is-open .acc-plus {
    box-shadow: inset 0 0 0 1px var(--tint-3);
    color: var(--o-700);
    background-color: transparent;
    transform: none;
}

.acc-plus::before,
.acc-plus::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 2px;
    margin: 0;
    border-radius: 1px;
    background: currentColor;
    transform: translate(-50%, -50%);
    transition: transform .28s cubic-bezier(.22, .61, .36, 1);
}

.acc-plus::after {
    transform: translate(-50%, -50%) rotate(90deg);
}

details.acc.is-open .acc-plus::after {
    transform: translate(-50%, -50%) rotate(90deg) scaleX(0);
}

@media (prefers-reduced-motion: reduce) {

    .acc-plus,
    .acc-plus::before,
    .acc-plus::after {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v81 — ФОКУС, ГАЛОЧКА, ТЕЛЕФОН

   1. ФОКУС. Чёрная рамка появлялась потому, что у полей стоял класс
   focus:border-slate-900 — цвет заголовков. На тёплом бежевом листе
   это читалось как провал, будто поле выключено. Заменяем на мягкое
   кольцо: рамка теплеет, вокруг разливается прозрачная подсветка,
   фон белеет. Всё три величины меняются за четверть секунды, так что
   переход виден, но не отвлекает.

   Кольцо нарисовано тенью, а не рамкой: тень не занимает места, и
   поле не дёргается на пиксель при получении фокуса.

   2. ГАЛОЧКА. Была квадратной коробкой с галочкой из SVG. Сделана
   заново: скруглённый квадрат, при включении заливается оранжевым с
   лёгким подскоком, а сама галочка прорисовывается штрихом слева
   направо. Приём известный — длина контура задаётся через
   stroke-dasharray и анимируется смещением, поэтому линия именно
   рисуется, а не проявляется.

   3. ТЕЛЕФОН. Поле получило подсказку в привычном виде и режим
   цифровой клавиатуры на телефоне.
   ------------------------------------------------------------------- */

/* 1. Фокус полей */
#lead-form input,
#lead-form textarea,
#lead-form .cselect-btn {
    transition: border-color .22s ease, box-shadow .24s ease, background-color .22s ease;
}

#lead-form input:focus,
#lead-form textarea:focus,
#lead-form .cselect-btn:focus-visible {
    outline: none;
    border-color: var(--o-500);
    box-shadow: 0 0 0 3px rgba(var(--o-600-rgb, 194, 86, 15), .14);
    background-color: #fff;
}

#lead-form input:focus::placeholder,
#lead-form textarea:focus::placeholder {
    color: #A9A196;
}

/* 2. Галочка согласия */
.consent-box {
    position: relative;
    width: 1.25rem;
    height: 1.25rem;
    border-radius: .375rem;
    border: 1px solid var(--line-2);
    background: #fff;
    background-image: none;
    flex: none;
    transition: background-color .2s ease, border-color .2s ease, transform .18s cubic-bezier(.34, 1.4, .64, 1);
}

.consent:hover .consent-box {
    border-color: var(--tint-3);
}

.consent input:checked + .consent-box {
    background-color: var(--o-700);
    background-image: none;
    border-color: var(--o-700);
    transform: scale(1.06);
}

.consent-box::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 12px;
    margin: -6px 0 0 -6px;
    background: none;
    border: 0;
    transform: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.6l3 3 6-6' stroke-dasharray='16' stroke-dashoffset='16'%3E%3Canimate attributeName='stroke-dashoffset' from='16' to='0' dur='0.22s' fill='freeze'/%3E%3C/path%3E%3C/svg%3E");
    background-size: 12px 12px;
    background-repeat: no-repeat;
    opacity: 0;
    transition: opacity .12s ease;
}

.consent input:checked + .consent-box::after {
    opacity: 1;
}

.consent input:focus-visible + .consent-box {
    box-shadow: 0 0 0 3px rgba(194, 86, 15, .18);
}

html:not(.kbd) .consent input:focus + .consent-box {
    box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {

    .consent-box,
    #lead-form input,
    #lead-form textarea {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v82 — ТЕМЫ СЛЕВА И ЗАПАС ВЫСОТЫ ПО РАСЧЁТУ

   1. РАСКЛАДКА. Кнопки тем переехали из строки над списком в колонку
   слева. На широком экране это выигрыш вдвойне: список вопросов
   становится уже и читается лучше, а темы стоят столбиком, где их
   легче окинуть взглядом. От планшета и ниже колонка схлопывается
   обратно в строку — там ширины на две колонки нет.

   2. ЗАПАС ВЫСОТЫ. Посчитал, сколько места занимают ответы. При
   ширине колонки около шестисот пикселей выходит так: в теме
   «Стоимость» медиана ответа 160 пикселей, максимум 184; в «Технике»
   медиана 184, максимум 424; в «Сроках» 160 и 371; в «Документах»
   184 и 184. Список при этом занимает от 174 пикселей в самой
   короткой теме до 522 в самой длинной.

   Значит, если под списком держать невидимый запас размером с
   типичный ответ, то раскрытие почти никогда не будет двигать
   страницу: ответ развернётся внутрь запаса. Запас считает скрипт при
   каждой смене темы: берёт высоту списка и прибавляет самый высокий
   из ответов этой темы, но не больше потолка прокрутки.

   Запас невидим: он на обёртке, а не на белой карточке, поэтому под
   списком остаётся просто фон страницы, а не пустая белая плита.
   ------------------------------------------------------------------- */
.qa-layout {
    display: grid;
    gap: 1rem;
    max-width: 48rem;
    margin: 0 auto;
}

.qa-layout > .qa-filter {
    margin: 0;
    max-width: none;
}

.qa-reserve {
    min-height: var(--qa-reserve, auto);
    transition: min-height .28s ease;
}

@media (min-width: 1024px) {
    .qa-layout {
        grid-template-columns: 13.5rem minmax(0, 1fr);
        gap: 1.75rem;
        max-width: 56rem;
        align-items: start;
    }

    .qa-layout > .qa-filter {
        flex-direction: column;
        align-items: stretch;
        position: sticky;
        top: 6rem;
    }

    .qa-layout > .qa-filter .qa-chip {
        text-align: left;
        padding: .6rem .9rem;
    }
}

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

/* -------------------------------------------------------------------
   v83 — ГАЛОЧКА ЗАНОВО, ТЕМЫ В ПОЛЕ, БЕЗ ПУСТОГО ЗАПАСА

   1. ГАЛОЧКА. Прошлая не работала: сама галочка была нарисована в SVG
   с анимацией длины штриха, а анимация внутри фоновой картинки
   браузером не выполняется — линия так и оставалась спрятанной, и в
   квадрате виднелся её обрывок. Отсюда и красный сгусток на снимке.

   Собрана заново и без хитростей: квадрат со спокойной серой рамкой,
   при включении заливается оранжевым, галочка — обычный контур из SVG
   без анимаций внутри. Появление делает CSS: галочка выезжает с
   уменьшения в полный размер за одну шестую секунды. Всё, что может
   не сработать, из картинки убрано.

   2. ТЕМЫ. Колонка тем сдвигала список вопросов вправо, и раздел
   переставал быть по центру страницы. Теперь список остаётся ровно
   там, где был, по центру и прежней ширины, а колонка тем вынесена в
   левое поле — она не участвует в раскладке и ничего не двигает.
   Кнопки уменьшены: мельче шрифт, уже поля, плотнее строка.

   Поле слева есть не на всяком экране, поэтому вынос включается от
   1280 пикселей. Ниже темы возвращаются строкой над списком, как
   было.

   3. ЗАПАС. Невидимый запас под списком убран: на широком экране он
   оставлял под вопросами полтора пустых экрана. Идея была верной по
   расчёту, но не по виду.
   ------------------------------------------------------------------- */

/* 1. Галочка */
.consent input + .consent-box {
    width: 1.25rem;
    height: 1.25rem;
    border-radius: .375rem;
    border: 1.5px solid var(--line-2);
    background: #fff;
    box-shadow: none;
    transition: background-color .18s ease, border-color .18s ease;
}

.consent:hover input:not(:checked) + .consent-box {
    border-color: #C4BCB1;
}

.consent input:checked + .consent-box {
    background-color: var(--o-700);
    border-color: var(--o-700);
    transform: none;
}

.consent input + .consent-box::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 12px;
    margin: -6px 0 0 -6px;
    border: 0;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.4l3 3 6-6'/%3E%3C/svg%3E") center / 12px 12px no-repeat;
    opacity: 0;
    transform: scale(.6);
    transition: opacity .12s ease, transform .18s cubic-bezier(.34, 1.45, .64, 1);
}

.consent input:checked + .consent-box::after {
    opacity: 1;
    transform: scale(1);
}

/* 2. Темы в левом поле, список на месте */
.qa-layout {
    display: block;
    position: relative;
    max-width: 48rem;
    margin: 0 auto;
}

.qa-layout > .qa-filter {
    margin: 0 auto 1rem;
    max-width: 48rem;
}

@media (min-width: 1280px) {
    .qa-layout > .qa-filter {
        position: absolute;
        right: calc(100% + 1.5rem);
        top: 0;
        width: 10.5rem;
        margin: 0;
        flex-direction: column;
        align-items: stretch;
        gap: .35rem;
    }

    .qa-layout > .qa-filter .qa-chip {
        text-align: left;
        font-size: .75rem;
        padding: .45rem .75rem;
    }
}

@media (prefers-reduced-motion: reduce) {

    .consent input + .consent-box,
    .consent input + .consent-box::after {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v85 — ГАЛОЧКА: ТОЛЬКО РЕМЕСЛО, ЦВЕТ ПРЕЖНИЙ

   Цвет не трогаем — остаётся тот же, что был. Меняется только то, из
   чего складывается аккуратность.

   ПРОПОРЦИИ. Квадрат 18 пикселей вместо 20: рядом со строкой в 14
   пикселей двадцать выглядели тяжело. Скругление 6 — заметное, но не
   до капсулы; при таком радиусе квадрат читается как отметка, а не
   как кнопка.

   ОПТИЧЕСКИЙ ЦЕНТР. Галочка — фигура несимметричная: её визуальный
   вес смещён вниз и влево от геометрической середины. Если ставить
   строго по центру, кажется, что она сползла вниз. Поднимаем на
   полпроцента вверх — ровно настолько, чтобы глаз перестал спорить.

   ФОРМА САМОЙ ГАЛОЧКИ. Плечи в отношении один к двум, как в
   классическом начертании; излом ровно на сорок пять градусов; концы
   скруглены. Толщина 2,2 при поле 14 — тоньше выглядит бедно, толще
   заливает угол.

   ВЫРАВНИВАНИЕ. Квадрат прижат к первой строке текста, а не к её
   середине: при переносе на две строки отметка остаётся наверху,
   рядом со словом «Даю».

   ДВИЖЕНИЕ. При включении квадрат делает короткий подскок, галочка
   выезжает следом с задержкой в четыре сотых — так отметка читается
   как поставленная, а не как появившаяся.
   ------------------------------------------------------------------- */
.consent {
    align-items: flex-start;
}

.consent input + .consent-box {
    width: 18px;
    height: 18px;
    margin-top: 1px;
    border-radius: 6px;
    border: 1.5px solid #D5CCC0;
    background-color: #fff;
    background-image: none;
    box-shadow: none;
    transition: background-color .18s ease,
        border-color .18s ease,
        transform .2s cubic-bezier(.34, 1.5, .64, 1);
}

.consent:hover input:not(:checked) + .consent-box {
    border-color: #BEB3A5;
}

.consent input:checked + .consent-box {
    background-color: var(--o-700);
    border-color: var(--o-700);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16);
    transform: scale(1.04);
}

.consent input + .consent-box::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 12px;
    margin: 0;
    border: 0;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14' fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7.2l2.8 2.8L11.2 4.4'/%3E%3C/svg%3E") center / 12px 12px no-repeat;
    opacity: 0;
    transform: translate(-50%, -52%) scale(.7);
    transition: opacity .1s ease, transform .2s cubic-bezier(.34, 1.5, .64, 1);
}

.consent input:checked + .consent-box::after {
    opacity: 1;
    transform: translate(-50%, -52%) scale(1);
    transition-delay: .04s;
}

@media (prefers-reduced-motion: reduce) {

    .consent input + .consent-box,
    .consent input + .consent-box::after {
        transition: none;
    }
}

/* -------------------------------------------------------------------
   v86 — ЗВЁЗДОЧКА У ГАЛОЧКИ И ВЫРАВНИВАНИЕ БЛОКА

   Звёздочка переехала из конца строки к галочке — туда, где ставится
   отметка. В конце текста она висела оторванно от смысла; у галочки
   сразу ясно, что отметка обязательна.

   Метка теперь строится сеткой из трёх колонок: звёздочка, квадрат,
   текст. Все три выровнены по верхней линии текста, поэтому квадрат
   стоит вровень со словом «Даю», а звёздочка — вплотную к квадрату
   слева, на той же высоте.

   Весь низ формы приведён к одной левой линии: галочка, подпись под
   ней и кнопка начинаются с одной вертикали. Отступы между строкой
   согласия, подписью и кнпткой заданы поровну, чтобы блок читался
    step-за-step, а не слипшимися строками.
   ------------------------------------------------------------------- */
.consent {
    display: flex;
    align-items: center;
    gap: .6rem;
}

/* Звёздочку сажаем на верхний-правый угол квадрата: строка не
   рвётся, а обязательность читается прямо на отметке. Позиционируется
   относительно самого квадрата. */
.consent input + .consent-box {
    position: relative;
    overflow: visible;
    flex: none;
}



.consent-text {
    align-self: center;
}

/* Подпись под галочкой начинается от текста, а не от края */
.consent-note {
    margin-top: .55rem;
    padding-left: 0;
}

#lead-form button[type="submit"] {
    margin-top: 1rem;
}

/* -------------------------------------------------------------------
   v88 — ЗВЁЗДОЧКА: БЕЗ ФОНА, ПО ЦЕНТРУ УГЛА

   Кружок-подложку убираем — она делала из значка «наклейку». Остаётся
   чистая пятилучевая звёздочка прямо на углу квадрата.

   Про центровку. Раньше я сажал значок отрицательными отступами top и
   right «на глаз», и он висел то выше, то правее угла. Теперь точка
   привязки — сам угол: значок помещён так, что его геометрический
   центр совпадает с верхним-правым углом квадрата. Достигается это
   пятью строчками без магических чисел: значок абсолютом ставится в
   правый-верхний угол (top:0; right:0), а затем сдвигается ровно на
   половину собственного размера вверх и вправо через translate(50%,
   -50%). Половина считается автоматически, поэтому центр попадает на
   угол при любом размере значка — как перекрестие у плюса, которое мы
   уже так центрировали.

   Пятилучевую взял вместо шестилучевой: у звёздочки-«снежинки» шесть
   одинаковых лучей читаются как декор, а пятиконечная сразу
   опознаётся как пометка «обязательно».

   Размер значка — 0,62rem: крупнее спорит с галочкой внутри квадрата,
   мельче теряется. Луч рисуется из currentColor, цвет прежний.
   ------------------------------------------------------------------- */
.consent-req {
    position: absolute;
    top: 0;
    right: 0;
    width: .8rem;
    height: .8rem;
    /* Центр значка вынесен по диагонали наружу: символ касается угла,
       но не наезжает на квадрат. Проценты берутся от размера самого
       значка, поэтому положение не зависит от единиц. */
    transform: translate(58%, -58%);
    font-size: 0;
    background: none;
    border-radius: 0;
    color: var(--o-700);
    pointer-events: none;
}

.consent-req {
    display: flex;
    align-items: center;
    justify-content: center;
}

.consent-req::before {
    content: "\002A";
    display: block;
    font-size: 1.05rem;
    line-height: 0;
    /* глиф «*» стоит высоко над базовой линией: опускаем на треть
       кегля, чтобы оптический центр звёздочки попал в центр значка */
    margin-top: .34em;
    color: currentColor;
    background: none;
    -webkit-mask: none;
    mask: none;
}
