/* The five chip typefaces.
 *
 * This lives here, in the stylesheet, and nowhere else. It used to be a
 * <link> duplicated in sections/neon-customizer.liquid and dev-server.js, and
 * it drifted twice: a font listed in one file and missing from the other makes
 * its chip fall back to a system face, so two styles look identical in one
 * environment and correct in the other — which reads as a broken control
 * rather than a missing stylesheet. A third copy was about to be added for the
 * Next.js test page, so it moved to the one file every host already loads.
 *
 * @import costs a little: the browser cannot discover these until this
 * stylesheet has parsed, where a <link> in the head starts immediately. For
 * five display faces on one widget that is a fair trade for a hazard that has
 * already bitten twice. */
@import url('https://fonts.googleapis.com/css2?family=Baloo+2:wght@500&family=Comfortaa:wght@300&family=Grenze+Gotisch:wght@500&family=Poiret+One&family=Sacramento&display=swap');

/*
 * Lux Neon USA — custom neon sign customizer
 * ------------------------------------------
 * Mobile first. Every rule below is the phone layout; the media queries at the
 * bottom widen it. Everything is namespaced `nc-` so it cannot leak into the
 * rest of the theme, and nothing here depends on theme classes.
 *
 * 2026-08-04 redesign: the live neon preview is gone. The viewport is empty
 * until the customer taps it, and the AI render fills it. That makes the tap
 * target the largest thing on the page, which is the intent.
 */

.neon-customizer {
  /* ---- Tunables ---------------------------------------------------------
   * --nc-input-size is the message field, the one number asked to be trialled
   * at 20-24px. 22px is the midpoint and the only value to change to move it.
   * Anything at or above 16px also avoids iOS Safari zooming the page when the
   * field takes focus, which is a real bug at smaller sizes. */
  --nc-input-size: 22px;

  --nc-bg: #0d0f14;
  --nc-panel: #14171f;
  --nc-panel-hi: #1c212c;
  --nc-line: #262c38;
  --nc-line-strong: #46506a;
  --nc-text: #f2f4f8;
  --nc-muted: #98a1b4;
  --nc-accent: #25d366; /* WhatsApp green — the one branded colour here */
  --nc-radius: 14px;

  /* The single vertical rhythm value. Every gap between the viewport, the
   * field and the three chip rows is one of these, so the whole layout
   * tightens or loosens from here.
   *
   * 10px on a phone (owner, 2026-08-05): the target is the entire widget on
   * one screen without scrolling, and the labels that used to justify 22px of
   * separation are gone — unlabelled rows only need enough space to read as
   * separate rows, which is much less. */
  --nc-gap: 10px;

  display: block;
  box-sizing: border-box;
  max-width: 720px;
  margin: 0 auto;
  padding: 0;
  color: var(--nc-text);
  font-size: 16px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

.neon-customizer *,
.neon-customizer *::before,
.neon-customizer *::after {
  box-sizing: inherit;
}

/* ------------------------------------------------------------- viewport */

/* 3:2 everywhere, matching the 1536x1024 the endpoint returns.
 *
 * Was 4:3 on a phone. The image is `object-fit: cover`, so a 4:3 frame was
 * cropping the left and right off every render — on a sign that reads
 * left-to-right, that is the worst axis to lose. Matching the source ratio
 * shows the whole sign AND is shorter, which is most of the vertical space
 * this redesign needed to find. */
.nc-view {
  position: relative;
  aspect-ratio: 3 / 2;
  border-radius: var(--nc-radius);
  border: 1px solid var(--nc-line);
  background: radial-gradient(120% 90% at 50% 40%, #171b24 0%, var(--nc-bg) 70%);
  background-size: cover;
  background-position: center;
  overflow: hidden;
}

/* Fallback for browsers without aspect-ratio (Safari < 15). */
@supports not (aspect-ratio: 1) {
  .nc-view { height: 61vw; max-height: 380px; }
}

.nc-view--photo::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(6, 8, 12, 0.55);
}

/* The execute control, centred in the frame.
 *
 * This used to be the entire viewport — a transparent, full-bleed button with
 * centred text — and it read as placeholder copy rather than a control. It is
 * now a real bordered button, sized and coloured like something to press, with
 * its hint on a separate line below so the button stays compact. */
.nc-idle {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 20px;
  text-align: center;
  pointer-events: none; /* only the button itself is clickable */
}

.nc-run {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 22px;
  border: 1px solid var(--nc-line-strong);
  border-radius: 999px;
  background: rgba(28, 33, 44, 0.92);
  color: var(--nc-text);
  font-family: inherit;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  transition: transform 0.12s ease, border-color 0.15s ease, background 0.15s ease;
}

.nc-run:hover:not(:disabled) {
  background: #232936;
  border-color: #5d6885;
}

.nc-run:active:not(:disabled) { transform: scale(0.98); }

.nc-run:focus-visible {
  outline: 2px solid var(--nc-text);
  outline-offset: 3px;
}

.nc-run:disabled {
  cursor: default;
  opacity: 0.5;
  box-shadow: none;
}

/* The run glyph. A triangle rather than a sparkle: this is the one control
 * that executes something, and it should look like it. */
.nc-run__icon {
  font-size: 12px;
  line-height: 1;
  color: var(--nc-muted);
}

.nc-run:hover:not(:disabled) .nc-run__icon { color: var(--nc-text); }

.nc-hint {
  margin: 0;
  max-width: 28ch;
  font-size: 13px;
  line-height: 1.35;
  color: var(--nc-muted);
}

/* Once an image exists the control stops covering it: it drops to a pill in
 * the corner so the render owns the frame. */
.nc-view.has-shot .nc-idle {
  inset: auto 10px 10px auto;
  padding: 0;
  align-items: flex-end;
}

.nc-view.has-shot .nc-run {
  min-height: 42px;
  padding: 0 16px;
  font-size: 14px;
  background: rgba(10, 12, 17, 0.86);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* The render failure message. Sits above the run button in the same flex
 * column, so the two stack instead of overlapping — see the note in the
 * widget's build(). Kept narrow and centred like .nc-hint, but at full
 * contrast: this one is the reason the frame is empty, not a footnote. */
.nc-err {
  margin: 0;
  max-width: 34ch;
  padding: 10px 14px;
  border-radius: 12px;
  background: rgba(10, 12, 17, 0.86);
  border: 1px solid var(--nc-line-strong);
  /* Kept from the rule this replaced: a failure should read as a problem,
     not as another hint. */
  color: #ffb4b4;
  font-size: 13.5px;
  line-height: 1.4;
}

.nc-view.has-shot .nc-hint { display: none; }

/* The picture on screen already IS the current design, so the run button has
 * nothing left to do — it is hidden and the reason beside it becomes the whole
 * message. Only applies once there is a render to be current WITH; every other
 * disabled state keeps the button, because it is what will work again. */
.nc-view.has-shot .nc-idle.is-current .nc-run { display: none; }


/* …except when the button is disabled, where the hint IS the explanation. On
 * a photograph it needs its own backing to stay readable. */
.nc-view.has-shot .nc-idle.is-blocked .nc-hint {
  display: block;
  max-width: 22ch;
  padding: 5px 11px;
  border-radius: 999px;
  background: rgba(10, 12, 17, 0.86);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-size: 11.5px;
  text-align: right;
}

.nc-shot {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

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

/* Stale: the picture is from a design the customer has since changed. Dimmed
 * and desaturated so it plainly reads as "not current", with a badge naming
 * the reason — a silent stale image makes the font and colour controls look
 * broken. */
.nc-view.is-stale .nc-shot__img {
  filter: saturate(0.45) brightness(0.5);
  transition: filter 0.2s ease;
}

.nc-view.is-stale::before {
  content: 'Preview of an earlier design';
  position: absolute;
  z-index: 1;
  top: 10px;
  left: 10px;
  padding: 5px 11px;
  border-radius: 999px;
  background: rgba(10, 12, 17, 0.86);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--nc-line);
  font-size: 11.5px;
  color: var(--nc-text);
}

/* Loading: a slow sweep across the empty viewport. No percentage and no timer
 * — the wait is 20-30s and a stalled bar reads as broken. */
.nc-view.is-loading::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 20%,
    rgba(255, 255, 255, 0.07) 50%,
    transparent 80%
  );
  background-size: 220% 100%;
  animation: nc-sweep 1.4s ease-in-out infinite;
  pointer-events: none;
}

@keyframes nc-sweep {
  from { background-position: 180% 0; }
  to { background-position: -80% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .nc-view.is-loading::after { animation: none; }
}

/* ------------------------------------------------------------- controls */

.nc-panel { padding: var(--nc-gap) 0 0; }

/* The message field. The largest type in the widget by a wide margin: these
 * are the customer's own words, and on a phone it is what they watch while
 * thumbing it in. */
.nc-input {
  display: block;
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--nc-line);
  border-radius: var(--nc-radius);
  background: var(--nc-panel);
  color: var(--nc-text);
  font-family: inherit;
  font-size: var(--nc-input-size);
  line-height: 1.3;
  resize: none;
  transition: border-color 0.15s ease;
}

.nc-input::placeholder {
  color: var(--nc-muted);
  opacity: 0.7;
  font-size: 0.8em;
}

.nc-input:focus {
  outline: none;
  border-color: var(--nc-line-strong);
}

/* Error line. Empty and fully collapsed the rest of the time — `:empty`
 * removes the box entirely rather than reserving a blank strip, which is what
 * keeps the field sitting directly on top of the style row in the normal case.
 * No min-height for the same reason. */
.nc-note {
  margin: var(--nc-gap) 2px 0;
  font-size: 12.5px;
  line-height: 1.35;
  color: #ffb4b4;
  overflow-wrap: anywhere;
}

.nc-note:empty { display: none; }

/* Rows carry their own separation now that the headings that used to provide
 * it are gone. */
.nc-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: var(--nc-gap);
}

/* ---- font + size chips ---- */

.nc-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--nc-line);
  border-radius: var(--nc-radius);
  background: var(--nc-panel);
  color: var(--nc-text);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
  /* 48px. Tightened from 52 to buy vertical space, and this is as far as it
   * goes: 44px is the accessibility floor for a touch target, and the 4px of
   * headroom is what absorbs the chip's own border. */
  min-height: 48px;
}

.nc-chip:focus-visible {
  outline: 2px solid var(--nc-line-strong);
  outline-offset: 2px;
}

.nc-chip.is-selected {
  border-color: var(--nc-line-strong);
  background: var(--nc-panel-hi);
}

/* Five styles now, so these share the row rather than sitting at a fixed width.
 * The min-width keeps an Aa legible on a 375px phone and lets the row wrap
 * rather than crushing the chips if a sixth style is ever added. */
.nc-row--fonts .nc-chip {
  flex: 1 1 0;
  min-width: 62px;
  font-size: 22px;
  line-height: 1;
  padding: 10px 4px;
}

.nc-row--sizes {
  /* Two across on a phone: four would squeeze the price range onto three
   * lines. */
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}

.nc-row--sizes .nc-chip {
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 9px 12px;
  text-align: left;
}

.nc-chip__size {
  font-size: 15px;
  font-weight: 600;
}

.nc-chip__price {
  font-size: 11.5px;
  color: var(--nc-muted);
  white-space: nowrap;
}

/* ---- colour dots ---- */

/* Eleven dots. On a 390px phone this lands as 8 + 3 across two rows; the dot
 * cannot shrink enough to fit all eleven on one without dropping under the
 * 44px touch floor, and a second row is cheaper than a control a thumb misses. */
.nc-row--colors { gap: 8px; }

.nc-dot {
  position: relative;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
}

.nc-dot::before {
  content: '';
  position: absolute;
  inset: 5px;
  border-radius: 50%;
  background: var(--nc-dot);
  box-shadow: 0 0 9px var(--nc-dot-glow);
  transition: inset 0.12s ease;
}

/* The selection ring sits outside the dot rather than replacing its border, so
 * a white swatch on a dark panel stays legible when picked. */
.nc-dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid transparent;
  transition: border-color 0.12s ease;
}

.nc-dot.is-selected::before { inset: 7px; }
.nc-dot.is-selected::after { border-color: var(--nc-text); }

.nc-dot:focus-visible {
  outline: 2px solid var(--nc-line-strong);
  outline-offset: 2px;
}

/* ------------------------------------------------------------------ bar */

/* Sticky on a phone: the price is the number the customer is deciding on, and
 * it should not scroll away while they change their mind about the size. */
.nc-bar {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: var(--nc-gap);
  padding: 10px 14px;
  /* Clears the iPhone home indicator. */
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  border: 1px solid var(--nc-line);
  border-radius: var(--nc-radius);
  background: rgba(15, 18, 24, 0.94);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.nc-price {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

/* Never wraps. The label changes with the line count, and a two-line label
 * would push the bar taller mid-typing — the copy is kept short enough to fit
 * (see paintPrice), and this is the guard if it ever stops being. */
.nc-price__label {
  font-size: 10.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--nc-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nc-price__value {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.nc-wa {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  background: var(--nc-accent);
  color: #04210f;
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}

.nc-wa:hover { filter: brightness(1.06); }
.nc-wa:focus-visible { outline: 2px solid var(--nc-text); outline-offset: 2px; }

/* Inert until there is a design to send. The `href` is removed in JS, so this
 * is only the appearance of that state — but `pointer-events: none` is not
 * decoration either: without it the hover brightening below still fires and
 * the button reads as clickable while doing nothing, which is worse than
 * either state on its own. */
.nc-wa.is-disabled {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
}

/* ------------------------------------------------------------- >= 600px */

@media screen and (min-width: 600px) {
  .neon-customizer {
    --nc-input-size: 24px;
    /* Desk and tablet have the height to spare, so the layout loosens back up
     * once the one-screen-on-a-phone constraint stops applying. */
    --nc-gap: 14px;
  }

  /* Four across once there is room for the price range on one line. */
  .nc-row--sizes { grid-template-columns: repeat(4, 1fr); }

  .nc-run { font-size: 18px; min-height: 54px; padding: 0 28px; }
  .nc-hint { font-size: 13.5px; }

  .nc-row--fonts .nc-chip { font-size: 27px; min-height: 58px; }

  .nc-dot { width: 42px; height: 42px; }
  .nc-row--colors { gap: 10px; }

  .nc-price__value { font-size: 22px; }
}

@supports not (aspect-ratio: 1) {
  @media screen and (min-width: 600px) {
    .nc-view { height: 400px; max-height: none; }
  }
}

/* ------------------------------------------------------------- >= 900px */

@media screen and (min-width: 900px) {
  /* The bar stops sticking once the whole widget fits on one screen — a
   * floating bar over a fully visible layout is just clutter. */
  .nc-bar { position: static; }
}

/* --------------------------------------------------------- light theme */

/* The widget stays dark in a light theme rather than inverting: a neon sign
 * only reads as lit against a dark field, and every reference photograph is a
 * dark wall. The border is lifted so the panel does not float untethered on a
 * white page. */
@media (prefers-color-scheme: light) {
  .neon-customizer { --nc-line: #2f3644; }
}

/* ------------------------------------------------------------ >= 1024px */

/* TWO COLUMNS: the picture beside the controls (owner, 2026-09-22).
 *
 * Below this the widget is one column capped at 720px, which is right on a
 * phone and wasteful on a desktop — the sign, the thing the page exists to
 * show, was rendered at 720px on a 1920px screen with empty gutters either
 * side of it. Here the picture takes the width it earns and the controls sit
 * beside it, so the whole tool is visible at once and the sign is plainly the
 * subject.
 *
 * WHY THE CONTROL COLUMN SCALES, AND WHY THAT IS WHAT MAKES 1024px WORK
 * ---------------------------------------------------------------------
 * A fixed 420px column broke this at 1024px: the picture gets whatever width
 * is left, which was a 570x380 frame beside controls ~525px tall — the picture
 * SHORTER than the controls it is meant to lead, with a dead gap under it. It
 * read as broken because it was.
 *
 * Two alternatives were measured and rejected:
 *
 *   - Raise the breakpoint and widen the single column. Gives a bigger picture
 *     (880x587 at 1024px) and pushes every control below the fold on a 768px
 *     laptop. Unusable for a tool whose job is changing a setting and seeing
 *     the result.
 *   - Keep the fixed column and start two columns at 1280px. Made the picture
 *     SHRINK as the screen grew — 880px wide at 1279px, 776px at 1280px.
 *
 * Scaling the column solves both. At 1024px it is 340px, leaving the picture
 * 592x394: wider than the controls so the hierarchy reads, and short enough
 * that the whole widget fits a 1024x768 laptop without scrolling. It reaches
 * its 420px ceiling around 1400px and stops, because past that the chips have
 * no reason to keep growing and the picture should take the width.
 */
@media screen and (min-width: 1024px) {
  .neon-customizer {
    /* The one number the rest of this block is derived from. It appears in the
       column track AND in the picture's width limit below; they must agree, so
       it is a variable rather than two literals that can drift apart. */
    --nc-panel-w: clamp(340px, 30vw, 420px);

    /* A ceiling, not a width. The picture is sized from the viewport and the
       grid is content-sized, so the widget is as wide as the picture makes it;
       this only stops it running away on an ultrawide. */
    max-width: min(1760px, 100%);
    display: grid;

    /* `auto`, not a fraction, for the picture column.
     *
     * This is what lets the picture drive the layout instead of the other way
     * round. The frame has a definite height and a 3:2 ratio, so its own width
     * is fixed at 1.5x that height; an `auto` track takes exactly that, and the
     * widget ends up as wide as the screen's HEIGHT allows. A fractional track
     * would do the reverse — stretch the frame to whatever width was going and
     * force its height to follow, which is how this ended up frozen at 772x515
     * on every screen from 1280 to 1920. */
    grid-template-columns: auto var(--nc-panel-w);

    /* The picture spans both rows; the controls sit at the top of the right
     * column and the price bar is pinned to the bottom of it.
     *
     * The bar used to span the full width underneath. That worked, but the
     * picture is sized from the viewport and is far taller than the controls —
     * on a 1080px screen it left roughly 300px of nothing under the size chips.
     * Putting the bar at the foot of that column fills it and reads as intended
     * rather than as a gap: options at the top, price and commit button at the
     * bottom, level with the base of the picture. It also buys height, since
     * there is no full-width row below taking any. */
    grid-template-areas:
      'view panel'
      'view bar';
    grid-template-rows: auto 1fr;
    column-gap: 28px;

    /* Centres the pair when the picture's height-derived width leaves room —
       on a 1920x1080 screen the height runs out first, so there is spare width
       and the widget would otherwise sit hard against the left. */
    justify-content: center;

    /* Top-aligned: the picture is taller than the controls, and stretching the
       chip rows to match would space them by an accident of the render's
       aspect ratio rather than by the rhythm --nc-gap sets. */
    align-items: start;
  }

  .nc-view {
    grid-area: view;

    /* THE PICTURE IS SIZED FROM THE VIEWPORT, and 3:2 is preserved rather than
     * stretched. That ratio is load-bearing — see the note on .nc-view above:
     * the frame matches the 1536x1024 the endpoint returns so `cover` has
     * nothing to crop. Widening the frame to fill the screen would start
     * cropping the sign instead, which is the one thing this must not do.
     *
     * So height leads and width follows, under TWO limits, whichever is
     * tighter:
     *
     *   - `100vh - 300px` is the chrome above and below — nav, heading, subtitle,
     *     and the page's bottom padding. Binding on a short window.
     *   - the second term is the WIDTH limit solved for height. The widget is
     *     the picture plus the control column plus a 28px gap, so the picture
     *     can be at most (available - column - 28) wide, and at 3:2 that is a
     *     height of a third less again. Binding on a wide, short screen. 64px
     *     covers the page's 16px gutters plus a scrollbar, which `100vw` counts
     *     and the layout does not get to use.
     *
     * Without the second term the picture overflows sideways on any screen
     * wider than it is tall, which is most of them.
     *
     * The floor keeps it from collapsing on a short window; the ceiling stops a
     * tall monitor producing a picture so large the controls look abandoned. */
    height: clamp(
      320px,
      min(
        calc(100vh - 300px),
        calc((min(100vw - 64px, 1760px) - var(--nc-panel-w) - 28px) / 1.5)
      ),
      820px
    );
    width: auto;

    /* Without this the grid item stretches to its track and the track sizes to
       the item — circular, and the browser resolves it by ignoring the height. */
    justify-self: start;
  }

  .nc-panel {
    grid-area: panel;
    /* No top padding here: in one column this gap separates the panel from the
       picture above it, and side by side there is nothing above it to clear —
       the padding would just drop the controls out of line with the picture. */
    padding-top: 0;
  }

  .nc-bar {
    grid-area: bar;
    /* Pinned to the foot of its row, which stretches to the picture's base. */
    align-self: end;
    margin-top: var(--nc-gap);
  }

  /* Two across, not the four the >=600px rule sets. Four chips inside a 340px
     column leaves ~80px each, and a chip carries a label, an inch figure
     and a price range. Two across is the treatment the phone layout already
     uses and is tuned for, and it costs one row in a column with height to
     spare. */
  .nc-row--sizes { grid-template-columns: repeat(2, 1fr); }

  /* The five style chips must stay on ONE row, and at the narrow end of this
     breakpoint they missed by two pixels: 5 x 62px plus four 8px gaps is 342px
     in a 340px column, so the fifth wrapped onto a line of its own and — being
     `flex: 1 1 0` — stretched across the full width, which reads as a separate
     control rather than the last of a set. 56px clears it with room. This only
     moves the wrap threshold; the chips still grow to fill the row, so nothing
     changes once the column is wider. */
  .nc-row--fonts .nc-chip { min-width: 56px; }
}

/* ------------------------------------------------------------ >= 1280px */

/* The controls grow once the column does — and only if there is height for it.
 *
 * Held back to 1280px on purpose. These sizes need the control column to be
 * near its 420px ceiling; applied at 1024px, where the column is 340px, the
 * larger colour dots reflow to three rows and the panel grows taller than the
 * picture beside it — the same inversion this layout exists to avoid.
 *
 * THE HEIGHT CONDITION IS NOT DECORATION. Enlarged, the control column stands
 * about 545px; on a 1366x768 laptop that is taller than the picture beside it,
 * so the COLUMN becomes what sets the widget's height and the page overflowed
 * by 4px — enough for a full-height scrollbar over a layout that otherwise
 * fits exactly. Shrinking the picture does not help, because the picture was
 * never the tall one. Below 820px of viewport the controls stay at their
 * >=600px size, the picture leads again, and 1366x768 fits.
 *
 * Worth doing on its own merits and not only to fill space: this is a mouse on
 * a large screen, and every one of these was sized for a thumb on a phone.
 *
 * Deliberately NOT stretched to match the picture. Spacing the rows by the
 * render's aspect ratio would make the gaps drift every time the picture
 * changed shape, and rows that far apart stop reading as one control. */
@media screen and (min-width: 1280px) and (min-height: 820px) {
  .neon-customizer { --nc-gap: 20px; }
  .nc-row--fonts .nc-chip { min-height: 68px; }
  .nc-dot { width: 48px; height: 48px; }
  .nc-row--colors { gap: 12px; }
  .nc-chip--size { padding-top: 14px; padding-bottom: 14px; }
}
