sqzass.git

sqzass

~ A static site generator written in Rust
git clone https://github.com/debuman/sqzass.git

docs/static/css/landing.css (17951B)

      1 /* 랜딩 전용 스타일. docs(main.css)와 독립.
      2    브랜드: sqzass = squeeze — 웜 피치/코랄, 눌리는(스퀴즈) 상호작용,
      3    말랑한 블롭. 구조: 고정 글래스 내비 / 히어로 / 벤토 그리드 / 푸터.
      4    테마: docs와 같은 [data-theme] 계약으로 라이트·다크 둘 다.
      5    인라인 style 금지 — 장식은 이 시트와 SVG 표현 속성이 진다. */
      6 
      7 @font-face {
      8   font-family: "JetBrains Mono";
      9   src: url("/fonts/jetbrains-mono-400.woff2") format("woff2");
     10   font-weight: 400;
     11   font-style: normal;
     12   font-display: swap;
     13 }
     14 @font-face {
     15   font-family: "JetBrains Mono";
     16   src: url("/fonts/jetbrains-mono-700.woff2") format("woff2");
     17   font-weight: 700;
     18   font-style: normal;
     19   font-display: swap;
     20 }
     21 
     22 :root {
     23   /* 다크가 기본 — 템플릿이 data-theme="dark"를 쓴다 */
     24   --bg: #141110;
     25   --bg-hi: #1b1715;
     26   --panel: rgba(255, 226, 205, 0.05);
     27   --panel-hi: rgba(255, 226, 205, 0.09);
     28   --line: rgba(255, 214, 189, 0.13);
     29   --text: #f4eee7;
     30   --muted: #a79c92;
     31   --accent: #ffa171;      /* 피치 — 큰 요소·그래픽 */
     32   --accent-ink: #ffb98f;  /* 본문 위 링크·강조 텍스트 */
     33   --coral: #ff5e42;
     34   --grad: linear-gradient(98deg, #ffc296 0%, #ff9d63 45%, #ff5e42 100%);
     35   --bar-other: #4d4540;
     36   --btn-ink: #2a1408;
     37   --glass: rgba(20, 17, 16, 0.72);
     38   --shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
     39   --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
     40           "Noto Sans KR", "Apple SD Gothic Neo", sans-serif;
     41   --mono: "JetBrains Mono", ui-monospace, "SF Mono", Consolas, monospace;
     42   --wrap: 71rem;
     43   color-scheme: dark;
     44 }
     45 :root[data-theme="light"] {
     46   --bg: #faf5ef;
     47   --bg-hi: #ffffff;
     48   --panel: rgba(255, 255, 255, 0.65);
     49   --panel-hi: rgba(255, 255, 255, 0.95);
     50   --line: rgba(84, 48, 24, 0.14);
     51   --text: #261c15;
     52   --muted: #75665b;
     53   --accent: #e86a30;
     54   --accent-ink: #c2440f;
     55   --coral: #e04022;
     56   --grad: linear-gradient(98deg, #f2924e 0%, #e86a30 50%, #d43a1a 100%);
     57   --bar-other: #d4c8bd;
     58   --btn-ink: #ffffff;
     59   --glass: rgba(250, 245, 239, 0.75);
     60   --shadow: 0 18px 44px rgba(120, 70, 30, 0.14);
     61   color-scheme: light;
     62 }
     63 
     64 * { box-sizing: border-box; }
     65 
     66 html { scroll-behavior: smooth; }
     67 
     68 body {
     69   margin: 0;
     70   background: var(--bg);
     71   color: var(--text);
     72   font-family: var(--sans);
     73   font-size: 1.0625rem;
     74   line-height: 1.7;
     75   -webkit-text-size-adjust: 100%;
     76   overflow-x: clip;
     77 }
     78 
     79 a { color: var(--accent-ink); text-decoration: none; }
     80 a:hover { text-decoration: underline; text-underline-offset: 3px; }
     81 :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
     82 
     83 img { max-width: 100%; }
     84 
     85 .skip {
     86   position: absolute;
     87   left: -999px;
     88   top: 0;
     89   background: var(--coral);
     90   color: #fff;
     91   padding: 0.5rem 1rem;
     92   z-index: 30;
     93   font-weight: 700;
     94 }
     95 .skip:focus { left: 0.5rem; top: 0.5rem; }
     96 
     97 /* ── 고정 글래스 내비 ── */
     98 .nav {
     99   position: fixed;
    100   inset: 0 0 auto 0;
    101   z-index: 20;
    102   background: var(--glass);
    103   -webkit-backdrop-filter: blur(14px) saturate(1.5);
    104   backdrop-filter: blur(14px) saturate(1.5);
    105   border-bottom: 1px solid var(--line);
    106 }
    107 .nav-in {
    108   max-width: var(--wrap);
    109   margin: 0 auto;
    110   padding: 0.72rem clamp(1.25rem, 4vw, 2.5rem);
    111   display: flex;
    112   align-items: center;
    113   justify-content: space-between;
    114   gap: 1rem;
    115 }
    116 .mark {
    117   display: inline-flex;
    118   align-items: center;
    119   gap: 0.55rem;
    120   color: var(--text);
    121   font-weight: 800;
    122   font-size: 1.06rem;
    123 }
    124 .mark:hover { text-decoration: none; }
    125 .mark img { width: 26px; height: 26px; }
    126 :root[data-theme="dark"] .mark img { filter: invert(1); }
    127 .nav-links {
    128   display: flex;
    129   align-items: center;
    130   gap: 1.2rem;
    131   font-size: 0.93rem;
    132   font-weight: 600;
    133 }
    134 .nav-links > a { color: var(--muted); }
    135 .nav-links > a:hover { color: var(--text); text-decoration: none; }
    136 .nav-icon {
    137   display: inline-flex;
    138   align-items: center;
    139   justify-content: center;
    140   width: 2.1rem;
    141   height: 2.1rem;
    142   border: 1px solid var(--line);
    143   border-radius: 9px;
    144   background: transparent;
    145   color: var(--muted);
    146   cursor: pointer;
    147   padding: 0;
    148 }
    149 .nav-icon:hover { color: var(--text); border-color: var(--accent); }
    150 :root[data-theme="dark"] .theme-toggle .i-moon { display: none; }
    151 :root[data-theme="light"] .theme-toggle .i-sun { display: none; }
    152 
    153 main { display: block; }
    154 
    155 .inner { max-width: var(--wrap); margin: 0 auto; padding-inline: clamp(1.25rem, 4vw, 2.5rem); }
    156 
    157 /* ── 히어로: 말랑한 블롭 하나가 뒤를 받친다 ── */
    158 .hero {
    159   position: relative;
    160   padding-block: clamp(8rem, 16vw, 11.5rem) clamp(2.5rem, 5vw, 4rem);
    161   overflow: clip;
    162 }
    163 .blob {
    164   position: absolute;
    165   top: clamp(2rem, 6vw, 5rem);
    166   right: -12rem;
    167   width: clamp(26rem, 46vw, 44rem);
    168   aspect-ratio: 1;
    169   background: var(--grad);
    170   opacity: 0.24;
    171   filter: blur(70px);
    172   border-radius: 43% 57% 52% 48% / 55% 44% 56% 45%;
    173   animation: squish 16s ease-in-out infinite;
    174   pointer-events: none;
    175 }
    176 :root[data-theme="light"] .blob { opacity: 0.28; }
    177 @keyframes squish {
    178   0%, 100% { border-radius: 43% 57% 52% 48% / 55% 44% 56% 45%; transform: scale(1); }
    179   33%      { border-radius: 56% 44% 46% 54% / 46% 58% 42% 54%; transform: scale(1.06, 0.94); }
    180   66%      { border-radius: 48% 52% 58% 42% / 52% 46% 54% 48%; transform: scale(0.95, 1.05); }
    181 }
    182 
    183 .hero .inner { position: relative; }
    184 .kicker {
    185   margin: 0 0 1.2rem;
    186   display: inline-flex;
    187   align-items: center;
    188   gap: 0.55rem;
    189   color: var(--accent-ink);
    190   font-family: var(--mono);
    191   font-size: 0.8rem;
    192   font-weight: 700;
    193 }
    194 .kicker::before {
    195   content: "";
    196   width: 1.5rem;
    197   height: 3px;
    198   border-radius: 2px;
    199   background: var(--grad);
    200 }
    201 .display {
    202   margin: 0;
    203   font-size: clamp(2.8rem, 6.4vw, 5.2rem);
    204   line-height: 1.03;
    205   font-weight: 800;
    206   letter-spacing: -0.028em;
    207   text-wrap: balance;
    208 }
    209 .display em {
    210   display: inline-block; /* 스퀴즈 스프링의 transform 대상 */
    211   font-style: normal;
    212   background: var(--grad);
    213   -webkit-background-clip: text;
    214   background-clip: text;
    215   color: transparent;
    216   transform-origin: left center;
    217 }
    218 .sub {
    219   margin: 1.5rem 0 0;
    220   max-width: 52ch;
    221   font-size: 1.14rem;
    222   line-height: 1.75;
    223   color: var(--muted);
    224   text-wrap: pretty;
    225 }
    226 .cta { margin: 2.1rem 0 0; display: flex; gap: 0.75rem; flex-wrap: wrap; }
    227 .btn {
    228   display: inline-flex;
    229   align-items: center;
    230   min-height: 2.95rem;
    231   padding: 0 1.5rem;
    232   border-radius: 14px;
    233   font-weight: 700;
    234   font-size: 0.99rem;
    235   transition: transform 0.14s ease, box-shadow 0.14s ease, border-color 0.14s ease;
    236   cursor: pointer;
    237 }
    238 .btn:hover { text-decoration: none; transform: translateY(-1px); }
    239 .btn:active { transform: scale(0.96); } /* 쥐어짜면 눌린다 */
    240 .btn-primary {
    241   background: var(--grad);
    242   color: var(--btn-ink);
    243   box-shadow: 0 6px 24px rgba(255, 110, 60, 0.35);
    244 }
    245 .btn-primary:hover { box-shadow: 0 8px 30px rgba(255, 110, 60, 0.48); }
    246 .btn-ghost { color: var(--text); border: 1px solid var(--line); background: var(--panel); }
    247 .btn-ghost:hover { border-color: var(--accent); }
    248 
    249 .lede { margin-top: clamp(2.6rem, 5vw, 3.6rem); }
    250 .lede p {
    251   max-width: 62ch;
    252   margin: 0 0 1rem;
    253   font-size: 1rem;
    254   color: var(--muted);
    255 }
    256 
    257 /* ── 벤토 ── */
    258 .bento-wrap { padding-block: clamp(2rem, 4vw, 3rem) clamp(4rem, 7vw, 6rem); }
    259 .bento {
    260   display: grid;
    261   grid-template-columns: repeat(4, 1fr);
    262   grid-auto-flow: dense;
    263   gap: 0.9rem;
    264 }
    265 .tile {
    266   border: 1px solid var(--line);
    267   border-radius: 18px;
    268   background: var(--panel);
    269   padding: 1.5rem 1.6rem;
    270   margin: 0;
    271   transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease;
    272 }
    273 .tile:hover {
    274   transform: scale(0.988); /* 타일도 쥐면 눌린다 */
    275   border-color: var(--accent);
    276   background: var(--panel-hi);
    277 }
    278 .tile h2 { margin: 0 0 0.55rem; font-size: 1.08rem; font-weight: 800; letter-spacing: -0.01em; }
    279 /* keep-all은 CJK에만 작용한다 — 영문 페이지에 인용된 한국어 예시
    280    (검색엔진최적화)가 단어 중간에서 꺾이지 않게 전역으로 준다. */
    281 .tile .note { margin: 0; font-size: 0.93rem; line-height: 1.65; color: var(--muted); word-break: keep-all; }
    282 .tile-link { margin: 1.1rem 0 0; font-weight: 700; font-size: 0.95rem; }
    283 
    284 .t-bench { grid-column: span 2; grid-row: span 2; }
    285 .t-wide { grid-column: span 2; }
    286 
    287 /* 벤치마크 타일 */
    288 .t-bench .note { margin-bottom: 1.4rem; }
    289 .row { margin: 0 0 1.25rem; }
    290 .row h3 { margin: 0 0 0.4rem; font-size: 0.94rem; font-weight: 700; }
    291 .meas {
    292   display: grid;
    293   grid-template-columns: 4.6rem 1fr 6.8rem;
    294   align-items: center;
    295   gap: 0.7rem;
    296   margin: 0.3rem 0;
    297 }
    298 .who { font-size: 0.82rem; font-weight: 600; color: var(--muted); }
    299 .meas.us .who { color: var(--accent-ink); }
    300 .track { width: 100%; height: 1.05rem; display: block; }
    301 .fill { fill: var(--bar-other); transform-box: fill-box; transform-origin: left center; }
    302 .meas.us .fill { fill: var(--accent); }
    303 .val {
    304   font-family: var(--mono);
    305   font-variant-numeric: tabular-nums;
    306   font-size: 0.9rem;
    307   font-weight: 700;
    308   color: var(--muted);
    309   white-space: nowrap;
    310 }
    311 .meas.us .val { color: var(--accent-ink); }
    312 
    313 /* 0 바이트 타일 */
    314 .t-zero { display: flex; flex-direction: column; justify-content: center; gap: 0.5rem; }
    315 .zero-num {
    316   margin: 0;
    317   font-family: var(--mono);
    318   font-size: clamp(2.8rem, 5vw, 4rem);
    319   font-weight: 700;
    320   line-height: 1;
    321   background: var(--grad);
    322   -webkit-background-clip: text;
    323   background-clip: text;
    324   color: transparent;
    325 }
    326 
    327 /* 설치 명령 — 페이지에서 제일 중요한 것. CTA 바로 아래에서 눈에 띈다.
    328 
    329    탭과 명령을 카드 하나로 묶는다. 둘을 떼어 놓으면 화면에 떠 있는 조각이
    330    둘이 되고, 무엇이 무엇을 바꾸는지가 근접성으로만 설명된다. */
    331 .install {
    332   display: inline-flex;
    333   flex-direction: column;
    334   align-items: stretch;
    335   max-width: 100%;
    336   border: 1px solid var(--line);
    337   border-radius: 15px;
    338   background: var(--bg-hi);
    339   padding: 0;
    340   margin: 1.6rem 0 0;
    341   min-width: 0;
    342   overflow: hidden;
    343 }
    344 .pick-tabs {
    345   position: relative;
    346   display: flex;
    347   gap: 0.15rem;
    348   padding: 0.32rem;
    349   border-bottom: 1px solid var(--line);
    350 }
    351 .pick-in {
    352   position: absolute;
    353   width: 1px;
    354   height: 1px;
    355   opacity: 0;
    356   pointer-events: none;
    357 }
    358 .pick-tab {
    359   position: relative;
    360   z-index: 1;
    361   cursor: pointer;
    362   font-family: var(--mono);
    363   font-size: 0.8rem;
    364   line-height: 1;
    365   color: var(--muted);
    366   padding: 0.44rem 0.8rem;
    367   border-radius: 9px;
    368   transition: color 0.18s;
    369 }
    370 .pick-tab:hover { color: var(--ink); }
    371 .pick-in:checked + .pick-tab { color: var(--accent-ink); }
    372 /* JS가 없을 때의 선택 표시. 표시자가 살아나면 이 배경은 넘겨준다. */
    373 .pick-in:checked + .pick-tab { background: rgba(255, 120, 70, 0.12); }
    374 .install.is-enhanced .pick-in:checked + .pick-tab { background: transparent; }
    375 /* 라디오가 화면에 없으므로 포커스 링은 라벨이 받는다. 없으면 키보드로 못 쓴다. */
    376 .pick-in:focus-visible + .pick-tab {
    377   outline: 2px solid var(--accent);
    378   outline-offset: 2px;
    379 }
    380 /* 선택을 따라 미끄러지는 표시자. 이동하는 동안 진행 방향으로 눌렸다 펴진다 —
    381    히어로의 squeeze와 같은 동작이고, 이 제품이 하는 일 자체다. */
    382 .pick-marker {
    383   position: absolute;
    384   top: 0.32rem;
    385   left: 0;
    386   height: calc(100% - 0.64rem);
    387   border-radius: 9px;
    388   background: rgba(255, 120, 70, 0.14);
    389   box-shadow: inset 0 0 0 1px rgba(255, 140, 90, 0.4);
    390   opacity: 0;
    391   pointer-events: none;
    392   transform-origin: center;
    393 }
    394 .install.is-enhanced .pick-marker { opacity: 1; }
    395 
    396 /* 패널을 격자 한 칸에 겹쳐 쌓는다. 카드 폭이 가장 긴 명령에 맞춰 고정되므로
    397    탭을 옮길 때마다 상자가 늘었다 줄었다 하지 않는다.
    398    숨김은 display가 아니라 visibility다 — 폭 계산에는 남아 있어야 한다. */
    399 .pick-panels {
    400   display: grid;
    401   min-width: 0;
    402   max-width: 100%;
    403 }
    404 .pick-panel {
    405   grid-area: 1 / 1;
    406   display: flex;
    407   align-items: center;
    408   gap: 0.6rem;
    409   min-width: 0;
    410   padding: 0.78rem 0.6rem 0.78rem 1.05rem;
    411   visibility: hidden;
    412   opacity: 0;
    413   transform-origin: left center;
    414 }
    415 /* 복사 버튼은 카드 오른쪽 끝에 붙는다. 명령 길이를 따라가면 탭을 옮길 때마다
    416    버튼이 300px씩 이동하는데, 누르려던 것이 도망가는 셈이다. */
    417 .pick-panel > .inst-copy { margin-left: auto; }
    418 /* 첫 패널이 기본이다. `:has()`를 모르는 브라우저에서도 curl 명령은 그대로
    419    보이고 탭만 안 먹는다 — 이 페이지에서 가장 중요한 내용이 사라지면 안 된다.
    420    채널이 셋인 건 packaging/README.md의 결정이고, 늘리면 여기도 는다. */
    421 .pick-panel:first-child { visibility: visible; opacity: 1; }
    422 .install:has(#inst-2:checked) .pick-panel,
    423 .install:has(#inst-3:checked) .pick-panel { visibility: hidden; opacity: 0; }
    424 .install:has(#inst-2:checked) .pick-panel:nth-child(2),
    425 .install:has(#inst-3:checked) .pick-panel:nth-child(3) { visibility: visible; opacity: 1; }
    426 
    427 .install-note { margin: 0.7rem 0 0; font-size: 0.82rem; color: var(--muted); }
    428 /* 설치 카드보다 조용해야 한다. 대부분의 방문자는 셸에 붙여 넣지 에이전트에
    429    붙여 넣지 않으므로, 있는 걸 알아볼 수는 있되 먼저 눈에 들어오면 안 된다. */
    430 .install-agent { margin: 0.55rem 0 0; }
    431 .agent-copy {
    432   display: inline-flex;
    433   align-items: center;
    434   gap: 0.34rem;
    435   white-space: nowrap;
    436   padding: 0.34rem 0.66rem;
    437   border: 1px solid var(--line);
    438   border-radius: 999px;
    439   background: transparent;
    440   color: var(--muted);
    441   font: inherit;
    442   font-size: 0.78rem;
    443   line-height: 1.2;
    444   cursor: pointer;
    445   transition: color 0.15s, border-color 0.15s;
    446 }
    447 .agent-copy:hover { color: var(--ink); border-color: var(--ink-3); }
    448 .agent-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    449 .agent-copy.is-done { color: var(--accent-ink); border-color: rgba(255, 140, 90, 0.55); }
    450 .agent-copy svg { flex: none; }
    451 
    452 .pick-panel > code {
    453   font-family: var(--mono);
    454   font-size: 0.92rem;
    455   font-weight: 700;
    456   color: var(--accent-ink);
    457   white-space: nowrap;
    458   overflow-x: auto;
    459   scrollbar-width: none;
    460 }
    461 /* 화면에서는 안 보이고 스크린리더에는 읽히는 것. fieldset의 legend가 그렇다 —
    462    눈으로는 버튼 행 자체가 무엇을 고르는지 말해 주지만, 소리로는 아니다. */
    463 .sr-only {
    464   position: absolute;
    465   width: 1px;
    466   height: 1px;
    467   padding: 0;
    468   margin: -1px;
    469   overflow: hidden;
    470   clip-path: inset(50%);
    471   white-space: nowrap;
    472   border: 0;
    473 }
    474 .inline { font-family: var(--mono); font-size: 0.85em; color: var(--accent-ink); }
    475 .inst-copy {
    476   flex: none;
    477   display: inline-flex;
    478   align-items: center;
    479   justify-content: center;
    480   width: 2rem;
    481   height: 2rem;
    482   border: 0;
    483   border-radius: 8px;
    484   background: transparent;
    485   color: var(--muted);
    486   cursor: pointer;
    487 }
    488 .inst-copy:hover { color: var(--accent-ink); background: var(--panel-hi); }
    489 .inst-copy:active { transform: scale(0.9); }
    490 .inst-copy .i-check { display: none; }
    491 .inst-copy.is-done .i-copy { display: none; }
    492 .inst-copy.is-done .i-check { display: block; color: var(--accent-ink); }
    493 
    494 /* ── 푸터 — docs와 같은 colophon. 마크업은 partials/colophon.html 하나를
    495    양쪽이 include하고, 여기서는 main.css의 .colophon 블록을 랜딩의 토큰
    496    (--line·--text·--muted·--wrap)으로 옮겨 적는다. 값의 근거는 main.css 쪽
    497    주석에 있다. docs의 회색 두 단(ink-2/ink-3)은 랜딩엔 --muted 하나라
    498    한 단으로 눕는다. */
    499 .colophon {
    500   border-top: 1px solid var(--line);
    501   max-width: var(--wrap);
    502   margin: 0 auto;
    503   padding: 2.5rem clamp(1.25rem, 4vw, 2.5rem) 3.5rem;
    504   font-size: 0.8125rem;
    505 }
    506 .colophon-grid {
    507   display: grid;
    508   grid-template-columns: minmax(0, 20rem) repeat(2, minmax(0, 9rem));
    509   justify-content: start;
    510   gap: clamp(1.5rem, 5vw, 4rem);
    511 }
    512 .colophon-mark {
    513   display: inline-flex;
    514   align-items: center;
    515   gap: 0.5rem;
    516   font-family: var(--mono);
    517   font-size: 1.0625rem;
    518   font-weight: 600;
    519   letter-spacing: -0.03em;
    520   color: var(--muted);
    521 }
    522 .colophon-mark:hover { color: var(--text); text-decoration: none; }
    523 .colophon-brand img { width: 1.7rem; height: 1.7rem; opacity: 0.6; }
    524 :root[data-theme="dark"] .colophon-brand img { filter: invert(1); }
    525 .colophon-brand p { margin: 0.6rem 0 0; max-width: 24rem; color: var(--muted); line-height: 1.6; }
    526 .colophon h2 {
    527   margin: 0 0 0.7rem;
    528   font-family: var(--mono);
    529   font-size: 0.72rem;
    530   font-weight: 500;
    531   letter-spacing: 0.08em;
    532   text-transform: uppercase;
    533   color: var(--muted);
    534 }
    535 .colophon ul { list-style: none; margin: 0; padding: 0; }
    536 .colophon li + li { margin-top: 0.4rem; }
    537 .colophon a { color: var(--muted); transition: color 0.14s ease; }
    538 .colophon a:hover { color: var(--accent-ink); text-decoration: underline; }
    539 
    540 /* ── 한국어: 자간·모노·대문자 장치를 한글에 적용하지 않는다 ── */
    541 html[lang="ko"] .display { letter-spacing: 0; }
    542 html[lang="ko"] .kicker { font-family: var(--sans); font-size: 0.85rem; }
    543 html[lang="ko"] .colophon h2 { font-family: var(--sans); letter-spacing: -0.01em; text-transform: none; }
    544 html[lang="ko"] h1, html[lang="ko"] h2, html[lang="ko"] h3 { word-break: keep-all; }
    545 html[lang="ko"] .sub, html[lang="ko"] .note { word-break: keep-all; }
    546 
    547 /* ── 반응형 ── */
    548 @media (max-width: 62rem) {
    549   .bento { grid-template-columns: repeat(2, 1fr); }
    550   .t-bench { grid-column: span 2; }
    551   .nav-links > a:first-child { display: none; }
    552   .colophon-grid { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
    553 }
    554 @media (max-width: 42rem) {
    555   .bento { grid-template-columns: 1fr; }
    556   .t-bench, .t-install, .t-demo { grid-column: span 1; }
    557   .meas { grid-template-columns: 3.9rem 1fr 6rem; gap: 0.5rem; }
    558   .display { font-size: clamp(2.3rem, 11vw, 3rem); }
    559   .blob { right: -16rem; opacity: 0.18; }
    560 }
    561 
    562 @media (prefers-reduced-motion: reduce) {
    563   html { scroll-behavior: auto; }
    564   .btn, .tile, .inst-copy { transition: none; }
    565   .blob { animation: none; }
    566 }