:root{--radius-xs:5px;--radius-sm:8px;--radius-md:13px;--radius-lg:21px;--radius-pill:999px;--radius-circle:50%;--bg:#fbfbf9;--surface:#fcfcfb;--text:#1c1917; --logo-color:#293133;--text-muted:#6f6b64;--border:#dfdbd3;--accent:#286aec;--on-accent:#ffffff;--accent-weak:#f7faff;--shadow:0 1px 2px rgba(16,24,40,0.04),0 8px 24px rgba(16,24,40,0.06);--space-1:8px;--space-2:13px;--space-3:21px;--space-4:34px;--space-5:55px}
*{box-sizing:border-box}
html,body{margin:0;padding:0;background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.5;min-height:100vh;display:flex;flex-direction:column}
main{flex:1;width:100%;max-width:880px;margin:0 auto;padding:var(--space-3) var(--space-2)}
h1{font-size:28px;line-height:1.2;margin:0 0 var(--space-2)}
h2{font-size:20px;line-height:1.3;margin:var(--space-4) 0 var(--space-1)}
h3{font-size:16px;line-height:1.4;margin:var(--space-2) 0 var(--space-1)}
p{margin:0 0 var(--space-2)}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}


/* Topnav + brand — mirrors d4t0 portal pattern (home/style.css .menu, .menu-btn).
   Two nested boxes, same pattern as main -> article below: the outer box
   matches main's 880px+padding exactly, and .cy-topnav-inner matches
   article's 760px cap, so the logo/pill align with the page's actual
   text column instead of main's wider padded box. */
.cy-topnav{padding:var(--space-2) var(--space-2);max-width:880px;margin:0 auto;width:100%}
.cy-topnav-inner{display:flex;align-items:center;justify-content:space-between;max-width:760px;margin:0 auto;width:100%}
.cy-logo{display:flex;align-items:center;gap:8px;color:var(--text)}
/* 36×36, khớp đúng hai nút điều hướng bên cạnh (cy-menu-btn /
   cy-lang-switch).

   Trước 21/08/2026 chỗ này là 25.5px kèm transform: rotate(45deg) — xoay làm
   hộp bao phình lên 25.5×√2 ≈ 36, nên phải đặt 25.5 để ra đúng 36. Nay mark
   đã xoay sẵn NGAY TRONG logo.svg (xem LOGO_ROTATE ở branding/build-icons.py)
   và được thu vừa khung, nên xoay thêm ở đây là xoay hai lần thành 90°. Bỏ cả
   transform lẫn phép bù kích thước. */
.cy-logo-mark{width:36px;height:36px;display:inline-block;background-color:var(--logo-color);-webkit-mask-image:url("/logo.svg");mask-image:url("/logo.svg");-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}
/* This used to be an Apple-style floating pill: the language switch and the
   hamburger sharing one rounded background, one 1.5px border, one two-layer
   shadow, with a 1px divider between them drawn as a border-left on the button.

   Gone as of 22/08/2026 (repo-wide — branding/chrome.css). That pill was
   exactly the box the standard removes: "hamburger has no border and no fill",
   and here the border did not even belong to the button, which is why it needs
   saying twice. The two controls now sit side by side as plain controls, and
   each keeps its own hover state to carry the affordance.

   Still no "overflow: hidden" on the group — the dropdown panel is an
   absolutely-positioned child (right: 0; top: calc(100% + 8px)) and would be
   clipped by it. */
.cy-actions-pill{display:flex;align-items:center;gap:2px}
.cy-lang-switch{display:flex;align-items:center;gap:5px;padding:0 12px;height:36px;border-radius:var(--radius-sm);color:var(--text-muted);font-size:13px;font-weight:500;transition:color .15s;text-decoration:none}
.cy-lang-switch:hover{color:var(--accent);text-decoration:none}
.cy-lang-switch svg{flex:none}
.cy-menu{position:relative}
.cy-menu-btn{position:relative;width:44px;height:36px;background:transparent;border:none;border-radius:var(--radius-sm);cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;transition:background .15s}
.cy-menu-btn:hover{background:var(--bg)}
.cy-menu-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cy-menu-btn span{position:absolute;left:50%;top:50%;display:block;width:16px;height:1.5px;margin:-0.75px 0 0 -8px;background:var(--text);border-radius:1px;transition:transform .15s}
.cy-menu-btn span:nth-child(1){transform:translateY(-4px)}
.cy-menu-btn span:nth-child(2){transform:translateY(3px)}
.cy-menu-btn[aria-expanded="true"]{background:var(--accent-weak)}
.cy-menu-btn[aria-expanded="true"] span{background:var(--accent)}
.cy-menu-btn[aria-expanded="true"] span:nth-child(1){transform:rotate(45deg)}
.cy-menu-btn[aria-expanded="true"] span:nth-child(2){transform:rotate(-45deg)}
.cy-menu-panel{position:absolute;right:0;top:calc(100% + 8px);min-width:200px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow);padding:8px;display:flex;flex-direction:column;gap:4px;z-index:50}
/* CLAUDE.md gotcha #1: explicit hidden override — without this,
   .cy-menu-panel { display: flex } silently wins over the UA default
   [hidden] { display: none } and the menu is always visible. */
.cy-menu-panel[hidden]{display:none}
.cy-menu-panel a{padding:8px 12px;border-radius:var(--radius-xs);color:var(--text);font-size:14px}
.cy-menu-panel a:hover{background:var(--accent-weak);text-decoration:none;color:var(--accent)}
.cy-menu-panel a.active{background:var(--accent-weak);color:var(--accent);font-weight:500}
.cy-menu-divider{height:1px;background:var(--border);margin:4px 0}

/* "Info" disclosure inside the nav, holding About / Contact / Privacy.
   Those three used to be a second footer line, which CLAUDE.md §Footer forbids;
   flat in the nav they made eleven items and overflowed the 760px column the
   topnav shares with the article. One disclosure brings the row back to nine.

   Two shapes, same markup: inside the vertical dropdown (mobile) it expands in
   place as an indented block, because a popup inside an already-absolute panel
   has nowhere to go. In the horizontal row (desktop) it is a normal popup
   hanging under its button. */
.cy-navmore{position:relative}
.cy-navmore-btn{display:flex;align-items:center;gap:4px;width:100%;padding:8px 12px;border:none;border-radius:var(--radius-xs);background:transparent;color:var(--text);font:inherit;font-size:14px;text-align:left;cursor:pointer}
.cy-navmore-btn:hover{background:var(--accent-weak);color:var(--accent)}
.cy-navmore-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cy-navmore-btn svg{flex:none;transition:transform .15s}
.cy-navmore-btn[aria-expanded="true"] svg{transform:rotate(180deg)}
.cy-navmore-panel{display:flex;flex-direction:column;gap:2px;padding:2px 0 2px 12px}
/* Explicit [hidden] override — setting display on a class that can carry the
   hidden attribute beats the UA default and un-hides it. CLAUDE.md gotcha #1. */
.cy-navmore-panel[hidden]{display:none}
.cy-navmore-panel a{padding:8px 12px;border-radius:var(--radius-xs);color:var(--text-muted);font-size:14px}
.cy-navmore-panel a:hover{background:var(--accent-weak);text-decoration:none;color:var(--accent)}

@media (min-width:960px){
  .cy-navmore-btn{width:auto;padding:8px 7px;font-size:13px;white-space:nowrap}
  .cy-navmore-panel{position:absolute;right:0;top:calc(100% + 8px);z-index:60;min-width:170px;padding:8px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow)}
  .cy-navmore-panel[hidden]{display:none}
}

/* Desktop: no hamburger, links laid out in a row (branding/chrome.css).
   CSS only — button and panel keep their ids, so the inlined toggle script
   still drives the dropdown below the breakpoint.

   960px, not the 720px standard, and the standard says to raise rather than
   lower: this nav is eight items ("N cubic yards", "Conversions", …), not the
   three or four every other project carries, and .cy-topnav-inner caps the row
   at 760px. At 720px the row overflows the logo. */
@media (min-width:960px){
  .cy-menu-btn{display:none}
  /* Both selectors: [hidden] is a UA default that a bare class rule loses to
     (gotcha #1 again, from the other side). */
  .cy-menu-panel,.cy-menu-panel[hidden]{display:flex}
  .cy-menu-panel{position:static;flex-direction:row;align-items:center;min-width:0;padding:0;gap:0;background:none;border:none;box-shadow:none}
  /* 7px, not the 9px the other projects use. The row has nine items and shares
     a 760px cap with the article column -- and that cap does not grow with the
     viewport, so a wider screen buys nothing here and raising the breakpoint
     fixes nothing. Vietnamese is the binding case ("Trang chủ", "Hướng dẫn",
     "N khối (yd³)"): at 9px it came to 727px against a ~700px budget and sat
     flush against the logo. Measured, not guessed. */
  .cy-menu-panel a{padding:8px 7px;font-size:13px;white-space:nowrap}
  .cy-menu-divider{width:1px;height:14px;margin:0 4px}
}

/* Footer */
/* Centred (22/08/2026, repo-wide — branding/chrome.css). */
.cy-footer{padding:var(--space-3) var(--space-2);text-align:center;color:var(--text-muted);font-size:13px;border-top:1px solid var(--border);background:var(--bg);margin-top:auto}
.cy-footer p{margin:0}
.cy-footer-copyright{margin-bottom:4px}
.cy-footer a{color:var(--text-muted)}
.cy-footer a:hover{color:var(--text)}

/* Article */
article{max-width:760px;margin:0 auto}
.cy-lede{font-size:18px;line-height:1.5;color:var(--text-muted)}

/* Calculator */
.cy-calc{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-3);box-shadow:var(--shadow);margin:var(--space-3) 0}
.cy-modes{display:flex;gap:4px;margin-bottom:var(--space-2);flex-wrap:wrap}
.cy-mode{padding:8px 12px;border:1px solid transparent;border-radius:var(--radius-xs);background:var(--bg);color:var(--text);cursor:pointer;font-size:14px}
.cy-mode.active{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
.cy-shapes{display:grid;grid-template-columns:repeat(6,1fr);gap:6px;margin-bottom:var(--space-2)}
@media (max-width:600px){.cy-shapes{grid-template-columns:repeat(4,1fr)}}
/* Border only on the active tile (a meaningful signal), not on all 12 —
   a 1px solid on every tile in a 6x2 grid reads as a wall of boxes.
   transparent (not "none") keeps the same box size so nothing shifts
   when a tile becomes active. */
.cy-shape{display:flex;flex-direction:column;align-items:center;padding:6px;border:1px solid transparent;border-radius:var(--radius-sm);background:var(--bg);color:var(--text-muted);cursor:pointer;font-size:11px;gap:2px}
.cy-shape.active{background:var(--accent-weak);border-color:var(--accent);color:var(--accent)}
.cy-shape svg{width:24px;height:24px}
.cy-shape-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.cy-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:var(--space-2);margin-bottom:var(--space-2)}
.cy-grid-3{grid-template-columns:repeat(auto-fit,minmax(120px,1fr))}
.cy-field{display:flex;flex-direction:column;gap:4px}
.cy-field-label{font-size:12px;color:var(--text-muted);font-weight:500}
.cy-field-row{display:flex;align-items:center;gap:6px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:6px 10px}
.cy-field-row input,.cy-field-row select{flex:1;border:none;background:none;font:inherit;color:var(--text);outline:none;min-width:0;padding:0}
.cy-field-row input:focus-visible,.cy-field-row select:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.cy-field-unit{color:var(--text-muted);font-size:14px;white-space:nowrap}
.cy-more{margin-bottom:var(--space-2);padding:var(--space-2);background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm)}
.cy-more summary{cursor:pointer;font-weight:500;color:var(--text-muted)}
.cy-results{background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:var(--space-2);margin-top:var(--space-2)}
.cy-results.is-empty{opacity:0.5}
.cy-result-primary{display:flex;align-items:baseline;gap:8px;padding-bottom:var(--space-2);border-bottom:1px solid var(--border);margin-bottom:var(--space-1)}
.cy-result-primary .cy-result-label{color:var(--text-muted);font-size:14px}
.cy-result-primary .cy-result-value{font-size:36px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text);line-height:1.1}
.cy-result-primary .cy-result-unit{color:var(--text-muted);font-size:18px}
.cy-result-list{list-style:none;padding:0;margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:4px 16px}
.cy-result-list li{display:flex;justify-content:space-between;gap:8px;padding:4px 0;border-bottom:1px dashed var(--border);font-size:14px}
.cy-result-list li span{color:var(--text-muted)}
.cy-result-list li strong{color:var(--text);font-weight:500;font-variant-numeric:tabular-nums}
.cy-coverage{margin-top:var(--space-2);padding-top:var(--space-2);border-top:1px solid var(--border)}
.cy-coverage h3{font-size:14px;color:var(--text-muted);margin:0 0 6px}
.cy-coverage ul{list-style:none;padding:0;margin:0;display:flex;flex-wrap:wrap;gap:6px}
.cy-coverage li{background:var(--accent-weak);color:var(--accent);padding:4px 10px;border-radius:var(--radius-xs);font-size:13px;font-variant-numeric:tabular-nums}
.cy-actions{display:flex;gap:8px;margin-top:var(--space-2);flex-wrap:wrap}
.cy-btn{padding:8px 14px;border:1px solid var(--accent);border-radius:var(--radius-xs);background:var(--accent);color:var(--on-accent);cursor:pointer;font-size:14px}
.cy-btn:hover{background:#1d4fb0;border-color:#1d4fb0}
.cy-btn-secondary{background:transparent;color:var(--text);border-color:var(--border)}
.cy-btn-secondary:hover{background:var(--bg);color:var(--text);border-color:var(--text-muted)}

/* Converter (for conversion pages) */
.cy-conv{display:flex;align-items:end;gap:var(--space-2);background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-3);box-shadow:var(--shadow);margin:var(--space-2) 0}
.cy-conv-eq{font-size:24px;color:var(--text-muted);padding-bottom:8px}
.cy-conv-result{display:flex;flex-direction:column;gap:4px;flex:1}
.cy-conv-result .cy-result-value{font-size:32px;font-weight:600;font-variant-numeric:tabular-nums}

/* Tables */
.cy-table{width:100%;border-collapse:collapse;margin:var(--space-2) 0;font-size:14px}
.cy-table caption{text-align:left;padding:6px 0;color:var(--text-muted);font-size:13px}
.cy-table th,.cy-table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--border)}
.cy-table th{color:var(--text-muted);font-weight:500;font-size:12px;text-transform:uppercase;letter-spacing:0.04em}
.cy-table tbody tr:hover{background:var(--bg)}

/* Card grid (hubs) */
.cy-hub-list{list-style:none;padding:0;margin:var(--space-2) 0;display:flex;flex-wrap:wrap;gap:6px 16px}
.cy-hub-list li{margin:0}
.cy-hub-list a{color:var(--accent);text-decoration:none}
.cy-hub-list a:hover{text-decoration:underline}

/* Mat/conv/guide grids on home */
.cy-mat-grid,.cy-conv-grid,.cy-guide-grid{list-style:none;padding:0;margin:var(--space-2) 0;display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:6px}
.cy-mat-grid li,.cy-conv-grid li,.cy-guide-grid li{padding:4px 0}
.cy-mat-grid a,.cy-conv-grid a,.cy-guide-grid a{display:block;color:var(--text);font-weight:500}
.cy-mat-grid a:hover,.cy-conv-grid a:hover,.cy-guide-grid a:hover{color:var(--accent)}

/* Collapsible content sections (below the calculator) — icon + heading in
   <summary>, chevron rotates on open. Native <details>, zero JS. */
.cy-section{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);margin:var(--space-2) 0;overflow:hidden}
.cy-section summary{display:flex;align-items:center;gap:10px;padding:var(--space-2) var(--space-3);cursor:pointer;font-weight:500;list-style:none}
.cy-section summary::-webkit-details-marker{display:none}
.cy-section-icon{flex:none;display:flex;color:var(--text-muted)}
.cy-section-title{flex:1;min-width:0}
.cy-section-hint{font-weight:400;color:var(--text-muted);font-size:13px}
.cy-section-chevron{flex:none;display:flex;color:var(--text-muted);transition:transform .15s}
.cy-section[open] .cy-section-chevron{transform:rotate(180deg)}
.cy-section[open] summary{border-bottom:1px solid var(--border)}
.cy-section-body{padding:0 var(--space-3) var(--space-2)}
.cy-section-body>*:first-child{margin-top:var(--space-2)}
.cy-section-body table{margin-bottom:0}
/* Several collapsible()s in a row (see src/templates/collapsible.ts
   sectionGroup()) share one outer border/background instead of stacking
   N separately-bordered cards — a wall of 4-5 rounded rectangles in a row
   reads as "too many borders" on a narrow mobile column. */
.cy-section-group{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);margin:var(--space-2) 0;overflow:hidden}
.cy-section-group .cy-section{background:none;border:none;border-radius:0;margin:0;border-bottom:1px solid var(--border)}
.cy-section-group .cy-section:last-child{border-bottom:none}

/* FAQ — one bordered card with a hairline divider between questions,
   not N separately-bordered/rounded boxes. */
.cy-faq{margin:var(--space-2) 0;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}
.cy-faq details{padding:0 var(--space-3);border-bottom:1px solid var(--border)}
.cy-faq details:last-child{border-bottom:none}
.cy-faq summary{cursor:pointer;font-weight:500;padding:var(--space-2) 0;list-style:none}
.cy-faq summary::after{content:"+";float:right;color:var(--text-muted)}
.cy-faq details[open] summary::after{content:"−"}
.cy-faq p{margin:0 0 var(--space-2);color:var(--text-muted);font-size:14px}

/* Variant */
.cy-variant-nav{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-3);padding-top:var(--space-2);border-top:1px solid var(--border);flex-wrap:wrap}

/* Print */
@media print{
  header.cy-topnav,footer.cy-footer,.cy-actions,.cy-more,.cy-modes,.cy-shapes,.cy-conv,.cy3d{display:none !important}
  .cy-calc,.cy-conv-page,.cy-table{border:none;box-shadow:none;background:#fff}
  .cy-result-primary .cy-result-value{font-size:24px}
  body{font-size:11pt}
  /* Collapsible sections print open regardless of on-screen state, and
     drop the interactive chrome (summary::marker is already hidden). */
  .cy-section{border:none;background:none}
  .cy-section-body{display:block !important;padding:0}
  .cy-section-chevron{display:none}
}

/* ─── Mobile (iPhone 14 / 390px baseline; also covers SE 375, Mini 375, Plus 428, Pro Max 430) ─── */
@media (max-width:640px){
  /* Container & type scale — give the calculator room to breathe on small screens */
  main{padding:16px 14px}
  h1{font-size:24px}
  h2{font-size:18px;margin-top:24px}
  h3{font-size:15px}
  p{font-size:15px}
  .cy-lede{font-size:16px}

  /* Topnav — the logo text "D4T0" wraps to two lines on narrow screens;
     the mask-image icon already shows in the placeholder, so hide the
     accessible text and let the icon speak.

     The mark stays 36×36 here too. This rule used to shrink it to 25.5px, the
     last surviving piece of the pre-rotation compensation (25.5 × √2 ≈ 36);
     with the rotation baked into logo.svg that just shipped a small mark on
     phones. 36 is the size, at every width — branding/chrome.css. */
  .cy-logo{font-size:0}

  /* Lang switch: icon-only on narrow screens, drop the padding/gap the
     text label needed. */
  .cy-lang-switch{width:36px;padding:0;justify-content:center}
  .cy-lang-switch span{display:none}

  /* Collapsible sections: tighter padding to match the rest of the mobile pass. */
  .cy-section summary{padding:12px 14px;gap:8px}
  .cy-section-body{padding:0 14px 12px}

  /* Calculator primary result: 36px is fine on desktop, way too big for
     a 358px content column (the unit "yd³" gets pushed off the right edge). */
  .cy-result-primary .cy-result-value{font-size:24px}
  .cy-result-primary .cy-result-label{font-size:12px}
  .cy-result-primary .cy-result-unit{font-size:14px}

  /* Result list: 2-col grid (auto-fit minmax(180px,1fr)) leaves <170px per
     column on 390px viewports. "— half-ton pickups" doesn't fit. Stack
     to 1 column. */
  .cy-result-list{grid-template-columns:1fr;gap:0}

  /* Conversion page result: 32px is too big, "0.037" overflows the right edge. */
  .cy-conv-result .cy-result-value{font-size:22px}
  .cy-conv{display:flex-direction:column;align-items:stretch;gap:8px}
  .cy-conv-eq{align-self:center;transform:rotate(90deg);padding:0}

  /* Shape picker: 4 columns on small screens was right per v1, but the
     icon + label squeezed in 90px cells. Bump to 3 columns for legibility. */
  .cy-shapes{grid-template-columns:repeat(3,1fr);gap:6px}
  .cy-shape{padding:8px 4px}
  .cy-shape svg{width:22px;height:22px}
  .cy-shape-label{font-size:10px}

  /* Tables: shrink padding + font, and let the table itself scroll
     horizontally if the content is wider than the column (e.g. the
     homepage density chart and truck-capacity table). display:block
     makes the overflow-x: auto actually take effect on a <table>. */
  .cy-table{display:block;font-size:13px;overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
  .cy-table th,.cy-table td{padding:5px 8px;white-space:nowrap}
  .cy-table caption{font-size:12px}

  /* Hub card grids — let them stack to 1 column under 480px for the
     10-card materials hub. The auto-fit / minmax already does most of
     this; just tighten the gap. */
  .cy-mat-grid,.cy-conv-grid,.cy-guide-grid{gap:6px}
  .cy-mat-grid,.cy-conv-grid,.cy-guide-grid{grid-template-columns:1fr 1fr}

  /* Visual scale: 3 items at 390px is tight; the SVG icons are too big. */
  .cy-scale{gap:14px}
  .cy-scale li{align-items:center}
  .cy-scale-svg svg{width:48px;height:auto}

  /* Worked example cards: 1 column on mobile. */
  .cy-examples{grid-template-columns:1fr}

  /* Coverage chips in calculator result — wrap to 2 columns. */
  .cy-coverage ul{gap:4px}
  .cy-coverage li{font-size:12px;padding:3px 8px}

  /* FAQ: slightly tighter padding. */
  .cy-faq details{padding:10px 12px}

  /* Conversion "two-way converter" — stack the input + result vertically. */
  .cy-conv{flex-direction:column;align-items:stretch}

  /* Actions row: full-width buttons on mobile. */
  .cy-actions{flex-direction:column}
  .cy-btn{width:100%;padding:12px}

  /* "Pick a material" / hub link lists on homepage: stay 2-col, smaller
     font. */
  .cy-mat-grid a,.cy-conv-grid a,.cy-guide-grid a{font-size:13px}

  /* 3D preview on mobile: square still, just smaller (capped at 320px
     so it doesn't dominate the small screen). Aspect ratio reverts
     to 1:1 here because 16:9 on a 320px-wide phone would be a thin
     strip; centered again because there's no longer a wide canvas
     to fill. */
  .cy3d{max-width:320px;aspect-ratio:1;margin:8px auto 16px}
}

/* ── 3D shape preview (Three.js WebGL) ── */
.cy3d{position:relative;width:100%;aspect-ratio:16/9;margin:0;overflow:hidden;background:linear-gradient(180deg,var(--surface) 0%,var(--bg) 100%);touch-action:none}
.cy3d canvas{display:block;width:100%!important;height:100%!important;cursor:grab}
.cy3d canvas:active{cursor:grabbing}
.cy3d-controls{position:absolute;bottom:8px;right:8px;z-index:2}
.cy3d-controls button{appearance:none;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-xs);width:30px;height:30px;cursor:pointer;color:var(--text);font-size:14px;line-height:1;display:flex;align-items:center;justify-content:center;font-family:ui-monospace,SFMono-Regular,monospace;box-shadow:0 1px 2px rgba(0,0,0,0.06)}
.cy3d-controls button:hover{background:var(--accent-weak);color:var(--accent);border-color:var(--accent)}
.cy3d-controls button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.cy3d-controls button.on{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}

/* 3D preview — toolbar layout (reset + rotate + fullscreen + cross-section buttons) */
.cy3d-controls{display:flex;gap:4px}
.cy3d-controls button{appearance:none;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-xs);width:30px;height:30px;cursor:pointer;color:var(--text);font-size:14px;line-height:1;display:flex;align-items:center;justify-content:center;font-family:ui-monospace,SFMono-Regular,monospace;box-shadow:0 1px 2px rgba(0,0,0,0.06)}
.cy3d-controls button:hover{background:var(--accent-weak);color:var(--accent);border-color:var(--accent)}
.cy3d-controls button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.cy3d-controls button.on{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
.cy3d-controls button svg{width:14px;height:14px}

/* 3D preview — fullscreen state */
.cy3d:fullscreen,.cy3d:-webkit-full-screen{border:none;border-radius:0;background:var(--surface);width:100vw !important;height:100vh !important;max-width:100vw !important;aspect-ratio:auto}
.cy3d:fullscreen canvas,.cy3d:-webkit-full-screen canvas{width:100% !important;height:100% !important}

/* 3D preview — loading skeleton (covers Three.js init lag; CSS-only, no JS) */
.cy3d-skeleton{position:absolute;inset:0;background:linear-gradient(90deg,var(--bg) 0%,var(--accent-weak) 50%,var(--bg) 100%);background-size:200% 100%;animation:cy3d-shimmer 1.4s ease-in-out infinite;z-index:1}
@keyframes cy3d-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.cy3d-loaded .cy3d-skeleton{display:none}
@media (prefers-reduced-motion: reduce){.cy3d-skeleton{animation:none}}
/* WebGL unsupported fallback: keep skeleton visible and hide the now-meaningless chrome. */
.cy3d-not-supported .cy3d-controls,.cy3d-not-supported .cy3d-section-controls,.cy3d-not-supported .cy3d-labels{display:none}

/* 3D preview — cross-section controls (slider + label, top of canvas) */
.cy3d-section-controls{position:absolute;top:8px;left:8px;right:8px;display:flex;gap:8px;align-items:center;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-xs);padding:6px 8px;font-size:11px;color:var(--text-muted);z-index:2;pointer-events:auto;box-shadow:0 1px 2px rgba(0,0,0,0.06)}
.cy3d-section-controls input[type="range"]{flex:1;accent-color:var(--accent);min-width:0;height:14px}
.cy3d-section-controls span{font-variant-numeric:tabular-nums;min-width:36px;text-align:right}
.cy3d-section-controls span:first-of-type{color:var(--text);font-weight:500}

/* Round 8 layout: 3D preview lifted out of the main calculator card
   into its own card above it. The preview's mesh, controls, and labels
   are self-contained — we just give the card a frame and a small gap
   to the calculator card below. */
.cy-preview-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--space-2);box-shadow:var(--shadow);margin:var(--space-3) 0}
.cy-preview-card .cy3d{margin:0 auto 0}

/* 3D preview — dimension labels (HTML overlay, repositions on camera move) */
.cy3d-labels{position:absolute;inset:0;pointer-events:none;z-index:3;overflow:hidden}
.cy3d-label-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}
.cy3d-label-line{stroke:var(--accent);stroke-width:1.2;fill:none;opacity:.65}
.cy3d-label-dot{fill:var(--accent);stroke:var(--surface);stroke-width:1.5;opacity:.9}
.cy3d-label{position:absolute;background:var(--surface);border:1px solid var(--accent-weak);border-radius:var(--radius-xs);padding:3px 8px;font-size:12px;line-height:1.2;color:var(--text);font-variant-numeric:tabular-nums;font-weight:500;pointer-events:none;transform:translate(-50%,-50%);box-shadow:0 1px 2px rgba(0,0,0,.08),0 0 0 3px rgba(47,111,237,.06);white-space:nowrap;z-index:3}
.cy3d-label::after{content:"";position:absolute;width:7px;height:7px;background:var(--accent);border:2px solid var(--surface);border-radius:var(--radius-circle);top:-7px;left:-7px;box-shadow:0 1px 2px rgba(0,0,0,.15)}

/* 3D preview — print rule (extend existing @media print block; .cy3d already hidden, but new controls must hide too) */
@media print{
  .cy3d-skeleton,.cy3d-label,.cy3d-labels,.cy3d-section-controls{display:none !important}
}

/* 3D preview — mobile (iPhone 390px baseline): toolbar compact, smaller labels */
@media (max-width:640px){
  .cy3d-controls{gap:3px}
  .cy3d-controls button{width:28px;height:28px}
  .cy3d-controls button svg{width:12px;height:12px}
  .cy3d-section-controls{font-size:10px;padding:4px 6px}
  .cy3d-label{font-size:10px;padding:1px 4px}
}
