/* Mojavatar Maker - page-specific styles.
   The card layout, form language, buttons, steppers, sliders and the pinnable floating
   preview pane all come from /styles/tool-cards.css (shared with the pixel-art tag
   generator, which this page's design mirrors). This file keeps only what is unique to
   the 3D viewer: the stage and canvas, its overlays, the transform rails, the parts
   outliner, the body-type pills, the skin entry card, and the modals.
   Load order: tool-cards.css first, then this file. */

/* ===== VISIBILITY CONTRACT =====
   All show/hide goes through setVisible(), which writes the `hidden` attribute. The browser's own
   `[hidden] { display: none }` rule is weak: any class here with a `display` of its own would beat
   it and the element would refuse to hide. So state it loudly, once, and never write inline
   display from JS again. */
#viewer-section [hidden] {
  display: none !important;
}

/* Buttons do not inherit the page font by default (UA gives them Arial on Windows), and the
   tag generator's text buttons all carry the site font via their shared classes. This page's
   own button classes (.vp-btn, .vp-seg-btn, .vp-btn-text, ...) get the same treatment in one
   stroke; icon-only buttons render no text, so the blanket is safe. */
#viewer-section button,
.vp-modal button {
  font-family: inherit;
}

/* A dependent control (shadow intensity, outline width) is dimmed in place rather than hidden, so
   ticking its parent checkbox does not make the panel jump under the cursor. */
.is-disabled {
  opacity: 0.45;
}
.is-disabled input,
.is-disabled select,
.is-disabled button {
  cursor: not-allowed;
}

#viewer-section { margin-top: 20px; }

/* ============================================================================
   THE STAGE
   The screen inside the Preview card. The canvas carries its own height via
   aspect-ratio, which is WIDTH-driven, so a mobile URL bar hiding on scroll can
   never change it (the vh/dvh scroll-jump lesson).
   ============================================================================ */
/* .preview-card centers its children (align-items from tool-cards.css), so the stage
   must claim the full card width itself. */
#viewer-section .preview-card .vp-stage {
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 0;
  border-radius: 12px;
  overflow: hidden;
  background: #4a4a4a;
}
#viewer-section .vp-canvas-col {
  position: relative;
  width: 100%;
  min-width: 0;
  min-height: 0;   /* the canvas buffer size must never become the column's floor, or the
                      column can grow with the renderer but never shrink back (ratchet) */
  aspect-ratio: 16 / 9;
  max-height: 560px;
}

/* NOTHING may be placed inside #viewerContainer: the raycast maths assumes the
   container rect IS the canvas rect. Overlays are siblings, in .vp-overlay. */
#viewer-section .viewer-container {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  background: #4a4a4a;
  cursor: grab;
  touch-action: auto;
}
#viewer-section .viewer-container:active { cursor: grabbing; }
#viewer-section .viewer-container canvas {
  display: block;
  width: 100% !important;                   /* load-bearing: renderer.setSize writes inline px
                                               during the 512 to 4096 square exports */
  height: 100% !important;
  touch-action: none;                       /* the canvas eats the gesture; the page still
                                               scrolls from the gutters either side */
}

.viewer-loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 10px;
  color: #ccc;
  font-size: 15px;
  z-index: 2;
}
.viewer-loading .spinner { animation: spin 1s linear infinite; }
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Overlay layer: a SIBLING of #viewerContainer, never a child. */
#viewer-section .vp-overlay { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
#viewer-section .vp-overlay > * { pointer-events: auto; }

/* Drop target. The canvas accepts dropped files; this tells you so. */
#viewer-section .vp-drop {
  position: absolute; inset: 10px; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  border: 3px dashed rgba(255, 255, 255, 0.7); border-radius: 16px;
  background: rgba(24, 28, 33, 0.72); color: #fff;
  pointer-events: none;                       /* must not eat the drop event itself */
  backdrop-filter: blur(2px);
}
#viewer-section .vp-drop-title { font-size: 17px; font-weight: 700; }
#viewer-section .vp-drop-sub { font-size: 13px; opacity: 0.8; }

/* ============================================================================
   PREVIEW CARD EXTRAS
   The card itself (pin, drag grip, resize corner, dock zone) is tool-cards.css.
   Mojavatar adds the undo/redo pair in the header, and lets the stage fill the
   floating pane's height when the pane is pinned and resized.
   ============================================================================ */
/* Undo/redo sit just left of the pin button, so the top-left corner stays free for the
   drag grip when the pane floats. */
#viewer-section .preview-history {
  position: absolute; right: 52px; top: 12px; z-index: 6;
  display: flex; gap: 6px;
}
#viewer-section .preview-history .vp-chip-history { display: flex; gap: 6px; }
#viewer-section .vp-tb-icon {
  width: 32px; height: 32px; padding: 0; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid #d1d5db; border-radius: 50%;
  background: #fff; color: #666; cursor: pointer;
}
#viewer-section .vp-tb-icon:hover:not(:disabled) { border-color: #3984a7; background: #f0f8ff; color: #3984a7; }
#viewer-section .vp-tb-icon:disabled { opacity: 0.5; cursor: not-allowed; }
/* Align the floating pane's drag grip with the header row (undo/redo/pin sit at top 12). */
#viewer-section .preview-drag-handle { top: 12px; left: 10px; }

/* A pinned pane carries an inline height (its docked height, or the user's resize):
   the stage stretches into it and the canvas follows, instead of the 16:9 preference. */
#viewer-section .preview-card.pinned .vp-stage { flex: 1 1 0; min-height: 100px; }
#viewer-section .preview-card.pinned .vp-canvas-col {
  height: 100%;
  aspect-ratio: auto;
  max-height: none;
}

/* ============================================================================
   ACTIONS CARD
   ============================================================================ */
/* Two corner circles ride the card, mirroring the tag generator's config gear: the ? help button
   on the left, the settings-config gear on the right. They share the circle style; only the side
   differs (positioned per id so they never collide). The shared sheet keeps .actions-card unpositioned. */
#viewer-section .actions-card { position: relative; }
#viewer-section .config-btn {
  padding: 6px;
  border: 2px solid #d1d5db;
  border-radius: 50%;
  background: #fff;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: #666;
  position: absolute;
  top: 12px;
}
/* Both circles sit in the top-right corner: the gear at the edge, the ? just left of it (its right
   offset clears the 32px gear + an 8px gap). */
#viewer-section #configBtn { right: 12px; }
#viewer-section .config-btn:hover { background: #f0f8ff; border-color: #3984a7; color: #3984a7; }
#viewer-section .config-btn svg { width: 18px; height: 18px; display: block; }

/* ONE icon ladder for all four corner circles (undo, redo, pin, ?). Their svgs are flex
   items, so button padding was silently CRUSHING them width-first (the pin rendered
   16, 12, then 8px wide while staying 18 tall): kill the padding, pin the svg size, and
   let flex centering do the placement. The per-breakpoint steps live in the media blocks. */
#viewer-section .pin-preview-btn,
#viewer-section .config-btn { padding: 0; }
#viewer-section .vp-tb-icon svg,
#viewer-section .pin-preview-btn .pin-icon {
  width: 18px; height: 18px; flex: 0 0 auto;
}

/* The status line is ALWAYS in the layout (it ships with default text, and keeps its row
   even if a message is cleared), so posting a status never shoves the card taller. */
#viewer-section .tool-status { min-height: 20px; text-align: center; font-size: 13px; color: #666; }
/* Clear is destructive: a ghost-red hover on an otherwise quiet secondary button. */
#viewer-section #resetAllBtn:hover { border-color: #dc3545; color: #dc3545; background: #fdf3f4; }

/* ============================================================================
   SHARED FIELD EXTRAS (small vocabulary the shared sheet does not carry)
   ============================================================================ */
#viewer-section .vp-hint { margin: 0; text-align: center; font-size: 13px; color: #666; line-height: 1.5; }
/* Group heads keep the page's centered-label language; the small action chip rides
   the right edge without pushing the label off-center. */
#viewer-section .vp-group-head { position: relative; display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 28px; margin-bottom: 8px; }
#viewer-section .vp-group-head label { margin: 0; }
#viewer-section .vp-group-head .vp-btn-text { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
/* Slider labels sit left, like the tag generator's. */
#viewer-section .input-group .slider-label { text-align: left; }
/* Small corner actions (Reset, Show all, Remove custom skin): the tag generator's
   gradient-reset chip, verbatim. */
#viewer-section .vp-btn-text {
  background: none; border: 1px solid #d1d5db; color: #888;
  font-size: 12px; padding: 3px 12px; border-radius: 12px; cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
#viewer-section .vp-btn-text:hover { color: #555; border-color: #999; }
#viewer-section .vp-btn-text:disabled { color: #c9ced4; border-color: #e5e7eb; cursor: default; }   /* nothing to remove */
/* Sliders sit tight against their value span without the wrapper gap. */
#viewer-section .slider-wrapper input[type="range"] { margin-right: 10px; }

/* ============================================================================
   SKIN CARD
   ============================================================================ */
/* Body type: Auto / Steve / Alex pills, every width. (A phone-only native dropdown used
   to swap in here; that was a Round 5 toolbar-width constraint, and once body type moved
   into the full-width Skin panel the pills fit everywhere and the unstyled select was the
   only thing the swap still delivered.) */
#viewer-section .mv-bodytype { display: flex; justify-content: center; }
#viewer-section .mv-bodytype #modelToggle { display: flex; width: 100%; gap: 4px; background: #eef2f4; padding: 4px; border-radius: 24px; }
#viewer-section .mv-bodytype .vp-seg-btn {
  flex: 1; min-width: 60px; min-height: 34px; padding: 0 14px;
  border: none; background: transparent; border-radius: 20px;
  font-size: 14px; font-weight: 600; color: #667; white-space: nowrap; cursor: pointer;
}
#viewer-section .mv-bodytype .vp-seg-btn.active { background: #fff; color: #3984a7; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); }
/* Auto has RESOLVED to Steve or Alex: mark the resolved pill with a quiet dot, so Auto
   is never making a silent choice on the user's behalf (the FAQ promises this). */
#viewer-section .mv-bodytype .toggle-btn.is-resolved:not(.active) { color: #24677f; position: relative; }
#viewer-section .mv-bodytype .toggle-btn.is-resolved:not(.active)::after {
  content: ""; position: absolute; left: 50%; bottom: 3px; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: #3984a7;
}
#viewer-section .username-row { display: flex; align-items: stretch; gap: 8px; }
#viewer-section .username-row #usernameInput { flex: 1; min-width: 0; width: auto; margin: 0; }
#viewer-section .username-row #loadSkinBtn { width: auto; flex: 0 0 auto; height: auto; padding: 0 22px; }

/* The upload area is the tag generator's icon drop zone, verbatim: dashed rest state,
   tinted hover, solid border while a file hovers over it. */
#viewer-section .icon-drop-zone {
  border: 2px dashed #d1d5db;
  border-radius: 18px;
  padding: 22px 20px;
  text-align: center;
  cursor: pointer;
  transition: all 0.2s ease;
  background: #fff;
  min-height: 90px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}
#viewer-section .icon-drop-zone:hover {
  border-color: #3984a7;
  background: #f0f8ff;
}
#viewer-section .icon-drop-zone.drag-over {
  border-color: #3984a7;
  background: #e6f3ff;
  border-style: solid;
}
#viewer-section .drop-zone-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
#viewer-section .drop-icon {
  width: 24px;
  height: 24px;
  color: #666;
}
#viewer-section .icon-drop-zone p {
  margin: 0;
  color: #666;
  font-size: 14px;
  font-weight: 500;
  word-break: break-word;
  text-align: center;
}

/* The loaded skin, as a nested white entry (the tag generator's color-entry pattern):
   name + download action in the header, its settings underneath. */
#viewer-section .skin-entry {
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px; background: #fff; border: 1px solid #e9ecef; border-radius: 12px;
}
#viewer-section .skin-entry-header { display: flex; align-items: center; gap: 8px; }
#viewer-section .vp-skin-name {
  flex: 1; min-width: 0; font-size: 13px; font-weight: 600; color: #495057;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#viewer-section .skin-entry-btn {
  flex: 0 0 auto; width: 24px; height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid #d1d5db; border-radius: 50%; background: #fff; color: #6b7280; cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
#viewer-section .skin-entry-btn:hover:not(:disabled) { border-color: #3984a7; color: #3984a7; }
/* Remove is dimmed while the built-in skin is loaded (there is nothing to remove),
   and hovers RED when live: it discards, it does not download. */
#viewer-section .skin-entry-btn:disabled { opacity: 0.4; cursor: not-allowed; }
#viewer-section #removeSkinBtn:hover:not(:disabled) { border-color: #dc3545; color: #dc3545; }

.skin-error {
  padding: 10px 16px;
  background: #fef2f2;
  border: 1px solid #fca5a5;
  border-radius: 12px;
  color: #991b1b;
  font-size: 13px;
  text-align: center;
}

/* ============================================================================
   OUTPUT FORMAT: the fast path's three tiles, in the Actions card. Same shape
   language as the export modal's .vp-size-btn - a name over the exact pixels it
   writes - so the two size-ish controls read as one family.
   ============================================================================ */
.vp-format-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 10px; }
.vp-format-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 54px; padding: 8px 6px;
  border: 2px solid #d1d5db; border-radius: 14px; background: #fff;
  color: #333; cursor: pointer; transition: all 0.15s ease;
}
.vp-format-btn:hover { border-color: #3984a7; background: #f0f8ff; }
.vp-format-btn.active { border-color: #3984a7; background: #eaf3f7; color: #3984a7; }
.vp-format-name { font-size: 13px; font-weight: 700; line-height: 1.15; text-align: center; }
.vp-format-px { font-size: 11px; color: #6b7280; font-variant-numeric: tabular-nums; line-height: 1; }
.vp-format-btn.active .vp-format-px { color: #3984a7; }
/* The readout sits between the tiles and Download, so the last thing read before the press is a
   description of the file about to be written. */
#formatHint { margin: 0; }

@media (max-width: 420px) {
  .vp-format-grid { grid-template-columns: 1fr; gap: 6px; }
  .vp-format-btn { flex-direction: row; gap: 8px; min-height: 44px; }
}

/* ============================================================================
   POSE + CAMERA: white pill buttons in preset grids
   ============================================================================ */
#viewer-section .vp-preset-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
#viewer-section .vp-pose-grid { grid-template-columns: 1fr 1fr; }
#viewer-section .vp-btn {
  min-height: 40px; padding: 8px 10px;
  border: 2px solid #d1d5db; border-radius: 24px; background: #fff;
  color: #333; font-size: 14px; font-weight: 600; cursor: pointer;
  transition: all 0.15s ease;
}
#viewer-section .vp-btn:hover { border-color: #3984a7; background: #f0f8ff; color: #3984a7; }
#viewer-section .vp-btn.active { border-color: #3984a7; background: #eaf3f7; color: #3984a7; }

/* ============================================================================
   BACKGROUND SWATCHES: circles, with the colour picker AS the seventh swatch and the image tile
   as the eighth (they wrap to two rows of four on a phone)
   ============================================================================ */
#viewer-section .bg-presets { display: grid; grid-template-columns: repeat(8, 1fr); gap: 8px; justify-items: center; }
#viewer-section .bg-preset,
#viewer-section .bg-custom {
  width: 100%; aspect-ratio: 1; max-width: 44px; min-width: 0; height: auto; padding: 0;
  border: 2px solid #d1d5db; border-radius: 50%; cursor: pointer;
}
#viewer-section .bg-preset:hover,
#viewer-section .bg-custom:hover { border-color: #3984a7; }
#viewer-section .bg-preset.active { border-color: #3984a7; box-shadow: 0 0 0 2px rgba(57, 132, 167, 0.3); }
#viewer-section .bg-custom {
  position: relative; overflow: hidden; background: #fff; color: #6b7280;
  display: flex; align-items: center; justify-content: center;
}
#viewer-section .bg-custom:hover { color: #3984a7; }
#viewer-section .bg-custom svg { width: 18px; height: 18px; pointer-events: none; }
/* The real color input covers the whole swatch, invisible, so the native picker opens on tap. */
#viewer-section .bg-custom input[type="color"],
#viewer-section .bg-image-tile input[type="file"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  padding: 0; margin: 0; border: none; opacity: 0; cursor: pointer;
}
/* The image swatch wears the chosen picture; its icon steps aside once one is set. */
#viewer-section .bg-image-tile { background-size: cover; background-position: center; }
#viewer-section .bg-image-tile.has-image svg { display: none; }
#viewer-section .bg-image-tile.active { border-color: #3984a7; box-shadow: 0 0 0 2px rgba(57, 132, 167, 0.3); }

/* The outline color is the tag generator's color-entry (swatch + hex), all shared CSS. */

/* ============================================================================
   LAYERS ISLAND + OPTIONS ISLAND
   The minecraft-gui-generator's master-detail layout: setting groups and model
   parts as selectable rows on the left, one contextual options island on the
   right. Row/pill values match its Layers.module.css on the shared palette.
   ============================================================================ */
#viewer-section .mv-bottom-row {
  display: grid; grid-template-columns: 2fr 3fr; gap: 15px;   /* same gutter as .top-section */
  /* start, not stretch: a short options panel stretched to the list's height
     reads as a page of empty card. */
  align-items: start; margin-top: 15px;
}
/* gap stays the .tool-card 12px, the tag generator's card rhythm */
#viewer-section .mv-island { background: #f8f9fa; }
#viewer-section .mv-layers { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
#viewer-section .mv-layer-row {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 6px 10px 6px 14px;
  border: 2px solid #d1d5db; border-radius: 24px; background: #fff;
  color: #333; font-family: inherit; font-size: 15px; font-weight: 600; text-align: left;
  cursor: pointer; min-width: 0; transition: all 0.2s ease;
}
#viewer-section .mv-layer-row:hover { border-color: #3984a7; background: #f0f8ff; }
#viewer-section .mv-layer-row:focus-visible { outline: 2px solid #3984a7; outline-offset: 3px; }
/* Selected = the Cube chip's pressed look (solid fill, white text): unmissable, where the
   old pale #f0f8ff tint barely differed from the hover state. Hover keeps the tint, so
   hover and selected stay distinguishable; source order makes selected beat hover. */
#viewer-section .mv-layer-row.is-active {
  border-color: #3984a7; background: #3984a7; color: #fff;
}
#viewer-section .mv-layer-icon { width: 20px; height: 20px; flex: 0 0 auto; }
#viewer-section .mv-layer-row > span {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The part list is a flat outliner (Blockbench-style), not a stack of buttons:
   quiet rows, hover tint, a left accent on the selected one, overlay layers indented. */
#viewer-section .mv-parts { display: flex; flex-direction: column; overscroll-behavior: contain; }
#viewer-section .vp-part {
  display: flex; align-items: center; gap: 10px;
  min-height: 38px; padding: 0 8px 0 10px; border-radius: 10px;
  cursor: pointer; min-width: 0; transition: background 0.15s ease;
}
#viewer-section .vp-part:hover { background: #f0f8ff; }
#viewer-section .vp-part .vp-part-name {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 14px; font-weight: 600; color: #333;
}
#viewer-section .vp-part-sub { padding-left: 40px; min-height: 34px; }
#viewer-section .vp-part-sub .vp-part-name { font-size: 13px; font-weight: 500; color: #6b7280; }
#viewer-section .vp-part-sub .mv-layer-icon { width: 16px; height: 16px; color: #9aa4ac; }
/* A hidden part's name dims so the list mirrors the model. */
#viewer-section .vp-part:has(.vp-eye input:not(:checked)) .vp-part-name { color: #b6bec4; }

/* The eye is a REAL checkbox, only visually hidden: the JS delegates the change event
   from #visibility-section and reads .checked. Turn it into a button and the tool
   starts lying after undo. Styled as the GUI creator's round row action. */
#viewer-section .vp-eye {
  position: relative;
  width: 28px; height: 28px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  border-radius: 50%; background: rgba(0, 0, 0, 0.08); color: #374151;
  transition: background 0.15s ease, color 0.15s ease;
}
#viewer-section .vp-eye:hover { background: rgba(0, 0, 0, 0.16); color: #3984a7; }
/* The checkbox fills the whole circle, invisible, so the touch target is honest. */
#viewer-section .vp-eye input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer; z-index: 1;
}
#viewer-section .vp-eye svg { width: 15px; height: 15px; }
#viewer-section .vp-eye .eye-off { display: none; }
#viewer-section .vp-eye input:not(:checked) ~ .eye-on { display: none; }
#viewer-section .vp-eye input:not(:checked) ~ .eye-off { display: block; color: #9aa4ac; }
#viewer-section .vp-eye input:focus-visible ~ svg { outline: 2px solid #3984a7; outline-offset: 3px; border-radius: 4px; }
#viewer-section .mv-showall { align-self: center; }

/* The options panels: exactly one is shown, picked by the selected settings row.
   Controls sit in TINTED, borderless option groups on the island's #f8f9fa pane (the tag
   generator's two-tier depth); 16px between groups is its tool-panel rhythm. */
#viewer-section .mv-panel { display: flex; flex-direction: column; gap: 16px; }
#viewer-section .mv-panel .tool-card { border: none; }
#viewer-section .mv-panel-sub { display: flex; flex-direction: column; gap: 12px; }

/* ============================================================================
   MODALS (export / help / confirm)
   NOT scoped under #viewer-section: they are reparented to <body> at init, so
   `inert` on the page background cannot inert them. Lifted from the glyph
   generator's modal system.
   ============================================================================ */
.vp-modal-overlay {
  position: fixed; inset: 0; z-index: 1100;   /* above the site navbar */
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(17, 24, 39, 0.55); overflow-y: auto;
}
/* The modals are reparented to <body>, so `#viewer-section [hidden]` never covered them and any
   class in here with a `display` of its own quietly resurrected a hidden element. That is exactly
   how the Aspect row stayed on screen under Pixel art, lighting up while doing nothing. */
.vp-modal [hidden], .config-modal [hidden] { display: none !important; }
.vp-modal-overlay[hidden] { display: none !important; }
.vp-modal {
  background: #fff; border-radius: 20px; width: 100%; max-width: 560px;
  max-height: calc(100dvh - 40px); overflow-y: auto; margin: auto;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
.vp-modal-narrow { max-width: 460px; }
.vp-modal-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px; background: #fff; border-bottom: 1px solid #eef0f2; border-radius: 20px 20px 0 0;
}
.vp-modal-head h3 { margin: 0; font-size: 20px; color: #2c3e50; }
.vp-modal-close {
  width: 34px; height: 34px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  background: #f3f4f6; color: #6b7280; cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
.vp-modal-close svg { display: block; width: 15px; height: 15px; }
.vp-modal-close:hover { background: #e5e7eb; color: #2c3e50; }
.vp-modal-body { padding: 22px 20px; display: flex; flex-direction: column; gap: 24px; }
.vp-modal-body p { margin: 0; color: #374151; line-height: 1.6; }

/* Fields inside a modal: the label sits ABOVE the control and both are centred. */
.vp-field-stack { display: flex; flex-direction: column; min-width: 0; }
.vp-grid2 { display: grid; gap: 20px 16px; align-items: start; grid-template-columns: 1fr 1fr; }
.vp-grid2 > * { min-width: 0; }
.vp-modal-body .vp-hint { display: block; text-align: center; font-size: 12px; color: #6b7280; margin: 8px 0 0; }

/* Sections inside a modal: a hairline, then a centred uppercase title. */
.vp-export-section { display: flex; flex-direction: column; gap: 12px; }
.vp-export-section + .vp-export-section {
  margin-top: 4px; padding-top: 22px; border-top: 1px solid #e5e7eb;
}

/* Modal fields: the label above its control, both full width. One control per decision - on touch
   a select opens the OS picker instead of wrapping a row of pills over three lines. */
.vp-field-stack > label {
  display: block; margin-bottom: 6px;
  font-size: 12px; font-weight: 600; color: #6b7280;
}
.vp-modal-body .vp-select {
  width: 100%; box-sizing: border-box;
  padding: 11px 13px;
  border: 2px solid #d1d5db; border-radius: 12px;
  background: #fff; color: #2c3e50;
  font-family: inherit; font-size: 15px; font-weight: 600;
  text-align: left; font-variant-numeric: tabular-nums;
}
.vp-modal-body .vp-select:focus { outline: none; border-color: #3984a7; }
/* A refinement of the format above it, not a third independent axis. */
.vp-field-sub { margin-left: 14px; padding-left: 12px; border-left: 2px solid #e5e7eb; }

.vp-export-title {
  display: block; text-align: center;
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;
  color: #9ca3af; margin-bottom: 6px;
}
.vp-confirm-actions { display: flex; gap: 12px; justify-content: center; }
.vp-confirm-actions button { margin: 0; flex: 1 1 140px; width: auto; }
/* The confirm's Reset is the destructive primary. */
#confirmOk { background: #dc3545; border-color: #dc3545; color: #fff; }
#confirmOk:hover { background: #bb2d3b; border-color: #bb2d3b; }

/* Settings-config modal: copied VERBATIM from the pixel-art tag generator (pixel-art-tag-generator.css),
   same classes so the panel is identical. Its .config-btn corner rule is omitted here because this tool
   already scopes .config-btn under #viewer-section for its two corner circles (help + gear). */
.icon-drop-zone {
  border: 2px dashed #d1d5db;
  border-radius: 18px;
  padding: 30px 20px;
  text-align: center;
  cursor: pointer;
  transition: all 0.2s ease;
  background: #fff;
  min-height: 120px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}
.icon-drop-zone:hover { border-color: #3984a7; background: #f0f8ff; }
.icon-drop-zone.drag-over { border-color: #3984a7; background: #e6f3ff; border-style: solid; }
.drop-zone-content { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.drop-icon { width: 24px; height: 24px; color: #666; }
.icon-drop-zone p { margin: 0; color: #666; font-size: 14px; font-weight: 500; word-break: break-word; text-align: center; }

.config-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.config-modal-overlay[hidden] { display: none; }
.config-modal {
  background: #fff;
  border-radius: 18px;
  width: 100%;
  max-width: 700px;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
  overflow: hidden;
}
.config-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-bottom: 1px solid #eee;
}
.config-modal-header h3 { margin: 0; font-size: 17px; color: #333; }
.config-modal-close {
  border: none;
  background: #f3f4f6;
  cursor: pointer;
  color: #6b7280;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
}
.config-modal-close:hover { background: #e5e7eb; color: #2c3e50; }
.config-modal-close svg { width: 20px; height: 20px; }
.config-modal-body {
  padding: 14px 20px 20px;
  overflow-y: auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  column-gap: 12px;
  row-gap: 16px;
}
.config-modal-body > * { min-width: 0; }
.config-section { display: flex; flex-direction: column; gap: 10px; }
.config-section-label {
  font-weight: bold;
  font-size: 13px;
  color: #555;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.config-drop-zone { min-height: 200px; padding: 20px; height: auto; }
.config-modal .config-drop-zone { border-radius: 12px; min-height: 200px; border-width: 2px; }
.config-status { font-size: 13px; min-height: 18px; color: #666; }
.config-status:empty { display: none; }
.config-status.error { color: #d9534f; }
.config-status.success { color: #4CAF50; }
.config-export-text {
  min-height: 200px;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  font-family: monospace;
  font-size: 12px;
  line-height: 1.4;
  border: 2px solid #d1d5db;
  border-radius: 12px;
  padding: 10px;
  resize: vertical;
  background: #fafafa;
  color: #333;
  scrollbar-width: thin;
  scrollbar-color: #c0c0c0 transparent;
}
.config-export-text::-webkit-scrollbar { width: 6px; }
.config-export-text::-webkit-scrollbar-track { background: transparent; margin: 14px 0; }
.config-export-text::-webkit-scrollbar-thumb { background: #c0c0c0; border-radius: 3px; }
.config-export-text::-webkit-scrollbar-thumb:hover { background: #999; }
.config-modal-actions {
  grid-column: 1 / -1;
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}
.config-modal-actions .json-button-secondary { height: 40px; font-size: 14px; width: auto; flex: 1 1 140px; white-space: nowrap; }

@media (max-width: 600px) {
  .config-modal-body { grid-template-columns: 1fr; }
  .config-modal .config-drop-zone { min-height: 100px; }
  .config-export-text { min-height: 100px; }
  .config-modal-actions { margin-top: 10px; }
}

@media (max-width: 560px) {
  .vp-export-grid { grid-template-columns: 1fr 1fr; }
}

/* ============================================================================
   RESPONSIVE
   One block, at the end, so the display swaps cannot be undone by a later
   same-specificity desktop rule (that bug shipped once). The shared sheet
   already stacks .top-section and .tool-container at 710px.
   ============================================================================ */
/* The mojavatar-specific components follow the SAME scale ladder as the shared sheet:
   @710 controls drop to 36px (buttons 40), @480 card padding/radius 12, @360 padding/
   radius 10 and tighter gaps. Anything added here must pick its size from that ladder. */
@media (max-width: 710px) {
  /* Shorter than the old 4:5 portrait slab (the pinned pane carries this docked height),
     but tall enough that the figure reads: 16:9, ~195px on a 390 phone. */
  #viewer-section .vp-canvas-col { aspect-ratio: 16 / 9; max-height: 260px; }
  /* Layers stack above the options island, like the GUI creator. */
  #viewer-section .mv-bottom-row { grid-template-columns: 1fr; gap: 15px; }
  /* The SETTINGS list turns sideways: a horizontally scrollable chip strip costs one row
     (~40px) instead of six stacked rows (~280px) before the options island appears.
     A SLIM scrollbar is shown so a narrow NON-touch screen (a narrow desktop window that
     hits this breakpoint) can still drag the strip; touch users swipe, and showOptionsPanel()
     slides the lit chip into view either way. */
  #viewer-section .mv-layers {
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: thin;
    scrollbar-color: #c0c0c0 transparent;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 4px;   /* room for the bar so it doesn't crowd the chips */
  }
  #viewer-section .mv-layers::-webkit-scrollbar { height: 6px; }
  #viewer-section .mv-layers::-webkit-scrollbar-track { background: transparent; }
  #viewer-section .mv-layers::-webkit-scrollbar-thumb { background: #c0c0c0; border-radius: 3px; }
  #viewer-section .mv-layers::-webkit-scrollbar-thumb:hover { background: #999; }
  #viewer-section .mv-layer-row { flex: 0 0 auto; min-height: 40px; font-size: 14px; padding: 4px 16px 4px 13px; }
  #viewer-section .icon-drop-zone { border-radius: 14px; }
  /* Pill buttons and the color swatch match the shared 36px control height
     (padding shrinks too: with border-box sizing it would floor the height at 41px). */
  #viewer-section .vp-btn { min-height: 36px; padding: 5px 10px; }
  /* Panel stacks track the card rhythm at this width. */
  #viewer-section .mv-panel { gap: 12px; }
  #viewer-section .mv-panel-sub { gap: 10px; }
}

@media (pointer: coarse) {
  #viewer-section .vp-part { min-height: 44px; }
  #viewer-section .vp-eye { width: 40px; height: 40px; }
}

@media (max-width: 480px) {
  #viewer-section .username-row { flex-direction: column; }
  /* In a column the row's flex axis is vertical: flex:1 would turn the input's height into
     a flex-basis of 0 and collapse it, so pin it back to its own height. */
  #viewer-section .username-row #usernameInput { flex: 0 0 auto; }
  #viewer-section .username-row #loadSkinBtn { width: 100%; height: 40px; }   /* shared button height here */
  #viewer-section .vp-preset-grid { gap: 8px; }
  #viewer-section .icon-drop-zone { border-radius: 12px; padding: 18px 16px; }
  /* Eight swatches in one row need 8 x 44px + 7 x 8px = 408px of panel to stay tappable, which a
     phone does not have: they were shrinking to 26px at 360 and 21px at 320. Two rows of four
     holds every circle at its full 44px instead. */
  #viewer-section .bg-presets { grid-template-columns: repeat(4, 1fr); }
  /* The pin moves to the 8px corner here (shared); the chips and the ? button ride along. */
  #viewer-section .preview-history { top: 8px; right: 48px; }
  #viewer-section .config-btn { top: 8px; }
  #viewer-section #configBtn { right: 8px; }
  /* Modals track the 12px card radius step. */
  .vp-modal { border-radius: 16px; }
  .vp-modal-head { padding: 14px 16px; border-radius: 16px 16px 0 0; }
  .vp-modal-body { padding: 18px 16px; gap: 20px; }
}

/* The paired modal controls stop sitting side by side here, at the same step the format tiles
   break at. "Download PNG" is the widest label in the modal and its column falls below the label's
   own width around 320px, so each control takes its own full-width line before that. The tighter
   gap keeps a stacked pair reading as one pair. Must live AFTER the .vp-grid2 base rule above, not
   up with the format-tile breakpoint: same specificity, so source order decides. */
@media (max-width: 420px) {
  .vp-grid2 { grid-template-columns: 1fr; gap: 10px; }
}

@media (max-width: 360px) {
  #viewer-section .mv-bottom-row { gap: 12px; margin-top: 12px; }
  #viewer-section .mv-panel { gap: 10px; }
  #viewer-section .mv-panel-sub { gap: 8px; }
  #viewer-section .mv-layer-row { min-height: 38px; padding: 4px 14px 4px 12px; }
  /* The pin drops to 28px at top 6 (shared): the chips and grip follow. */
  #viewer-section .preview-history { top: 6px; right: 40px; }
  #viewer-section .vp-tb-icon { width: 28px; height: 28px; }
  #viewer-section .config-btn { top: 6px; width: 28px; height: 28px; }
  #viewer-section #configBtn { right: 6px; }
  /* 28px circles carry 16px icons (the desktop 18/32 ratio, held down the ladder). */
  #viewer-section .vp-tb-icon svg,
  #viewer-section .pin-preview-btn .pin-icon { width: 16px; height: 16px; }
  #viewer-section .preview-drag-handle { top: 8px; left: 8px; }
  #viewer-section .icon-drop-zone { border-radius: 10px; padding: 16px 12px; min-height: 76px; }
  #viewer-section .skin-entry { padding: 10px; }
}

@media (max-width: 270px) {
  /* The pin drops to 24px at the 4px corner (shared): the ? and the chips keep tracking it. */
  #viewer-section .config-btn { top: 4px; width: 24px; height: 24px; }
  #viewer-section #configBtn { right: 4px; }
  #viewer-section .preview-history { top: 4px; right: 34px; }
  #viewer-section .vp-tb-icon { width: 24px; height: 24px; }
  /* 24px circles carry 14px icons, same ratio again. */
  #viewer-section .vp-tb-icon svg,
  #viewer-section .pin-preview-btn .pin-icon { width: 14px; height: 14px; }
}
