/* Generated by tools/02-css.mjs — do not edit by hand.
   Source: cursor-clone/product.html (6 inlined \3c style> blocks)
   Block order is preserved exactly; the cascade depends on it. */

/* ======================================================================
   block 1/6 — compiled Tailwind (vendored from capture)
   ====================================================================== */
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-leading:initial;--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-scroll-snap-strictness:proximity;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0#0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0#0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0#0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0#0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0#0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-serif:ui-serif,Georgia,Cambria,"Times New Roman",Times,serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-500:#fb2c36;--color-red-600:#e40014;--color-green-50:#f0fdf4;--color-green-500:#00c758;--color-green-600:#00a544;--color-green-800:#016630;--color-green-900:#0d542b;--color-blue-50:#eff6ff;--color-blue-600:#155dfc;--color-blue-800:#193cb8;--color-blue-900:#1c398e;--color-indigo-600:#4f39f6;--color-purple-400:#c07eff;--color-slate-200:#e2e8f0;--color-slate-500:#62748e;--color-gray-50:#f9fafb;--color-gray-100:#f3f4f6;--color-gray-200:#e5e7eb;--color-gray-300:#d1d5dc;--color-gray-500:#6a7282;--color-gray-600:#4a5565;--color-gray-700:#364153;--color-gray-800:#1e2939;--color-gray-900:#101828;--color-gray-950:#030712;--color-neutral-500:#737373;--color-neutral-800:#262626;--color-neutral-900:#171717;--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-md:660px;--container-2xs:18rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height:calc(1.5/1);--text-lg:2.25rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:3.25rem;--text-2xl:4.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25/1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-light:300;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tighter:-.05em;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--leading-tight:1.1;--leading-snug:1.25;--leading-normal:1.5;--leading-relaxed:1.625;--radius-xs:4px;--radius-sm:.25rem;--radius-md:8px;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1) infinite;--blur-sm:8px;--blur-md:12px;--blur-lg:16px;--aspect-video:16/9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-system:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;--shadow-outline-theme:0 0 0 1px var(--color-theme-border-02);--shadow-flyout:0 0 1rem #00000005,0 0 .5rem #00000002;--duration:.14s;--duration-slow:.25s;--ease-out-spring:cubic-bezier(.25,1,.5,1);--animate-gallery-marquee-item-slide-up:gallery-marquee-item-slide-up 1s var(--ease-out-spring) both;--tracking-sm:.01em;--tracking-base:.005em;--tracking-md:-.005em;--tracking-md-lg:-.0125em;--tracking-lg:-.02em;--tracking-xl:-.025em;--tracking-2xl:-.03em;--tracking-product-sm:.0044em;--leading-2xsnug:1.15;--leading-xsnug:1.2;--leading-snug-plus:1.3;--leading-cozy:1.4;--leading-product-sm:1.27273;--leading-product-base:1.33333;--leading-product-base-mono:1.25rem;--text-md-sm:1.125rem;--text-md:1.375rem;--text-md-lg:1.625rem;--text-product-sm:.6875rem;--text-product-base:.75rem;--text-product-lg:.8125rem;--text-slack-sm:.75rem;--text-slack-base:.875rem;--text-slack-lg:1rem;--katex-font-size:1.15rem;--radius-2xs:2px;--grid-gap:calc(12rem/15);--g:calc(10rem/16);--spacing-g0\.25:calc(var(--g)*.25);--spacing-g0\.5:calc(var(--g)*.5);--spacing-g0\.75:calc(var(--g)*.75);--spacing-g1:calc(var(--g)*1);--spacing-g1\.25:calc(var(--g)*1.25);--spacing-g1\.5:calc(var(--g)*1.5);--spacing-g1\.75:calc(var(--g)*1.75);--spacing-g2:calc(var(--g)*2);--spacing-g2\.5:calc(var(--g)*2.5);--spacing-g3:calc(var(--g)*3);--v:1rem*1.4;--spacing-v1\/12:calc(var(--v)*1/12);--spacing-v2\/12:calc(var(--v)*2/12);--spacing-v2\.5\/12:calc(var(--v)*2.5/12);--spacing-v3\/12:calc(var(--v)*3/12);--spacing-v4\/12:calc(var(--v)*4/12);--spacing-v5\/12:calc(var(--v)*5/12);--spacing-v6\/12:calc(var(--v)*6/12);--spacing-v7\/12:calc(var(--v)*7/12);--spacing-v8\/12:calc(var(--v)*8/12);--spacing-v9\/12:calc(var(--v)*9/12);--spacing-v10\/12:calc(var(--v)*10/12);--spacing-v1:calc(var(--v));--spacing-v1\.25:calc(var(--v)*1.25);--spacing-v1\.5:calc(var(--v)*1.5);--spacing-v2:calc(var(--v)*2);--spacing-v2\.5:calc(var(--v)*2.5);--spacing-v3:calc(var(--v)*3);--spacing-v4:calc(var(--v)*4);--spacing-v4\.5:calc(var(--v)*4.5);--spacing-v5:calc(var(--v)*5);--spacing-v6:calc(var(--v)*6);--spacing-v8:calc(var(--v)*8);--spacing-prose-narrow:48ch;--spacing-prose-medium-wide:80ch;--spacing-prose-wide:96ch;--button-padding-default:.89em 1.45em .91em;--button-padding-md-sm:.64em 1.2em .66em;--button-padding-sm:.45em .8em .46em;--button-padding-xs:.15em .5em;--site-header-height:56px;--site-sticky-top:64px;--max-width-container:1300px;--color-theme-bg:#f7f7f4;--color-theme-fg:#26251e;--color-theme-fg-01:#26251e03}@supports (color:lab(0%0 0)){:root{--color-red-500:lab(55.4814%75.0732 48.8528);--color-red-600:lab(48.4493%77.4328 61.5452);--color-green-50:lab(98.1563% -5.60117 2.75915);--color-green-500:lab(70.5521% -66.5147 45.8073);--color-green-600:lab(59.0978% -58.6621 41.2579);--color-green-800:lab(37.4616% -36.7971 22.9692);--color-green-900:lab(30.797% -29.6927 17.382);--color-blue-50:lab(96.492% -1.14644-5.11479);--color-blue-600:lab(44.0605%29.0279-86.0352);--color-blue-800:lab(30.2514%27.7853-70.2699);--color-blue-900:lab(26.1542%15.7545-51.5504);--color-indigo-600:lab(38.4009%52.6132-92.3857);--color-purple-400:lab(63.6946%47.6127-59.2066);--color-slate-200:lab(91.7353% -.998765-4.76968);--color-slate-500:lab(48.0876% -2.03595-16.5814);--color-gray-50:lab(98.2596% -.247031-.706708);--color-gray-100:lab(96.1596% -.0823438-1.13575);--color-gray-200:lab(91.6229% -.159115-2.26791);--color-gray-300:lab(85.1236% -.612259-3.7138);--color-gray-500:lab(47.7841% -.393182-10.0268);--color-gray-600:lab(35.6337% -1.58697-10.8425);--color-gray-700:lab(27.1134% -.956401-12.3224);--color-gray-800:lab(16.1051% -1.18239-11.7533);--color-gray-900:lab(8.11897%.811279-12.254);--color-gray-950:lab(1.90334%.278696-5.48866);--color-neutral-500:lab(48.496%0 0);--color-neutral-800:lab(15.204%0-.00000596046);--color-neutral-900:lab(7.78201% -.0000149012 0)}}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-01:color-mix(in oklab,var(--color-theme-fg) 1%,transparent)}}:root{--color-theme-fg-02-5:#26251e06}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-02-5:color-mix(in oklab,var(--color-theme-fg) 2.5%,transparent)}}:root{--color-theme-fg-05:#26251e0d}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-05:color-mix(in oklab,var(--color-theme-fg) 5%,transparent)}}:root{--color-theme-fg-07-5:#26251e13}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-07-5:color-mix(in oklab,var(--color-theme-fg) 7.5%,transparent)}}:root{--color-theme-fg-08:#26251e14}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-08:color-mix(in oklab,var(--color-theme-fg) 8%,transparent)}}:root{--color-theme-fg-10:#26251e1a}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-10:color-mix(in oklab,var(--color-theme-fg) 10%,transparent)}}:root{--color-theme-fg-15:#26251e26}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-15:color-mix(in oklab,var(--color-theme-fg) 15%,transparent)}}:root{--color-theme-fg-20:#26251e33}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-fg-20:color-mix(in oklab,var(--color-theme-fg) 20%,transparent)}}:root{--color-theme-card-hex:#f2f1ed;--color-theme-card:linear-gradient(var(--color-theme-card-hex) 0% 100%);--color-theme-accent:#f54e00;--color-theme-text:var(--color-theme-fg);--color-theme-text-mid:#26251e80}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-text-mid:color-mix(in oklab,var(--color-theme-fg) 50%,transparent)}}:root{--color-theme-text-sec:#26251e99}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-text-sec:color-mix(in oklab,var(--color-theme-fg) 60%,transparent)}}:root{--color-theme-text-tertiary:#26251e66}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-text-tertiary:color-mix(in oklab,var(--color-theme-fg) 40%,transparent)}}:root{--color-theme-card-01-hex:#f0efeb;--color-theme-card-02-hex:#ebeae5;--color-theme-card-02:linear-gradient(var(--color-theme-fg-02-5) 0% 100%),linear-gradient(var(--color-theme-card-hex) 0% 100%);--color-theme-card-03-hex:#e6e5e0;--color-theme-card-03:linear-gradient(var(--color-theme-fg-05) 0% 100%),linear-gradient(var(--color-theme-card-hex) 0% 100%);--color-theme-card-04-hex:#e1e0db;--color-theme-card-warm-hex:#f3ede6;--color-theme-fg-02:#3b3a33;--color-theme-border-01:#26251e06}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-border-01:color-mix(in oklab,var(--color-theme-fg) 2.5%,transparent)}}:root{--color-theme-border-01-5:#26251e0d}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-border-01-5:color-mix(in oklab,var(--color-theme-fg) 5%,transparent)}}:root{--color-theme-border-02:#26251e1a}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-border-02:color-mix(in oklab,var(--color-theme-fg) 10%,transparent)}}:root{--color-theme-border-02-5:#26251e33}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-border-02-5:color-mix(in oklab,var(--color-theme-fg) 20%,transparent)}}:root{--color-theme-border-03:#26251e99}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-border-03:color-mix(in oklab,var(--color-theme-fg) 60%,transparent)}}:root{--color-theme-border:var(--color-theme-border-01);--color-theme-card-hover-hex:#ebeae5;--color-theme-card-hover-light-hex:#f0efeb;--color-theme-card-hover-border:var(--color-theme-border-02);--color-theme-button-bg:var(--color-theme-fg);--color-theme-button-text:var(--color-theme-bg);--color-theme-button-hover-bg:var(--color-theme-fg-02);--color-theme-button-hover-border:var(--color-theme-fg-02);--color-theme-button-hover-text:var(--color-theme-bg);--color-theme-button-sec-bg:transparent;--color-theme-button-sec-border:var(--color-theme-border-03);--color-theme-button-sec-text:var(--color-theme-fg);--prose-body:var(--color-theme-fg);--prose-headings:var(--color-theme-fg);--prose-bullets:#26251e66}@supports (color:color-mix(in lab,red,red)){:root{--prose-bullets:color-mix(in oklab,var(--color-theme-fg) 40%,transparent)}}:root{--prose-hr:var(--color-theme-border-02);--prose-captions:var(--color-theme-text-sec);--prose-code:var(--color-theme-fg);--prose-code-bg:var(--color-theme-card-hex);--prose-code-border:var(--color-theme-border-01);--prose-code-border-radius:var(--radius-xs);--prose-pre-code:var(--color-theme-fg);--prose-pre-bg:var(--color-theme-border-01);--color-theme-product-chrome:var(--color-theme-card-hex);--color-theme-product-editor:var(--color-theme-bg);--color-theme-product-text:#26251eeb}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-product-text:color-mix(in oklab,var(--color-theme-fg) 92%,transparent)}}:root{--color-theme-product-text-sec:#26251e99}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-product-text-sec:color-mix(in oklab,var(--color-theme-fg) 60%,transparent)}}:root{--color-theme-product-text-tertiary:#26251e66}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-product-text-tertiary:color-mix(in oklab,var(--color-theme-fg) 40%,transparent)}}:root{--color-theme-product-ansi-green:#1f8a65;--color-theme-product-ansi-red:#cf2d56;--color-theme-product-line-inserted-line-background:#1f8a6514}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-product-line-inserted-line-background:color-mix(in srgb,var(--color-theme-product-ansi-green) 8%,transparent)}}:root{--color-theme-product-removed-line-background:#cf2d560f}@supports (color:color-mix(in lab,red,red)){:root{--color-theme-product-removed-line-background:color-mix(in srgb,var(--color-theme-product-ansi-red) 6%,transparent)}}[data-theme=dark]{--color-theme-bg:#14120b;--color-theme-fg:#edecec;--color-theme-fg-02:#d7d6d5;--color-theme-card-hex:#1b1913;--color-theme-card-rgb:27 29 39;--color-theme-card-01-hex:#1d1b15;--color-theme-card-02-hex:#201e18;--color-theme-card-03-hex:#26241e;--color-theme-card-04-hex:#2b2923;--color-theme-card-hover-hex:#201e18;--color-theme-card-hover-light-hex:#1d1b15;--color-theme-card-warm-hex:#1c1713;--color-theme-product-chrome:var(--color-theme-bg);--color-theme-product-editor:var(--color-theme-card-hex);--color-theme-product-text:#26251eeb}@supports (color:color-mix(in lab,red,red)){[data-theme=dark]{--color-theme-product-text:color-mix(in oklab,var(--color-theme-fg) 92%,transparent)}}[data-theme=dark]{--color-theme-product-text-sec:#26251e99}@supports (color:color-mix(in lab,red,red)){[data-theme=dark]{--color-theme-product-text-sec:color-mix(in oklab,var(--color-theme-fg) 60%,transparent)}}[data-theme=dark]{--color-theme-product-text-tertiary:#26251e66}@supports (color:color-mix(in lab,red,red)){[data-theme=dark]{--color-theme-product-text-tertiary:color-mix(in oklab,var(--color-theme-fg) 40%,transparent)}}[data-theme=dark]{--color-theme-product-ansi-green:#1f8a65;--color-theme-product-ansi-red:#cf2d56;--color-theme-product-line-inserted-line-background:#1f8a6514}@supports (color:color-mix(in lab,red,red)){[data-theme=dark]{--color-theme-product-line-inserted-line-background:color-mix(in srgb,var(--color-theme-product-ansi-green) 8%,transparent)}}[data-theme=dark]{--color-theme-product-removed-line-background:#cf2d560f}@supports (color:color-mix(in lab,red,red)){[data-theme=dark]{--color-theme-product-removed-line-background:color-mix(in srgb,var(--color-theme-product-ansi-red) 6%,transparent)}}[data-theme=dark]{--window-shadow-inner:0-1px 0 0 var(--color-theme-border-02) inset}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html{tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}h1,h2,h3{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}strong{font-weight:bolder}code{font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal)}ol,ul{list-style:none}img,svg,video,iframe{vertical-align:middle;display:block}img,video{max-width:100%}button,input,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}button{appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}@layer base{html{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;scroll-padding-top:var(--site-sticky-top);scroll-behavior:auto;font-size:.9375rem}@media (prefers-reduced-motion:no-preference){html{scroll-snap-type:none}}@media (min-width:900px){html{font-size:1rem}}body::selection{background-color:var(--color-theme-fg-15)}body{-ms-overflow-style:-ms-autohiding-scrollbar}main{min-height:70vh}video:not([controls])::-webkit-media-controls-start-playback-button{-webkit-appearance:none!important;display:none!important}video:not([controls])::-webkit-media-controls-panel{-webkit-appearance:none!important;display:none!important}body.password-protected input[type=password]:focus,body.password-protected input[type=text]:focus{font-size:16px!important}}}@layer components{@layer components{.btn{background-color:var(--color-theme-button-bg);border:1px solid var(--color-theme-button-bg);color:var(--color-theme-button-text);cursor:pointer;font-size:var(--text-base);font-weight:var(--font-weight-normal);padding:var(--button-padding-default);border-radius:3.40282e38px;justify-content:center;align-items:center;line-height:1;display:inline-flex}.btn:hover{background-color:var(--color-theme-button-hover-bg);border-color:var(--color-theme-button-hover-border);color:var(--color-theme-button-hover-text)}.btn:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn:active{background-color:var(--color-theme-button-bg)}.btn--sm{font-size:var(--text-sm);padding:var(--button-padding-sm)}.btn--secondary{background-color:var(--color-theme-card-03-hex);border:1px solid var(--color-theme-border-01);color:var(--color-theme-text)}.btn--secondary:hover{background-color:var(--color-theme-card-04-hex);border-color:var(--color-theme-border-01);color:var(--color-theme-text)}.btn--secondary:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn--secondary:active{background-color:var(--color-theme-card-03-hex);border-color:var(--color-theme-border-01);color:var(--color-theme-text)}.btn--ghost{border:1px solid var(--color-theme-border-02-5);color:var(--color-theme-text);cursor:pointer;font-weight:var(--font-weight-normal);background-color:#0000;border-radius:3.40282e38px;justify-content:center;align-items:center;line-height:1}.btn--ghost:hover{background-color:var(--color-theme-card-hex);border-color:var(--color-theme-border-02-5);color:var(--color-theme-text)}.btn--ghost:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn--ghost:active{background-color:var(--color-theme-card-03-hex)}.btn--ghost.btn--sm{font-size:var(--text-sm);padding:var(--button-padding-sm)}.btn-tertiary{padding:unset;color:var(--color-theme-accent);background-color:#0000;border:0;justify-content:center;align-items:center;text-decoration:none;display:inline-flex}.btn-tertiary:hover{color:#f54e00bf}@supports (color:color-mix(in lab,red,red)){.btn-tertiary:hover{color:color-mix(in oklab,var(--color-theme-accent) 75%,transparent)}}.btn-tertiary:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn--quaternary:hover{color:color-mix(in srgb,--alpha(var(--color-theme-fg)/60%) 75%,transparent)}@supports (color:color-mix(in lab,red,red)){.btn--quaternary:hover{color:color-mix(in oklab,var(--color-theme-text-sec) 75%,transparent)}}.btn--quaternary:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn-text{font-size:var(--text-base);line-height:var(--leading-normal);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-base);color:var(--color-theme-accent);text-decoration:none}.btn-text:hover{color:#f54e00bf}@supports (color:color-mix(in lab,red,red)){.btn-text:hover{color:color-mix(in oklab,var(--color-theme-accent) 75%,transparent)}}.btn-text:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn-icon{padding-inline-start:.25em;display:inline-flex}.btn--banner:hover{background-color:var(--color-theme-card-hover-hex);border:1px solid var(--color-theme-border);color:var(--color-theme-text)}.btn--banner:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn-block:hover{background-color:var(--color-theme-card-hover-hex)}.btn-block:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn-block:active{background-color:var(--color-theme-card-hex)}.btn--quinary{color:var(--color-theme-fg);cursor:pointer;font-size:var(--text-sm);background-color:#0000;border:1px solid #0000;padding:.25rem;text-decoration:none;display:inline-flex}.btn--quinary:hover{color:#26251ebf}@supports (color:color-mix(in lab,red,red)){.btn--quinary:hover{color:color-mix(in oklab,var(--color-theme-fg) 75%,transparent)}}.btn--quinary:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.btn:disabled:hover,.btn--secondary:disabled:hover,.btn--ghost:disabled:hover,.btn--banner:disabled:hover,.btn--quinary:disabled:hover,.btn:disabled:active,.btn--secondary:disabled:active,.btn--ghost:disabled:active,.btn--banner:disabled:active,.btn--quinary:disabled:active{background-color:var(--color-theme-card-03-hex);border-color:var(--color-theme-border-01);color:var(--color-theme-text-sec)}.pill-padding-sm{padding:var(--button-padding-sm)}.card{position:relative}.card:before{content:"";pointer-events:none;border-radius:var(--radius-xs);border:1px solid var(--color-theme-border-01);z-index:30;position:absolute;inset:0}.card{padding-inline:var(--spacing-g1\.75);padding-top:calc(var(--spacing-g1\.75) - .1rem);padding-bottom:var(--spacing-g1\.75);background-color:var(--color-theme-card-hex);border-radius:var(--radius-xs)}.card--text{padding-inline:var(--spacing-g1\.5);padding-top:calc(var(--spacing-g1\.5) - .1rem);padding-bottom:var(--spacing-g1\.5)}.card:is(a):hover{background-color:var(--color-theme-card-hover-hex)}.card:is(a):focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.card:is(a):active,.card--no-hover:is(a):hover{background-color:var(--color-theme-card-hex)}.card--feature:is(a):hover{background-color:var(--color-theme-card-hover-light-hex)}.card-border:before{content:"";pointer-events:none;border-radius:var(--radius-xs);border:1px solid var(--color-theme-border-01);z-index:30;position:absolute;inset:0}.card--bg-02{background-color:var(--color-theme-card-02-hex)}.card--bg-02:is(a):hover{background-color:var(--color-theme-card-03-hex)}.card--flyout:before{border-color:var(--color-theme-border-01-5)}.card--media:before{content:"";pointer-events:none;border-radius:var(--radius-xs);border:1px solid var(--color-theme-border-01);z-index:2;position:absolute;inset:0}.card--media header:after{content:"";pointer-events:none;border-radius:inherit;border:1px solid var(--color-theme-border-01);z-index:2;position:absolute;inset:0}.blog-directory__row:hover:before,.blog-directory__row:hover:after{content:"";pointer-events:none;background-color:var(--color-theme-card-02-hex);height:2px;position:absolute;left:0;right:0}.blog-directory__row:hover:before{top:-1px}.blog-directory__row:hover:after{bottom:-1px}.grid-cursor-v1{--gap:var(--grid-gap);--grid-cols:12;gap:var(--gap);display:grid}.grid-cursor{grid-template-columns:repeat(24,1fr);display:grid}.insights-theme .section-number:before{content:counter(section-index)"."}.insights-theme .slide-number:before{content:counter(section-index)"."counter(slide-index)"."}media-time-range::part(thumb),media-time-range::part(slider-thumb){visibility:hidden!important;opacity:0!important;width:0!important;height:0!important;display:none!important}.media-control-bar-time-range::-webkit-slider-thumb{visibility:hidden!important;opacity:0!important;-webkit-appearance:none!important;width:0!important;height:0!important;display:none!important}media-time-range::-webkit-slider-thumb{visibility:hidden!important;opacity:0!important;-webkit-appearance:none!important;width:0!important;height:0!important;display:none!important}.media-control-bar-time-range input::-webkit-slider-thumb{visibility:hidden!important;opacity:0!important;-webkit-appearance:none!important;width:0!important;height:0!important;display:none!important}media-time-range input::-webkit-slider-thumb{visibility:hidden!important;opacity:0!important;-webkit-appearance:none!important;width:0!important;height:0!important;display:none!important}.media-control-bar-time-range::-moz-range-thumb{visibility:hidden!important;opacity:0!important;-moz-appearance:none!important;width:0!important;height:0!important;display:none!important}media-time-range::-moz-range-thumb{visibility:hidden!important;opacity:0!important;-moz-appearance:none!important;width:0!important;height:0!important;display:none!important}.media-control-bar-time-range input::-moz-range-thumb{visibility:hidden!important;opacity:0!important;-moz-appearance:none!important;width:0!important;height:0!important;display:none!important}media-time-range input::-moz-range-thumb{visibility:hidden!important;opacity:0!important;-moz-appearance:none!important;width:0!important;height:0!important;display:none!important}.media-control-bar-time-range::-ms-thumb{visibility:hidden!important;opacity:0!important;width:0!important;height:0!important;display:none!important}media-time-range::-ms-thumb{visibility:hidden!important;opacity:0!important;width:0!important;height:0!important;display:none!important}.media-control-bar-time-range input::-ms-thumb{visibility:hidden!important;opacity:0!important;width:0!important;height:0!important;display:none!important}media-time-range input::-ms-thumb{visibility:hidden!important;opacity:0!important;width:0!important;height:0!important;display:none!important}.media-control-bar-time-range::-webkit-slider-track{background:0 0;border-radius:2px;height:4px}media-time-range::-webkit-slider-track{background:0 0;border-radius:2px;height:4px}.media-control-bar-time-range input::-webkit-slider-track{background:0 0;border-radius:2px;height:4px}media-time-range input::-webkit-slider-track{background:0 0;border-radius:2px;height:4px}.media-control-bar-time-range::-moz-range-track{background:0 0;border:none;border-radius:2px;height:4px}media-time-range::-moz-range-track{background:0 0;border:none;border-radius:2px;height:4px}.media-control-bar-time-range input::-moz-range-track{background:0 0;border:none;border-radius:2px;height:4px}media-time-range input::-moz-range-track{background:0 0;border:none;border-radius:2px;height:4px}.media-control-bar-time-range::-ms-track{background:0 0;border:none;border-radius:2px;height:4px}media-time-range::-ms-track{background:0 0;border:none;border-radius:2px;height:4px}.media-control-bar-time-range input::-ms-track{background:0 0;border:none;border-radius:2px;height:4px}media-time-range input::-ms-track{background:0 0;border:none;border-radius:2px;height:4px}.multicard-flex::-webkit-scrollbar-thumb{background-clip:content-box;background-color:var(--theme-bg);border:5px solid #0000;border-radius:99em}.nav__link,.nav__btn{cursor:pointer;font-size:var(--text-sm);padding-inline:var(--spacing-g1\.5);padding-block:var(--spacing-v3\/12);background-color:#0000;border:1px solid #0000;display:inline-flex}.nav__link:hover,.nav__btn:hover{color:#26251ebf}@supports (color:color-mix(in lab,red,red)){.nav__link:hover,.nav__btn:hover{color:color-mix(in oklab,var(--color-theme-fg) 75%,transparent)}}.nav__link:focus-visible,.nav__btn:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.nav__btn-caret{opacity:0;transition:opacity var(--duration) var(--ease-out-spring);align-items:center;display:flex}.nav__btn-caret--sm{font-size:var(--text-sm);line-height:var(--leading-normal);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-sm);position:absolute;inset:0 0 0 auto}.nav__btn-caret:focus-visible{opacity:1;outline:0}.nav__btn-caret:focus-visible .nav__btn-caret__icon{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.nav__btn-caret__icon{transition:transform var(--duration) var(--ease-out-spring);display:inline-flex}.nav__sub-nav{opacity:0;visibility:hidden;transition:opacity var(--duration) var(--ease-out-spring),transform var(--duration) var(--ease-out-spring),visibility 0s linear var(--duration);transform:translateY(-.4rem)}.privacy-dropdown-upward{opacity:0;visibility:hidden;transition:opacity var(--duration) var(--ease-out-spring),transform var(--duration) var(--ease-out-spring),visibility 0s linear var(--duration);transform:translateY(.4rem)}.nav__sub-nav__link{font-size:var(--text-sm);display:flex}.nav__sub-nav__link--responsive{font-size:var(--text-base)}.nav__sub-nav__link--responsive:hover{color:#26251ebf}@supports (color:color-mix(in lab,red,red)){.nav__sub-nav__link--responsive:hover{color:color-mix(in oklab,var(--color-theme-fg) 75%,transparent)}}.nav__sub-nav__link--responsive:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}@media (min-width:768px){.nav__sub-nav__link--responsive{font-size:var(--text-sm)}}.nav__sub-nav__link:hover{color:#26251ebf}@supports (color:color-mix(in lab,red,red)){.nav__sub-nav__link:hover{color:color-mix(in oklab,var(--color-theme-fg) 75%,transparent)}}.nav__sub-nav__link:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.nav__sub-nav__link__icon{transition:opacity var(--duration) var(--ease-out-spring)}@media (hover:none){.nav__sub-nav__link__icon{opacity:1}}@media (hover:hover){.nav__sub-nav__link__icon{opacity:0}.nav__sub-nav__link:hover .nav__sub-nav__link__icon,.nav__sub-nav__link:focus-visible .nav__sub-nav__link__icon,.footer-link:hover .nav__sub-nav__link__icon,.footer-link:focus-visible .nav__sub-nav__link__icon{opacity:1}}@keyframes navItemSlideIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}@keyframes navItemSlideOut{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(4px)}}@keyframes navItemFade{0%{opacity:0}to{opacity:1}}.footer-link:hover{color:#26251ebf}@supports (color:color-mix(in lab,red,red)){.footer-link:hover{color:color-mix(in oklab,var(--color-theme-text) 75%,transparent)}}.footer-link:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}@media (hover:hover){.footer-link:hover .footer-link__icon,.footer-link:focus-visible .footer-link__icon{opacity:1}}.skipnav{z-index:50;border-radius:3.40282e38px;display:block;position:absolute;top:.82rem;left:-.2rem}.skipnav:not(:focus-visible):not(:active){clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;overflow:hidden}.card--sub-nav{padding:calc(var(--spacing-g1) - .15em) var(--spacing-g1\.5) calc(var(--spacing-g1) + .2em)}.section{padding:var(--spacing-v3) var(--spacing-g2)}.section--breadcrumb{padding-block-start:var(--spacing-v5);padding-block-end:0}.section--breadcrumb+main>.section:first-of-type{padding-block-start:var(--spacing-v2\/12)}.section--headline{padding-block-start:var(--spacing-v3)}.section+.section.bg-theme-card-hex{margin-block-start:var(--spacing-v3);padding-block-start:var(--spacing-v4);padding-block-end:var(--spacing-v4)}.section--flush{padding:0}main:not(.section)>:last-child.section{padding-block-end:var(--spacing-v6)}.theme-switch-btn.is-active:before{content:"";pointer-events:none;border:1px solid var(--color-theme-border-01);z-index:2;border-radius:3.40282e38px;position:absolute;inset:0}[data-theme=dark] .media-dark{display:block!important}[data-theme=dark] .media-light{display:none!important}.icon-glyph-08{font-family:var(--font-sans);font-feature-settings:"ss08"}.icon-glyph-09{font-family:var(--font-sans);font-feature-settings:"ss09"}}}@layer utilities{.\@container{container-type:inline-size}.prose>:is(h1,h2,h3,h4,h5,h6).anchor-heading:hover .anchor-link,.prose>:is(h1,h2,h3,h4,h5,h6).anchor-heading .anchor-link:focus{opacity:1}.prose>:is(h1,h2,h3,h4,h5,h6).anchor-heading .anchor-link:hover{color:var(--color-theme-text)}.prose .math-wrapper pre:before{display:none}.prose pre.math-display:before{display:none}.prose pre:has(>code.math-display):before{display:none}.prose pre:has(>.katex-display):before{display:none}.prose pre:before{content:"";pointer-events:none;border-radius:var(--radius-xs);border:1px solid var(--color-theme-border-01);z-index:2;position:absolute;inset:0}:is(.prose pre:has(.katex),.prose pre:has(.katex-display)):before{display:none}.prose pre code:before{display:none}.prose .prose-code-block:before{content:"";pointer-events:none;border-radius:var(--radius-xs);border:1px solid var(--color-theme-border-01);z-index:2;position:absolute;inset:0}.prose .prose-code-block pre:before{display:none}.prose ol>li::marker{color:currentColor;font-weight:400}.prose ul>li::marker{color:var(--prose-bullets)}.prose a:not([class]):hover{color:currentColor}@supports (color:color-mix(in lab,red,red)){.prose a:not([class]):hover{color:color-mix(in oklab,currentColor 75%,transparent)}}.prose a.underline:hover,.prose a u:hover{color:currentColor}@supports (color:color-mix(in lab,red,red)){.prose a.underline:hover,.prose a u:hover{color:color-mix(in oklab,currentColor 75%,transparent)}}.prose.prose--blog>p:last-of-type:after{content:" ■";color:var(--color-theme-accent)}.prose.prose--blog sup a:hover{color:var(--color-theme-text);text-decoration:underline}.scrim-dark:before{content:"";pointer-events:none;background-color:var(--scrim-color,#26251e);position:absolute;inset:0}@supports (color:color-mix(in lab,red,red)){.scrim-dark:before{background-color:color-mix(in srgb,var(--scrim-color,var(--color-theme-fg)) var(--scrim-opacity,3%),transparent)}}.scrim-dark:before{z-index:5;display:none}[data-theme=dark] .scrim-dark:before{display:block}.scrim-light:before{content:"";pointer-events:none;background-color:var(--scrim-color,#26251e);position:absolute;inset:0}@supports (color:color-mix(in lab,red,red)){.scrim-light:before{background-color:color-mix(in srgb,var(--scrim-color,var(--color-theme-fg)) var(--scrim-opacity,3%),transparent)}}.scrim-light:before{z-index:5}[data-theme=dark] .scrim-light:before{display:none}.media-border-container{z-index:1;border-radius:var(--radius-xs);overflow:hidden}.media-border-container:after{content:"";pointer-events:none;border-radius:var(--radius-xs);border:1px solid var(--color-theme-border-01);z-index:30;position:absolute;inset:0}.media-border-container.interactive:before{content:"";pointer-events:none;z-index:5;background-color:#00000006;position:absolute;inset:0}.avatar-border-container{z-index:1;border-radius:3.40282e38px;position:relative;overflow:hidden}.avatar-border-container:after{content:"";pointer-events:none;border:1px solid var(--color-theme-border-01);z-index:30;border-radius:9999px;position:absolute;inset:0}.card-border:before{content:"";pointer-events:none;border-radius:var(--radius-xs);border:1px solid var(--color-theme-border-01);z-index:30;position:absolute;inset:0}.scrim-both:before{content:"";pointer-events:none;background-color:var(--scrim-color,#26251e);position:absolute;inset:0}@supports (color:color-mix(in lab,red,red)){.scrim-both:before{background-color:color-mix(in srgb,var(--scrim-color,var(--color-theme-fg)) var(--scrim-opacity,3%),transparent)}}.scrim-both:before{z-index:5}.ios-glass:before{content:"";pointer-events:none;border-radius:inherit;opacity:.58;background:radial-gradient(at 50%0,#ffffff75 0%,#fff3 48%,#fff0 76%);height:34%;position:absolute;inset:0 9%auto}@media (hover:hover){.ios-glass:hover{background:linear-gradient(135deg,#ffffff78 0%,#ffffff3b 52%,#ffffff1f 100%)}}[data-theme=dark] .ios-glass:before{opacity:.5;background:radial-gradient(at 50%0,#ffffff2e 0%,#ffffff12 52%,#fff0 76%)}@media (hover:hover){[data-theme=dark] .ios-glass:hover{background:linear-gradient(135deg,#fff2 0%,#ffffff14 48%,#ffffff0b 100%);border-color:#ffffff24}}.aspect-1\/1-box{grid-template-rows:repeat(1,minmax(0,1fr));grid-template-columns:repeat(1,minmax(0,1fr));display:grid}.aspect-1\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:100%}.aspect-2\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:50%}.aspect-3\/2-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:66.6667%}.aspect-4\/3-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:75%}.aspect-4\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:125%}.aspect-5\/4-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:80%}.aspect-8\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:62.5%}.aspect-8\/9-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:112.5%}.aspect-16\/9-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:56.25%}.aspect-natural-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:0}.pointer-events-none{pointer-events:none}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-x-2{inset-inline:calc(var(--spacing)*2)}.inset-y-2{inset-block:calc(var(--spacing)*2)}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.top-\[0\.2rem\]{top:.2rem}.top-\[var\(--site-sticky-top\)\]{top:var(--site-sticky-top)}.top-full{top:100%}.right-0{right:calc(var(--spacing)*0)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-3{bottom:calc(var(--spacing)*3)}.bottom-8{bottom:calc(var(--spacing)*8)}.bottom-\[8px\]{bottom:8px}.bottom-full{bottom:100%}.left-0{left:calc(var(--spacing)*0)}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing)*3)}.left-4{left:calc(var(--spacing)*4)}.left-\[-1px\]{left:-1px}.left-\[-2px\]{left:-2px}.z-0{z-index:0}.z-1{z-index:1}.z-2{z-index:2}.z-10{z-index:10}.z-15{z-index:15}.z-20{z-index:20}.z-25{z-index:25}.z-50{z-index:50}.col-span-full{grid-column:1/-1}.col-start-1{grid-column-start:1}.col-start-2{grid-column-start:2}.col-end-2{grid-column-end:2}.col-end-3{grid-column-end:3}.row-span-full{grid-row:1/-1}.row-start-1{grid-row-start:1}.row-end-2{grid-row-end:2}@media (min-width:420px){.container{max-width:420px}}@media (min-width:660px){.container{max-width:660px}}@media (min-width:900px){.container{max-width:900px}}@media (min-width:1140px){.container{max-width:1140px}}@media (min-width:1380px){.container{max-width:1380px}}@media (min-width:1470px){.container{max-width:1470px}}.-mx-g2{margin-inline:calc(var(--spacing-g2)*-1)}.mx-0\.5{margin-inline:calc(var(--spacing)*.5)}.mx-3{margin-inline:calc(var(--spacing)*3)}.mx-auto{margin-inline:auto}.my-0\.5{margin-block:calc(var(--spacing)*.5)}.my-v2{margin-block:var(--spacing-v2)}@supports (initial-letter:2) or (-webkit-initial-letter:2){.prose-dropcap>p:first-of-type:first-letter{-webkit-initial-letter:2 2;initial-letter:2 2;margin-block-start:.067rem;margin-inline:.067rem .475rem}}.mt-0{margin-top:calc(var(--spacing)*0)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-2\.5{margin-top:calc(var(--spacing)*2.5)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-5{margin-top:calc(var(--spacing)*5)}.mt-auto{margin-top:auto}.mt-v1{margin-top:var(--spacing-v1)}.mt-v2{margin-top:var(--spacing-v2)}.mt-v9\/12{margin-top:var(--spacing-v9\/12)}.-mr-1{margin-right:calc(var(--spacing)*-1)}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-g1{margin-bottom:var(--spacing-g1)}.mb-v1{margin-bottom:var(--spacing-v1)}.mb-v2{margin-bottom:var(--spacing-v2)}.mb-v2\.5{margin-bottom:var(--spacing-v2\.5)}.mb-v4\.5{margin-bottom:var(--spacing-v4\.5)}.mb-v6\/12{margin-bottom:var(--spacing-v6\/12)}.ml-0\.5{margin-left:calc(var(--spacing)*.5)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-1\.5{margin-left:calc(var(--spacing)*1.5)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-0\.5{height:calc(var(--spacing)*.5)}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-4\.5{height:calc(var(--spacing)*4.5)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-\[1em\]{height:1em}.h-\[2\.5rem\]{height:2.5rem}.h-\[5px\]{height:5px}.h-\[18px\]{height:18px}.h-\[48px\]{height:48px}.h-\[378px\]{height:378px}.h-\[var\(--site-header-height\)\]{height:var(--site-header-height)}.h-full{height:100%}.max-h-\[200px\]{max-height:200px}.max-h-\[var\(--max-h-mobile\)\]{max-height:var(--max-h-mobile)}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-2{min-height:calc(var(--spacing)*2)}.min-h-\[1\.5em\]{min-height:1.5em}.min-h-\[36px\]{min-height:36px}.min-h-\[52px\]{min-height:52px}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-1{width:calc(var(--spacing)*1)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-4\.5{width:calc(var(--spacing)*4.5)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-\[1\.5px\]{width:1.5px}.w-\[2\.5rem\]{width:2.5rem}.w-\[18px\]{width:18px}.w-\[95\.37px\]{width:95.37px}.w-\[103px\]{width:103px}.w-\[calc\(\(\(\(100vw-\(2\*var\(--spacing-g2\)\)\)-\(23\*var\(--spacing-g1\)\)\)\/24\)\*18\+\(17\*var\(--spacing-g1\)\)\)\]{width:calc((((100vw - (2*var(--spacing-g2))) - (23*var(--spacing-g1)))/24)*18 + (17*var(--spacing-g1)))}.w-auto{width:auto}.w-full{width:100%}.w-max{width:max-content}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-\[48ch\]{max-width:48ch}.max-w-\[320px\]{max-width:320px}.max-w-\[340px\]{max-width:340px}.max-w-\[580px\]{max-width:580px}.max-w-\[calc\(100\%-12px\)\]{max-width:calc(100% - 12px)}.max-w-prose{max-width:65ch}.max-w-prose-medium-wide{max-width:var(--spacing-prose-medium-wide)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-\[2rem\]{min-width:2rem}.min-w-\[8rem\]{min-width:8rem}.min-w-\[10rem\]{min-width:10rem}.min-w-\[220px\]{min-width:220px}.min-w-\[340px\]{min-width:340px}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.shrink-0{flex-shrink:0}.grow,.grow-1{flex-grow:1}.-translate-x-1\/2{--tw-translate-x:calc(calc(1/2*100%)*-1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1/2*100%)*-1);translate:var(--tw-translate-x) var(--tw-translate-y)}.video-smooth{backface-visibility:hidden;image-rendering:-webkit-optimize-quality;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translateZ(0) scale(1)}.animate-pulse{animation:var(--animate-pulse)}.cursor-default{cursor:default}.cursor-ew-resize{cursor:ew-resize}.cursor-nesw-resize{cursor:nesw-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-ns-resize{cursor:ns-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.snap-x{scroll-snap-type:x var(--tw-scroll-snap-strictness)}.snap-mandatory{--tw-scroll-snap-strictness:mandatory}.snap-start{scroll-snap-align:start}.scroll-px-g2{scroll-padding-inline:var(--spacing-g2)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.grid-cols-\[1fr_auto_auto\]{grid-template-columns:1fr auto auto}.grid-cols-\[repeat\(2\,auto\)\]{grid-template-columns:repeat(2,auto)}.grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-row-reverse{flex-direction:row-reverse}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0{gap:calc(var(--spacing)*0)}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-2\.5{gap:calc(var(--spacing)*2.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-g0\.5{gap:var(--spacing-g0\.5)}.gap-g0\.75{gap:var(--spacing-g0\.75)}.gap-g1{gap:var(--spacing-g1)}.gap-g1\.5{gap:var(--spacing-g1\.5)}.gap-px{gap:1px}.gap-v2{gap:var(--spacing-v2)}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*0)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*0)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-v6\/12>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing-v6\/12)*var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing-v6\/12)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-v9\/12>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing-v9\/12)*var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing-v9\/12)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-\(--grid-gap\){column-gap:var(--grid-gap)}.gap-x-g1{column-gap:var(--spacing-g1)}:where(.space-x-\(--grid-gap\)>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(var(--grid-gap)*var(--tw-space-x-reverse));margin-inline-end:calc(var(--grid-gap)*calc(1 - var(--tw-space-x-reverse)))}.gap-y-v2{row-gap:var(--spacing-v2)}.justify-self-end{justify-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.overscroll-auto{overscroll-behavior:auto}.overscroll-y-auto{overscroll-behavior-y:auto}.rounded{border-radius:.25rem}.rounded-2xs{border-radius:var(--radius-2xs)}.rounded-\[10px\]{border-radius:10px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-l-\[2px\]{border-top-left-radius:2px;border-bottom-left-radius:2px}.rounded-r-\[2px\]{border-top-right-radius:2px;border-bottom-right-radius:2px}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-\[1\.5px\]{border-style:var(--tw-border-style);border-width:1.5px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.start-hero-media:after{border-color:var(--color-theme-border-02)}[data-theme=dark] .start-hero-media:after{border-color:var(--color-theme-border-02)}.border-theme-border{border-color:var(--color-theme-border)}.border-theme-border-01{border-color:var(--color-theme-border-01)}.border-theme-border-02{border-color:var(--color-theme-border-02)}.border-theme-text{border-color:var(--color-theme-text)}.border-theme-text\/20{border-color:#26251e33}@supports (color:color-mix(in lab,red,red)){.border-theme-text\/20{border-color:color-mix(in oklab,var(--color-theme-text) 20%,transparent)}}.border-transparent{border-color:#0000}.bg-black{background-color:var(--color-black)}.bg-theme-bg{background-color:var(--color-theme-bg)}.bg-theme-border-02{background-color:var(--color-theme-border-02)}.bg-theme-button-bg{background-color:var(--color-theme-button-bg)}.bg-theme-card{background-color:var(--color-theme-card)}.bg-theme-card-02-hex{background-color:var(--color-theme-card-02-hex)}.bg-theme-card-02-hex\/50{background-color:#ebeae580}@supports (color:color-mix(in lab,red,red)){.bg-theme-card-02-hex\/50{background-color:color-mix(in oklab,var(--color-theme-card-02-hex) 50%,transparent)}}.bg-theme-card-02-hex\/80{background-color:#ebeae5cc}@supports (color:color-mix(in lab,red,red)){.bg-theme-card-02-hex\/80{background-color:color-mix(in oklab,var(--color-theme-card-02-hex) 80%,transparent)}}.bg-theme-card-03-hex{background-color:var(--color-theme-card-03-hex)}.bg-theme-card-04-hex{background-color:var(--color-theme-card-04-hex)}.bg-theme-card-hex{background-color:var(--color-theme-card-hex)}.bg-theme-fg-10{background-color:var(--color-theme-fg-10)}.bg-theme-product-chrome{background-color:var(--color-theme-product-chrome)}.bg-theme-product-editor{background-color:var(--color-theme-product-editor)}.bg-theme-text{background-color:var(--color-theme-text)}.bg-theme-text\/30{background-color:#26251e4d}@supports (color:color-mix(in lab,red,red)){.bg-theme-text\/30{background-color:color-mix(in oklab,var(--color-theme-text) 30%,transparent)}}.bg-transparent{background-color:#0000}.bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-\[image\:var\(--color-theme-card-03\)\]{background-image:var(--color-theme-card-03)}.from-theme-product-chrome{--tw-gradient-from:var(--color-theme-product-chrome);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from) var(--tw-gradient-from-position),var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from) var(--tw-gradient-from-position),var(--tw-gradient-to) var(--tw-gradient-to-position))}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.px-0{padding-inline:calc(var(--spacing)*0)}.px-0\.5{padding-inline:calc(var(--spacing)*.5)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-g1{padding-inline:var(--spacing-g1)}.px-g1\.5{padding-inline:var(--spacing-g1\.5)}.px-g2{padding-inline:var(--spacing-g2)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-0\.75{padding-block:calc(var(--spacing)*.75)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-1\.25{padding-block:calc(var(--spacing)*1.25)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-g0\.75{padding-block:var(--spacing-g0\.75)}.py-v2\.5\/12{padding-block:var(--spacing-v2\.5\/12)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-0\.5{padding-top:calc(var(--spacing)*.5)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-1\.5{padding-top:calc(var(--spacing)*1.5)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-2\.5{padding-top:calc(var(--spacing)*2.5)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-5{padding-top:calc(var(--spacing)*5)}.pt-10{padding-top:calc(var(--spacing)*10)}.pt-\[var\(--site-header-height\)\]{padding-top:var(--site-header-height)}.pt-g1\.75{padding-top:var(--spacing-g1\.75)}.pt-v3{padding-top:var(--spacing-v3)}.pt-v3\/12{padding-top:var(--spacing-v3\/12)}.pt-v8\/12{padding-top:var(--spacing-v8\/12)}.pr-1\.5{padding-right:calc(var(--spacing)*1.5)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-3{padding-right:calc(var(--spacing)*3)}.pb-0{padding-bottom:calc(var(--spacing)*0)}.pb-1{padding-bottom:calc(var(--spacing)*1)}.pb-1\.5{padding-bottom:calc(var(--spacing)*1.5)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pb-6{padding-bottom:calc(var(--spacing)*6)}.pb-20{padding-bottom:calc(var(--spacing)*20)}.pb-g2{padding-bottom:var(--spacing-g2)}.pb-px{padding-bottom:1px}.pb-v2\.5\/12{padding-bottom:var(--spacing-v2\.5\/12)}.pb-v3{padding-bottom:var(--spacing-v3)}.pl-0{padding-left:calc(var(--spacing)*0)}.pl-1{padding-left:calc(var(--spacing)*1)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-3{padding-left:calc(var(--spacing)*3)}.pl-g0\.5{padding-left:var(--spacing-g0\.5)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.type-product-sm-medium{font-family:var(--font-system);font-size:var(--text-product-sm);line-height:var(--leading-product-sm);letter-spacing:var(--tracking-product-sm);font-weight:var(--font-weight-medium)}.type-product-sm{font-family:var(--font-system);font-size:var(--text-product-sm);line-height:var(--leading-product-sm);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-product-sm)}.type-sm{font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-normal);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-sm)}.type-product-base-medium{font-family:var(--font-system);font-size:var(--text-product-base);line-height:var(--leading-product-base);font-weight:var(--font-weight-medium)}.type-product-base{font-family:var(--font-system);font-size:var(--text-product-base);line-height:var(--leading-product-base);font-weight:var(--font-weight-normal)}.type-product-base-mono{font-family:var(--font-berkeley-mono),ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:var(--text-product-base);line-height:var(--leading-product-base-mono);font-weight:var(--font-weight-normal)}.type-product-lg{font-family:var(--font-system);font-size:var(--text-product-lg);line-height:var(--leading-product-base);font-weight:var(--font-weight-normal)}.font-berkeley-mono{font-family:var(--font-berkeley-mono),ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.type-base{font-size:var(--text-base);line-height:var(--leading-normal);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-base)}.type-lg{font-size:var(--text-lg);line-height:var(--leading-xsnug);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-lg)}.type-md-lg{font-size:var(--text-md-lg);line-height:var(--leading-snug);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-md-lg)}.type-xl{font-size:var(--text-xl);line-height:var(--leading-2xsnug);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-xl)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[1\.08rem\]{font-size:1.08rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[20px\]{font-size:20px}.\!leading-none{--tw-leading:1!important;line-height:1!important}.leading-\[1\.35\]{--tw-leading:1.35;line-height:1.35}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.text-balance{text-wrap:balance}.text-pretty{text-wrap:pretty}.break-words{overflow-wrap:break-word}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-green-600{color:var(--color-green-600)}.text-red-600{color:var(--color-red-600)}.text-theme-button-text{color:var(--color-theme-button-text)}.text-theme-product-ansi-green{color:var(--color-theme-product-ansi-green)}.text-theme-product-ansi-red{color:var(--color-theme-product-ansi-red)}.text-theme-product-text-sec{color:var(--color-theme-product-text-sec)}.text-theme-text{color:var(--color-theme-text)}.text-theme-text-mid{color:var(--color-theme-text-mid)}.text-theme-text-sec{color:var(--color-theme-text-sec)}.text-theme-text\/50{color:#26251e80}@supports (color:color-mix(in lab,red,red)){.text-theme-text\/50{color:color-mix(in oklab,var(--color-theme-text) 50%,transparent)}}.text-theme-text\/90{color:#26251ee6}@supports (color:color-mix(in lab,red,red)){.text-theme-text\/90{color:color-mix(in oklab,var(--color-theme-text) 90%,transparent)}}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.logo-monochrome:hover{opacity:1}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.opacity-100{opacity:1}.shadow-\[var\(--shadow-flyout\)\]{--tw-shadow:var(--shadow-flyout);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.transition-\[border-color\]{transition-property:border-color;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:pointer-events-auto:is(:where(.group):hover *){pointer-events:auto}.group-hover\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:scale-\[1\.02\]:is(:where(.group):hover *){scale:1.02}.group-hover\:bg-theme-card-02-hex:is(:where(.group):hover *){background-color:var(--color-theme-card-02-hex)}.group-hover\:text-theme-accent:is(:where(.group):hover *){color:var(--color-theme-accent)}.group-hover\:text-theme-text:is(:where(.group):hover *){color:var(--color-theme-text)}.group-hover\:text-theme-text-sec:is(:where(.group):hover *){color:var(--color-theme-text-sec)}.group-hover\:opacity-60:is(:where(.group):hover *){opacity:.6}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.group-hover\/add-tool\:bg-theme-card-hover-hex:is(:where(.group\/add-tool):hover *),.group-hover\/add-trigger\:bg-theme-card-hover-hex:is(:where(.group\/add-trigger):hover *){background-color:var(--color-theme-card-hover-hex)}.group-hover\/added\:opacity-100:is(:where(.group\/added):hover *){opacity:1}.group-hover\/agent\:bg-\[\#3b82f6\]:is(:where(.group\/agent):hover *){background-color:#3b82f6}.group-hover\/agent\:text-theme-product-ansi-green:is(:where(.group\/agent):hover *){color:var(--color-theme-product-ansi-green)}.group-hover\/agent\:text-theme-text:is(:where(.group\/agent):hover *){color:var(--color-theme-text)}.group-hover\/agent\:opacity-100:is(:where(.group\/agent):hover *){opacity:1}.group-hover\/copy\:text-theme-text-sec:is(:where(.group\/copy):hover *){color:var(--color-theme-text-sec)}.group-hover\/desktop\:scale-105:is(:where(.group\/desktop):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\/desktop\:scale-110:is(:where(.group\/desktop):hover *){--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\/desktop\:bg-black\/60:is(:where(.group\/desktop):hover *){background-color:#0009}@supports (color:color-mix(in lab,red,red)){.group-hover\/desktop\:bg-black\/60:is(:where(.group\/desktop):hover *){background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.group-hover\/desktop\:opacity-100:is(:where(.group\/desktop):hover *){opacity:1}.group-hover\/event-title\:text-theme-accent:is(:where(.group\/event-title):hover *){color:var(--color-theme-accent)}.group-hover\/link\:translate-x-0\.5:is(:where(.group\/link):hover *){--tw-translate-x:calc(var(--spacing)*.5);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\/link\:-translate-y-0\.5:is(:where(.group\/link):hover *){--tw-translate-y:calc(var(--spacing)*-.5);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\/mobile-loop\:scale-100:is(:where(.group\/mobile-loop):hover *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\/mobile-loop\:opacity-100:is(:where(.group\/mobile-loop):hover *){opacity:1}.group-hover\/pill\:bg-theme-fg-15:is(:where(.group\/pill):hover *){background-color:var(--color-theme-fg-15)}.group-hover\/rec\:scale-110:is(:where(.group\/rec):hover *){--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\/rec\:opacity-100:is(:where(.group\/rec):hover *){opacity:1}.group-hover\/row\:opacity-0:is(:where(.group\/row):hover *){opacity:0}.group-hover\/row\:opacity-100:is(:where(.group\/row):hover *),.group-hover\/tab\:opacity-100:is(:where(.group\/tab):hover *),.group-hover\/trigger-row\:opacity-100:is(:where(.group\/trigger-row):hover *){opacity:1}}.selection\:bg-theme-text\! ::selection{background-color:var(--color-theme-text)!important}.selection\:bg-theme-text\!::selection{background-color:var(--color-theme-text)!important}.selection\:text-theme-bg\! ::selection{color:var(--color-theme-bg)!important}.selection\:text-theme-bg\!::selection{color:var(--color-theme-bg)!important}.placeholder\:text-base::placeholder{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.placeholder\:text-sm::placeholder{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.placeholder\:text-\[color\:var\(--mobile-phone-placeholder\)\]::placeholder{color:var(--mobile-phone-placeholder)}.placeholder\:text-\[color\:var\(--visual-feedback-number-text\)\]::placeholder{color:var(--visual-feedback-number-text)}.placeholder\:text-\[var\(--color-theme-text-mid\)\]::placeholder{color:var(--color-theme-text-mid)}.placeholder\:text-neutral-500\!::placeholder{color:var(--color-neutral-500)!important}.placeholder\:text-theme-text::placeholder{color:var(--color-theme-text)}.placeholder\:text-theme-text-sec::placeholder{color:var(--color-theme-text-sec)}.placeholder\:opacity-60::placeholder{opacity:.6}.placeholder\:opacity-100::placeholder{opacity:1}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:top-0:before{content:var(--tw-content);top:calc(var(--spacing)*0)}.before\:bottom-0:before{content:var(--tw-content);bottom:calc(var(--spacing)*0)}.before\:left-0:before{content:var(--tw-content);left:calc(var(--spacing)*0)}.before\:hidden:before{content:var(--tw-content);display:none}.before\:w-0\.5:before{content:var(--tw-content);width:calc(var(--spacing)*.5)}.before\:rounded-lg:before{content:var(--tw-content);border-radius:var(--radius-lg)}.before\:bg-theme-border-02:before{content:var(--tw-content);background-color:var(--color-theme-border-02)}.before\:transition-colors:before{content:var(--tw-content);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.before\:duration-\[var\(--duration\)\]:before{content:var(--tw-content);--tw-duration:var(--duration);transition-duration:var(--duration)}.before\:ease-\[var\(--ease-out-spring\)\]:before{content:var(--tw-content);--tw-ease:var(--ease-out-spring);transition-timing-function:var(--ease-out-spring)}.after\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-\[-0\.5rem\]:after{content:var(--tw-content);inset:-.5rem}.after\:inset-\[-4px\]:after{content:var(--tw-content);inset:-4px}.after\:right-0:after{content:var(--tw-content);right:calc(var(--spacing)*0)}.after\:bottom-0:after{content:var(--tw-content);bottom:calc(var(--spacing)*0)}.after\:left-0:after{content:var(--tw-content);left:calc(var(--spacing)*0)}.after\:h-px:after{content:var(--tw-content);height:1px}.after\:rounded-\[22px\]:after{content:var(--tw-content);border-radius:22px}.after\:rounded-\[23px\]:after{content:var(--tw-content);border-radius:23px}.after\:border:after{content:var(--tw-content);border-style:var(--tw-border-style);border-width:1px}.after\:border-\[color\:var\(--visual-feedback-selected-border\)\]:after{content:var(--tw-content);border-color:var(--visual-feedback-selected-border)}.after\:bg-theme-product-editor:after{content:var(--tw-content);background-color:var(--color-theme-product-editor)}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.focus-within\:relative:focus-within{position:relative}.focus-within\:z-1:focus-within{z-index:1}.focus-within\:w-\[15rem\]:focus-within{width:15rem}.focus-within\:max-w-\[15rem\]:focus-within{max-width:15rem}.focus-within\:border-theme-border:focus-within{border-color:var(--color-theme-border)}.focus-within\:border-theme-fg:focus-within{border-color:var(--color-theme-fg)}.focus-within\:bg-theme-card-01-hex:focus-within{background-color:var(--color-theme-card-01-hex)}.focus-within\:bg-theme-product-editor:focus-within{background-color:var(--color-theme-product-editor)}@media (hover:hover){.hover\:-translate-y-1:hover{--tw-translate-y:calc(var(--spacing)*-1);translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:border-red-500:hover{border-color:var(--color-red-500)}.hover\:border-theme-border:hover{border-color:var(--color-theme-border)}.hover\:border-theme-border-01:hover{border-color:var(--color-theme-border-01)}.hover\:border-theme-border-02:hover{border-color:var(--color-theme-border-02)}.hover\:border-theme-border-02-5:hover{border-color:var(--color-theme-border-02-5)}.hover\:border-theme-border-02\/40:hover{border-color:color-mix(in srgb,--alpha(var(--color-theme-fg)/10%) 40%,transparent)}@supports (color:color-mix(in lab,red,red)){.hover\:border-theme-border-02\/40:hover{border-color:color-mix(in oklab,var(--color-theme-border-02) 40%,transparent)}}.hover\:border-theme-text-sec:hover{border-color:var(--color-theme-text-sec)}.hover\:border-theme-text-tertiary:hover{border-color:var(--color-theme-text-tertiary)}.hover\:\!bg-theme-card-01-hex:hover{background-color:var(--color-theme-card-01-hex)!important}.hover\:\!bg-theme-card-03-hex:hover{background-color:var(--color-theme-card-03-hex)!important}.hover\:\!bg-theme-card-hex:hover{background-color:var(--color-theme-card-hex)!important}.hover\:bg-\[\#15692d\]:hover{background-color:#15692d}.hover\:bg-black\/60:hover{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/60:hover{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.hover\:bg-neutral-800:hover{background-color:var(--color-neutral-800)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-600\/90:hover{background-color:#e40014e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-600\/90:hover{background-color:color-mix(in oklab,var(--color-red-600) 90%,transparent)}}.hover\:bg-theme-accent\/90:hover{background-color:#f54e00e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-theme-accent\/90:hover{background-color:color-mix(in oklab,var(--color-theme-accent) 90%,transparent)}}.hover\:bg-theme-button-hover-bg:hover{background-color:var(--color-theme-button-hover-bg)}.hover\:bg-theme-card-01-hex:hover{background-color:var(--color-theme-card-01-hex)}.hover\:bg-theme-card-02-hex:hover{background-color:var(--color-theme-card-02-hex)}.hover\:bg-theme-card-02-hex\/60:hover{background-color:#ebeae599}@supports (color:color-mix(in lab,red,red)){.hover\:bg-theme-card-02-hex\/60:hover{background-color:color-mix(in oklab,var(--color-theme-card-02-hex) 60%,transparent)}}.hover\:bg-theme-card-03-hex:hover{background-color:var(--color-theme-card-03-hex)}.hover\:bg-theme-card-04-hex:hover{background-color:var(--color-theme-card-04-hex)}.hover\:bg-theme-card-hex:hover{background-color:var(--color-theme-card-hex)}.hover\:bg-theme-card-hover-hex:hover{background-color:var(--color-theme-card-hover-hex)}.hover\:bg-theme-card-hover-hex\/40:hover{background-color:#ebeae566}@supports (color:color-mix(in lab,red,red)){.hover\:bg-theme-card-hover-hex\/40:hover{background-color:color-mix(in oklab,var(--color-theme-card-hover-hex) 40%,transparent)}}.hover\:bg-theme-card-hover-hex\/60:hover{background-color:#ebeae599}@supports (color:color-mix(in lab,red,red)){.hover\:bg-theme-card-hover-hex\/60:hover{background-color:color-mix(in oklab,var(--color-theme-card-hover-hex) 60%,transparent)}}.hover\:bg-theme-text\/\[0\.04\]:hover{background-color:#26251e0a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-theme-text\/\[0\.04\]:hover{background-color:color-mix(in oklab,var(--color-theme-text) 4%,transparent)}}.hover\:bg-white\!:hover{background-color:var(--color-white)!important}.hover\:\[color\:--alpha\(var\(--color-theme-fg\)\/75\%\)\]:hover{color:#26251ebf}@supports (color:color-mix(in lab,red,red)){.hover\:\[color\:--alpha\(var\(--color-theme-fg\)\/75\%\)\]:hover{color:color-mix(in oklab,var(--color-theme-fg) 75%,transparent)}}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-theme-accent:hover{color:var(--color-theme-accent)}.hover\:text-theme-accent\/80:hover{color:#f54e00cc}@supports (color:color-mix(in lab,red,red)){.hover\:text-theme-accent\/80:hover{color:color-mix(in oklab,var(--color-theme-accent) 80%,transparent)}}.hover\:text-theme-text:hover{color:var(--color-theme-text)}.hover\:text-theme-text-sec:hover{color:var(--color-theme-text-sec)}.hover\:text-theme-text\/75:hover{color:#26251ebf}@supports (color:color-mix(in lab,red,red)){.hover\:text-theme-text\/75:hover{color:color-mix(in oklab,var(--color-theme-text) 75%,transparent)}}.hover\:text-white:hover{color:var(--color-white)}.hover\:no-underline:hover{text-decoration-line:none}.hover\:underline:hover{text-decoration-line:underline}.hover\:decoration-current:hover{text-decoration-color:currentColor}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-75:hover{opacity:.75}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}.hover\:brightness-110:hover{--tw-brightness:brightness(110%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}.focus\:\[border-color\:var\(--color-theme-fg\)\]:focus{border-color:var(--color-theme-fg)}.focus\:\!bg-theme-card-01-hex:focus{background-color:var(--color-theme-card-01-hex)!important}.focus\:bg-theme-card-hover-hex:focus{background-color:var(--color-theme-card-hover-hex)}.focus\:bg-white\!:focus{background-color:var(--color-white)!important}.focus\:opacity-100:focus{opacity:1}.focus\:ring-0:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-\[var\(--color-theme-accent\)\]:focus{--tw-ring-color:var(--color-theme-accent)}.focus\:ring-theme-fg:focus{--tw-ring-color:var(--color-theme-fg)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:pointer-events-auto:focus-visible{pointer-events:auto}.focus-visible\:text-theme-accent:focus-visible{color:var(--color-theme-accent)}.focus-visible\:text-theme-text:focus-visible{color:var(--color-theme-text)}.focus-visible\:opacity-100:focus-visible{opacity:1}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-theme-text\/20:focus-visible{--tw-ring-color:#26251e33}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-theme-text\/20:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-theme-text) 20%,transparent)}}.focus-visible\:focus-visible:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-indigo-600:focus-visible{outline-color:var(--color-indigo-600)}.focus-visible\:outline-theme-accent:focus-visible{outline-color:var(--color-theme-accent)}.focus-visible\:outline-theme-border-03:focus-visible{outline-color:var(--color-theme-border-03)}.focus-visible\:outline-theme-button-bg:focus-visible{outline-color:var(--color-theme-button-bg)}.focus-visible\:outline-theme-fg:focus-visible{outline-color:var(--color-theme-fg)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:cursor-grabbing:active{cursor:grabbing}.active\:\!bg-theme-card-02-hex:active{background-color:var(--color-theme-card-02-hex)!important}.active\:text-theme-text:active{color:var(--color-theme-text)}.active\:text-theme-text-mid:active{color:var(--color-theme-text-mid)}.active\:text-theme-text-sec:active{color:var(--color-theme-text-sec)}.disabled\:cursor-default:disabled{cursor:default}.disabled\:opacity-30:disabled{opacity:.3}@media (hover:hover){.data-\[interactive\]\:hover\:bg-theme-card-hover-hex[data-interactive]:hover{background-color:var(--color-theme-card-hover-hex)}}@media not all and (min-width:767px){.max-\[767px\]\:pointer-events-none{pointer-events:none}.max-\[767px\]\:opacity-0{opacity:0}}@media not all and (min-width:420px){.max-sm\:hidden{display:none}}@media (min-width:420px){.sm\:aspect-1\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:100%}.sm\:aspect-2\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:50%}.sm\:aspect-3\/2-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:66.6667%}.sm\:aspect-4\/3-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:75%}.sm\:aspect-4\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:125%}.sm\:aspect-5\/4-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:80%}.sm\:aspect-8\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:62.5%}.sm\:aspect-16\/9-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:56.25%}.sm\:aspect-natural-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:0}.sm\:block{display:block}.sm\:hidden{display:none}.sm\:w-\[calc\(\(\(\(100vw-\(2\*var\(--spacing-g2\)\)\)-\(23\*var\(--spacing-g1\)\)\)\/24\)\*16\+\(15\*var\(--spacing-g1\)\)\)\]{width:calc((((100vw - (2*var(--spacing-g2))) - (23*var(--spacing-g1)))/24)*16 + (15*var(--spacing-g1)))}.sm\:type-2xl{font-size:var(--text-2xl);line-height:var(--leading-tight);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-2xl)}@media (hover:hover){.sm\:group-hover\:pointer-events-auto:is(:where(.group):hover *){pointer-events:auto}.sm\:group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.sm\:focus-visible\:pointer-events-auto:focus-visible{pointer-events:auto}.sm\:focus-visible\:opacity-100:focus-visible{opacity:1}}@media (min-width:660px){.md\:aspect-1\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:100%}.md\:aspect-2\/1-box{grid-template-rows:repeat(1,minmax(0,1fr));grid-template-columns:repeat(1,minmax(0,1fr));display:grid}.md\:aspect-2\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:50%}.md\:aspect-3\/2-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:66.6667%}.md\:aspect-4\/3-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:75%}.md\:aspect-4\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:125%}.md\:aspect-5\/4-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:80%}.md\:aspect-8\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:62.5%}.md\:aspect-16\/9-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:56.25%}.md\:aspect-natural-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:0}.md\:right-0{right:calc(var(--spacing)*0)}.md\:left-auto{left:auto}.md\:col-start-1{grid-column-start:1}.md\:col-start-7{grid-column-start:7}.md\:col-end-7{grid-column-end:7}.md\:col-end-25{grid-column-end:25}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-\[calc\(\(\(\(100vw-\(2\*var\(--spacing-g2\)\)\)-\(23\*var\(--spacing-g1\)\)\)\/24\)\*14\+\(13\*var\(--spacing-g1\)\)\)\]{width:calc((((100vw - (2*var(--spacing-g2))) - (23*var(--spacing-g1)))/24)*14 + (13*var(--spacing-g1)))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:pb-g3{padding-bottom:var(--spacing-g3)}.md\:type-sm{font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-normal);font-weight:var(--font-weight-normal);letter-spacing:var(--tracking-sm)}.md\:group-focus-within\:pointer-events-auto:is(:where(.group):focus-within *){pointer-events:auto}.md\:group-focus-within\:opacity-100:is(:where(.group):focus-within *){opacity:1}.md\:group-focus-within\/item\:pointer-events-auto:is(:where(.group\/item):focus-within *){pointer-events:auto}.md\:group-focus-within\/item\:opacity-100:is(:where(.group\/item):focus-within *){opacity:1}@media (hover:hover){.md\:group-hover\:pointer-events-auto:is(:where(.group):hover *){pointer-events:auto}.md\:group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.md\:group-hover\/item\:pointer-events-auto:is(:where(.group\/item):hover *){pointer-events:auto}.md\:group-hover\/item\:opacity-100:is(:where(.group\/item):hover *){opacity:1}}.md\:focus-visible\:pointer-events-auto:focus-visible{pointer-events:auto}.md\:focus-visible\:opacity-100:focus-visible{opacity:1}}@media (min-width:900px){.lg\:aspect-1\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:100%}.lg\:aspect-2\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:50%}.lg\:aspect-3\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:33.3333%}.lg\:aspect-3\/2-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:66.6667%}.lg\:aspect-4\/3-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:75%}.lg\:aspect-4\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:125%}.lg\:aspect-5\/2-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:40%}.lg\:aspect-5\/4-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:80%}.lg\:aspect-8\/5-box{grid-template-rows:repeat(1,minmax(0,1fr));grid-template-columns:repeat(1,minmax(0,1fr));display:grid}.lg\:aspect-8\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:62.5%}.lg\:aspect-16\/9-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:56.25%}.lg\:aspect-natural-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:0}.lg\:col-start-1{grid-column-start:1}.lg\:col-start-3{grid-column-start:3}.lg\:col-start-9{grid-column-start:9}.lg\:col-end-9{grid-column-end:9}.lg\:col-end-25{grid-column-end:25}.lg\:col-end-\[-1\]{grid-column-end:-1}.lg\:mx-0{margin-inline:calc(var(--spacing)*0)}.lg\:mb-0{margin-bottom:calc(var(--spacing)*0)}.lg\:block{display:block}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:inline-flex{display:inline-flex}.lg\:w-\[95\.75px\]{width:95.75px}.lg\:w-auto{width:auto}.lg\:w-full{width:100%}.lg\:shrink{flex-shrink:1}.lg\:snap-none{scroll-snap-type:none}.lg\:scroll-px-0{scroll-padding-inline:calc(var(--spacing)*0)}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-\[auto_1fr_auto\]{grid-template-columns:auto 1fr auto}.lg\:overflow-visible{overflow:visible}.lg\:px-0{padding-inline:calc(var(--spacing)*0)}@media (hover:hover){.lg\:group-hover\/item\:opacity-100:is(:where(.group\/item):hover *){opacity:1}}}@media (min-width:1140px){.xl\:aspect-1\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:100%}.xl\:aspect-2\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:50%}.xl\:aspect-3\/2-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:66.6667%}.xl\:aspect-4\/3-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:75%}.xl\:aspect-4\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:125%}.xl\:aspect-5\/4-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:80%}.xl\:aspect-8\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:62.5%}.xl\:aspect-16\/9-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:56.25%}.xl\:aspect-natural-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:0}.xl\:inset-0{inset:calc(var(--spacing)*0)}.xl\:left-6{left:calc(var(--spacing)*6)}.xl\:col-start-1{grid-column-start:1}.xl\:col-start-7{grid-column-start:7}.xl\:col-end-7{grid-column-end:7}.xl\:col-end-19{grid-column-end:19}.xl\:block{display:block}.xl\:hidden{display:none}.xl\:inline{display:inline}.xl\:max-h-none{max-height:none}.xl\:translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x) var(--tw-translate-y)}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:1380px){.\32 xl\:aspect-1\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:100%}.\32 xl\:aspect-2\/1-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:50%}.\32 xl\:aspect-3\/2-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:66.6667%}.\32 xl\:aspect-4\/3-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:75%}.\32 xl\:aspect-4\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:125%}.\32 xl\:aspect-5\/4-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:80%}.\32 xl\:aspect-8\/5-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:62.5%}.\32 xl\:aspect-16\/9-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:56.25%}.\32 xl\:aspect-natural-box:after{content:"";pointer-events:none;grid-area:1/1/-1/-1;padding-top:0}}@container (min-width:24rem){.\@sm\:col-start-1{grid-column-start:1}.\@sm\:col-end-2{grid-column-end:2}}@media (prefers-color-scheme:dark){@media (hover:hover){.dark\:hover\:bg-\[\#1f7530\]:hover{background-color:#1f7530}.dark\:hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.dark\:hover\:bg-gray-800:hover{background-color:var(--color-gray-800)}}}@media (pointer:fine){@media (hover:hover){.pointer-fine\:group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}}.\[\&_\:\:selection\]\:bg-theme-bg ::selection{background-color:var(--color-theme-bg)}.\[\&_\:\:selection\]\:text-theme-text ::selection{color:var(--color-theme-text)}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button{appearance:none}.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{appearance:none}.\[\&\:\:-webkit-scrollbar\]\:hidden::-webkit-scrollbar{display:none}.\[\&\:\:-webkit-search-cancel-button\]\:appearance-none::-webkit-search-cancel-button{appearance:none}.\[\&\:\:-webkit-search-decoration\]\:appearance-none::-webkit-search-decoration{appearance:none}.\[\&\:\:-webkit-search-results-button\]\:appearance-none::-webkit-search-results-button{appearance:none}.\[\&\:\:-webkit-search-results-decoration\]\:appearance-none::-webkit-search-results-decoration{appearance:none}.\[\&\:\:after\]\:border-theme-border-02:after{border-color:var(--color-theme-border-02)}.\[\&\:\:before\]\:border-theme-border-01-5:before{border-color:var(--color-theme-border-01-5)}.thin-scrollbar{scrollbar-width:thin;scrollbar-color:var(--color-theme-fg-20) transparent}.thin-scrollbar::-webkit-scrollbar{width:8px;height:8px}.thin-scrollbar::-webkit-scrollbar-track{background:0 0}.thin-scrollbar::-webkit-scrollbar-thumb{background-color:var(--color-theme-fg-20);background-clip:padding-box;border:2px solid #0000;border-radius:50%}.thin-scrollbar::-webkit-scrollbar-corner{background:0 0}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.no-scrollbar::-webkit-scrollbar{display:none}.container{max-width:var(--max-width-container);width:100%;margin-inline:auto}@keyframes shimmer{0%{background-position:200%0}to{background-position:-200%0}}@keyframes shimmer-slide{0%{transform:translate(-100%)}to{transform:translate(200%)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes mobile-chat-enter{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes mobile-interaction-pulse{0%,to{opacity:.08;box-shadow:inset 0 0 0 1px rgb(var(--mobile-interaction-color)/.22),inset 0 0 8px rgb(var(--mobile-interaction-color)/.08)}50%{opacity:.2;box-shadow:inset 0 0 0 1px rgb(var(--mobile-interaction-color)/.52),inset 0 0 12px rgb(var(--mobile-interaction-color)/.24)}}@keyframes mobile-interaction-glow{0%,to{outline-color:rgb(var(--mobile-interaction-color)/.08);outline-offset:0;filter:drop-shadow(0 1px 2px rgb(0 0 0/var(--mobile-interaction-shadow-opacity-rest))) drop-shadow(0 0 0 rgb(var(--mobile-interaction-color)/0))}50%{outline-color:rgb(var(--mobile-interaction-color)/.2);outline-offset:2px;filter:drop-shadow(0 3px 5px rgb(0 0 0/var(--mobile-interaction-shadow-opacity-peak))) drop-shadow(0 0 5px rgb(var(--mobile-interaction-color)/.2))}}@keyframes mobile-workspace-interaction-pulse{0%,to{box-shadow:0 0 0 1px rgb(var(--mobile-interaction-accent-color)/.24);transform:scale(1)}50%{box-shadow:0 0 0 6px rgb(var(--mobile-interaction-accent-color)/0);transform:scale(1.24)}}.mobile-interaction-pulse[data-interaction-pulse]:after{content:"";pointer-events:none;z-index:40;border-radius:inherit;background:radial-gradient(circle at 50% 28%,rgb(var(--mobile-interaction-gradient-color)/.58),rgb(var(--mobile-interaction-gradient-color)/.16) 48%,transparent 76%);will-change:opacity,box-shadow;animation:2.8s ease-in-out infinite mobile-interaction-pulse;position:absolute;inset:0}.mobile-interaction-pulse--workspace-row[data-interaction-pulse]:after{display:none}@media (prefers-reduced-motion:reduce){.mobile-interaction-pulse[data-interaction-pulse]:after{opacity:.15;animation:none}}@keyframes mobileWorkingVerticalOpacity{0%,to{opacity:.72}20%{opacity:.28}40%{opacity:.72}60%,80%{opacity:.28}}@keyframes mobileWorkingHorizontalOpacity{0%,to{opacity:.72}20%,40%{opacity:.28}60%{opacity:.72}80%{opacity:.28}}@keyframes mobileWorkingCenterOpacity{0%,40%,60%,to{opacity:.18}20%,80%{opacity:.64}}@keyframes mobileRecordingWaveform{0%{transform:scaleY(var(--waveform-min))}50%{transform:scaleY(var(--waveform-mid))}to{transform:scaleY(var(--waveform-end))}}@keyframes shake{0%,to{transform:translate(0)}20%{transform:translate(-6px)}40%{transform:translate(5px)}60%{transform:translate(-3px)}80%{transform:translate(2px)}}@keyframes streaming-word-fade{0%{opacity:.4}to{opacity:1}}}.katex .katex-version:after{content:"0.16.39"}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .eqn-num:before{content:"("counter(katexEqnNo)")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"("counter(mmlEqnNo)")";counter-increment:mmlEqnNo}body{counter-reset:katexEqnNo mmlEqnNo}@keyframes newsletter-submit-pulse{0%,to{opacity:1}50%{opacity:.3}}@keyframes gallery-marquee-item-slide-up{0%{opacity:0;transform:translateY(25%)}to{opacity:1;transform:translate(0)}}@media (min-width:900px){:root{--button-padding-default:.78em 1.35em .8em;--button-padding-md-sm:.6em 1.25em .62em;--button-padding-sm:.4em .75em .42em;--button-padding-xs:.15em .5em;--site-header-height:52px}}.no-drag-img img{-webkit-user-drag:none;-webkit-user-select:none;user-select:none}.font-feature-case{font-feature-settings:"case"1}pre:has(.katex):before{display:none}pre:has(.katex-display):before{display:none}.mobile-app-qr-link:focus-visible{outline:2px solid var(--color-theme-fg);outline-offset:.3ch}@view-transition{navigation:none}::view-transition-old(root){animation-duration:0s}::view-transition-new(root){animation-duration:0s}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-leading{syntax:"*";inherits:false}@property --tw-border-spacing-x{syntax:"<length>";inherits:false;initial-value:0}@property --tw-border-spacing-y{syntax:"<length>";inherits:false;initial-value:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-scroll-snap-strictness{syntax:"*";inherits:false;initial-value:proximity}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0#0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0#0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0#0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0#0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0#0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}

/* ======================================================================
   block 2/6 — font faces
   ====================================================================== */
@font-face{font-family:CursorGothic;src:url(/fonts/8c871be81e3b01ee.woff2)format("woff2");font-weight:400;font-style:normal}@font-face{font-family:CursorGothic;src:url(/fonts/a9978131f519749a.woff2)format("woff2");font-weight:400;font-style:italic}@font-face{font-family:CursorGothic Fallback;src:local(Arial);ascent-override:92.76%;descent-override:23.7%;line-gap-override:9.67%;size-adjust:103.38%}.cursorgothic_71ba3fd8-module__Sj65MG__variable{--font-sans:"CursorGothic","CursorGothic Fallback",system-ui,Helvetica Neue,Helvetica,Arial,sans-serif}@font-face{font-family:berkeleyMono;src:url(/fonts/11d9573b6fc17704.woff2)format("woff2");font-weight:400;font-style:normal}@font-face{font-family:berkeleyMono;src:url(/fonts/2faa15c4271fa3d5.woff2)format("woff2");font-weight:400;font-style:italic}.berkeleymono_82e21e43-module__aDRXRq__variable{--font-berkeley-mono:"berkeleyMono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}@font-face{font-family:EB Garamond;font-style:italic;font-weight:400;src:url(/fonts/d2761541b314509e.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:EB Garamond;font-style:italic;font-weight:500;src:url(/fonts/d2761541b314509e.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:EB Garamond;font-style:normal;font-weight:400;src:url(/fonts/68fd4b247dde8998.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:EB Garamond;font-style:normal;font-weight:500;src:url(/fonts/68fd4b247dde8998.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}.eb_garamond_f923f39e-module__8qwdia__variable{--font-serif:"EB Garamond",Iowan Old Style,Palatino Linotype,URW Palladio L,P052,ui-serif,Georgia,Cambria,Times New Roman,Times,serif}

/* ======================================================================
   block 3/6 — keyframes
   ====================================================================== */
:root{--color-bg:var(--color-theme-bg);--color-text-primary:var(--color-theme-text);--color-text-muted:var(--color-theme-text-sec);--color-text-secondary:var(--color-theme-text-sec);--color-border:var(--color-theme-border-02);--color-accent:var(--color-theme-accent);--color-code-bg:var(--color-theme-card-hex);--color-code-text:var(--color-theme-text);--color-bg-hover:var(--color-theme-card-hover-hex);--color-border-bright:var(--color-theme-border-02-5);--color-success:var(--color-theme-product-ansi-green);--color-success-bg:color-mix(in srgb,var(--color-theme-product-ansi-green) 15%,transparent);--color-error:var(--color-theme-product-ansi-red);--color-error-bg:color-mix(in srgb,var(--color-theme-product-ansi-red) 15%,transparent);--color-warning:var(--color-theme-accent);--color-warning-bg:color-mix(in srgb,var(--color-theme-accent) 15%,transparent);--color-accent-bg:color-mix(in srgb,var(--color-theme-accent) 10%,transparent);--color-accent-bg-strong:color-mix(in srgb,var(--color-theme-accent) 25%,transparent);--color-info:var(--color-theme-border-03);--font-mono:var(--font-berkeley-mono),ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--spacing-xs:var(--spacing-g0\.25);--spacing-sm:var(--spacing-g0\.5);--spacing-md:var(--spacing-g1);--spacing-lg:var(--spacing-g1\.5);--spacing-xl:var(--spacing-g2);--spacing-2xl:var(--spacing-g3);--transition-fast:var(--duration);--color-bg-elevated:var(--color-theme-card-hex);--color-timeline-thinking:#dfa88f;--color-timeline-grep:#9fc9a2;--color-timeline-read:#9fbbe0;--color-timeline-edit:#c0a8dd}.prose-column a:hover{opacity:.7}.interactive-chip:focus-visible,.interactive-surface:focus-visible,.candidate-summary-row:focus-visible,.compressed-index:focus-visible,.mmap-compact-row:focus-visible,.suffix-row:focus-visible{box-shadow:0 0 0 2px var(--color-bg),0 0 0 4px var(--color-accent);outline:none}.text-input:focus,.pattern-input-wrapper:focus-within{border-color:var(--color-accent)}.control-button:hover{background:var(--color-bg-hover);border-color:var(--color-border-bright)}.step-dot:hover:not(.active){background:var(--color-border-bright)}.demo-search-input:focus{border-color:var(--color-accent)}.demo-tab:hover{color:var(--color-text-primary)}.part-button:hover{background:var(--color-bg-hover);border-color:var(--color-accent)}.clear-button:hover{border-color:var(--color-error);color:var(--color-error)}.run-benchmark-btn:hover:not(:disabled){filter:brightness(1.1)}.toggle-btn:hover{color:var(--color-text-primary)}.document-row:hover{border-color:var(--color-border-bright)}.doc-input:hover{border-color:var(--color-border)}.doc-input:focus{background:var(--color-bg);border-color:var(--color-accent)}.trigram-badge-sm:hover{border-color:var(--color-accent);background:var(--color-accent-bg)}.index-entry-compact:hover{background:var(--color-accent-bg)}.search-input-compact:focus{border-color:var(--color-accent)}.search-input-compact::placeholder{color:var(--color-text-muted)}.mode-btn:hover{color:var(--color-text-primary)}.search-input-wrapper:focus-within{border-color:var(--color-accent)}.search-input-wrapper .search-input-compact:focus{border-color:#0000}.select-input:focus{border-color:var(--color-accent)}.mode-toggle-btn:hover:not(.active){color:var(--color-text-secondary)}@keyframes pulse-emit{0%,to{transform:scale(1)}50%{transform:scale(1.1)}}.suffix-input:focus{border-color:var(--color-accent)}.suffix-row:hover{background:var(--color-bg-hover)}.suffix-row.in-range:hover{background:color-mix(in srgb,var(--color-success-bg) 80%,var(--color-bg-hover))}.toggle-steps-btn:hover{border-color:var(--color-accent);color:var(--color-accent)}.compressed-index:hover{background:var(--color-accent-bg);border-color:var(--color-accent)}.token-position-badge:hover{border-color:var(--color-accent);background:var(--color-accent-bg)}.phrase-index-entry:hover{background:var(--color-accent-bg)}@keyframes pulse-bit{0%{transform:scale(1)}to{transform:scale(1.1)}}.candidate-summary-row:hover{border-color:var(--color-border-bright);background:var(--color-code-bg)}.pattern-link:hover{background:var(--color-accent-bg);border-bottom-style:solid;border-radius:2px}.mmap-compact-row:hover{background:var(--color-accent-bg)}.agent-timeline-toggle-btn:hover{background:var(--color-bg-hover)}.agent-timeline-toggle-btn.is-active:hover{background:var(--color-accent)}

/* ======================================================================
   block 4/6 — sonner toast styles (vendored)
   ====================================================================== */
[data-sonner-toast]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-styled=true] [data-button]:focus-visible{box-shadow:0 0 0 2px rgba(0,0,0,.4)}[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover{background:var(--gray2);border-color:var(--gray5)}[data-sonner-toast][data-swiping=true]::before{content:"";position:absolute;left:-100%;right:-100%;height:100%;z-index:-1}[data-sonner-toast][data-y-position=top][data-swiping=true]::before{bottom:50%;transform:scaleY(3) translateY(50%)}[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before{top:50%;transform:scaleY(3) translateY(-50%)}[data-sonner-toast][data-swiping=false][data-removed=true]::before{content:"";position:absolute;inset:0;transform:scaleY(2)}[data-sonner-toast][data-expanded=true]::after{content:"";position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}[data-sonner-toast][data-removed=true][data-front=false]::before{height:calc(var(--initial-height) + 20%)}@keyframes swipe-out-left{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}100%{opacity:.15}}

/* ======================================================================
   block 5/6 — SingleFile capture helpers
   ====================================================================== */
.sf-hidden{display:none!important}

/* ======================================================================
   block 6/6 — SingleFile capture helpers
   ====================================================================== */
img[src="data:,"],source[src="data:,"]{display:none!important}
/*
 * /bid's hero window, filled with the app's map view.
 *
 * NOT generated by `pnpm run port`. Hand-maintained, imported by the product
 * page alone (see `astroPage` in tools/04-generate.mjs) and paired with
 * tools/fragments/bid-hero-mockup.mjs, which puts the markup inside the
 * captured window frame. Every number below was read out of bidlo-light-1.0
 * rather than eyeballed, and the app's own value is in the comment beside it.
 *
 * WHAT THIS IS
 * ------------
 * The same **advanced** layout complexity `src/styles/hero-mockup.css` draws —
 * no global header, no gutters or radii between the columns, main region on
 * `--background` — with a **map view** in the main region instead of a project
 * document page. A map view is a full-bleed Mapbox canvas with every control
 * floating over it, so unlike the home hero there is no page padding, no title
 * block and no view header band: below the content header, the map is the
 * whole region.
 *
 * SCALE
 * -----
 * **0.86**, the same ratio the home hero uses and for the same reason: it is
 * the capture's product type scale (`--text-product-base`, 12px) over the app's
 * body text (`text-sm`, 14px). Lengths are the app's value x 0.86 rounded to a
 * whole pixel, with the app's number in the comment. The same three standing
 * exceptions apply — type reads the capture's `--text-product-*` tokens
 * directly, **radii are not scaled at all**, and hairlines stay 1px.
 *
 * The map image is the one thing with no scale: a map is a picture of the
 * world, not of a UI, so it is drawn at its own size and only the chrome and
 * the markers on it are scaled.
 *
 * COLOUR
 * ------
 * The same `--bh-*` set the home hero declares as `--hm-*`, mapped onto the
 * app's tokens the same way (see that file's table), plus two this window needs
 * and it does not:
 *
 *   --bh-popover   the app's `--popover`, which the hover card sits on. In
 *                  light the app defines it as `--background` outright; in dark
 *                  it is `oklch(0.26 0 0)`, a step *lighter* than the dark
 *                  background, so dark restates it by hand rather than
 *                  inheriting an inversion. Same trap `--hc-doc-paper` and
 *                  `--im-pop-fill` document in their own files.
 *   --bh-frost     `bg-foreground/80 dark:bg-background/80` from
 *                  `components/ui/overlay-pill.ts`, which is what every
 *                  floating control on the map is painted in. **It inverts**:
 *                  in light these are dark pills with light glyphs, in dark
 *                  they are light pills with dark ones. That is the app, and
 *                  the reason is that a map control has to stay legible over
 *                  satellite imagery as well as over a mono street style.
 *
 * ONE COUPLING TO KNOW ABOUT
 * --------------------------
 * `src/scripts/empty-mockups.js` blanks a window mockup by finding a `<div>`
 * with exactly three small round children — the traffic lights — and hiding all
 * of that container's siblings. The filled body carries `data-mockup-filled` so
 * the script skips it, and **nothing in here may be a `<div>` with exactly
 * three round children of 5-15px**. Almost every box in the fragment is a
 * `<span>` for that reason, and the zoom pill's divider is deliberately 1px:
 * the script requires `b.width > 4` of all three children.
 */

.bid-hero-mockup {
  /* The home hero's table, verbatim. */
  --bh-surface: var(--color-theme-bg);
  --bh-chrome: var(--color-theme-card-hex);
  --bh-hover: var(--color-theme-card-02-hex);
  --bh-active: var(--color-theme-card-03-hex);
  --bh-line: var(--color-theme-border-02);
  --bh-text: var(--color-theme-product-text);
  --bh-text-sec: var(--color-theme-product-text-sec);
  --bh-text-dim: var(--color-theme-product-text-tertiary);
  --bh-accent: #2483e2;

  /* This window's own two — see COLOUR above. */
  --bh-popover: var(--color-theme-bg);
  --bh-frost: color-mix(in oklab, var(--color-theme-fg) 80%, transparent);
  --bh-frost-fg: var(--color-theme-bg);

  display: flex;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  background: var(--bh-surface);
  color: var(--bh-text);
  font-family: var(--font-system);
  font-size: var(--text-product-base);
  line-height: var(--leading-product-base);
  text-align: left;
  cursor: default;
}

:root[data-theme='dark'] .bid-hero-mockup {
  /* The app's dark `--popover` is oklch(0.26 0 0) — lighter than its dark
     `--background`, where the light ladder has them equal. Continued by hand at
     the same step `--hc-doc-paper` takes, and it must stay clear of
     `--color-theme-card-hover-hex` (#242424) or the card loses its own edge. */
  --bh-popover: #2a2a2a;
  --bh-frost: color-mix(in oklab, var(--color-theme-bg) 80%, transparent);
  --bh-frost-fg: var(--color-theme-fg);
}

.bid-hero-mockup *,
.bid-hero-mockup *::before,
.bid-hero-mockup *::after {
  box-sizing: border-box;
}

/* Lucide, the app's own set. `non-scaling-stroke` makes `stroke-width` mean
   device pixels rather than user units, so 1 is exactly 1px in every box at
   every size and matches the hairlines around it — see the home hero's
   stylesheet for the full argument. It has to go on the shapes: unlike
   `stroke-width`, the property does not inherit. */
.bid-hero-mockup svg {
  display: block;
  flex: none;
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bid-hero-mockup svg * {
  vector-effect: non-scaling-stroke;
}

.bh-ellipsis {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The capture's small tier, for the things that label a list rather than belong
   to it — here, the sidebar's section names and nothing else. */
.bh-section {
  font-size: var(--text-product-sm);
  line-height: var(--leading-product-sm);
  letter-spacing: var(--tracking-product-sm);
}

/* =========================================================================
   Left rail — the sidebar. A fork of the home hero's, value for value; see
   `src/styles/hero-mockup.css` for why each number is what it is. The one
   difference is behavioural and lives in the fragment: the map page is lit,
   because it is the route.
   ========================================================================= */

.bh-side {
  width: 190px;
  flex: none;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--bh-chrome);
  border-right: 1px solid var(--bh-line);
}

.bh-side-head {
  height: 45px; /* h-13 = 52 */
  flex: none;
  display: flex;
  align-items: center;
  gap: 1px;
  padding: 0 7px;
}

.bh-team {
  flex: 1;
  min-width: 0;
  height: 31px; /* h-9 = 36 */
  display: flex;
  align-items: center;
  padding-right: 7px;
  border-radius: 999px;
  font-weight: var(--font-weight-medium);
}

.bh-team-well {
  width: 34px; /* size-10 = 40 */
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 10px is the app's own `text-[10px]`, not 0.86 of it: an initial is a mark
   rather than a word and it has a 17px plate to sit in. */
.bh-avatar {
  width: 17px; /* size-5 = 20 */
  height: 17px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--bh-accent);
  color: #fff;
  font-size: 10px;
  line-height: 1;
  font-weight: var(--font-weight-medium);
}

.bh-icon-btn {
  width: 24px; /* size-9 = 36 */
  height: 24px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  color: var(--bh-text-dim);
}

.bh-nav {
  flex: none;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 7px 7px;
  font-size: var(--text-product-lg); /* the container is text-[15px] */
}

.bh-nav-item {
  height: 31px; /* h-sidebar-nav-item = 36 under advanced */
  display: flex;
  align-items: center;
  border-radius: 999px;
  color: var(--bh-text-dim);
}

.bh-nav-well {
  width: 31px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bh-accent);
}

.bh-nav-well svg {
  width: 16px; /* size-4.5 = 18 */
  height: 16px;
}

.bh-nav-item[data-active] {
  padding-right: 9px;
  background: var(--bh-active);
  color: var(--bh-text);
}

.bh-nav-item:not([data-active]) .bh-nav-label {
  display: none;
}

.bh-pages {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  padding: 0 7px;
}

.bh-section {
  height: 28px; /* h-sidebar-item = 32 under advanced */
  display: flex;
  align-items: center;
  padding: 0 7px;
  border-radius: 8px;
  color: var(--bh-text-dim);
  font-weight: var(--font-weight-medium);
}

.bh-section:not(:first-child) {
  margin-top: 6px;
}

.bh-page {
  height: 28px;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 0 7px;
  border-radius: 8px;
  color: var(--bh-text-sec);
}

.bh-page svg {
  width: 13px;
  height: 13px;
  color: var(--bh-text-dim);
}

/* The route IS this page, so this row is lit — the one place this sidebar and
   the home hero's differ. There the route is a project, which is in none of
   these lists, so nothing is lit at all. */
.bh-page[data-active] {
  background: var(--bh-active);
  color: var(--bh-text);
}

.bh-page[data-active] svg {
  color: var(--bh-text-sec);
}

.bh-side-foot {
  flex: none;
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px;
}

.bh-search {
  flex: 1;
  min-width: 0;
  height: 34px; /* h-10 = 40 */
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 0 10px;
  border: 1px solid var(--bh-line);
  border-radius: 999px;
  background: var(--bh-surface);
  color: var(--bh-text-dim);
  box-shadow: 0 1px 1px rgb(0 0 0 / 0.03);
}

.bh-round-btn {
  position: relative;
  width: 34px;
  height: 34px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--bh-line);
  border-radius: 999px;
  background: var(--bh-surface);
  color: var(--bh-text-sec);
}

.bh-badge {
  position: absolute;
  top: -1px;
  right: -1px;
  min-width: 13px;
  height: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--bh-accent);
  color: #fff;
  font-size: 9px;
  line-height: 1;
  font-weight: var(--font-weight-medium);
}

/* =========================================================================
   Centre column — the main region.
   ========================================================================= */

.bh-main {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--bh-surface);
}

/* group/content-header: advanced forces height 52 and padding 0 8, and removes
   the border-b the other two modes carry — so the app's own number here is 45.

   **39 SINCE 2026-09-01, AND IT IS SOLVED RATHER THAN PICKED.** It was reported
   as carrying too much air under it, with the ideal named: the map's filter row
   should land on the sidebar's own nav strip across the divider from it. That
   strip starts directly under the sidebar head (45) and its items are 31 tall,
   so its centre line is at **60.5**. A filter pill is 28 tall and its top is
   this header's height plus `.bh-toolbar`'s top padding, so those two have to
   come to 46.5. They are 39 + 8, which puts the pill's centre at 61 — half a
   pixel, which is as close as two integers get.

   Two things move with it and neither is optional. The crumb inside is still 31
   tall, so the header's own slack goes 7px a side to 4 — that is the "less
   bottom padding" half of the ask, and 4 is the floor, since 3 would leave the
   crumb's own hover fill touching the frame. And the canvas below takes the 6px
   this gives up, which is why `MAP.h` and `.bh-map-img` are 553 and why
   `bid-map-render.mjs` renders 1780x1106: at a fixed image height and a taller
   box the map would leave a 6px band of `--bh-hover` across the bottom, and at
   `height: 100%` it would rescale and walk every marker off its coordinate. */
.bh-main-head {
  height: 39px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 7px;
  padding: 0 7px;
}

/* `DocumentCrumb` is a `size-9` well and an `h-9` title field, so the whole
   crumb is **36 tall (31 here)** inside a 52 (45) header — exactly the ladder
   the sidebar's own head runs, where an `h-9` team row sits in the same `h-13`
   band. Both boxes are stated for that reason: this crumb had a width and no
   height until 2026-09-01, so it collapsed to its 17px glyph and the header
   read as a thick empty band whose content did not line up with the row across
   the divider from it. Give either of them a size and the other has to follow. */
.bh-crumb {
  height: 31px; /* the well and the title field are both h-9 = 36 */
  display: flex;
  align-items: center;
  min-width: 0;
}

/* A 36px well holding the document's own 20px icon. For a *page* document that
   icon is `ViewAvatarStatic` — the glyph the saved layout names in
   VIEW_LAYOUT_OPTIONS, which for "map" is lucide's Map. */
.bh-crumb-well {
  width: 31px; /* size-9 = 36 */
  height: 31px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bh-text-dim); /* DEFAULT_ICON_COLOR_CLASS */
}

.bh-crumb-well svg {
  width: 17px; /* size-5 = 20 */
  height: 17px;
}

/* The name is an inline text field, not a heading: `h-9 px-2 py-1 text-sm
   font-medium` with a transparent border that only appears on hover. */
.bh-crumb-title {
  height: 31px; /* h-9 = 36 */
  display: flex;
  align-items: center;
  margin-left: -1px;
  padding: 0 7px;
  font-weight: var(--font-weight-medium);
}

.bh-head-actions {
  display: flex;
  align-items: center;
  gap: 3px;
}

/* Two faces, not three — see the empty-mockups note in the header comment. */
.bh-faces {
  display: flex;
  align-items: center;
  margin-right: 3px;
}

.bh-face {
  width: 20px;
  height: 20px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  border: 1px solid var(--bh-surface);
  background: var(--bh-active);
  color: var(--bh-text-sec);
  font-size: 10px;
  line-height: 1;
  font-weight: var(--font-weight-medium);
}

.bh-face + .bh-face {
  margin-left: -4px;
}

/* =========================================================================
   The map.

   `MapResizeWrapper` is `flex-1 h-full relative min-w-0` over a map container
   that is `relative flex-1 h-full w-full overflow-hidden`, so the canvas takes
   everything under the content header: **890 x 553** in this window (1080 less
   the 190px sidebar; 620 less the 28px title bar and the 39px header).

   That box is why `tools/bid-map-render.mjs` renders 1780x1106 — a 1:1 retina
   source — and why `MAP` in the fragment carries the same two numbers: the
   markers are projected into this box, not laid out by eye.

   **The image is pinned at its own size rather than fitted.** Below about
   1100px of viewport the captured window is squeezed toward its `min-width:420px`
   and the main region narrows; a `100%`-wide image would rescale and every
   marker would drift off its coordinate. At a fixed 890x553 in an
   `overflow: hidden` box the frame simply crops from the right, which is what a
   window running off the edge of a container does anyway, and every marker stays
   exactly on its job.
   ========================================================================= */

.bh-map {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: var(--bh-hover); /* the app's own bg-muted/20 under a loading map */
}

.bh-map-img {
  position: absolute;
  left: 0;
  top: 0;
  width: 890px;
  height: 553px; /* see .bh-main-head — the header gave up 6px on 2026-09-01 */
  object-fit: cover;
}

/* The app picks its style off the resolved theme (`getMapStyle`: Bidlo's custom
   light style, and `mapbox/dark-v11`, which is what it falls back to), so this
   does too. Only one of the two is ever painted. */
.bid-hero-mockup [data-theme-dark] {
  display: none;
}

:root[data-theme='dark'] .bid-hero-mockup [data-theme-light] {
  display: none;
}

:root[data-theme='dark'] .bid-hero-mockup [data-theme-dark] {
  display: block;
}

/* -------------------------------------------------------------------------
   The markers.

   `MapMarker`'s default branch at `anchor="bottom"`: a 36px hit box whose
   bottom-centre is the coordinate, holding a 32px square rotated 45 degrees
   with three round corners and one sharp one — that sharp corner is the pin's
   point — and inside that a counter-rotated 30px disc with a 2px ring carrying
   the document's avatar.

   The radii are the app's own and are not scaled: `rounded-2xl` is Tailwind's
   flat 16px and `rounded-br-[5px]` is literal.
   ------------------------------------------------------------------------- */

.bh-markers {
  position: absolute;
  inset: 0;
  display: block;
}

.bh-pin {
  position: absolute;
  width: 31px; /* w-9 h-9 = 36 */
  height: 31px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* anchor="bottom": the box's bottom-centre sits on the point. */
  transform: translate(-50%, -100%);
}

/* A hovered marker and its card must sit over every other marker; without this
   a card opening downward from a northern job renders under the pins it
   crosses. */
.bh-pin:hover {
  z-index: 20;
}

.bh-pin-shell {
  width: 28px; /* w-8 h-8 = 32 */
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 16px 16px 5px 16px; /* rounded-2xl rounded-br-[5px] */
  background: #fff;
  transform: rotate(45deg);
  box-shadow:
    0 4px 6px -1px rgb(0 0 0 / 0.1),
    0 2px 4px -2px rgb(0 0 0 / 0.1); /* shadow-md */
}

.bh-pin-turn {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: rotate(-45deg);
}

.bh-pin-disc {
  width: 26px; /* size-7.5 = 30 */
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 2px solid #fff;
  border-radius: 999px;
  background: #fff;
}

/* The app's inner wrapper: `bg-primary` with `text-white`, then the avatar over
   it. Every one of these 23 projects carries an image icon (TxDOT's mark), so
   the picture covers the fill outright — the fill is drawn because it is what
   shows through an icon with transparency, and because it is the app. The
   marker's own `[&>span]:rounded-none!` is why the picture squares off and the
   disc above is what rounds it. */
.bh-pin-fill {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bh-accent);
}

/* **The white backdrop is a divergence and it is deliberate.** The platform's
   agency icon set is a mix of opaque and transparent PNGs — TxDOT's is RGB with
   its own white ground, Austin's and the Corps' are RGBA — and the app's
   `bg-primary` behind the avatar therefore swallows a transparent wordmark
   whole: a City of Austin marker comes out as a plain blue disc with a blue
   mark on it. The fill above is still drawn, because it is the app and it is
   what an icon with a transparent *edge* sits on; the picture just gets its own
   ground so every mark in the set reads the same way. Nothing changes for an
   opaque icon. */
.bh-pin-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #fff;
}

/* `isHighlighted` — `isMarkerActive`, which is the marker's own hover, so this
   is `:hover` and not a state attribute. Shell and ring both flip to the
   primary. */
.bh-pin:hover .bh-pin-shell {
  background: var(--bh-accent);
}

.bh-pin:hover .bh-pin-disc {
  border-color: var(--bh-accent);
}

/* -------------------------------------------------------------------------
   The hover card.

   `MapHoverPopup` -> `MapHoverCard` -> `GalleryCard`, at `anchor="bottom"
   offset={12}`. It is **closed at rest** — `hoveredPoint$` is null until the
   pointer is on a marker — and every card is a child of its own pin, so
   `.bh-pin:hover` is the whole mechanism and no script is involved.

   Two things ride on that nesting and both are load-bearing.

   `display: none` (rather than `visibility` or an opacity fade) is what keeps
   the **cover images off the page**: an element that generates no box never
   fetches its `background-image`, so nineteen Street View panoramas cost the
   hero nothing until a marker is hovered and then cost it exactly one. Do not
   swap it for a fade.

   And the card's `left` plus one of `top`/`bottom` are written by the fragment
   **against the pin's own border box**, not against the canvas — a card ABOVE
   its marker by its bottom edge and every other side by its top, since compact
   fields wrap and a card is 258 or 278 tall depending on whether its four
   values fit one line; the edge facing the marker is the one that has to stay
   put.

   `data-side` is Radix's own vocabulary and the app's: `top` is the shipped
   `anchor="bottom"`, and `bottom`/`right`/`left` are the fallbacks this file
   adds. A card fits above a marker or below it, and three of the nineteen sit
   in a band where neither is true — a 278px card wants 323px of clear canvas
   above or 292 below, and the canvas is 553 — so those go beside it.

   **That edge clears the marker's ink, not the coordinate.** In the app both
   the marker and the popup are `anchor="bottom"`, so the card's bottom lands
   `offset` above the *point* — i.e. inside a marker that is 31 tall and hangs
   4px lower still, its shell being a 28px square at `rotate(45deg)`. The app
   gets away with a card resting on its own marker because the reader watched it
   open under their pointer; a still does not, which is what it was reported for
   on 2026-09-01. So the fragment measures from the ink: 45px above the point
   and 14 below, both solved from `.bh-pin`'s height and `.bh-pin-shell`'s
   diagonal rather than typed. Move either and re-solve `CARD.pin` — the pin is a 31px square under
   `translate(-50%, -100%)`, so its origin is (x - 15.5, y - 31). The fragment
   solves the canvas box first (centred on the marker, bottom edge 10px above
   it, flipped below when it would run off the top, then clamped 8px inside the
   canvas on both axes) and subtracts. Every number it solves with is restated
   below and a test pins that the two files agree.
   ------------------------------------------------------------------------- */

.bh-card {
  position: absolute;
  display: none;
  width: 292px; /* w-[340px] */
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--bh-line); /* border-border/50, at 1px indistinguishable */
  border-radius: 10px; /* rounded-[10px], literal in the app and not scaled */
  background: var(--bh-popover);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); /* shadow-sm */
}

/* `MapHoverPopup` wraps the card in
   `origin-bottom animate-in fade-in-0 zoom-in-95 duration-100` — Tailwind's
   animate-in at its `--tw-enter-opacity: 0` / `--tw-enter-scale: .95` defaults
   over 100ms. That is the whole appearance the app has; there is no exit, the
   popup being unmounted the moment the pointer leaves the marker, which is what
   `display: none` here already does.

   It runs on `display: none -> flex` because a CSS animation starts when the
   element first generates a box, which is exactly when the pointer lands. So
   the resting still is unchanged and nothing is animated that was not hovered.

   `transform-origin` follows the side: the app is always `origin-bottom`
   because `anchor="bottom"` means its card is always above the point, and the
   three other sides are this file's own — growing a card below or beside a
   marker out of its bottom edge would be a card expanding away from the thing
   it belongs to. */
.bh-pin:hover .bh-card {
  display: flex;
  transform-origin: bottom;
  animation: bh-card-in 100ms ease-out;
}

.bh-pin:hover .bh-card[data-side='bottom'] {
  transform-origin: top;
}

.bh-pin:hover .bh-card[data-side='right'] {
  transform-origin: left;
}

.bh-pin:hover .bh-card[data-side='left'] {
  transform-origin: right;
}

@keyframes bh-card-in {
  from {
    opacity: 0;
    transform: scale(0.95);
  }
}

@media (prefers-reduced-motion: reduce) {
  .bh-pin:hover .bh-card {
    animation: none;
  }
}

/* `preview === "street_view"`: an `aspect-video` box holding the Street View
   Static response at `object-cover`. 164 is that ratio on the 292px card and is
   one of the four numbers the fragment adds up to place the card; the images
   are 584x328, a 2x source for exactly this box. `--bh-hover` is the app's
   `bg-hover` under the image, which shows for as long as it takes to arrive. */
.bh-card-cover {
  display: block;
  height: 164px;
  flex: none;
  background-color: var(--bh-hover);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* CardContent, non-unscheduled: `flex flex-col gap-1.5 px-2.5 pt-2.5 pb-2`.
   The card is 258 or 278 tall out of this box — see the placement note above. */
.bh-card-body {
  display: flex;
  flex-direction: column;
  gap: 5px; /* gap-1.5 = 6 */
  padding: 9px 9px 7px; /* px-2.5 pt-2.5 pb-2 */
}

.bh-card-title {
  display: flex;
  align-items: flex-start;
  gap: 7px; /* gap-2 = 8 */
  min-width: 0;
  font-weight: var(--font-weight-medium);
}

/* `show_document_icon` is saved true on this view, so the title leads with
   `PkAvatar` at `size-5 mt-0.5 shrink-0` — for these documents the project's
   own stored image icon. `DocumentIconGlyph`'s image branch is
   `rounded-[4px] overflow-hidden` over an `object-cover` fill. */
.bh-card-avatar {
  flex: none;
  width: 17px; /* size-5 = 20 */
  height: 17px;
  margin-top: 2px; /* mt-0.5 */
  overflow: hidden;
  border-radius: 4px; /* rounded-[4px], literal */
}

.bh-card-avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #fff; /* see .bh-pin-img */
}

/* `CARD_FIELD_ROWS_CLASS`, the `card_layout: "compact"` half:
   `flex-row flex-wrap items-center gap-x-3 gap-y-1`. It was the `list` half —
   `flex flex-col gap-1` — until 2026-09-01, when the view's saved layout
   changed and the two stacked values became four inline ones. Both halves are
   the same class in the same component; nothing here is a departure.

   The wrap is load-bearing rather than defensive: all four values are wider
   than the 274px content box together and land on exactly two lines at every
   card, which is what keeps `CARD.body` in the fragment one number the
   placement is solved from. A fifth field, or a longer one, has to be measured
   before it ships. */
.bh-card-rows {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 10px; /* gap-x-3 = 12 */
  row-gap: 3px; /* gap-y-1 = 4 */
  min-width: 0;
}

/* `GalleryCell`'s wrapper takes `w-fit max-w-full` under compact, where under
   list it fills the row. */
.bh-card-rows > * {
  width: fit-content;
  max-width: 100%;
}

/* A gallery card prints values and puts the field's *name* in a tooltip — there
   is no label row in read mode, and an empty field renders nothing at all
   (`GalleryCell` returns null). So a card with two configured fields and two
   stored values is exactly two lines of text. */
.bh-card-field {
  display: block;
  color: var(--bh-text);
}

/* -------------------------------------------------------------------------
   The Owner tag and the County relation, both of which resolve to `Color`.

   Same badge `items-mockup.css` draws and the same reason: both collections
   carry `icon_type: "color"`, so `DocumentPkAvatar` falls through to
   `bg-<c>-badge text-<c>-badge-foreground rounded px-1.5 leading-5 text-sm` —
   a 4px corner, no vertical padding, one hue per document taken from that
   document's own stored icon. Nine tones are drawn here against that file's
   five, because a county map reaches most of the palette; every one is the
   app's own oklch converted with the same formula, so `blue`, `green`,
   `yellow` and `orange` come out byte-identical to that sheet's without either
   file importing the other. Dark re-fills every hue at the higher alpha the
   app uses there and collapses the text to white at 87%.
   ------------------------------------------------------------------------- */

.bh-card-chip {
  display: inline-flex;
  align-items: center;
  min-width: 0;
  max-width: 100%;
  height: 17px; /* leading-5 = 20 */
  padding: 0 5px; /* px-1.5 = 6 */
  border-radius: 4px; /* rounded */
  background: var(--bh-chip-bg);
  color: var(--bh-chip-fg);
}

.bh-card-chip[data-tone='blue'] {
  --bh-chip-bg: rgb(65 139 198 / 0.27);
  --bh-chip-fg: #001f43;
}

.bh-card-chip[data-tone='green'] {
  --bh-chip-bg: rgb(80 158 114 / 0.27);
  --bh-chip-fg: #002f23;
}

.bh-card-chip[data-tone='yellow'] {
  --bh-chip-bg: rgb(243 171 28 / 0.39);
  --bh-chip-fg: #4b3314;
}

.bh-card-chip[data-tone='orange'] {
  --bh-chip-bg: rgb(226 146 18 / 0.27);
  --bh-chip-fg: #4e2b0d;
}

.bh-card-chip[data-tone='red'] {
  --bh-chip-bg: rgb(255 171 158 / 0.4);
  --bh-chip-fg: #71000b;
}

.bh-card-chip[data-tone='purple'] {
  --bh-chip-bg: rgb(155 120 201 / 0.27);
  --bh-chip-fg: #362348;
}

.bh-card-chip[data-tone='pink'] {
  --bh-chip-bg: rgb(216 146 187 / 0.27);
  --bh-chip-fg: #461b32;
}

.bh-card-chip[data-tone='brown'] {
  --bh-chip-bg: rgb(205 173 151 / 0.35);
  --bh-chip-fg: #4c2c1b;
}

.bh-card-chip[data-tone='gray'] {
  --bh-chip-bg: rgb(84 72 49 / 0.15);
  --bh-chip-fg: #1d1b16;
}

:root[data-theme='dark'] .bh-card-chip {
  --bh-chip-fg: rgb(255 255 255 / 0.87);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='blue'] {
  --bh-chip-bg: rgb(0 103 165 / 0.5);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='green'] {
  --bh-chip-bg: rgb(0 128 85 / 0.5);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='yellow'] {
  --bh-chip-bg: rgb(246 168 0 / 0.5);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='orange'] {
  --bh-chip-bg: rgb(239 146 0 / 0.45);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='red'] {
  --bh-chip-bg: rgb(238 107 93 / 0.45);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='purple'] {
  --bh-chip-bg: rgb(138 103 229 / 0.34);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='pink'] {
  --bh-chip-bg: rgb(213 97 181 / 0.4);
}

:root[data-theme='dark'] .bh-card-chip[data-tone='brown'] {
  --bh-chip-bg: rgb(189 124 91 / 0.45);
}

/* The one hue whose dark fill is opaque: `--gray-badge` is `--gray-primary`
   itself there, where every other tone is an alpha. */
:root[data-theme='dark'] .bh-card-chip[data-tone='gray'] {
  --bh-chip-bg: rgb(127 127 127);
}

/* CardFooter, map only: `px-1.5 pb-1.5 pt-0.5 flex gap-1.5 w-full mt-auto`. */
.bh-card-foot {
  display: flex;
  align-items: center;
  gap: 5px; /* gap-1.5 = 6 */
  padding: 2px 5px 5px; /* pt-0.5 px-1.5 pb-1.5 */
}

/* Both footer buttons are the same control — `<Button variant="secondary"
   size="sm" className="flex-1 bg-primary/20 text-primary shadow-none">` — so
   both are `flex-1`, both are **pills** (`buttonVariants` is `rounded-full` at
   its root and `size="sm"` restates it) and both carry a glyph and a word. The
   third, `Include`, is drawn only under haul-advantage mode. */
.bh-card-btn {
  flex: 1;
  height: 28px; /* h-8 = 32 */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px; /* gap-1.5 = 6 */
  border-radius: 999px; /* rounded-full */
  background: color-mix(in oklab, var(--bh-accent) 20%, transparent);
  color: var(--bh-accent);
  font-weight: var(--font-weight-medium);
}

.bh-card-btn svg {
  width: 14px; /* size-4 = 16 */
  height: 14px;
}

/* =========================================================================
   The floating chrome.

   One recipe, `components/ui/overlay-pill.ts`:

     OVERLAY_PILL_STYLES   "overflow-hidden rounded-full border border-foreground/10
                            bg-foreground/80 dark:bg-background/80"
     OVERLAY_BUTTON_STYLES  + "text-background dark:text-foreground"

   `--bh-frost` and `--bh-frost-fg` are those two, and they INVERT between
   themes — dark pills with light glyphs in light mode, light pills with dark
   glyphs in dark. See COLOUR at the top.
   ========================================================================= */

.bh-pill-btn,
.bh-zoom,
.bh-style-btn {
  border: 1px solid var(--bh-line);
  border-radius: 999px;
  background: var(--bh-frost);
  color: var(--bh-frost-fg);
  overflow: hidden;
}

.bh-pill-btn {
  width: 31px; /* h-9 w-9 = 36 */
  height: 31px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* `OVERLAY_BUTTON_STYLES`' third line, verbatim: the wash is an inset shadow
   rather than a background, because it has to LAYER ON the frosted fill instead
   of replacing it — a `hover:bg-*` here would drop the backdrop out from under
   the glyph. And like the fill it inverts: light washes toward `--background`,
   dark toward `--foreground`.

   `.bh-zoom-seg` takes `OVERLAY_CONTROL_HOVER` instead, which IS a background —
   that one is a segment inside a shared pill, so it has no fill of its own to
   preserve and lightens only the half under the pointer.

   All of it is guarded on `hover: hover`. A touch device leaves hover stuck on
   the last thing pressed, and a map whose Layers menu is pinned open because
   someone scrolled past it is a state the app never has. Same guard
   `integrations.css` puts on its model picker. */
@media (hover: hover) {
  .bh-pill-btn:hover,
  .bh-filter-btn:hover,
  .bh-style-btn:hover {
    box-shadow: inset 0 0 0 999px color-mix(in oklab, var(--color-theme-bg) 25%, transparent);
  }

  :root[data-theme='dark'] .bh-pill-btn:hover,
  :root[data-theme='dark'] .bh-filter-btn:hover,
  :root[data-theme='dark'] .bh-style-btn:hover {
    box-shadow: inset 0 0 0 999px color-mix(in oklab, var(--color-theme-fg) 15%, transparent);
  }

  .bh-zoom-seg:hover {
    background: color-mix(in oklab, var(--color-theme-bg) 25%, transparent);
  }

  :root[data-theme='dark'] .bh-zoom-seg:hover {
    background: color-mix(in oklab, var(--color-theme-fg) 15%, transparent);
  }
}

.bh-pill-btn svg {
  width: 17px; /* size-5 = 20 */
  height: 17px;
}

/* -------------------------------------------------------------------------
   The view's toolbar, floated over the top of the canvas.

   `ActiveSortsAndFilters` with `isOverlay`. Note the height: the bar is `h-14
   px-1` and the `complexity-advanced:h-12` that shortens it belongs to the
   NON-overlay branch, so a map's toolbar is 56 at every complexity. Its two
   clusters then carry `py-3 pl-2 pr-2` of their own, which is where the 10px
   inset comes from (4 + 8 = 12 at 0.86).

   What is in it is the app's default plus this view's three saved filters — see
   the long note in tools/fragments/bid-hero-mockup.mjs. There is no sort pill
   (this view saves no sort) and no "No place (n)" pill (every project has a
   location).
   ------------------------------------------------------------------------- */

.bh-toolbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 48px; /* h-14 = 56; complexity-advanced:h-12 is the non-overlay branch */
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  /* 8 on top rather than 10: that number and `.bh-main-head`'s height are one
     pair, solved together so the pill row centres on the sidebar's nav strip.
     Read the note on that rule before moving either. */
  padding: 8px 10px 10px;
}

/* `<HorizontalSortableFilterList>` and the add-filter button share one
   `flex items-center text-sm gap-1` row. */
.bh-toolbar-left {
  display: flex;
  align-items: center;
  gap: 3px; /* gap-1 = 4 */
  min-width: 0;
}

/* `PillWrapper`: `flex min-w-0 rounded-full px-2 h-7 items-center font-medium
   border`, and an active AND filter — which all three of these are — is
   `border-transparent text-primary bg-primary/20`.

   **It is h-8 and not h-7**, which is the one place reading `PillWrapper` alone
   gets you the wrong number: `HorizontalFilterItem` passes `className="h-8"`
   down through `FilterPill` to that wrapper, and it lands after the base class.
   So a filter pill is exactly as tall as the New button and the add-filter
   button beside it, which is what stops the row reading as three sizes.

   The opaque plate under it is the app's and is why a pill reads as solid over
   a map: `before:-inset-px before:rounded-full before:bg-muted`, which in the
   main region under advanced complexity is
   `complexity-advanced-main:before:bg-background`. It is drawn as a background
   on the pill itself rather than as a `::before` ring, the 1px of bleed being
   invisible under a fill that is already 20% opaque over it. */
.bh-pill {
  height: 28px; /* h-8 = 32 */
  flex: none;
  display: flex;
  align-items: center;
  padding: 0 7px; /* px-2 = 8 */
  border: 1px solid transparent;
  border-radius: 999px;
  background:
    linear-gradient(
      color-mix(in oklab, var(--bh-accent) 20%, transparent),
      color-mix(in oklab, var(--bh-accent) 20%, transparent)
    ),
    var(--bh-surface);
  color: var(--bh-accent);
  font-weight: var(--font-weight-medium);
}

/* `FieldAvatar` at `size-3.5 mr-[5px]`, then the field name and PillValue, then
   `ChevronDown` at `size-3.5 ml-0.5`. */
.bh-pill svg {
  width: 12px; /* size-3.5 = 14 */
  height: 12px;
}

.bh-pill-text {
  margin: 0 1px 0 4px; /* mr-[5px] on the glyph, ml-0.5 before the chevron */
}

/* The add-filter button. In overlay mode it takes OVERLAY_BUTTON_STYLES rather
   than the flat `text-muted-foreground` variant the other layouts draw. */
.bh-filter-btn {
  height: 28px; /* h-8 = 32 */
  flex: none;
  display: flex;
  align-items: center;
  gap: 3px; /* gap-1 = 4 */
  padding: 0 7px; /* px-2 = 8 */
  border: 1px solid var(--bh-line);
  border-radius: 999px;
  background: var(--bh-frost);
  color: var(--bh-frost-fg);
  font-weight: var(--font-weight-medium);
}

.bh-filter-btn svg {
  width: 14px; /* size-4 = 16 */
  height: 14px;
}

/* `<Button size="sm" variant="default">New</Button>` — the trigger of the map's
   Add-to-map popover, and the one saturated thing on the canvas. `rounded-full`
   comes from `buttonVariants`' root, not from a modifier. */
.bh-new-btn {
  height: 28px; /* h-8 = 32 */
  flex: none;
  display: flex;
  align-items: center;
  padding: 0 10px; /* px-3 = 12 */
  border-radius: 999px; /* rounded-full */
  background: var(--bh-accent);
  color: #fff;
  font-weight: var(--font-weight-medium);
}

/* -------------------------------------------------------------------------
   Bottom-left: the style selector, then tools and layers.
   `absolute bottom-3 left-3 flex items-end gap-1.5`.
   ------------------------------------------------------------------------- */

.bh-bl {
  position: absolute;
  bottom: 10px; /* bottom-3 = 12 */
  left: 10px;
  display: flex;
  align-items: flex-end;
  gap: 5px; /* gap-1.5 = 6 */
}

/* `h-16 w-16 rounded-xl p-1 shadow-sm` over the current style's thumbnail. The
   app anchors its radius ladder on `--radius: 0.625rem`, so `rounded-xl` is 14
   and `rounded-lg` is 10 — not Tailwind's unanchored 12 and 8. */
.bh-style-btn {
  width: 55px; /* h-16 w-16 = 64 */
  height: 55px;
  flex: none;
  padding: 3px; /* p-1 = 4 */
  border-radius: 14px;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

/* The hover wash replaces the plate's own drop shadow while it is on, which the
   app does too — `OVERLAY_BUTTON_STYLES`' inset and `shadow-sm` are one
   property, and the inset is what the button's `hover:` arm sets. The thumbnail
   sits at `z-10` above it, so only the 4px frame lightens. */
.bh-style-thumb {
  position: relative;
  z-index: 1;
}

.bh-style-thumb {
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 10px; /* rounded-lg */
}

.bh-style-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.bh-tools {
  display: flex;
  align-items: center;
  gap: 5px;
}

/* =========================================================================
   THE THREE MENUS THE BOTTOM-LEFT PAIR OPENS

   Style (a HoverCard), Layers (a Popover) and Tools (a DropdownMenu). All
   three open on hover in the app — the style button declares
   `<HoverCard openDelay={100} closeDelay={100}>` and the other two run through
   `useHoverOpenMenu`, whose only job is that a pointer resting on the trigger
   opens the menu without a click — so `:hover` on the host is the app's own
   gesture and not a convenience taken here.

   `.bh-menu-host` is one trigger and its menu. It is `position: relative` and
   the menu is absolutely positioned off its bottom-left corner, which is
   `align="start" side="top"` at the shared `sideOffset = 4` (3 here). Its
   z-index puts every menu over the markers and their cards: the chrome already
   paints after the marker layer, but a hover card opened on a marker under the
   menu would otherwise interleave with it.

   All three contents share the shell every Radix surface in this app carries —
   `bg-popover text-popover-foreground rounded-md border shadow-md` — and the
   same entrance, `data-[state=open]:animate-in fade-in-0 zoom-in-95` plus
   `data-[side=top]:slide-in-from-bottom-2`. That is one animation for all
   three, so it is one keyframe here. The style card is the one that overrides
   the radius: `rounded-xl p-0`.
   ========================================================================= */

.bh-menu-host {
  position: relative;
  display: flex;
}

.bh-pop {
  position: absolute;
  left: 0;
  bottom: calc(100% + 3px); /* sideOffset = 4 */
  z-index: 20;
  display: none;
  flex-direction: column;
  border: 1px solid var(--bh-line);
  border-radius: 6px; /* rounded-md */
  background: var(--bh-popover);
  color: var(--bh-text);
  /* shadow-md */
  box-shadow:
    0 4px 6px -1px rgb(0 0 0 / 0.1),
    0 2px 4px -2px rgb(0 0 0 / 0.1);
}

@media (hover: hover) {
  .bh-menu-host:hover .bh-pop {
    display: flex;
    transform-origin: bottom left; /* --radix-popper-transform-origin */
    animation: bh-pop-in 150ms ease-out;
  }
}

/* `fade-in-0 zoom-in-95 slide-in-from-bottom-2` — Tailwind's animate-in enter
   defaults, and the 2 is `--spacing` x 2 = 8px. */
@keyframes bh-pop-in {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.95);
  }
}

@media (prefers-reduced-motion: reduce) {
  .bh-menu-host:hover .bh-pop {
    animation: none;
  }
}

/* `w-80 p-2` on the layers popover, `w-52` on the tools menu (whose padding is
   `DropdownMenuContent`'s own `p-1`), `w-72 p-0 rounded-xl` on the style card.
   All three are the app's number at this window's 0.86. */
.bh-pop--layers {
  width: 275px; /* w-80 = 320 */
  padding: 7px; /* p-2 = 8 */
}

.bh-pop--tools {
  width: 179px; /* w-52 = 208 */
  padding: 3px; /* p-1 = 4 */
}

.bh-pop--style {
  width: 248px; /* w-72 = 288 */
  border-radius: 12px; /* rounded-xl = 14 */
  overflow: hidden;
}

/* `DropdownMenuLabel` and `CommandGroup`'s heading land in the same place and
   read the same, so one rule draws both: `px-2 py-1.5 text-secondary`, at
   `text-sm` for the label and `text-xs` for the heading — which at this scale
   is one step of the capture's product ladder apart. */
.bh-menu-head {
  display: flex;
  align-items: center;
  padding: 5px 7px; /* px-2 py-1.5 */
  color: var(--bh-text-sec);
  font-weight: var(--font-weight-medium);
}

.bh-cmd-head {
  display: flex;
  align-items: center;
  padding: 5px 7px;
  color: var(--bh-text-sec);
  font-size: var(--text-product-sm); /* text-xs against the label's text-sm */
  font-weight: var(--font-weight-medium);
}

/* `MenuItem` / `DropdownMenuItem` / `MenuToggle` / `MenuSelect`'s trigger are
   one row: `h-8 px-2 rounded-sm text-sm gap-2`, with glyphs at `size-5` in
   `text-muted-foreground-opaque`. */
.bh-menu-row {
  height: 28px; /* h-8 = 32 */
  display: flex;
  align-items: center;
  gap: 7px; /* gap-2 = 8 */
  padding: 0 7px; /* px-2 = 8 */
  border-radius: 3px; /* rounded-sm */
}

.bh-menu-row svg {
  width: 17px; /* size-5 = 20 */
  height: 17px;
  flex: none;
  color: var(--bh-text-sec); /* text-muted-foreground-opaque */
}

/* Every row in these menus takes `hover:bg-hover`. A still that draws a menu
   whose rows do not answer the pointer reads as a picture of a menu. */
@media (hover: hover) {
  .bh-menu-row--item:hover,
  .bh-cmd-item:not(.bh-cmd-item--primary):hover {
    background: var(--bh-hover);
  }
}

.bh-menu-label {
  flex: 1;
  min-width: 0;
}

/* `MenuSelect`'s value is `text-secondary whitespace-nowrap`, and its chevron is
   a `size-4 text-secondary` — smaller than a row glyph, which is why it is
   stated rather than left to the rule above. */
.bh-menu-value {
  color: var(--bh-text-sec);
  white-space: nowrap;
}

.bh-menu-row .bh-menu-value + svg {
  width: 14px; /* size-4 = 16 */
  height: 14px;
  margin-left: -3px; /* the row's gap-2 is wider than a chevron wants */
}

/* -------------------------------------------------------------------------
   The style card's own contents: `PopoverGroup className="p-3 pb-2"` over the
   four-thumbnail selector, then the toggles, then a separator and the foot.
   ------------------------------------------------------------------------- */

.bh-pop-group {
  display: flex;
  flex-direction: column;
  padding: 10px 10px 7px; /* p-3 pb-2 */
}

.bh-pop-group--foot {
  padding: 5px; /* p-1.5 = 6 */
}

.bh-pop-rule {
  height: 1px;
  flex: none;
  background: var(--bh-line);
}

/* `mb-2 grid grid-cols-4 gap-4 bg-hover rounded-md p-2` */
.bh-style-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px; /* gap-4 = 16 */
  margin-bottom: 7px; /* mb-2 = 8 */
  padding: 7px; /* p-2 = 8 */
  border-radius: 6px;
  background: var(--bh-hover);
}

/* `flex w-full flex-col items-center gap-1.5 text-muted-foreground`, and the
   selected one goes `text-primary`. */
.bh-style-opt {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px; /* gap-1.5 = 6 */
  min-width: 0;
  color: var(--bh-text-dim);
}

.bh-style-opt[data-selected] {
  color: var(--bh-accent);
}

/* `aspect-square w-full rounded-md ring-2 ring-transparent`, with the ring
   `ring-border` at rest and `ring-primary` on the selected one. A `box-shadow`
   ring rather than an outline, so it costs the thumbnail no width — same call
   `nav-panel.css` makes for the panel's own hairline. */
.bh-style-opt-thumb {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 6px;
  background: var(--bh-hover);
  box-shadow: 0 0 0 2px var(--bh-line);
}

.bh-style-opt[data-selected] .bh-style-opt-thumb {
  box-shadow: 0 0 0 2px var(--bh-accent);
}

.bh-style-opt-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* `text-xs font-medium` */
.bh-style-opt-label {
  font-size: var(--text-product-sm);
  font-weight: var(--font-weight-medium);
}

/* `Switch`: `h-5 w-8 p-0.5 rounded-full` over a `size-3.5` thumb. Every toggle
   in this card is off, so all four take the unchecked fill — `bg-foreground/30`
   in light and `bg-foreground/10` in dark, with the thumb at the page ground. */
.bh-switch {
  width: 28px; /* w-8 = 32 */
  height: 17px; /* h-5 = 20 */
  flex: none;
  display: flex;
  align-items: center;
  padding: 2px; /* p-0.5 */
  border-radius: 999px;
  background: var(--color-theme-fg-30, color-mix(in oklab, var(--color-theme-fg) 30%, transparent));
}

:root[data-theme='dark'] .bh-switch {
  background: color-mix(in oklab, var(--color-theme-fg) 10%, transparent);
}

.bh-switch-thumb {
  width: 12px; /* size-3.5 = 14 */
  height: 12px;
  border-radius: 999px;
  background: var(--bh-surface);
}

:root[data-theme='dark'] .bh-switch-thumb {
  background: var(--color-theme-fg);
}

/* -------------------------------------------------------------------------
   The layers popover's own contents. This view saves no layers, so what is
   drawn is `map-layers-popover.tsx`' `layers.length === 0` arm: the muted
   sentence, then the primary "Add layer" and the secondary closer under it —
   both of which centre their label at that count, which is the app and is why
   neither row is left-aligned like a tools row.
   ------------------------------------------------------------------------- */

/* `px-2 pb-2 pt-1 text-sm text-muted-foreground` */
.bh-cmd-empty {
  display: block;
  padding: 3px 7px 7px;
  color: var(--bh-text-sec);
}

.bh-cmd-item {
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 0 7px;
  border-radius: 3px;
}

.bh-cmd-item + .bh-cmd-item {
  margin-top: 2px; /* mt-0.5 */
}

.bh-cmd-item svg {
  width: 17px;
  height: 17px;
  flex: none;
  color: var(--bh-text-sec);
}

/* The empty state's primary CTA — `bg-primary text-primary-foreground`, with
   the glyph taking `text-primary-foreground!` rather than the muted colour
   every other row's does. */
.bh-cmd-item--primary {
  background: var(--bh-accent);
  color: #fff;
}

.bh-cmd-item--primary svg {
  color: #fff;
}

/* -------------------------------------------------------------------------
   Bottom-right: street view, then the zoom pill.
   `absolute bottom-3 right-3 flex items-center gap-1.5`.

   Note that the capture puts its second hero window over this corner from about
   1024px of viewport up, so at most widths this pair is behind it. It is drawn
   anyway: it is what the app has there, it is visible while the frame is
   narrow, and hiding it would mean a rule that answers to the landing page in a
   place the app has no state for.
   ------------------------------------------------------------------------- */

.bh-br {
  position: absolute;
  bottom: 10px;
  right: 10px;
  display: flex;
  align-items: center;
  gap: 5px;
}

.bh-zoom {
  display: flex;
  align-items: center;
}

.bh-zoom-seg {
  height: 31px; /* h-9 = 36 */
  display: flex;
  align-items: center;
  padding: 0 7px; /* px-2.5, overridden to px-2 = 8 in zoom-pill.tsx */
}

.bh-zoom-seg svg {
  width: 17px; /* size-5 = 20 */
  height: 17px;
}

/* `h-4 w-px bg-foreground/20 dark:bg-foreground/10`. THE 1px IS LOAD-BEARING:
   the zoom pill is a container with exactly three children, which is the shape
   `empty-mockups.js` hunts for, and it is only safe because that script
   requires all three to be wider than 4px. Do not widen it. */
.bh-zoom-rule {
  width: 1px;
  height: 14px; /* h-4 = 16 */
  flex: none;
  background: var(--color-theme-fg-20);
}

:root[data-theme='dark'] .bh-zoom-rule {
  background: var(--color-theme-fg-10);
}

/* =========================================================================
   Narrow frames.

   The captured window is 1080px wide and clamps to `min-width: 420px`, so below
   about 1100px of viewport it is squeezed rather than resized and the map
   becomes a crop of itself — which is handled by the fixed-size image above and
   needs no rule. Two things do need one.
   ========================================================================= */

/* The card had a rule here until 2026-09-01 and no longer needs one. It used to
   be open at rest, drawn over one marker, and the capture's second hero window
   has a fixed top edge and a left edge that walks inward as the viewport
   narrows, so below about 1182px that frame cut the card in half and the rule
   hid it. Nothing to hide now: no card is open until a marker is hovered, and a
   marker that frame covers cannot be hovered, so the only cards a reader can
   open are the ones that are on screen. The markers under it are still drawn,
   for the reason the home hero lets its Items table run under the same window.

   That frame was the 480x360 CLI window when the rule was written, at this
   window's y 278 with its left edge at 665 at 1440, 556 at 1140 and 440 at
   1024. **It is 450x500 as of 2026-09-01** and holds the app's floating chat —
   see tools/fragments/bid-hero-chat.mjs and src/styles/bid-hero-chat.css, which
   is where its geometry now lives. Measured on the built page, its left edge is
   680px inside this window at 1440, 663 at 1280, 455 at 1024, 331 at 900, 202
   at 768 and 55 at 390, and its top edge is 138 below this window's at every one
   of them. So it covers 70px more at the top and 70 more at the bottom than the
   captured frame did, and gives 15px back at each side. Page height is
   unchanged at all six widths. */

/* At this width the sidebar would be most of what is visible, and the map
   beside it is what is worth keeping. Same call the home hero makes. */
@media (max-width: 767px) {
  .bh-side {
    display: none;
  }
}
/*
 * /bid's second hero window — the Bidlo AI chat — filled.
 *
 * NOT generated by `pnpm run port`. Hand-maintained, imported by the product
 * page alone (see `astroPage` in tools/04-generate.mjs) and paired with
 * tools/fragments/bid-hero-chat.mjs, which puts the markup inside the captured
 * window frame. Every number below was read out of bidlo-light-1.0 rather than
 * eyeballed, and the app's own value is in the comment beside it.
 *
 * A FORK OF src/styles/hero-chat.css, NOT A SHARE
 * -----------------------------------------------
 * This began as a byte-for-byte copy of that file with `hc-` renamed `bc-` and
 * `.hero-chat` renamed `.bid-hero-chat`, which is the rule every hero-shell
 * fork on this site follows. A change that genuinely belongs to both windows
 * has to be made twice, on purpose. Three things already differ and none of
 * them is cosmetic: this window draws **no assistant avatar** (a plain chat,
 * not an agent thread), its crumb icon is the **Chats collection's glyph**
 * rather than an agent's initial, and its answer ends on a **generated-view
 * card** rather than a document card. The fragment's header has the app
 * citations for all three.
 *
 * WHAT THIS IS
 * ------------
 * Bidlo Lite's chat in its **floating** presentation — the
 * `data-chat-surface="floating"` overlay in `features/layout/components/page/
 * client-panel-wrapper.tsx`. Three stacked regions in the app's own
 * proportions: a 36px header (the chat bar's crumb row), the scrollback, and
 * the composer. The floating window sits on `--muted` rather than on
 * `--background`, which is why the surface here is the *card* token and the
 * composer field inside it is the *page* token — the one reversal a reader of
 * the sibling stylesheets will not expect.
 *
 * SCALE
 * -----
 * **0.75**, the same number `src/styles/hero-chat.css` derives and deliberately
 * not the 0.86 that `src/styles/bid-hero-mockup.css` draws the map window
 * beside this one at. Both windows target the same rendered body size — the
 * capture's `--text-product-base`, 12px — but they show two app surfaces whose
 * body type differs: chat prose is 16px (`.font-message` in app/globals.css), a
 * view's table is 14px (`text-sm`). 12/16 is 0.75; 12/14 is 0.86.
 *
 * Lengths are the app's value x 0.75 rounded to a whole pixel, with the app's
 * number in the comment.
 *
 * TYPE, WHICH DOES NOT COME FROM THE SCALE
 * ----------------------------------------
 * Type picks a **tier** off the capture's three-step product ladder —
 * `--text-product-sm` 11 / `--text-product-base` 12 / `--text-product-lg` 13 —
 * rather than multiplying the app's px by 0.75. Prose sits on `lg`, the app's
 * `text-sm` chrome on `base`, and `text-xs` chrome on `sm`, with the prose
 * leading following the app's own 1.5 ratio (24 x 0.8125 = 19.5 -> 20) rather
 * than the token's 1.333.
 *
 * **Radii here are scaled, which is the opposite of what the map window beside
 * this one does, and the difference is the radii themselves rather than a
 * disagreement.** That window's corners are the app's 4 / 6 / 8, where 0.86
 * moves them under a pixel and buys a difference no one can see. This one's are
 * the app's 10 / 14 / 16 / 18, where 0.75 moves them 2 to 4px — the difference
 * between a card and a lozenge. Note the app anchors its ladder on
 * `--radius: 0.625rem`, so `rounded-sm` is 6, `-md` 8, `-lg` **10** and `-xl`
 * **14**, not the 8 and 12 Tailwind's unanchored defaults give; `rounded-2xl`
 * (16) and `rounded-xs` (2) *are* Tailwind's own.
 *
 * THE CONTAINER QUERY, WHICH IS THE WHOLE SHAPE OF THIS WINDOW
 * -----------------------------------------------------------
 * The floating window is a **fixed 450x500** (`FLOATING_CHAT_WIDTH` /
 * `FLOATING_CHAT_HEIGHT` in `features/layout/constants/chat-bar.ts`) and it
 * declares `@container` on itself. Every `@lg:` utility inside therefore
 * resolves against 450px, against a `@lg` of 32rem = **512px** — a number the
 * app names outright, capping the *docked* bar at `CHAT_CONTAINER_LG_REM * 16
 * - 1` precisely so a drag can never reach the desktop prompt layout. The
 * floating chat is thus permanently below its own `@lg`, and not one `@lg:`
 * variant in `chat-conversation.tsx` fires inside it:
 *
 *   - `ConversationContent` is `px-3 @lg:px-[52px]` -> **12px** gutters, not 52
 *     (and see the note at `.bc-log` for why the drawn value is 24, the one
 *     length in this window that is measured rather than scaled);
 *   - the assistant and user segments are `flex-col @lg:flex-row gap-2
 *     @lg:gap-3` -> stacked, gap 8;
 *   - the composer wrapper is `px-3 pb-2 @lg:px-4` plus `pb-3 @lg:pb-4` ->
 *     **12px** of surround, not 16.
 *
 * The captured frame is rewritten to 450x**528** by `resizeChatWindow` — the
 * app's 500 plus the captured title bar's 28 — so the chat body below that bar
 * is exactly 500 and 450 is simply 450. The home page's window is 28px short of
 * that and the fragment's header says why that is a fork rather than a bug to
 * fix in two files at once.
 *
 * COLOUR
 * ------
 * Same discipline as every other mockup here: the window declares its own
 * `--bc-*` set, every rule reads from it, and it follows the site into dark
 * mode. The mapping onto the app's tokens:
 *
 *      app                       this file        note
 *   --muted                   --bc-bg          the floating window's surface
 *   --background              --bc-surface     the composer field
 *   --border                  --bc-line
 *   --foreground              --bc-text
 *   --muted-foreground        --bc-text-sec    and -opaque, defined identically
 *   --secondary-opaque        --bc-text-dim    the placeholder, the step rail
 *   --primary                 --bc-accent      #2483e2 in both themes
 *
 * The mapping is exact and every value is one of this site's own tokens, so
 * this file has **no dark block**. `--card` and `--hover` were in that table
 * while the generated-view card was drawn — worth remembering if it ever comes
 * back, because `--card` is the one app token that is not constant across
 * themes (light resolves it to `--background`, dark to `--muted`, which in a
 * floating window is the window's own fill).
 *
 * ONE COUPLING TO KNOW ABOUT
 * --------------------------
 * `src/scripts/empty-mockups.js` finds window mockups by a container with
 * exactly three small round children — the traffic lights — and hides all of
 * that container's siblings. The filled body carries `data-mockup-filled` so
 * the script skips it (`exemptFilledMockups` in hero-mockup.mjs, shared, not
 * forked). And **nothing in here may be a `<div>` with exactly three round
 * children of 5-15px**: every box in this window is a `<span>`, which the
 * detector never queries, and the view card's three map pins sit in a container
 * that also holds the map itself.
 */

.bid-hero-chat {
  /* See COLOUR above. */
  --bc-bg: var(--color-theme-card-hex); /* --muted */
  --bc-surface: var(--color-theme-bg); /* --background */
  --bc-line: var(--color-theme-border-02); /* --border */
  --bc-text: var(--color-theme-product-text); /* --foreground */
  --bc-text-sec: var(--color-theme-product-text-sec); /* --muted-foreground */
  --bc-text-dim: var(--color-theme-product-text-tertiary); /* --secondary-opaque */
  --bc-accent: #2483e2; /* --primary */

  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  background: var(--bc-bg);
  color: var(--bc-text);
  font-family: var(--font-system);
  font-size: var(--text-product-lg); /* .font-message 16 — see TYPE above */
  line-height: 20px; /* 24 x 0.8125 = 19.5 */
  text-align: left;
  cursor: default;
}

/* Every colour in this window derives from the site's own tokens, so there is
   no dark block here at all — unlike `src/styles/hero-chat.css`, which needs one
   for its document card's paper and its cursor. */

.bid-hero-chat *,
.bid-hero-chat *::before,
.bid-hero-chat *::after {
  box-sizing: border-box;
}

/* Every box in this window is a `<span>` — see THE COUPLING above — so the
   default `display: inline` has to be undone once at the top.

   **It is wrapped in `:where()` and that is load-bearing.** A bare
   `.bid-hero-chat span` scores (0,1,1), which outranks every single-class rule
   below it — so `.bc-head { display: flex }` and its eight neighbours would all
   silently lose to this and the whole window would render as a stack of blocks.
   `:where()` contributes nothing, bringing this to (0,1,0) and letting source
   order decide, which is what the same trap cost `automate-hero-mockup.css` a
   build. Never take the `:where()` off. */
.bid-hero-chat :where(span) {
  display: block;
}

/* Lucide, the app's own icon set — 24 viewBox, no fill, round caps. The box is
   the only size control, as it is in the app.

   The stroke is the one icon value that is not the app's, for the reason
   `src/styles/hero-mockup.css` sets out at the same rule: lucide's
   `stroke-width: 2` is two *user units* on a 24 viewBox, so it lands as
   2 x box/24 — 1.00px in this window's 12px boxes but 1.25px in its 15px ones,
   and a 1.25px line is a 1px line plus a grey one. `non-scaling-stroke` makes
   the width mean device pixels. It has to go on the shapes: unlike
   `stroke-width`, it does not inherit. */
.bid-hero-chat svg {
  display: block;
  flex: none;
  width: 12px; /* size-4 = 16 */
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bid-hero-chat svg * {
  vector-effect: non-scaling-stroke;
}

.bc-ellipsis {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* -------------------------------------------------------------------------
   The chrome tier — everything the app sets in `text-sm` (14) — one step below
   the prose on the same three-step ladder.
   ------------------------------------------------------------------------- */
.bc-crumb-title,
.bc-cot-head,
.bc-step {
  font-size: var(--text-product-base); /* text-sm 14 */
}

/* =========================================================================
   The header — the chat bar's own crumb row.

   `p-2 flex items-center justify-between shrink-0 border-b
   complexity-advanced:border-none`, so at the complexity this site draws there
   is no rule under it and the window's surface carries straight through.
   ========================================================================= */

.bc-head {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  padding: 6px; /* p-2 = 8 */
}

.bc-crumb {
  display: flex;
  flex: 1;
  min-width: 0;
  align-items: center;
}

/* DocumentCrumb's icon well: `size-9` holding the document's own icon at
   `size-5 text-[20px]`. This chat has no icon of its own, so `DocumentPkAvatar`
   falls through to `CollectionAvatar` and draws the Chats collection's stored
   `icon_chat` — lucide's `MessageSquare` — in `text-muted-foreground-opaque`,
   which this capture and `--muted-foreground` resolve identically. */
.bc-crumb-well {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 27px; /* size-9 = 36 */
  height: 27px;
  color: var(--bc-text-sec);
}

.bc-crumb-well svg {
  width: 15px; /* size-5 = 20 */
  height: 15px;
}

/* The crumb's name is an inline text field, not a heading: `h-9 -ml-1 px-2 py-1
   rounded-md border border-transparent`, `text-sm font-medium`. */
.bc-crumb-title {
  height: 27px; /* h-9 = 36 */
  margin-left: -3px; /* -ml-1 = -4 */
  padding: 0 6px; /* px-2 = 8 */
  line-height: 27px;
  font-weight: 500;
}

.bc-head-actions {
  display: flex;
  flex: none;
  align-items: center;
  gap: 2px; /* gap-0.5 = 2 */
}

.bc-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 27px; /* size-9 = 36 */
  height: 27px;
  border-radius: 999px;
  color: var(--bc-text-sec);
}

/* The floating window's close is `Minus` at `size-5`, not `size-4`. */
.bc-icon-btn--lg svg {
  width: 15px; /* 20 */
  height: 15px;
}

.bc-head-rule {
  width: 1px;
  height: 15px; /* h-5 = 20 */
  margin: 0 3px; /* mx-1 = 4 */
  background: var(--bc-line);
}

/* =========================================================================
   The scrollback.

   `ConversationContent` is `flex flex-col gap-8 p-4` overridden to `gap-2 px-3
   @lg:px-[52px] pb-8`, on a scroll area stuck to the bottom. `px-3` is the half
   that renders — see THE CONTAINER QUERY above. The frame is a still, so
   instead of scrolling the column is bottom-aligned and the overflow is
   clipped, which puts the cut where the app's scroll would put it, at the top.

   THE BUDGET, MEASURED ON THE BUILT PAGE. The chat body under the captured
   frame's 28px title bar is 472 tall, the header takes 39 and the composer 86,
   which leaves **347**; this rule's own padding takes 12 above and 24 below, so
   the conversation has **311px** — the same figure the home page's window is
   written against, the two frames being identical. What is in it comes to
   **~279**:

       56   the reader's ask, two lines in a 318px bubble (its text is 377
             wide, so it wraps whatever is done to it)
        6   gap-2
      119   the chain of thought, open, with three steps
        6   gap-2
       92   two paragraphs of answer, two lines each

   The slack sits **above** the first message, because the column is
   bottom-stuck: what a reader sees is a short conversation scrolled to its end,
   which is what a short conversation looks like. What it is not is room for a
   third element — a `GeneratedViewMessage` card is 129px plus its own `my-3`
   and was cut for that as much as for how it renders (see the fragment).
      ========================================================================= */

/* THE GUTTER IS THE ONE LENGTH IN THIS WINDOW THAT IS NOT THE APP'S, and the
   two candidates that are were both rejected in the sibling file for reasons
   that hold here unchanged. The strict x 0.75 of `px-3` is **9px**, at which
   the conversation touches the frame; the app's 12 drawn at the frame's own
   ratio is **13px**, which still reads tight — because the gutter is doing
   optical rather than proportional work. It separates prose from a hard frame
   edge, and prose set at 13px wants very nearly the same *absolute* air off
   that edge as the app's 16px prose does off its own.

   24 is what the sibling window measured and it is carried here rather than
   re-derived, the two frames being identical. **The composer deliberately does
   not follow and stays at 16 on all three of its sides** — that is the app's
   own arrangement, since at `@lg` it insets the conversation to `px-[52px]`
   while the composer wrapper stays at `px-4`. */
.bc-log {
  display: flex;
  flex: 1;
  min-height: 0;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  /* `pt-4` is drawn here where the home page's window drops it, and that is the
     slack rather than a disagreement: that conversation overflows its
     scrollback, so its top padding is above the cut and never seen, and it
     spent the 12px on a document card instead. This one has 30px to spare, so
     the padding does what it is for — it stops the first message touching the
     header on a chat short enough to show its top. */
  padding: 12px 24px 24px; /* pt-4 16, px-3 12 -> see above, pb-8 32 */
}

.bc-thread {
  display: flex;
  flex-direction: column;
  gap: 6px; /* gap-2 = 8 */
}

/* -------------------------------------------------------------------------
   The reader's message. `ml-auto w-fit max-w-[min(484px,85%)]`, and a bubble
   that overrides the shared `bg-secondary` with `bg-primary/20 rounded-2xl
   rounded-tr-xs py-2.5`. 484 is the half that binds — 85% of this frame's
   conversation column is 342 — so the cap is that percentage, and the literal
   is carried scaled beside it for the day the frame grows.
   ------------------------------------------------------------------------- */

.bc-msg--user {
  display: flex;
  justify-content: flex-end;
}

.bc-bubble {
  max-width: min(363px, 85%); /* max-w-[min(484px,85%)] */
  padding: 8px 12px; /* py-2.5 10, px-4 16 */
  border-radius: 12px 2px 12px 12px; /* rounded-2xl 16, rounded-tr-xs 2 */
  background: rgb(36 131 226 / 0.2); /* bg-primary/20 */
}

/* -------------------------------------------------------------------------
   The agent's message.

   `flex-col @lg:flex-row gap-2 @lg:gap-3`, and this window is below its `@lg`.
   **There is no avatar in it**, which is the app rather than an omission:
   `showAssistantAvatar` is `showAgentAvatar || hasOtherParticipantMessages`,
   and `chat-page.tsx` passes `showAgentAvatar={Boolean(effectiveAgentId)}` — a
   plain chat with one participant draws none, so the answer runs the full
   column. The segment wrapper still renders around its single child, which is
   why it is still here.
   ------------------------------------------------------------------------- */

.bc-msg--bot {
  display: flex;
  flex-direction: column; /* @lg:flex-row */
  gap: 6px; /* gap-2 = 8, @lg:gap-3 = 12 */
}

/* MessageContent, `flex min-w-0 max-w-full flex-col gap-2`. Its own wrapper
   (`flex-1 flex flex-col gap-1 min-w-0`) has exactly one child here, so its gap
   never renders and the two collapse into this one node. */
.bc-body {
  display: flex;
  flex: 1;
  width: 100%;
  min-width: 0;
  flex-direction: column;
  gap: 6px; /* MessageContent gap-2 = 8 */
}

/* -------------------------------------------------------------------------
   The chain of thought.

   Drawn open, which the fragment explains. `ChainOfThought` is `w-full
   space-y-4 pl-1` with `-mb-1` while open; the header is a pill trigger at
   `h-8 -ml-3 -mt-1.5 px-3` whose chevron is rotated 180 when open; each step is
   `pl-6 pb-3 text-sm` with its dot and rail painted by pseudo-elements rather
   than by extra nodes, and its own collapsed chevron at `-rotate-90`.
   ------------------------------------------------------------------------- */

.bc-cot {
  padding-left: 3px; /* pl-1 = 4 */
  margin-bottom: -3px; /* -mb-1 = -4 */
}

.bc-cot-head {
  display: inline-flex;
  align-items: center;
  gap: 6px; /* gap-2 = 8 */
  height: 24px; /* h-8 = 32 */
  margin: -4px 0 0 -9px; /* -mt-1.5 = -6, -ml-3 = -12 */
  padding: 0 9px; /* px-3 = 12 */
  border-radius: 999px;
  color: var(--bc-text-sec);
}

.bc-cot-head svg {
  width: 12px; /* size-4 */
  height: 12px;
  color: var(--bc-text-dim); /* text-secondary */
  transform: rotate(180deg); /* open */
}

/* `ChainOfThoughtContent` also takes `pb-4` whenever the message has text under
   the chain, which this one does — that is the air between the last step and
   the first line of the answer, and without it the two read as one list. */
.bc-cot-body {
  margin-top: 18px; /* space-y-4 16 + ChainOfThoughtContent mt-2 8 */
  padding-bottom: 12px; /* pb-4 = 16, hasTextBelow */
}

.bc-step {
  position: relative;
  padding-left: 18px; /* pl-6 = 24 */
  padding-bottom: 9px; /* pb-3 = 12 */
  line-height: 16px; /* leading-5 = 20 */
  color: var(--bc-text-sec); /* status: complete */
}

.bc-step:last-child {
  padding-bottom: 0;
}

.bc-step::before {
  content: '';
  position: absolute;
  top: 5px; /* 7 */
  left: 4px; /* 5 */
  width: 4px; /* size-1.5 = 6 */
  height: 4px;
  border-radius: 999px;
  background: var(--bc-text-dim);
  opacity: 0.6;
}

.bc-step::after {
  content: '';
  position: absolute;
  top: 13px; /* 18 */
  bottom: -2px;
  left: 5px; /* 7 */
  width: 1px;
  background: var(--bc-text-dim);
  opacity: 0.2;
}

.bc-step:last-child::after {
  display: none;
}

.bc-step-label {
  display: inline-flex;
  align-items: center;
  gap: 3px; /* gap-1 = 4 */
  max-width: 100%;
}

.bc-step-label span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bc-step-label svg {
  width: 10px; /* size-3.5 = 14 */
  height: 10px;
  color: var(--bc-text-dim);
  transform: rotate(-90deg); /* collapsed */
}

/* -------------------------------------------------------------------------
   The reply. `.notion-prose p` is `min-height:40px; margin:0; padding:8px 2px`,
   and its last child drops both the padding and the floor — which is what puts
   air between two paragraphs of an answer but none under the last one. There is
   one paragraph here (see THE BUDGET), so only the last-child arm ever renders;
   the general rule is kept so a second paragraph behaves if one is ever
   affordable.
   ------------------------------------------------------------------------- */

.bc-prose > span {
  /* The app's 40 is exactly one 24px line plus its 8+8 of padding, and that
     identity is the point of it. So the floor follows the *drawn* line and the
     drawn padding (20 + 6 + 6) rather than 0.8125 of 40. */
  min-height: 32px; /* 40 */
  margin: 0;
  padding: 6px 2px; /* 8px 2px */
}

.bc-prose > span:last-child {
  min-height: 0;
  padding: 0;
}

/* -------------------------------------------------------------------------
   The view's mention.

   `create_database_view` returns a document mention — a markdown link minted by
   `formatDocumentMention` — which chat prose draws through `DocumentMentionLink`.
   Geometry is `.notion-prose a`'s exactly: `text-decoration-thickness: 1px`, a
   `text-underline-offset` of 2 (the app's 2 rather than 0.75 of it, an underline
   offset being an optical gap like the gutter is) and a decoration colour at 64%
   of the text colour, which is what keeps the rule from reading heavier than the
   word beside it.

   **The colour is the one deliberate departure in this rule**, and it is the
   same one `src/styles/hero-chat.css` makes at `.hc-link`. The app's markup asks
   for `text-primary`, but chat prose carries `.notion-prose`, whose `a` rule
   sets `color: var(--muted-foreground) !important` at a higher specificity than
   `.notion-page-mention` — so a live mention actually renders muted grey and the
   `text-primary` never lands. Muted grey underlined does not read as clickable
   at 13px in a still, which is the whole job here, so this takes the colour the
   markup asks for and the geometry the stylesheet wins with. It is the same
   `--bc-accent` the dictation circle already uses, so the window still has
   exactly one accent in it.
   ------------------------------------------------------------------------- */

.bc-link {
  display: inline; /* undoes the window's blanket span rule */
  color: var(--bc-accent); /* text-primary */
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in oklab, var(--bc-accent) 64%, transparent);
  text-underline-offset: 2px; /* underline-offset-2 */
}

/* =========================================================================
   The composer.

   The wrapper is `px-3 pb-2 @lg:px-4` plus `pb-3 @lg:pb-4` — below `@lg`, which
   this window always is, that is 12 on three sides, and the app's own comment
   says why they have to agree: the window rounds its corners hard enough that a
   mismatch reads as a misalignment. The field is an InputGroup at
   `rounded-[18px] border shadow-xs bg-background`, holding the textarea
   (`pt-4 px-4`) over a footer row (`h-12 px-2 pb-2` over the addon's own
   `py-1.5`). At rest the footer is the action-menu "+" on the left and, on the
   right, the context-usage ring and the dictation button — which is a filled
   primary circle precisely because the field is empty and there is no submit
   button to outrank it.

   The `::before` is the app's scroll fade: `absolute -top-6 h-6 bg-linear-to-b
   from-transparent to-muted`, i.e. to this window's own surface, so the
   conversation dissolves into the composer's ground rather than being cut by it.
   ========================================================================= */

.bc-composer {
  position: relative;
  flex: none;
  padding: 0 16px 16px; /* px-3 pb-3 = 12 -> the gutter, see .bc-log */
}

.bc-composer::before {
  content: '';
  position: absolute;
  top: -18px; /* -top-6 = -24 */
  left: 0;
  right: 0;
  height: 18px; /* h-6 = 24 */
  background: linear-gradient(to bottom, transparent, var(--bc-bg));
}

.bc-input {
  position: relative;
  border: 1px solid var(--bc-line);
  border-radius: 14px; /* rounded-[18px] */
  background: var(--bc-surface);
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.05); /* shadow-xs */
}

.bc-input-text {
  padding: 12px 12px 0; /* pt-4 px-4 = 16 */
  color: var(--bc-text-dim); /* placeholder:text-secondary */
}

.bc-input-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 36px; /* h-12 = 48 */
  padding: 4px 6px 6px; /* addon py-1.5 = 6, then px-2 pb-2 = 8 */
}

.bc-round {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px; /* size-8 = 32 */
  height: 24px;
  border-radius: 999px;
  color: var(--bc-text-sec);
}

.bc-foot-right {
  display: flex;
  align-items: center;
}

.bc-ring {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px; /* size-8 */
  height: 24px;
  margin-right: -3px; /* -mr-1 = -4 */
  color: var(--bc-text-sec);
}

.bc-ring svg {
  width: 15px; /* 20 */
  height: 15px;
  stroke-width: 0;
}

.bc-round--primary {
  margin-left: 4px; /* ml-1.5 = 6 */
  background: var(--bc-accent);
  color: #fff;
}

.bc-round--primary svg {
  width: 15px; /* [&_svg]:size-5 = 20 */
  height: 15px;
}
/*
 * /bid's "Collected from every source" section: the three window mockups on the
 * Projects, Vendors and Competitors cards.
 *
 * NOT generated by `pnpm run port`. Hand-maintained, imported by the product
 * page alone (see `astroPage` in tools/04-generate.mjs) and paired with
 * tools/fragments/bid-surfaces.mjs, which puts the markup inside the three
 * captured window frames. That file's header carries the reasoning for the
 * records, the crop and the scale; what follows is the drawing.
 *
 * EVERY LENGTH HERE IS THE APP'S OWN, UNSCALED
 * --------------------------------------------
 * This is `nav-panel.css`'s model rather than `hero-mockup.css`'s: `.bs-screen`
 * is a 657 x 431 sheet carrying one `transform: scale(0.7)`, so a number in this
 * file is the number in bidlo-light-1.0 and nothing is multiplied by hand. That
 * is the **opposite** of the four hero windows, which bake 0.86 or 0.75 into
 * every length because they sit beside captured chrome and have to land on the
 * capture's own type ladder. Nothing here sits beside anything — the frame's
 * title bar carries three traffic lights and no words.
 *
 * So: **do not import `--text-product-*` into this file, and do not scale a
 * length to make something fit.** Change `--bs-zoom` instead, and re-check the
 * three screens' row counts against the fragment's own arithmetic.
 *
 * COLOUR
 * ------
 * The `--bs-*` set is `bid-hero-mockup.css`' `--bh-*` verbatim, mapped onto the
 * app's tokens the same way, plus the one that file also needs:
 *
 *   --bs-popover   the app's `--popover`, which the gallery cards sit on. In
 *                  light the app defines it as `--background` outright; in dark
 *                  it is `oklch(0.26 0 0)`, a step *lighter* than the dark
 *                  background, so dark restates it by hand rather than
 *                  inheriting an inversion. Same trap `--hc-doc-paper` and
 *                  `--im-pop-fill` document in their own files.
 *   --bs-frost     `bg-foreground/80 dark:bg-background/80` from
 *                  `components/ui/overlay-pill.ts`, what every floating control
 *                  on a map is painted in. **It inverts**: in light these are
 *                  dark pills with light glyphs.
 *
 * ONE COUPLING TO KNOW ABOUT
 * --------------------------
 * `src/scripts/empty-mockups.js` blanks a window by finding a `<div>` with
 * exactly three small round children and hiding that container's siblings. All
 * three bodies carry `data-mockup-filled` so the script skips them, and nothing
 * in here is a `<div>` with exactly three round children wider than 4px — the
 * zoom pill is the shape it hunts for and is safe because every box in it is a
 * `<span>` and its divider is 1px.
 */

.bs-mockup {
  /* bid-hero-mockup.css' table, verbatim. */
  --bs-surface: var(--color-theme-bg);
  --bs-chrome: var(--color-theme-card-hex);
  --bs-hover: var(--color-theme-card-02-hex);
  --bs-active: var(--color-theme-card-03-hex);
  --bs-line: var(--color-theme-border-02);
  --bs-text: var(--color-theme-product-text);
  /* The secondary and tertiary ladders are FLATTENED here, and that is the
     point of them rather than a shortcut. The site's own
     `--color-theme-product-text-sec` / `-tertiary` are the foreground at 60 %
     and 40 % ALPHA, and a lucide glyph is several separate `<path>` elements —
     so wherever two of them cross (the table mark's grid, the tag mark's
     notch, the calendar's rails) the two translucent strokes composite and the
     intersection paints a step darker than the lines running into it. The app
     has the same pair of names for the same reason: `text-secondary-opaque`
     and `text-muted-foreground-opaque` are alpha-free values, not lighter
     steps. `color-mix(in srgb, …)` against the window's own ground is exactly
     what the alpha would have composited to, so nothing changes colour — only
     the crossings stop doubling. */
  --bs-text-sec: color-mix(in srgb, var(--color-theme-fg) 60%, var(--bs-surface));
  --bs-text-dim: color-mix(in srgb, var(--color-theme-fg) 40%, var(--bs-surface));
  --bs-accent: #2483e2;
  --bs-popover: var(--color-theme-bg);
  --bs-frost: color-mix(in oklab, var(--color-theme-fg) 80%, transparent);
  --bs-frost-fg: var(--color-theme-bg);

  /* The one number that turns app pixels into card pixels. Solved by the
     gallery's three-column minimum — see the fragment's header. */
  --bs-zoom: 0.7;

  width: 100%;
  height: 100%;
  overflow: hidden;
  background: var(--bs-surface);
  color: var(--bs-text);
  font-family: var(--font-system);
  /* text-sm / leading-5, the app's body size. Unscaled, like everything else. */
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  cursor: default;
}

:root[data-theme='dark'] .bs-mockup {
  /* The app's dark `--popover` is oklch(0.26 0 0) — lighter than its dark
     `--background`, where the light ladder has them equal. Continued by hand at
     the same step `--hc-doc-paper` takes, and it must stay clear of
     `--color-theme-card-hover-hex` (#242424) or a card loses its own edge. */
  --bs-popover: #2a2a2a;
  --bs-frost: color-mix(in oklab, var(--color-theme-bg) 80%, transparent);
  --bs-frost-fg: var(--color-theme-fg);
}

.bs-mockup *,
.bs-mockup *::before,
.bs-mockup *::after {
  box-sizing: border-box;
}

/* 657 x 431 x 0.7 = 459.9 x 301.7 against the 460 x 302 the frame leaves under
   its 28px title bar. The sheet is bigger than its box on both axes only by
   that third of a pixel; what crops the screen is the media panel around the
   whole window, not this. */
.bs-screen {
  width: 657px;
  height: 431px;
  display: flex;
  transform: scale(var(--bs-zoom));
  transform-origin: 0 0;
}

.bs-main {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--bs-surface);
}

/* Lucide, the app's own set. `non-scaling-stroke` makes `stroke-width` mean
   device pixels rather than user units — see hero-mockup.css for the argument.
   It has to go on the shapes: unlike `stroke-width`, the property does not
   inherit. Note this window scales the *whole sheet*, so a 1px rule here is
   0.7px on screen; the stroke follows it rather than the box, which is what
   keeps a glyph's line the same weight as the hairlines beside it. */
.bs-mockup svg {
  display: block;
  flex: none;
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}

.bs-mockup svg * {
  vector-effect: non-scaling-stroke;
}

/* The title field's icon is the one glyph that is drawn rather than stroked —
   the app renders the letters "Aa" as an inline SVG for it. */
.bs-mockup svg[data-filled] {
  fill: currentColor;
  stroke: none;
}

.bs-ellipsis {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* =========================================================================
   There is NO content header as of 2026-09-02 — the head, crumb, presence
   faces and icon-button rules all went with it, at the owner's direction; see
   `HEAD_H` in the fragment. Each screen opens straight on its
   content under the 28px title bar.
   ========================================================================= */

/* =========================================================================
   SCREEN 1 — the Projects table.

   The app's rows scroll under a sticky header inside the page's own scrollport.
   Here the body is a clipped box between the header and the calculations row
   instead, which gets the same picture without a scroll container: whatever does
   not fit is cut off mid-row at the window's edge.

   **The select gutter floats**, which is `data-floating-gutter` in the app:
   `{ width: calc(100% + 32px), marginLeft: -32px }` on the table, so the 32px
   column hangs off into the region's own `px-2` and the *first field column*
   starts flush with the table's box. Three things follow, all of them the app's
   — the gutter costs the field columns nothing, no rule crosses it in either
   direction (`TableRow`'s head shadow is dropped and redrawn per `TableHead`
   with `first:shadow-none`, and every body cell keeps its `border-b` except
   `first`), and `show_row_numbers` being false leaves it empty at rest.
   ========================================================================= */

.bs-table {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* The region carries the view header's own inline padding, which is what the
     floating gutter is measured from. */
  padding: 0 8px; /* px-2 */
}

.bs-tr {
  display: flex;
  align-items: stretch;
  flex: none;
  height: 36px; /* VIEW_TABLE_ROW_HEIGHT */
  /* floatingGutterStyle: the gutter keeps its real width and costs the field
     columns nothing. What hangs left of the padding box is clipped by
     `.bs-table`'s own overflow, which is free — the column is empty. */
  width: calc(100% + 32px);
  margin-left: -32px;
}

.bs-gutter {
  width: 32px;
  flex: none;
}

.bs-thead {
  height: 36px; /* TABLE_HEADER_HEIGHT */
  background: var(--bs-surface);
}

.bs-tbody {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.bs-cell {
  display: flex;
  align-items: center;
  gap: 8px; /* gap-2 */
  flex: none;
  min-width: 0;
  padding: 0 8px; /* p-2 */
  overflow: hidden;
  white-space: nowrap;
}

/* The header's rule is on its *cells*, not on the row, for the reason the
   body's is: with the gutter floating the app moves it off `TableRow` and onto
   each `TableHead`, `first:shadow-none` leaving the gutter bare. Still an inset
   shadow rather than a border so the sticky header keeps its exact height. */
.bs-thead .bs-cell {
  box-shadow: inset 0 -1px 0 0 var(--bs-line);
  /* font-medium text-muted-foreground, and the same text-sm as the body — the
     header is not smaller and not uppercase. */
  color: var(--bs-text-sec);
  font-weight: var(--font-weight-medium);
}

/* `text-muted-foreground-opaque`, which is the *same colour* as the label's own
   `text-muted-foreground` — globals.css defines the pair identically in both
   themes, the "opaque" spelling existing to give an alpha-free value rather
   than a lighter step. So the type icon reads at the label's weight. */
.bs-thead .bs-cell svg {
  width: 18px; /* size-4.5 */
  height: 18px;
  color: var(--bs-text-sec);
}

.bs-tbody .bs-cell {
  border-bottom: 1px solid var(--bs-line);
}

/* The add-column column: `flexGrow: 1`, empty cells, and a header holding one
   size-7 button with a size-5 plus in it. Not a hover state — it is only hidden
   on a published site. */
.bs-cell--add {
  flex: 1;
  min-width: 48px; /* ADD_FIELD_MIN_WIDTH */
  padding: 0 4px;
}

.bs-add-col {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px; /* size-7 */
  height: 28px;
  border-radius: 999px;
  color: var(--bs-text-sec);
}

.bs-add-col svg {
  width: 20px; /* size-5 */
  height: 20px;
}

/* Number cells are `flex w-full justify-end` with `text-right`, on tabular
   figures. */
.bs-num {
  justify-content: flex-end;
  font-variant-numeric: tabular-nums;
}

/* `CalculationsRow`, scoped to the view. `CalculationCellContent` sets the
   aggregation's name in `font-normal text-secondary-opaque` beside its result
   in `font-medium`, with the label `min-w-0 truncate` (and `dir="rtl"`) and the
   result `shrink-0` — so a narrow column loses the word and never a digit. */
.bs-calc-row {
  height: 38px;
  flex: none;
  border-top: 1px solid var(--bs-line);
  background: var(--bs-surface);
}

.bs-calc {
  gap: 6px;
}

.bs-calc-label {
  color: var(--bs-text-dim);
  font-weight: var(--font-weight-normal);
}

.bs-calc-result {
  flex: none;
  font-weight: var(--font-weight-medium);
  font-variant-numeric: tabular-nums;
}

/* The primary column's measured middle-truncation: head flush left, tail flush
   right, the ellipsis its own growing item between them, so whatever the split
   could not spend lands half on each side rather than all of it on one. Same
   arrangement `market-mockup.css` draws for the runtime port; see the
   fragment's header for why the split is a constant here. */
.bs-trunc {
  display: flex;
  align-items: baseline;
  min-width: 0;
  width: 100%;
}

.bs-trunc-h {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
}

.bs-trunc-e {
  flex: 1;
  text-align: center;
  white-space: pre;
}

.bs-trunc-t {
  flex: none;
  white-space: nowrap;
}

/* =========================================================================
   The `Color` badge, which is what both a tag and a relation resolve to when
   the collection they point at carries `icon_type: "color"` — the Agency and
   County collections behind the table's Owner column, and the Grade tag on the
   gallery's cards.

   `bg-<c>-badge text-<c>-badge-foreground rounded px-1.5 leading-5 text-sm`:
   a 4px corner, no vertical padding, one hue per document from that document's
   own stored icon. The nine tones are `bid-hero-mockup.css`' own, which are the
   app's oklch converted with the same formula — so `blue`, `green`, `yellow`
   and `orange` come out byte-identical to that sheet's and to
   `items-mockup.css`' without any of the three importing another. Dark re-fills
   every hue at the higher alpha the app uses there and collapses the text to
   white at 87%.
   ========================================================================= */

.bs-chip {
  display: inline-flex;
  align-items: center;
  min-width: 0;
  max-width: 100%;
  height: 20px; /* leading-5 */
  padding: 0 6px; /* px-1.5 */
  border-radius: 4px; /* rounded */
  background: var(--bs-chip-bg);
  color: var(--bs-chip-fg);
}

.bs-chip[data-tone='blue'] {
  --bs-chip-bg: rgb(65 139 198 / 0.27);
  --bs-chip-fg: #001f43;
}

.bs-chip[data-tone='green'] {
  --bs-chip-bg: rgb(80 158 114 / 0.27);
  --bs-chip-fg: #002f23;
}

.bs-chip[data-tone='yellow'] {
  --bs-chip-bg: rgb(243 171 28 / 0.39);
  --bs-chip-fg: #4b3314;
}

.bs-chip[data-tone='orange'] {
  --bs-chip-bg: rgb(226 146 18 / 0.27);
  --bs-chip-fg: #4e2b0d;
}

.bs-chip[data-tone='red'] {
  --bs-chip-bg: rgb(255 171 158 / 0.4);
  --bs-chip-fg: #71000b;
}

.bs-chip[data-tone='purple'] {
  --bs-chip-bg: rgb(155 120 201 / 0.27);
  --bs-chip-fg: #362348;
}

.bs-chip[data-tone='brown'] {
  --bs-chip-bg: rgb(205 173 151 / 0.35);
  --bs-chip-fg: #4c2c1b;
}

.bs-chip[data-tone='gray'] {
  --bs-chip-bg: rgb(84 72 49 / 0.15);
  --bs-chip-fg: #1d1b16;
}

:root[data-theme='dark'] .bs-chip {
  --bs-chip-fg: rgb(255 255 255 / 0.87);
}

:root[data-theme='dark'] .bs-chip[data-tone='blue'] {
  --bs-chip-bg: rgb(0 103 165 / 0.5);
}

:root[data-theme='dark'] .bs-chip[data-tone='green'] {
  --bs-chip-bg: rgb(0 128 85 / 0.5);
}

:root[data-theme='dark'] .bs-chip[data-tone='yellow'] {
  --bs-chip-bg: rgb(246 168 0 / 0.5);
}

:root[data-theme='dark'] .bs-chip[data-tone='orange'] {
  --bs-chip-bg: rgb(239 146 0 / 0.45);
}

:root[data-theme='dark'] .bs-chip[data-tone='red'] {
  --bs-chip-bg: rgb(238 107 93 / 0.45);
}

:root[data-theme='dark'] .bs-chip[data-tone='purple'] {
  --bs-chip-bg: rgb(138 103 229 / 0.34);
}

:root[data-theme='dark'] .bs-chip[data-tone='brown'] {
  --bs-chip-bg: rgb(189 124 91 / 0.45);
}

/* The one hue whose dark fill is opaque: `--gray-badge` is `--gray-primary`
   itself there, where every other tone is an alpha. */
:root[data-theme='dark'] .bs-chip[data-tone='gray'] {
  --bs-chip-bg: rgb(127 127 127);
}

/* =========================================================================
   SCREEN 2 — the Vendors map.

   A map view is the main region below the content header: a full-bleed canvas
   with every control floating over it. **No toolbar as of 2026-09-02** — the
   app draws one here (the gate is an OR and a map always passes
   `preNewAction`), and it came out at the owner's direction because in a 259px
   crop its one filter pill was the only piece of blue chrome in the row and
   read as a control. Its 56px went to the canvas.

   The image is pinned at 657 x 431 rather than fitted. It is a 2x render of
   exactly that box (`tools/vendor-map-render.mjs`), and a `width: 100%` here
   would rescale it against the sheet and walk all 42 markers off the
   coordinates the fragment projected them to.
   ========================================================================= */

.bs-map {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: var(--bs-hover); /* the app's bg-muted/20 under a loading map */
}

.bs-map-img {
  position: absolute;
  left: 0;
  top: 0;
  width: 657px;
  height: 431px;
  object-fit: cover;
}

/* The app picks its style off the resolved theme (`getMapStyle`: Bidlo's custom
   light style, and `mapbox/dark-v11`, which is what it falls back to), so this
   does too — for the canvas and for the style plate's thumbnail alike. Only one
   of each pair is ever painted. */
.bs-mockup [data-theme-dark] {
  display: none;
}

:root[data-theme='dark'] .bs-mockup [data-theme-light] {
  display: none;
}

:root[data-theme='dark'] .bs-mockup [data-theme-dark] {
  display: block;
}

.bs-markers {
  position: absolute;
  inset: 0;
  display: block;
}

/* `MapMarker`'s teardrop: a white square at `rotate(45deg)` with one square
   corner, holding a disc with the document's avatar in it — a `kind: "image"`
   icon here, the owning company's mark, drawn `size-full object-cover` in the
   disc the way the app draws one. `anchor="bottom"`: the box's bottom-centre
   sits on the point.

   **Drawn at 0.78 of the app's own 36 / 32 / 30 as of 2026-09-02**, at the
   owner's direction — see the fragment's WHAT IS IN A MARKER. The three
   numbers move together; the shell's radii are half its side and its 5px
   square corner scales with it. */
.bs-pin {
  position: absolute;
  width: 28px; /* w-9 h-9, at 0.78 */
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* `anchor="bottom"`: the box's bottom-centre sits on the point.
     **The individual `translate` property and NOT `transform`, as of
     2026-09-08.** The two render identically here and differ the moment
     anything scales the marker: the matrix is `translate · rotate · scale ·
     transform`, so a `transform` translate happens INSIDE a `scale` and gets
     multiplied by it, while this one happens outside. `bid-surfaces-quiet.css`
     counter-scales every pin by 0.527 inside a map layer at 2x, and with the
     translate on `transform` that dragged the anchor 13.25 app px right and
     26.48 down — measured — so every marker on the quiet section stood off its
     own plant, which is the one thing this projection exists to prevent. */
  translate: -50% -100%;
}

.bs-pin-shell {
  width: 25px; /* w-8 h-8, at 0.78 */
  height: 25px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12.5px 12.5px 4px 12.5px; /* rounded-2xl rounded-br-[5px] */
  background: #fff;
  transform: rotate(45deg);
  box-shadow:
    0 4px 6px -1px rgb(0 0 0 / 0.1),
    0 2px 4px -2px rgb(0 0 0 / 0.1); /* shadow-md */
}

.bs-pin-turn {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: rotate(-45deg);
}

.bs-pin-disc {
  width: 23px; /* size-7.5, at 0.78 */
  height: 23px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 2px solid #fff;
  border-radius: 999px;
  background: #fff;
}

/* `DocumentPkAvatar` at `size-full` for an image icon: the picture fills the
   disc and the disc's own radius crops it round. The file is a 512 square whose
   mark sits inside the inscribed circle, so nothing of the mark is lost. */
.bs-pin-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The floating chrome. `components/ui/overlay-pill.ts` is one recipe for all of
   it: `bg-foreground/80 dark:bg-background/80` with `text-background
   dark:text-foreground` and a hairline — so in LIGHT mode these are dark
   frosted pills with pale glyphs, which is the app and not an inversion made
   here. A map control has to stay legible over satellite imagery too. */
.bs-bl {
  position: absolute;
  bottom: 12px; /* bottom-3 */
  left: 12px;
  display: flex;
  align-items: flex-end;
  /* The app's is `gap-1.5` (6px); 3 at the owner's direction on 2026-09-02 —
     at 0.7 the three controls read as three things rather than one cluster. */
  gap: 3px;
}

.bs-br {
  position: absolute;
  bottom: 12px;
  right: 12px;
  display: flex;
  align-items: center;
}

.bs-pill-btn,
.bs-zoom,
.bs-style-btn {
  border: 1px solid var(--bs-line);
  border-radius: 999px;
  background: var(--bs-frost);
  color: var(--bs-frost-fg);
  backdrop-filter: blur(4px);
}

.bs-pill-btn {
  width: 36px; /* h-9 w-9 */
  height: 36px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

.bs-pill-btn svg {
  width: 20px; /* size-5 */
  height: 20px;
}

/* `h-16 w-16 rounded-xl p-1 shadow-sm` over the current style's thumbnail. The
   app anchors its radius ladder on `--radius: 0.625rem`, so `rounded-xl` is 14
   and `rounded-lg` is 10 — not Tailwind's unanchored 12 and 8. */
.bs-style-btn {
  width: 64px; /* h-16 w-16 */
  height: 64px;
  flex: none;
  padding: 4px; /* p-1 */
  border-radius: 14px;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.bs-style-thumb {
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 10px; /* rounded-lg */
}

.bs-style-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The zoom pill is one rounded container holding two segments and a hairline
   between them — three children, which is the shape `empty-mockups.js` hunts
   for. It is safe because every box here is a `<span>` (that script only queries
   `div`) and because the divider is 1px, which fails its `b.width > 4` test.
   Do not widen the divider. */
.bs-zoom {
  display: flex;
  align-items: center;
  overflow: hidden;
}

.bs-zoom-seg {
  height: 36px; /* h-9 */
  display: flex;
  align-items: center;
  padding: 0 8px; /* px-2 in zoom-pill.tsx */
}

.bs-zoom-seg svg {
  width: 20px; /* size-5 */
  height: 20px;
}

.bs-zoom-rule {
  width: 1px;
  height: 16px; /* h-4 */
  flex: none;
  background: var(--color-theme-fg-20);
}

:root[data-theme='dark'] .bs-zoom-rule {
  background: var(--color-theme-fg-10);
}

/* =========================================================================
   SCREEN 3 — the Competitors gallery.

   `GalleryGridBody`'s own surface: `p-4` plus `VIEW_CONTENT_INLINE_PADDING_WIDE`
   (`px-[var(--view-content-inline-padding,1.5rem)]`, so 24 a side on every
   desktop surface), over a grid that carries `pt-1! pb-4` of its own at a 16px
   gap. `cardSizeMap` is `{ small: 180, medium: 260, large: 320 }` and the min
   here is **140**, which is under all three — see the fragment's header for
   that departure and for the three defaults that came back on 2026-09-02.

   `align-content: start` has to be said outright: a grid with room to spare
   stretches its auto rows by default, which would space two rows of cards down
   the whole canvas instead of leaving the second one to bleed off the bottom.
   ========================================================================= */

.bs-gallery {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  padding: 16px 24px; /* p-4, with px-6 (VIEW_CONTENT_INLINE_PADDING_WIDE) */
}

/* **The app's own 20px above the first card is BACK as of 2026-09-02**, at
   the owner's direction — the surface's `p-4` and the grid's `pt-1!`. It was
   dropped earlier the same day so the first row of cards started on the same
   line as the table's column header and the map's canvas, and was reported as
   the cards sitting on the top edge. So the gallery now starts 14 CSS px
   lower than its two neighbours, which is what a gallery view looks like. */
.bs-grid {
  display: grid;
  /* **FOUR COLUMNS AT 140 AS OF 2026-09-04, back to `minmax(N, 1fr)`.**
     `cardSizeMap` is small 180 / medium 260 / large 320 and this is
     under all three — the departure this file has taken before, un-taken on
     2026-09-02 and re-taken now.

     The grid is 609 app px inside the surface's own padding, and a `1fr` track
     FILLS it, so a min only ever picks the column COUNT: 141..192 all draw the
     same three 192.33px columns and <=140.25 jumps to four at 140.25. There is
     nothing in between, which is why a stated 172px track was tried in between
     and reverted the same morning — 192 -> 172 is 11% and reads as no change
     at all, and a stated track also leaves an unused strip the `1fr` does not.

     **What the fourth column costs is the card's PROPORTION, and it lands on
     the app's own.** The body under the 3:1 cover is a fixed 64 app px, so a
     narrower card is relatively taller: 140 x 111 is 1.26, against 172's 1.42
     and 192's 1.50. 1.26 is exactly `medium`'s own 260 x 210, so the card is
     still the shape the app draws — it was `icon`'s 16:9 cover that made a
     140px card square back in September, and that cover is 3:1 now. */
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 16px;
  align-content: start;
  padding: 4px 0 16px; /* pt-1! pb-4 */
}

/* `GalleryCard`: `flex flex-col h-full w-full gap-0 py-0 overflow-hidden
   rounded-[10px]` plus the gallery variant's `shadow-sm border border-border/50
   bg-popover`. The radius is a literal in the app and is not derived from
   `--radius`, so it does not move with the ladder. */
.bs-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--bs-line); /* border-border/50, at 1px indistinguishable */
  border-radius: 10px;
  background: var(--bs-popover);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); /* shadow-sm */
}

/* **The cover is `card_preview: "banner"`'s own `aspect-3/1` as of
   2026-09-02** — it was `icon`'s `aspect-video` until then, at which a 140px
   card was square and the row was reported as noisy. See the fragment's header
   for the arithmetic; the argument against `banner` (it slices a square logo
   through the middle) stopped applying the day the logos became files drawn
   to whatever crop they are given. */
/* **The cover FILLS, and that is this screen's second departure from the app.**
   `card_preview: "icon"` centres `PkAvatar` at `size-16` on a `bg-hover/50`
   plate, so on a 192px card the mark is a 64px stamp with a wide grey band all
   round it — reported on 2026-09-02 as an empty cover. Every cover type that
   fills in the app (`banner`, `street_view`, `map_marker`, `content`) reads a
   value these records do not carry: **0 of 34,405 contractors on production
   store a banner, and 0 store a location**, so a filling cover would have had
   to invent one. Street View off each company's own Facility yards was built
   and looked at first — eight sampled, seven of them a Texas verge and a road,
   one an actual plant — and a map-marker cover repeats the Vendors card
   directly beside it.
   So the mark itself fills instead — `object-cover` edge to edge, with no
   plate showing at all. Each logo carries its own brand ground and every mark
   is drawn inside the middle 288 of its 512 square, which is exactly what a
   16:9 crop of a square keeps, so the crop takes ground and nothing else.
   `object-cover` is the app's own; what is not the app is that the image IS
   the cover rather than a `size-16` avatar centred on a plate.
   It shipped for one build as five real firms' own logos, `contain` on a white
   plate, which is what sent the companies invented — those files are
   white-ground squares and a row of them was five white rectangles. The plate
   colour below is only ever seen while an SVG is still loading. */
.bs-card-preview {
  aspect-ratio: 3 / 1; /* aspect-3/1 */
  flex: none;
  overflow: hidden;
  background: color-mix(in oklab, var(--bs-hover) 50%, transparent);
}

.bs-card-cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* `CardContent`: `flex flex-col gap-1.5 px-2.5 pt-2.5 pb-2`. */
.bs-card-body {
  display: flex;
  flex-direction: column;
  gap: 6px; /* gap-1.5 */
  min-width: 0;
  padding: 10px 10px 8px; /* px-2.5 pt-2.5 pb-2 */
}

/* The title cell is `px-0 py-0 font-medium text-sm`, and the name inside it is
   `min-w-0 flex-1 truncate` — ONE line with an ellipsis. `wrap_text` defaults
   to false and it is the flag that would let it wrap; a card that wraps is
   20px taller than its neighbours, and a grid row stretches every card to the
   tallest.

   `GalleryTitleCell` also puts the document's own icon at `size-5` in front of
   the name whenever `show_document_icon` is true, which is its default, and
   that was drawn until 2026-09-02. It came out at the owner's direction: with
   the cover filled by the same mark, a 20px copy of it beside the name was the
   logo twice on one card. `show_document_icon: false` is a real saved setting,
   so this is a config change rather than a departure. */
.bs-card-title {
  display: block;
  min-width: 0;
  font-weight: var(--font-weight-medium);
}

/* `CARD_FIELD_ROWS_CLASS` under `data-card-layout="compact"`: `flex-row
   flex-wrap items-center gap-x-3 gap-y-1`, which is what flows the two tags
   onto one row instead of stacking them. */
.bs-card-fields {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px; /* gap-y-1 gap-x-3 */
  min-width: 0;
}

/* The grid's last cell, and a real one: `flex items-center justify-center h-full
   w-full gap-1 min-h-10 rounded-[10px] border border-solid border-border
   text-secondary-opaque cursor-pointer text-sm shadow-sm`. */
.bs-card-new {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px; /* gap-1 */
  min-height: 40px; /* min-h-10 */
  border: 1px solid var(--bs-line);
  border-radius: 10px;
  color: var(--bs-text-dim);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.bs-card-new svg {
  width: 16px; /* size-4 */
  height: 16px;
}
/* Hand-maintained. Pairs with tools/fragments/bid-preview.mjs. Imported by the
 * bid page, and by `CardGallery.astro` — the gallery harvests that card as it
 * is drawn and renders it on every page, so the rules have to reach it there.
 *
 * **Nothing here is temporary despite the name.** Both scaffolds this file
 * shipped with on 2026-09-01 — a duplicated section, then a band of full-size
 * cards under it — came out once the owner had picked; see the fragment's
 * header. What is left is the page.
 *
 * The "Web & Mobile" card's graphic: the five things the page says it reads,
 * drawn as the app's own run rows. What it replaced was the capture's 300x650
 * phone; what it keeps is that phone's frame — the tinted plate and the 378px
 * viewport — so this cell is still exactly as tall as the three beside it and
 * the section's height does not move.
 *
 * The shape is a reference the owner supplied on 2026-09-01: a subagent list,
 * one flat filled card per row, a glyph at the left, a title, and one muted
 * line under it, with the rows at the bottom faded out. Five things follow
 * from it. Its glyphs are status marks — a spinner on a row that is working, a
 * hollow ring on one that has not started — and this file drew both until the
 * owner replaced them with one `database` glyph on every row: these are
 * datasets, not tasks, so a status was saying something untrue about them.
 *
 * **All the rows fit inside the 378px box, and the last one is the point.** It
 * was drawn taller than the frame until 2026-09-01, at the owner's own
 * direction, and cut mid-"Pay estimates" by the capture's `overflow-hidden` —
 * which meant the faded pending row the whole shape is borrowed from was never
 * on screen, and that row is the only thing here saying there is more. Two
 * things closed the gap and the second is the one that matters. The list lost
 * its seventh entry, a bare "And much more", because the faded row already
 * implies it (see the fragment); and the spacing came in — card padding 10 ->
 * g0.75, line-height 1.35 -> 1.3, description margin 2 -> g0.25 — which lands
 * a row at **48.7px** against the old 54.4, near enough to the ~45 the
 * reference's own rows measure. Six of those on a g1 gap, under the 24px top
 * pad below, put the last row's bottom edge at **366 of 378**. (As of
 * 2026-09-02 it is FIVE rows of 53 on an 8px gap — see the next note — ending
 * at **321 of 378**.)
 * Nothing about the cutting changed: the column is still top-aligned and every
 * card is still `flex: 0 0 auto`, so a row that ever does overflow runs off
 * the bottom rather than being squeezed, and the frame still cuts it. There is
 * ~15px of slack, which is one wrapped description, so keep the lines short.
 *
 * **The cards carry a hairline and no shadow.** The first version had a shadow
 * too, because it was a fanned stack and needed the lift to read as one; these
 * are a list, full width on a real gap, where the old ones overlapped and were
 * indented off alternate sides.
 *
 * **A ROW IS THE CAPTURED "Started 4 subagents" CARD, VALUE FOR VALUE, as of
 * 2026-09-02.** The section directly below this one (#context) still draws a
 * Cursor mockup whose list of subagent rows is the reference the owner
 * supplied for this stack in the first place — and the owner asked for these
 * rows to match THAT card exactly on fill, spacing and radius. So every number
 * here is read off that captured markup:
 *
 *   bg-theme-card-hex border-theme-border-02 rounded-lg border px-3 py-2
 *     -> fill `--color-theme-card-hex`, 1px `--color-theme-border-02` ring,
 *        `--radius-lg` (8px), 12px sides / 8px top and bottom
 *   space-y-2                       -> 8px between rows
 *   flex items-start gap-2.5        -> 10px between the glyph and the text
 *   h-5 w-5 mt-0.5, 18px glyph      -> a 20px well, 2px down, 18px of glyph
 *   type-product-lg leading-snug    -> 13px title on 1.375
 *   type-product-sm mt-0.5 leading-snug -> 11px line on 1.375, 2px under it
 *
 * Until then a row was `.card`'s own `--radius-xs` on the `--spacing-g*` scale
 * (g0.75 / g1.25 padding, g1 gap), which was the same fill at a 4px corner with
 * no ring — close, and reported as not the same. The reference is a card on
 * THIS page, so it wins.
 *
 * **Every other length is the reference's own.** Its panel is ~300px wide and
 * the figure here is ~292px at 1440, so the two are close enough to 1:1 that
 * the measured values transfer: a 14px glyph 10px from the text, a 13px title
 * over an 11px line.
 *
 * **The stack is capped at 320px and centred.** The section's grid is
 * `xl:grid-cols-4`, so the cap never binds at 1440 — but below 1140px that
 * grid is a single column and the figure runs the width of the container,
 * where an uncapped stack would be seven cards a thousand pixels wide.
 *
 * **The glyph is aligned to the title, not to the card.** The reference does
 * that, and it is what stops a two-line row from pushing its spinner into the
 * gap between the two lines.
 *
 * Colour is all tokens, so dark follows theme-dark.css. The only literal hexes
 * on this graphic are the capture's own plate, and they are in the markup, not
 * here.
 */

.bpv-stack {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  /* CENTRED as of 2026-09-02, at the owner's direction — it was top-aligned
     so the first row started on the same line as the three window mockups
     beside it, and the stack ended at 321 of 378 with the slack all at the
     bottom. Five rows now sit in the middle of the plate. If the stack ever
     overflows again this hides the cut at both ends rather than one; the
     length rule in the fragment is what keeps that from happening. */
  justify-content: center;
  gap: 8px; /* space-y-2 */
  max-width: 320px;
  margin-inline: auto;
  /* All four sides are the capture's own `--demo-pad`, which is what insets the
     window mockup inside the three sibling cards' identical plates — 24px down
     from the top and 24 in from the left, so the first row here starts on the
     same line as their window frames do and the rows sit on the same left edge.
     It is measured rather than inherited: that variable is declared on THEIR
     plates, and this card's plate is a plainer one that does not carry it. A
     test pins the 24 in the page against this, so a re-capture that moves it
     fails the port rather than the eye.

     The sides were `g1.25` and the top alone was matched first; the owner asked
     for the X to follow on 2026-09-01. It costs a row 23px of width — the
     longest description measures ~180px against the ~196 that leaves, so all
     six still hold one line, which is what the length rule above is for. The
     bottom matches the top now that the column is centred. */
  padding: 24px;
}

.bpv-card {
  /* A flex column shrinks its children to fit; these must keep their height
     and run off the bottom of the frame instead. */
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 10px; /* gap-2.5 */
  /* The captured subagent card's own shell — see the header. */
  border: 1px solid var(--color-theme-border-02);
  border-radius: var(--radius-lg); /* rounded-lg, 8px */
  background-color: var(--color-theme-card-hex);
  padding: 8px 12px; /* py-2 px-3 */
}

.bpv-card__glyph {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px; /* h-5 w-5 */
  height: 20px;
  margin-top: 2px; /* mt-0.5 */
  color: var(--color-theme-text-sec);
}

.bpv-card__glyph svg {
  width: 18px; /* the reference's 18px glyph in its 20px well */
  height: 18px;
  display: block;
}

.bpv-card__body {
  min-width: 0;
}

.bpv-card__t {
  display: block;
  color: var(--color-theme-text);
  font-size: 0.8125rem; /* type-product-lg */
  line-height: 1.375; /* leading-snug */
}

.bpv-card__d {
  display: block;
  color: var(--color-theme-text-sec);
  font-size: 0.6875rem; /* type-product-sm */
  line-height: 1.375; /* leading-snug */
  margin-top: 2px; /* mt-0.5 */
  text-wrap: pretty;
}

/* The rows at BOTH ENDS fade, and by different amounts — a ramp rather than a
   state, so the stack reads as a revolving list turning past one lit row.

   **It ran off the bottom alone until 2026-09-03**, when the owner asked for
   the top two to go the same way and for `Change orders` to take the middle
   slot, which is the one row a two-step ramp off each end of a five-row list
   leaves untouched. A list that fades at one end stops; one that fades at both
   turns. The fragment applies these by a row's distance from the NEARER end,
   so the two ends mirror by construction — nothing here is per-end, and there
   is no third class.

   **The ramp accelerates, and that is the point.** 1 / 0.5 / 0.25 halves
   twice, and at that the last row still read as a row — a third weight in a
   list of three, which was reported as awkward — where the ramp is meant to
   read as one row dimming and the next one leaving. So the second step is the
   steeper of the two as a ratio: 0.5 of full, then 0.3 of that. 0.15 is far
   enough under the plate's own contrast that the last row is a shape rather
   than a line to read, which is what puts the eye back on the four above it.
   A test fails if the two steps ever come back to even.

   Opacity on the whole card, never colour on its parts: the reference fades
   the card, so one value covers the title, the line, the fill and the glyph
   together, and the ramp stays one number per step. It also costs no layout —
   these rows still take their full height, which is what keeps the pitch even
   as the stack dims — and it is what makes the top fade free: a `.bpv-stack`
   that is `justify-content: center` puts the lit row on the plate's own
   centre line whatever the ends are doing. */
.bpv-card--fade-1 {
  opacity: 0.5;
}

.bpv-card--fade-2 {
  opacity: 0.15;
}
/*
 * /bid's "Know what's next." section: the three graphics on its cards.
 *
 * NOT generated by `pnpm run port`. Hand-maintained, imported by the product
 * page alone (see `astroPage` in tools/04-generate.mjs) and by
 * `CardGallery.astro`, which harvests these cards and renders them on every
 * page — the same argument `bid-preview.css` and `bid-surfaces.css` already
 * make. Pairs with tools/fragments/bid-context.mjs, whose header carries the
 * records, the invented halves and the crop; what follows is the drawing.
 *
 * THREE SCALES, ON PURPOSE
 * ------------------------
 * Card 1 is **all app, all 1:1**. `.bx-popover` is the app's price popover and
 * every length in it is bidlo-light-1.0's own number: a 280px `w-70`, `p-3`, a
 * 144px chart panel, an `h-9` badge and three `size-8` tabs. `.bx-price__table`
 * above it is the cell the popover hangs off, at the same scale: `text-sm` in a
 * 36px row, `p-2` a side, a `size-4.5` type mark. (It was a label and a 28px
 * number on the capture's `--text-product-*` ladder until 2026-09-04; the
 * paragraph on that block below says why it stopped being.)
 *
 * The markup comes from `tools/lib/price-forecast.mjs`, and the home page's
 * items window draws the same object at **0.86**, where it has to land on the
 * capture's product ladder beside captured chrome. One drawing, two scales, two
 * sheets — the split `hero-chat.css` and `integrations.css` already make. Do
 * not import either file's numbers into the other.
 *
 * `.bx-screen` is the third and takes `nav-panel.css`'s model, which
 * `bid-surfaces.css` sets out at length: a 571 x 500 sheet under one
 * `transform: scale(var(--bx-zoom))`, so every length under it is the app's
 * number. Do not scale one by hand; move `--bx-zoom` and re-derive.
 *
 * `.bx-market` is NOT a fourth of that kind and is the one graphic here with no
 * scale at all: its render is made at exactly the 287 x 290 box it is drawn in
 * (`tools/market-map-render.mjs`), so every blob sits at the pixel `project()`
 * gave it and the pills under it are card pixels too.
 *
 * COLOUR
 * ------
 * The `--bx-*` set is `bid-surfaces.css`' `--bs-*` mapped the same way, plus
 * the three the borrowed popover needs. The secondary and tertiary steps are
 * **flattened** with `color-mix` rather than left as alpha, for the reason that
 * file gives: a lucide glyph is several separate `<path>`s, and two translucent
 * strokes crossing paint a step darker than the lines running into them. The
 * app has the same pair of names for the same reason
 * (`text-muted-foreground-opaque` is an alpha-free value, not a lighter step).
 *
 * ONE COUPLING TO KNOW ABOUT
 * --------------------------
 * `src/scripts/empty-mockups.js` blanks a window by finding a `<div>` holding
 * exactly three small round children. Nothing below is that shape and it is
 * safe twice over: every box in that half of the file is a `<span>`, which that
 * script's `div` query cannot reach, and the filled body carries
 * `data-mockup-filled`. `.bx-pop-tabs` is the same shape in `<div>`s and is
 * safe on its own count: its three children are **32px**, and that script
 * requires every one of them to be under 16. Do not shrink them.
 */

.bx-price,
.bx-mockup,
.bx-market {
  /* bid-surfaces.css' table, less the map's two. */
  --bx-surface: var(--color-theme-bg);
  --bx-chrome: var(--color-theme-card-hex);
  --bx-line: var(--color-theme-border-02);
  --bx-text: var(--color-theme-product-text);
  --bx-text-sec: color-mix(in srgb, var(--color-theme-fg) 60%, var(--color-theme-bg));
  --bx-text-dim: color-mix(in srgb, var(--color-theme-fg) 40%, var(--color-theme-bg));
  --bx-accent: #2483e2;

  /* `--gray-badge`, AND IT IS A TOKEN BECAUSE TWO CARDS READ IT. The planholder
     chip on card 2 (`.bx-rel`) and the filter pill on card 3 (`.bx-pill`) are
     both badges, they sit side by side on one row, and they were two literals
     until 2026-09-07 — so a change to one was a change to half a row. Declared
     once here, with the dark half beside it, they cannot drift.

     `bg-gray-badge dark:bg-default-badge`, AND BOTH HALVES ARE AN ALPHA.
     The dark half was `--color-theme-card-02-hex` (#242424) until 2026-09-10,
     which was a misreading and cost the row its badges: the app's dark
     `--default-badge` is `--hover`, i.e. `oklch(1 0 0 / 5.5%)`, a translucent
     WHITE wash and not an opaque surface token — and #242424 is exactly the
     token these two cards' plates are painted with (`restatePlates` normalises
     both to `bg-theme-card-02-hex`), so in dark the chip and the pill were the
     same colour as the ground under them and vanished outright. Reported.

     A wash cannot collapse that way on any ground, which is the whole reason
     both halves are one: 5.5% over the #242424 plate renders #303030, a step
     past `--color-theme-card-04-hex` (#2e2e2e), which is the fill
     `bid-rfq.css` already carries for a badge on a card-02 plate. DO NOT PUT A
     SURFACE TOKEN BACK HERE — a plate that ever moves takes the badge with
     it. */
  --bx-badge: rgb(84 72 49 / 0.15);

  /* ONE SCALE FOR THE ROW, AND IT IS WHAT MAKES THE THREE CARDS LOOK LIKE ONE
     SECTION. Every length in cards 1 and 2 is the app's own number and none may
     be typed larger or smaller, so the only lever on how much of a 391.7 x 378
     plate a card fills is a transform over the whole drawing — and if the two
     cards take DIFFERENT ones, the same 14px app type renders at two sizes side
     by side, which is what a reader sees before they read a word.

     IT IS SOLVED OFF TYPE, NOT OFF AIR, AS OF 2026-09-04. It was 0.82, which
     was solved to put card 1's air inside the band #workflows carries — and
     that section's own body type is a flat **13px** (`--text-product-lg`),
     which the owner named as the size this row should read at. At 0.82 the
     app's `text-sm` landed at 11.5. So the scale is `13 / 14`: the app's body
     size renders at exactly the reference's 13, its `text-xs` at 11.14, which
     is where card 3's own pills already sit, and the axis labels at 9.3. One
     ladder across all three cards.

     WHAT IT COSTS IS CARD 1'S VERTICAL AIR, and that is the trade rather than
     an oversight. At 13/14 that block is 286 x 302.7, so its width and its side
     air (52.9) land on #workflows' own 288 and 51.8 to the pixel and its
     vertical air comes to 37.6 against their 58.3-71.6 — a portrait block in a
     landscape plate cannot have both, and type is the half the owner named.
     Card 2 is square either way; see `.bx-field`, whose balance is independent
     of this number. Card 3 has no scale at all: its render is made at exactly
     the box it is drawn in.

     RE-SOLVE AGAINST THE TYPE. 13 is measured off the reference card and 14 is
     the app's `text-sm`; do not nudge this to buy air. */
  --bx-fit: 0.9286;

  box-sizing: border-box;
}

/* The popover's own three, and the one hue the trend line's confidence mix runs
   to. `--popover` is the page background in light and its own lighter gray in
   dark; `--hover-opaque` is the panel inside it, and on a popover a panel
   painted in the popover's own colour is no panel at all. Both are restated in
   dark at the foot of this file. `--destructive` is oklch(0.6367 0.1731 27.57)
   by arithmetic — the app mixes primary into it to say how far the fit is
   extrapolating, and nothing else here is red. */
.bx-price {
  --bx-popover: var(--color-theme-bg);
  /* TWO fills and not one, because the app has two. `--hover` is a 6% WASH of
     the foreground — translucent, so it darkens whatever is under it — and is
     what `hover:bg-hover` paints on a hovered table row; `--hover-opaque` is
     the fixed light gray the chart panel inside the popover takes. On this
     card's tinted plate the difference is visible rather than academic: the
     opaque fill is LIGHTER than the plate, so a lit cell read as a white box
     stamped on the table instead of as a row under the pointer. The wash needs
     no dark restatement — it reads `--color-theme-fg`, which inverts on its
     own. */
  --bx-hover: color-mix(in srgb, var(--color-theme-fg) 6%, transparent);
  --bx-hover-opaque: var(--color-theme-card-02-hex);
  --bx-pop-fill: var(--bx-hover-opaque);
  --bx-danger: #e1584d;
}

.bx-price *,
.bx-mockup *,
.bx-market * {
  box-sizing: border-box;
}

/* A `<span>` is inline; almost every box here is one, so the default has to be
   undone once. `:where()` contributes no specificity, which is what stops this
   outranking the single-class layout rules under it — the trap
   `automate-hero-mockup.css` paid a build for. */
.bx-price :where(span),
.bx-mockup :where(span),
.bx-market :where(span) {
  display: block;
}

/* Every glyph in here is lucide's at stroke-width 1 in DEVICE pixels: a
   window's contents are scaled by 0.7, and a 1px rule at 0.7 is a smear.
   `vector-effect` does not inherit, so it is set on the elements. */
.bx-price svg,
.bx-mockup svg,
.bx-market svg {
  display: block;
  flex: 0 0 auto;
  stroke-width: 1.5;
}

.bx-mockup svg * {
  vector-effect: non-scaling-stroke;
}

/* =========================================================================
   1 — Price forecasting

   The field, its estimated value, and the app's own popover under it. Drawn
   1:1 inside the capture's own `max-w-[340px]` column, which is centred in a
   378px box.
   ========================================================================= */

.bx-price {
  display: flex;
  flex-direction: column;
  /* The popover hangs off the lit cell, so the gap is the app's `sideOffset`
     read downwards rather than a section step: small enough that the two read
     as one object. The app's own is 0 to the right of the cell; 10 is what a
     drop shadow needs to clear the cell's bottom edge and be seen. */
  gap: 10px;
  width: 100%;
  color: var(--bx-text);
  /* The row's own scale — see `--bx-fit`. About the centre, so the plate's own
     `items-center` still centres it and the layout box does not move: this is
     paint, not layout, which is what makes it cost the page no height. A
     portrait block in a landscape plate cannot match all four of #workflows'
     margins at once, and with the scale solved off type it is the WIDTH that
     lands on theirs (286 against 288, on 52.9 of side air against 51.8) while
     the vertical comes to 37.6 against their 58.3-71.6. */
  transform: scale(var(--bx-fit));
  transform-origin: center;
}

/* -------------------------------------------------------------------------
   THE TRIGGER IS A TABLE SLICE, AT THE APP'S OWN 1:1.

   It was the field's name over one 28px italic number until 2026-09-04 — a
   document *sidebar*'s way of drawing a value — and beside two cards holding
   app screens it was the one of the three that did not look like the product.
   What the popover hangs off in the app is a Unit Price **cell**, so that is
   what is drawn: `SortableHeader`'s `Aa Name` / `# Unit Price` over two body
   rows on the app's own 36px pitch, the second lit and carrying the pointer.

   Every length is the app's at 1:1, as the popover under it is: `text-sm` in a
   36px row, `p-2` either side of a cell, `gap-2` to a `size-4.5` type mark.
   Do not scale one by hand.

   ONE ROW, AND THE PLATE WOULD TAKE TWO. The captured slot is a `height:378px`
   box that centres its column and clips both ends, so anything taller loses
   its head and its foot together: the ceiling is a header and two rows (36 +
   72 + this column's 10px gap + the popover's 244 = 362 of 378), and a third
   is 398. One is drawn at the owner's direction — this is a trigger, not a
   table, and a second row is another item's price under a popover about the
   first.

   The table is 280 wide — the popover's own width, Name 170 + Unit Price 110 —
   so the two share a left edge and a right edge and read as one gesture. Name
   is the only column that truncates, and the lit row's name fits whole.
   ------------------------------------------------------------------------- */

.bx-price__table {
  position: relative;
  width: 280px;
  max-width: 100%;
  font-size: 0.875rem; /* text-sm */
  line-height: 1.4286;
}

.bx-price__row {
  display: flex;
  align-items: stretch;
  height: 36px; /* VIEW_TABLE_ROW_HEIGHT, and TABLE_HEADER_HEIGHT is the same */
}

/* The app draws the header's rule as an inset shadow rather than a border, so
   the sticky header group keeps its exact height. */
.bx-price__head {
  box-shadow: inset 0 -1px 0 0 var(--bx-line);
}

.bx-price__th,
.bx-price__td {
  display: flex;
  align-items: center;
  gap: 8px; /* gap-2 */
  min-width: 0;
  padding: 0 8px; /* p-2 */
  overflow: hidden;
  white-space: nowrap;
}

/* `font-medium text-muted-foreground`, at the same `text-sm` as the body — the
   header is neither smaller nor uppercase. */
.bx-price__th {
  color: var(--bx-text-sec);
  font-weight: var(--font-weight-medium);
}

/* `text-muted-foreground-opaque`, which is the SAME colour as the label's own
   `text-muted-foreground`: the app defines the pair identically and the
   "opaque" spelling exists to give an alpha-free value, not a lighter step. So
   the type mark reads at the label's weight and not below it. */
.bx-price__mark {
  width: 18px; /* size-4.5 */
  height: 18px;
  color: var(--bx-text-sec);
  stroke-width: 1.5;
}

/* The title field's "Aa" is drawn rather than stroked. */
.bx-price__mark[data-filled] {
  fill: currentColor;
  stroke: none;
}

.bx-price__row:not(.bx-price__head) .bx-price__td {
  border-bottom: 1px solid var(--bx-line);
}

/* Numbers are always right-aligned, on tabular figures. */
.bx-price__td--num {
  justify-content: flex-end;
  font-variant-numeric: tabular-nums;
}

/* THE CELL IS LIT AND NOT THE ROW, which is the divergence `items-mockup.css`
   sets out at length and takes for the same reason: the app lights the whole
   row, but a row-wide band says "this row" where the popover hanging under it
   is about **this price**. Lighting the cell the popover comes out of is what
   makes the pointer, the fill and the popover read as one gesture. */
.bx-price__td[data-hover] {
  background: var(--bx-hover);
}

/* `estimatedValueClassName`, the app's whole marker for a value the model
   produced rather than a person typed: `italic text-foreground/50`. The italic
   is half the signal and not decoration — `EstimatedValue` sets the two
   together and nothing in the app sets one without the other. Unlike the block
   this replaced, the SIZE is the app's too. */
.bx-price__value {
  font-style: italic;
  color: color-mix(in srgb, var(--bx-text) 50%, transparent);
}

/* The product's own pointer, at the size the app's own demo videos draw it.
   The path's tip is 76 of its 512 viewBox in from both edges, so the box is
   offset by 14.84% of itself and `left`/`top` are where the tip lands: inside
   the lit Unit Price cell, which sits under the header row — so it runs
   x 170..280, y 36..72, and the tip is just right of the figure and a little
   below its middle. */
/* A POINTER IS ON THE SCREEN, NOT INSIDE THE APP, so it divides the row's scale
   back out and lands at 22 CSS px whatever `--bx-fit` becomes — the rule
   `.bx-cursor` on card 2 already follows. Without it the two cards drew the same
   demo cursor at 18 and 24. */
.bx-price__cursor {
  --bx-price-cursor: calc(22px / var(--bx-fit));

  position: absolute;
  z-index: 2;
  left: calc(238px - var(--bx-price-cursor) * 0.1484);
  top: calc(58px - var(--bx-price-cursor) * 0.1484);
  width: var(--bx-price-cursor);
  height: var(--bx-price-cursor);
  /* The asset's own `feDropShadow dx=7 dy=9 stdDeviation=7` in #4B5563 at 22%,
     restated at the size it is drawn. */
  filter: drop-shadow(0 1px 1.5px rgb(75 85 99 / 0.22));
}

.bx-price .bx-price__cursor svg {
  width: 100%;
  height: 100%;
}

/* The paint is restated here rather than left on the paths because this file's
   own `svg` rule would otherwise repaint the glyph, and an author rule beats a
   presentation attribute. These are the asset's values — a `#C9CDD3` 38 ring
   under a `#FAFAFA` 30 one, over a black fill — and the stroke widths are user
   units on its own 512 viewBox. */
.bx-price .bx-price__cursor path {
  fill: #000;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bx-price .bx-price__cursor path:first-child {
  stroke: #c9cdd3;
  stroke-width: 38;
}

.bx-price .bx-price__cursor path:last-child {
  stroke: #fafafa;
  stroke-width: 30;
}

/* -------------------------------------------------------------------------
   The price popover, at 1:1.

   `w-70 rounded-md border border-border/70 bg-popover p-3` with
   `--popover-shadow` verbatim, which is the one shadow here that cannot come
   off a theme token because it must stay black in both themes. Height is
   content, as it is in the app — the 144px chart is the only fixed piece of it:
   2 of border + 24 of padding + head 28 + 2 + chart 144 + 6 + foot 38 = 244,
   measured on the built page.

   Type is the app's `text-xs`, 12px, because this object IS the app's at 1:1.
   ------------------------------------------------------------------------- */

.bx-popover {
  display: flex;
  flex-direction: column;
  width: 280px;
  max-width: 100%;
  padding: 12px; /* p-3 */
  border: 1px solid color-mix(in srgb, var(--bx-line) 70%, transparent);
  border-radius: 8px; /* rounded-md */
  background: var(--bx-popover);
  font-size: 0.75rem;
  line-height: 1.333;
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.1),
    0 4px 6px -4px rgb(0 0 0 / 0.1);
}

/* The generic span rule above blocks every `<span>` on this card; the two
   inside the popover's badge are text runs and have to stay inline. Two classes
   beat that rule's one. */
.bx-price .bx-popover :where(span) {
  display: inline;
}

.bx-ell {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `size-4` at 1:1, against the 14px `items-mockup.css` draws the same glyphs at
   under its own 0.86. It is keyed off the two SLOTS that hold an icon and not
   off `svg` inside the popover, which would also catch the chart: `.bx-price
   svg` already scores (0,1,1), so an `.bx-price .bx-popover svg` would need the
   plot to outrank (0,2,1) and there is no honest selector for that. */
.bx-pop-more svg,
.bx-pop-tab svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* `flex min-w-0 items-center gap-2 p-1`, with a `size-7` round ghost button on
   the right of it — and the ONE length in this popover that is not the app's.
   That `p-1` is there to grow the ghost button's hit area, and a still has no
   pointer to grow it for: at 1:1 it stacks 4px on top of the popover's own 12
   and the 6 the 28px button already centres a 16px title inside, which reads as
   a title floating in a band. The vertical half comes off (36 -> 28), which
   puts the title 19px below the popover's top edge and takes the popover 252 ->
   244. The horizontal half stays — that one is the gap to the border. */
.bx-pop-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 4px;
}

.bx-pop-name {
  flex: 1 1 auto;
  font-weight: var(--font-weight-medium);
  color: var(--bx-text);
}

.bx-pop-more {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px; /* size-7 */
  height: 28px;
  border-radius: 9999px;
  color: var(--bx-text-sec); /* text-muted-foreground-opaque */
}

/* `relative h-[144px] w-full overflow-hidden rounded-xl bg-hover`. The radius is
   the app's own 14 — `--radius` is 0.625rem there, so `rounded-xl` is 14 and not
   Tailwind's unanchored 12. */
.bx-pop-chart {
  position: relative;
  height: 144px;
  margin-top: 2px; /* space-y-0.5 */
  overflow: hidden;
  border-radius: 14px;
  background: var(--bx-pop-fill);
}

/* THE CHART IS THE APP'S OWN UNITS AND THE BOX IS 1:1 WITH THEM. The viewBox is
   256 x 144 into a 256 x 144 box, so the plot inset is the app's 22/8/8/22, the
   axis type is its 10px and the trend stroke its 3 — all at their own size,
   which is the whole difference between this sheet and items-mockup's. That is
   also why `vector-effect` is off in here: the icons want device pixels, a
   chart wants its strokes to scale with it. */
.bx-price .bx-pop-plot {
  display: block;
  width: 100%;
  height: 100%;
  fill: none;
  stroke: none;
  stroke-width: 1;
  color: var(--bx-text-sec);
}

.bx-price .bx-pop-plot * {
  vector-effect: none;
}

.bx-pop-axis {
  fill: currentColor;
  font-size: 10px;
}

/* The two `stroke-border` axis lines. */
.bx-pop-rule {
  stroke: var(--bx-line);
}

/* `strokeWidth 3`, `strokeLinecap round`, and a per-segment stroke the fragment
   writes: `color-mix(in oklab, primary N%, destructive 100-N%)` off that
   segment's own confidence. */
.bx-pop-trend {
  stroke-width: 3;
  stroke-linecap: round;
}

/* `fill-muted-foreground`, r 3; an outlier is hidden rather than removed, which
   on this chart is `opacity-30` — the reader can see what the fit ignored. */
.bx-pop-dot {
  fill: currentColor;
}

.bx-pop-dot--out {
  opacity: 0.3;
}

/* The estimate marker: `text-foreground` dashed drops at 60%, and a ringed dot
   whose centre is `fill-hover-opaque` so the dots behind it do not read
   through. It sits on the right edge of the plot because this row's quantity is
   the largest on the chart — which is the app, not a clip. */
.bx-pop-est {
  color: var(--bx-text);
}

.bx-pop-est line {
  stroke: currentColor;
  stroke-dasharray: 3 3;
  opacity: 0.6;
}

.bx-pop-est circle {
  fill: var(--bx-pop-fill);
  stroke: currentColor;
  stroke-width: 1.5;
}

/* `flex min-w-0 items-center gap-0.5` under a `mt-1.5`. */
.bx-pop-foot {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-top: 6px;
}

/* `h-9 min-w-0 flex-1 justify-between gap-2 rounded-full border-transparent
   bg-hover px-3 font-normal`. */
.bx-pop-badge {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  height: 36px; /* h-9 */
  padding: 0 12px; /* px-3 */
  border-radius: 9999px;
  background: var(--bx-pop-fill);
  color: var(--bx-text);
  font-variant-numeric: tabular-nums;
}

/* `PricePerUnitLabel` sets the unit suffix in `text-secondary`, and the
   confidence sits in a `text-secondary` pill of its own. */
.bx-pop-unit,
.bx-pop-conf {
  color: var(--bx-text-dim);
}

.bx-pop-conf {
  flex: none;
  padding: 2px 6px; /* px-1.5 py-0.5 */
  border-radius: 9999px;
  white-space: nowrap;
}

/* `flex shrink-0 items-center gap-0 rounded-full border border-border/70 p-0.5`.
   None of the three is active: an active tab lights `bg-hover text-primary`,
   swaps its glyph for an X and widens the whole popover to 440. */
.bx-pop-tabs {
  flex: none;
  display: flex;
  align-items: center;
  padding: 2px; /* p-0.5 */
  border: 1px solid color-mix(in srgb, var(--bx-line) 70%, transparent);
  border-radius: 9999px;
}

/* `icon-sm`, i.e. `size-8`. It is 32 and not 25 here because this popover is
   drawn at 1:1 — and it is also what keeps this row of three clear of
   `empty-mockups.js`, which only matches children under 16px wide. */
.bx-pop-tab {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 9999px;
  color: var(--bx-text-sec);
}

/* =========================================================================
   2 — Likely bidders

   `.bx-screen` is the app's own pixels; everything under it is
   bidlo-light-1.0's number.

   THE CAPTURED WINDOW IS DRAWN BARE. The fragment's `bareWindow` strips that
   frame's radius and fill and re-centres it on the plate, so what is left is a
   400 x 378 positioning box with nothing painted on it — cards 1 and 3 lie on
   their plate the same way, and a browser round one list in a row of three
   cards read as a second frame inside a card that is already a frame. Nothing
   below paints a surface for the same reason.
   ========================================================================= */

.bx-mockup {
  /* DRAWN 1:1 IN CARD PIXELS, which is the other two cards' scale and is what
     the frame going bare changes. A window mockup is a scaled picture of a
     screen, so this wrote the app's own 571 x 540 under a `scale(0.7)`; a free
     block lying on a plate has no screen to be a scaled copy of, and at 0.7 it
     set the app's 14px type at 9.8 beside card 1's popover at 14. So every
     length below is the app's number in card pixels, and the box is the plate
     — `bareWindow` gives the captured frame `inset: 0` at 100%.

     IT TAKES THE ROW'S SCALE AND NOT ONE OF ITS OWN. It stood at 1.25 for one
     build, to make the five chips fill more of the plate, and came back the
     same day at the owner's direction: at 1.25 this card's 14px chips rendered
     17.5 beside card 1's 11.5, so the same app type stood at two sizes on one
     row. `--bx-zoom` is `--bx-fit` now — the pointer still divides by it, so it
     still lands at 24 CSS px — and the chips are back at the app's own TYPE,
     which is the half of that report that mattered. Their box has since grown
     by its padding alone (see `.bx-rel`); a scale would have taken the type
     with it, and that is the line. */
  --bx-zoom: var(--bx-fit);

  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  color: var(--bx-text);
}

.bx-screen {
  width: 100%;
  height: 100%;
  min-height: 0;
}

/* THE COLUMN IS CENTRED AND THE LIST IS LEFT-ALIGNED INSIDE IT. The label hangs
   above the list and the tooltip below it, both absolutely, leaving the chips
   as the only thing in flow — so `justify-content` centres them down the card
   and `align-items` centres the COLUMN across it, which is `.bx-field`'s 287
   rather than the widest chip's width. See that rule. With all three in flow
   the tooltip's ~84px dragged the list a third of the way up the plate. */
.bx-doc {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  /* 16 a side rather than 20, at the width the three-column grid is narrowest.
     From ~1210px of viewport up the column's own 287 is the smaller of the two
     and this pad does nothing; below it the column clamps against the pad, and
     16 lands the clamped list on card 1's own left air to a hundredth of a
     pixel at 1140 (26.45 against 26.46) where 20 left it 3.7px inside. */
  padding: 16px;
  overflow: hidden;
  /* THE WHOLE DRAWING TAKES THE ROW'S SCALE, HERE AND NOT ON `.bx-field`. The
     tooltip is a sibling of the field rather than a child, so a transform on
     the field alone would leave a 12px panel beside 11.5px chips. Scaling this
     box scales the field, the label, the pointer and the tooltip together, and
     it costs the tooltip's own solved 288px floor nothing: a transform does not
     change layout, so the name inside it is measured at 288 and only then drawn
     smaller. */
  transform: scale(var(--bx-fit));
  transform-origin: center;
}

/* One field row from `DocumentSidebarTabs`' Details panel, drawn filling the
   window rather than docked beside the page it belongs to — see the fragment's
   header for why. */
.bx-field {
  /* The containing block for the label, the pointer and the tooltip, all three
     of which are out of flow — so its own box is the LIST's height and the
     COLUMN's width.

     THE COLUMN IS 287 RENDERED, WHICH IS CARD 3'S MAP, AND THAT IS WHAT
     LEFT-ALIGNS THIS LIST ON THE SECTION'S OWN PADDING. Measured on the built
     page at 1440, the plate is 391.66 and both neighbours are fixed-width
     blocks centred on it — card 1's table starts 52.8px in, card 3's map 52.3
     — while a list sized to its own widest chip and centred started somewhere
     else on every render, which is what was reported on 2026-09-04.
     A 287 column centred on the same plate puts the chips' left edge on the
     map's to within half a pixel, at every width the three-column grid draws,
     and 287 is already stated in `bid-context.mjs`, `tools/market-map-render.mjs`
     and this sheet's own `.bx-market`. Move all four together.

     DIVIDED BY THE SCALE, which `.bx-doc` carries and this rule must not: 287 is
     the RENDERED width, so the declaration is 309.06 and the pair cannot drift
     when `--bx-fit` is re-solved. `max-width` is what stops it overflowing the
     plate below the three-column grid, where it simply narrows and stays left
     aligned. */
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  width: calc(287px / var(--bx-fit));
  max-width: 100%;
  /* THE LABEL SITS ON CARD 1'S COLUMN HEADERS, WHICH IS WHY THIS IS 124 AND NOT
     A CENTRING. `.bx-doc` centres its one in-flow child, so at rest the LIST sat
     on the plate's middle — and the two cards' blocks are different heights, so
     two centred blocks put "Planholders" 25px below "Aa Name" and the row read
     as two drawings that missed each other. The owner asked for the two lines to
     meet.

     SOLVED IN UNSCALED SPACE, WHICH IS WHAT MAKES IT HOLD AT ANY `--bx-fit`.
     Both cards are centred on the same plate and scaled about its middle, so
     two labels that meet at 1 meet at every shared scale — and the two numbers
     that have to agree are card 1's header centre and this label's. Card 1's
     block is 326 tall, so centred its top is 163 above the plate's middle and
     the header's own 36px row centres its text 18 below that: 145 above the
     middle. Here the label is 20px on a 6px margin over half a list of seven
     30px chips on six 6px gaps, i.e. 16 + 123 = 139 above the list's
     centre — so the list's centre has to sit 145 - 139 = 6 ABOVE the
     plate's middle, and a `justify-content: center` item moves by HALF its
     margin.

     WHAT THE MARGIN ACTUALLY DOES IS HOLD THE LIST'S TOP AT 60 whatever the
     list holds — the constant the plate's whole balance is derived from (see
     `DRAWN`) — so its sign follows the count rather than meaning anything: the
     whole expression is `258 - listH`, i.e. 66 at six 27px chips, -24 at eight
     30px ones and **12 at the seven this card draws**. A list long enough to
     reach past the plate's middle needs the item pushed DOWN off centre to keep
     that top edge; seven 30px chips is not such a list. Re-solve both halves
     together if either block's height, the chip height or the chip count moves
     — all three feed the same half-list. */
  margin-bottom: 12px;
}

/* ABOVE THE LIST RATHER THAN IN FLOW WITH IT, so it costs the field no height
   — see `.bx-doc`. The 6px is the field's own gap, kept as a margin.

   IT DRAWS NO MARK. `field-icon.tsx` maps a `relation` field to lucide's Link2
   and this label carried it until 2026-09-04; it came off at the owner's
   direction, that mark being how a reader tells apart a COLUMN of fields and
   this card drawing one. See the fragment's header. */
.bx-field__label {
  position: absolute;
  bottom: 100%;
  left: 0;
  margin-bottom: 6px;
  white-space: nowrap;
  font-size: 0.875rem; /* text-sm */
  line-height: 1.428; /* 20px */
  color: var(--bx-text-dim);
}

/* -------------------------------------------------------------------------
   The relations, and the two of them that are still estimates.

   `RelationValue` in `field-value-display.tsx` lays a field's relation chips
   out `flex flex-wrap gap-1.5`; each is `bg-gray-badge dark:bg-default-badge`
   in the same `rounded px-1.5 leading-5 text-sm` a tag badge takes, with the
   collection's own glyph in front of the name where the target document has no
   icon of its own. The wrap is replaced by a column here — see the rule
   below.
   ------------------------------------------------------------------------- */

/* A COLUMN, WHERE THE APP WRAPS. `RelationValue` is `flex flex-wrap gap-1.5`
   over `w-fit` chips, and at any width wide enough to read these names two of
   the short ones share a line — "Halcort Paving" and "Delvary Aggregates" come
   to 295px between them. A five-across ragged wrap is not the list this card is
   about. The chips keep `w-fit`; only the wrapping moves. */
.bx-rels {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
}

.bx-rel {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  max-width: 100%;
  min-width: 0;
  /* PADDED WELL PAST THE APP'S `px-1.5 leading-5`, at the owner's direction on
     2026-09-04 after two reports that the badges read too tight. The app's chip
     is 22 — a 20px line box on nothing — and this is **30**: 5px above and
     below, 8px either side (`px-2`, its own next step). It was 27 (3.5px) until
     2026-09-04, when a name came off the list and the balance below bought the
     other 3. The type is untouched,
     being the row's shared 13px ladder, which is what sent a 1.25 scale back
     the day before.

     NO `height`, DELIBERATELY: the chip is its line box plus its padding, so
     the one declaration says what grew. It used to be the length the plate's
     whole balance was solved against — with the tooltip overlaid the block sits
     square when the list is 284 tall, which a 27px chip makes at nine and a 30px
     chip at eight, so a name off the list was 3px onto every chip. **THAT CHAIN
     WAS BROKEN ON 2026-09-07 AND DELIBERATELY**: the owner took a seventh name
     off because the graphic was too tall, and paying for it here would have
     grown the chip to 35.4 and returned exactly the height that was asked for.
     So the chip stayed and the block sits high — 45.1 above, 80.4 below. See
     `DRAWN` in the fragment. */
  padding: 5px 8px;
  border-radius: 4px;
  background: var(--bx-badge);
  font-size: 0.875rem;
  line-height: 1.428;
}

/* `CollectionAvatar`'s answer for the Contractor collection's own
   `icon_contractor`, which is what a contractor with no stored logo draws. It
   was each firm's own `<slug>-mark.svg` for one build on 2026-09-04 and the
   owner reverted it the same day — six coloured lockups cropped to 16px are six
   pictures down one column, where one glyph repeated is a list. */
.bx-rel__mark {
  width: 14px;
  height: 14px;
  color: var(--bx-text-dim);
}

.bx-rel__name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* An estimated relation edge. `DocumentPkAvatar` puts `opacity-50` on the chip
   and `estimatedChipValueClassName` — `italic text-muted-foreground` — on the
   label inside it, and sets the two together; nothing in the app sets one
   without the other. */
.bx-rel[data-estimate] {
  opacity: 0.5;
}

.bx-rel[data-estimate] .bx-rel__name {
  font-style: italic;
  color: var(--bx-text-sec);
}

/* -------------------------------------------------------------------------
   The Estimates tooltip the pointer has opened.

   `field-value-display.tsx` wraps every relation chip in `EstimatedValue
   showTooltip`, so in the app hovering an ESTIMATED chip — and only an
   estimated one — opens `EstimateDetails` in a tooltip: `border
   border-border/70 bg-popover p-2 text-popover-foreground shadow-popover` at
   `max-w-[20rem]`. Here it hangs off a CONFIRMED chip, which is this card's one
   departure from that rule and the owner's own call; see `HOVERED` in the
   fragment.

   Inside it: `space-y-3 text-xs`, a `font-medium text-secondary` head, then the
   value's own `space-y-1.5` block — label, a dotted rule that grows, and the
   confidence indicator, which is `font-mono text-[11px]` — and under it the
   `└ source • date` line in `italic text-muted-foreground`.
   ------------------------------------------------------------------------- */

.bx-est {
  /* UNDER THE HOVERED CHIP, NOT BESIDE IT. The app places this
     `side="right" align="start" sideOffset={10}`; the plate is ~392px, the
     hovered chip ends around 282, and the panel beside it would run half off
     the card. So it keeps the app's 10px offset and takes it downward. Radix
     would flip such a tooltip to `left`, which does not fit either.

     IT HANGS OFF `.bx-field` — the column — AS OF 2026-09-04, where it hung off
     `.bx-doc` and was centred on the plate. Both of those offsets were solves
     against the plate's middle, and both are gone: the panel is narrower than
     the 287 column, so its edges are the column's.

     IT SITS UNDER THE HOVERED CHIP AND OVER THE ONES BELOW IT, AND IT IS
     RIGHT-ALIGNED, both at the owner's direction on 2026-09-04. The pointer
     moved to the third chip, so the `sideOffset` is now a real solve rather
     than a `top: 100%`: `HOVERED + 1` chips, `HOVERED` gaps, then the app's own
     10 — 3 * 30 + 2 * 6 + 10 = 112. Re-derive it from `HOVERED` alongside
     `.bx-cursor`'s `top`; the two name the same chip and a stale one names two
     different ones. It costs the block no height either way, being out of flow
     — which is what let the list run past the plate's middle at all (see
     `DRAWN`), and what keeps its foot inside a seven-chip list.

     `right: 0` rather than `left: 0` puts the panel's right edge on the 287
     column's, i.e. on card 3's map's right edge, so the graphic closes on the
     same line the row does; the chips stay flush to the column's left edge, so
     the ~45px of each covered chip that still shows is what an overlay looks
     like. It must stay at or under 287 or that edge stops being the column's. */
  position: absolute;
  top: calc(3 * 30px + 2 * 6px + 10px); /* HOVERED's chip + the app's `sideOffset` */
  right: 0;
  /* NARROWER THAN THE APP'S `max-w-[20rem]`, which is a CEILING on a panel that
     otherwise sizes to its content — and in the app that content is a whole
     cell's set of values where this draws one. At 320 the dotted rule ran ~85px
     and the panel read as an empty box with a name in one corner.

     IT WAS 288 FOR A DAY AND CAME IN AGAIN THE SAME DAY at the owner's
     direction, and both that 288 and the 248 that replaced it were solved
     against a NAME — the hovered chip's — which is the lesson here rather than
     either number: the row's label followed `DRAWN`, so the panel's floor moved
     every time the chip count did. It does not any more. `ESTIMATE_LABEL` is
     fixed copy ("Likelihood to Bid") as of 2026-09-04, so the floor is a
     constant. Measured on the built page at 1440, `.bx-est__row`'s parts are
     2 (the panel's own borders) + 16 (`p-2` a side) + **90.3** (the label at
     12px/500) + 8 + 16 (`.bx-est__rule`'s own `min-w-4`) + 8 + 105.11 (the
     13-block bar and its percentage at 11px mono) = **245.4**. So the floor is
     246, and **264** ships: the rule then draws ~35px, which is a dotted leader
     rather than a stub. A pixel under the floor ellipsises the one thing the
     panel exists to name, and neither the bar nor the percentage can give
     anything up — thirteen blocks is `CONFIDENCE_BAR_WIDTH`. RE-MEASURE if
     `ESTIMATE_LABEL` is reworded; and the panel must stay inside the 287
     column, or its right edge stops being the column's. */
  width: 264px;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px; /* space-y-3 */
  padding: 8px; /* p-2 */
  border: 1px solid color-mix(in srgb, var(--bx-line) 70%, transparent);
  border-radius: 8px;
  background: var(--bx-surface);
  font-size: 0.75rem; /* text-xs */
  line-height: 1.333;
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.1),
    0 4px 6px -4px rgb(0 0 0 / 0.1);
}

.bx-est__head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: var(--font-weight-medium);
  color: var(--bx-text-sec);
}

.bx-est__mark {
  width: 16px;
  height: 16px;
  fill: currentColor;
  stroke: none;
}

.bx-est__item {
  display: flex;
  flex-direction: column;
  gap: 6px; /* space-y-1.5 */
  min-width: 0;
}

.bx-est__row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.bx-est__label {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: var(--font-weight-medium);
  color: var(--bx-text);
}

/* `min-w-4 flex-1 border-b border-dotted border-border`. */
.bx-est__rule {
  flex: 1 1 auto;
  min-width: 16px;
  border-bottom: 1px dotted var(--bx-line);
}

/* `shrink-0 font-mono text-[11px] text-muted-foreground`. The blocks are the
   app's own bar — thirteen of them, filled by `floor(percent / 100 * 13)` — so
   the fragment emits the string and this only sets it in the face that makes a
   run of blocks read as a bar. */
.bx-est__conf {
  flex: none;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  letter-spacing: -0.04em;
  color: var(--bx-text-dim);
}

.bx-est__meta {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-style: italic;
  color: var(--bx-text-dim);
}

.bx-est__tee {
  flex: none;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-style: normal;
  opacity: 0.7;
}

.bx-est__src {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.bx-est__dot,
.bx-est__when {
  flex: none;
}

.bx-est__dot {
  opacity: 0.6;
}

/* -------------------------------------------------------------------------
   The pointer.

   `bidlo-demo-cursor.svg`, the cursor Bidlo's feature videos are rendered with,
   restated the way `items-mockup.css` restates it — the paint lives here rather
   than on the paths because the `svg *` rule at the top of this file sets a
   non-scaling stroke for every lucide glyph in the window, and an author rule
   beats a presentation attribute.

   IT IS DRAWN AT 24 CSS px, NOT 24 APP px. A pointer is on the screen, not
   inside the app, so it does not take the window's 0.7 — dividing by the zoom
   here is what lands it at the size the same glyph is drawn at in the items
   window. Its own tip is at 76/512 of its box, so `left`/`top` are the point it
   rests on less that offset: the third chip's own row, a little inside its
   name.
   ------------------------------------------------------------------------- */
.bx-cursor {
  --bx-cursor-size: calc(24px / var(--bx-zoom));

  position: absolute;
  z-index: 3;
  /* Inside `.bx-field`, whose box is the LIST's height — the label hangs above
     it absolutely and the tooltip below it — so both numbers are solved off
     lengths stated above: down is `HOVERED` chip pitches and half a chip, i.e.
     the centre of the third one, across is a little inside its name. The
     glyph's own tip sits at 76/512 of its box, which is what the two
     subtractions are. (The label's 20px line box and the field's 6px gap were
     in this sum until 2026-09-04 and left it with the label.)

     IT IS `HOVERED`'S ROW AND NOTHING ELSE, WHICH IS WHY IT IS TWO PITCHES AS
     OF 2026-09-04: the owner moved the pointer to the THIRD chip, so the sum is
     `HOVERED` pitches and half a chip. It was four pitches while the list held
     five, then five while it held six, and each of those was the same solve
     with a different count — a stale one here puts the pointer on one chip
     while the panel explains another, which this card has already shipped
     once. Re-derive it from `HOVERED`; never nudge it. */
  left: calc(56px - var(--bx-cursor-size) * 0.1484);
  top: calc(2 * (30px + 6px) + 15px - var(--bx-cursor-size) * 0.1484);
  width: var(--bx-cursor-size);
  height: var(--bx-cursor-size);
  /* The asset's own `feDropShadow dx=7 dy=9 stdDeviation=7` in #4B5563 at 22%,
     restated at the size it is drawn. */
  filter: drop-shadow(0 1px 1.5px rgb(75 85 99 / 0.22));
}

.bx-mockup .bx-cursor svg {
  display: block;
  width: 100%;
  height: 100%;
}

.bx-mockup .bx-cursor path {
  fill: #000;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: none;
}

/* The asset's two rings: a `#C9CDD3` 38 under a `#FAFAFA` 30, over a black
   fill. Both widths are user units on its own 512 viewBox, so
   `non-scaling-stroke` has to come back off. */
.bx-mockup .bx-cursor path:first-child {
  stroke: #c9cdd3;
  stroke-width: 38;
}

.bx-mockup .bx-cursor path:last-child {
  stroke: #fafafa;
  stroke-width: 30;
}

/* =========================================================================
   3 — Market trends

   A statewide seal-coat market as the app's cloud heatmap, with the two saved
   filters drawn as `PillWrapper` pills under it. Drawn inside the capture's
   "Tool calls" region, which caps at min(83.3333%, 360px).

   EVERY LENGTH HERE IS A CARD PIXEL AND THERE IS NO SCALE.
   The map read the Vendors card's 657 x 431 render through a crop and a zoom
   variable until 2026-09-04; it has its own render now, made at exactly
   the box it is drawn in (`tools/market-map-render.mjs`), so the crop, the zoom
   and the sheet they moved are all gone and a blob sits at the pixel
   `project()` gave it with nothing in between.

   THE BOX IS 287 x 199 AND IT IS MEASURED, NOT PICKED. With `.bx-filters` over
   it the graphic stands 26 + 10 + 199 = 235, which is what the Document-analysis
   card one section over measures on the built page: its wrapper is
   `w-full max-w-[320px] px-4`, i.e. a 288px card 234.8 tall, centred by the
   shared 378px plate's own `items-center` with 71.6px above and below. So the
   two cards carry the same air on all four sides. `MARKET_MAP` in the fragment
   carries both numbers and the render is a 2x of them, so moving one means
   moving all three and re-running the script.
   ========================================================================= */

.bx-market {
  display: block;
  width: 287px;
  max-width: 100%;
  margin: 0 auto;

  /* THE PLATE UNDER THIS CARD, AND IT IS THE ONE OF THE THREE THAT IS NOT THE
     PAGE. `restatePlates` puts cards 1 and 2 on `bg-theme-card-02-hex` and
     leaves card 3 on `MARKET_PLATE`, which is `olive` — `#CFCEBC / #5C5743`,
     `media-plate.mjs`' own pair, restated here because a stylesheet cannot
     read it. Move the two together; a test pins them equal.

     What reads it is `--bx-mark`, one line down. */
  --bx-plate: #cfcebc;

  /* THE MARK'S INK, SOLVED AGAINST THAT PLATE RATHER THAN AGAINST THE PAGE.
     It was `--bx-text-dim` until 2026-09-10, i.e. the foreground at 40% mixed
     into `--color-theme-bg` — which is right on cards 1 and 2, whose plates
     ARE a step off the page, and wrong here. Measured in dark: the page is
     #191919 and that mix renders #636363, while this plate is #5C5743 and the
     pill's own 5.5% white wash over it renders ~#676257 — so the glyph landed
     within a couple of values of the fill it sits on and did not draw at all.
     Reported.

     It is the SAME fault `--bx-badge` at the head of this file records paying
     for one layer out, and the same fix: solve against the ground the thing is
     actually on. The percentage is `--bx-text-dim`'s own 40, so the mark still
     reads as a step back from the label rather than as a second word in the
     pill — in dark it is #9d9a8e on ~#676257, in light #86847a on ~#bdbaa7.

     IT IS OPAQUE AND MUST STAY SO. Both glyphs here are lucide LINE marks
     whose paths cross (the calendar's rule over its own rect, the tag's two
     arcs), so an alpha composites twice at every crossing and paints the
     junction a step darker than the strokes running into it — the call
     `nav-panel.css` records for its own plates. */
  --bx-mark: color-mix(in srgb, var(--color-theme-fg) 40%, var(--bx-plate));
}

.bx-heat {
  /* The root of this graphic is a `<span>` and the blockifier at the top of this
     file only reaches its DESCENDANTS, so it has to say so itself — an inline
     box ignores width, height and `overflow`, which draws the map unclipped
     from wherever the line box happens to sit. */
  display: block;
  position: relative;
  width: 287px;
  height: 199px;
  max-width: 100%;
  overflow: hidden;
  /* THE POPOVER'S OWN ELEVATION, AS OF 2026-09-04 and at the owner's direction:
     the two cards to the left both raise their subject off the plate — card 1's
     popover and card 2's tooltip carry this exact border, radius and three-layer
     shadow — and this one lay flat on it, which read as a picture pasted on the
     card rather than a surface lying on it. It is the same declaration verbatim,
     dark half included, so the three cannot drift; the radius came 10 -> 8 with
     it, `rounded-md` being what both of those surfaces are. The shadow cannot
     come off a theme token: it must stay black in both themes. */
  border: 1px solid color-mix(in srgb, var(--bx-line) 70%, transparent);
  border-radius: 8px;
  background: var(--bx-chrome);
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.1),
    0 4px 6px -4px rgb(0 0 0 / 0.1);
}

/* THE ZOOM, 2026-09-07 at the owner's direction — *"zoom in on the heat map
   graphic just a little bit so that the text in the bottom left-hand corner is
   cut off"*. That text is the Mapbox / OpenStreetMap attribution, which
   `market-map-render.mjs` draws onto the canvas by hand at (8, H-6) of the 2x
   render, i.e. a 6px line with its baseline 3px off this box's bottom edge —
   so it is INSIDE the picture and the only way past it is to crop the picture.

   IT IS A DISPLAY ZOOM AND NOT A RE-RENDER, and that is forced rather than
   lazy: re-rendering at a higher map zoom draws the attribution again, in the
   same corner, at the same size. So the frame stays 287 x 199 and the layers
   grow inside it, which `.bx-heat`'s own `overflow: hidden` then cuts.

   1.12 IS SOLVED OFF THAT TEXT, NOT PICKED. About the box's centre a point at
   y maps to `99.5 + (y - 99.5) * Z`, and the top of the attribution's ascender
   sits at ~190, so clearing the 199px bottom edge wants `Z >= 1.099`. 1.12 puts
   it at 200.9, i.e. ~2px clear, which is the margin a WebP's own edge softness
   and a half-pixel of layout need. Under 1.10 the tops of the glyphs come back.

   BOTH LAYERS TAKE IT AS ONE OBJECT — the same scale about the same origin on
   the basemap and on the field — which is the whole reason it is a variable and
   not two numbers: they are aligned pixel for pixel today, and a field that
   scales differently from the ground under it is a heatmap of somewhere else.
   `bid-surfaces-quiet.css` scales its own map and marker layer the same way and
   for the same reason. The old `--bx-heat-zoom` / `--bx-heat-crop-*` this must
   not be confused with were something else: they cropped the VENDORS card's
   render, which this card borrowed before it had one of its own.

   Upscaling costs nothing to look at here. The basemaps are a 2x source, so at
   1.12 they still draw at 1.79x density; the field is 1x but is a Gaussian of
   sigma 4.4 drawn px, which is the render script's own argument for writing it
   at 1x in the first place. */
.bx-heat {
  --bx-heat-in: 1.12;
}

/* The two basemap renders are a 2x of exactly this box
   (`tools/market-map-render.mjs`); a `width: 100%` here would rescale them
   against the frame and walk the field off the ground it belongs to. */
.bx-heat__img {
  position: absolute;
  left: 0;
  top: 0;
  width: 287px;
  height: 199px;
  object-fit: cover;
  transform: scale(var(--bx-heat-in));
  transform-origin: 50% 50%;
}

/* The app picks its style off the resolved theme (`getMapStyle`: Bidlo's custom
   light style, and `mapbox/dark-v11`, which is what it falls back to), so this
   does too. Only one of the pair is ever painted. */
.bx-heat [data-theme-dark] {
  display: none;
}

:root[data-theme='dark'] .bx-heat [data-theme-light] {
  display: none;
}

:root[data-theme='dark'] .bx-heat [data-theme-dark] {
  display: block;
}

/* THE FIELD IS ONE IMAGE AS OF 2026-09-05, AND THIS RULE HAS NOTHING LEFT TO
   DECIDE. It was a `.bx-heat__layer` holding 33 `.bx-heat__blob` spans — one
   `radial-gradient` disc per 0.25-degree cell, under a `blur()` — and it was
   reported, three times, as a scatter of cloudy dots. It was: a mosaic
   approximating a shader. `tools/market-heat-render.mjs` now evaluates the
   app's own `heatmap.fragment.glsl` once per pixel and writes the answer to
   `market-heat.webp`, so what is here is a picture of the field.

   THE BLUR WENT WITH THE DISCS. It existed to hide the lattice — 9px statewide,
   5 on the wider band, 3 with an 18px cell, 2 once the field was accumulated
   per cell — and every one of those also blurred the ramp's own steps into each
   other, which is what "too fuzzy" actually was: a heatmap's structure lives in
   where the colour CHANGES. There is no lattice now. Do not add one back.

   IT IS DRAWN AT 1x WHERE THE BASEMAPS ARE 2x, deliberately — see the render
   script's header; the field is a Gaussian of sigma 4.4 drawn px, so a second
   sample buys nothing an eye can see and costs 50KB on every page of the site.
   Sizing it here rather than letting it stretch is what keeps it on the map. */
.bx-heat__field {
  position: absolute;
  left: 0;
  top: 0;
  width: 287px;
  height: 199px;
  /* The SAME scale about the SAME origin as the basemap under it — see the
     `--bx-heat-in` note above. These two move together or not at all. */
  transform: scale(var(--bx-heat-in));
  transform-origin: 50% 50%;
}

/* -------------------------------------------------------------------------
   The two saved filters.

   `PillWrapper`: `flex min-w-0 rounded-full px-2 items-center font-medium
   border`, and an active AND filter — which both of these are —
   is `border-transparent text-primary bg-primary/20`. It is `h-8` and not the
   wrapper's own `h-7`, `HorizontalFilterItem` passing `className="h-8"`; drawn
   here at the app's own 32, this graphic being 1:1 in card pixels.

   THE CHEVRON IS GONE AND THE PILLS GREW INTO ITS WIDTH, 2026-09-04, at the
   owner's direction and in one pass — the two halves pay for each other. A
   chevron is 12px of glyph plus its 2px margin, so dropping both took 28px off
   the pair, and the box spends it: 20 -> 24, the mark 12 -> 13, the padding
   back to the app's own 8.

   THE TYPE IS 13px, WHICH IS THE ROW'S BODY SIZE, as of 2026-09-04 and at the
   owner's direction. It was 11 for two builds — the app's own `text-xs`, which
   is where `--bx-fit` lands cards 1 and 2's small type (11.14) — and the ladder
   argument for it was reading the wrong step: a pill is a BADGE, and the badge
   beside it on card 2 (`.bx-rel`, the planholder chip) renders the app's
   `text-sm` at 13. So two chips of the same kind stood at two sizes on one row,
   which is what it was reported as. 0.8125rem is 13 at this card's own 1:1, i.e.
   the same rendered size as `14 * --bx-fit` next door, and re-solving the fit
   moves both together only if this is re-measured with it.

   WHAT IT COSTS IS THE ROW'S SLACK, AND THE 390 VIEWPORT IS THE BUDGET. There
   the root steps 16 -> 15, so the pair is at its narrowest and so is the plate:
   measured on the built page, 262.44 of ink against the 266.39 the graphic
   gets, i.e. under 4px spare (at 1440 it is 274.87 of 287). A longer label, a
   wider mark or a third pill overflows visibly rather than wrapping, `nowrap`
   being stated for exactly that. Re-measure rather than growing one number.

   THEY SIT ON ONE ROW AND THAT IS WHY THE LABELS ARE SHORT. At the fields' own
   names the pair came to ~390px of ink against a 287px graphic and wrapped;
   `Type` and `Date` fit in 268, which is the owner's own call and is recorded
   in `MARKET_FILTERS`. `nowrap` is stated so a longer label overflows visibly
   rather than quietly going back to two rows.

   THEY ARE `PillWrapper`'s INACTIVE VARIANT AS OF 2026-09-07, at the owner's
   direction — the grey pill, not the blue one. That component has three states
   and the two that matter here are `ACTIVE_AND_STYLE`
   (`border-transparent text-primary bg-primary/20`) and `INACTIVE_STYLE`
   (`border-transparent text-muted-foreground`, no fill of its own). This drew
   the first until then, which is what the app paints on a filter that is
   holding a value — true of both of these — so the departure is deliberate and
   is composition rather than fidelity: on a card whose whole subject is a
   heatmap, two saturated blue pills over it were the first thing the eye landed
   on, ahead of the field they scope. Grey they read as chrome, which is what a
   filter row is.

   AND LATER THE SAME DAY IT TOOK THE BADGE'S OWN COLOURS, at the owner's
   direction — *"make the filter pill colouring match the badges in the previous
   graphic"*. Those are the planholder chips on card 2, one card left and level
   with this row, so what the two cards were disagreeing about was the colour of
   the same object: a badge. `--bx-badge` at the head of the file is that fill
   for both now, with the ink at `--bx-text` and the glyph at `--bx-text-dim`,
   which is the chip's own split. Nothing about the SHAPE followed — the chip is
   a 4px corner on 5px/8px of padding and this is a 28px pill on 6 — because
   these are a filter row and those are a list, and the row's own width budget
   is what set that padding. It is the paint that is shared.

   `PillWrapper`'s `::before` GROUND CAME OFF WITH THE SWAP. That component
   paints one at `-inset-px` in `bg-muted`, or `bg-background` under
   `complexity-advanced-main` — the arm this window is on — and it was
   `var(--bx-surface)` here. It cannot stay under a badge fill: `--bx-badge` is
   a 15% wash, so 15% over a light surface is not the colour 15% over the plate
   is, and the pills would have been a near-match rather than a match. The chip
   sits straight on the plate; so does this.
   ------------------------------------------------------------------------- */

.bx-filters {
  display: flex;
  flex-wrap: nowrap;
  /* 7, up from 4 on 2026-09-07 at the owner's direction, and it is NOT a free
     number: the pair has to stay inside the 270.83 the graphic clamps to in the
     1140-1175 band, and at the old 7px padding it had under a pixel of slack.
     So the gap and the padding move together — +3 here is paid for by -1 a side
     on `.bx-pill`, which is a net -1 on the pair. Re-solve the two as one, and
     re-measure at 1140 rather than growing this alone. */
  gap: 7px;
  /* 10, having gone 10 -> 14 -> 10 on 2026-09-07, the last step at the owner's
     direction once the row moved above the map: *"make the gap between the
     little pills and the actual heat map graphic a little smaller"*. It pays
     for the pill's own 24 -> 28 in the same pass, so the graphic is still
     28 + 10 + 199 = 237 in a plate that is a fixed 378 and the #workflows band
     this row is measured against did not move — 0 px, measured.

     IT IS A `margin-bottom` AS OF 2026-09-07, the row having moved ABOVE the
     map that day at the owner's direction — see `marketArt()` in the fragment
     for the call and for why "on top of" was not the answer. The gap itself is
     the same number on the same side of the same seam, so the graphic is the
     same 237 and the row's air did not move: this costs 0 px of page height and
     0 px of the #workflows band. Reverting is this property and the order of
     two spans in `marketArt()`. */
  margin-bottom: 10px;
}

.bx-pill {
  display: flex;
  align-items: center;
  /* 24, up from the 20 this took on 2026-09-04 to match the #workflows chips
     and back off the app's own 32. Those chips are 20px pills on 11px type and
     they are a chip on a card; these are a CONTROL, the pair of filters the
     field below was computed with, and at 20 beside a 199px map they read as
     two small labels rather than as the thing that scoped the picture. 24 with
     12px type is the step between that chip and the app's own box, and the
     chevron's width is what pays for it.

     28 AS OF 2026-09-07, at the owner's direction — *"add a slight bit more
     padding to the filter pills"*. It is the HEIGHT that grew and not the
     inline padding, and the reason is the width budget rather than a reading of
     the ask: the pair already has under 2px of slack in the 1140-1175 band (see
     below), so +1px a side is +4px on the pair and overflows it, and buying
     that back out of the row's gap is the 4px gap this pass has already been
     asked to move away from. Height is free — the plate is fixed and the seam
     above pays for it — and it is what a 13px label sitting in 5.5px of air
     needs anyway: 7.5px now, which is a step TOWARDS the app's own `h-8` (32)
     rather than away from it. If the sides are wanted too, a label has to get
     shorter first; nothing else on this row has anything left to give. */
  height: 28px;
  /* UNDER THE APP'S OWN `px-2`, AND THE ROW'S GAP IS WHY. It went 8 -> 7 on
     2026-09-04 when 13px type spent the width the chevron's removal bought, and
     7 -> 6 on 2026-09-07 to pay for the gap going 4 -> 7. The binding width is
     the 1140-1175 band, where the graphic clamps to 270.83: at 8/5 the pair ran
     4px past the map's own right edge, at 7/4 it had 0.99px of slack, and at
     6/7 it has 1.99. The padding and the gap are ONE number against that band;
     move either and re-measure both. */
  /* 6, down from 7 on 2026-09-07: this is what pays for the row's wider gap.
     The two are one number against the 1140 band's 270.83 — see `.bx-filters`.
     The app's own is `px-2`; this has been under it since 13px type landed. */
  padding: 0 6px;
  /* `INACTIVE_STYLE`'s own `border-transparent`, which the active arm carries
     too — the border is here so the box does not change size between states. */
  border: 1px solid transparent;
  border-radius: 999px;
  /* THE BADGE'S OWN FILL AND INK, WHICH IS THE ROW READING AS ONE THING. See
     `--bx-badge` at the head of the file: the planholder chip one card left is
     the same badge, so the two share a token rather than two literals.

     THE `::before` GROUND CAME OFF WITH IT, and that is what makes it a match
     rather than an approximation: `--bx-badge` is a 15% wash, and 15% over a
     light `--bx-surface` is not the colour 15% over the plate is. The chip
     sits straight on the plate; so does this now. */
  background: var(--bx-badge);
  color: var(--bx-text);
  /* 13, which is the size card 2's own chip renders at (`.bx-rel`, the app's
     `text-sm` under `--bx-fit`) — a badge beside a badge. It was 11 until
     2026-09-04, read off the app's `text-xs` step, and two chips of one kind at
     two sizes is what that was reported as. See the block above for the 390
     budget this spends. */
  font-size: 0.8125rem; /* the row's body size, 14 * --bx-fit */
  font-weight: var(--font-weight-medium);
  line-height: 1;
  white-space: nowrap;
}

/* `FieldAvatar` at `size-3.5 mr-[5px]`, then the field name and PillValue. The
   `ChevronDown` that closed the pill is gone; see `.bx-pill` above. */
.bx-pill__mark {
  flex: none;
  width: 13px;
  height: 13px;
  /* The chip's own split, one card left: `.bx-rel__mark` is a step back from a
     full-ink name, so the glyph is a mark on the badge rather than a second
     word in it. Both badges say it the same way — but this one says it against
     the OLIVE plate rather than against the page, which is what `--bx-mark` on
     `.bx-market` is for; read its note before changing this line. */
  color: var(--bx-mark);
}

.bx-pill__t {
  margin-left: 5px;
}

/* =========================================================================
   Dark

   Only what does not follow from the tokens above. The surfaces, the text and
   the hairline all invert with `theme-dark.css`; the badges do not — the app
   refills every hue at a higher alpha there and collapses the label to white
   at 87% — and neither does the popover, whose two surfaces are the one pair
   in the app that does not simply follow the page.
   ========================================================================= */

/* The badge's dark half, on the TOKEN rather than on one card's chip, so the
   filter pill on card 3 follows it. It is `--default-badge`, i.e. the app's own
   `--hover` — a translucent white wash, NOT a surface token; see `--bx-badge`
   at the head of the file for the build it spent invisible on its own plate. */
:root[data-theme='dark'] .bx-price,
:root[data-theme='dark'] .bx-mockup,
:root[data-theme='dark'] .bx-market {
  --bx-badge: rgb(255 255 255 / 0.055);
}

/* `olive`'s dark half, the other of `media-plate.mjs`' pair, and card 3's
   alone — the two beside it are on the neutral token and read nothing here.
   `--bx-mark` reads it, so the pill's glyph is solved against the plate in
   both themes; see its note on `.bx-market`. */
:root[data-theme='dark'] .bx-market {
  --bx-plate: #5c5743;
}

:root[data-theme='dark'] .bx-price {
  /* oklch(0.26 0 0) = #242424, the app's dark `--popover`, and oklch(0.30),
     its `--hover-opaque`, for the panel inside it. */
  --bx-popover: var(--color-theme-card-02-hex);
  --bx-hover-opaque: var(--color-theme-card-04-hex);
  --bx-danger: #dd594e; /* oklch(0.6336 0.1674 27.91) */
}

/* Tailwind's `shadow-lg` carries no dark variant and the captured cards it is
   lifted from do not restate it, so neither does this: the 1px border above is
   what draws these surfaces' edges in dark. */
:root[data-theme='dark'] .bx-popover,
:root[data-theme='dark'] .bx-est,
:root[data-theme='dark'] .bx-heat {
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.3),
    0 4px 6px -4px rgb(0 0 0 / 0.3);
}
/*
 * /bid's "Cover more ground" section: the three graphics on its cards.
 *
 * NOT generated by `pnpm run port`. Hand-maintained, imported by the product
 * page alone (see `astroPage` in tools/04-generate.mjs) and by
 * `CardGallery.astro`, which harvests these cards and renders them on every
 * page — the same argument `bid-context.css` and `bid-surfaces.css` already
 * make. Pairs with tools/fragments/bid-capabilities.mjs, whose header carries
 * the records, the queries, the invented halves and the crop; what follows is
 * the drawing.
 *
 * IT DRAWS TWO OF THE THREE CARDS AND NOT THE THIRD
 * -------------------------------------------------
 * "Item quantities" is the CAPTURE's own list graphic with its content
 * rewritten, so it has no rules here and must not grow any: it reads the
 * capture's `type-product-*` classes and its own inline styles exactly as it
 * did with Cursor's task names in it. Everything below belongs to the two map
 * tools.
 *
 * `.bk-stage` is `bid-surfaces.css`' model: a 560 x 504 sheet under one
 * `transform: scale(var(--bk-zoom))`, so **every length under it is
 * bidlo-light-1.0's own number in the app's own pixels**. Do not scale one by
 * hand and do not import the capture's product type ladder into it; move
 * `--bk-zoom` and re-derive in the fragment.
 *
 * COLOUR
 * ------
 * The `--bk-*` set is `bid-surfaces.css`' `--bs-*` mapped the same way. The
 * secondary and tertiary steps are **flattened** with `color-mix` rather than
 * left as alpha, for the reason that file gives at length: a lucide glyph is
 * several separate `<path>`s, and two translucent strokes crossing paint a
 * step darker than the lines running into them.
 *
 * Four values are literal hexes and all four are the app's own, converted from
 * its oklch: `--bk-primary` (`#2483e2`), `--bk-destructive`, `--bk-orange`
 * (the soil pin's `--orange-primary`) and the unit-badge tones. A haul
 * contractor's colour is not here at all — it is a `MAP_COLOR_MAP` entry
 * emitted inline by the fragment, because it belongs to the contractor rather
 * than to the sheet.
 *
 * ONE COUPLING TO KNOW ABOUT
 * --------------------------
 * `src/scripts/empty-mockups.js` blanks a window by finding a `<div>` holding
 * exactly three small round children. Every box in this file is a `<span>`,
 * which that script's `div` query cannot reach. Keep it that way.
 */

.bk-frame,
.bk-stage {
  /* bid-surfaces.css' table, less the two only its own chrome needs. */
  --bk-surface: var(--color-theme-bg);
  --bk-popover: var(--color-theme-card-hex);
  --bk-hover: var(--color-theme-card-02-hex);
  --bk-line: var(--color-theme-border-02);
  --bk-text: var(--color-theme-product-text);
  --bk-text-sec: color-mix(in srgb, var(--color-theme-fg) 60%, var(--bk-surface));
  --bk-text-dim: color-mix(in srgb, var(--color-theme-fg) 40%, var(--bk-surface));
  --bk-primary: #2483e2;
  --bk-destructive: #e1584d;
  --bk-orange: #e48900;
  /* `bg-muted/40` and `border-border/50`, the accordion item's own pair. */
  --bk-sunk: color-mix(in srgb, var(--color-theme-fg) 3%, transparent);
  --bk-line-soft: color-mix(in srgb, var(--color-theme-fg) 8%, transparent);

  /* THE ONE NUMBER THAT TURNS APP PIXELS INTO CARD PIXELS. Solved by the
     340px tool card against the narrowest plate — see the fragment's header.

     **IT WENT TO `13 / 14` FOR A FEW HOURS ON 2026-09-08 AND CAME BACK.** That
     pass was answering a real report — these cards read smaller than the
     capture's own list beside them — with the wrong lever: the sheet under
     this scale is a MAP, so enlarging it enlarged the picture while the slice
     (`plate / zoom`) shrank, which cut the haul card's background routes, ran
     its `$/ton` badges off the narrow plates and bled both cards past the
     378px box. The type has its own lever now, two lines down; this one is the
     geometry's and stays where the crop puts it. */
  --bk-zoom: 0.75;

  /* THE CARDS' TYPE LADDER, AND THE ONE TYPE DEPARTURE IN THIS FILE.
     Every other length under `.bk-stage` is bidlo-light-1.0's own number, and
     these two are not: they are the row's own two steps — card 2 is the
     CAPTURE's list at `type-product-lg` (13) over `type-product-sm` (11) —
     stated in DRAWN pixels and divided back out by the scale, which is the
     idiom `SOIL_TOP_BAND` already uses for the band above the pin. So both
     tool cards render at exactly 13 and 11 whatever `--bk-zoom` becomes, and
     the map underneath does not move.

     They supersede the 2026-09-05 note further down about stepping the soil
     card one notch below the app's ladder: at one drawn ladder the two tools
     already agree, so there is no second departure to make. What paid for the
     bigger type is CONTENT — the soil card lost its Properties grid and its
     Soil profile, the haul card its `$/hr` field. See the fragment. */
  --bk-type-lg: calc(13px / var(--bk-zoom));
  --bk-type-sm: calc(11px / var(--bk-zoom));

  box-sizing: border-box;
}

:root[data-theme='dark'] .bk-stage {
  --bk-destructive: #dd594e;
  --bk-orange: #e28600;
}

.bk-stage * {
  box-sizing: border-box;
}

/* A `<span>` is inline; every box here is one, so the default has to be undone
   once. `:where()` contributes no specificity, which is what stops this
   outranking the single-class layout rules under it — the trap
   `automate-hero-mockup.css` paid a build for. */
.bk-stage :where(span) {
  display: block;
}

/* Every glyph in here is lucide's in DEVICE pixels: the stage is scaled by
   0.75, and a 1px rule at 0.75 is a smear. `vector-effect` does not inherit,
   so it is set on the elements. */
.bk-stage svg {
  display: block;
}

.bk-stage svg * {
  vector-effect: non-scaling-stroke;
}

/* The clipper. The captured 378px media box is `relative` on all three cards
   and `overflow-hidden` on only one of them, and a 420px stage inside a 325px
   box has to be cut by something — so it is cut here rather than by whichever
   captured wrapper happens to clip. That crop is the composition; see the
   fragment's header. */
.bk-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

/* The stage's own drawn box, stated so it can be placed rather than stretched:
   a scaled element's LAYOUT box is still its unscaled size, so `.bk-stage`
   measures 560 x 504 however small it draws. The box is `560 x 504` times the
   zoom — 420 x 378 at 0.75, so it covers the capture's fixed 378px plate
   exactly and is cut across but never down — and `margin-inline: auto` does
   the placing — auto margins absorb positive free
   space only, so this centres inside the wide single-column boxes at 1024 and
   768 and sits at the left edge, overflowing right, on the three-column ones.
   Stretching instead would walk every marker off its coordinate. */
.bk-fit {
  /* Stated outright: this is a <span> outside `.bk-stage`, so the
     `:where(span)` blockifier below does not reach it — and an inline box
     ignores `margin-inline: auto`. */
  display: block;
  position: relative;
  width: calc(560px * var(--bk-zoom));
  height: calc(504px * var(--bk-zoom));
  margin-inline: auto;
  overflow: hidden;
}

/* =========================================================================
   THE STAGE

   560 x 504 app px under one scale, drawn into the capture's own 378px media
   box. `transform-origin: top left` and the box's own overflow are what make
   the sides a crop rather than a squeeze.
   ========================================================================= */

.bk-stage {
  position: absolute;
  left: 0;
  top: 0;
  width: 560px;
  height: 504px;
  overflow: hidden;
  background: var(--bk-surface);
  color: var(--bk-text);
  font-family: var(--font-system);
  /* text-sm / leading-5, the app's body size. Unscaled, like everything else. */
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  cursor: default;
  transform: scale(var(--bk-zoom));
  transform-origin: top left;
}

.bk-map-img {
  position: absolute;
  max-width: none;
  object-fit: cover;
}

/* The haul card reuses the Vendors card's theme pair and picks by the site's
   own theme attribute, exactly as that card does. The soil card ships ONE
   image — `getMapStyle("soil", theme)` ignores the theme — so it carries
   neither attribute and neither rule reaches it. */
.bk-stage [data-theme-dark] {
  display: none;
}

:root[data-theme='dark'] .bk-stage [data-theme-light] {
  display: none;
}

:root[data-theme='dark'] .bk-stage [data-theme-dark] {
  display: block;
}

/* =========================================================================
   `MapToolCard`: the shell both map tools wear.

   `flex min-h-0 w-full max-w-[340px] flex-col border border-border bg-popover
   py-0 gap-0 overflow-hidden text-popover-foreground shadow-popover`, over a
   `min-h-10` header carrying the icon, the title and the controls. Width and
   position are emitted inline by the fragment; the card is TALLER than the
   stage leaves and is cut by it, which is the app's own `overflow-y-auto`
   content seen through a 378px window.
   ========================================================================= */

.bk-card {
  position: absolute;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--bk-line);
  border-radius: 10px; /* the app anchors --radius at 0.625rem */
  background: var(--bk-popover);
  /* `--popover-shadow`, both themes below. */
  box-shadow:
    rgb(0 0 0 / 0.1) 0 14px 28px -6px,
    rgb(0 0 0 / 0.06) 0 2px 4px -1px,
    rgb(84 72 49 / 0.08) 0 0 0 1px;
}

:root[data-theme='dark'] .bk-card {
  box-shadow:
    rgb(0 0 0 / 0.2) 0 14px 28px -6px,
    rgb(0 0 0 / 0.12) 0 2px 4px -1px,
    rgb(255 255 255 / 0.094) 0 0 0 1px;
}

/* **BOTH MAP GRAPHICS ARE CENTRED IN THE FRAME, NOT FLUSH LEFT.** `.bk-fit`'s
   `margin-inline: auto` centres the 420px drawing when the frame is wider than
   it is and pins it to the left edge — clipping only the right — when it is
   not. Neither of these two cards wants that: each is a pin or a set of them
   with a panel under it, and the panel is on the graphic's own midline, so a
   right-only crop put the whole subject to the right of the card it belongs
   to. Here the fit fills the frame and the STAGE is centred inside it instead,
   so the crop falls evenly on both sides and the pins, the card and the frame
   share one centre at every width. `left` is on the stage rather than the fit
   because a scaled element's layout box is its unscaled size — `.bk-stage`
   measures 560 and draws `560 * --bk-zoom`, so the offset is half of what it
   DRAWS and is derived rather than typed (it was a literal 210px while the
   zoom was 0.75, and a re-solved zoom left it behind).

   The soil card took this on 2026-09-05 and the haul card the same day, at the
   owner's direction; what it costs the haul card is that its markers now have
   to clear a crop on BOTH sides, which `CROP` in the fragment is and which its
   own throw checks. */
.bk-fit--soil {
  width: 100%;
}

.bk-stage--soil {
  left: calc(50% - (560px * var(--bk-zoom)) / 2);
}

.bk-fit--haul {
  width: 100%;
}

.bk-stage--haul {
  left: calc(50% - (560px * var(--bk-zoom)) / 2);
}

/* **THE MAP BLEEDS PAST THE STAGE AND THE FIT IS WHAT CUTS IT, AS OF
   2026-09-08.** Both renders are far wider than the 560 app px the stage is,
   centred on it, so at the single-column widths — where the plate is 1064 css
   px at 1139 against a 420 px drawing — the map reaches both edges instead of
   sitting as an island in ~250 px of bare plate down each side, which is what
   was reported. Nothing else in either stage comes near its own box (the
   widest is the haul card at 153..493 of 560), so the stage gives up a clip it
   was not using; `.bk-fit` is `overflow: hidden` at the plate's own width and
   is where the crop has always actually happened. The composition is
   untouched at every width — see `SOIL_MAP` and `HAUL_MAP`, which hold
   `x + w / 2` fixed so not one marker moves.

   **Scoped to the two stages that carry a map**, so a future stage keeps the
   clip by default. */
.bk-stage--soil,
.bk-stage--haul {
  overflow: visible;
}

/* With the switch gone the head is an icon, a name and (on the soil card) the
   survey-area badge, so both pads are the app's own `px-3`. The rule under it
   stays: it is the popover's own header boundary, not one of the internal
   dividers this pass took out. */
.bk-card__head {
  display: flex;
  min-height: 40px; /* min-h-10 */
  flex-shrink: 0;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  border-bottom: 1px solid var(--bk-line);
  padding: 8px 12px; /* py-2 px-3 */
}

.bk-card__title {
  display: flex;
  min-width: 0;
  flex: 1;
  align-items: center;
  gap: 8px; /* gap-2 */
}

/* `<Truck className="size-4.5 shrink-0 text-primary" />` — and MapPin the same
   on the soil card. */
.bk-card__mark {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--bk-primary);
}

/* `CardTitle className="truncate text-sm font-semibold m-0 leading-none"`. */
.bk-card__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--bk-type-lg);
  font-weight: 600;
  line-height: 1;
}

/* `titleSuffix`: `<Badge variant="outline" className="text-[10px] px-1.5 py-0
   h-5">` carrying the survey area's symbol. */
.bk-badge-outline {
  flex-shrink: 0;
  height: 20px;
  padding: 0 6px;
  border: 1px solid var(--bk-line);
  border-radius: 999px;
  font-size: var(--bk-type-sm);
  font-weight: 500;
  line-height: 18px; /* the pill is the app's 20px; the label is the row's */
  color: var(--bk-text-sec);
}

/* **THE `controls` SWITCH IS GONE, AS OF 2026-09-08.** `.bk-switch` and its
   knob drew the app's own `<Switch checked>` in the head of both cards — the
   control that turns the tool off — and left at the owner's direction with the
   rest of this pass's chrome. See `toolCard()` in
   `tools/fragments/bid-capabilities.mjs` for the reasoning. */

.bk-card__body {
  min-height: 0;
  flex: 1;
}

/* Soil: the head, the three figures and the restriction as one flex column in
   the card's own padding. It was `contentClassName="p-0"` with the accordion
   carrying its own `p-2` inside a bordered item — see `soilArt()` for why
   that frame came out. */
.bk-card__body--soil {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 12px;
}

/* Haul: `contentClassName="p-3 space-y-4"`. */
.bk-card__body--haul {
  padding: 12px;
}

/* =========================================================================
   THE SOIL CARD'S BODY

   `Accordion type="multiple" className="w-full space-y-2.5 p-2"` holding one
   item per component, the first open.

   **ITS TYPE IS THE ROW'S DRAWN LADDER, AS OF 2026-09-08.** The app sets this
   card's component title at `text-[15px]`, its three metric figures at
   `text-lg` (18) and its properties at `text-[13px]`, against the haul card's
   `text-sm` (14) / `text-xs` (12) — so drawn side by side at one scale the two
   tools read at two sizes. A 2026-09-05 pass fixed that by stepping this card
   down onto the haul card's own ladder, which made the two agree with each
   other and left both a third smaller than the CAPTURE's list on the same row.
   Both now read `--bk-type-lg` / `--bk-type-sm` instead — 13 and 11 drawn,
   divided back out by the scale, which is card 2's own two steps. Every
   padding, gap, radius and border is still the app's, and so is every length
   on the map under it; see the token block at the top of this file.
   ========================================================================= */

/* **THE ACCORDION'S FRAME, ITS DIVIDER AND THE THREE TILE BOXES ARE ALL GONE,
   AS OF 2026-09-08.** `.bk-acc`, `.bk-acc__item` and `.bk-acc__body` drew a
   `rounded-xl border bg-background` card inside the popover with a rule across
   its own head, and `.bk-metric` drew three more bordered boxes inside that —
   three frames deep to say one soil name and three numbers. They came out at
   the owner's direction; what is left is the head, the figures and the
   restriction as plain blocks in `.bk-card__body--soil`'s own padding. The
   head's two names keep the app's arrangement under `bk-soil__*` names. */

.bk-soil__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}

.bk-soil__names {
  display: flex;
  min-width: 0;
  flex-direction: column;
}

.bk-soil__title {
  font-size: var(--bk-type-lg);
  font-weight: 700;
  line-height: 1.25;
}

.bk-soil__sub {
  margin-top: 2px;
  font-size: var(--bk-type-sm);
  font-weight: 600;
  color: var(--bk-text-sec);
}

/* **AND THE RED WARNING CHIP BESIDE IT WENT WITH THEM.** `.bk-warn-chip` was a
   `size-6` disc on `bg-destructive/15` holding a `strokeWidth={3}` triangle,
   and the RESTRICTION callout three rows below it is the same mark saying the
   same thing with the fact attached — one warning per card. Restoring it means
   restoring its markup in `soilArt()` too. */

/* `<Badge variant="secondary">` — the component percentage. **Its plate came
   off in the same pass**: on a flattened card a filled pill was the only
   remaining box in the head, and the figure reads on its own beside a name. */
.bk-pct {
  flex-shrink: 0;
  font-size: var(--bk-type-lg);
  font-weight: 700;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
}

/* The three figures: `grid grid-cols-3 gap-2`, each an
   `border-border/50 bg-background/40 rounded-lg p-2.5` box holding a two-line
   label over one number — the app's own tile.

   **THEY KEEP THEIR BOXES, WHERE EVERY OTHER FRAME ON THIS CARD LOST ONE.**
   They were flattened with the rest on 2026-09-08 and put back the same day at
   the owner's direction: these three are the card's PRIMARY figures — the
   numbers that move a bid sheet — and the tile is what makes them read as a
   row of measurements rather than as three more lines of the paragraph above
   them. The frames this pass removed were all CONTAINERS (an accordion item
   round the whole body, a pill round the percentage, an outline round a tint);
   this is the one that carries meaning, so it is the one that stays.

   **AND IT IS A FILL RATHER THAN AN OUTLINE, AS OF 2026-09-08**, at the
   owner's direction. The app draws `border-border/50` over `bg-background/40`,
   i.e. an outline round a wash — and on a card this pass has just taken every
   other outline off, three outlined boxes were the only drawn edges left in
   the body and read as the frames rather than as the figures. A solid block
   says "a tile" with nothing to draw. `--bk-hover` is the tone: it is the step
   this same file already fills a chip with (`.bk-hauler__badge` on the haul
   card), so the two cards agree on what a filled block looks like, and it is a
   theme token, so there is no dark restatement to keep in step. */
.bk-metrics {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.bk-metric {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px;
  border-radius: 8px;
  background: var(--bk-hover);
}

.bk-metric__label {
  font-size: var(--bk-type-sm);
  font-weight: 500;
  line-height: 1.25;
  color: var(--bk-text-sec);
}

.bk-metric__value {
  font-size: var(--bk-type-lg);
  font-weight: 700;
  line-height: 1;
}

/* The restriction callout. It renders only when the component carries a
   `reskind` or a `resdept_r`, and this one carries both. */
/* **IT DRAWS NO BORDER, AS OF 2026-09-08.** It carried a 1px rule in the
   destructive tone over a 10 % wash of the same colour — an outline round a
   tint that already reads as a callout, i.e. the one box on a card this pass
   spent taking boxes off. The tint alone is unmistakably not a card, and the
   pad it used to spend on its own border goes into the block instead. */
.bk-restrict {
  padding: 10px 12px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--bk-destructive) 10%, transparent);
}

.bk-restrict__head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  color: var(--bk-destructive);
  font-size: var(--bk-type-sm);
  font-weight: 700;
}

.bk-restrict__mark {
  width: 14px;
  height: 14px;
}

.bk-restrict__body {
  font-size: var(--bk-type-lg);
  font-weight: 500;
  color: color-mix(in srgb, var(--bk-destructive) 90%, var(--bk-surface));
}

/* **THE PROPERTIES GRID AND THE SOIL PROFILE ARE GONE, AS OF 2026-09-08.**
   `.bk-props`, `.bk-props__head`, `.bk-props__grid`, `.bk-prop`,
   `.bk-prop__label`, `.bk-prop__value`, `.bk-hz`, `.bk-hz__name`,
   `.bk-hz__depth` and `.bk-hz__tex` all lived here and all left with the
   markup that read them: eight `label: value` pairs and three horizon rows of
   the smallest type on the row, in a card the plate was already cutting, and
   the block that had to give when the card's type went to the row's own 13/11.
   The values are all still queried and still in the stats — see `SOIL_CUT` in
   `tools/fragments/bid-capabilities.mjs`, which carries the reasoning and what
   putting them back would cost. */

/* `SoilSurveyClickMarker`: react-map-gl's default teardrop, `anchor="bottom"`,
   tinted `var(--orange-primary)`. The disc inside it is the marker's own
   `#000` at 25 %, which is what gives a flat pin its hole. */
.bk-soil-pin {
  position: absolute;
  width: 27px;
  height: 41px;
  color: var(--bk-orange);
  transform: translate(-50%, -100%);
  filter: drop-shadow(0 2px 3px rgb(0 0 0 / 0.35));
}

.bk-soil-pin svg {
  width: 27px;
  height: 41px;
}

/* =========================================================================
   THE HAUL CARD'S BODY

   `MenuLabel` over one group per contractor: the contractor row, then its
   source row. See the fragment's header for why no advantage delta is drawn.
   ========================================================================= */

/* `<MenuLabel className="px-0 pt-0 text-muted-foreground" />`. */
.bk-label {
  margin-bottom: 8px;
  font-size: var(--bk-type-sm);
  font-weight: 500;
  color: var(--bk-text-sec);
}

.bk-hauler + .bk-hauler {
  margin-top: 10px;
}

/* `<div className="rounded-md p-2 hover:bg-hover">` around the row.

   **ITS INLINE PADDING IS 0 AS OF 2026-09-08, AND SO IS THE SOURCE ROW'S.**
   That padding is the hover fill's breathing room — `hover:bg-hover` is the
   only thing it is there for — and a still is never hovered, so all it did
   here was take 16 app px off the one line on this card that has to hold a
   company's whole name. With the card's type at the row's own 13 (see
   `--bk-type-lg`) `Calder Rock & Materials` measured 189 against 168, and
   this is 16 of the 24 that buys it. The vertical half stays: it is what
   separates the two haulers. */
.bk-hauler__row {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
}

/* `HaulDocumentMenuRow ... avatarClassName="size-7"` — passed as a prop, so
   `size-6` is as much the app's as `size-7` is, and 24 is what a 13px name
   wants beside it rather than 28. 4 more of the 24 the name needed. */
.bk-hauler__avatar {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  overflow: hidden;
  border-radius: 6px;
}

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

.bk-hauler__name {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--bk-type-lg);
  font-weight: 500;
}

/* The result pill: `inline-flex rounded-full px-2 py-1 text-xs font-semibold`
   with `tabular-nums`. `bg-accent text-foreground` by default and
   `bg-primary/10 text-primary` for the team's own contractor, which is the
   app's own rule. No red/green delta — see the fragment's header. */
.bk-hauler__badge {
  flex-shrink: 0;
  /* `px-1.5` rather than the app's `px-2` — the last 4 of the 24 app px
     `Calder Rock & Materials` needed at 13px. See `.bk-hauler__row`. */
  padding: 4px 6px;
  border-radius: 999px;
  background: var(--bk-hover);
  font-size: var(--bk-type-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.bk-hauler__badge[data-default] {
  background: color-mix(in srgb, var(--bk-primary) 10%, transparent);
  color: var(--bk-primary);
}

/* `SelectedHaulSourceDocument`, drawn as a SUB-ITEM OF ITS CONTRACTOR AS OF
   2026-09-09, at the owner's direction. It was a two-column grid whose first
   cell was a bordered callout tile holding the drive time over the distance,
   with the plant's name beside it — so the two figures were the largest ink in
   the panel and the plant they belong to read as a caption on them. The plant
   is the thing the row is about, so it is a title now with the drive as a
   secondary subtitle under it, which is also the shape the contractor row
   above already takes: a name, then what is known about it.

   **THE INDENT IS SOLVED, NOT PICKED**, and it is `bid-extend.css`' own
   `.be-subs` one page over: the rule hangs at half the parent's glyph box and
   the padding carries the label back to the parent label's own left edge. The
   avatar is 24 on an 8px gap, so the name starts at 32 — hence 12 + the rule's
   own 1 + 19, which lands the plant's name on its contractor's to the pixel
   (measured on the built page). **The border is in that sum**: `.be-subs` over
   in `bid-extend.css` leaves it out and sits 1px wide of its parent for it.
   Move `.bk-hauler__avatar` or its gap and re-solve all three terms rather
   than nudging one.

   **AND THE RULE IS WHERE THE CONTRACTOR'S COLOUR SURVIVES.** The tile's
   border was what tied a row to its own line on the map behind it; the avatar
   cannot do that job, being the firm's mark rather than the route's colour. So
   the hanging rule takes it, at the same 40 % mix the border ended on and for
   the same reason — a saturated rule beside a `$/ton` badge is louder than the
   comparison the card exists to make. Emitted per contractor in `haulArt()`. */
.bk-source {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
  margin: 2px 0 2px 12px;
  padding: 2px 0 2px 19px;
  border-left: 1px solid;
}

.bk-source__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--bk-type-lg);
}

/* The subtitle: the route's own two figures on one line, at the panel's small
   step in secondary ink — `text-muted-foreground`, which is what the app sets
   a row's supporting line in and what `.bk-label` above already reads. */
.bk-source__meta {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-size: var(--bk-type-sm);
  color: var(--bk-text-sec);
  font-variant-numeric: tabular-nums;
}

.bk-source__sep {
  opacity: 0.6;
}

/* **THE `$105 $/hr` FIELD IS GONE, AS OF 2026-09-08.** `.bk-source__rate` and
   `.bk-source__unit` drew `HaulUnitInput` — an editable field a still has
   nothing to type into, and the one thing on this card a reader cannot act on
   — and they left with it when the card's type went to the row's own 13/11.
   `TRUCK_HOUR_RATE` is unchanged and still prices every `$/ton` badge above;
   it is simply not drawn. See `HAUL_CUT` in the fragment. */

/* =========================================================================
   THE HAUL MAP'S OWN LAYERS
   ========================================================================= */

/* `HaulRoutesOverlay` stacks two lines on one geometry: a 6px base at 22 % and
   the animated 4px dash at 90 %. The dash is drawn at rest — a still frame
   cannot march — and the geometry is this file's own two-legged path rather
   than a Directions route, which the fragment's header records as invented. */
.bk-routes {
  position: absolute;
  left: 0;
  top: 0;
}

.bk-markers {
  position: absolute;
  inset: 0;
  display: block;
}

/* `MapMarker`'s teardrop at the same 28 / 25 / 23 the Vendors card draws — the
   app's own 36 / 32 / 30 at 0.78, restated here so the two maps on one page
   draw one marker. `anchor="bottom"`: the box's bottom-centre is the point. */
.bk-pin {
  position: absolute;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: translate(-50%, -100%);
}

.bk-pin-shell {
  width: 25px;
  height: 25px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12.5px 12.5px 4px 12.5px;
  background: #fff;
  transform: rotate(45deg);
  box-shadow:
    0 4px 6px -1px rgb(0 0 0 / 0.1),
    0 2px 4px -2px rgb(0 0 0 / 0.1);
}

.bk-pin-turn {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: rotate(-45deg);
}

.bk-pin-disc {
  width: 23px;
  height: 23px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 2px solid #fff;
  border-radius: 999px;
  background: #fff;
}

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

/* The same component's other half: `map/index.tsx` draws a PINNED document as
   a `size-8 rounded-md border-[3px] border-white bg-white shadow-md
   overflow-hidden` plate holding its avatar `size-full rounded-none`, and
   anchors it on its own centre — `anchor={point.isPinned ? "center" :
   "bottom"}` — where an unpinned point gets the teardrop above. The job is the
   pinned document here: it is what the tool is run FROM, and a haul route is
   built per contractor TO it.

   Drawn at the same 0.78 the teardrop takes, so the two markers on this map
   read as one component: 32 -> 25, the 3px ring -> 2, `rounded-md` -> 6 (that
   step is **8** on the app's own `--radius: 0.625rem` ladder, not Tailwind's
   unanchored 6). The avatar inside is `.bk-pin-img`, shared rather than
   restated. */
.bk-plate {
  position: absolute;
  width: 25px;
  height: 25px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 2px solid #fff;
  border-radius: 6px;
  background: #fff;
  transform: translate(-50%, -50%);
  box-shadow:
    0 4px 6px -1px rgb(0 0 0 / 0.1),
    0 2px 4px -2px rgb(0 0 0 / 0.1);
}
/*
 * /bid's "Works where you work" section: the three graphics on its cards.
 *
 * NOT generated by `pnpm run port`. Hand-maintained, imported by the product
 * page alone (see `astroPage` in tools/04-generate.mjs) and by
 * `CardGallery.astro`, which harvests these cards and renders them on every
 * page — the same argument `bid-context.css` and `bid-surfaces.css` already
 * make. Pairs with tools/fragments/bid-extend.mjs, whose header carries the
 * records, the invented halves and the reasoning; what follows is the drawing.
 *
 * ONE SCALE, AND IT IS 1:1
 * ------------------------
 * All three drawings sit on a card rather than inside a window, so they are
 * drawn **1:1 in card pixels** — exactly what the Cursor blocks they replace
 * did. There is no `transform: scale()` in this file and there should not be
 * one: `bid-context.css` sets out when the other model is right (a window whose
 * whole contents are the app's own numbers), and none of these is a window.
 *
 * Type reads the capture's own `--text-product-*` ladder (11 / 12 / 13), which
 * is the same call.
 *
 * COLOUR
 * ------
 * The `--be-*` set is `bid-context.css`' `--bx-*`, restated rather than shared
 * because these are separate sheets on separate cards. The secondary and
 * tertiary steps are **flattened** with `color-mix` rather than left as alpha,
 * for the reason that file gives: a lucide glyph is several separate
 * `<path>`s, and two translucent strokes crossing paint a step darker than the
 * lines running into them.
 *
 * The one exception is the plan sheet, which is **white in both themes** with
 * a grey ink ladder of its own. That is not a miss: PDFium renders the file, so
 * a plan sheet in the app is the colour the plan sheet is.
 */

.be-plan,
.be-ask {
  /* bid-context.css' table. */
  --be-surface: var(--color-theme-bg);
  --be-chrome: var(--color-theme-card-hex);
  --be-line: var(--color-theme-border-02);
  --be-text: var(--color-theme-product-text);
  --be-text-sec: color-mix(in srgb, var(--color-theme-fg) 60%, var(--color-theme-bg));
  --be-text-dim: color-mix(in srgb, var(--color-theme-fg) 40%, var(--color-theme-bg));
  --be-hover: var(--color-theme-card-02-hex);
  --be-accent: #2483e2;

  /* The markups panel's own ground, and it is deliberately NOT `--be-surface`.
     Card 3 one cell right draws the captured Document-analysis card, whose
     plates are `bg-theme-product-editor`; this panel is the same kind of
     object — a floating app surface on a tinted plate — and at
     `--color-theme-bg` it sat a step apart from them, which is what the row
     was reported for. One token, read by the panel and by the ground its
     group heads mix against, so the two cannot drift. */
  --be-panel-bg: var(--color-theme-product-editor);

  box-sizing: border-box;
  color: var(--be-text);
}

.be-plan *,
.be-ask * {
  box-sizing: border-box;
}

/* A `<span>` is inline; every box here is one, so the default has to be undone
   once. `:where()` contributes no specificity, which is what stops this
   outranking the single-class layout rules under it — the trap
   `automate-hero-mockup.css` paid a build for. */
.be-plan :where(span) {
  display: block;
}

/* lucide at stroke-width 1.5, the app's own weight for a 16px glyph. */
.be-plan svg,
.be-ask svg {
  display: block;
  flex: 0 0 auto;
  stroke-width: 1.5;
}

/* The capture's own 378px box, restated because the fragment rebuilds the
   figure whole. `overflow: hidden` is what lets all three run off the bottom,
   which is what the two captured menus already did. */
.be-box {
  position: relative;
  width: 100%;
  height: 378px;
  overflow: hidden;
}

/* =========================================================================
   2 — PDFs & plan sets: a takeoff, and the panel that lists it

   Two objects on one stage: the pdf viewer's page, and `PdfMarkupsPanel` lying
   over its left. Every value in the panel below is the app's own component's,
   with its class beside it; the sheet is this file's invention and says so.

   Read tools/fragments/bid-extend.mjs' card-2 block before moving a number —
   the composition, the three departures and the arithmetic are all set out
   there.
   ========================================================================= */

.be-plan {
  position: absolute;
  inset: 0;
}

/*
 * THE STAGE IS 320 WIDE, WHICH IS THE SECTION'S OWN MEASURE. Card 3 and every
 * card in #workflows draw their contents in a `max-w-[320px] px-4` column, so
 * measured on the built page at 1440 they stand 320 wide on **35.8px of air a
 * side**; this stage is that same 320 and lands on the same two numbers. It was
 * 384 and flush to both edges for one build, which is what it was reported for.
 *
 * ITS AUTO MARGINS ARE LOAD-BEARING, and they are `bid-capabilities.css`'
 * `.bk-fit`: auto inline margins absorb positive free space only, so the stage
 * CENTRES inside the wide plates (391.67 at 1440, 371.67 at 1280, 949 at a 1024
 * viewport, 698 at 768) and sits flush and slightly cropped on the narrow ones
 * (325 at 1140, 319.69 at 390) — which is exactly what those neighbours do
 * there too. Stretching it instead would walk every markup off its coordinate.
 *
 * `display: block` is stated rather than inherited from the `:where(span)`
 * blockifier at the top of this file: an inline box ignores an auto margin, and
 * that is a silent failure rather than a visible one.
 */
.be-fit {
  position: relative;
  display: block;
  width: 320px;
  height: 378px;
  margin-inline: auto;
}

/*
 * VERTICAL AIR IS 44 TOP AND BOTTOM, AND IT IS ONE NUMBER DOING THREE JOBS:
 * the air above the page, the air below the panel, and how far the panel hangs
 * below the page. It is card 1's own bottom air, measured on the same row.
 *
 * It is deliberately NOT the 71.6 those neighbours take above and below their
 * contents, and the arithmetic says why: the panel is a 300 x 196 object in a
 * 378px box, so 71.6 a side would leave the plan a 27px band. 44 leaves it 82,
 * which is about a third of the block — the proportion the reference front this
 * card is drawn against gives the plan above its own list. The SIDES match exactly;
 * the vertical is as close as a taller object can get.
 *
 * The page is inset 20 on the left, so the panel — flush to the stage — hangs
 * 20px past its left edge onto the plate, which is what makes the pair read as
 * two layers rather than as one pane split down the middle.
 */
.be-paper {
  position: absolute;
  top: 44px;
  left: 20px;
  width: 300px;
  height: 246px;
  overflow: hidden;
  background: #fff;
  border: 1px solid rgb(0 0 0 / 10%);
  border-radius: 6px;
  box-shadow:
    0 1px 2px rgb(0 0 0 / 10%),
    0 10px 26px -10px rgb(0 0 0 / 24%);
}

.be-sheet {
  display: block;
  width: 100%;
  height: 100%;
}

.be-ink {
  fill: none;
  stroke: #9a978f;
  stroke-width: 1;
}

.be-thin {
  stroke: #c4c1b9;
  stroke-width: 0.75;
}

/* A centreline. */
.be-dash {
  stroke-dasharray: 8 6;
}

/* A property line, which every plan draws as dash-dot. */
.be-dashdot {
  stroke-dasharray: 12 4 2 4;
}

/* Sheet type, drawn as bars rather than as unreadable strings. */
.be-bar {
  fill: none;
  stroke: #cbc8c0;
  stroke-width: 3;
  stroke-linecap: round;
}

/* A callout's own type, one step finer than the frontage's. */
.be-bar--sm {
  stroke-width: 2;
}

/* A station callout. Filled, so it reads as lying on the linework the way a
   plan's own callouts do. */
.be-callout {
  fill: #fff;
  stroke: #b6b3ab;
  stroke-width: 0.75;
}

/* `PdfMarkupShape`'s `length` arm: the polyline in the markup's own colour at
   `DEFAULT_MARKUP_STYLE.strokeWidth`, round joins and caps, no fill. The colour
   is emitted per shape, so nothing in this sheet names one. */
.be-run {
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Its `count` arm: an `r=7` pin on a 1.5 white stroke, numbered 1..n at
   `fontSize 8 fontWeight 700` on a `+2.6` baseline. */
.be-pin {
  stroke: #fff;
  stroke-width: 1.5;
}

.be-pin__n {
  font-family: inherit;
  font-size: 8px;
  font-weight: 700;
  fill: #fff;
}

/* -------------------------------------------------------------------------
   `PdfMarkupsPanel`.

   THREE DEPARTURES, ALL IN THIS BLOCK AND ALL FORCED BY THE CARD. In the app
   the panel is a docked right-hand column — `border-l`, no radius, no
   elevation, the sheet ending where it begins. Here it lies OVER the sheet, so
   it takes a full border, an 8px radius and the shadow a floating surface
   needs.

   AND IT IS 276 WIDE, NOT THE APP'S OWN `w-[300px]`. That is the fourth
   departure and the newest: at 300 the panel's right edge landed 20px short of
   the page's, so the plan it lies on showed a 20px rind down one side and read
   as a pane that had been covered rather than as a document with a list laid
   over it. 276 leaves **44**, which is the block's own band — the same number
   `.be-fit` spends above the page, below the panel and on the panel's drop —
   rather than a width picked by eye, so the plan's right margin and the card's
   air are one value. It is nowhere near a floor: the widest thing in the panel
   is the first group's head, `px-3` + its 111.98px subject + `gap-2` + its
   43.41px total + two borders = **189.4**, measured on the built page, so there
   are 87px of slack. Nothing wraps at 276 — every name is `truncate` and every
   row is one line — so the 208px height solve below is untouched.

   ITS HEIGHT IS FIXED AND THE LIST IS CUT BY IT, which is the app rather than a
   crop: that list is `flex-1 overflow-y-auto`, so a panel shorter than its own
   contents clips its last row exactly like this. 208 is SOLVED rather than
   picked — the panel's bottom edge lands on the box's own 44px air, the plan
   keeps an 82px band above it, and the cut falls 24px into the second group's
   first row, which is its 6px of padding plus the whole 16px `Count` line. A
   pixel either side of that band cuts a word in half; 196 did, and it read as a
   rendering fault rather than as a list with more under it.
   ------------------------------------------------------------------------- */

.be-panel {
  position: absolute;
  top: 126px;
  left: 0;
  width: 276px;
  height: 208px;
  overflow: hidden;
  line-height: 1.5;
  color: var(--be-text);
  background: var(--be-panel-bg);
  border: 1px solid var(--be-line);
  border-radius: 8px;
  box-shadow:
    0 1px 2px rgb(0 0 0 / 8%),
    0 12px 28px -12px rgb(0 0 0 / 28%);
}

/* `flex h-9 shrink-0 items-center gap-1 border-b px-2`. */
.be-panel__head {
  display: flex;
  gap: 4px;
  align-items: center;
  height: 36px;
  padding: 0 8px;
  border-bottom: 1px solid var(--be-line);
}

/* `text-xs font-medium`. */
.be-panel__title {
  font-size: 12px;
  font-weight: 500;
  line-height: 16px;
}

/* The `flex-1` that pushes the two buttons right, and the same one inside a
   group head that pushes its total right. */
.be-spacer {
  flex: 1;
}

/* Two `size-6` ghost buttons carrying `size-3.5` glyphs. */
.be-panel__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  color: var(--be-text);
  border-radius: 6px;
}

.be-panel__btn svg {
  width: 14px;
  height: 14px;
}

.be-panel__list {
  display: block;
}

/* Every group carries `border-b`. */
.be-group {
  display: block;
  border-bottom: 1px solid var(--be-line);
}

/* `px-3 py-1.5 gap-2`. The app's own `bg-muted/50` is DELIBERATELY NOT drawn:
   at this scale, on a panel that already floats on a tinted plate, a filled
   band per subject read as three stacked surfaces rather than as one list, and
   the card's own ground separates the sections well enough on its own. Put it
   back with `background: color-mix(in srgb, var(--be-hover) 50%,
   var(--be-panel-bg))`, which is what that alpha composites to here. */
.be-group__head {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 6px 12px;
}

/* `truncate text-xs font-semibold`. */
.be-group__name {
  overflow: hidden;
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `whitespace-nowrap text-[11px] tabular-nums text-muted-foreground`. */
.be-group__total {
  font-size: 11px;
  color: var(--be-text-sec);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* `flex w-full items-center gap-2 px-3 py-1.5 text-left`. Its find-similar and
   delete buttons are `opacity-0 group-hover:opacity-100`, so a panel nobody is
   pointing at draws neither — which is also what buys the row its width. */
.be-row {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 6px 12px;
}

/* `size-2.5 shrink-0 rounded-full border border-black/15`, filled with
   `markup.style.color` — emitted per row, so this sheet names no colour. */
.be-dot {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border: 1px solid rgb(0 0 0 / 15%);
  border-radius: 999px;
}

.be-row__main {
  flex: 1;
  min-width: 0;
}

/* `truncate text-xs`. */
.be-row__type {
  overflow: hidden;
  font-size: 12px;
  line-height: 16px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `ml-1 rounded bg-primary/10 px-1 text-[10px] text-primary`, drawn on a row
   whose `source` is `agent` and on no other. `display: inline-block` has to win
   over the `:where(span)` blockifier at the top of this file — the two tie at
   (0,1,0), so this rule keeps working only because it is BELOW it. */
.be-ai {
  display: inline-block;
  padding: 0 4px;
  margin-left: 4px;
  font-size: 10px;
  color: var(--be-accent);
  background: color-mix(in srgb, var(--be-accent) 10%, transparent);
  border-radius: 4px;
}

/* `text-[11px] text-muted-foreground`. */
.be-row__sub {
  font-size: 11px;
  color: var(--be-text-sec);
}

/* =========================================================================
   3 — Deep research: the captured card, running

   THERE IS ALMOST NOTHING HERE, AND THAT IS THE POINT. This card is drawn in
   the markup of the section's own captured "Document analysis" graphic, class
   for class, so `product.css` already carries its column width, its plates,
   its radii, its padding and the gap between them — and the two cards cannot
   drift apart. What is left below is the box that centres it, the progress
   bar (the capture has no such control) and the three glyph sizes.

   Re-measure against the captured card rather than nudging a value here.
   ========================================================================= */

/* The captured graphic's own box: `flex w-full items-center justify-center`
   inside the 378px frame, which `.be-box` already is. */
.be-ask {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* `deep-research-card.tsx`'s bar: `h-1 w-full rounded-full bg-muted` under a
   `bg-primary` fill, painted in the captured card's own ochre rather than the
   app's blue, this graphic carrying one accent. */
.be-prog {
  width: 100%;
  height: 4px;
  overflow: hidden;
  background: var(--be-hover);
  border-radius: 999px;
}

.be-prog__fill {
  display: block;
  height: 100%;
  background: rgb(192 133 50);
  border-radius: 999px;
}

/* The three step marks. `size-3.5` in the app; the done and running ones take
   the accent, the one not started the dim ink the row already carries. */
.be-step {
  width: 14px;
  height: 14px;
}

.be-step--done,
.be-step--active {
  color: rgb(192 133 50);
}

.be-step--pending {
  color: var(--be-text-dim);
}

/* A still of a running job reads as a still of a stopped one, so the spinner
   turns. One transform, no layout — and in EIGHT STEPS rather than smoothly,
   the glyph having eight spokes on a per-spoke opacity ladder: a 45° step
   hands every spoke the next value down, which is the trail. A linear turn
   would drag the ladder round with the spokes and read as a rotating
   asterisk. */
.be-step--active {
  transform-origin: 50% 50%;
  animation: be-spin 0.8s steps(8, end) infinite;
}

@keyframes be-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .be-step--active {
    animation: none;
  }
}

/* A step's own subagents, indented under it.
   -----------------------------------------
   The rule hangs on the glyph's centre line — a `h-4.5` box is 18px, so 9 —
   and the padding carries the label back to its parent's left edge, 18 + the
   row's `gap-2` = 26. So a sub-row starts exactly under the step it belongs
   to and the rule reads as the tie between them. Both numbers are the
   captured row's own; re-solve them against it rather than nudging.

   `pl-*` at this step is not in `product.css` — the captured card never
   indents anything — which is the whole reason this is a rule here. */
.be-subs {
  margin: 2px 0 2px 9px;
  padding-left: 17px;
  border-left: 1px solid var(--be-line);
}

.be-subs > div {
  padding: 2px 0;
}

/* One subagent's avatar. `AgentAvatar`'s smallest is `size-6` with `text-sm`;
   this is `hero-mockup.css`' own `.hm-agent` — 14px on 10px medium — which is
   what that mark comes to beside 11px type. A circle, `Avatar` being
   `rounded-full`.

   The two tints are the app's `--<c>-badge` pair, converted from oklch by the
   same arithmetic `items-mockup.css` and `market-mockup.css` used and copied
   rather than shared, per the fork rule. Adding a hue means converting it, not
   guessing. Dark restates the fill and lifts the ink to white at 87%. */
.be-agent {
  width: 14px;
  height: 14px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  font-size: 10px;
  line-height: 1;
  font-weight: var(--font-weight-medium);
  background: var(--be-agent-bg);
  color: var(--be-agent-fg);
}

.be-agent[data-tone='blue'] {
  --be-agent-bg: rgb(65 139 198 / 0.27);
  --be-agent-fg: #001f43;
}

.be-agent[data-tone='green'] {
  --be-agent-bg: rgb(80 158 114 / 0.27);
  --be-agent-fg: #002f23;
}

:root[data-theme='dark'] .be-agent {
  --be-agent-fg: rgb(255 255 255 / 0.87);
}

:root[data-theme='dark'] .be-agent[data-tone='blue'] {
  --be-agent-bg: rgb(0 103 165 / 0.5);
}

:root[data-theme='dark'] .be-agent[data-tone='green'] {
  --be-agent-bg: rgb(0 128 85 / 0.5);
}

/* An agent that is WORKING: one pulse, then a rest.
   -------------------------------------------------
   The mark is the captured pages' own `.shimmer` — a 90deg gradient
   background-clipped to the text, the highlight walked across it — restated
   here because `product.css` emits no such rule (this page's capture has
   nothing that streams). What is NOT theirs is the rhythm, at the owner's
   direction: theirs walks the position linearly for the whole cycle, which
   with a two-width tile crosses the word twice at a constant speed and reads
   as a stiff loop. This FIRES and then WAITS.

   THREE NUMBERS MAKE IT ONE PASS AND NOT TWO, and they are solved together.
   A background tiles, so the highlight comes round once per tile: at the
   capture's `200%` the tiles are two element widths apart and a full walk
   shows two of them. At `300%` they are three widths apart, which is wider
   than the box, so **only one highlight can ever be on the word**. Its offset
   is `p% x (W - 3W)`, i.e. `-2pW/100`, so the band's own centre sits at
   `1.5W - 2pW/100` — off the left edge at **p = 85%** and off the right at
   **p = 15%**. Those are the two ends of the pass, and the second-nearest tile
   is 3W away throughout, so it never appears.

   THE PASS IS A QUARTER OF THE CYCLE AND THE REST OF IT HOLDS STILL. The
   position stops at 15% and stays there, which is the word in plain base ink —
   the same still a reduced-motion reader gets. The curve is fast out of the
   gate and settles, so what a reader sees is a pulse rather than a scan.
   Re-solve the pair (85/15) against `background-size` rather than nudging one:
   they ARE the size. */
.be-shimmer {
  --be-shimmer-base: var(--be-text-sec);
  /* `--shimmer-intensity` is the capture's own knob and its default is 70%,
     i.e. the full ink mixed 70% into the base. At this card's 11px that read
     as nothing at all — the ramp's peak is a few pixels of a word — so it is
     turned up to the full ink, which is the SAME step the running step's own
     label takes above it (`text-theme-text` against `text-theme-text-sec`). */
  --be-shimmer-highlight: var(--be-text);

  background-image: linear-gradient(
    90deg,
    var(--be-shimmer-base) 0%,
    var(--be-shimmer-base) 40%,
    var(--be-shimmer-highlight) 50%,
    var(--be-shimmer-base) 60%,
    var(--be-shimmer-base) 100%
  );
  background-position: 15% 0;
  background-size: 300% 100%;
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: be-shimmer 3s infinite;
}

/* THE ROWS ARE OUT OF PHASE, which is what stops two agents reading as one
   animated block. One pass per cycle now, so half the duration really is
   anti-phase — and it is NEGATIVE, so the second row is already deep in its
   own rest on the first paint rather than waiting out a beat. */
.be-subs > div:nth-child(even) .be-shimmer {
  animation-delay: -1.5s;
}

@keyframes be-shimmer {
  0% {
    background-position: 85% 0;
    animation-timing-function: cubic-bezier(0.33, 0, 0.15, 1);
  }

  25%,
  100% {
    background-position: 15% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .be-shimmer {
    background-image: none;
    -webkit-text-fill-color: currentcolor;
    animation: none;
  }
}

/* The `+N more` row carries no avatar, so it is indented past the column the
   avatars make — the badge plus the row's own `gap-2`. */
.be-agent-rest {
  padding-left: 22px;
}

/* The composer's two marks, at the captured card's own sizes. */
.be-chip__mark {
  width: 12px;
  height: 12px;
}

.be-chip__chev {
  width: 12px;
  height: 12px;
}

.be-send__mark {
  width: 12px;
  height: 12px;
}
/*
 * /bid — #workflows' "Assign activities" card. Hand-maintained; pairs with
 * tools/fragments/bid-activities.mjs, imported by /bid and by the temporary
 * card gallery (which harvests this card onto every page).
 *
 * WHAT THIS IS: bid-context.css' price-table slice, FORKED under `ba-` — the
 * same `Aa Name` / mark-and-label header, the same 36px app pitch, the same
 * inset header rule, the same `13 / 14` scale over the whole drawing so the
 * app's `text-sm` renders at the 13px this row is measured in. Per the fork
 * rule a change that belongs to both is made twice, on purpose; a test fails
 * if a `bx-` (or any sibling) prefix appears here.
 *
 * EVERY LENGTH IS THE APP'S OWN NUMBER under one `transform: scale(--ba-fit)`
 * about the centre — a 36px row, `p-2` cells, `gap-2` to a `size-4.5` mark,
 * a `leading-5 px-1.5 rounded` badge. Do not scale one by hand.
 *
 * THE SCORE IS AT THE FOOT OF THE FILE and follows automate-mockup.css' rules,
 * which are load-bearing: every state is ADDED by the animation and never
 * removed, so THE REST STATE IS THE FILLED TABLE — what `prefers-reduced-
 * motion` shows through one `animation: none` block, and what the 100%
 * keyframe holds so `pnpm run verify`'s collapsed frame is the still. The
 * stagger is a NEGATIVE delay measured back from the last row, so a fresh
 * paint never draws a filled cell and then empties it.
 */

.ba-map {
  /* bid-context.css' token set, less the popover's. */
  --ba-surface: var(--color-theme-bg);
  --ba-chrome: var(--color-theme-card-hex);
  --ba-line: var(--color-theme-border-02);
  --ba-text: var(--color-theme-product-text);
  --ba-text-sec: color-mix(in srgb, var(--color-theme-fg) 60%, var(--color-theme-bg));
  /* `--gray-badge`, the tone the crew tag and the HCSS badge both take; the
     dark half is restated at the foot of the file. */
  --ba-badge: rgb(84 72 49 / 0.15);
  /* A skeleton bar is `bg-muted`'s pulse: a wash of the foreground, so it
     needs no dark restatement. Two stops, for the pulse. */
  --ba-skel: color-mix(in srgb, var(--color-theme-fg) 12%, transparent);
  --ba-skel-dim: color-mix(in srgb, var(--color-theme-fg) 6%, transparent);

  /* ONE SCALE, AND IT IS THE #context ROW'S: `13 / 14`, so the app's body
     type lands at the 13px `--text-product-lg` this section's own cards are
     set in. Re-solve against the type, not against air. */
  --ba-fit: 0.9286;

  /* The cycle, and where in it the column fills. `--ba-fill` is the beat the
     LAST row populates on; each earlier row is `--ba-step` ahead of it. */
  --ba-cycle: 10s;
  --ba-step: 0.12s;

  display: block;
  position: relative;
  width: 344px;
  max-width: 100%;
  box-sizing: border-box;
  color: var(--ba-text);
  font-size: 0.875rem; /* text-sm */
  line-height: 1.4286;
  transform: scale(var(--ba-fit));
  transform-origin: center;
}

.ba-map * {
  box-sizing: border-box;
}

/* A `<span>` is inline; every box here is one, so the default is undone once.
   `:where()` contributes no specificity, so this cannot outrank the
   single-class layout rules under it. */
.ba-map :where(span) {
  display: block;
}

.ba-map svg {
  display: block;
  flex: 0 0 auto;
}

.ba-table {
  width: 100%;
}

.ba-row {
  display: flex;
  align-items: stretch;
  height: 36px; /* VIEW_TABLE_ROW_HEIGHT; TABLE_HEADER_HEIGHT is the same */
}

/* The header's rule is an inset shadow rather than a border, as the app draws
   it, so the row keeps its exact height. */
.ba-head {
  box-shadow: inset 0 -1px 0 0 var(--ba-line);
}

.ba-th,
.ba-td {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px; /* gap-2 */
  min-width: 0;
  padding: 0 8px; /* p-2 */
  overflow: hidden;
  white-space: nowrap;
}

/* `font-medium text-muted-foreground`, at the body's own `text-sm`. */
.ba-th {
  color: var(--ba-text-sec);
  font-weight: var(--font-weight-medium);
}

.ba-ell {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* `text-muted-foreground-opaque`, the same colour as the label. */
.ba-mark {
  width: 18px; /* size-4.5 */
  height: 18px;
  color: var(--ba-text-sec);
  stroke-width: 1.5;
}

.ba-mark[data-filled] {
  fill: currentColor;
  stroke: none;
}

.ba-row:not(.ba-head) .ba-td {
  border-bottom: 1px solid var(--ba-line);
}

/* -------------------------------------------------------------------------
   The HCSS badge, on the activity column's header.

   The connector's mark and its name on the badge tone, pushed to the header's
   right edge. The loader is stacked on the mark's own slot so the badge is
   one width in both states; which of the two shows is the score's, and at
   rest it is the mark.
   ------------------------------------------------------------------------- */

.ba-src {
  display: flex;
  align-items: center;
  gap: 4px; /* gap-1 */
  margin-left: auto;
  height: 20px; /* leading-5 */
  padding: 0 6px; /* px-1.5 */
  border-radius: 4px; /* rounded */
  background: var(--ba-badge);
  color: var(--ba-text);
  font-size: 0.75rem; /* text-xs */
  font-weight: var(--font-weight-medium);
  line-height: 20px;
}

.ba-src__slot {
  position: relative;
  width: 18px;
  height: 13px;
}

.ba-src__mark {
  display: block;
  width: 18px;
  height: 13px;
  object-fit: contain;
}

/* The loader is the mark's size, centred on its slot, and hidden at rest. */
.ba-src__spin {
  position: absolute;
  left: 2px;
  top: -0.5px;
  width: 14px;
  height: 14px;
  color: var(--ba-text-sec);
  stroke-width: 2.25;
  opacity: 0;
  transform-origin: 50% 50%;
}

/* -------------------------------------------------------------------------
   The activity cell: the code on tabular figures, then the crew as a tag
   badge — `Crew` is a `tag` field in the app, and a tag renders `Color`'s
   `rounded px-1.5 leading-5 text-sm` badge.
   ------------------------------------------------------------------------- */

.ba-act {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.ba-code {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ba-crew {
  height: 20px;
  padding: 0 6px;
  border-radius: 4px;
  background: var(--ba-badge);
  line-height: 20px;
  white-space: nowrap;
}

/* The skeleton bar: `h-3 rounded bg-muted` at the cell's own inset, the width
   set per row by the fragment. Hidden at rest; the score shows it. */
.ba-skel {
  position: absolute;
  left: 8px;
  top: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--ba-skel);
  opacity: 0;
}

/* -------------------------------------------------------------------------
   THE SCORE.

   One ten-second cycle. The column is skeleton bars for the first four
   seconds (the loader turning on the badge), then the rows populate top to
   bottom 120ms apart, and the filled table holds for the rest — a little over
   half the loop, which is what anyone scrolling past at the wrong moment sees.

   THE 100% KEYFRAME IS THE FILLED STATE, not the reset: the loop's own wrap
   from 100% back to 0% is the only instant the cells go dark, and it means
   the frame `pnpm run verify` collapses to is the still the card rests on.

   THE STAGGER IS A NEGATIVE DELAY, measured back from the LAST row (which
   gets 0), so every cell is already in flight on the first paint and none is
   drawn filled and then emptied. Both the value and its bar take the same
   delay, on different properties, so the one cannot lead the other. Move
   `--ba-step`, the fill percentage and `--ba-cycle` together.
   ------------------------------------------------------------------------- */

.ba-td--act {
  --ba-delay: calc((var(--ba-i, 0) - var(--ba-last, 0)) * var(--ba-step));
}

.ba-act {
  animation: ba-fill var(--ba-cycle) linear infinite var(--ba-delay);
}

.ba-skel {
  /* Two animations on two properties: the score owns `opacity`, the pulse
     owns `background-color`. A pulse on opacity would win and the bar would
     never clear. */
  animation:
    ba-skel var(--ba-cycle) linear infinite var(--ba-delay),
    ba-pulse 1.6s ease-in-out infinite;
}

/* The badge follows the LAST row: the loader turns until the column has
   filled, then the mark comes back. Both are on the undelayed clock. */
.ba-src__spin {
  animation:
    ba-spin-show var(--ba-cycle) linear infinite,
    ba-turn 0.8s steps(8, end) infinite;
}

.ba-src__mark {
  animation: ba-mark-show var(--ba-cycle) linear infinite;
}

@keyframes ba-fill {
  0%,
  40% {
    opacity: 0;
  }

  42%,
  100% {
    opacity: 1;
  }
}

@keyframes ba-skel {
  0%,
  40% {
    opacity: 1;
  }

  42%,
  100% {
    opacity: 0;
  }
}

/* `animate-pulse`'s own shape, on colour rather than opacity. */
@keyframes ba-pulse {
  0%,
  100% {
    background-color: var(--ba-skel);
  }

  50% {
    background-color: var(--ba-skel-dim);
  }
}

@keyframes ba-spin-show {
  0%,
  40% {
    opacity: 1;
  }

  42%,
  100% {
    opacity: 0;
  }
}

@keyframes ba-mark-show {
  0%,
  40% {
    opacity: 0;
  }

  42%,
  100% {
    opacity: 1;
  }
}

@keyframes ba-turn {
  to {
    transform: rotate(360deg);
  }
}

/* A reader who has asked for less motion gets the table as it rests — every
   activity assigned, the HCSS mark on the badge, no bars. One block, no
   second set of values, because nothing above changed a resting property. */
@media (prefers-reduced-motion: reduce) {
  .ba-act,
  .ba-skel,
  .ba-src__spin,
  .ba-src__mark {
    animation: none;
  }
}

/* The badge's dark half — `dark:bg-default-badge` is the plain hover fill,
   which on THIS card is the plate's own `card-02`; two steps up is the first
   fill that reads as a badge on it. */
:root[data-theme='dark'] .ba-map {
  --ba-badge: var(--color-theme-card-04-hex);
}
/*
 * /bid — #workflows' "Subs & Suppliers" card: the drafted RFQ.
 * ============================================================
 *
 * Hand-maintained; pairs with tools/fragments/bid-rfq.mjs, which carries the
 * reasoning. Imported by /bid and by the temporary card gallery, which
 * harvests this card onto every page.
 *
 * ONE SCALE, AND IT IS THE ROW'S. Every length under `.br-rfq` is the app's
 * own number and the block takes one `transform: scale(var(--br-fit))` about
 * its centre, `--br-fit` being 13 / 14 — the app's `text-sm` rendering at the
 * 13px this section's own body type is (see `--bx-fit` in bid-context.css for
 * the full argument). Do not scale a length by hand here. The letter page is
 * the one thing drawn in its OWN pixels: it sits inside the viewer at page
 * size, the way the bond form on index does, and the viewer cuts it at the
 * fold.
 *
 * `br-` is this file's prefix. Nothing here reads a `bx-`, `be-`, `bk-`,
 * `bs-` or `am-` rule, on the fork rule every card on this page follows.
 */

.br-rfq {
  --br-surface: var(--color-theme-bg);
  --br-chrome: var(--color-theme-card-hex);
  --br-line: var(--color-theme-border-02);
  --br-text: var(--color-theme-product-text);
  --br-text-sec: color-mix(in srgb, var(--color-theme-fg) 60%, var(--color-theme-bg));
  --br-text-dim: color-mix(in srgb, var(--color-theme-fg) 40%, var(--color-theme-bg));
  --br-accent: #2483e2;
  /* `--gray-badge`, the relation chip's fill and the state pill's; a TONE in
     dark rather than an alpha, as bid-context.css records for its own copy. */
  --br-badge: rgb(84 72 49 / 0.15);
  /* The sheet: white paper in light, and in dark the +12 step over the pane
     the bond form takes (`--am-bb-paper`) rather than an inverted page. */
  --br-paper: #fff;
  --br-paper-ink: #1a1a1a;
  --br-paper-dim: #6b6b6b;
  --br-paper-rule: rgb(0 0 0 / 0.12);
  --br-fit: 0.9286;

  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 344px;
  box-sizing: border-box;
  transform: translate(-50%, -50%) scale(var(--br-fit));
  transform-origin: center;
  color: var(--br-text);
  font-family: var(--font-system);
  font-size: 14px;
  line-height: 20px;
  text-align: left;
}

.br-rfq *,
.br-rfq *::before,
.br-rfq *::after {
  box-sizing: border-box;
}

/* Every box in this card is a span (see the fragment header). `:where()` keeps
   this at zero specificity so the single-class rules below still win. */
.br-rfq :where(span) {
  display: block;
}

.br-rfq svg {
  display: block;
  flex: none;
}

/* -------------------------------------------------------------------------
   The Vendors field: `text-sm text-muted-foreground` label over the chips.
   ------------------------------------------------------------------------- */

.br-field__label {
  margin-bottom: 6px;
  color: var(--br-text-sec);
}

.br-rels {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* `RelationValue`: `rounded px-1.5 leading-5 text-sm` on `bg-gray-badge`. The
   sides are the app's own 6; 3px above and below is one step past its bare
   line box (a 26px chip against the app's 22), the same lever #context's
   planholder chip pulls harder. THE SIDES ARE NOT 8 FOR A REASON: at 8 the two
   longest names no longer share a 344px row and the three chips stack one to a
   line, which cost the block 26px of height it did not have. */
.br-rel {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  min-width: 0;
  padding: 3px 6px;
  border-radius: 4px;
  background: var(--br-badge);
}

/* `DocumentIconGlyph`'s image branch: `size-4 rounded-[4px] overflow-hidden`
   with the picture `object-cover` inside it. */
.br-rel__icon {
  flex: none;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  overflow: hidden;
}

.br-rel__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.br-rel__name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* -------------------------------------------------------------------------
   The file viewer: a title bar over the page, on the popover's elevation.
   ------------------------------------------------------------------------- */

.br-viewer {
  overflow: hidden;
  border: 1px solid var(--br-line);
  border-radius: 8px;
  background: var(--br-surface);
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.1),
    0 4px 6px -4px rgb(0 0 0 / 0.1);
}

.br-viewer__bar {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 12px;
  border-bottom: 1px solid var(--br-line);
  background: var(--br-chrome);
}

.br-viewer__mark {
  width: 16px;
  height: 16px;
  color: var(--br-text-sec);
}

.br-viewer__name {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: 500;
}

/* The document's state, as a tag badge: `rounded-full px-2 text-xs`. */
.br-state {
  flex: none;
  padding: 1px 8px;
  border-radius: 9999px;
  background: var(--br-badge);
  font-size: 12px;
  line-height: 16px;
  color: var(--br-text-sec);
}

/* `ml-1 rounded bg-primary/10 px-1 text-[10px] text-primary` — the badge an
   agent-written row wears in the markups panel one section down. */
.br-ai {
  flex: none;
  padding: 0 4px;
  border-radius: 4px;
  font-size: 10px;
  line-height: 16px;
  color: var(--br-accent);
  background: color-mix(in srgb, var(--br-accent) 10%, transparent);
}

/* The viewer's pane: the page's top, cut at the fold. The height is what puts
   the block on the row's own air; re-measure rather than nudging it. */
.br-viewer__body {
  height: 170px;
  padding: 12px 16px 0;
  overflow: hidden;
}

/* -------------------------------------------------------------------------
   The page. A US-Letter sheet 312 wide (so 404 tall), drawn in its own
   pixels — every size below is page type, not app type.
   ------------------------------------------------------------------------- */

.br-page {
  width: 312px;
  height: 404px;
  padding: 20px 22px;
  background: var(--br-paper);
  color: var(--br-paper-ink);
  font-size: 9px;
  line-height: 13px;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--color-theme-fg) 8%, transparent),
    0 1px 3px rgb(0 0 0 / 0.08);
}

.br-page__title {
  font-size: 11px;
  line-height: 14px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.br-page__job {
  margin-top: 4px;
  font-size: 9.5px;
  font-weight: 600;
}

.br-page__meta {
  display: flex;
  gap: 12px;
  margin-top: 2px;
  color: var(--br-paper-dim);
  white-space: nowrap;
}

.br-page__table {
  margin-top: 12px;
}

.br-page__row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 3px 0;
  border-top: 1px solid var(--br-paper-rule);
  font-size: 9.5px;
}

.br-page__row > span:last-child {
  flex: none;
  font-variant-numeric: tabular-nums;
}

.br-page__row--head {
  padding-top: 0;
  border-top: 0;
  font-size: 8px;
  line-height: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--br-paper-dim);
}

.br-page__row:last-child {
  border-bottom: 1px solid var(--br-paper-rule);
}

.br-page__note {
  margin-top: 10px;
  color: var(--br-paper-dim);
}

/* -------------------------------------------------------------------------
   Dark. The tokens that invert wrong, and nothing else.
   ------------------------------------------------------------------------- */

:root[data-theme='dark'] .br-rfq {
  /* NOT card-02, which is what bid-context.css takes for the same badge: this
     card's captured plate IS `bg-theme-card-02-hex`, so at that tone a chip
     vanished into the plate in dark. One step up is what the app's own
     `--hover-opaque` maps to here. */
  --br-badge: var(--color-theme-card-04-hex);
  --br-paper: #252525;
  --br-paper-ink: #ededed;
  --br-paper-dim: #9a9a9a;
  --br-paper-rule: rgb(255 255 255 / 0.14);
}

:root[data-theme='dark'] .br-viewer {
  box-shadow:
    0 10px 15px -3px rgb(0 0 0 / 0.3),
    0 4px 6px -4px rgb(0 0 0 / 0.3);
}
/* /bid's "Data from everywhere" section, drawn QUIET.
 *
 * Pairs with tools/fragments/bid-surfaces-quiet.mjs, whose header carries the
 * argument and the history. Every rule here is scoped to
 * `[data-surfaces-quiet]`, the attribute that pass puts on the section — and
 * that scope is load-bearing rather than tidiness: `CardGallery.astro`
 * imports bid-surfaces.css because the temporary card gallery harvests these
 * same cards onto every page, and /manage carries a frozen copy of this whole
 * section under the same `#surfaces` id. Neither carries this attribute, so
 * neither is touched. **Nothing in here may be written unscoped.**
 *
 * It is loaded by /bid only.
 *
 * The four changes, in the order they matter:
 *
 *   1  COLOUR   ~28 saturated things become 0. Cursor's four mockups carry no
 *               colour at all and their section has exactly one accent, the
 *               orange CTA. Ours had ten agency badges, ten logo grounds and
 *               twenty grade/backlog chips competing with it.
 *   2  CONTRAST Mockup text steps down to the secondary/tertiary ink. Cursor's
 *               is gray-on-gray and reads as texture; ours was near-white and
 *               invited reading.
 *   3  DENSITY  The table's calculations footer, the map's three floating
 *               controls, and one of the two chips on a gallery card. Every
 *               one is a removal of the thing there is most of, or of a
 *               control the reader cannot use in a still.
 *   4  SCALE    `--bs-zoom` 0.7 -> 0.85, and the map's raster a further 2.0
 *               on top of that (with its pins counter-scaled so they do not
 *               grow). Cursor's mockups are a few big shapes in a frame;
 *               ours was a whole app screen shrunk to a fifth, so the crop
 *               was full of detail nobody can read.
 *
 * Card four wore the same browser chrome as the three beside it for one build
 * and it came out on 2026-09-03 at the owner's direction — the dataset stack
 * is back on the capture's own bare plate, so this file draws no window and
 * the fragment writes no markup.
 *
 * All of it is inside the capture's own fixed 378px plates, so none of it
 * moves the page's height.
 */

/* =========================================================================
   1 — COLOUR
   ========================================================================= */

/* The Projects table's Owner column was ten filled badges in four hues, right
   aligned in their own column, which is the loudest thing in the card and sits
   where the eye rests at the end of a row. `tag` fields render as chips in the
   app, so this is a departure — but at 0.7 scale in a 259px crop a chip is a
   coloured blob rather than a control, and the column reads perfectly as text.
   Padding goes with the fill: an unfilled chip that keeps its inset looks like
   a cell that failed to align. */
[data-surfaces-quiet] .bs-tbody .bs-chip {
  background: none;
  color: var(--bs-text-dim);
  padding-inline: 0;
  border-radius: 0;
}

/* The gallery's two chips per card become one neutral one. See DENSITY below
   for which one goes and why it is that one. */
/* `:root` and `[data-tone]` are both load-bearing, and one build was spent
   finding out. The tone this overrides is set twice — `.bs-chip[data-tone='green']`
   at (0,2,0) in light, and `:root[data-theme='dark'] .bs-chip[data-tone='green']`
   at (0,4,0) in dark — so a plain `[scope] .bs-card-fields .bs-chip` at (0,3,0)
   won in light and lost in dark, and the chip stayed green on the only theme
   this section is usually looked at in. (0,5,0) clears both. */
:root [data-surfaces-quiet] .bs-card-fields .bs-chip[data-tone] {
  --bs-chip-bg: color-mix(in srgb, var(--color-theme-fg) 8%, transparent);
  --bs-chip-fg: var(--bs-text-dim);
}

/* The ten contractor lockups are drawn on spread grounds — one light, one
   warm, one cool, two dark — precisely so the row does not read as one colour.
   That is right on a real gallery and wrong in a 259px crop, where the covers
   are the biggest coloured areas on the page. Grayscale keeps the LOCKUP (the
   thing that says "these are companies") and drops the ground to a value.

   `contrast()` before `opacity()` on purpose: the dark-ground marks come out
   near-black under grayscale alone, and pulling contrast toward mid-gray first
   is what stops the row reading as two black tiles and three pale ones.

   IT WENT LIGHTER ON 2026-09-04 AND THEN THE LIGHTENING MOVED OUT OF THIS
   RULE, in one afternoon, and the second half is the part worth keeping. The
   covers were reported as the darkest thing in the row of four mockups, and
   this took a `brightness(1.5)` step ahead of the contrast. **That clips.**
   Grayscale puts a saturated yellow at luminance ~214 and a near-white ground
   at ~247; a 1.5x lift takes both past 255, so an accent mark and the tile
   behind it land on the SAME white and the mark disappears — which is exactly
   what happened to Trebark's and Vantel's blades. Never lift a filter chain
   whose subject is already near the top of the range.

   The lightness comes from the ARTWORK now: the ten lockups were redrawn the
   same day against six reference logos, seven of which sit on a near-white
   ground where the set before them was all mid-tone slate and stone. So this
   is back to the original shape — one contrast flattening and one opacity
   blend toward the card — with the contrast a step HIGHER than it ever was
   (0.62 against 0.55), which is what keeps a one-colour accent mark separable
   from its ground after the blend.

   AND THE CONTRAST CAME DOWN AGAIN ON 2026-09-04, 0.62 -> 0.52, on a report
   that the banners still read loud. `contrast()` is the one dial here that
   MUTES rather than dims: it pulls both ends toward mid-grey at once, so the
   near-white grounds come down and the two near-black ones come up, and the
   spread across the ten covers narrows from ~70 values to ~61 while the row's
   average brightness barely moves. `opacity()` cannot do that — it scales
   every cover toward the card by the same fraction and keeps the ten as far
   apart from each other as they were, which is what "loud" actually meant.
   The price is paid on the LOCKUP, whose separation from its own ground falls
   with the same factor, and 0.46 is where that stops: it is 26% less ink than
   0.62, it puts the ten covers inside a ~54-value band against the ~70 they
   started in, and under about 0.45 a one-colour mark on a near-white ground
   (Trebark's yellow blade, Vantel's) stops being a mark at all. **Treat 0.46
   as the floor rather than the next step down.** It went 0.62 -> 0.52 -> 0.46
   in one morning, on the same report each time; if the row still reads loud
   after this, the thing to change is the ARTWORK's ten grounds, not this. */
[data-surfaces-quiet] .bs-card-cover {
  filter: grayscale(1) contrast(0.46) opacity(0.5);
}

/* Each marker carries an invented company's mark in its disc — a colour ground
   at 23px, which is under a tenth of the marker's own area and none of what
   the marker is saying. The teardrop itself stays white: it is white on the
   app's own dark map style, and dimming the shell rather than the mark is what
   made an earlier pass illegible. */
[data-surfaces-quiet] .bs-pin-img {
  filter: grayscale(1) contrast(0.7);
}

/* THE SHELL TAKES THE GALLERY CARD'S OWN SHADOW, at the owner's direction on
   2026-09-03. It was `box-shadow: none` — the app's `shadow-md` (a 6px blur
   over a 4px one) at 25px on a dark canvas thickened every marker's edge, and
   thirty-odd of them read as a clot rather than as a scatter. But with nothing
   under them the white teardrops sit flat ON the raster instead of over it,
   which is what was reported.

   So it is `shadow-sm`, and specifically the value `.bs-card` carries in the
   sibling sheet — the `GalleryCard` in the Competitors screen directly to the
   right of this one. One shadow across the row of three mockups rather than
   two, and it is a tenth of the ink the app's own marker shadow was. Move the
   two together. */
[data-surfaces-quiet] .bs-pin-shell {
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); /* shadow-sm, `.bs-card`'s own */
}

/* THE MARKER LAYER IS FULLY OPAQUE AS OF 2026-09-08, at the owner's direction.
   It was `opacity: 0.72` — a white teardrop on the app's dark map style is the
   highest-contrast thing in the row, and stepping the whole layer back was the
   answer while the map still drew nine pins with four of them piled on one
   another: a translucent clot reads as one soft blob rather than as four
   markers. `bid-surfaces.mjs` de-clusters now (see `SPACING` there), so the
   crop holds seven markers that do not touch, and the thing that was too loud
   about them was the count rather than the value. A half-faded pin reads as a
   rendering fault, which is what was reported.

   The mark inside the disc keeps its `grayscale(1) contrast(0.7)` above; that
   is colour, not transparency, and it is the same quiet treatment the covers
   take. **Nothing in this section may dim a marker again** — put the count
   back down first if the layer ever reads loud. */

/* =========================================================================
   2 — CONTRAST

   The mockups are pictures of software, not documents. Cursor's are set in one
   flat gray at one size and cut mid-sentence, so the eye takes them as texture
   and moves on to the card's own two lines — which is where the reading
   actually is. Ours were drawn at the app's own contrast, which is correct
   inside the app and wrong at a fifth of the size behind a card's copy.

   These step the BODY down and leave the column headers and the chrome where
   they are: those were already secondary ink, and taking them further would
   flatten the screens into one value.
   ========================================================================= */

/* The calculations row is cut outright below, so only the body is left to
   step down. */
[data-surfaces-quiet] .bs-tbody .bs-cell {
  color: var(--bs-text-sec);
}

[data-surfaces-quiet] .bs-card-title {
  color: var(--bs-text-sec);
}

/* The dataset stack is already monochrome; it is only its top line that sits
   at full ink. */
[data-surfaces-quiet] .bpv-card__t {
  color: var(--color-theme-text-sec);
}

[data-surfaces-quiet] .bpv-card__d {
  color: var(--color-theme-text-tertiary);
}

/* =========================================================================
   3 — DENSITY

   Three removals: the table's footer, the map's three floating controls, and
   one of the two chips on a gallery card. The markers were a fourth until the
   map's scale started doing that job by cropping — see below.
   ========================================================================= */

/* THE MAP LOSES NO MARKERS *HERE*, AND IT HAS NOT SINCE THE SCALE STEP. This
   rule hid every second one (`nth-child(2n)`) while the map was drawn at 0.7
   and whole: 33 white teardrops on a dark canvas was the densest object in the
   row, and past about a dozen a scatter reads as noise rather than as plants.

   Two things do that job now and neither is a rule in this file. The SCALE step
   below thins by cropping — at 2.0 the card shows about a fifth of the canvas
   rather than all of it — and as of 2026-09-08 `bid-surfaces.mjs` de-clusters
   the drawing itself, so no marker is within another's own 28px box anywhere on
   the canvas, at any scale. Between them the desktop crop holds seven markers
   that do not touch, and every one is still on its own stored coordinate.

   **Do not thin again here.** Halving seven leaves three, which reads as a map
   with almost nothing on it — the opposite claim from the one the card makes.
   The lever is `SPACING` in the fragment, and it is measured in the marker's
   own width. */

/* THE GRADE CHIP GOES AND THE BACKLOG ONE STAYS, at the owner's direction on
   2026-09-03. Both halves were built: the first pass kept Grade, on the
   argument that it is a real live `tag` field where Backlog is not a field at
   all (the four that once held it were deleted from the app on 2026-07-30, so
   `bid-surfaces.mjs` derives that chip from an invented capacity percentage).
   The owner's call is that a bare A / B / C says nothing to a reader who does
   not know the scale, and Hungry / Neutral / Full is a word they can read at a
   glance — which is the whole test this section is being drawn for.

   So the row is one chip either way; the difference is which. See
   `SURFACES_INVENTED` in the fragment for what that costs in provenance.

   `:first-child` rather than a shorter chip list in `bid-surfaces.mjs`: that
   file's markup is also what the card gallery harvests onto every page and
   what /manage's frozen copy of this section holds, so the change belongs
   here, scoped, rather than in the drawing. */
[data-surfaces-quiet] .bs-card-fields .bs-chip:first-child {
  display: none;
}

/* THE TABLE'S CALCULATIONS FOOTER GOES. `Count 12` is the app's own row and it
   is correct there; in a 259px crop it is one more line of small type under a
   table whose rows are already cut, saying a number about records the reader
   cannot see. Dropping it also hands the table its 38px back, so a ninth row
   is reachable. */
[data-surfaces-quiet] .bs-calc-row {
  display: none;
}

/* THE MAP'S THREE FLOATING CONTROLS GO. Tools, layers and the style plate are
   the app's own bottom-left cluster, and they are the only thing in the row of
   four mockups that is a CONTROL — a still cannot open any of them, and at 0.7
   the style plate's thumbnail is a 25px picture of the map it sits on. The
   bottom-right zoom pill is untouched here because the crop never reaches it.
   */
[data-surfaces-quiet] .bs-bl {
  display: none;
}

/* =========================================================================
   4 — SCALE: everything is drawn bigger inside the same frames

   `bid-surfaces.css` states every length in the app's own pixels and turns
   them into card pixels with ONE number, `--bs-zoom` on `.bs-screen` — which
   is exactly so this can be re-solved rather than nudged. Nothing below is
   scaled by hand and nothing in the fragment moves.

   0.7 is solved by the gallery's three-column minimum (see that file's header:
   four 140px cards need 656 app px, and 460 / 0.7 = 657). 0.85 gives the body
   541 x 355 app px instead of 657 x 431, so:

     Projects     the desktop crop reaches 305 app px rather than 370 — Name in
                  full and the Owner beside it, where it used to reach into Bid
                  Date. With the calculations row gone the body is 355 - 36 of
                  column header = 319, i.e. eight whole 36px rows and a ninth
                  cut 31px in. Twelve are in the markup, so nothing runs out.
     Vendors      the render is pinned at 657 x 431 and the markers are
                  absolutely positioned at projected coordinates, so this crops
                  the canvas rather than rescaling it — every surviving pin is
                  still on its own stored coordinate.
     Competitors  three whole 140px columns plus 121px of a fourth, and 2.8
                  rows of the 127px pitch, so the grid still bleeds on both
                  axes the way it did at 0.7.

   Past about 0.9 the gallery drops to three columns and the table's Owner
   column leaves the desktop crop, which is the two things this row is read by.
   Re-check both before moving it again.

   THE MAP TAKES A SECOND STEP ON TOP OF IT, and it is a different mechanism.
   `--bs-zoom` enlarges a screen drawn in app pixels; the map is not drawn, it
   is a RASTER at a fixed Mapbox frame (z8.8 on [-95.25, 29.83], rendered by
   `tools/vendor-map-render.mjs`) with 36 markers absolutely positioned at
   coordinates `project()` solved against exactly that frame. So the honest way
   to zoom the map is to re-render at a higher Mapbox zoom and re-project — and
   that cannot be done here, this file being CSS while the render and the
   projection live in `bid-surfaces.mjs` and `tools/vendor-map-render.mjs`,
   which the card gallery's harvested copies read too.

   What this does instead is enlarge the whole picture: one `scale()` on the
   image AND on the marker layer, same factor, same origin, so every pin stays
   on its own feature to the pixel. It is not a higher-zoom render — the roads
   and labels grow rather than being re-tiled — and that is the point rather
   than a compromise: the ask is LESS fidelity, and a re-render at z9.5 would
   draw the same detail sharply where this draws less of it, softly.

   Sharpness is what it spends, and **2.0 IS THE CEILING RATHER THAN A NUMBER
   PICKED BY EYE**. The file is a 2x source (1314 x 862) drawn into 657 x 431,
   so it has exactly two device pixels per CSS pixel to give away: 1.3 left
   1.54, 1.7 left 1.18, and **2.0 leaves 1.00 — native, the last factor that
   is not upscaling.** Past it the browser is inventing pixels and the picture
   is genuinely blurred; the honest fix there is a second pair of renders at a
   higher Mapbox zoom with `project()` re-solved against them, which needs
   `bid-surfaces.mjs` and the render script, i.e. a change to the drawing
   rather than to this scoped sheet.

   It went 1.7 -> 2.0 on 2026-09-03 at the owner's direction, the map having
   been reported as wanting to be bigger. What it costs is the crop: the
   desktop reader's 305 x 355 app px window over the raster tightens from
   63..242 / 73..282 to 76..229 / 89..267, so the card shows **8 of the 36
   drawn pins where it showed 9** — measured, not estimated. Every one is
   still on its own stored coordinate.

   THE ORIGIN IS A CROP, AND IT IS SOLVED AGAINST THE STORED COORDINATES
   RATHER THAN SET TO THE MIDDLE. It was `152px 178px` — dead centre of what a
   1440 reader sees (305 / 2, 355 / 2) — and centring the growth is not the
   same thing as framing what grew: at 2.0 that window falls on 63..242 of the
   raster, which is the sparse western end, and the card drew five pins bunched
   into its top-left with the lower half empty. Reported on 2026-09-03 as the
   markers being consolidated in one spot, which they were — of the frame, not
   of Texas.

   The arithmetic that makes it solvable: at scale S about origin O, the
   visible V app px start at `O - O/S` in the raster's own pixels, so at S = 2
   **the window's top-left corner is exactly half the origin**. Pick the corner
   and double it.

   IT WENT `362px 314px` -> `417px 305px` ON 2026-09-08, AND IT IS A DIFFERENT
   SOLVE BECAUSE THE DRAWING CHANGED UNDER IT. That corner was searched against
   all 36 on-canvas points and scored on count, quadrant coverage and mean
   pairwise distance, and it won with nine — but four of those nine sat inside
   26px of each other, three of them inside 12, so what the card actually drew
   was six pins and a pile. `bid-surfaces.mjs` de-clusters at the marker's own
   28px box now, which leaves 17 on the canvas, and the search was re-run over
   those with two conditions the first one did not have: **no drawn marker may
   be clipped by the crop's edge** (a marker's box is 29.5 app px tall above its
   own anchor and 14.75 either side of it — the first search scored anchors, so
   its best windows all put a pin half off the top), and **no pair may be closer
   than a marker's width**.

   `417px 305px` puts the desktop crop on **208.5..360.6 / 152.5..330.1** of the
   raster and draws **seven markers, in all four quadrants, none of them
   clipped, the closest pair 66 app px apart** — over twice a marker's width.
   The window is 258.5 x 302 CSS px, measured on the built page, i.e. 304.1 x
   355.3 app px, and the seven sit on **16.2 / 16.4 of side margin and 9.5 /
   8.3 of top and bottom** — solved, so the block is centred in its own crop
   rather than pushed against an edge. Nothing in the window is partly drawn: a
   pin is wholly in or wholly out, which the first search could not promise
   because it scored ANCHORS, and a marker's box stands 29.5 app px above its
   own anchor and 14.75 either side of it.

   Below 1024 the crop is the whole 541 x 355, i.e. the same corner and a wider
   window (208.5..478), which holds 10 — that width has the room for them. The
   raster covers the frame at both: scaled about this origin it spans
   -417..897 across and -305..557 down, against a 541 x 355 body.

   Re-solve it whenever `--bs2-map-scale`, `SPACING`, the render's frame or the
   query moves; every one of those changes which pins a given corner catches. */

[data-surfaces-quiet] .bs-mockup {
  --bs-zoom: 0.85;

  /* One factor and one origin, read by the two rules below. They must stay
     equal — the moment they differ, a pin is no longer on its plant. */
  --bs2-map-scale: 2;
  --bs2-map-origin: 417px 305px;
}

[data-surfaces-quiet] .bs-map-img,
[data-surfaces-quiet] .bs-markers {
  transform: scale(var(--bs2-map-scale));
  transform-origin: var(--bs2-map-origin);
}

/* And the pins give all of that back. A marker is a 28px teardrop, which is
   already the largest single object in the row; at the map's own factor it read
   as a badge on a map rather than a point on it. **The product of the two is
   the only number that matters here, and it is 1.05** — so a pin renders at
   the same 29px it did before the map grew, which is what the owner asked for:
   the graphic bigger, the markers where they were. 0.527 x 2 = 1.054, exactly
   the 0.62 x 1.7 it replaces. **Re-solve it against `--bs2-map-scale` rather
   than nudging it.**

   `transform-origin: bottom` is load-bearing and is the marker's own anchor:
   `MapMarker` is `anchor="bottom"`, drawn here as `translate(-50%, -100%)`, so
   the box's bottom-centre IS the coordinate. Scaling about it leaves the point
   exactly where it was; scaling about the default centre would walk every pin
   half its own height off its plant. The `scale` property rather than a second
   `transform` for the same reason — the captured translate has to survive. */
[data-surfaces-quiet] .bs-pin {
  transform-origin: bottom center;
  scale: 0.527;
}
