/* ========================= LAYOUT SHELL =========================
   The whole app is one absolutely-positioned frame: a fixed header, the 3D stage, and the config
   panel beside it. These three used to be pinned with INLINE styles (top:60px; right:440px;
   width:440px), which is why there was no mobile layout at all — an inline style outranks every
   stylesheet rule, so no media query could move them without !important. The geometry now lives
   here, driven by two variables, and the breakpoints simply retune those.

   On a 390px phone the old numbers were not merely cramped, they were degenerate: the panel was
   440px — wider than the screen — and the stage's `right: 440px` gave it a NEGATIVE width, so the
   3D view collapsed to nothing. */
:root {
    --header-h: 60px;
    --panel-w: 440px;
}

#configurator {
    /* dvh, not vh: on mobile browsers 100vh is the viewport WITHOUT the address bar, so the
       bottom of the layout — the price and "Add to cart" — sat underneath the browser chrome and
       could not be reached. vh first as the fallback for anything that doesn't know dvh. */
    height: 100vh;
    height: 100dvh;
    width: 100%;          /* not 100vw — that ignores the scrollbar and forces a sideways scroll */
}

#app-header  { height: var(--header-h); }
#stage       { top: var(--header-h); right: var(--panel-w); }
#config-panel{ top: var(--header-h); width: var(--panel-w); }

/* ========================= BASE ========================= */
html, body { height: 100%; }
body {
    margin: 0;
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Display",
        "SF Pro Text", "Segoe UI", Roboto, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    letter-spacing: -0.01em;
    background: #f5f5f7;
    color: #1d1d1f;
    overflow: hidden;
}
code, .mono {
    font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}
::selection { background: rgba(0, 113, 227, 0.18); color: #1d1d1f; }
.hidden { display: none !important; }

/* ========================= SCROLLBAR ========================= */
.scroll-slim::-webkit-scrollbar { width: 8px; }
.scroll-slim::-webkit-scrollbar-track { background: transparent; }
.scroll-slim::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.12);
    border-radius: 9999px;
    border: 2px solid #ffffff;
}
.scroll-slim::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.24); }

/* ========================= APPLE-STYLE CARD ========================= */
.apple-card {
    background: #ffffff;
    border: 1px solid #d2d2d7;
    border-radius: 12px;
    transition: border-color 150ms ease, box-shadow 180ms ease, transform 120ms ease;
}
.apple-card:hover { box-shadow: 0 4px 16px -6px rgba(0, 0, 0, 0.08); }
.apple-card-selected { border-color: #0071e3; box-shadow: 0 0 0 1px #0071e3; }

/* Hover must never erase the selection. Both rules set box-shadow, and `.apple-card:hover`
   (0-2-0) outranks `.apple-card-selected` (0-1-0) — so pointing at the selected card swapped its
   blue ring for a plain grey shadow and the card went white, reading as DESELECTED exactly when
   you aimed at it. Keep the ring and layer the lift on top, tinted blue to match. */
.apple-card-selected:hover {
    box-shadow: 0 0 0 1px #0071e3, 0 4px 16px -6px rgba(0, 113, 227, 0.28);
}

/* Tool toggles (Show Dimensions / Day-night).
   Hover must never change which STATE the button is in — only lighten the colour it already has:
   white stays white, blue goes light blue. The markup hard-codes Tailwind's `hover:bg-[#f5f5f7]`,
   and styleToggleButton() only swaps the BASE colour to `bg-[#0071e3]` when the tool is ON — it
   never drops that hover class. And `.hover\:bg-\[#f5f5f7\]:hover` (0-2-0) outranks
   `.bg-\[#0071e3\]` (0-1-0), so pointing at an ACTIVE tool washed it to near-white and it read as
   switched OFF; moving away turned it blue again.

   Both rules below are scoped to #top-tools on purpose: the option cards and swatches carry
   aria-pressed too, and a blue fill on those would be plain wrong. */

/* OFF + hover → darkens. Tailwind's own `hover:bg-[#f5f5f7]` was too faint to register against a
   white button, so it just looked dirty rather than responsive.
   `:not([aria-pressed="true"])` rather than `[aria-pressed="false"]` so it also covers Reset
   camera, which is a momentary button and carries no aria-pressed at all — otherwise it would be
   the one tool left with the old, barely-visible hover. */
#top-tools button:not([aria-pressed="true"]):hover {
    background-color: #e8e8ed;
    border-color: #86868b;
}

/* ON + hover → light blue. Still unmistakably "on", still visibly reacting to the cursor. */
#top-tools button[aria-pressed="true"]:hover {
    background-color: #4da2ff;
    border-color: #4da2ff;
}

/* ========================= BUTTONS ========================= */
.btn-apple {
    background: #0071e3;
    color: #ffffff;
    border-radius: 10px;
    padding: 10px 22px;
    font-weight: 500;
    font-size: 15px;
    line-height: 1;
    letter-spacing: -0.01em;
    transition: background-color 150ms ease, transform 120ms ease;
    border: none;
    cursor: pointer;
}
.btn-apple:hover { background: #0077ed; }
.btn-apple:active { transform: scale(0.98); }
.btn-apple:disabled { background: #e5e5ea; color: #8e8e93; cursor: not-allowed; }
/* ...except while it is working. A button that greys out the moment you press it reads as a
   failure; with the spinner beside the label it should look like the thing it is doing. */
.btn-apple:disabled.is-busy { background: #0071e3; color: #ffffff; cursor: progress; }

.btn-apple-lg { padding: 16px 28px; font-size: 17px; font-weight: 600; }

.btn-apple-outline {
    background: #ffffff;
    color: #1d1d1f;
    border: 1px solid #d2d2d7;
    border-radius: 10px;
    padding: 9px 18px;
    font-weight: 500;
    font-size: 14px;
    line-height: 1;
    letter-spacing: -0.01em;
    transition: background-color 150ms ease, border-color 150ms ease, transform 120ms ease;
    cursor: pointer;
}
.btn-apple-outline:hover { background: #f5f5f7; border-color: #b8b8bd; }
.btn-apple-outline:active { transform: scale(0.98); }
.btn-apple-outline-sm { padding: 7px 14px; font-size: 13px; }

/* ========================= PRESS FEEDBACK =========================
   One press behaviour for the whole panel: the control dips under the finger and springs back
   when released. Two different curves on purpose — the dip is short and linear (90ms) so it lands
   the instant you click, and the release overshoots slightly (a spring curve) so it feels like
   something physical let go rather than a value easing back.

   The amount of dip scales INVERSELY with the control's size: a 48px round button can travel 8%
   and still look composed, while the same 8% on a full-width card would look like the panel is
   collapsing. Big surfaces get a much smaller number to read as the same gesture.

   The spring curve is scoped to `transform` alone. These controls also transition border-color and
   box-shadow, and an overshoot curve on a COLOUR overshoots past the target value — the border
   would flick a shade too dark before settling. Colours keep their own plain ease. */
#top-tools button {
    /* Tailwind's `transition-all` on the markup covers transform too, but with its own flat
       150ms curve — restate the whole list so the spring lands only on transform. */
    transition: background-color 150ms ease, border-color 150ms ease,
                transform 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.apple-card {
    transition: border-color 150ms ease, box-shadow 180ms ease,
                transform 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn-apple {
    transition: background-color 150ms ease,
                transform 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn-apple-outline {
    transition: background-color 150ms ease, border-color 150ms ease,
                transform 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Held down: snap in fast and flat. The spring belongs to the RELEASE — springing INTO a press
   would put the dip a beat behind the cursor and feel mushy. */
#top-tools button:active   { transform: scale(0.88);  transition: transform 90ms ease-out; }
.btn-apple:active          { transform: scale(0.96);  transition: transform 90ms ease-out; }
.btn-apple-outline:active  { transform: scale(0.96);  transition: transform 90ms ease-out; }
.apple-card:active         { transform: scale(0.985); transition: transform 90ms ease-out; }

/* Swatches: the colour dot itself takes the press, not the label under it. Outranks Tailwind's
   `group-hover:scale-[1.05]` (0-2-0) on an id-anchored selector, so no !important needed. */
#group-exterior button:active span:first-child { transform: scale(0.92); }

/* § 5 DDG / Art. 13 DSGVO — reachable from the configurator page too.

   ⚠ This block MUST stay ABOVE the breakpoint blocks below. It used to sit at the end of the
   file, where — same specificity, later in source — it beat the mobile overrides and re-applied
   `right`/`bottom` on top of their `left`/`top`. A fixed box with BOTH horizontal and BOTH
   vertical offsets set stretches to fill the gap, so the strip became a full-screen invisible
   tap target: it swallowed taps meant for the Phone Booth option, the total price and the CTA.

   `width: max-content` is the belt-and-braces half of the fix — with it the box hugs its two
   links no matter what the cascade does with the offsets, so this can never regress into a
   page-sized hit area again. */
.legal-links { position: fixed; right: 12px; bottom: 10px; z-index: 60; display: flex; gap: 14px;
  width: max-content; max-width: calc(100vw - 28px); height: max-content;
  font-size: 12px; font-family: inherit;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(255,255,255,0.82); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: #1d1d1f; }
.legal-links a { color: inherit; text-decoration: none; opacity: .75; }
.legal-links a:hover, .legal-links a:focus-visible { opacity: 1; text-decoration: underline; }

/* A press is a gesture, not decoration — but honour the OS switch and drop the movement. */
@media (prefers-reduced-motion: reduce) {
    #top-tools button:active,
    .apple-card:active,
    .btn-apple:active,
    .btn-apple-outline:active,
    #group-exterior button:active span:first-child { transform: none; }
}

.btn-apple-link {
    color: #0071e3;
    font-weight: 500;
    font-size: 14px;
    letter-spacing: -0.01em;
    transition: color 150ms ease;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
}
.btn-apple-link:hover {
    color: #0077ed;
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* ========================= ANIMATIONS ========================= */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}
.fade-in { animation: fadeIn 0.4s cubic-bezier(0.2, 0.7, 0.2, 1) both; }

@keyframes pricePop {
    0%   { transform: translateY(3px); opacity: 0.4; }
    100% { transform: translateY(0); opacity: 1; }
}
.price-pop { animation: pricePop 0.3s cubic-bezier(0.2, 0.7, 0.2, 1); }

/* ========================= SCENE ========================= */
.scene-blueprint { filter: grayscale(1) opacity(0.25); transition: filter 400ms ease; }
.scene-exploded  { filter: saturate(0.7) opacity(0.85); transition: filter 400ms ease; }
.scene-night     { filter: brightness(0.6) contrast(1.1) hue-rotate(-10deg); transition: filter 400ms ease; }
.scene-canvas    { transition: filter 400ms ease; background: transparent; }

/* Remove standard focus outline from Unity canvas */
#unity-canvas:focus { outline: none; }

/* Unity warning banners */
.unity-banner {
    background: #ffffff;
    border: 1px solid #e5e5ea;
    color: #1d1d1f;
    box-shadow: 0 8px 24px rgba(0,0,0,0.08);
    border-radius: 10px;
    padding: 10px 14px;
    font-size: 13px;
    margin-bottom: 8px;
    max-width: min(480px, calc(100vw - 32px));
}
.unity-banner-warning { border-color: #f5c518; background: #fffbe6; }
.unity-banner-error   { border-color: #ff3b30; background: #fff1f0; color: #a8071a; }

/* ========================= TOASTS ========================= */
.toast-container {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 8px;
    pointer-events: none;
}
.toast {
    background: #ffffff;
    border: 1px solid #e5e5ea;
    color: #1d1d1f;
    box-shadow: 0 8px 24px rgba(0,0,0,0.08);
    border-radius: 10px;
    padding: 12px 16px;
    min-width: 280px;
    max-width: 360px;
    pointer-events: auto;
    animation: toastIn 0.25s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
.toast.leaving { animation: toastOut 0.2s ease both; }
@keyframes toastIn  { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
@keyframes toastOut { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(20px); } }
.toast-title { font-weight: 600; font-size: 14px; }
.toast-desc  { font-size: 13px; color: #86868b; margin-top: 2px; }

/* ========================= TOOLTIPS ========================= */
.tooltip-el {
    position: absolute;
    left: 50%;
    bottom: calc(100% + 8px);
    transform: translateX(-50%);
    background: #1d1d1f;
    color: #ffffff;
    font-size: 11px;
    letter-spacing: 0.02em;
    padding: 4px 8px;
    border-radius: 6px;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity 120ms ease;
    z-index: 20;
}
.tool-btn-wrap:hover .tooltip-el { opacity: 1; }

/* ========================= MODAL ========================= */
.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    animation: fadeIn 0.2s ease;
}
.modal-card {
    background: white;
    border-radius: 16px;
    max-width: 900px;
    width: 95vw;
    max-height: 90vh;
    /* Scrollable ALWAYS, not only under a width breakpoint. This card is capped at 90vh and its
       form column is ~550px tall, so the moment the viewport is short the bottom of it — the
       Message field and the "Place order" button — is simply cut off. With `overflow: hidden` and
       `body { overflow: hidden }` there was no scroll container anywhere in the ancestry, so the
       order could not be placed at all. A phone in LANDSCAPE (844×390, 852×393, 932×430) is WIDER
       than the old 767px rescue breakpoint, so it never matched: the one layout where the card is
       shortest was the one layout with no way out. */
    overflow: hidden auto;
    box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
    display: grid;
    /* minmax(0, ...) rather than a bare fr. A grid item's min-width defaults to auto, i.e. its
       min-content width, so a bare 1fr column can never be narrower than its widest
       unbreakable child — and the summary column has `truncate` rows whose min-content is the
       full untruncated string. The column claimed 473px inside a 370px card, the text ran off
       the right edge, and `truncate` never got the chance to do the one thing it is for. */
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
}

/* Two columns need width; one column needs height. Gate each on the axis it actually depends on —
   a landscape phone has the width for two columns but nowhere near the height. */
@media (max-width: 767px), (max-height: 620px) {
    .modal-card {
        grid-template-columns: minmax(0, 1fr);
        max-height: 92vh;
        max-height: 92dvh;
    }
    /* Stacked to one column, the summary panel's left divider hangs as a stray vertical line —
       turn it into a horizontal rule between the stacked halves instead. */
    .modal-card > div:last-child { border-left: none; border-top: 1px solid #e5e5ea; }
}
/* iOS Safari auto-zooms into any focused input under 16px; keep the checkout fields at 16px on phones. */
@media (max-width: 430px) {
    .modal-card .field { font-size: 16px; }
}

/* ========================= UNITY GATE =========================
   While Unity is rebuilding the model the panel must not accept input — otherwise
   the user out-clicks the engine and the panel shows a state the scene hasn't
   applied. Set on <body> by setBusy() in app.js; cleared by ReceiveDataFromUnity. */
/* #hdr-share and #hdr-pdf belong in here too. Both ask Unity for a SNAPSHOT of the current pod —
   a share id, or the render + id for the quote. Left ungated, they were tappable while Unity was
   still rebuilding the model, so the link (or the printed PDF) could describe the pod the user had
   just navigated AWAY from. On a phone the engine takes long enough that this is easy to hit. */
#config-panel .scroll-slim,
#btn-checkout,
#top-tools,
#hdr-reset,
#hdr-share,
#hdr-pdf {
    transition: opacity 160ms ease;
}
.unity-busy #config-panel .scroll-slim,
.unity-busy #btn-checkout,
.unity-busy #top-tools,
.unity-busy #hdr-reset,
.unity-busy #hdr-share,
.unity-busy #hdr-pdf {
    pointer-events: none;
    opacity: 0.45;
}
/* the whole panel blocks input, but keep it scrollable so long option lists stay readable */
.unity-busy #config-panel .scroll-slim { pointer-events: auto; }
.unity-busy #config-panel .apple-card,
.unity-busy #config-panel button {
    pointer-events: none;
    cursor: wait;
}

/* ========================= SPINNER ========================= */
.spinner {
    width: 16px; height: 16px;
    border: 2px solid rgba(255,255,255,0.3);
    border-top-color: #ffffff;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ========================= TOUCH: KILL LATCHED HOVER =========================
   On a touch screen there is no hover, but the browser fakes one: after a tap the element keeps
   :hover until you tap somewhere ELSE. Every hover style in this file therefore latches — the tool
   you tapped stays lit, its tooltip hangs there, the card you picked keeps its lift.

   Merely wrapping our own rules in `@media (hover: hover)` would NOT fix it: the markup also
   carries Tailwind's `hover:bg-[#f5f5f7]` utility, which latches just the same and would then win
   uncontested — tapping an ACTIVE (blue) tool would leave it washed grey. So the hover states are
   pinned back to their resting values instead of merely being switched off.

   Comes last on purpose: these tie on specificity with the rules they undo, so order decides. */
@media (hover: none) {
    #top-tools button:not([aria-pressed="true"]):hover { background-color: #ffffff; border-color: #d2d2d7; }
    #top-tools button[aria-pressed="true"]:hover       { background-color: #0071e3; border-color: #0071e3; }
    .tool-btn-wrap:hover .tooltip-el { opacity: 0; }

    .apple-card:hover          { box-shadow: none; }
    .apple-card-selected:hover { box-shadow: 0 0 0 1px #0071e3; }

    .btn-apple:hover         { background: #0071e3; }
    .btn-apple-outline:hover { background: #ffffff; border-color: #d2d2d7; }
}

/* ========================= RESPONSIVE =========================
   Media queries add no specificity, so these override the shell at the top of the file purely by
   coming later. Keep them last.

   The axis that runs out is NOT always width. A phone held sideways (844×390) has plenty of width
   and almost no height, so stacking the panel under the model there would leave a ~160px sliver of
   3D — worse than doing nothing. Hence portrait and landscape are treated as different layouts,
   not as one "mobile" bucket. */

/* Small laptops and landscape tablets: the panel keeps its shape, the model just gets more room. */
@media (max-width: 1100px) {
    :root { --panel-w: 360px; }
}

/* ── Phones / small tablets, PORTRAIT: model on top, panel below ───────────────────────────── */
@media (max-width: 820px) and (orientation: portrait) {
    :root {
        --header-h: 56px;
        /* Everything the stacked layout hangs off. Shrinking the 3D window hands the difference
           straight to the panel below it, which is the part you actually scroll.

           vh first, dvh only where it exists. The usual `height: A; height: B;` fallback does NOT
           work through a variable: `height: var(--stage-h)` is valid whatever the variable holds,
           so a browser without dvh does not skip the declaration — it accepts it, fails at
           computed-value time, and lands on `auto`. On an absolutely-positioned, empty stage that
           is 0px: the 3D view would vanish entirely instead of degrading. @supports is the only
           construct that actually guards it. */
        --stage-h: 36vh;
    }
    @supports (height: 1dvh) {
        :root { --stage-h: 36dvh; }
    }

    #stage {
        right: 0;
        /* Tailwind pins bottom-0 on the stage. It has to be released, or the stage keeps
           stretching to the floor and the panel is drawn on top of the model. */
        bottom: auto;
        height: var(--stage-h);
    }

    #config-panel {
        top: calc(var(--header-h) + var(--stage-h));
        left: 0;
        width: auto;                   /* left:0 + right:0 → full width */
        border-left: none;
        border-top: 1px solid #d2d2d7;
        box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.04);
    }

    /* Header: icons only. "Reset · Share · PDF export" as words cannot fit beside the logo at
       390px — they were squashing the brand out of the row. */
    #app-header { padding-left: 14px; padding-right: 14px; }
    #app-header .btn-label { display: none; }
    #app-header .btn-apple-outline { padding: 8px; gap: 0; }
    #app-header #size-chip,
    #app-header .hdr-divider { display: none; }   /* the model name is already in the panel */

    /* Tools: out of the centre, down into the bottom-RIGHT corner, stacked as a column that grows
       upward. Centred at the bottom they sat right across the middle of the pod on a stage this
       short — the one place the model actually occupies. The corner is dead space.
       Both `left: auto` and `transform: none` are required: Tailwind pins left-1/2 and centres it
       with -translate-x-1/2, and leaving either in place drags the strip back toward the middle. */
    #top-tools {
        left: auto;
        right: 12px;
        bottom: 12px;
        transform: none;
    }
    #top-tools > div { flex-direction: column; gap: 4px; padding: 4px; }
    #top-tools button { height: 36px; width: 36px; }
    #top-tools button svg { height: 16px; width: 16px; }

    /* The divider is `w-px` — a 1px-WIDE vertical rule, correct for the desktop row. In a column it
       collapses to a zero-height sliver: no line at all, just its my-2 margins left behind as an
       unexplained 16px hole between the toggles and Reset camera. Lay it on its side and tighten it. */
    #top-tools .tool-divider {
        width: 100%;
        height: 1px;
        margin: 3px 0;
    }

    /* Tooltips default to sitting ABOVE the button — in a column that lands on top of the button
       above it. Swing them out to the LEFT: the strip is against the right edge of the screen, so
       to the right there is nothing to swing into. */
    #top-tools .tooltip-el {
        left: auto;
        right: calc(100% + 8px);
        bottom: 50%;
        transform: translateY(50%);
    }

    /* 32px of side padding is a lot of a 390px screen. */
    #config-panel .panel-head,
    #config-panel .scroll-slim,
    #config-panel .panel-foot { padding-left: 18px; padding-right: 18px; }

    #config-panel .panel-head { padding-top: 14px; padding-bottom: 12px; }
    #config-panel .panel-head h2 { font-size: 22px; }
    #config-panel .panel-head p  { display: none; }   /* strapline is a luxury down here */

    #config-panel .scroll-slim { padding-top: 16px; padding-bottom: 16px; }
    #config-panel .scroll-slim > * + * { margin-top: 20px; }   /* beats Tailwind's space-y-8 */

    /* env() keeps the buy button clear of the iPhone home indicator, which otherwise sits right
       on top of it. */
    #config-panel .panel-foot {
        padding-top: 14px;
        padding-bottom: calc(14px + env(safe-area-inset-bottom));
    }
    #config-panel .panel-foot #total-price { font-size: 24px; }

    /* Toasts default to the top-right — which on a phone is exactly where the header's icon-only
       buttons now live. A 280px-wide toast covered them and ate taps for its full 3.5s life.
       Send them to the bottom instead, spanning the width. */
    .toast-container {
        top: auto;
        left: 16px;
        right: 16px;
        bottom: calc(96px + env(safe-area-inset-bottom));   /* clear the full-width panel-foot "Add to cart" */
    }
    .toast { min-width: 0; max-width: none; }
    /* The fixed legal strip sat on the bottom-right CTA. Move it under the 3D stage, on the left. */
    /* In flow, under the quote button, rather than floating over the panel. The pill's own
       white background and blur are pointless against the white panel foot, and dropping the
       blur removes a backdrop-filter from the phone that was only ever there to sit on top of
       the 3D view. Every offset is reset: the base rule sets right/bottom, and a stale offset
       on a static box is silently ignored until something turns positioning back on. */
    .legal-links { position: static; top: auto; right: auto; bottom: auto; left: auto;
      margin: 14px 0 0 auto; padding: 0; background: none;
      backdrop-filter: none; -webkit-backdrop-filter: none;
      color: #86868b; }
}

/* ── Phones, LANDSCAPE: height is the scarce axis — keep the panel beside the model ────────── */
@media (max-height: 520px) and (orientation: landscape) {
    :root {
        --header-h: 48px;
        --panel-w: 300px;
    }

    #app-header { padding-left: 14px; padding-right: 14px; }
    #app-header .btn-label { display: none; }
    #app-header .btn-apple-outline { padding: 7px; gap: 0; }
    #app-header #size-chip,
    #app-header .hdr-divider { display: none; }

    /* Same move as portrait: bottom-right column, out of the model's way (see above). */
    #top-tools {
        left: auto;
        right: 12px;
        bottom: 10px;
        transform: none;
    }
    #top-tools > div { flex-direction: column; gap: 4px; padding: 4px; }
    #top-tools button { height: 34px; width: 34px; }
    #top-tools button svg { height: 15px; width: 15px; }
    #top-tools .tool-divider { width: 100%; height: 1px; margin: 3px 0; }
    #top-tools .tooltip-el {
        left: auto;
        right: calc(100% + 8px);
        bottom: 50%;
        transform: translateY(50%);
    }

    #config-panel .panel-head,
    #config-panel .scroll-slim,
    #config-panel .panel-foot { padding-left: 18px; padding-right: 18px; }

    #config-panel .panel-head { padding-top: 12px; padding-bottom: 10px; }
    #config-panel .panel-head h2 { font-size: 20px; }
    #config-panel .panel-head p  { display: none; }

    #config-panel .scroll-slim { padding-top: 14px; padding-bottom: 14px; }
    #config-panel .scroll-slim > * + * { margin-top: 16px; }

    #config-panel .panel-foot {
        padding-top: 12px;
        padding-bottom: calc(12px + env(safe-area-inset-bottom));
    }
    #config-panel .panel-foot #total-price { font-size: 22px; }

    /* Same as portrait: keep the toasts off the icon-only header buttons. */
    .toast-container {
        top: auto;
        left: auto;
        right: 16px;
        bottom: calc(72px + env(safe-area-inset-bottom));   /* clear the panel-foot CTA */
    }
    /* legal strip to the bottom-left, clear of the right-hand panel + its CTA */
    .legal-links { left: 14px; right: auto; bottom: 8px; }
}



/* ── Language switch ──────────────────────────────────────────────────────────
   Rendered by i18n.js into [data-lang-slot] where a page offers one, and floated
   bottom-left where none does — so the Demo and the legal pages get it without
   either of them having to know about it. */
#lang-switch {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  border-radius: 999px;
  border: 1px solid var(--line-0, rgba(255, 255, 255, 0.12));
  background: var(--surface-veil, rgba(20, 22, 28, 0.6));
}
#lang-switch.lang-switch-float {
  position: fixed;
  left: 12px;
  bottom: 10px;
  z-index: 60;
  background: rgba(255, 255, 255, 0.86);
  border-color: rgba(0, 0, 0, 0.12);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.lang-btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  background: none;
  color: var(--fg-2, #86868b);
  font: 600 11px/1 var(--font-mono, ui-monospace, monospace);
  letter-spacing: 0.08em;
  padding: 5px 9px;
  border-radius: 999px;
  transition: color 0.2s, background-color 0.2s;
}
#lang-switch.lang-switch-float .lang-btn { color: #6e6e73; }
.lang-btn:hover { color: var(--fg-0, #fff); }
#lang-switch.lang-switch-float .lang-btn:hover { color: #1d1d1f; }
.lang-btn.is-on {
  background: var(--accent, #4163e4);
  color: #fff;
}
#lang-switch.lang-switch-float .lang-btn.is-on { background: #0071e3; color: #fff; }
.lang-btn:focus-visible { outline: 2px solid var(--accent, #4163e4); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .lang-btn { transition: none; }
}
