/* STL & OBJ Viewer — tool styles.
   Every value comes from a design token (§4.2). Scoped to .ftb-tool-stl so
   nothing here can reach another tool. */

/* ---------------------------------------------------------------- intake */

.ftb-tool-stl .ftb-stl-drop{display:flex;flex-direction:column;align-items:center;
  gap:var(--ftb-space-3);padding:var(--ftb-space-8) var(--ftb-space-5);
  background:var(--ftb-surface-2);border:2px dashed var(--ftb-border-strong);
  border-radius:var(--ftb-radius-lg);text-align:center;
  transition:border-color var(--ftb-transition),background var(--ftb-transition)}
.ftb-tool-stl .ftb-stl-drop[data-drag=over]{border-color:var(--ftb-primary);
  background:var(--ftb-primary-soft)}

/* The real <input type=file> stays in the DOM and stays focusable — it is
   only visually replaced by its own label. Hiding it with display:none would
   take it out of the tab order and make drag-and-drop the only way in, which
   is a straight keyboard failure. */
/* The picker is a <label> styled as a primary button. core.css sets
   `.ftb-tool label{color:var(--ftb-text)}` (0,1,1), which outranks
   `.ftb-btn--primary` (0,1,0), so the button shipped with dark text on indigo
   at 2.94:1. Measured live 2026-09-13 while building youtube-to-mp3. */
.ftb-tool-stl .ftb-stl-file,
.ftb-tool-stl .ftb-stl-file:hover{color:var(--ftb-on-primary)}
.ftb-tool-stl .ftb-stl-file{position:relative;overflow:hidden;cursor:pointer}
.ftb-tool-stl .ftb-stl-file input[type=file]{position:absolute;inset:0;width:100%;height:100%;
  opacity:0;cursor:pointer}
.ftb-tool-stl .ftb-stl-file:focus-within{outline:2px solid var(--ftb-primary);outline-offset:2px}

.ftb-tool-stl .ftb-stl-drophint{margin:0;font-size:var(--ftb-text-sm);color:var(--ftb-text-muted)}
.ftb-tool-stl .ftb-stl-samples{display:flex;flex-wrap:wrap;justify-content:center;
  gap:var(--ftb-space-3);margin-top:var(--ftb-space-2)}

.ftb-tool-stl .ftb-stl-progress{margin:var(--ftb-space-4) 0 0;padding:var(--ftb-space-3) var(--ftb-space-4);
  background:var(--ftb-surface-2);border-radius:var(--ftb-radius);
  font-size:var(--ftb-text-sm);color:var(--ftb-text-muted)}

/* --------------------------------------------------------------- verdict */

.ftb-tool-stl .ftb-stl-result{margin-top:var(--ftb-space-6)}

.ftb-tool-stl .ftb-stl-verdict{padding:var(--ftb-space-4) var(--ftb-space-5);
  border:1px solid currentColor;border-left-width:4px;border-radius:var(--ftb-radius);
  margin-bottom:var(--ftb-space-5)}
/* The colour pairs below are the ones core.css already uses for tool
   messages, so they are contrast-checked in both themes. Do not substitute
   a raw colour here without re-checking §9. */
.ftb-tool-stl .ftb-stl-verdict[data-state=pass]{background:var(--ftb-success-soft);color:var(--ftb-success)}
.ftb-tool-stl .ftb-stl-verdict[data-state=warn]{background:var(--ftb-warning-soft);color:var(--ftb-warning)}
.ftb-tool-stl .ftb-stl-verdict[data-state=fail]{background:var(--ftb-error-soft);color:var(--ftb-error)}
.ftb-tool-stl .ftb-stl-verdict-line{display:flex;flex-wrap:wrap;align-items:baseline;
  gap:var(--ftb-space-2) var(--ftb-space-4);margin:0}
.ftb-tool-stl .ftb-stl-verdict-line strong{font-size:var(--ftb-text-lg);
  letter-spacing:var(--ftb-ls-tight)}
.ftb-tool-stl .ftb-stl-filename{font-size:var(--ftb-text-sm);font-family:var(--ftb-mono);
  color:var(--ftb-text-muted);overflow-wrap:anywhere}
.ftb-tool-stl .ftb-stl-verdict-detail{margin:var(--ftb-space-2) 0 0;
  font-size:var(--ftb-text-sm);line-height:var(--ftb-lh-snug);color:var(--ftb-text)}

/* ---------------------------------------------------------------- layout */

.ftb-tool-stl .ftb-stl-split{display:grid;gap:var(--ftb-space-5);grid-template-columns:minmax(0,1fr)}
/* A grid item's default min-width is AUTO, not 0, so a wide descendant — the
   three-column weight table, or a select with a long option in it — makes the
   whole column wider than the grid and the page scrolls sideways. Measured at
   25px of overflow at 360px before this rule (§12). */
.ftb-tool-stl .ftb-stl-split > *{min-width:0}
@media (min-width:1024px){
  /* The numbers sit beside the model, not under it, so the verdict and the
     shape are on screen together. Every competitor gives the canvas the whole
     fold and pushes the data below it. */
  .ftb-tool-stl .ftb-stl-split{grid-template-columns:minmax(0,1.35fr) minmax(0,1fr)}
}

/* --------------------------------------------------------------- viewer */

/* Height is reserved before anything loads, so the canvas appearing never
   shifts the page (§10, CLS < 0.05). */
.ftb-tool-stl .ftb-stl-canvas-wrap{position:relative;width:100%;aspect-ratio:4/3;
  min-height:260px;background:var(--ftb-surface-2);border:1px solid var(--ftb-border);
  border-radius:var(--ftb-radius);overflow:hidden}
.ftb-tool-stl canvas{display:block;width:100%;height:100%;touch-action:none;cursor:grab}
.ftb-tool-stl canvas:active{cursor:grabbing}
.ftb-tool-stl canvas:focus-visible{outline:2px solid var(--ftb-primary);outline-offset:-2px}
.ftb-tool-stl .ftb-stl-nowebgl{position:absolute;inset:0;display:flex;align-items:center;
  margin:0;padding:var(--ftb-space-5);font-size:var(--ftb-text-sm);
  line-height:var(--ftb-lh-snug);color:var(--ftb-text-muted)}

.ftb-tool-stl .ftb-stl-viewbar{display:flex;flex-wrap:wrap;align-items:flex-end;
  gap:var(--ftb-space-3) var(--ftb-space-4);margin-top:var(--ftb-space-4)}
.ftb-tool-stl .ftb-field--inline{flex-direction:column;margin-bottom:0;min-width:11rem}
.ftb-tool-stl .ftb-field--inline select,
.ftb-tool-stl .ftb-field--inline input[type=number]{width:auto;min-width:11rem}
.ftb-tool-stl .ftb-btn--small{padding:0 var(--ftb-space-4);min-height:var(--ftb-tap);
  font-size:var(--ftb-text-sm)}

.ftb-tool-stl .ftb-stl-toggle{display:flex;align-items:center;gap:var(--ftb-space-2);
  min-height:var(--ftb-tap);font-size:var(--ftb-text-sm);cursor:pointer}
.ftb-tool-stl .ftb-stl-toggle input{width:24px;height:24px;flex:none;accent-color:var(--ftb-primary)}

/* Explicit rotate/zoom controls. 44px square — comfortably past the 24px
   floor in WCAG 2.2 SC 2.5.8, and this is a control people press repeatedly. */
.ftb-tool-stl .ftb-stl-nudge{display:flex;flex-wrap:wrap;gap:var(--ftb-space-2);
  margin-top:var(--ftb-space-3)}
.ftb-tool-stl .ftb-stl-nudge-btn{width:44px;height:44px;display:inline-flex;
  align-items:center;justify-content:center;font-size:var(--ftb-text-base);
  color:var(--ftb-text);background:var(--ftb-surface);
  border:1px solid var(--ftb-border-interactive);border-radius:var(--ftb-radius);cursor:pointer}
.ftb-tool-stl .ftb-stl-nudge-btn:hover{border-color:var(--ftb-primary);color:var(--ftb-primary)}

/* The legend always carries a count next to the swatch, so the highlight
   colours are never the only signal (§9). The same two numbers appear again
   in the checks table. */
.ftb-tool-stl .ftb-stl-legend{list-style:none;display:flex;flex-wrap:wrap;
  gap:var(--ftb-space-2) var(--ftb-space-5);margin:var(--ftb-space-3) 0 0;padding:0;
  font-size:var(--ftb-text-sm);color:var(--ftb-text-muted)}
.ftb-tool-stl .ftb-stl-legend-item{display:flex;align-items:center;gap:var(--ftb-space-2)}
.ftb-tool-stl .ftb-stl-swatch{width:1rem;height:.25rem;border-radius:var(--ftb-radius-sm);flex:none}
.ftb-tool-stl .ftb-stl-legend-item--boundary .ftb-stl-swatch{background:#e64040}
.ftb-tool-stl .ftb-stl-legend-item--nonmanifold .ftb-stl-swatch{background:#f2a627}

/* ---------------------------------------------------------------- stats */

.ftb-tool-stl .ftb-stl-stats h3,
.ftb-tool-stl .ftb-stl-h{font-size:var(--ftb-text-base);margin:var(--ftb-space-5) 0 var(--ftb-space-3)}
.ftb-tool-stl .ftb-stl-stats h3:first-child{margin-top:0}

.ftb-tool-stl .ftb-stl-dl{display:grid;grid-template-columns:auto 1fr;
  gap:var(--ftb-space-2) var(--ftb-space-4);margin:0;font-size:var(--ftb-text-sm)}
.ftb-tool-stl .ftb-stl-dl dt{color:var(--ftb-text-muted);white-space:nowrap}
.ftb-tool-stl .ftb-stl-dl dd{margin:0;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
/* A continuation row (the inches line under the millimetre one) has no term
   of its own but still needs to occupy the grid column. */
.ftb-tool-stl .ftb-stl-dt-cont{min-height:1px}

/* --------------------------------------------------------------- tables */

.ftb-tool-stl .ftb-stl-table{width:100%;border-collapse:collapse;
  font-size:var(--ftb-text-sm);line-height:var(--ftb-lh-snug)}
.ftb-tool-stl .ftb-stl-table th,
.ftb-tool-stl .ftb-stl-table td{padding:var(--ftb-space-3);text-align:left;
  border-bottom:1px solid var(--ftb-border);vertical-align:top}
.ftb-tool-stl .ftb-stl-table thead th{font-weight:var(--ftb-fw-semi);
  color:var(--ftb-text-muted);white-space:nowrap}
.ftb-tool-stl .ftb-stl-table tbody th{font-weight:var(--ftb-fw-medium)}
.ftb-tool-stl .ftb-stl-checks td:nth-child(3){font-variant-numeric:tabular-nums;
  white-space:nowrap}
.ftb-tool-stl .ftb-stl-checks td:last-child{color:var(--ftb-text-muted);min-width:18rem}

/* Status is a WORD, in a badge. The colour is a second signal and never the
   first, which is what §9 requires and what makes the text report identical
   in meaning to the table. */
.ftb-tool-stl .ftb-stl-status{display:inline-block;padding:2px var(--ftb-space-2);
  border:1px solid currentColor;border-radius:var(--ftb-radius-sm);
  font-size:var(--ftb-text-sm);font-weight:var(--ftb-fw-semi);white-space:nowrap}
.ftb-tool-stl .ftb-stl-status--pass{color:var(--ftb-success);background:var(--ftb-success-soft)}
.ftb-tool-stl .ftb-stl-status--warn{color:var(--ftb-warning);background:var(--ftb-warning-soft)}
.ftb-tool-stl .ftb-stl-status--fail{color:var(--ftb-error);background:var(--ftb-error-soft)}
.ftb-tool-stl .ftb-stl-status--skipped{color:var(--ftb-text-muted);background:var(--ftb-surface-2)}
.ftb-tool-stl .ftb-stl-checks tr[data-status=fail] th{color:var(--ftb-error)}

/* ------------------------------------------------------------------ fit */

.ftb-tool-stl .ftb-stl-custom{display:flex;flex-wrap:wrap;gap:var(--ftb-space-4);
  margin-bottom:var(--ftb-space-4)}
.ftb-tool-stl .ftb-stl-fit{margin:var(--ftb-space-3) 0 0;padding:var(--ftb-space-3) var(--ftb-space-4);
  border-left:4px solid currentColor;border-radius:var(--ftb-radius);
  font-size:var(--ftb-text-sm);line-height:var(--ftb-lh-snug)}
.ftb-tool-stl .ftb-stl-fit[data-state=pass]{background:var(--ftb-success-soft);color:var(--ftb-success)}
.ftb-tool-stl .ftb-stl-fit[data-state=warn]{background:var(--ftb-warning-soft);color:var(--ftb-warning)}
.ftb-tool-stl .ftb-stl-fit[data-state=fail]{background:var(--ftb-error-soft);color:var(--ftb-error)}

/* ----------------------------------------------------------- disclaimer */

.ftb-tool-stl .ftb-tool-disclaimer{margin:var(--ftb-space-6) 0 0;padding:var(--ftb-space-5);
  background:var(--ftb-surface-2);border-radius:var(--ftb-radius);
  font-size:var(--ftb-text-sm);line-height:var(--ftb-lh-snug);color:var(--ftb-text-muted)}

/* ------------------------------------------------------------ narrow UI */

@media (max-width:479px){
  .ftb-tool-stl .ftb-stl-drop{padding:var(--ftb-space-5) var(--ftb-space-4)}
  .ftb-tool-stl .ftb-stl-samples{width:100%}
  .ftb-tool-stl .ftb-stl-samples .ftb-btn{flex:1 1 auto}
  .ftb-tool-stl .ftb-stl-checks td:last-child{min-width:14rem}
  .ftb-tool-stl .ftb-stl-dl{grid-template-columns:1fr}
  .ftb-tool-stl .ftb-stl-dl dt{white-space:normal}
}
