*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --bg: #f8fafc;          /* app background */
  --surface: #ffffff;     /* panels, cards, inputs */
  --preview-bg: #f1f5f9;  /* preview canvas backdrop */
  --border: #e2e8f0;
  --primary: #2563eb;
  --primary-hover: #1d4ed8;
  --primary-soft: #eff6ff;
  --primary-ring: rgba(37, 99, 235, 0.14);
  --danger: #dc2626;
  --warning: #d97706;
  --text: #0f172a;        /* headings, values */
  --muted: #64748b;       /* secondary text, labels */
  --faint: #94a3b8;       /* tertiary: sheet labels, hints */
  --radius: 8px;
  --radius-sm: 6px;
  /* One .image-row: the info block (name / dims / warning slot) stacked above the
     controls block (two 34px pills + gap), plus 28px of vertical padding and the
     1px divider. The thumbnail spans both rows and is shorter, so it never sets
     the height. Used to cap the list at ~4 rows. */
  --image-row-h: 150px;
}

/* The page itself never scrolls: the viewport is split into a fixed left panel
   and a scrollable preview. Anything that scrolls does so inside its own box. */
body {
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 14px;
  background: var(--bg);
  color: var(--text);
  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

/* ── Header ──────────────────────────────────────────────────────────────── */
header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 0 20px;
  height: 48px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  z-index: 10;
}

/* Doubles as the link back to the landing page. */
.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-sm);
}

.brand:hover h1 { color: var(--primary); }

.brand:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-ring);
}

.brand-icon {
  display: flex;
  color: var(--primary);
}

.brand-icon svg {
  width: 18px;
  height: 18px;
}

header h1 {
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.021em;
  color: var(--text);
}

header .subtitle {
  font-size: 0.75rem;
  color: var(--faint);
  padding-left: 12px;
  border-left: 1px solid var(--border);
  letter-spacing: -0.005em;
}

/* ── Layout ──────────────────────────────────────────────────────────────── */
/* Deliberately NOT height: 100vh — the header sits above this in the same flex
   column, so 100vh here would push the panels' bottom (the footer and its
   Generar PDF button) past the clipped edge of body. flex:1 + min-height:0
   already resolves to exactly "viewport minus header". */
.layout {
  display: grid;
  grid-template-columns: 420px 1fr;
  flex: 1;
  min-height: 0;
}

/* ── Left panel ──────────────────────────────────────────────────────────── */
/* Fixed: fills the column and never scrolls as a whole. Only the image list
   inside it scrolls, so the drop zone, the options and the footer button stay
   put no matter how many images are loaded. */
.panel-left {
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

/* Everything in the panel holds its natural height; .image-section below opts
   back into shrinking, so it's the one that gives when the viewport is short. */
.section {
  padding: 18px 20px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--muted);
  margin-bottom: 12px;
}

.section-title .step,
.panel-right h2 .step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  border-radius: 5px;
  background: var(--primary-soft);
  color: var(--primary);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0;
}

/* ── Drop zone ───────────────────────────────────────────────────────────── */
.drop-zone {
  border: 1.5px dashed #cbd5e1;
  border-radius: var(--radius);
  padding: 16px;
  text-align: center;
  cursor: pointer;
  background: #fcfdfe;
  transition: border-color 0.15s, background 0.15s;
  user-select: none;
}

.drop-zone:hover,
.drop-zone.dragover,
.drop-zone:focus-visible {
  border-color: var(--primary);
  background: var(--primary-soft);
  outline: none;
}

.drop-zone .dz-icon {
  display: block;
  color: var(--faint);
  margin-bottom: 6px;
  transition: color 0.15s;
}

.drop-zone .dz-icon svg {
  width: 22px;
  height: 22px;
}

.drop-zone:hover .dz-icon,
.drop-zone.dragover .dz-icon {
  color: var(--primary);
}

.drop-zone .dz-label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text);
}

.drop-zone .dz-label span {
  color: var(--primary);
}

.drop-zone .dz-sub {
  font-size: 0.6875rem;
  color: var(--faint);
  margin-top: 2px;
  letter-spacing: 0.02em;
}

/* ── Image list ──────────────────────────────────────────────────────────── */
/* The list box is the panel's only elastic child: it takes whatever height is
   left over, so its size depends on the WINDOW, never on how many images are
   loaded. Adding a 2nd image no longer pushes the options and the footer down —
   the extra row just fills space the box already had, and past that the box
   scrolls internally.
   - basis = 4 rows: the box's natural size, so it can't collapse to nothing.
   - grow  = 1: eats the leftover height, which is what actually pins the
     options and the footer to the bottom of the panel. A fixed height can't do
     that — it only makes the box taller and leaves the same slack underneath.
   - shrink = 1 + min-height:0: on a short window the box gives height back
     instead of pushing the Generar PDF button out of the clipped panel. */
.image-section {
  padding: 0;
  flex: 1 1 calc(4 * var(--image-row-h));
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

#image-list {
  display: flex;
  flex-direction: column;
}

#empty-state {
  padding: 28px 24px;
  text-align: center;
  color: var(--faint);
  font-size: 0.8125rem;
}

/* Thumb spans both rows; the name/dimensions sit on row 1 and the controls get
   the full remaining width on row 2, which is what lets the size fields carry
   inline labels without crowding the file name. */
.image-row {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: 12px;
  row-gap: 10px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  align-items: start;
  transition: background 0.12s;
}

.image-row:hover { background: #fafcfe; }

.thumb-wrap {
  grid-row: 1 / 3;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--preview-bg);
  border: 1px solid var(--border);
  flex-shrink: 0;
}

.thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* min-width: 0 is the critical fix: without it, a grid cell with 1fr won't
   shrink below its content's intrinsic width, causing horizontal overflow */
.image-info {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  overflow: hidden;
  padding-top: 1px;
}

.image-name {
  font-weight: 500;
  font-size: 0.8125rem;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.image-dims {
  font-size: 0.6875rem;
  color: var(--faint);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

.dpi-warn {
  font-size: 0.6875rem;
  margin-top: 4px;
  line-height: 1.35;
}

.dpi-warn:empty { display: none; }
.dpi-warn.warning { color: var(--warning); }
.dpi-warn.error   { color: var(--danger); font-weight: 500; }

.image-controls {
  grid-column: 2;
  grid-row: 2;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

/* W [🔒] H — one joined pill, with the lock as the divider between the fields */
.size-row {
  display: flex;
  align-items: stretch;
  height: 34px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
  transition: border-color 0.15s, box-shadow 0.15s;
  align-self: flex-start;
}

.size-row:focus-within {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-ring);
}

/* Labels sit inline, to the left of their field */
.size-row label,
.copies-row label {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  padding: 0 8px;
  min-width: 0;
}

.size-row label span,
.copies-row label span {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

/* Copies pill + remove button */
.copies-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.copies-row label {
  height: 34px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  transition: border-color 0.15s, box-shadow 0.15s;
}

.copies-row label:focus-within {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-ring);
}

/* Fields are borderless: the pill around them draws the border and focus ring */
.image-controls input[type=number] {
  width: 46px;
  height: 100%;
  padding: 0;
  border: none;
  background: transparent;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  text-align: right;
}

.image-controls input[type=number]:focus { outline: none; }

/* Lock button sits between the two size inputs, drawn as the pill's divider */
.btn-lock {
  flex-shrink: 0;
  width: 32px;
  padding: 0;
  background: #fbfcfd;
  border: none;
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
  border-radius: 0;
  cursor: pointer;
  font-size: 0.75rem;
  line-height: 1;
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s, color 0.15s;
}

.btn-lock:hover { background: var(--preview-bg); }

.btn-lock.locked {
  background: var(--primary-soft);
  color: var(--primary);
}

/* Only surfaces on hover / keyboard focus, so the resting row stays quiet */
.btn-remove {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  background: none;
  border: none;
  color: var(--faint);
  cursor: pointer;
  font-size: 0.75rem;
  padding: 0;
  border-radius: var(--radius-sm);
  line-height: 1;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s, color 0.15s;
}

.image-row:hover .btn-remove,
.btn-remove:focus-visible { opacity: 1; }

.btn-remove:hover {
  background: #fef2f2;
  color: var(--danger);
}

/* ── Options ─────────────────────────────────────────────────────────────── */
.options-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.option-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 24px;
}

.option-row label {
  font-size: 0.8125rem;
  color: var(--muted);
}

select {
  appearance: none;
  -webkit-appearance: none;
  padding: 7px 30px 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text);
  background-color: var(--surface);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 14px;
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
}

select:hover { border-color: #cbd5e1; }

select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-ring);
}

input[type=checkbox] {
  width: 16px;
  height: 16px;
  accent-color: var(--primary);
  cursor: pointer;
}

/* ── Advanced-options gear + popover ─────────────────────────────────────── */
/* The gear lives at the right end of the "2 · Opciones" header and anchors the
   popover directly beneath itself. */
.gear-wrap {
  position: relative;
  margin-left: auto;
}

.options-gear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}

.options-gear svg { width: 16px; height: 16px; }

.options-gear:hover,
.options-gear[aria-expanded="true"] {
  background: var(--primary-soft);
  color: var(--primary);
}

.options-gear:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-ring);
}

.options-popover {
  position: absolute;
  /* Opens UPWARD by default: the gear sits low in the left panel, so growing
     downward would push the popover off the bottom of the screen. JS picks the
     final direction (popover-up / popover-down) from the available space. */
  bottom: calc(100% + 8px);
  right: 0;
  width: 240px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12);
  padding: 16px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* .section-title is uppercase + tracked; reset so the popover reads normally */
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}

/* Direction chosen by positionPopover() in app.js from measured space. */
.options-popover.popover-up   { bottom: calc(100% + 8px); top: auto; }
.options-popover.popover-down { top: calc(100% + 8px); bottom: auto; }

/* Respect the hidden attribute even though the base rule sets display:flex */
.options-popover[hidden] { display: none; }

/* Inside the narrow popover, stack each label above its control */
.options-popover .option-row {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  min-height: 0;
}

.options-popover .custom-select { width: 100%; }

/* ── Custom dropdowns ────────────────────────────────────────────────────── */
.custom-select { position: relative; }

/* The visible sheet-size dropdown gets a fixed width so choosing a shorter
   option (e.g. A3) doesn't resize the control — wide enough for the longest
   label ("Legal (216 × 356 mm)") to stay on one line. */
.options-grid .custom-select { width: 200px; }

.cs-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  background: #fff;
  font: inherit;
  font-size: 14px;
  color: #0f172a;
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
}

.cs-trigger:hover { border-color: #2563eb; }

.cs-trigger[aria-expanded="true"] {
  border-color: #2563eb;
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}

/* Keep the selected label on a single line; ellipsis only as a last resort. */
.cs-label {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.cs-arrow {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: #64748b;
}

.cs-options {
  position: absolute;
  top: calc(100% + 4px);
  /* Anchored to the RIGHT so, when an option is wider than the trigger, the list
     grows leftward instead of overflowing the panel's right edge. min-width keeps
     it at least as wide as the trigger; width:max-content lets it fit the longest
     option on a single line. */
  right: 0;
  left: auto;
  min-width: 100%;
  width: max-content;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.10);
  list-style: none;
  margin: 0;
  padding: 4px;
  z-index: 100;
  display: none;
}

.cs-trigger[aria-expanded="true"] + .cs-options { display: block; }

.cs-option {
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 13px;
  color: #0f172a;
  white-space: nowrap;
  cursor: pointer;
}

.cs-option:hover { background: #f1f5f9; }

.cs-option[aria-selected="true"] {
  background: #eff6ff;
  color: #2563eb;
  font-weight: 500;
}

/* ── Generate button & sheet count ──────────────────────────────────────── */
/* Not position:sticky — the panel doesn't scroll any more, so the footer is
   simply the last item in the column and is always on screen. */
.panel-footer {
  padding: 14px 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
}

.sheet-info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.75rem;
  color: var(--muted);
}

.sheet-info strong {
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* The two footer actions sit side by side and split the width evenly. */
.footer-buttons {
  display: flex;
  gap: 10px;
}

.footer-buttons .btn-generate,
.footer-buttons .btn-print {
  flex: 1;
  width: auto;
}

/* Secondary action: blue outline on white, mirroring the primary Generar PDF. */
.btn-print {
  background: var(--surface);
  color: var(--primary);
  border: 1px solid var(--primary);
  border-radius: var(--radius);
  padding: 11px 22px;
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  cursor: pointer;
  transition: background 0.15s, box-shadow 0.15s, opacity 0.15s;
}

.btn-print:hover:not(:disabled) {
  background: var(--primary-soft);
}

.btn-print:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-ring);
}

.btn-print:disabled {
  color: var(--faint);
  border-color: var(--border);
  cursor: not-allowed;
}

.btn-generate {
  width: 100%;
  background: var(--primary);
  color: #fff;
  border: none;
  border-radius: var(--radius);
  padding: 11px 22px;
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(37, 99, 235, 0.2);
  transition: background 0.15s, box-shadow 0.15s, opacity 0.15s;
}

.btn-generate:hover:not(:disabled) {
  background: var(--primary-hover);
  box-shadow: 0 2px 8px rgba(37, 99, 235, 0.28);
}

.btn-generate:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-ring);
}

.btn-generate:disabled {
  background: #cbd5e1;
  box-shadow: none;
  cursor: not-allowed;
}

/* ── Right panel – preview ───────────────────────────────────────────────── */
/* The only scroller on the page. min-height:0 lets it shrink inside the grid
   row; without it a tall stack of sheets would push the row taller than the
   viewport instead of scrolling. */
.panel-right {
  display: flex;
  flex-direction: column;
  padding: 20px 24px 32px;
  min-height: 0;
  overflow-y: auto;
  background: var(--preview-bg);
}

.panel-right h2 {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--muted);
  margin-bottom: 20px;
  flex-shrink: 0;
}

/* Nothing to preview yet — the empty state below carries the message instead. */
.panel-right:has(#preview-wrap:empty) h2 { display: none; }

/* flex: 0 0 auto so the sheets keep their real height inside the scroller
   instead of being squashed by the column's default shrinking. */
#preview-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 32px 28px;
  align-items: flex-start;
  justify-content: center;
  flex: 0 0 auto;
}

.preview-sheet {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sheet-label {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--faint);
  text-align: center;
  letter-spacing: 0.02em;
}

.preview-sheet canvas {
  border-radius: 3px;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
  display: block;
  background: #fff;
}

/* ── Empty state (preview) ───────────────────────────────────────────────── */
/* Driven entirely by :empty on the preview container, which app.js clears and
   refills — no JS involvement needed. */
.preview-empty { display: none; }

#preview-wrap:empty + .preview-empty {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  padding-bottom: 24px;
}

.pe-icon {
  color: #cbd5e1;
  margin-bottom: 12px;
}

.pe-icon svg {
  width: 56px;
  height: 56px;
}

.pe-title {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: -0.01em;
}

.pe-sub {
  font-size: 0.8125rem;
  color: var(--faint);
}

/* ── Scrollbars ──────────────────────────────────────────────────────────── */
.image-section::-webkit-scrollbar,
.panel-right::-webkit-scrollbar { width: 10px; }

.image-section::-webkit-scrollbar-thumb,
.panel-right::-webkit-scrollbar-thumb {
  background: #d7dee7;
  border-radius: 10px;
  border: 3px solid transparent;
  background-clip: content-box;
}

.image-section::-webkit-scrollbar-thumb:hover,
.panel-right::-webkit-scrollbar-thumb:hover { background-clip: content-box; background-color: #b9c3cf; }

/* ── Responsive ──────────────────────────────────────────────────────────── */
@media (max-width: 800px) {
  .layout {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
  }
  /* Stacked, there isn't room for a fixed panel with its own inner scroller:
     capping the list at 4 rows would push the footer button off a phone screen.
     Hand scrolling back to the panel as a whole and let the list run free. */
  .panel-left {
    max-height: 60vh;
    overflow-y: auto;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
  .image-section {
    max-height: none;
    overflow-y: visible;
  }
  header .subtitle { display: none; }
  .btn-remove { opacity: 1; }
}
