/* Viewer-specific layout. Tokens and components come from styles.css. */

/* The point of this page is the model, so the stage takes the window and the
   controls float over it. Only the masthead keeps a row of its own. */
.viewer-page { height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
.viewer-page .masthead { flex: none; }
/* No .wrap on the masthead: it runs the full width, like the stage under it. */

.viewer-layout { flex: 1; min-height: 0; position: relative; }

.viewer-stage { position: absolute; inset: 0; overflow: hidden; }
.viewer-stage canvas { display: block; width: 100%; height: 100%; }

/* The view cube. Its canvas carries a wide transparent margin the cube never
   reaches into, so the home button sits in that dead corner without covering
   anything clickable. */
/* Clear of the right-hand panel, which reaches down the side of the stage. */
/* The cube scales with the window: on a laptop it would otherwise take a
   quarter of the height, and on a small window it would sit on the model. vmin
   rather than vw, so a short wide window shrinks it too. */
.viewcube {
  position: absolute;
  right: 306px;
  bottom: 12px;
  z-index: 1;
  --cube: clamp(116px, 22vmin, 264px);
  --home: clamp(32px, 6vmin, 48px);
}
.viewcube.panel-hidden { right: 12px; }
/* Named through the parent: `.viewer-stage canvas` sets the stage canvas to
   100% and would otherwise win on specificity, which is what used to pin the
   cube at its intrinsic 264px whatever the stylesheet asked for. */
.viewcube .axes { display: block; width: var(--cube); height: var(--cube); cursor: pointer; }
.viewcube #view-home {
  position: absolute;
  right: 0;
  bottom: 0;
  width: var(--home);
  height: var(--home);
  padding: 0;
  font: inherit;
  font-size: calc(var(--home) * 0.58);
  line-height: 1;
  border-radius: 10px;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  box-shadow: 0 6px 24px rgb(0 0 0 / 18%);
  cursor: pointer;
}
.viewcube #view-home:hover { border-color: var(--accent); color: var(--accent); }

.viewer-controls {
  position: absolute;
  top: 12px;
  width: 282px;
  max-height: calc(100% - 24px);
  display: grid;
  /* minmax(0, 1fr) rather than the implicit auto track: an auto track is at
     least min-content wide, and a file input showing a long filename has a
     min-content width of the whole filename. That is what used to push the
     panel sideways and give it a horizontal scrollbar. */
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  align-content: start;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Over the model rather than beside it, so it has to stay readable against
     whatever is behind it. */
  background: color-mix(in srgb, var(--surface-1) 92%, transparent);
  backdrop-filter: blur(8px);
  box-shadow: 0 6px 24px rgb(0 0 0 / 18%);
}
.viewer-controls[hidden] { display: none; }
.panel-left { left: 12px; }
.panel-right { right: 12px; }
/* The Foam Designer stands beside the parts panel, to its left, short of the
   view cube below; with the parts panel folded away it takes its place, under
   the Show button. */
.panel-foam {
  right: 306px;
  top: 12px;
  max-height: calc(100% - 36px - clamp(116px, 22vmin, 264px));
}
.panel-foam.parts-hidden { right: 12px; top: 52px; }
.panel-toggle[hidden] { display: none; }

/* A panel's heading, with its Hide button beside it. */
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.panel-title { margin: 0; font-size: 1rem; font-weight: 700; }
.panel-hide {
  font: inherit; font-size: 0.78rem; padding: 4px 9px; border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); background: var(--surface-1); color: var(--text-secondary); cursor: pointer;
}
.panel-hide:hover { color: var(--text-primary); border-color: var(--accent); }
.photo-field .file-button { width: 100%; padding: 7px 10px; }
/* :not([hidden]) because this rule would otherwise outrank styles.css's
   .field[hidden] - same specificity, later sheet - and a field switched
   off in the markup would still be laid out. */
.viewer-controls .field:not([hidden]) {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px;
}
/* The filename crops inside the control instead of widening the panel. */
.viewer-controls input[type="file"] { min-width: 0; max-width: 100%; overflow: hidden; }
.viewer-controls select,
.viewer-controls input[type="file"] {
  font: inherit;
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  color: var(--text-primary);
}
.viewer-controls input[type="range"] { width: 100%; accent-color: var(--accent); }

.panel-toggle {
  position: absolute;
  top: 12px;
  z-index: 2;
  left: 12px;
  font: inherit;
  font-size: 0.78rem;
  padding: 5px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--surface-1) 92%, transparent);
  color: var(--text-secondary);
  cursor: pointer;
}
.panel-toggle[aria-expanded="true"] { left: 306px; }
.panel-toggle-right { left: auto; right: 12px; }
.panel-toggle-right[aria-expanded="true"] { left: auto; right: 306px; }
.panel-toggle:hover { color: var(--text-primary); }

.section-controls,
.measure-controls,
.part-list,
.turn-controls,
.project-controls,
.foam-controls {
  display: grid;
  /* A fieldset's min-inline-size is min-content by default, so one long
     filename in the list would hold the whole panel open however narrow the
     column is told to be. Both halves of that have to be said. */
  min-inline-size: 0;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
/* A class rule outranks the UA's own [hidden], so both need saying. */
.part-list[hidden],
.turn-controls[hidden] { display: none; }

/* A swatch per part, so the colours in the scene and the list agree. */
.part-list ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px;
}
/* The part rows are built in JS, so they miss the shared .check rule; the
   panel's own controls carry the accent instead of the browser's default. */
.viewer-controls input[type="checkbox"],
.viewer-controls input[type="radio"],
.part-list input[type="checkbox"],
.part-list input[type="radio"] { accent-color: var(--accent); }
.part-list li { display: flex; align-items: center; gap: 7px; font-size: 0.83rem; min-width: 0; }
.part-list li > label { min-width: 0; }
.part-list li.part-child { padding-left: 16px; }
/* The swatch is a real colour input, styled down to the square it used to be:
   clicking it opens the browser's own picker. */
.part-list .swatch {
  width: 14px;
  height: 14px;
  padding: 0;
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--text-primary) 30%, transparent);
  background: none;
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  cursor: pointer;
}
.part-list .swatch::-webkit-color-swatch-wrapper { padding: 0; }
.part-list .swatch::-webkit-color-swatch { border: none; border-radius: 2px; }
.part-list .swatch::-moz-color-swatch { border: none; border-radius: 2px; }
.part-list .swatch:hover { border-color: var(--accent); }
.part-list label { display: flex; align-items: center; gap: 7px; cursor: pointer; }
.part-list .part-group > label { font-weight: 650; }
.part-list .part-count { color: var(--text-muted); font-variant-numeric: tabular-nums; }

.turn-buttons,
.button-row { display: flex; gap: 6px; }
.turn-buttons button,
.button-row button {
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 6px 0;
  flex: 1;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  color: var(--text-primary);
  cursor: pointer;
}
.turn-buttons button:hover,
.button-row button:hover { border-color: var(--accent); color: var(--accent); }
.turn-buttons .turn-reset { flex: 1.9; font-weight: 500; }
/* A tool that is on says so, the same way the view cube's faces do. */
.button-row button[aria-pressed="true"] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-1));
  color: var(--accent);
}

/* One reading per row: the distance, then the gap along each axis under it. */
.measure-list { border-top: 0; padding-top: 0; gap: 6px; }
.measure-list:empty { display: none; }
.measure-list dt { font-variant-numeric: tabular-nums; }

/* A part the user brought. The name selects it, the box shows it, the cross
   takes it away, so the three jobs do not fight over the same click. */
.part-list .part-file { display: flex; align-items: center; gap: 6px; }
.part-list .part-pick {
  font: inherit;
  font-size: 0.83rem;
  font-weight: 650;
  text-align: left;
  flex: 1;
  min-width: 0;
  padding: 2px 4px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-primary);
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A body's row sits under its file's, so it is named more quietly. */
.part-list .part-pick-body { font-weight: 500; display: flex; gap: 7px; align-items: center; }
.part-list .part-pick[aria-pressed="true"] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}
/* The photo's own window, from its row: beside the cross, quieter than the name. */
.part-list .part-edit {
  font: inherit;
  font-size: 0.75rem;
  line-height: 1;
  flex: none;
  padding: 4px 7px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  color: var(--text-secondary);
  cursor: pointer;
}
.part-list .part-edit:hover { border-color: var(--accent); color: var(--accent); }
.part-list .part-drop {
  font: inherit;
  font-size: 0.95rem;
  line-height: 1;
  flex: none;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  color: var(--text-secondary);
  cursor: pointer;
}
.part-list .part-drop:hover { border-color: var(--critical); color: var(--critical); }

.viewer-summary { display: grid; gap: 8px; margin: 0; border-top: 1px solid var(--border); padding-top: 12px; }
.viewer-summary > div { display: grid; gap: 1px; min-width: 0; }
/* A CAD filename has no spaces to break at, so it is told to break anywhere. */
.viewer-summary dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.viewer-summary dt {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 650;
}
.viewer-summary dd { margin: 0; font-size: 0.88rem; font-variant-numeric: tabular-nums; }

.status { font-size: 0.83rem; color: var(--text-secondary); margin: 0; min-height: 1.2em; }
.status.bad { color: var(--critical); font-weight: 600; }
/* The viewer's own line, over the view rather than down a panel: it shows only
   while there is something to say, and never takes a click meant for the model.
   Across the top, between the panels - the width left between them is what it
   may take, so it never runs under one. */
.stage-status {
  position: absolute; left: 50%; top: 12px; transform: translateX(-50%); z-index: 2;
  max-width: min(44ch, calc(100% - 620px));
  margin: 0; padding: 7px 11px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--surface-1) 92%, transparent);
  backdrop-filter: blur(8px); box-shadow: var(--shadow);
  text-align: center; pointer-events: none; min-height: 0;
}
.stage-status:empty { display: none; }

/* Narrow screens cannot spare the width for a floating panel, so it goes back
   to being a column above the stage and the page scrolls again. */
@media (max-width: 1100px) {
  /* Two floating panels need the width of a desktop; below that the parts
     panel tucks under the controls rather than overlapping the model. */
  .panel-right { right: 12px; top: auto; bottom: 12px; max-height: 45%; }
  .panel-foam, .panel-foam.parts-hidden { right: 12px; top: 12px; max-height: 45%; }
  .viewcube, .viewcube.panel-hidden { right: 12px; bottom: 12px; }
}

@media (max-width: 760px) {
  /* Nothing floats beside the stage down here, so the line may have its width. */
  .stage-status { max-width: calc(100% - 24px); }
  .viewer-page { height: auto; overflow: visible; }
  .viewer-layout { display: grid; gap: 12px; padding: 12px; }
  .viewer-stage { position: relative; inset: auto; height: 58vh; border-radius: var(--radius-sm); }
  .viewer-controls {
    position: relative;
    inset: auto;
    width: auto;
    max-height: none;
    backdrop-filter: none;
    box-shadow: none;
  }
  .panel-toggle { display: none; }
}

/* ---------- the foam insert ---------- */

.foam-controls[hidden], .foam-body[hidden], .foam-cut[hidden] { display: none; }
.foam-body, .foam-cut { display: grid; gap: 8px; }
.foam-cut { border-top: 1px dashed var(--border); padding-top: 8px; }
.foam-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 8px; }
.foam-grid .field[hidden] { display: none; }
.foam-controls input[type="number"],
.foam-controls input[type="text"],
.foam-controls select { width: 100%; min-width: 0; }
.foam-cuts { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px; }
.foam-cuts li { display: flex; align-items: center; gap: 6px; }
.foam-cuts .foam-cut-name {
  flex: 1; min-width: 0; text-align: left; font: inherit; font-size: 0.82rem;
  padding: 4px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-1); color: var(--text-primary); cursor: pointer;
}
.foam-cuts .foam-cut-name[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
.foam-controls input[type="number"],
.foam-controls input[type="text"] {
  font: inherit;
  font-size: 0.88rem;
  padding: 6px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  color: var(--text-primary);
}
.foam-controls input[type="number"] { font-variant-numeric: tabular-nums; }
.foam-controls input[type="checkbox"] { accent-color: var(--accent); }
.foam-cuts .remove {
  flex: none; width: 26px; height: 26px; padding: 0; font: inherit; font-size: 1rem; line-height: 1;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-1); color: var(--text-muted); cursor: pointer;
}
.foam-cuts .remove:hover { border-color: var(--critical); color: var(--critical); }
.button-row button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.button-row button.primary:hover { filter: brightness(1.06); color: #fff; }
.foam-from-part { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 8px; align-items: end; }
.foam-from-part button {
  font: inherit; font-size: 0.82rem; font-weight: 600; padding: 7px 8px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-1); color: var(--text-primary); cursor: pointer;
}
.foam-from-part button:hover { border-color: var(--accent); color: var(--accent); }
.foam-photos { display: grid; gap: 6px; }
.foam-photos .file-button { justify-content: center; }

/* The photo dialog: the photo as big as the window lets it be - the corners are
   clicked on it, and the outline is dragged into shape point by point, which
   both want every pixel the window can spare. */
.photo-dialog {
  width: calc(100vw - 24px);
  max-width: 1800px;
  height: calc(100dvh - 24px);
  max-height: calc(100dvh - 24px);
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-1);
  color: var(--text-primary);
  box-shadow: var(--shadow);
}
.photo-dialog[open] { display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto; gap: 10px; }
.photo-dialog::backdrop { background: rgba(11, 11, 11, 0.45); }
.photo-head { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; justify-content: space-between; }
.photo-head h2 { font-size: 1.1rem; margin: 0; }
.photo-sheet { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; font-weight: 600; }
/* An author's display rule beats the browser's [hidden], so the sheet asked
   for on the first step says so itself when it is put away on the second. */
.photo-sheet[hidden] { display: none; }
.photo-sheet select { font: inherit; padding: 6px 8px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); }
.photo-body { display: grid; gap: 12px; grid-template-columns: minmax(0, 1fr); align-items: stretch; min-height: 0; }
/* The settings beside the picture while it is being tuned, under it when the
   window is too narrow to put them anywhere else. */
.photo-body.tuning { grid-template-columns: minmax(0, 1fr) 260px; }
/* Too narrow to put the settings beside the picture: they go under it, and the
   stage keeps a height of its own rather than being stretched to the row. */
@media (max-width: 720px) {
  .photo-body.tuning { grid-template-columns: minmax(0, 1fr); align-items: start; overflow: auto; }
  .photo-stage { height: 52dvh; }
}
.photo-tune { display: grid; gap: 8px; align-content: start; min-inline-size: 0; overflow: auto; }
.photo-tune #photo-refind {
  font: inherit; font-size: 0.8rem; justify-self: start; padding: 5px 10px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-1); color: var(--text-secondary); cursor: pointer;
}
.photo-tune #photo-refind:hover { border-color: var(--accent); color: var(--accent); }
.photo-tune #photo-refind[hidden] { display: none; }
.photo-tune input[type="range"]:disabled { opacity: 0.4; }
.photo-tune[hidden] { display: none; }
.photo-tune input[type="number"] {
  font: inherit; font-size: 0.88rem; padding: 6px 8px; width: 100%; min-width: 0;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-1); color: var(--text-primary);
}
.photo-stage { min-height: 0; display: grid; place-items: center; background: #e7e6e2; border-radius: var(--radius-sm); overflow: hidden; }
.photo-stage canvas { display: block; cursor: crosshair; touch-action: none; }
.photo-actions { justify-content: flex-end; }
.photo-actions button { flex: 0 1 160px; padding: 7px 12px; }
.button-row button:disabled { opacity: 0.45; cursor: default; }

/* The Foam Designer's heading, and the switch that turns it on. */
.foam-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.foam-title { margin: 0; font-size: 1rem; font-weight: 700; }
.beta-tag {
  display: inline-block; margin-left: 4px; padding: 1px 6px; border-radius: 999px;
  background: #e0483f; color: #fff; font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase; vertical-align: 2px;
}
.switch { position: relative; display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 0.82rem; font-weight: 600; color: var(--text-secondary); }
/* The checkbox itself lies over the whole switch, unseen: it is what is clicked. */
.switch input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; z-index: 1; }
.switch-track {
  position: relative; width: 38px; height: 22px; flex: none; border-radius: 999px;
  background: var(--border-strong); transition: background 0.15s;
}
.switch-track::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgb(0 0 0 / 30%); transition: transform 0.15s;
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.panel-foam .foam-body { border-top: 1px solid var(--border); padding-top: 12px; }

