:root { --ink:#132033; --muted:#5d6977; --paper:#f7f8f5; --card:#fff; --line:#dbe2e5; --accent:#087e8b; --warn:#9a6517; --error:#a53131; --copper:#d7771f; --pi:#d1af46; }
* { box-sizing:border-box; }
[hidden] { display:none !important; }
body { margin:0; background:var(--paper); color:var(--ink); font-family:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,"Segoe UI",sans-serif; }
.masthead { min-height:104px; padding:22px clamp(24px,5vw,76px) 20px; display:grid; grid-template-columns:auto auto minmax(0,1fr) auto; grid-template-areas:"title modes . links" "tagline tagline tagline tagline"; align-items:center; gap:10px 30px; color:#fff; background:linear-gradient(110deg,#132033,#075c69); }
h1,h2,p { margin:0; } h1 { font-size:clamp(1.55rem,3.2vw,2.35rem); letter-spacing:-.04em; } h2 { font-size:1rem; letter-spacing:-.015em; }.eyebrow { font-size:.67rem; letter-spacing:.115em; font-weight:750; color:#63808a; }.masthead .eyebrow { color:#b9e6df; margin-bottom:6px; }.profile-badge { border:1px solid rgba(255,255,255,.45); padding:8px 11px; border-radius:999px; font-size:.76rem; white-space:nowrap; }
main { display:grid; grid-template-columns:minmax(270px,340px) minmax(0,1fr); max-width:1600px; margin:0 auto; min-height:calc(100vh - 104px); }.controls { padding:25px; background:#fbfcfa; border-right:1px solid var(--line); }.controls section { border-bottom:1px solid var(--line); padding:0 0 20px; margin:0 0 20px; }.controls section:last-of-type { border:none; padding-bottom:0; }.controls h2 { margin-bottom:13px; }input,select,button { font:inherit; }input,select { width:100%; min-height:35px; background:#fff; border:1px solid #bfcbd0; border-radius:5px; padding:6px 8px; color:var(--ink); } input:focus,select:focus { outline:2px solid #77cbd1; border-color:var(--accent); }.two-col { display:grid; grid-template-columns:1fr 1fr; gap:11px; }.hint,.local-note,.manufacturing-note { font-size:.76rem; line-height:1.45; color:var(--muted); }.hint { margin:-4px 0 12px; }.locked { background:#f0f4ef; border:1px solid #d7e1d8 !important; border-radius:7px; padding:13px !important; }.locked dl { margin:0; display:grid; grid-template-columns:1fr auto; gap:7px 8px; font-size:.72rem; }.locked dt { color:var(--muted); }.locked dd { margin:0; text-align:right; font-weight:700; }.primary { width:100%; min-height:42px; border:0; border-radius:6px; font-weight:800; background:var(--accent); color:#fff; cursor:pointer; }.primary:hover { background:#066d78; }.primary:disabled { opacity:.5; cursor:wait; }.local-note { margin-top:12px; }.workbench { padding:25px clamp(20px,4vw,58px) 45px; min-width:0; }.status { padding:12px 14px; border-left:4px solid #607d8b; border-radius:4px; background:#edf2f4; font-size:.86rem; }.status.is-ok { border-color:var(--accent); background:#e7f4f0; }.status.is-warning { border-color:var(--warn); background:#fff7e5; }.status.is-error { border-color:var(--error); background:#fff0ef; }.summary { margin:15px 0; padding:14px; background:var(--card); border:1px solid var(--line); border-radius:7px; }.summary .issue-list { margin:8px 0 0; padding-left:20px; color:#3d4b58; font-size:.8rem; }.previews { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px; margin-top:20px; }.board-card,.downloads { background:var(--card); border:1px solid var(--line); border-radius:8px; box-shadow:0 3px 14px rgba(25,43,54,.035); }.board-card header { min-height:82px; padding:15px 16px 10px; display:flex; justify-content:space-between; gap:8px; align-items:start; border-bottom:1px solid var(--line); }.board-card header .eyebrow { margin-bottom:4px; }.board-card header span { color:var(--muted); font-size:.72rem; text-align:right; }.canvas-wrap { min-height:430px; display:grid; place-items:center; overflow:hidden; color:var(--muted); background:radial-gradient(#edf0ec 1px,transparent 1px) 0 0/14px 14px; }.downloads { display:grid; grid-template-columns:1.2fr 1fr; gap:18px; align-items:center; margin-top:20px; padding:17px; }.downloads h2 { margin:3px 0 5px; }.downloads p:not(.eyebrow) { color:var(--muted); font-size:.78rem; line-height:1.45; }.download-actions { display:flex; flex-wrap:wrap; gap:9px; justify-content:flex-end; }.download-actions button { border:1px solid #9eafb5; background:#fff; color:var(--ink); border-radius:5px; min-height:36px; padding:0 10px; font-weight:700; font-size:.77rem; cursor:pointer; }.download-actions button:disabled { color:#9aa5aa; border-color:#d1d8da; cursor:not-allowed; }.manufacturing-note { grid-column:1/-1; border-top:1px solid var(--line); padding-top:12px; }
@media (max-width:980px) { main { grid-template-columns:1fr; }.controls { border-right:0; border-bottom:1px solid var(--line); }.previews { grid-template-columns:1fr; }.canvas-wrap { min-height:360px; }.downloads { grid-template-columns:1fr; }.download-actions { justify-content:flex-start; } }
@media (max-width:520px) { .masthead { padding:20px; }.profile-badge { white-space:normal; border-radius:12px; }.controls,.workbench { padding:20px; }.two-col { grid-template-columns:1fr; gap:0; } }

/* Form fields: limits and choices come from /api/default profile.limits.
   Messages show the client checks first, else the server's validation. */
.field { display:grid; gap:5px; margin-bottom:11px; align-content:start; }
.field-head { display:flex; flex-wrap:wrap; justify-content:space-between; align-items:baseline; gap:2px 6px; }
.field label { color:#354454; font-size:.76rem; font-weight:700; }
.field-range { color:var(--muted); font-size:.66rem; font-weight:500; }
.field-message { display:grid; gap:3px; font-size:.68rem; line-height:1.35; }
.field-message .is-error { color:var(--error); }
.field-message .is-warning { color:var(--warn); }
.field-message code { font-size:.62rem; }
input[aria-invalid="true"],select[aria-invalid="true"] { border-color:var(--error); background:#fff7f6; }
/* mm inputs step at the 1e-6 mm input resolution: no spinner; the arrow keys nudge instead. */
input.fine-step { appearance:textfield; -moz-appearance:textfield; }
input.fine-step::-webkit-inner-spin-button,input.fine-step::-webkit-outer-spin-button { -webkit-appearance:none; margin:0; }
.summary .hint { margin:6px 0 0; }
.summary .issue-list li { margin:3px 0; }
.summary .issue-list li.is-error code { color:var(--error); }
.summary .issue-list li.is-warning code { color:var(--warn); }
.summary .note-list { margin:8px 0 0; padding-left:20px; color:var(--muted); font-size:.74rem; }
.field-link { border:0; padding:0; background:none; color:var(--accent); font-weight:700; text-decoration:underline; cursor:pointer; }
.summary a,.field-path { color:var(--accent); font-weight:700; }

/* Stale state (D20): the form no longer matches the shown build. */
.stale-banner { margin-top:10px; padding:10px 14px; display:flex; flex-wrap:wrap; gap:8px 14px; align-items:center; justify-content:space-between; border-left:4px solid var(--warn); border-radius:4px; background:#fff7e5; font-size:.84rem; }
.stale-banner button { border:1px solid var(--warn); background:#fff; color:#6b4510; border-radius:5px; min-height:32px; padding:0 12px; font-weight:800; cursor:pointer; }
.workbench.is-stale .canvas-wrap,.workbench.is-stale .stacked-canvas,.workbench.is-stale .drc-grid { opacity:.5; filter:grayscale(.7); transition:opacity .15s; }
.workbench.is-stale .status,.workbench.is-stale .summary { opacity:.6; }

/* Preview cards: ARIA tabs plus zoom tools for the 2D view, in the view's frame since 2026-11-06 (.figure-tools, below). */
.preview-tabs,.mode-switch { display:flex; gap:7px; }
.preview-tabs button,.mode-switch button { cursor:pointer; border:1px solid transparent; border-radius:4px; background:transparent; color:var(--muted); padding:5px 8px; font-size:.72rem; font-weight:750; }
.preview-tabs button[aria-selected="true"],.mode-switch button[aria-pressed="true"] { color:#075c69; border-color:#b9d9db; background:#eef9f8; }
.zoom-tools { display:flex; align-items:center; gap:4px; font-size:.68rem; }
.zoom-tools.is-inactive { display:none; }
.zoom-tools button { min-width:28px; min-height:26px; border:1px solid #c7d3d7; border-radius:4px; background:#fff; color:var(--ink); font-weight:750; cursor:pointer; }
.zoom-tools button:disabled { color:#9aa5aa; cursor:not-allowed; }
.zoom-tools output { min-width:96px; text-align:right; color:var(--muted); font-variant-numeric:tabular-nums; }
.view-pane { width:100%; min-height:430px; display:grid; place-items:center; }
.view-pane:focus-visible { outline:2px solid #77cbd1; outline-offset:-2px; }
.zoom-pane { overflow:hidden; cursor:grab; touch-action:none; user-select:none; }
.zoom-pane.is-panning { cursor:grabbing; }
/* The padding keeps the screen-sized pin-1 ring visible beyond the view box. */
.zoom-pane svg { width:100%; min-width:0; height:430px; padding:12px; display:block; overflow:visible; }
.pin1-ring { fill:none; stroke:#b42318; stroke-width:2px; vector-effect:non-scaling-stroke; pointer-events:none; }
.pin1-ring.is-upper { stroke:#087e8b; }.pin1-ring.is-lower { stroke:#d7771f; }
/* The stacked view's rings are drawn above both boards (app.js drawPin1Rings); the ones in the boards place them, unseen. */
.assembly-board .pin1-ring { visibility:hidden; }
.pin1-overlay { position:absolute; inset:0; width:100%; height:100%; overflow:visible; pointer-events:none; z-index:2; }
.pin1-ring-drawn { fill:none; stroke-width:2px; }
.preview-help { margin:8px 2px 0; color:var(--muted); font-size:.72rem; }
.board-card.has-drc-failure { border-color:var(--error); }

/* A local oblique review is intentionally derived from the same SVG geometry.
   It is a rigid tilt of the top view, not a flex model or an EDA 3D viewer. */
.three-d-pane { overflow:hidden; perspective:900px; background:linear-gradient(155deg,#eef5f3,#dbe8e6); position:relative; }
.three-d-pane::after { content:"Rigid oblique tilt of the top view · not a flex model · PI stiffener (bottom side) in grey · thickness at least 2 px"; position:absolute; left:12px; bottom:10px; padding:4px 6px; font-size:.63rem; color:#3d555a; border-radius:3px; background:rgba(255,255,255,.72); }
/* The board box has the SVG's aspect ratio; fitOblique shrinks it until the tilt fits the pane. */
/* Its angles as it opens are app.js's OBLIQUE_HOME; a drag turns it (attachOrbit).  Two-sided: the copper side faces the
   camera as it opens, the other side (.board-back-face) shows where the drag turns the board over; the shadow is a leaf
   of its own (.board-shadow, below; each side's SVG had a drop-shadow before the views' light of 2026-11-05), since a
   filter on the board would flatten it into one double-sided picture. */
.oblique-board { position:relative; width:min(88%,calc(340px * var(--board-ratio,1.6))); transform:rotateX(var(--orbit-tilt,56deg)) rotateZ(var(--orbit-spin,-22deg)) translateY(-16px); transform-origin:center; transform-style:preserve-3d; }
.oblique-board svg { width:100%; min-width:0; height:auto; aspect-ratio:var(--board-ratio,auto); display:block; }
.oblique-board > svg { backface-visibility:hidden; }
.three-d-pane:has(.oblique-board) { cursor:grab; touch-action:none; user-select:none; }
.three-d-pane.is-orbiting { cursor:grabbing; }
/* A board's other side (app.js boardBackSvg): turned half round and its picture mirrored back, so its features lie under
   the copper side's; each side drawn only where it faces the camera. */
.board-back-face { position:absolute; inset:0; transform:translateZ(calc(var(--fpc-mm,0) * -1 * var(--depth-px,0px))) rotateY(180deg); backface-visibility:hidden; }
/* The board's thickness (app.js boardDepth): --depth-px is a millimetre of depth on screen, the board's own scale (100cqw
   is its view box width, --view-mm) or more, so that the FPC (--fpc-mm, 0.11 mm: half a pixel at the views' usual scale)
   is at least 2 px thick, the stiffener in proportion.  Its other side lies --fpc-mm behind the copper side; each wall
   ring (a constant 1.6 px wide) and the stiffener's outer face at its --depth-mm; never a pointer target, no shadow. */
.oblique-board,.assembly-board { container-type:inline-size; --depth-px:max(100cqw / var(--view-mm,1), 2px / var(--fpc-mm,1)); }
:is(.oblique-board,.assembly-board) svg.board-edge { position:absolute; inset:0; width:100%; height:100%; aspect-ratio:auto; overflow:visible;
  pointer-events:none; filter:none; transform:translateZ(calc(var(--depth-mm) * -1 * var(--depth-px))); }
/* The walls in the views' light (app.js boardDepth, lightView): each ring is split by the way its walls face, each part
   (--wall-light: its ring's --wall-0 ... --wall-11) the material's colour (--edge-paint: the polyimide's, the stiffener's)
   times the light on a wall facing that way, so the strip that faces the key is lit and the one turned from it in shade;
   the stiffener's outer face, a face of the board's other side, as that side (--back-light). */
.fpc-edge-ring,.fpc-stiffener-ring { fill:none; stroke-width:1.6; vector-effect:non-scaling-stroke; stroke-linejoin:round; stroke-linecap:round;
  stroke:color-mix(in srgb, var(--edge-paint) var(--wall-light,60%), #000); }
.fpc-edge-ring { --edge-paint:#f4d7a1; }
.fpc-stiffener-ring { --edge-paint:#667085; }
.fpc-stiffener-face { --edge-paint:#667085; fill:color-mix(in srgb, var(--edge-paint) var(--back-light,60%), #000); }
.board-back-face > svg { width:100%; height:100%; transform:scaleX(-1); }
/* Each side's light (app.js lightBoard, lightView): a leaf of the 3D view over the side's SVG, in its plane (the other
   side's: .is-back, as .board-back-face and its mirrored SVG lie) and with its view box, whose two paths are the board's
   outline less its cutouts (even-odd), so nothing is painted outside the board: the shade (black, or white where the side
   faces the lights better than at the home view) and the satin sweep, a gradient of --sheen (the light the coverlay gives
   back, tinted toward the art's amber) whose stops app.js sets as the view turns.  Never a pointer target, no shadow of
   its own; hidden with the plan view, which stays a flat plan. */
:is(.oblique-board,.assembly-face) > svg:is(.board-light,.board-shadow) { position:absolute; inset:0; width:100%; height:100%; aspect-ratio:auto; pointer-events:none;
  filter:none; backface-visibility:hidden; --sheen:#ffeac6; }
:is(.oblique-board,.assembly-face) > svg.board-light.is-back { transform:translateZ(calc(var(--fpc-mm,0) * -1 * var(--depth-px,0px))) rotateY(180deg) scaleX(-1); }
.board-light stop { stop-color:var(--sheen); }
.board-shadow path { fill:rgb(13 43 45); }
.stacked-canvas.is-plan :is(.board-light,.board-shadow) { display:none; }
/* The shadow on the table, cast away from the views' key light (app.js lightBoard, lightView; the user's request of
   2026-11-05: the light of the sensor on the part in every 3D view): the board's outline blurred as its sides'
   drop-shadow was (--shadow-blur, the Gaussian's deviation: 4.5 px; sRGB 13 43 45 at 28 %), a leaf just behind its
   copper side drawn from both of its sides, moved in the board's plane by --shadow-u / --shadow-v (a share of the length
   it has on screen at the view's home, 21 px as before) and faded by --shadow-k, so that on screen it falls down and to
   the right whatever the turn (before, it turned with the board, up and to the left once the board had spun half round). */
.oblique-board { --shadow-blur:4.5px; }
.oblique-board > svg.board-shadow { overflow:visible; opacity:calc(.28 * var(--shadow-k,1)); backface-visibility:visible;
  transform:translateZ(-.5px) translate(calc(var(--shadow-u,.034) * 21px), calc(var(--shadow-v,1.539) * 21px)); }
.board-back .outline { fill:#ecd3a3; }
.board-back :is(.electrode,.pad) { fill:#d9672b; stroke:none; opacity:.32; }
.board-back .route { opacity:.32; }
.board-back .stiffener { fill:#667085; opacity:.85; }
.board-back .pin1-back { fill:#b42318; opacity:.6; }

/* Registered assembly review: the physical top board is upper and turned over
   about its tail axis (copper down, tail still on its exit side), the bottom
   board lower and face up; both are placed on the taxel field
   (data-active-region of each SVG). */
.stacked-review { margin-top:18px; }
.stacked-review header { min-height:66px; padding:15px 16px 10px; display:flex; justify-content:space-between; gap:8px; align-items:start; border-bottom:1px solid var(--line); }
.stacked-review header .eyebrow { margin-bottom:4px; }
.stacked-review header span { color:var(--muted); font-size:.72rem; line-height:1.4; text-align:right; }
.stacked-canvas { height:560px; display:grid; place-items:center; overflow:hidden; color:var(--muted); background:linear-gradient(155deg,#eef5f3,#dbe8e6); position:relative; }
/* The tilted camera shows about half the scene height; the plan view shows all of it. */
.assembly-scene { width:min(74%,620px,calc(520px * var(--assembly-ratio))); aspect-ratio:var(--assembly-aspect); position:relative; perspective:1050px; transform-style:preserve-3d; }
/* Its angles as the view opens are app.js's ORBIT_HOME; a drag sets them (attachOrbit). */
.assembly-camera { position:absolute; inset:0; transform-style:preserve-3d; transform:rotateX(var(--orbit-tilt,57deg)) rotateZ(var(--orbit-spin,-20deg)) scale(.82); transform-origin:center; }
.stacked-canvas:not(.is-plan):has(.assembly-camera) { cursor:grab; touch-action:none; user-select:none; }
.stacked-canvas.is-orbiting { cursor:grabbing; }
.stacked-canvas:focus-visible { outline:2px solid #087e8b; outline-offset:-2px; }
.assembly-board { position:absolute; transform-style:preserve-3d; }
.assembly-board svg { width:100%; height:100%; min-width:0; display:block; overflow:visible; }
.assembly-board .view-label { display:none; }
/* The shadows and the upper board's translucency are on the SVGs: on the board or its face they would flatten it, and its
   turned-over face would show the copper side from behind instead of the board's other side. */
.assembly-board.is-lower { transform:translateZ(0); }
.assembly-board.is-upper { transform:translateZ(96px); }
.assembly-board.is-upper svg { opacity:.78; }
/* The shadows, cast away from the key as the oblique view's (app.js lightBoard, lightView: --shadow-u / --shadow-v in the
   boards' plane, a share of their length on screen at the home view, 15.6 and 13.8 px before the camera's scale, blurred
   and dark as the sides' drop-shadows were, the top FPC's under its translucency); the turned-over top FPC's leaf mirrored
   as its face is (flip-x y, flip-y x).  Before, the sides' drop-shadow rule had the weight of the walls' filter:none, so
   that every wall ring cast a shadow too, nine a board. */
.assembly-face > svg.board-shadow { backface-visibility:visible; }
.assembly-board.is-lower { --shadow-blur:4px; }
.assembly-board.is-upper { --shadow-blur:3.5px; }
.assembly-board.is-lower .assembly-face > svg.board-shadow { opacity:calc(.28 * var(--shadow-k,1));
  transform:translateZ(-.5px) translate(calc(var(--shadow-u,.075) * 15.6px), calc(var(--shadow-v,1.576) * 15.6px)); }
.assembly-board.is-upper.flip-x .assembly-face > svg.board-shadow { opacity:calc(.78 * .23 * var(--shadow-k,1));
  transform:translateZ(-.5px) translate(calc(var(--shadow-u,.075) * 13.8px), calc(var(--shadow-v,1.576) * -13.8px)); }
.assembly-board.is-upper.flip-y .assembly-face > svg.board-shadow { opacity:calc(.78 * .23 * var(--shadow-k,1));
  transform:translateZ(-.5px) translate(calc(var(--shadow-u,.075) * -13.8px), calc(var(--shadow-v,1.576) * 13.8px)); }
.assembly-face { position:relative; width:100%; height:100%; transform-style:preserve-3d; backface-visibility:visible; }
.assembly-face > svg { backface-visibility:hidden; }
.assembly-board.flip-x .assembly-face { transform:rotateX(180deg); }
.assembly-board.flip-y .assembly-face { transform:rotateY(180deg); }
.taxel-field { fill:none; stroke:#087e8b; stroke-width:.3; stroke-dasharray:1.2 .8; }
.taxel-outline { fill:rgba(8,126,139,.08); stroke:#087e8b; stroke-width:.25; }
.mask-badge { margin:.35rem 0; font-weight:600; color:#087e8b; }
/* --plan-fit: below 1 where the boards would lie under the frame's controls (app.js clearPlanOfControls, 2026-11-06). */
.stacked-canvas.is-plan .assembly-scene { width:calc(min(80%,680px,calc(470px * var(--assembly-ratio))) * var(--plan-fit,1)); }
.stacked-canvas.is-plan .assembly-camera { transform:none; }
.stacked-canvas.is-plan .assembly-board { transform:none; filter:none; }
.stacked-canvas.is-plan .assembly-board svg { filter:none; }
.stacked-canvas.is-plan .assembly-board.is-upper svg { opacity:.6; }
.stacked-canvas.is-plan .board-edge { display:none; }
.stacked-canvas.is-plan .board-back-face { transform:rotateY(180deg); }
@media (max-width:980px) { .stacked-canvas { height:480px; }.assembly-scene { width:min(80%,570px,calc(440px * var(--assembly-ratio))); }.stacked-canvas.is-plan .assembly-scene { width:calc(min(86%,570px,calc(420px * var(--assembly-ratio))) * var(--plan-fit,1)); } }

/* DRC panel: every rule of board.verification.checks with its measured value. */
.drc-panel { margin-top:18px; }
.drc-panel header { min-height:66px; padding:15px 16px 10px; display:flex; justify-content:space-between; gap:8px; align-items:start; border-bottom:1px solid var(--line); }
.drc-panel header .eyebrow { margin-bottom:4px; }
.drc-panel header span { color:var(--muted); font-size:.72rem; text-align:right; }
.drc-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px; padding:14px 16px 16px; color:var(--muted); font-size:.8rem; }
.drc-table { width:100%; border-collapse:collapse; font-size:.7rem; color:var(--ink); }
.drc-table caption { text-align:left; font-weight:750; font-size:.78rem; padding:0 0 7px; }
.drc-table th,.drc-table td { padding:4px 6px; border-bottom:1px solid var(--line); text-align:left; vertical-align:top; }
.drc-table thead th { color:var(--muted); font-weight:750; border-bottom-color:#c7d3d7; }
.drc-table tbody th { font-weight:650; }
.drc-table .num { text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums; }
.drc-table tr.is-fail { background:#fff0ef; }
.drc-where { display:block; color:var(--muted); font-weight:500; font-size:.62rem; overflow-wrap:anywhere; }
.drc-badge { font-weight:800; letter-spacing:.03em; }
.drc-badge.pass { color:#087e8b; }.drc-badge.fail { color:var(--error); }
.drc-note { grid-column:1/-1; font-size:.7rem; color:var(--muted); line-height:1.45; }
@media (max-width:1180px) { .drc-grid { grid-template-columns:1fr; } }
@media (max-width:640px) { .drc-grid { padding:12px 10px 14px; }.drc-table th,.drc-table td { padding:4px 3px; }.drc-table .num { white-space:normal; } }

/* Manufacturing note: the profile's JLCPCB order options and review notes (ORDER_NOTES.txt). */
.order-note h3 { margin:11px 0 4px; font-size:.76rem; color:var(--ink); }
.order-note ul { margin:0; padding-left:18px; font-size:.76rem; line-height:1.45; color:var(--ink); }
.order-note li { margin:3px 0; }
.order-note .order-why { display:block; color:var(--muted); font-size:.7rem; }

/* CAD mode (web/cad/): the masthead mode switch and the CAD workspace above the shared workbench.
   Rectangular mode only shows the switch; the workspace is hidden there. */
/* The title and its line on FlexiTac (links to the paper and the website); the mode switch right after them (the user's
   requests of 2026-10-30), large enough to read as the page's first choice. */
/* The header (2026-11-02): FlexiTac-Gen, the mode switch and the community links on one line, centred on the title; the
   line on FlexiTac under them, one line where it fits. */
.masthead-title { display:contents; }
.masthead h1 { grid-area:title; line-height:1.1; white-space:nowrap; }
.masthead-tagline { grid-area:tagline; font-size:.86rem; line-height:1.45; color:#cfe9e6; }
.masthead .mode-picker { grid-area:modes; justify-self:start; }
.masthead-links { display:flex; align-items:center; gap:10px; }
.masthead-link { display:inline-flex; align-items:center; gap:9px; height:44px; padding:0 16px 0 12px; border:1px solid rgba(255,255,255,.38); border-radius:8px;
  background:rgba(255,255,255,.06); color:#fff; font-size:.95rem; font-weight:700; text-decoration:none; }
.masthead-link svg { flex:none; width:26px; height:26px; }
.masthead-link:hover { border-color:rgba(255,255,255,.75); background:rgba(255,255,255,.14); }
.masthead-link:focus-visible { outline:2px solid #77cbd1; outline-offset:2px; }
.masthead-gh-text { display:flex; flex-direction:column; gap:1px; line-height:1.2; }
.masthead-gh-name { font-size:.9rem; font-weight:700; }
.masthead-gh-stats { display:flex; gap:11px; font-size:.75rem; font-weight:600; color:rgba(255,255,255,.78); }
.masthead-gh-stats[hidden] { display:none; }
.masthead-gh-stats > span { display:inline-flex; align-items:center; gap:4px; }
.masthead-gh-stats svg { width:13px; height:13px; }
.masthead-discord { border-color:#5865f2; background:#5865f2; }
.masthead-discord:hover { border-color:#4752c4; background:#4752c4; }
@media (max-width:1100px) { .masthead { grid-template-columns:minmax(0,1fr) auto; grid-template-areas:"title links" "modes modes" "tagline tagline"; } }
/* The links' icons alone up to 600 px: the title and the GitHub card with its counts (four characters each, 9.9k)
   clear each other at every width (2026-11-04: from 520 px, the counts' card ran over the title's end). */
@media (max-width:600px) { .masthead-link { padding:0 10px; } .masthead-link > span { display:none; } }
.masthead-tagline a { color:#fff; font-weight:700; text-decoration:underline; text-underline-offset:2px; }
.masthead-tagline a:hover { color:#b9e6df; }
.masthead-tagline a:focus-visible { outline:2px solid #77cbd1; outline-offset:2px; border-radius:2px; }
.mode-picker { position:relative; margin:0; padding:0; display:flex; border:1px solid rgba(255,255,255,.55); border-radius:999px; overflow:hidden; }
.mode-picker legend,.cad-sr-only { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; padding:0; }
.mode-picker label { position:relative; cursor:pointer; }
.mode-picker input,.cad-seg input { position:absolute; inset:0; width:100%; height:100%; min-height:0; margin:0; opacity:0; cursor:pointer; }
.mode-picker span { display:block; padding:11px 24px; font-size:1rem; font-weight:750; color:#d9efec; white-space:nowrap; }
.mode-picker input:checked + span { background:#fff; color:#075c69; }
.mode-picker input:focus-visible + span { outline:2px solid #77cbd1; outline-offset:-3px; }
/* The Library link (library.html; the user's requests of 2026-11-05): a text link right after the mode switch, 12 px
   after it (the grid's 30 px gap less 18), on the title's line, its own grid area; the line on FlexiTac spans all five.
   At 1100 px and below (the header's two lines, as before the link) it is a flex row that wraps: the title and the
   community links, a line break (the ::after), the mode switch and the Library link, then the line on FlexiTac; where
   the link no longer fits beside the switch it takes a line of its own.  From 1101 to 1130 px the one line keeps the
   link with the grid's gaps 18 px, not 30, the link still 12 px after the switch (the review of 2026-11-05: with 30 px
   the community links ran up to 23 px into the right padding there, and a two-line header from 1130 px cost those
   widths 54 px; 18 px leaves 13 px to spare at 1101 px, measured).  `order` keeps the title block's children (display
   contents: the title, then its line) in that order; `.masthead .mode-picker` outranks the 520 px rule below
   (.mode-picker's margin 0).  The SVG's size is here: the theme styles no SVG element. */
.masthead { grid-template-columns:auto auto auto minmax(0,1fr) auto; grid-template-areas:"title modes library . links" "tagline tagline tagline tagline tagline"; }
.masthead-library { grid-area:library; justify-self:start; margin-left:-18px; display:inline-flex; align-items:center; gap:6px; height:44px; padding:0 10px;
  border-radius:980px; color:inherit; font-size:16px; font-weight:600; line-height:1.25; text-decoration:none; white-space:nowrap; }
.masthead-library svg { width:16px; height:16px; flex:none; }
@media (min-width:1101px) and (max-width:1130px) { .masthead { column-gap:18px; } .masthead-library { margin-left:-6px; } }
@media (max-width:1100px) { .masthead { display:flex; flex-wrap:wrap; align-items:center; column-gap:30px; row-gap:0; }
  .masthead h1 { order:0; flex:1 1 0; min-width:0; } .masthead-end { order:1; }
  .masthead::after { content:""; order:2; flex-basis:100%; height:0; }
  .masthead .mode-picker { order:3; margin-top:10px; margin-right:-18px; } .masthead-library { order:4; margin:10px 0 0; }
  .masthead-tagline { order:5; flex-basis:100%; margin-top:10px; } }
/* On a phone the link 36 px tall and its padding 4 px: it stays beside the switch down to 480 px and takes its own
   line (+46 px) below, which the user accepted at 480 px and below (10 px of padding wrapped it from 491 px; measured
   in Chromium on Linux, DejaVu Sans; with other fonts the wrap point shifts). */
@media (max-width:520px) { .masthead-library { height:36px; padding:0 4px; gap:5px; } }
/* The header's right (2026-11-06): the look's toggle (web/theme.js), then the community links, one row 10 px apart in the
   grid's links area (the flex row's second item at 1100 px and below).  The toggle is an icon button as tall as the
   community's pills and round: a moon while the page is light (it turns dark on), a sun while it is dark, by <html
   data-theme> (no script swaps them, so a page opened dark shows its sun from the first paint).  Its room (measured in
   Chromium on Linux, DejaVu Sans, the widest of the system fonts here; with the GitHub card's counts none, 159 / 17 and
   9.9k / 999): from 1101 to 1220 px and from 601 to 630 px Discord shows its mark alone (the line had no 54 px to
   spare there: at 1101 px the switch's Library link ran into the toggle, at 601 px with 9.9k counts the title ran
   into it; with Discord named the grid's spacer, not its two 30 px gaps, has to give the 54 px, so with 9.9k / 999
   the community links ran into the header's right padding up to 1219 px, with 159 / 17 up to 1207 px, the review of
   2026-11-06; GitHub keeps its name and counts); up to 410 px the three are circles 6 px apart, as large as the line
   leaves room for with the title 8 px or more clear (the revision of 2026-11-06: one fixed 32 px had left 40 px unused
   at 390 px): 44 px up to 410, 40 up to 394 (390 px, an iPhone), 36 up to 382 (375 px) and 32 up to 370 (at 360 px the
   title, 28 px, left 12 px beside the two pills; below 360 px it is 22 px), each a 4 px step from about 9 px of
   clearance, the line still 44 px tall.  The rest of the header is as before. */
.masthead-end { grid-area:links; display:flex; align-items:center; gap:10px; }
.masthead-theme { display:inline-flex; align-items:center; justify-content:center; flex:none; width:44px; height:44px; margin:0; padding:0;
  border:1px solid rgba(255,255,255,.38); border-radius:999px; background:rgba(255,255,255,.06); color:#fff; cursor:pointer; }
.masthead-theme svg { flex:none; width:20px; height:20px; }
.masthead-theme .theme-sun { display:none; }
:where(:root[data-theme="dark"]) .masthead-theme .theme-moon { display:none; }
:where(:root[data-theme="dark"]) .masthead-theme .theme-sun { display:block; }
@media (min-width:601px) and (max-width:630px), (min-width:1101px) and (max-width:1220px) { .masthead-discord { padding:0 10px; } .masthead-discord > span { display:none; } }
@media (max-width:410px) { .masthead-end { min-height:44px; } .masthead-end,.masthead-links { gap:6px; }
  .masthead-theme,.masthead-link { width:44px; height:44px; justify-content:center; padding:0; } .masthead-link svg { width:26px; height:26px; } }
@media (max-width:394px) { .masthead-theme,.masthead-link { width:40px; height:40px; } .masthead-link svg { width:24px; height:24px; } }
@media (max-width:382px) { .masthead-theme,.masthead-link { width:36px; height:36px; } .masthead-theme svg { width:18px; height:18px; } .masthead-link svg { width:22px; height:22px; } }
@media (max-width:370px) { .masthead-theme,.masthead-link { width:32px; height:32px; } .masthead-link svg { width:18px; height:18px; } }
.cad-workspace { margin:0 0 20px; }
.cad-workspace > header { min-height:66px; padding:15px 16px 10px; display:flex; justify-content:space-between; gap:12px; align-items:start; border-bottom:1px solid var(--line); }
.cad-workspace > header .eyebrow { margin-bottom:4px; }
.cad-workspace input[type="radio"] { width:auto; min-height:0; }
.cad-workspace input[type="range"] { min-height:0; padding:0; border:0; background:none; accent-color:var(--accent); }
.cad-workspace button:not(.primary) { border:1px solid #9eafb5; background:#fff; color:var(--ink); border-radius:5px; min-height:30px; padding:0 9px; font-weight:700; font-size:.72rem; cursor:pointer; }
.cad-workspace button:not(.primary):disabled { color:#9aa5aa; border-color:#d1d8da; cursor:not-allowed; }
.cad-workspace .primary { width:auto; padding:0 18px; }
/* CAD mode's status at the 3D view's bottom right (index.html #cadStatus): "name loaded", or a warning, an error or
   the work under way; and the loading veil over the view (#cadLoading). */
.cad-status { position:absolute; right:12px; bottom:12px; z-index:3; display:block; max-width:min(60%, 520px); padding:6px 10px; border-left:4px solid #607d8b;
  border-radius:4px; background:rgba(237,242,244,.94); box-shadow:0 1px 4px rgba(19,32,51,.12); font-size:.76rem; line-height:1.4; overflow-wrap:anywhere; }
.cad-status[hidden] { display:none; }
.cad-loading { position:absolute; inset:0; z-index:2; display:grid; place-content:center; justify-items:center; gap:10px; pointer-events:none;
  background:rgba(248,250,251,.62); color:var(--ink); font-size:.9rem; font-weight:750; }
.cad-loading[hidden] { display:none; }
.cad-spinner { width:30px; height:30px; border:3px solid #c9d8dc; border-top-color:var(--accent); border-radius:50%; animation:cad-spin .9s linear infinite; }
@keyframes cad-spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cad-spinner { animation-duration:3s; } }
.cad-status.is-loaded { border-color:var(--accent); background:rgba(231,244,240,.94); font-weight:700; }
.cad-status.is-warning { border-color:var(--warn); background:#fff7e5; }
.cad-status.is-error { border-color:var(--error); background:#fff0ef; }
.cad-main { display:grid; grid-template-columns:minmax(0,1fr); border-bottom:1px solid var(--line); }
.cad-stage { display:flex; flex-direction:column; min-width:0; }
/* Step 1's file and selection settings above the 3D view (index.html .cad-filebar). */
.cad-filebar { display:flex; flex-wrap:wrap; align-items:center; gap:8px 14px; padding:10px 12px; border-bottom:1px solid var(--line); background:#fbfdfd; }
.cad-filebar-label { margin:0; font-size:.86rem; }
.cad-filebar input[type="file"] { width:auto; max-width:330px; min-height:0; padding:4px 6px; }
.cad-filebar .cad-file-note { margin:0; flex:1 1 300px; }
.cad-filebar .cad-more { margin:0; }
.cad-filebar .cad-more[open] { flex-basis:100%; }
.cad-workspace > header h2 { display:flex; align-items:center; gap:9px; font-size:1.15rem; }
.cad-title-hint { margin:4px 0 0; }
/* The tools in the 3D view's window, on its left (index.html .cad-view-tools; the user's request of 2026-10-30): a
   column of larger buttons over the view, beside it in the DOM so its clicks, double clicks and keys are not the view's. */
.cad-view-wrap { position:relative; }
.cad-view-tools { position:absolute; left:12px; top:12px; z-index:3; display:flex; flex-direction:column; align-items:stretch; gap:6px; width:max-content; }
.cad-tools { margin:0 0 6px; padding:0; border:0; display:flex; flex-direction:column; gap:6px; min-width:0; }
.cad-tools legend { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; padding:0; }
.cad-tools label { position:relative; }
.cad-tools input { position:absolute; inset:0; width:100%; height:100%; min-height:0; margin:0; opacity:0; cursor:pointer; }
.cad-tools span,.cad-workspace .cad-view-tools button { display:block; min-width:150px; min-height:0; padding:9px 14px; text-align:left; font-size:.84rem; font-weight:750;
  border:1px solid #b9c8cc; border-radius:6px; background:rgba(255,255,255,.95); color:#33454d; box-shadow:0 1px 3px rgba(20,40,50,.14); }
.cad-tools input:checked + span { background:#e3f4f2; border-color:#3aa3a8; color:#075c69; }
.cad-tools input:focus-visible + span,.cad-view-tools button:focus-visible { outline:2px solid #77cbd1; outline-offset:1px; }
.cad-workspace .cad-view-tools button:disabled { opacity:.55; cursor:not-allowed; }
/* The camera in the 3D view's window, top right (index.html .cad-views; beside the view in the DOM, as the tools). */
.cad-views { position:absolute; right:12px; top:12px; z-index:3; display:flex; flex-wrap:wrap; justify-content:flex-end; gap:4px; max-width:calc(100% - 190px); }
.cad-workspace .cad-views button { min-height:30px; padding:0 9px; border:1px solid #b9c8cc; border-radius:6px; background:rgba(255,255,255,.95);
  color:#33454d; font-size:.78rem; font-weight:750; box-shadow:0 1px 3px rgba(20,40,50,.14); }
.cad-view { position:relative; height:500px; overflow:hidden; background:#f2f5f6; touch-action:none; }
.cad-view:focus-visible { outline:2px solid #77cbd1; outline-offset:-2px; }
.cad-view .cad-canvas { position:absolute; inset:0; width:100%; height:100%; display:block; }
.cad-view.is-dropping { outline:3px dashed var(--accent); outline-offset:-7px; }
/* A drag over the 3D view (a file, a sample's tile: main.js) drawn over the part too: the canvas covers the view's own
   outline once a model is shown (theme.css draws the focus ring so as well; 2026-11-06). */
.cad-view.is-dropping::after { content:""; position:absolute; inset:5px; z-index:2; border:3px dashed var(--accent); border-radius:10px; pointer-events:none; }
.cad-drop { position:absolute; inset:14px; z-index:1; display:grid; place-content:center; gap:6px; padding:12px; text-align:center; border:2px dashed #9eb7bb; border-radius:8px; color:var(--muted); font-size:.84rem; background:rgba(255,255,255,.72); }
.cad-drop strong { color:var(--ink); font-size:1rem; }
.cad-view.is-unavailable .cad-drop { border-color:var(--warn); }
.cad-file-link { color:var(--accent); font-weight:750; text-decoration:underline; cursor:pointer; }
.cad-help { padding:7px 12px; border-top:1px solid var(--line); color:var(--muted); font-size:.72rem; }
/* The border editor (main.js; docs/CAD_BORDER.md): the toggle, its bar under the toolbar, the hover label in the view. */
.cad-workspace button.cad-edit-border:not(:disabled) { border-color:#1e88e5; color:#0d47a1; }
.cad-workspace button.cad-edit-border[aria-pressed="true"] { background:#1e88e5; border-color:#1565c0; color:#fff; }
.cad-border-bar { display:flex; flex-wrap:wrap; align-items:center; gap:5px 6px; padding:6px 11px; border-bottom:1px solid var(--line); background:#f1f7fd; }
.cad-workspace .cad-border-bar button { min-height:26px; padding:0 7px; }
.cad-border-inset { display:inline-flex; align-items:center; gap:4px; }
.cad-border-inset span,.cad-border-snap { color:#354454; font-size:.72rem; font-weight:700; }
.cad-workspace .cad-border-inset input { width:56px; min-height:26px; padding:2px 5px; font-size:.76rem; }
.cad-border-snap { display:inline-flex; align-items:center; gap:4px; cursor:pointer; }
.cad-workspace .cad-border-snap input { width:auto; min-height:0; margin:0; accent-color:#1e88e5; }
.cad-view.is-editing { outline:2px solid #1e88e5; outline-offset:-2px; }
.cad-hover-label { position:absolute; z-index:2; pointer-events:none; padding:3px 7px; border-radius:4px; background:rgba(19,32,51,.88); color:#fff; font-size:.7rem; font-weight:700; white-space:nowrap; }
.cad-border-note { margin:0 0 8px; }
.cad-side-none { margin:6px 0 0; font-size:.72rem; }
/* The step column scrolls inside the height of the 3D view (contain:size keeps it out of the row height). */
.cad-step { padding:14px 16px; border-bottom:1px solid var(--line); min-width:0; }
.cad-workspace > .cad-step:last-of-type { border-bottom:0; }
/* The steps' titles (the user's request of 2026-10-30: larger). */
.cad-step h3 { margin:0 0 12px; display:flex; align-items:center; gap:9px; font-size:1.08rem; letter-spacing:-.015em; }
.cad-step-no { display:inline-grid; place-items:center; width:24px; height:24px; flex:none; border-radius:50%; background:#075c69; color:#fff; font-size:.78rem; font-weight:800; }
.cad-step .hint { margin:0 0 9px; }
.cad-subhead { margin:14px 0 7px; font-size:.76rem; }
.cad-row { display:grid; gap:4px; margin-bottom:9px; }
.cad-row label,.cad-field label { color:#354454; font-size:.74rem; font-weight:700; }
.cad-range { grid-template-columns:minmax(0,1fr) 44px; align-items:center; }
.cad-range label { grid-column:1/-1; }
.cad-range-value { text-align:right; font-size:.72rem; font-variant-numeric:tabular-nums; color:var(--muted); }
.cad-facts { display:grid; grid-template-columns:auto minmax(0,1fr); gap:3px 10px; margin:10px 0 0; font-size:.71rem; line-height:1.35; }
.cad-facts dt { color:var(--muted); }
.cad-facts dd { margin:0; overflow-wrap:anywhere; }
.cad-seg { margin:0 0 9px; padding:0; border:0; display:flex; flex-wrap:wrap; align-items:center; gap:4px; min-width:0; }
.cad-seg legend { float:left; margin:0 6px 0 0; padding:0; color:var(--muted); font-size:.72rem; font-weight:700; }
.cad-seg label { position:relative; }
.cad-seg span { display:block; padding:4px 9px; border:1px solid #c7d3d7; border-radius:4px; background:#fff; color:var(--muted); font-size:.72rem; font-weight:700; }
.cad-seg input:checked + span { color:#075c69; border-color:#7fc2c6; background:#eef9f8; }
.cad-seg input:focus-visible + span { outline:2px solid #77cbd1; outline-offset:1px; }
/* The base edge under the 3D view, across (index.html .cad-base; main.js renderSides): its choices as pills. */
.cad-base { padding:9px 12px 10px; border-top:1px solid var(--line); background:#fbfdfd; }
.cad-base-title { margin:0 0 7px; display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 9px; font-size:.82rem; }
.cad-base-title .hint { margin:0; font-weight:500; }
.cad-base .cad-sides { display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.cad-base .cad-sides legend { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; padding:0; }
.cad-base .cad-sides > .hint { margin:0; }
.cad-base .cad-side { padding:5px 11px; border:1px solid #c7d3d7; border-radius:999px; background:#fff; font-size:.76rem; }
.cad-base .cad-side:has(input:checked) { border-color:#c2185b; background:#fdf0f5; color:#7a1040; font-weight:700; }
.cad-base .cad-side input { margin:0; }
.cad-base .cad-side-list { display:contents; }
.cad-base :is(.cad-side-short,.cad-side-none) { flex-basis:100%; margin:2px 0 0; }
.cad-sides { margin:0; padding:0; border:0; min-width:0; }
.cad-sides legend { padding:0; margin-bottom:6px; color:#354454; font-size:.74rem; font-weight:700; }
.cad-side { display:flex; gap:7px; align-items:center; padding:3px 4px; border-radius:4px; font-size:.72rem; cursor:pointer; }
.cad-side:hover { background:#fbe9f1; }
.cad-side-list { max-height:190px; overflow-y:auto; margin-top:2px; border-top:1px dashed var(--line); padding-top:3px; }
.cad-side:focus-within { background:#fbe9f1; }
.cad-side-short { margin:6px 0 0; }
/* "More options" larger than the other folds (the user's request of 2026-10-30): the fit's other options are found there. */
#cadConfigMore > summary { font-size:.95rem; font-weight:750; color:#26404a; }
.cad-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(178px,1fr)); gap:10px 14px; }
.cad-field { display:grid; gap:4px; align-content:end; }
.cad-field input:disabled,.cad-field select:disabled { background:#f1f4f4; color:#8a969c; }
.cad-workspace input[aria-invalid="true"] { border-color:var(--error); background:#fff7f6; }
.cad-actions { display:flex; flex-wrap:wrap; align-items:center; gap:10px 16px; margin-top:13px; }
.cad-actions .hint { margin:0; flex:1 1 260px; }
/* Step 2 side by side (index.html .cad-resolution): the settings, Fit and the proposals left, the flattened map right, in
   sight while the left scrolls; the left takes the whole width until there is a map, and the two stack when narrow. */
.cad-resolution { display:grid; grid-template-columns:minmax(0,1fr) minmax(300px,40%); border-bottom:1px solid var(--line); }
.cad-resolution-main { min-width:0; }
.cad-resolution-main > .cad-step:last-child { border-bottom:0; }
.cad-resolution-map { min-width:0; padding:14px 16px; border-left:1px solid var(--line); }
/* The map's window keeps its size whatever the sensor's (the drawing scales inside it). */
.cad-resolution-map .cad-flat { position:sticky; top:14px; max-width:none; height:min(620px,80vh); display:flex; flex-direction:column; }
.cad-resolution-map .cad-flat-svg { flex:1 1 auto; min-height:0; width:100%; height:100%; max-height:none; }
/* Two settings a row on the left (the user's request of 2026-10-30). */
.cad-resolution-main .cad-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
.cad-fit-note:empty { display:none; }
.cad-fit-facts { margin:8px 0 0; }
@media (max-width:1100px) { .cad-resolution { grid-template-columns:minmax(0,1fr); }
  .cad-resolution-map { border-left:0; border-top:1px solid var(--line); }.cad-resolution-map .cad-flat { position:static; height:460px; } }
.cad-proposals { container:cad-proposals / inline-size; }
.cad-proposals-body { display:grid; gap:12px; }
.cad-table-wrap { min-width:0; overflow-x:auto; }
.cad-table { width:100%; border-collapse:collapse; font-size:.71rem; }
.cad-table caption { text-align:left; font-weight:750; font-size:.76rem; padding:0 0 6px; }
.cad-table th,.cad-table td { padding:5px 6px; border-bottom:1px solid var(--line); text-align:left; white-space:nowrap; }
.cad-table thead th { color:var(--muted); font-weight:750; border-bottom-color:#c7d3d7; white-space:normal; vertical-align:bottom; }
/* A narrow table drops its low-value columns (angle, stack width, traces) instead of scrolling. */
@container cad-proposals (max-width:900px) { .cad-table .cad-opt { display:none; } }
.cad-table .num { text-align:right; font-variant-numeric:tabular-nums; }
.cad-table tbody tr { cursor:pointer; }
.cad-table tbody tr:hover { background:#f6faf9; }
.cad-table tbody tr.is-chosen { background:#eef9f8; box-shadow:inset 3px 0 0 var(--accent); }
.cad-card { max-width:760px; padding:8px 10px; border:1px solid var(--line); border-radius:6px; background:#fbfcfc; }
.cad-card .cad-subhead { margin:0 0 4px; }
.cad-card .cad-facts { margin:0; }
.cad-flat { margin:0; padding:8px; max-width:760px; border:1px solid var(--line); border-radius:6px; background:radial-gradient(#edf0ec 1px,transparent 1px) 0 0/12px 12px; }
.cad-flat-svg { width:100%; height:auto; max-height:300px; display:block; }
.cad-flat { position:relative; }
.cad-flat.is-empty .cad-flat-svg { visibility:hidden; }
.cad-flat-empty { display:none; position:absolute; inset:0; margin:0; place-content:center; padding:20px; text-align:center; color:var(--muted); font-size:.86rem; font-weight:700; }
.cad-flat.is-empty .cad-flat-empty { display:grid; }
.cad-flat.is-fitting .cad-flat-empty { color:#075c69; }
.cad-flat figcaption { margin-top:6px; color:var(--muted); font-size:.72rem; line-height:1.4; }
.cad-flat-svg .flat-surface { fill:#fbe3c8; stroke:#b9772f; stroke-width:1; vector-effect:non-scaling-stroke; }
.cad-flat-svg .flat-hole { fill:#fff; stroke:#b9772f; stroke-width:1; vector-effect:non-scaling-stroke; }
.cad-flat-svg .flat-row { fill:rgba(215,119,31,.16); stroke:#d7771f; stroke-width:1.2; vector-effect:non-scaling-stroke; }
.cad-flat-svg .flat-column { fill:rgba(8,126,139,.12); stroke:#087e8b; stroke-width:1.2; vector-effect:non-scaling-stroke; }
/* A board's parts off the surface (main.js draws each board dotted and faint, then solid clipped to the surface). */
.cad-flat-svg .flat-row.is-off { fill:rgba(215,119,31,.06); stroke-dasharray:2 2; }
.cad-flat-svg .flat-column.is-off { fill:rgba(8,126,139,.05); stroke-dasharray:2 2; }
.cad-advice { margin:.25rem 0 0; padding-left:1.1rem; }
.cad-advice li { margin:.15rem 0; }
.cad-view-note { margin:.35rem 0 0; }
.cad-flat-svg .flat-field { fill:none; stroke:#132033; stroke-width:1; stroke-dasharray:4 3; vector-effect:non-scaling-stroke; }
.cad-flat-svg .flat-base { stroke:#c2185b; stroke-width:3.5; stroke-linecap:round; vector-effect:non-scaling-stroke; }
.cad-flat-svg .flat-taxels { fill:#1a7f3c; }
/* The fit's border: blue, hatched where it lies beyond the surface (no taxels there). */
.cad-flat-svg .flat-border { fill:none; stroke:#1e88e5; stroke-width:2; vector-effect:non-scaling-stroke; }
.cad-flat-svg .flat-border-out { fill:url(#cadFlatHatch); stroke:none; }
.cad-flat-svg .flat-hatch { stroke:#546e7a; stroke-width:1; vector-effect:non-scaling-stroke; }
/* Step 3 side by side (index.html .cad-build): Generate left, the build on the part and its files right; stacked when narrow. */
.cad-build { display:grid; grid-template-columns:max-content minmax(0,1fr); border-bottom:1px solid var(--line); }
.cad-build > .cad-step { border-bottom:0; }
.cad-build > .cad-verify { border-left:1px solid var(--line); grid-template-columns:minmax(0,1fr); gap:4px; }
.cad-build .cad-actions { margin-top:0; flex-direction:column; align-items:flex-start; gap:8px; }
.cad-build .cad-actions .primary { width:auto; padding:0 20px; white-space:nowrap; }
.cad-build .cad-actions .hint { flex:none; max-width:36ch; }
.cad-build .cad-downloads { grid-template-columns:minmax(0,1fr); gap:6px; margin-top:4px; }
.cad-build .cad-downloads > .hint { margin:0; }
@media (max-width:1100px) { .cad-build { grid-template-columns:minmax(0,1fr); }.cad-build > .cad-verify { border-left:0; border-top:1px solid var(--line); } }
.cad-verify { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(220px,1fr); gap:6px 18px; align-items:start; }
.cad-verify > h3 { grid-column:1/-1; }
.cad-verify-body { min-width:0; }
.cad-verify-body.is-stale { opacity:.5; filter:grayscale(.7); transition:opacity .15s; }
.cad-verify-table td,.cad-verify-table th { white-space:normal; }
.cad-error { color:var(--error); font-size:.78rem; font-weight:700; }
.cad-error small { display:block; color:var(--muted); font-weight:500; }
.cad-downloads { display:grid; gap:8px; align-content:start; }
.cad-download-actions { display:flex; flex-wrap:wrap; gap:7px; }
.cad-diagnostics { padding:9px 16px 13px; border-top:1px solid var(--line); color:var(--muted); font-size:.7rem; }
.cad-diagnostics summary { cursor:pointer; font-weight:700; }
.cad-diagnostics .cad-row { margin-top:8px; max-width:520px; }
.cad-state { display:block; margin:6px 0 0; white-space:pre-wrap; overflow-wrap:anywhere; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.7rem; }
.cad-state[hidden] { display:none; }
.cad-limits-note { margin:8px 0 0; color:var(--warn); }
.template-note { margin:8px 0 0; font-size:.72rem; }
.mask-note { margin:8px 0 0; font-size:.72rem; }
.mask-note p { margin:0; }
.mask-note.is-invalid { border-left:3px solid var(--error); padding-left:7px; }
.mask-issues { margin:6px 0 0; padding-left:18px; }
.mask-issues .is-error { color:var(--error); }
.mask-note button { margin-top:7px; border:1px solid #9eafb5; background:#fff; color:var(--ink); border-radius:5px; min-height:30px; padding:0 10px; font-weight:700; font-size:.74rem; cursor:pointer; }
@media (max-width:1200px) { .cad-view { height:430px; }
  .cad-verify { grid-template-columns:1fr; }.cad-status { max-width:calc(100% - 24px); } }
@media (max-width:640px) { .cad-workspace > header { flex-direction:column; }.cad-view { height:340px; } }
@media (max-width:520px) { .mode-picker { margin:0; } }

/* A clean interface: the basic controls in sight, the rest folded (details.advanced, .cad-more, the design checks,
   the stacked view's notes and the ordering notes). */
details > summary { cursor:pointer; list-style:none; }
details > summary::-webkit-details-marker { display:none; }
details > summary::before { content:"▸"; display:inline-block; width:1em; color:var(--muted); transition:transform .12s; }
details[open] > summary::before { transform:rotate(90deg); }
.advanced { margin:0 0 18px; border:1px solid var(--line); border-radius:7px; background:#fff; }
.advanced > summary { padding:10px 12px; font-size:.82rem; font-weight:750; color:#354454; }
.advanced[open] > summary { border-bottom:1px solid var(--line); }
.advanced > :not(summary) { margin-left:12px; margin-right:12px; }
.advanced > .hint:first-of-type { margin-top:10px; }
.advanced section { border-bottom:1px solid var(--line); padding:12px 0 8px; margin:0; }
.advanced section:last-child { border-bottom:0; margin-bottom:12px; }
.advanced h2 { font-size:.86rem; margin-bottom:10px; }
.locked .profile-badge { margin:0 0 9px; font-size:.72rem; font-weight:750; color:#354454; }
.controls .mask-note { margin:0 0 14px; }
/* CAD mode has its own steps: the rectangular form stays in the page (Generate writes into it), out of sight. */
/* Gen by Surface: the order of work on the page's left (index.html .flow-guide; main.js renderGuide), in sight from top
   to bottom, each step scrolling to its part on the right; done (teal), next (blue), in view (its row lit). */
body[data-mode="cad"] main { grid-template-columns:minmax(240px,280px) minmax(0,1fr); }
.flow-guide { display:none; }
body[data-mode="cad"] .flow-guide { display:block; position:sticky; top:0; align-self:start; height:100vh; overflow-y:auto; padding:22px 16px 24px;
  background:#fbfcfa; border-right:1px solid var(--line); }
.flow-guide-title { margin:0 4px 18px; font-size:1.06rem; line-height:1.3; letter-spacing:-.015em; color:#132033; }
.flow-steps { list-style:none; margin:0; padding:0; }
.flow-step { position:relative; padding:0 0 10px; }
.flow-step:not(:last-child)::before { content:""; position:absolute; left:23px; top:42px; bottom:0; width:2px; border-radius:1px; background:#d6e2e5; }
.flow-step.is-done:not(:last-child)::before { background:#82c3c7; }
.flow-step > button { display:grid; grid-template-columns:30px minmax(0,1fr); gap:10px; width:100%; padding:8px; border:1px solid transparent; border-radius:10px;
  background:none; color:inherit; font:inherit; text-align:left; cursor:pointer; }
.flow-step > button:hover { background:#eef5f5; }
.flow-step button:focus-visible { outline:2px solid #77cbd1; outline-offset:1px; }
.flow-step.is-here > button { background:#eaf2fe; border-color:#bdd2f6; }
.flow-no { display:grid; place-items:center; width:30px; height:30px; border-radius:50%; border:2px solid #a9bcc1; background:#fff; color:#53676e; font-size:.86rem; font-weight:800; }
.flow-step.is-done .flow-no { border-color:#075c69; background:#075c69; color:#fff; }
.flow-step.is-next .flow-no { border-color:#2563eb; background:#2563eb; color:#fff; box-shadow:0 0 0 4px rgba(37,99,235,.15); }
.flow-text strong { display:block; margin-top:5px; font-size:.93rem; color:#20303a; }
.flow-step.is-next .flow-text strong { color:#1d4ed8; }
.flow-text > span { display:block; margin-top:3px; color:var(--muted); font-size:.76rem; line-height:1.45; }
/* The samples under the steps (index.html #samplePalette, main.js loadSample; the user's request of 2026-11-06): two
   columns of tiles, each a thumbnail and a name, dragged onto the 3D view or pressed; the one shown edged (is-current).
   On a narrow page (the guide above the workspace) one row that scrolls sideways, so the steps and the view stay near. */
.sample-palette { margin:12px 4px 0; padding-top:10px; border-top:1px solid var(--line); }
.sample-palette-title { margin:0 0 2px; font-size:.86rem; line-height:1.3; }
.sample-palette-hint { margin:0 0 6px; color:var(--muted); font-size:.7rem; line-height:1.4; }
.sample-grid { list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:5px; }
.sample-grid > li { min-width:0; }
.sample { position:relative; display:flex; flex-direction:column; gap:1px; width:100%; min-height:44px; padding:3px; border:1px solid var(--line); border-radius:9px;
  background:#fff; color:inherit; font:inherit; text-align:left; cursor:grab; }
.sample:hover { border-color:#9fc6cb; }
.sample:focus-visible { outline:2px solid #77cbd1; outline-offset:1px; }
.sample:active { cursor:grabbing; }
.sample.is-current { border-color:var(--accent); box-shadow:inset 0 0 0 1px var(--accent); }
.sample[aria-busy="true"] { cursor:progress; }
.sample img { display:block; width:100%; height:auto; aspect-ratio:24 / 11; margin-bottom:2px; border-radius:6px; background:#f5f5f7; pointer-events:none; }
.sample-name { display:block; padding:0 3px; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; font-size:.72rem; font-weight:700; line-height:1.3; }
.sample-hint-tap { display:none; }
@media (pointer:coarse) { .sample-hint-click { display:none; } .sample-hint-tap { display:inline; } }
/* Compact tiles, each thumbnail beside its name (two lines at most), 30 px high: a laptop's window (under 920 px the
   guide had overflowed its window-high column, its own scrollbar narrowing the steps, the grippers out of sight), the
   side of a narrow page's guide and a phone's row (the review of 2026-11-06), so that the palette moves nothing. */
@media (min-width:981px) and (max-height:919px), (max-width:980px) {
  .sample-grid .sample { flex-direction:row; align-items:center; gap:5px; min-height:30px; padding:2px; }
  .sample-grid .sample img { flex:none; width:clamp(44px,36%,72px); margin:0; }
  .sample-grid .sample-name { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; padding:0 2px 0 0; white-space:normal; font-size:11px; line-height:13px; }
}
/* The guide's column a flex column: on a short window the palette gives way, its grid scrolling inside it, never the
   steps (Advanced Customization's steps open, a window under 720 px); its focus rings inside the scroller's padding. */
@media (min-width:981px) {
  body[data-mode="cad"] .flow-guide { display:flex; flex-direction:column; }
  .sample-palette { display:flex; flex-direction:column; min-height:0; }
  .sample-grid { min-height:64px; margin:0 -3px; padding:3px; overflow-y:auto; }
}
@media (min-width:981px) and (max-height:919px) {
  .sample-palette { margin-top:6px; padding-top:8px; }
  .sample-palette-hint { margin-bottom:3px; }
  .sample-grid { gap:3px 4px; }
  body[data-mode="cad"] .flow-guide { padding-bottom:16px; }
}
/* A narrow page (the guide above the workspace): beside the steps, which take no more than their own width, two
   columns of four as on the left; on a phone one row under them that scrolls sideways, the heading and the hint on one
   line, about 80 px in all.  Either way the workspace starts where it did without the palette, or near. */
@media (min-width:760px) and (max-width:980px) {
  body[data-mode="cad"] .flow-guide { display:grid; grid-template-columns:max-content minmax(0,1fr); column-gap:24px; align-items:start; }
  .flow-guide-title { grid-column:1 / -1; }
  .sample-palette { grid-column:2; grid-row:2; margin:0; padding:0 0 0 18px; border-top:0; border-left:1px solid var(--line); }
  .sample-grid { margin:0 -3px; padding:3px; gap:3px 4px; }
}
@media (max-width:759px) {
  .sample-palette { display:grid; grid-template-columns:auto minmax(0,1fr); column-gap:8px; align-items:baseline; margin:8px 0 0; padding-top:8px; }
  .sample-palette-title,.sample-palette-hint { margin:0; }
  .sample-grid { grid-column:1 / -1; grid-template-columns:none; grid-auto-flow:column; grid-auto-columns:138px; overflow-x:auto; gap:4px; margin:4px -3px 0; padding:3px; }
  /* The row's tiles one height (the verification of 2026-11-06: a tile with a two-line name 32 px, the others 30 px, its
     second line 3 px off the tile's bottom edge): each tile as tall as its row, 34 px, a two-line name 4 px clear of
     either edge. */
  .sample-grid > li { display:flex; }
  .sample-grid .sample { min-height:34px; padding-top:3px; padding-bottom:3px; }
}
#cadWorkspace,#cadStepConfig,#cadStepGenerate,#downloadsTitle { scroll-margin-top:14px; }
/* The Gerber ZIPs, first among the downloads and set off (index.html .download-main). */
.download-actions button.download-main { min-height:42px; padding:0 16px; border-color:var(--accent); background:var(--accent); color:#fff; font-size:.86rem; font-weight:800; }
.download-actions button.download-main:hover:not(:disabled) { background:#066d78; }
.download-actions button.download-main:disabled { background:#e3eceb; border-color:#c9d6d8; color:#8a9a9e; }
.flow-only { display:none; }
body[data-mode="cad"] .flow-only { display:inline-grid; margin-right:8px; vertical-align:2px; }
@media (max-width:980px) { body[data-mode="cad"] main { grid-template-columns:1fr; }
  body[data-mode="cad"] .flow-guide { position:static; height:auto; border-right:0; border-bottom:1px solid var(--line); } }
body[data-mode="cad"] .controls { display:none; }
.cad-more { margin:6px 0 10px; }
.cad-more > summary { font-size:.74rem; font-weight:700; color:var(--muted); padding:3px 0; }
.cad-more[open] > summary { margin-bottom:8px; }
.cad-more .cad-grid { margin-top:4px; }
.cad-config-body > .cad-grid { margin-bottom:6px; }
.board-card header h2 { align-self:center; }
.board-card header { min-height:0; }
.drc-panel > summary { padding:14px 16px; display:flex; align-items:center; gap:12px; }
.drc-panel > summary::before { order:-1; }
.drc-panel[open] > summary { border-bottom:1px solid var(--line); }
.drc-total { font-size:.78rem; font-weight:750; }
.drc-total.is-pass { color:var(--accent); }.drc-total.is-fail { color:var(--error); }
.drc-bar { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:10px 16px 0; color:var(--muted); font-size:.7rem; }
.drc-bar button { border:1px solid #c7d3d7; border-radius:4px; background:#fff; color:var(--ink); font-size:.7rem; font-weight:700; padding:4px 9px; cursor:pointer; }
.drc-panel.only-failures tr.is-pass { display:none; }
.manufacturing-note > summary { font-size:.78rem; font-weight:750; color:#354454; padding:2px 0; }
.manufacturing-note[open] > summary { margin-bottom:8px; }
body.cad-no-build #results { display:none; }
.board-card header h2 { white-space:nowrap; }
.cad-verify-line { font-size:.8rem; font-weight:700; margin:0 0 4px; }
.cad-verify-line.is-pass { color:var(--accent); }.cad-verify-line.is-fail { color:var(--error); }

/* Looks (app.js decorateLook, setLook): the board previews and the stacked view (.look-scope) in Photo, colours sampled
   from a photograph of a JLCPCB FlexiTac pad on white, or Diagram, the server SVG's own colours.  The extra layers
   (.lk) show only in Photo; the CAD workspace's flat figure keeps its own colours. */
.look-defs { position:absolute; width:0; height:0; overflow:hidden; pointer-events:none; }
.lk { display:none; }
.lk polyline { fill:none; stroke-linejoin:round; stroke-linecap:round; }
.lk-gap polyline { stroke:#000; }.lk-gap :is(rect,polygon) { fill:#000; }
.look-bar { display:flex; align-items:center; justify-content:flex-end; gap:6px; margin:16px 0 -8px; font-size:.72rem; color:var(--muted); }
.look-bar span { font-weight:750; margin-right:2px; }
.look-bar button { cursor:pointer; border:1px solid transparent; border-radius:4px; background:transparent; color:var(--muted); padding:4px 8px; font-size:.72rem; font-weight:750; }
.look-bar button[aria-pressed="true"] { color:#075c69; border-color:#b9d9db; background:#eef9f8; }
body[data-look="photo"] .look-scope .outline { fill:#df8401; stroke:#6b2a03; stroke-width:.06; }
body[data-look="photo"] .look-scope .cutout { fill:var(--card); stroke:#6b2a03; stroke-width:.06; }
body[data-look="photo"] .look-scope .stiffener { display:none; }
body[data-look="photo"] .look-scope .opening { fill:#d98a2a; stroke:none; }
body[data-look="photo"] .look-scope .route { stroke:#d26a02; }
body[data-look="photo"] .look-scope :is(.electrode,.pad) { fill:#d26a02; stroke:none; }
body[data-look="photo"] .look-scope :is(.lk-gap,.lk-edge,.lk-stiff,.lk-gold,.lk-grain) { display:inline; }
/* The gaps' closing is a filter: in a 3D-transformed view it makes Chrome draw the board at a lower resolution (the
   stacked view's and the oblique view's boards are small there anyway), so only the flat top view has it. */
body[data-look="photo"] .look-scope :is(.stacked-canvas,.oblique-board) .lk-gap { display:none; }
body[data-look="photo"] .look-scope .lk-edge polyline { stroke:#b85a02; }
body[data-look="photo"] .look-scope .lk-edge :is(rect,polygon) { fill:#b85a02; stroke:#b85a02; stroke-width:.05; }
body[data-look="photo"] .look-scope .lk-stiff { fill:#4a1606; opacity:.9; mix-blend-mode:multiply; }
body[data-look="photo"] .look-scope .three-d-pane::after { content:"Rigid oblique tilt of the top view · not a flex model · PI stiffener (bottom side) in dark brown · thickness at least 2 px"; }
body[data-look="photo"] .look-scope .lk-gold polyline { stroke:url(#lk-enig); }
body[data-look="photo"] .look-scope .lk-gold :is(rect,polygon) { fill:url(#lk-enig); }
body[data-look="photo"] .look-scope .lk-gloss { fill:url(#lk-gloss-paint); mix-blend-mode:screen; }
/* The other side in Photo: the bare polyimide (no coverlay), its copper a darker shade seen through it, the PI stiffener
   on it outermost, a thick amber-brown. */
body[data-look="photo"] .look-scope .board-back .outline { fill:#d37c0c; stroke:#6b2a03; stroke-width:.06; }
body[data-look="photo"] .look-scope .board-back .route { stroke:#b4530a; opacity:.55; }
body[data-look="photo"] .look-scope .board-back :is(.electrode,.pad) { fill:#b4530a; opacity:.55; }
body[data-look="photo"] .look-scope .board-back .stiffener { display:inline; fill:#7a360c; stroke:#4a1606; stroke-width:.06; opacity:.95; }
body[data-look="photo"] .look-scope .fpc-edge-ring { --edge-paint:#df8401; }
body[data-look="photo"] .look-scope :is(.fpc-stiffener-ring,.fpc-stiffener-face) { --edge-paint:#7a360c; }
/* The sweep's tint in Photo (app.js lightView): the light the coverlay gives back, 72 % of the way from white to the open
   PI's hue (onpart.js BOARD_LOOK.tint; Diagram's, #ffeac6, is its PI's). */
body[data-look="photo"] .look-scope svg.board-light { --sheen:#ffbc90; }
/* The static gloss of Photo's grain is the flat view's: the 3D views have their light's sweep, which moves with the view. */
body[data-look="photo"] .look-scope :is(.oblique-board,.stacked-canvas:not(.is-plan)) .lk-gloss { display:none; }
/* The plan view sees the top FPC from above through its polyimide: its copper plainly (the registration to check). */
.stacked-canvas.is-plan .board-back :is(.electrode,.pad,.route) { opacity:.8; }
/* The coin for scale (app.js coinArt, coinPlane): the US Mint's photo of a quarter on a metal disk under a sheen, never a
   pointer target.  In the 2D view it lies on the page with its shadow round it; in the 3D views on a plane of its own
   below the boards' (.coin-plane, the table): its shadow on the table, hidden from below, its reeded side as disks rising
   through its thickness (--thickness, of the plane's width) to its face. */
.coin-ref,.coin-plane { pointer-events:none; }
.coin-lying { filter:url(#coin-shadow); }
.coin-body { fill:url(#coin-edge); }
.coin-sheen { fill:url(#coin-sheen); }
.coin-cast { fill:#000; filter:url(#coin-cast); }
.coin-side-disk { fill:url(#coin-side); }
.coin-side-reeds { fill:none; stroke:#4c5258; stroke-width:.16; opacity:.75; }
.coin-plane { position:absolute; aspect-ratio:1; transform-style:preserve-3d; container-type:inline-size; }
.coin-plane .coin-layer { position:absolute; inset:0; display:block; width:100%; height:100%; aspect-ratio:auto; overflow:visible; filter:none;
  transform:translateZ(calc(var(--thickness) * var(--rise) * 100cqw)); }
.coin-layer-cast { backface-visibility:hidden; }
/* Its shadow on the table turned about the coin's centre (app.js lightView, --coin-cast-turn) to fall away from the key as
   the boards' do, whatever the turn (index.html #coin-cast offsets it down and to the right in the coin's own plane). */
.coin-plane .coin-layer-cast { transform:rotate(var(--coin-cast-turn,0deg)); }
/* The face's gleam (app.js coinPlane): polished metal throws back the room, bright above its horizon and dark below; the
   gradient's angle undoes the view's spin and the horizon slides with the tilt, so it keeps to the room as the coin turns
   (--coin-tilt, --coin-spin: the view's angles, its own until it is first turned). */
.coin-plane .coin-layer-gleam { inset:7.0771%; width:auto; height:auto; border-radius:50%; backface-visibility:hidden;
  --horizon:calc(46% + 24% * sin(var(--coin-tilt) - 57deg));
  background:linear-gradient(calc(165deg - var(--coin-spin)), rgba(255,255,255,.3) 0%, rgba(236,243,252,.08) calc(var(--horizon) - 8%),
    rgba(44,62,84,.3) calc(var(--horizon) + 3%), rgba(44,62,84,.1) calc(var(--horizon) + 26%), rgba(255,255,255,.12) 100%); }
.oblique-board .coin-plane { transform:translateZ(-28px); --coin-tilt:var(--orbit-tilt,56deg); --coin-spin:var(--orbit-spin,-22deg); }
.assembly-camera .coin-plane { transform:translateZ(-56px); --coin-tilt:var(--orbit-tilt,57deg); --coin-spin:var(--orbit-spin,-20deg); }
.stacked-canvas.is-plan :is(.coin-plane,.coin-layer) { transform:none; }

/* One step to a sensor (the user's request of 2026-11-02): the model and the sensor on it in sight, the rest under
   "Advanced Customization" (index.html #cadCustomize, #formCustomize), the stacked view and the other files folded. */
.cad-quick { display:flex; align-items:center; padding:12px; border-top:1px solid var(--line); background:#f6fbfa; }
.cad-workspace .cad-quick button.cad-quick-generate { width:auto; min-width:200px; min-height:46px; padding:0 26px; border:0; border-radius:6px; background:#0b5d9c;
  color:#fff; font-size:1rem; font-weight:800; cursor:pointer; }
.cad-workspace .cad-quick button.cad-quick-generate:hover:not(:disabled) { background:#094c80; }
.cad-workspace .cad-quick button.cad-quick-generate:disabled { background:#e3eceb; color:#8a9a9e; cursor:default; opacity:1; }
.cad-customize { border-top:1px solid var(--line); }
.cad-customize > summary { display:flex; flex-wrap:wrap; align-items:center; gap:4px 12px; padding:16px 16px; background:#e4f2f3; border-left:5px solid var(--accent);
  font-size:1.12rem; font-weight:800; color:#065560; cursor:pointer; }
.cad-customize > summary:hover { background:#d6ecee; }
.cad-customize > summary::before { content:"▸"; font-size:1rem; }
.cad-customize[open] > summary::before { content:"▾"; }
.cad-customize > summary::-webkit-details-marker { display:none; }
.cad-customize > summary { list-style:none; }
.cad-customize > summary .hint,.customize > summary .hint,.more-files > summary .hint { margin:0; font-size:.74rem; font-weight:500; }
.cad-customize > summary .hint { font-size:.8rem; color:#3f5f66; }
.cad-customize[open] > summary { border-bottom:1px solid var(--line); }
.customize { margin:16px 0 0; border:1px solid var(--line); border-radius:7px; background:#fff; }
.customize > summary { padding:11px 12px; font-size:.86rem; font-weight:800; color:#075c69; cursor:pointer; }
.customize[open] > summary { border-bottom:1px solid var(--line); }
.customize > :not(summary) { margin-left:12px; margin-right:12px; }
.customize > section { border-bottom:1px solid var(--line); padding:12px 0 8px; margin-top:0; margin-bottom:0; }
.customize > section h2 { font-size:.86rem; margin-bottom:10px; }
.customize > .advanced { margin-top:12px; margin-bottom:12px; }
.more-files { grid-column:1 / -1; }
.more-files > summary { font-size:.82rem; font-weight:750; color:#354454; padding:2px 0; cursor:pointer; }
.more-files[open] > summary { margin-bottom:10px; }
.more-files .download-actions { justify-content:flex-start; }

/* Advanced Customization in the guide (index.html #guideCustomize, #guideSubsteps; main.js syncGuideCustomize): a small
   switch under step 2, in step with the fold on the right; open, its two steps Fit and Generate, each done or next. */
.flow-more { margin:0 0 4px 48px; }
.flow-customize { padding:4px 11px; border:1px solid #9fc6cb; border-radius:999px; background:#fff; color:#075c69; font:inherit; font-size:.76rem; font-weight:750; cursor:pointer; }
.flow-customize::before { content:"▸ "; }
.flow-customize[aria-expanded="true"] { background:#e4f2f3; border-color:var(--accent); }
.flow-customize[aria-expanded="true"]::before { content:"▾ "; }
.flow-customize:hover { background:#eef7f7; }
.flow-customize:focus-visible,.flow-substep button:focus-visible { outline:2px solid #77cbd1; outline-offset:1px; }
.flow-substeps { list-style:none; margin:6px 0 0 6px; padding:0 0 0 10px; border-left:2px solid #d6e2e5; }
.flow-substep button { display:flex; align-items:center; gap:8px; width:100%; padding:5px 8px; border:1px solid transparent; border-radius:8px; background:none;
  color:#20303a; font:inherit; font-size:.84rem; font-weight:750; text-align:left; cursor:pointer; }
.flow-substep button:hover { background:#eef5f5; }
.flow-substep.is-here button { background:#eaf2fe; border-color:#bdd2f6; }
.flow-sub-no { display:grid; place-items:center; width:22px; height:22px; border-radius:50%; border:2px solid #a9bcc1; background:#fff; color:#53676e; font-size:.7rem; font-weight:800; }
.flow-substep.is-done .flow-sub-no { border-color:#075c69; background:#075c69; color:#fff; }
.flow-substep.is-next .flow-sub-no { border-color:#2563eb; background:#2563eb; color:#fff; }

/* The sensor on the part (the user's request of 2026-11-05; index.html .stack-figures, main.js 7b, web/cad/onpart.js): in
   Gen by Surface the stacked card holds two figures, the registration (#stackedPreview, as before) and the sensor on the
   part (#sensorOnPart, a second .stacked-canvas: the same height, lightbox and stale fade, its WebGL canvas transparent
   over it), side by side where the card is 720 px wide or more (a container query: the card's width is not the
   window's, the guide folds away at 980 px; inline-size containment makes no containing block of position:fixed), else
   one under the other, their frames level (their controls in them and no legends under them since 2026-11-06).
   Rectangular mode shows the registration alone: no container, no second figure. */
.stack-figure.is-on-part { display:none; }
body[data-mode="cad"] .stack-figure.is-on-part { display:block; border-top:1px solid var(--line); }
body[data-mode="cad"] .stacked-review { container:stack-card / inline-size; }
/* The card's last row rounded at its foot (the verification of 2026-11-06: the figures' gradient stood square out of the
   card's rounded foot): Rectangular's registration alone and Gen by Surface's sensor on the part under it both bottom
   corners, the registration above it none; side by side the left's bottom left, the right's bottom right.  The radius
   theme.css's (--stack-foot), else this sheet's card's 8 px less its edge. */
.stack-figure.is-registration > .stacked-canvas,body[data-mode="cad"] .stack-figure.is-on-part > .stacked-canvas { border-radius:0 0 var(--stack-foot,7px) var(--stack-foot,7px); }
body[data-mode="cad"] .stack-figure.is-registration > .stacked-canvas { border-radius:0; }
@container stack-card (min-width:720px) {
  .stack-figures { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); }
  .stack-figure { min-width:0; }
  body[data-mode="cad"] .stack-figure.is-on-part { border-top:0; border-left:1px solid var(--line); }
  body[data-mode="cad"] .stack-figure.is-registration > .stacked-canvas { border-radius:0 0 0 var(--stack-foot,7px); }
  body[data-mode="cad"] .stack-figure.is-on-part > .stacked-canvas { border-radius:0 0 var(--stack-foot,7px) 0; }
}
.figure-reset { position:absolute; top:12px; right:12px; z-index:3; min-height:26px; padding:0 10px; border:1px solid #c8ccd0; border-radius:4px; background:#fff; color:var(--ink);
  font-size:.68rem; font-weight:750; cursor:pointer; }
.figure-reset[hidden] { display:none; }
.figure-reset:disabled { color:#a0a4a8; cursor:not-allowed; }
.on-part-note { max-width:34ch; padding:0 16px; text-align:center; }
.on-part-canvas { position:absolute; inset:0; width:100%; height:100%; display:block; cursor:grab; user-select:none; }
.on-part-canvas:active { cursor:grabbing; }
.on-part-canvas[hidden] { display:none; }

/* The figures' controls in their frames (the user's request of 2026-11-06: "这个可以把button做进示意图框里面放在左侧排列
   更简洁; 同时字可以更简略; 而且可以统一, 3d view/2d view"; index.html .figure-tools): each figure's switch, then the 2D
   view's zoom, over its frame's top left, 12 px in, in one row, the pointer through the row's gaps to the figure; Reset
   view over the top right (.figure-reset: the registration's 3D view, the sensor on the part).  What a figure draws at
   its home stays off them: a 2D view whose board would reach under them starts its window under them (app.js fitScene,
   else as before), the plan view's scene shrinks where its boards would (app.js clearPlanOfControls, --plan-fit), and
   the sensor on the part's pane keeps a 40 px band at its top (a transparent border, the gradient under it; the pane's
   size as before, its canvas the room below; the Library's shots set border 0 and take the whole pane, as before). */
.canvas-wrap,.stack-figure { position:relative; }
.figure-tools { position:absolute; top:12px; left:12px; z-index:3; display:flex; flex-wrap:wrap; align-items:center; gap:8px; max-width:calc(100% - 24px);
  pointer-events:none; }
.figure-tools > * { pointer-events:auto; }
/* A frame narrower than the row at its size (a phone's): the segments and the zoom tighter, every button still 24 px
   or more each way, so the row fits in one line down to a 360 px wide page; narrower it wraps, and the 2D view's window
   starts under both rows where the board would lie under them (app.js fitScene). */
.canvas-wrap { container:figure-frame / inline-size; }
@container figure-frame (max-width:379.98px) {
  .canvas-wrap .figure-tools { gap:4px; }
  .canvas-wrap .figure-tools .preview-tabs button { padding-left:5px; padding-right:5px; }
  .canvas-wrap .figure-tools .zoom-tools button { min-width:24px; padding-left:5px; padding-right:5px; }
  .canvas-wrap .figure-tools .zoom-tools button[data-zoom="out"] { padding-left:6px; }
  .canvas-wrap .figure-tools .zoom-tools button[data-zoom="reset"] { padding-left:6px; padding-right:7px; }
  .canvas-wrap .figure-tools .zoom-tools output { margin-left:4px; }
  .canvas-wrap .figure-tools .zoom-tools { padding-right:7px; } }
.stacked-canvas.on-part-view { border-top:40px solid transparent; background-origin:border-box; }
