/* Exterior Studio — public/exterior-studio.html.
 *
 * Loaded after styles.css (tokens, header, .btn), toast.css and auth.css, so every
 * var(--…) below resolves. test/frontend/css-tokens.test.js enforces both halves of that:
 * a var() with no definition in scope drops its whole declaration silently, and a hex
 * literal that already HAS a token is a lint failure.
 *
 * Deliberately built on masking-studio.css's recipe rather than its own: same shell width,
 * same frosted .ex-panel, same numbered .ex-block steps, same 380px-controls-then-canvas
 * grid, same sentence-case button override. Two studios that look like two products is a
 * worse outcome than a little repetition between two stylesheets.
 */

/* Defensive: the studio never causes sideways scroll.
 *
 * `clip`, NOT `hidden`, on the root — and that is not a style preference. `overflow-x:
 * hidden` forces the other axis to compute to `auto`, which makes <html> a scroll
 * container of its own nested inside the one <body> already provides. masking-studio.css
 * gets away with `hidden` because that studio's layout fits the viewport, so the second
 * container never has anything to scroll. This page is a long, scrolling page, and there
 * the extra container leaves documentElement.scrollHeight pinned at the viewport height
 * while the real content runs past it. `clip` clips the same way and leaves overflow-y
 * `visible`, which is exactly what styles.css already does for the other long pages. */
html { overflow-x: clip; }
body { overflow-x: clip; max-width: 100vw; }

/* NOTE: there is deliberately NO gate dialog on this page. A signed-in free account used
 * to get a full-screen, undismissable "your account is on the free plan" overlay the
 * moment the page loaded — so for anyone who had just made an account it was the first
 * thing the product ever said to them, and it sat on top of the pitch that does the
 * selling. The hero's "Get Stagify+ to use it" button is the whole ask now. Do not
 * re-add an overlay here; requireProAccount on POST /api/enhance-exterior is the real
 * gate, and this page's job is to be readable by everyone including Googlebot. */

/* --- Shell + intro (masking-studio conventions) ---------------------------- */
.ex-shell { width: 100%; max-width: 1400px; margin: 0 auto; }
.ex-intro { text-align: center; margin: 4px auto 18px; max-width: 760px; }
.ex-intro h1 {
  margin: 0 0 8px;
  font-size: clamp(26px, 3.2vw, 38px);
  font-weight: 800;
  letter-spacing: -0.5px;
  color: var(--brand-deep);
}
.ex-intro p { margin: 0; color: var(--accent); font-size: 15px; line-height: 1.55; }
/* Matched as `p.ex-intro__actions`, not `.ex-intro__actions`: the rule above is (0,1,1)
 * and would otherwise win the margin outright, leaving the call to action flush against
 * the subtitle with no gap at all. */
.ex-intro p.ex-intro__actions { margin: 18px 0 0; }
.ex-intro__actions[hidden] { display: none; }
.ex-intro__actions a { text-decoration: none; }

/* The site's default buttons shout in uppercase; inside the studio they read as tool
 * controls, so use sentence case. Lifted verbatim from masking-studio.css — the scope is
 * the page's own container, never the global .btn, because that rule is what every other
 * page's buttons look like. */
.ex-shell .btn {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 600;
  border-radius: 10px;
  /* The base .btn sets no `display`, so an <a class="btn"> stays INLINE — and an inline
   * box's vertical padding paints outside its line box instead of growing it. The result
   * is a call-to-action link sitting on top of the paragraph above it. The studios never
   * hit this because their buttons are <button> elements, which are inline-block already. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* --- The public half ------------------------------------------------------ */
.ex-features-section { margin: 0 auto 8px; max-width: 1080px; }
.ex-features-section[hidden] { display: none; }
.ex-section__title {
  margin: 0 0 20px;
  text-align: center;
  font-size: clamp(1.2rem, 2.4vw, 1.6rem);
  color: var(--text-heading);
}
.ex-features {
  display: grid;
  /* auto-fit rather than a breakpoint ladder: four cards go 4 / 2 / 1 across on their
   * own, and the page is responsive by construction rather than at three chosen widths. */
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
}
.ex-feature {
  background: rgba(255, 255, 255, 0.5);
  backdrop-filter: blur(16px) saturate(170%);
  -webkit-backdrop-filter: blur(16px) saturate(170%);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 16px;
  box-shadow: 0 14px 40px rgba(30, 58, 138, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  padding: 20px 18px;
}
.ex-feature__title { margin: 0 0 8px; font-size: 15px; font-weight: 700; color: var(--brand-deep); }
.ex-feature__body { margin: 0; font-size: 14px; line-height: 1.6; color: var(--accent); }

/* --- The tool ------------------------------------------------------------- */
.ex-grid {
  display: grid;
  /* Controls first, canvas second — the masking studio's proportions exactly. */
  grid-template-columns: 380px 1fr;
  gap: 20px;
  align-items: start;
}
/* [hidden] is a UA rule at lower specificity than the class above, so without this the
 * grid stays laid out for a signed-out visitor. */
.ex-grid[hidden] { display: none; }

/* Frosted glass panel recipe used across the site. */
.ex-panel {
  background: rgba(255, 255, 255, 0.5);
  backdrop-filter: blur(16px) saturate(170%);
  -webkit-backdrop-filter: blur(16px) saturate(170%);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 16px;
  box-shadow: 0 14px 40px rgba(30, 58, 138, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.ex-toolbar { padding: 20px; }
.ex-toolbar button, .ex-workspace button { font-family: inherit; }
.ex-block { padding: 16px 0; border-top: 1px dashed rgba(37, 99, 235, 0.22); }
.ex-block:first-child { padding-top: 0; border-top: none; }
/* The submit button is the only thing under the last step, and it brings its own
 * margin-top. Padding does not collapse into that margin, so the two stacked into a
 * band of dead space beneath the notes box. The button owns that gap on its own. */
.ex-block:last-of-type { padding-bottom: 0; }
.ex-block-head { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.ex-block-head h2 { margin: 0; font-size: 15px; font-weight: 700; color: var(--text-heading); }
.ex-step {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--brand);
  color: var(--bg-elevated);
  font-size: 12px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.ex-hint { margin: 10px 0 0; font-size: 12px; color: var(--muted); line-height: 1.5; }
.ex-block-head + .ex-hint { margin-top: 0; }

/* Uploaded-photo card: the whole thumbnail is the replace button. */
.ex-photo-card {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 1.5px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-elevated);
  cursor: pointer;
  line-height: 0;
}
.ex-photo-card.hidden { display: none; }
.ex-photo-card img { width: 100%; height: 92px; object-fit: cover; display: block; }
.ex-photo-card__overlay {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 6px 8px;
  background: rgba(30, 58, 138, 0.72);
  color: var(--bg-elevated);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
}
.ex-photo-card:hover { border-color: var(--brand); }

/* Opt-in rows. Every one starts OFF and reveals its own sub-control, so a request to
 * clear the bins never carries a time-of-day change nobody asked for.
 *
 * NO rules between them. There were hairlines here when this was a list of seven
 * near-identical rows and the eye needed help telling one from the next; with only the two
 * presets left they were separating a pair, which is what whitespace is for. The block
 * keeps its own dashed divider, so the step boundaries still read. */
.ex-check {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  /* Padding rather than min-height: the row must be a comfortable tap target, and a
   * min-height on a flex item with wrapping label text leaves the text vertically
   * stranded instead of growing the row. */
  padding: 10px 2px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--fg);
  cursor: pointer;
}
/* The preset toggles wear the SAME tick box as the chips below them. A native checkbox
 * beside a custom chip is the tell that gives a panel away — two controls doing the same
 * job in two visual languages, one of them the browser's default. `accent-color` only
 * recolours the OS box; it cannot match a 5px radius or the chip's border weight. */
.ex-check input {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  margin: 0;
  border: 1.5px solid var(--border);
  border-radius: 5px;
  background-color: var(--bg-elevated);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 11px 11px;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.ex-check input:checked {
  background-color: var(--brand);
  border-color: var(--brand);
  /* White tick, because this box FILLS with brand — the inverse of the chip, whose box sits
   * on an already-brand pill and so carries a blue tick on white. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}
.ex-check input:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.ex-check:hover { color: var(--brand-strong); }
.ex-check:hover input { border-color: var(--brand); }
.ex-option__body { padding: 0 0 12px 25px; }
.ex-option__body[hidden] { display: none; }

/* A revealed select sits BESIDE its label rather than under it, which is a whole row back
 * per ticked preset. `flex-wrap` is the safety net, not decoration: "Time of day" is two
 * short words in English and a good deal longer in German, and a select squeezed to 60px
 * is worse than one that drops to its own line. */
.ex-option--inline { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
/* A floor, not a width: it lines the two selects up with each other in every language whose
 * labels fit inside it, and a longer one simply pushes its own select right rather than
 * being clipped. Worst case is the ragged edge we would have had anyway. */
.ex-option--inline .ex-check { flex: 0 0 auto; align-items: center; min-width: 112px; }
.ex-option--inline .ex-option__body {
  flex: 1 1 150px;
  min-width: 0;
  /* MUST be zero, not just smaller. The stacked rule above pads left to clear the checkbox
   * and bottom to separate the next row; inline, the first is meaningless and the second is
   * actively wrong — `align-items: center` centres the flex ITEM, padding included, so any
   * bottom padding here lifts the select by half of it and leaves it sitting above its own
   * label. The row's breathing space comes from .ex-check's own symmetric 10px instead. */
  padding: 0;
}

/* The removals: on/off, so chips rather than rows.
 *
 * The input is not hidden — it is stretched over the whole chip at zero opacity and left on
 * top. `display:none` would take it out of the accessibility tree and out of reach of a
 * click, and Playwright's `check()` needs a real, hit-testable box; this keeps the native
 * checkbox doing the semantics, the focus and the keyboard while the span does the looks. */
.ex-chips { border: 0; margin: 12px 0 0; padding: 0; min-width: 0; }
.ex-chips__legend { padding: 0; margin: 0 0 8px; font-size: 12px; font-weight: 600; color: var(--muted); }
.ex-chips__row { display: flex; flex-wrap: wrap; gap: 7px; }
.ex-chip { position: relative; display: inline-flex; }
.ex-chip input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}
.ex-chip__face {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px 7px 10px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--fg);
  background: var(--bg-elevated);
  border: 1.5px solid var(--border);
  border-radius: 999px;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
/* A tick box inside the chip, so "selected" is not carried by colour alone. */
.ex-chip__face::before {
  content: '';
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  border: 1.5px solid currentColor;
  border-radius: 5px;
  opacity: 0.4;
  background-position: center;
  background-repeat: no-repeat;
  background-size: 10px 10px;
  transition: opacity 0.15s ease, background-color 0.15s ease;
}
.ex-chip:hover .ex-chip__face { border-color: var(--brand); color: var(--brand-strong); }
/* `--bg-elevated` rather than a literal white: it is what .btn-primary already uses for
 * its own label, so "the colour that reads against brand blue" has one definition. */
.ex-chip input:checked + .ex-chip__face {
  color: var(--bg-elevated);
  background: var(--brand);
  border-color: var(--brand);
}
.ex-chip input:checked + .ex-chip__face::before {
  opacity: 1;
  background-color: var(--bg-elevated);
  border-color: var(--bg-elevated);
  /* The tick. Inlined as a data URI so the chip stays one element with no extra markup —
   * `img-src` in the site CSP allows data:, which is what a CSS background reads under. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}
.ex-chip input:focus-visible + .ex-chip__face {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.ex-select,
.ex-textarea {
  width: 100%;
  font: inherit;
  font-size: 14px;
  color: var(--fg);
  /* background-COLOR, not the shorthand: .ex-select paints a chevron into background-image
   * below, and the shorthand would reset it out from under itself on any future reorder. */
  background-color: var(--bg-elevated);
  border: 1.5px solid var(--border);
  border-radius: 10px;
  padding: 9px 11px;
}
.ex-select:focus-visible,
.ex-textarea:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}
.ex-textarea { resize: vertical; min-height: 56px; line-height: 1.5; }

/* The native select arrow is the other giveaway: it is drawn by the OS, so it ignores the
 * radius, the border and the type around it, and looks different on every machine. Same
 * treatment .language-dropdown already uses — appearance:none plus the site's own chevron
 * as an inline SVG — so the two selects on this page match the rest of the product. */
.ex-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 34px;
  font-weight: 600;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231e3a8a' stroke-width='2.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 11px center;
  background-size: 15px 15px;
}
.ex-select:hover { border-color: var(--brand); }
/* Firefox keeps a focus ring on the text inside a styled select. */
.ex-select:-moz-focusring { color: transparent; text-shadow: 0 0 0 var(--fg); }

/* Block-level flex with auto side margins, not the inline-flex `.ex-shell .btn` sets above:
 * below 480px the global `.btn { max-width: 280px }` caps this button narrower than the
 * toolbar, and an inline box lands flush against the panel's left edge instead of under the
 * centre of the controls it submits. The auto margins centre it there and are no-ops at
 * every width where the 100% still wins. `.btn` is in the selector only to outrank
 * `.ex-shell .btn` (0,2,0), which is the rule that sets the display. */
.ex-submit.btn { display: flex; width: 100%; margin: 12px auto 0; }
.ex-submit[disabled] { opacity: 0.55; cursor: not-allowed; }

/* --- Workspace ------------------------------------------------------------ */
/* min-height, not height: this is the floor for the empty state, and the number that
 * actually sizes the panel — the dropzone inside is flex:1, so it absorbs the height
 * rather than leaving a band of empty panel under it, and its own 420px min stays
 * non-binding at this width. The phone breakpoints below reset this to 0 and size the
 * dropzone directly, so they are unaffected. */
.ex-workspace { padding: 18px; display: flex; flex-direction: column; gap: 14px; min-height: 584px; min-width: 0; }
.ex-viewer-actions { display: flex; align-self: flex-end; gap: 8px; flex-wrap: wrap; }
.ex-viewer-actions.hidden { display: none; }

.ex-dropzone {
  width: 100%;
  flex: 1;
  min-height: 420px;
  border: 2px dashed var(--brand-pale);
  border-radius: 14px;
  display: grid;
  place-items: center;
  text-align: center;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.35);
  padding: 24px;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.ex-dropzone[hidden] { display: none; }
.ex-dropzone:hover, .ex-dropzone.is-drag-over { border-color: var(--brand); background: rgba(219, 234, 254, 0.45); }
.ex-dropzone:focus-visible { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15); }
.ex-dz-icon { color: var(--brand); margin-bottom: 10px; }
.ex-dz-icon svg { transition: transform 0.2s ease; }
.ex-dropzone:hover .ex-dz-icon svg, .ex-dropzone.is-drag-over .ex-dz-icon svg { transform: translateY(-3px) scale(1.06); }
@media (prefers-reduced-motion: reduce) {
  .ex-dropzone, .ex-dz-icon svg { transition: none; }
  .ex-dropzone:hover .ex-dz-icon svg { transform: none; }
}
.ex-dz-text { font-weight: 700; color: var(--text-heading); font-size: 16px; }
.ex-dz-sub { color: var(--muted); font-size: 13px; margin-top: 6px; max-width: 380px; }

/* Chosen photo, before enhancement */
.ex-preview { margin: 0; }
.ex-preview[hidden] { display: none; }
.ex-preview__img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 70vh;
  object-fit: contain;
  border-radius: 12px;
  background: var(--brand-wash);
}

/* Result + compare slider */
.ex-result { margin: 0; }
.ex-result[hidden] { display: none; }
.ex-result__head { margin-bottom: 12px; text-align: center; }
.ex-result__title { margin: 0 0 4px; font-size: 15px; font-weight: 700; color: var(--brand-deep); }
.ex-result__hint { margin: 0; font-size: 13px; color: var(--muted); }

.ex-compare {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  background: var(--brand-wash);
  /* Set from the loaded image's own dimensions by compare.js, so the box never jumps
   * between the before and after frames. */
  aspect-ratio: var(--ex-ar, 3 / 2);
  max-height: 70vh;
  margin: 0 auto;
}
.ex-compare__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* The AFTER image is clipped to the slider position and sits on top; the BEFORE image
 * underneath is what shows through the uncovered part. Both are absolutely positioned so
 * they occupy exactly the same box — a flow-positioned pair would offset by a hairline at
 * some zoom levels and read as a seam. */
.ex-compare__img--before { position: absolute; inset: 0; }
.ex-compare__after {
  position: absolute;
  inset: 0;
  width: var(--ex-split, 50%);
  overflow: hidden;
}
.ex-compare__after .ex-compare__img { width: var(--ex-after-width, 100%); }

.ex-compare__range {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
  width: calc(100% - 24px);
  margin: 0 12px;
  cursor: ew-resize;
}

.ex-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Narrow desktop windows: stack the panels, photo first ----------------- */
@media (max-width: 900px) {
  .ex-grid { grid-template-columns: 1fr; }
  /* The workspace leads on a stacked layout, same as the masking studio: the controls
   * are meaningless until there is a photo to apply them to. */
  .ex-workspace { order: 1; min-height: 0; }
  .ex-toolbar { order: 2; }
  .ex-dropzone { min-height: 260px; }
}

/* --- Phones: same tool, compact spacing ------------------------------------ */
@media (max-width: 768px) {
  /* The heading starts right under the header, and the language pill floats over that
   * same band from the zero-height picker container above — so without this the title
   * reads pinned to the nav. Breathing room on the shell, not on .ex-intro, so the whole
   * studio drops with the title instead of only the text. Matches .ms-shell. */
  .ex-shell { padding-top: 20px; }
  .ex-toolbar { padding: 14px; }
  .ex-workspace { padding: 12px; min-height: 0; }
  .ex-dropzone { min-height: 220px; }
  .ex-preview__img, .ex-compare { max-height: 60vh; }
}
