/* ── Upscale module ───────────────────────────────────────────────────────── */
/* Two-panel layout, same fixed-viewport pattern as app.html/tiling.html:
   header + .upscale-layout fill exactly 100vh (body's height:100vh/
   overflow:hidden from app.css applies as-is, no override needed here).
   Loaded after app.css in upscale.html, so same-named selectors here win over
   app.css purely by source order (no !important needed) — same convention
   tiling.css uses. */

.upscale-layout {
  display: grid;
  grid-template-columns: 1fr 300px;
  flex: 1;
  min-height: 0;
}

/* ── Left: comparator area ──────────────────────────────────────────────── */
.upscale-compare-area {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: 24px;
  background: var(--preview-bg);
  overflow: hidden;
}

/* State 1 (no image yet): the dropzone is the hero of the whole page, not a
   sidebar-sized control — sized and centered like landing.html's, at up to
   3x the width app.html/tiling.html give theirs. */
#upload-panel {
  width: 100%;
  max-width: 480px;
}
.upscale-compare-area .drop-zone { padding: 56px 32px; }
.upscale-compare-area .drop-zone .dz-icon { width: 60px; height: 60px; margin-bottom: 18px; }
.upscale-compare-area .drop-zone .dz-icon svg { width: 26px; height: 26px; }
.upscale-compare-area .drop-zone .dz-label { font-size: 17px; }
.upscale-compare-area .drop-zone .dz-sub { font-size: 0.8125rem; }

/* State 2 (loaded, not upscaled yet): just the original image, fit inside
   the available space the same way a plain <img> does via object-fit —
   simpler than .compare-wrap below since there's no clip/handle overlay to
   keep aligned with it. */
/* :not([hidden]) rather than a bare class selector — an author rule that sets
   display unconditionally always beats the UA stylesheet's [hidden]{display:
   none}, since origin precedence (author > user-agent) wins over specificity.
   That exact bug shipped once already on .compare-empty in this file; every
   display-setting rule on a JS-toggled element here must be gated this way. */
.original-preview:not([hidden]) {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  border-radius: 10px;
  border: 1px solid var(--border);
}

/* Sidebar's own state-1 placeholder (state 1's real dropzone now lives in
   the main area above) — fills the column so it's not just a stray line of
   text floating at the top. */
.sidebar-hint:not([hidden]) {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* ── Trailing: sidebar ─────────────────────────────────────────────────────
   Fixed width, own scroller if content ever runs tall — mirrors app.css's
   .panel-left, but content-driven top-to-bottom instead of a footer-pinned
   layout, since this module has no persistent footer action.

   Note this module puts the sidebar on the opposite side from the other two, so
   its divider is on the LEADING edge where .panel-left's is on the trailing one.
   Both mirror correctly under RTL for the same reason: the flex row reverses
   itself and the logical border follows. */
.upscale-sidebar {
  background: var(--surface);
  border-inline-start: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: 0;
  overflow-y: auto;
  padding: 20px;
}

/* Sidebar itself is already the bordered "card" (mirrors .panel-left); these
   panels are mutually-exclusive states inside it, not stacked sub-cards, so
   they carry no box styling of their own. */
.upscale-panel { min-height: 0; }

/* ── Dropzone ──────────────────────────────────────────────────────────────
   The primary action on this page (vs. a small step in a sidebar), so it's
   sized closer to landing.html's hero dropzone than app.html's compact one. */
.drop-zone {
  border: 2px dashed #BFDBFE;
  border-radius: var(--radius);
  padding: 40px 24px;
  text-align: center;
  cursor: pointer;
  background: #fcfdfe;
  transition: border-color 0.18s, background 0.18s;
  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: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  margin-bottom: 14px;
}
.drop-zone .dz-icon svg { width: 22px; height: 22px; }
/* app.css's .drop-zone:hover .dz-icon turns the icon var(--primary) blue —
   correct for its own plain gray-on-white icon, but here .dz-icon is a solid
   blue circle with a white icon; inheriting that rule would turn the icon
   blue-on-blue and make it disappear on hover. Same selector, same
   specificity, later in source — this is what actually wins. */
.drop-zone:hover .dz-icon,
.drop-zone.dragover .dz-icon {
  color: #fff;
}
.drop-zone .dz-label { font-size: 15px; font-weight: 600; color: var(--text); }
.drop-zone .dz-label span { color: var(--primary); }
.drop-zone .dz-sub { font-size: 0.75rem; color: var(--faint); margin-top: 4px; letter-spacing: 0.02em; }

.upscale-error {
  margin-top: 14px;
  font-size: 13px;
  font-weight: 500;
  color: var(--danger);
}
.upscale-error a { color: var(--primary); text-decoration: underline; }

/* ── Loaded image row ──────────────────────────────────────────────────────── */
.upscale-source-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 20px;
}
.upscale-source-thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--preview-bg);
  flex-shrink: 0;
}
.upscale-source-info { min-width: 0; flex: 1; }
.upscale-source-name {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.upscale-source-dims { font-size: 0.75rem; color: var(--muted); margin-top: 2px; }

/* app.css's .btn-remove is absolutely positioned and hidden until
   ".image-row:hover" — a sidebar-list-row context this row doesn't have.
   Here it's just a plain, always-visible icon button at the end of the row. */
.upscale-source-row .btn-remove {
  position: static;
  opacity: 1;
  flex-shrink: 0;
}

.upscale-btn-block { width: 100%; }

/* ── Before/after comparator ───────────────────────────────────────────────
   The "after" (upscaled) image is the base layer, always fully painted. The
   "before" (original) sits on top inside a clipped wrapper that only reveals
   its left X% — so only ONE element needs an animated clip-path. Dragging the
   handle right grows X (more of the original, on top, shows); dragging left
   shrinks it (more of the enhanced base layer shows through). */
/* Sized via aspect-ratio (set from JS once the result's real dimensions are
   known) plus max-width/max-height:100% — the same "fit inside the box,
   preserve ratio" behavior a plain <img> gets from object-fit:contain, but
   needed here on the wrapper div so the absolutely-positioned clip/handle
   layers inside line up exactly with the visible image, no letterboxing. */
.compare-wrap {
  position: relative;
  max-width: 100%;
  max-height: 100%;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface);
  user-select: none;
  touch-action: none;
  cursor: ew-resize;
}
.compare-img {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.compare-before-clip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  clip-path: inset(0 50% 0 0);
}
.compare-before-clip .compare-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
/* Opacity is driven from JS (setComparePosition) based on handle position —
   each tag fades out as the slider nears the edge that would hide it. The
   before-tag also lives inside .compare-before-clip so it clips away with
   the rest of that layer at the extreme, but the opacity fade is what makes
   it a gradual fade instead of a hard cut. transition smooths keyboard
   (arrow-key) steps; drag already updates every frame so it doesn't need it. */
.compare-tag {
  position: absolute;
  top: 10px;
  padding: 3px 9px;
  border-radius: 99px;
  font-size: 0.6875rem;
  font-weight: 600;
  color: #fff;
  background: rgba(15, 23, 42, 0.55);
  letter-spacing: 0.02em;
  pointer-events: none;
  transition: opacity 0.1s linear;
}
/* The comparator stays physically LTR in every language: original on the left,
   upscaled on the right. It is content — two stacked images and a clip — not
   chrome, the same call made for the crop stage and the tile grid, and the two
   tags name which side is which without relying on reading order. Mirroring it
   would mean inverting the clip inset, the handle position, pctFromClientX() and
   the arrow keys in js/upscale.js, i.e. rewriting a widget that works in every
   locale for no legibility gain. Revisit if RTL users actually report it. */
.compare-tag-before { left: 10px; }
.compare-tag-after { right: 10px; }
.compare-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 0;
  transform: translateX(-50%);
  touch-action: none;
}
.compare-handle::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: -1.5px;
  width: 3px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.15);
}
.compare-handle-grip {
  position: absolute;
  top: 50%;
  left: 0;
  width: 36px;
  height: 36px;
  margin-left: -18px;
  margin-top: -18px;
  border-radius: 50%;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--primary);
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.28);
}
.compare-handle:focus-visible .compare-handle-grip {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-ring), 0 2px 10px rgba(15, 23, 42, 0.28);
}

/* ── Result info ───────────────────────────────────────────────────────────
   Two visually distinct sections: "Resolución" is an objective fact about
   the file; "Calidad de impresión" is only meaningful relative to the A4
   reference size named in its own copy — the border-top + spacing keeps a
   reader from lumping them together as the same kind of information. */
.result-section + .result-section {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.result-stat-label {
  display: block;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.result-stat-value {
  margin-top: 4px;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.result-quality-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 8px;
  margin-top: 6px;
}
.result-quality-context {
  font-size: 0.8125rem;
  color: var(--muted);
}
/* Reuses app.css's .dpi-badge/.dpi-green|yellow|orange|red colors, but this
   one is static info, not a click-to-expand control. */
.result-quality-row .dpi-badge {
  font-size: 0.9375rem;
  cursor: default;
  user-select: text;
}
.result-quality-hint {
  margin-top: 8px;
  font-size: 0.75rem;
  color: var(--faint);
  line-height: 1.4;
}

.upscale-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 20px;
}
.upscale-actions .btn-secondary,
.upscale-actions .custom-select { flex: 1 1 160px; }
.upscale-actions .btn-secondary:disabled { opacity: 0.6; cursor: default; }
/* .cs-trigger's own justify-content:space-between (app.css) is built for a
   real select — label hugging the left edge, chevron hugging the right. Here
   the trigger is one of three plain action buttons, so its content needs to
   center as a single block instead, matching its .btn-secondary siblings. */
.upscale-actions .cs-trigger {
  justify-content: center;
  gap: 6px;
}

.upscale-link-btn {
  display: block;
  width: 100%;
  margin-top: 16px;
  padding: 8px;
  background: none;
  border: none;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--muted);
  text-decoration: underline;
  cursor: pointer;
  text-align: center;
}
.upscale-link-btn:hover { color: var(--text); }

/* ── Mobile: a stacked, state-driven flow (≤768px, site-wide stack breakpoint) ─
   The page is already a state machine (empty / loaded / result) showing one
   panel in the compare area and one in the sidebar. On mobile the compare area
   sits ON TOP in every state — it's the focus each time (dropzone hero, loaded
   preview, or before/after comparator) — with the sidebar's controls below it.
   (The comparator itself is already touch-ready via Pointer Events, unchanged.) */
@media (max-width: 768px) {
  .upscale-layout {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr auto;   /* compare area fills; sidebar takes its content */
  }

  /* No order flip — DOM order already puts the compare area first. */
  .upscale-compare-area { padding: 16px; }

  /* Sidebar drops below the compare area, capped with its own scroller (same
     pattern app.css's .panel-left uses). */
  .upscale-sidebar {
    max-height: 55vh;
    gap: 16px;
    padding: 16px;
    border-inline-start: none;
    border-top: 1px solid var(--border);
  }

  /* State 1 (empty): the sidebar's only content is the redundant hint, so hide
     the whole sidebar and let the dropzone hero own the screen. In states 2/3
     #sidebar-hint carries the [hidden] attribute (showPanel sets .hidden), so
     this doesn't match and the config / result panel shows normally. */
  .upscale-sidebar:has(#sidebar-hint:not([hidden])) { display: none; }

  /* Dropzone: compact enough for a phone but still the page's hero. */
  .upscale-compare-area .drop-zone { padding: 32px 20px; }
  .upscale-compare-area .drop-zone .dz-icon { width: 48px; height: 48px; margin-bottom: 14px; }
  .upscale-compare-area .drop-zone .dz-icon svg { width: 22px; height: 22px; }
  .upscale-compare-area .drop-zone .dz-label { font-size: 15px; }
  .upscale-compare-area .drop-zone .dz-sub { font-size: 0.75rem; }

  /* State 2: taller 2×/4× cards and a full-height primary button for thumbs. */
  .scale-options { gap: 12px; }
  .scale-card { padding: 18px 8px; }
  .upscale-btn-block { padding: 14px 18px; }

  /* State 3: download menu + secondary actions each take the full width. */
  .upscale-actions .btn-secondary,
  .upscale-actions .custom-select { flex: 1 1 100%; }
}
