:root {
  /* Neutrals: a cool, slightly desaturated gray family (not pure black/gray)
     — reads as precise/technical rather than generic SaaS default. */
  --color-bg: #f6f7f9;
  --color-surface: #ffffff;
  --color-surface-sunken: #eef1f5;
  --color-border: #dde1e7;
  --color-border-strong: #c3c9d1;
  --color-text: #14161a;
  --color-muted: #656c78;

  /* Accent: kept the established blue (already the right call — a
     laser-cutting order form isn't the place to invent a new brand hue) but
     given real roles instead of one flat value reused everywhere. */
  --color-accent: #2454ff;
  --color-accent-strong: #1a3fd1;
  --color-accent-soft: #e8edff;
  --color-accent-soft-hover: #d7e0ff;
  --color-on-accent: #ffffff;

  --color-error: #c62828;
  --color-error-bg: #fdecea;

  /* A real warn role — the old reset-button used one-off amber hex values
     found nowhere else in the system; "reset to original" is a caution
     action (undoes edits), not a validation error, so it earns its own
     token rather than reusing --color-error or being invented ad hoc. */
  --color-warn: #92400e;
  --color-warn-bg: #fef3e2;
  --color-warn-border: #f0b854;

  --radius: 12px;
  --radius-sm: 8px;
  --radius-pill: 999px;
  --shadow-card: 0 1px 2px rgba(20, 22, 26, 0.04), 0 2px 10px rgba(20, 22, 26, 0.06);

  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Theme the vendored dxf-preview widget (web/static/vendor/dxf-preview/)
     to match this page instead of its own defaults — it reads these same
     variable names from its light-DOM context. */
  --dxf-preview-accent: var(--color-accent);
  --dxf-preview-bg: var(--color-bg);
  --dxf-preview-border: var(--color-border);
  --dxf-preview-radius: var(--radius);
  --dxf-preview-text: var(--color-text);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  line-height: 1.4;
}

.page {
  max-width: 960px;
  margin: 0 auto;
  padding: 24px 16px 64px;
}

h1 {
  font-size: 1.5rem;
  margin: 0;
}

h2 {
  font-size: 1.1rem;
  margin: 0 0 8px;
}

.hint {
  color: var(--color-muted);
  margin-top: 0;
}

.drop-zone {
  border: 2px dashed var(--color-border);
  border-radius: var(--radius);
  padding: 32px 16px;
  text-align: center;
  cursor: pointer;
  background: var(--color-surface);
  margin: 16px 0;
  transition: border-color 0.15s ease, background 0.15s ease;
}

@media (prefers-reduced-motion: reduce) {
  .drop-zone {
    transition: none;
  }
}

.drop-zone.dragover {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}

.drag-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 9999;
  align-items: center;
  justify-content: center;
  background: rgba(36, 84, 255, 0.15);
  border: 4px dashed var(--color-accent);
  pointer-events: none;
}

.drag-overlay.active {
  display: flex;
}

.drag-overlay p {
  background: var(--color-surface);
  padding: 16px 28px;
  border-radius: var(--radius);
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--color-accent);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}

.page-heading {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

/* h2's own bottom margin (generic `h2` rule below) is asymmetric — as a
   flex child that adds extra space under its box only, which throws off
   align-items: center against the icon button next to it (same bug class
   already fixed on <h1> above). .page-heading's own margin-bottom already
   provides the gap after the row, so the heading itself doesn't need one. */
.page-heading h2 {
  margin: 0;
}

.help-icon-button {
  width: 22px;
  height: 22px;
  min-height: 0;
  padding: 0;
  border-radius: 50%;
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1;
  background: var(--color-accent-soft);
  border: 1px solid var(--color-accent);
  color: var(--color-accent-strong);
  cursor: pointer;
  flex-shrink: 0;
}

.help-icon-button:hover {
  background: var(--color-accent-soft-hover);
}

/* Unlike .drag-overlay (pointer-events: none, purely informational while
   dragging), this overlay is genuinely interactive — clicking the dimmed
   backdrop itself closes the modal (app.js checks e.target === helpModal
   so a click landing inside .help-modal-card doesn't bubble into a close). */
.help-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(20, 22, 26, 0.5);
  padding: 16px;
}

.help-modal[hidden] {
  display: none;
}

.help-modal-card {
  position: relative;
  background: var(--color-surface);
  border-radius: var(--radius);
  padding: 28px 24px 20px;
  max-width: 480px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: var(--shadow-card);
}

.help-modal-card h2 {
  margin-top: 0;
}

.help-modal-card li {
  margin-bottom: 8px;
}

.help-modal-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 28px;
  height: 28px;
  min-height: 0;
  padding: 0;
  border-radius: 50%;
  font-size: 1.1rem;
  line-height: 1;
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  color: var(--color-muted);
  cursor: pointer;
}

.order-error {
  color: var(--color-error);
  font-weight: 600;
  min-height: 1.2em;
  margin: 8px 0;
}

.group-controls {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 16px;
  margin: 16px 0;
}

.group-controls-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px 12px;
  align-items: center;
}

.row-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  align-items: center;
}

.row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.group-controls-row label,
.field-label {
  font-size: 0.9rem;
  color: var(--color-muted);
}

.group-controls-row + .group-controls-row {
  margin-top: 12px;
}

.group-controls-toprow {
  justify-content: space-between;
}

.toprow-buttons {
  display: flex;
  gap: 10px;
}

.select-all-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: var(--color-text);
  cursor: pointer;
}

.field-consent label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: normal;
  cursor: pointer;
}

/* Without this, .contact-form input's own width: 100% (meant for the text
   fields) also grabs this checkbox and stretches its box across the row. */
.field-consent input[type="checkbox"] {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.select-all-label input[type="checkbox"] {
  width: 20px;
  height: 20px;
}

#group-multiplier {
  width: 6em;
}

/* flex-wrap + min-width:0 on both children, unconditionally (not only
   inside the 480px media query below) — between 480px and ~750px there
   was no shrink protection at all, so this widget (especially once the
   custom-material input appears) would overflow its column instead of
   wrapping or shrinking. */
.material-control {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.material-control select {
  min-width: 0;
}

.material-custom-input {
  width: 10em;
  max-width: 100%;
  min-width: 0;
  flex: 1 1 auto;
}

section {
  margin: 24px 0;
}

.card-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.empty-hint {
  color: var(--color-muted);
  font-style: italic;
}

.file-card {
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 14px 18px;
  box-shadow: var(--shadow-card);
}

.file-card.has-error {
  border-color: var(--color-error);
  background: var(--color-error-bg);
  box-shadow: none;
}

/* The two-column part (body | controls) — kept separate from .file-card
   itself so the expanded DXF panel (appended straight to .file-card,
   full width) stacks cleanly below this row instead of becoming a third
   column inside it. */
.file-card-row {
  display: flex;
  align-items: stretch;
  gap: 16px;
}

/* Everything except the controls column — header, fields, filename preview
   — stacked as one block, the primary (left) side of the card. */
.file-card-body {
  flex: 1;
  min-width: 0;
}

.file-card-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  word-break: break-word;
}

.file-card-header input[type="checkbox"] {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.file-card-header span {
  flex: 1;
}

/* Secondary (right) side of the card: Reset/Remove, then — for a
   dxf/dwg cutting file — the thumbnail filling whatever height remains.
   .file-card's align-items: stretch makes this column match
   .file-card-body's real height, so the thumbnail scales with the whole
   card instead of being capped at the header row's height. */
.file-card-controls {
  display: flex;
  flex-direction: column;
  /* Buttons + thumbnail as a fixed-size block, vertically centered in
     whatever height .file-card's stretch gives this column — not the
     thumbnail itself stretching to fill it (that made its box a different
     rectangle on every card, tall on cards with more content above it,
     short on others, depending on .file-card-body's own height). */
  justify-content: center;
  gap: 10px;
  flex-shrink: 0;
  width: 148px;
}

.file-card-controls-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.file-card-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 10px;
  align-items: center;
}

.file-card-fields > div {
  min-width: 0;
  max-width: 100%;
}

.file-card-fields label {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-muted);
  display: block;
  margin-bottom: 2px;
}

.stepper {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.stepper button {
  min-width: 44px;
  min-height: 44px;
}

.stepper input {
  width: 3.5em;
  text-align: center;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}

.stepper input::-webkit-outer-spin-button,
.stepper input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.preview {
  margin-top: 20px;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 0.9rem;
  color: var(--color-accent);
  word-break: break-all;
}

/* Auto-fetched DXF thumbnail (web/static/vendor/dxf-preview/) — unrelated to
   .preview above, which is the computed *output filename* text, not an
   image of the drawing. */
.preview-thumb {
  position: relative;
  /* A real, fixed square, the same size on every card — not stretched to
     fill leftover card height (that made the box a different rectangle
     per card, depending on how tall .file-card-body happened to be).
     .file-card-controls centers this block vertically instead. */
  width: 100%;
  aspect-ratio: 1 / 1;
  flex-shrink: 0;
  /* Real breathing room between the drawing and the box edge — the
     service's own svg_margin_mm (app.js) helps too, but at this size its
     effect alone is too subtle to actually notice; this is what makes the
     margin visible. */
  padding: 8px;
  border: 1px solid var(--color-border);
  /* No rounding here on purpose: without it, a rounded corner would clip
     the drawing a second time right on top of the padding above. */
  border-radius: 0;
  /* Matches DXF_BACKGROUND_FILL in app.js, which tints the SVG's own
     rendered background rect the same color — same token, same reasoning:
     without this the tinted drawing sits inside a visibly mismatched
     lighter/whiter box. */
  background: var(--color-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

@media (prefers-reduced-motion: reduce) {
  .preview-thumb {
    transition: none;
  }
}

.preview-thumb.is-ready {
  cursor: pointer;
}

.preview-thumb.is-ready:hover {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-accent-soft);
}

.preview-thumb.is-ready:hover .preview-thumb-icon {
  color: var(--color-accent);
}

/* Always-visible affordance that the thumbnail expands on click — not
   hover-only, since touch devices have no hover to reveal it. Icon markup
   itself (DXF_ZOOM_ICON_SVG) lives in app.js. */
.preview-thumb-icon {
  position: absolute;
  right: 3px;
  bottom: 3px;
  padding: 3px;
  border-radius: 50%;
  color: var(--color-muted);
  background: color-mix(in srgb, var(--color-surface) 90%, transparent);
  box-shadow: 0 0 0 1px var(--color-border);
  transition: color 0.15s ease;
}

.preview-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.preview-thumb.is-unavailable {
  font-size: 0.7rem;
  color: var(--color-muted);
  text-align: center;
  line-height: 1.2;
  padding: 4px;
}

.dxf-panel-container {
  margin-top: 10px;
  width: 100%;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}

/* Facts read from the preview service's report (stock size, reliability,
   rotation) — a row of small tagged chips, not one muted text line, so the
   measured size actually reads as the headline fact it is instead of
   disappearing next to the panel above it. Lives right under the
   thumbnail inside .file-card-controls now, not a separate full-width row
   — the column's own `gap` already spaces it from the thumbnail above,
   and centering matches the thumbnail's own centered weight in this
   narrow column. */
.dxf-panel-info {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}

.dxf-fact {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  white-space: nowrap;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
}

.dxf-fact-label {
  font-weight: 500;
  opacity: 0.75;
  margin-right: 0.35em;
}

.dxf-fact-accent {
  background: var(--color-accent-soft);
  color: var(--color-accent-strong);
}

.dxf-fact-warn {
  background: var(--color-warn-bg);
  color: var(--color-warn);
  border-color: var(--color-warn-border);
}

.dxf-fact-muted {
  background: var(--color-surface-sunken);
  color: var(--color-muted);
}

.file-error {
  color: var(--color-error);
  font-size: 0.9rem;
  margin-top: 6px;
}

.remove-button {
  background: var(--color-error-bg);
  border: 1px solid var(--color-error);
  color: var(--color-error);
  border-radius: var(--radius-sm);
  min-height: 36px;
  padding: 4px 10px;
  cursor: pointer;
  flex-shrink: 0;
  font-weight: 600;
  transition: background-color 0.15s ease;
}

.remove-button:hover {
  background: color-mix(in srgb, var(--color-error) 14%, var(--color-error-bg));
}

/* "Reset to original values" undoes edits — a caution action, not a
   validation error, so it gets its own --color-warn role instead of
   reusing --color-error or one-off amber hex values found nowhere else. */
/* Neutral positive action, not warn/error like .reset-button/.remove-button
   — reuses the same soft-accent tone the generic unstyled <button> already
   gets (style.css's `button { ... }` rule), just spelled out explicitly
   since this needs the same compact box metrics as its .reset-button
   sibling in the same row. */
.download-button {
  background: var(--color-accent-soft);
  border: 1px solid var(--color-accent);
  color: var(--color-accent-strong);
  border-radius: var(--radius-sm);
  min-height: 36px;
  padding: 4px 10px;
  cursor: pointer;
  flex-shrink: 0;
  font-weight: 600;
  transition: background-color 0.15s ease;
}

.download-button:hover:not(:disabled) {
  background: var(--color-accent-soft-hover);
}

.reset-button {
  background: var(--color-warn-bg);
  border: 1px solid var(--color-warn-border);
  color: var(--color-warn);
  border-radius: var(--radius-sm);
  min-height: 36px;
  padding: 4px 10px;
  cursor: pointer;
  flex-shrink: 0;
  font-weight: 600;
  transition: background-color 0.15s ease;
}

.reset-button:hover {
  background: color-mix(in srgb, var(--color-warn-border) 30%, var(--color-warn-bg));
}

input, select, textarea, button {
  font: inherit;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  padding: 8px 10px;
}

/* 16px minimum on real form fields: below that, iOS Safari zooms the whole
   page in on focus (reads as "this field is broken"), and Android renders
   the native <select> popup at whatever font-size the element has, so a
   smaller inherited size shows up as tiny option text. */
input, select, textarea {
  font-size: 16px;
  max-width: 100%;
  background: var(--color-surface);
  color: var(--color-text);
}

select {
  text-overflow: ellipsis;
}

/* Buttons get a distinct accent-tinted look so they read as actions, not as
   more input fields — plain inputs/selects share the same white background,
   so an unstyled button was easy to mistake for another field. */
button {
  cursor: pointer;
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
  color: var(--color-accent-strong);
  font-weight: 600;
  transition: background-color 0.15s ease;
}

button:hover:not(:disabled) {
  background: var(--color-accent-soft-hover);
}

button:disabled {
  background: var(--color-surface-sunken);
  border-color: var(--color-border);
  color: var(--color-muted);
  cursor: not-allowed;
}

.contact-form .field {
  margin-bottom: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.contact-form input, .contact-form textarea {
  width: 100%;
}

.honeypot {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.submit-status {
  min-height: 1.4em;
  margin-bottom: 8px;
}

.submit-button {
  width: 100%;
  min-height: 48px;
  background: var(--color-accent);
  color: var(--color-on-accent);
  border: none;
  font-size: 1.05rem;
  font-weight: 600;
}

/* Without this, the generic button:hover:not(:disabled) rule (higher
   specificity than .submit-button alone) takes over and fades this solid
   primary button to the pale --color-accent-soft-hover meant for
   secondary buttons -- looks broken, like the CTA is disabling itself on
   hover instead of the expected "gets a shade darker" feedback. */
.submit-button:hover:not(:disabled) {
  background: var(--color-accent-strong);
}

:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: 2px;
}

/* Switches the card from two columns to one earlier than the 480px
   breakpoint below — .file-card-controls' fixed 148px+gap was permanently
   eating into .file-card-body's width all the way up to 480px, with no
   shrink protection in that range, which is what actually broke the
   thickness/material/qty fields around 570-630px (reported directly by
   testing a real resize): the fields never had a chance, regardless of
   how flexible the fields themselves are, while sharing the row with a
   column that refuses to shrink. Below this, .file-card-body gets the
   card's full width to itself; .file-card-fields still stays a wrapping
   row here (not forced into a column) since a full-width row is already
   roomy enough for thickness+material+qty side by side down to 480px. */
@media (max-width: 720px) {
  .file-card-row {
    flex-direction: column;
  }

  .file-card-controls {
    width: 100%;
  }

  .file-card-controls-buttons {
    justify-content: stretch;
  }

  .file-card-controls-buttons button {
    flex: 1;
  }
}

@media (max-width: 480px) {
  .group-controls-row {
    flex-direction: column;
    align-items: stretch;
  }

  .row-fields,
  .row-actions,
  .toprow-buttons {
    width: 100%;
  }

  .row-actions button,
  .toprow-buttons button {
    flex: 1;
  }

  /* .preview-thumb is otherwise width:100% of its (now full-width, thanks
     to the 720px breakpoint above) column — on an actual phone that ends
     up nearly as large as the expand panel itself. Capped here so it
     stays a quick visual anchor ("this is part X") instead of a
     redundant near-copy of the modal; real inspection still goes through
     the panel via zoom/pan, so the thumbnail doesn't need to be that big. */
  .preview-thumb {
    width: 200px;
    max-width: 100%;
    margin: 0 auto;
  }

  .file-card-fields {
    flex-direction: column;
    align-items: stretch;
  }

  .file-card-fields > div {
    width: 100%;
  }

  .material-control {
    display: flex;
  }

  .material-control select,
  .material-custom-input {
    flex: 1;
    min-width: 0;
  }
}
