/* The phone shell is an APP, not a document: nothing here is ever meant to scroll as a
   page (each surface scrolls its own body). Locking both elements matters because a page
   that can move by even the height of the URL bar lets the top or bottom of a fixed
   surface sit off-screen, and overscroll-behavior kills the rubber-band that shows the
   same crop momentarily. */
html {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
}

/* Screen-reader-only content (available to assistive tech and crawlers, not shown on screen) */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

body {
    font-family: Arial, sans-serif;
    text-align: center;
    margin: 0;
    /* dvh, not vh: vh is the LARGE viewport (URL bar hidden), so on a phone showing its
       URL bar a 100vh body is taller than the screen by exactly the bar. That is the
       "page is a little bit scrollable, the top/bottom get cropped" report. */
    height: 100%;
    height: 100dvh;
    box-sizing: border-box;
    overflow: hidden;
    overscroll-behavior: none;
    background: var(--pix-bg-page);
    padding-top: var(--canvas-top-offset);
}

:root {
    /* Background design (backgrounds.js) - the shipped values, mirrored from style.css.
       Choosing a background sets the same custom properties inline on <html>. Mobile has no
       lilac band and no tool buttons, but the vars are declared anyway so one design file
       serves both apps and nothing here can resolve to an empty image. */
    --pix-bg-page: #b4def3;
    --pix-bg-brown: url('assets/background/vines/brown.png');
    /* ONE tile (the shipped art is 960px = two curtains in turn); see style.css. */
    --pix-bg-vines: url('assets/background/vines/vines.png');
    --pix-bg-lilac: url('assets/background/vines/lilac.png');
    --pix-bg-btn-a: url('assets/background/vines/button-a.png');
    --pix-bg-btn-b: url('assets/background/vines/button-b.png');
    --pix-bg-btn-undo: url('assets/background/vines/button-undo.png');
    --pix-bg-btn-redo: url('assets/background/vines/button-redo.png');
    /* The same nine tool buttons addressed by POSITION, for a design that draws them as a
       series (button-1.png .. button-9.png) rather than as two alternating shapes. Same
       alternation as style.css. */
    --pix-bg-btn-1: var(--pix-bg-btn-a);
    --pix-bg-btn-2: var(--pix-bg-btn-b);
    --pix-bg-btn-3: var(--pix-bg-btn-b);
    --pix-bg-btn-4: var(--pix-bg-btn-a);
    --pix-bg-btn-5: var(--pix-bg-btn-b);
    --pix-bg-btn-6: var(--pix-bg-btn-a);
    --pix-bg-btn-7: var(--pix-bg-btn-b);
    --pix-bg-btn-8: var(--pix-bg-btn-b);
    --pix-bg-btn-9: var(--pix-bg-btn-a);
    --pix-bg-lila-active: #a667e4;
    /* The chrome: buttons, sliders, panels and readouts (backgrounds.js BUTTON_STYLES).
       The SHIPPED palette, mirrored from style.css - a cream face with an olive edge, which
       is why it needs to change when the art behind it does. face/face-2/edge are RGB
       TRIPLES so each rule keeps its own alpha: rgba(var(--pix-ui-face), 0.88). */
    --pix-ui-face: 255, 252, 238;
    --pix-ui-face-2: 241, 235, 199;
    --pix-ui-edge: 124, 143, 29;
    --pix-ui-ink: #2a2a22;
    --pix-ui-ink-soft: #6b6a58;
    --pix-ui-on: #2e2c1e;
    --pix-ui-on-ink: #f5f0d8;
    --pix-ui-accent: #7c8f1d;
    --pix-ui-accent-dark: #55691c;
    --brown-band-height: 88px;
    --canvas-below-brown: 19px;
    --canvas-top-offset: calc(var(--brown-band-height) + var(--canvas-below-brown));
    --yellow-vines-top: 56px;
    --yellow-vines-left: 0px;
    --yellow-vines-height: 320px;
    --lilac-image-offset-y: 60px;
    --lilac-image-offset-scaled: 90px;
    --lilac-panel-width: 140px;
    --lilac-panel-left: -1px;
    --lilac-panel-scale: 1.5;
    --scrollbar-track: rgba(var(--pix-ui-edge), 0.12);
    --scrollbar-thumb: rgba(var(--pix-ui-edge), 0.42);
    --scrollbar-thumb-hover: rgba(var(--pix-ui-edge), 0.56);
    --scrollbar-thumb-active: rgba(var(--pix-ui-edge), 0.66);
    /* Canvas-viewport scrollbars only: translucent so they sit over the picture. */
    --scrollbar-thumb-touch: rgba(var(--pix-ui-edge), 0.42);
    --scrollbar-thumb-touch-active: rgba(var(--pix-ui-edge), 0.7);
    --mobile-tools-width: 188px;
    --mobile-palette-height: 0px;
    /* The fixed bottom bar was replaced by the shell tab bar (mobile-home.js), which
       overlays the viewer instead of reserving layout space. Kept at 0 because the
       workspace/viewport height calcs below still subtract it. */
    --bottom-bar-height: 0px;
}

* {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}

*::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

*::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
    border-radius: 999px;
}

*::-webkit-scrollbar-thumb {
    background-color: var(--scrollbar-thumb);
    border-radius: 999px;
    border: 2px solid rgba(255, 255, 255, 0.25);
}

*::-webkit-scrollbar-thumb:hover {
    background-color: var(--scrollbar-thumb-hover);
}

*::-webkit-scrollbar-thumb:active {
    background-color: var(--scrollbar-thumb-active);
}

/* The canvas viewport's scrollbars are a REAL control on a phone — the second way to move
   around the picture, after two fingers — so they get a finger-sized target instead of the
   10px bar a mouse is happy with. The transparent border + background-clip trick keeps the
   grabbable area 18px wide while the visible thumb stays a slim translucent pill, so it reads
   as an overlay on the picture rather than a frame around it. The track is invisible.
   Unconditional: mobile.css is only ever loaded by mobile.html, which is the touch page. */
.canvas-viewport {
    scrollbar-width: auto;
    scrollbar-color: var(--scrollbar-thumb-touch) transparent;
}

.canvas-viewport::-webkit-scrollbar {
    width: 18px;
    height: 18px;
}

.canvas-viewport::-webkit-scrollbar-track {
    background: transparent;
    border-radius: 0;
}

.canvas-viewport::-webkit-scrollbar-thumb {
    background-color: var(--scrollbar-thumb-touch);
    border: 5px solid transparent;
    background-clip: padding-box;
    border-radius: 999px;
}

.canvas-viewport::-webkit-scrollbar-thumb:active {
    background-color: var(--scrollbar-thumb-touch-active);
}

.canvas-viewport::-webkit-scrollbar-corner {
    background: transparent;
}

/* ── First-visit tour (mobile-tour.js) ────────────────────────────────────────────────
   The dimming is four fixed panels laid around the spotlit control, not a clip-path: the
   gap between them is a real hole in the hit-testing, so "clicking elsewhere does nothing"
   is structural rather than a pointer-events trick that leaks on some engines. */
.mtour-bubble {
    position: fixed;
    width: min(92vw, 340px);
    box-sizing: border-box;
    padding: 14px 16px 12px;
    background: #fffcee;
    border: 1px solid rgba(var(--pix-ui-edge), 0.6);
    border-radius: 14px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
    text-align: left;
    font: 14px/1.5 system-ui, sans-serif;
    color: var(--pix-ui-ink);
}

.mtour-title {
    display: block;
    font-size: 16px;
    margin-bottom: 6px;
    color: #3e2159;
}

.mtour-body {
    margin: 0 0 12px;
    font-size: 13.5px;
    /* The welcome copy carries a real line break; keep it without resorting to innerHTML. */
    white-space: pre-line;
}

.mtour-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

.mtour-btn {
    min-height: 40px;
    padding: 8px 16px;
    border-radius: 999px;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    touch-action: manipulation;
}

.mtour-btn--ghost {
    background: transparent;
    border: 1px solid rgba(60, 70, 10, 0.35);
    color: #4a4a3a;
}

.mtour-btn--primary {
    margin-left: auto;
    background: linear-gradient(135deg, #8f6fd6, #7d5fc4);
    border: 1px solid transparent;
    color: #fff;
}

.mtour-hint {
    margin-left: auto;
    font-size: 12px;
    font-weight: 600;
    color: #6b5a85;
    text-align: right;
}

.mtour-progress {
    margin-top: 8px;
    font-size: 11px;
    color: #8a8570;
}

/* The "How do I…?" question index. Above the shell (1500), below the spotlight overlay
   (2100): picking a question closes this and runs the answer, then it reopens. */
.mfaq-sheet {
    position: fixed;
    inset: 0;
    z-index: 1850;
    display: flex;
    flex-direction: column;
    background: #fffcee;
    font: 14px/1.5 system-ui, sans-serif;
    color: var(--pix-ui-ink);
}

.mfaq-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 14px 10px;
    border-bottom: 1px solid rgba(var(--pix-ui-edge), 0.4);
    font-size: 17px;
}

.mfaq-close {
    margin-left: auto;
    width: 40px;
    height: 40px;
    border: 1px solid rgba(60, 70, 10, 0.3);
    border-radius: 999px;
    background: #fff;
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    touch-action: manipulation;
}

.mfaq-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 8px 12px calc(20px + env(safe-area-inset-bottom, 0px));
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* The written guide (a PDF), first thing in the sheet. A LINK, not a question, so it is a
   filled lilac banner rather than one of the white pills: everything below teaches one tap,
   this hands over the whole book. Built only in a language that has an edition written (see
   PixlaineGuide in shared-utils.js), so most readers never see this rule. */
.mfaq-guide {
    display: flex;
    align-items: center;
    gap: 11px;
    margin-bottom: 4px;
    padding: 11px 13px;
    border: 1px solid rgba(166, 103, 228, 0.55);
    border-left: 4px solid var(--pix-ui-accent);
    border-radius: 12px;
    background: linear-gradient(135deg, #f7ecff 0%, #efe0fb 100%);
    color: var(--pix-ui-ink);
    text-decoration: none;
    touch-action: manipulation;
}

.mfaq-guide:active {
    background: #efe0fb;
}

/* On its own cream tile: the logo is dark line work and would sink into the lilac. */
.mfaq-guide-icon {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    object-fit: contain;
    background: #fffdf6;
    border: 1px solid rgba(var(--pix-ui-edge), 0.28);
    border-radius: 10px;
    padding: 5px;
}

.mfaq-guide-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.mfaq-guide-text strong {
    font-size: 15px;
    color: #5a3480;
}

.mfaq-guide-text span {
    font-size: 12px;
    line-height: 1.3;
    color: #5c5a48;
}

.mfaq-section-title {
    margin-top: 12px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--pix-ui-accent);
}

/* display:flex, not the button default: a question long enough to wrap onto two lines
   was sitting low in its pill, because a button's own vertical centring works off the
   first line box rather than the wrapped block. Flex centres the whole text. */
.mfaq-q {
    display: flex;
    align-items: center;
    min-height: 48px;
    padding: 11px 14px;
    text-align: left;
    border: 1px solid rgba(var(--pix-ui-edge), 0.45);
    border-radius: 12px;
    background: #fff;
    color: var(--pix-ui-ink);
    font: inherit;
    cursor: pointer;
    touch-action: manipulation;
}

.mfaq-q:active {
    background: #f4f0dd;
}

/* No z-index lifting of the spotlit controls: the panels already paint OVER everything at
   1900, and the gap between them has nothing in it, so both the pixels and the taps reach the
   real button underneath. Raising the tab bar above the overlay would expose all three tabs
   and defeat the point of an action step. */

.bg-band {
    position: fixed;
    left: 0;
    width: 100vw;
    z-index: 0;
    pointer-events: none;
    background-repeat: repeat-x;
    background-position: left top;
}

.bg-brown {
    top: 0;
    height: var(--brown-band-height);
    background-image: var(--pix-bg-brown);
    background-size: auto var(--brown-band-height);
    image-rendering: pixelated;
    z-index: 26;
    pointer-events: none;
}

.bg-yellow {
    top: var(--yellow-vines-top);
    left: var(--yellow-vines-left);
    width: calc(100vw - var(--yellow-vines-left));
    height: var(--yellow-vines-height);
    background-image: var(--pix-bg-vines);
    background-size: auto var(--yellow-vines-height);
    background-repeat: repeat-x;
    background-position: left top;
    image-rendering: pixelated;
    z-index: 0;
}

.workspace,
.modal-backdrop {
    position: relative;
    z-index: 10;
}

.toolbar {
    position: fixed;
    top: 5px;
    left: 0;
    right: 0;
    min-height: 44px;
    background-color: transparent;
    border-bottom: none;
    margin-top: 0;
    margin-bottom: 20px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: 4px 8px 5px;
    z-index: 40;
}

.toolbar-row > button,
.toolbar .toolbar-icon-button,
.toolbar .file-menu > button,
.toolbar .reference-menu > button,
.toolbar .chart-menu > button {
    padding: 5px 10px;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    transform: translateY(-4px);
    color: var(--pix-ui-ink);
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.88) 0%, rgba(var(--pix-ui-face-2), 0.78) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.8);
    background-clip: padding-box;
    border-radius: 10px;
    box-shadow: none;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
    backdrop-filter: blur(1.5px);
    -webkit-backdrop-filter: blur(1.5px);
    transition: background 140ms ease, border-color 140ms ease, box-shadow 140ms ease, transform 120ms ease, color 140ms ease;
}

.toolbar-row > button:hover,
.toolbar .toolbar-icon-button:hover,
.toolbar .file-menu > button:hover,
.toolbar .reference-menu > button:hover,
.toolbar .chart-menu > button:hover {
    color: #1f1e18;
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.96) 0%, rgba(var(--pix-ui-face-2), 0.9) 100%);
    border-color: rgba(var(--pix-ui-edge), 0.98);
    box-shadow: none;
    transform: translateY(-5px);
}

.toolbar-row > button:focus-visible,
.toolbar .toolbar-icon-button:focus-visible,
.toolbar .file-menu > button:focus-visible,
.toolbar .reference-menu > button:focus-visible,
.toolbar .chart-menu > button:focus-visible {
    outline: 2px solid rgba(166, 103, 228, 0.65);
    outline-offset: 1px;
}

.toolbar-row > button:active,
.toolbar .toolbar-icon-button:active,
.toolbar .file-menu > button:active,
.toolbar .reference-menu > button:active,
.toolbar .chart-menu > button:active {
    border-color: rgba(var(--pix-ui-edge), 0.92);
    transform: translateY(-3px);
    box-shadow: none;
}

.toolbar-row > button:disabled,
.toolbar .toolbar-icon-button:disabled,
.toolbar .file-menu > button:disabled,
.toolbar .reference-menu > button:disabled,
.toolbar .chart-menu > button:disabled {
    opacity: 0.38;
    filter: saturate(0.3);
    pointer-events: none;
}

/* The viewer's primary action: reading the row-by-row pattern is what most visitors came
   for, so it is the one lilac button in an otherwise uniform olive bar. */
.toolbar-row > button.viewer-pattern-button {
    color: #3e2159;
    background: linear-gradient(180deg, rgba(247, 240, 255, 0.95) 0%, rgba(230, 214, 249, 0.9) 100%);
    border-color: rgba(150, 106, 205, 0.85);
}

.toolbar-row > button.viewer-pattern-button:hover {
    color: #2f1745;
    background: linear-gradient(180deg, rgba(251, 247, 255, 0.98) 0%, rgba(238, 226, 252, 0.95) 100%);
    border-color: rgba(150, 106, 205, 1);
}

.toolbar-row > button.viewer-pattern-button:active {
    border-color: rgba(150, 106, 205, 0.95);
}

.toolbar .toolbar-icon-button {
    width: 44px;
    min-width: 44px;
    height: 44px;
    min-height: 44px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    font-family: 'Segoe UI Symbol', 'Noto Sans Symbols 2', Arial, sans-serif;
    font-size: 1.35rem;
    font-weight: 600;
    line-height: 1;
    transform: none;
}

.toolbar .toolbar-icon-button:hover,
.toolbar .toolbar-icon-button:active,
.toolbar .toolbar-icon-button:focus-visible {
    transform: none;
}

.toolbar a.toolbar-icon-button {
    text-decoration: none;
    color: inherit;
}

#donateButton svg,
#instagramButton svg,
#kofiButton svg {
    display: block;
    fill: currentColor;
}

#openTutorial {
    margin-left: auto;
}

#undoButton {
    margin-left: auto;
}

/* Undo/redo step-count badge — shows how many steps remain. Large projects cap
   history well below MAX_HISTORY, so this keeps the user aware of the limit. */
#undoButton,
#redoButton {
    position: relative;
    overflow: visible;
}

#undoButton::after,
#redoButton::after {
    content: attr(data-count);
    position: absolute;
    top: 0;
    right: 0;
    min-width: 17px;
    height: 17px;
    padding: 0 3px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: Arial, sans-serif;
    font-size: 0.62rem;
    font-weight: 700;
    line-height: 1;
    color: #fffdf2;
    background: var(--pix-ui-accent);
    border: 1px solid rgba(255, 255, 255, 0.6);
    border-radius: 999px;
    pointer-events: none;
}

#undoButton:not([data-count])::after,
#redoButton:not([data-count])::after,
#undoButton[data-count="0"]::after,
#redoButton[data-count="0"]::after {
    display: none;
}

.toolbar-row {
    display: flex;
    align-items: center;
    gap: 5px;
    flex-wrap: nowrap;
}

.ref-short {
    display: none;
}

@media (max-width: 400px) {
    .ref-full {
        display: none;
    }
    .ref-short {
        display: inline;
    }
}

.file-menu {
    position: relative;
}

.reference-menu {
    position: relative;
}

.chart-menu {
    position: relative;
}

.dropdown {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1000;
    margin-top: 6px;
    background: rgba(var(--pix-ui-face), 0.97);
    border: 1px solid rgba(var(--pix-ui-edge), 0.55);
    border-radius: 10px;
    min-width: 140px;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
    overflow: hidden;
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}

.dropdown button {
    display: block;
    width: 100%;
    border: none;
    background: transparent;
    color: var(--pix-ui-ink);
    padding: 9px 12px;
    text-align: left;
    cursor: pointer;
}

.dropdown button:hover {
    background-color: rgba(166, 103, 228, 0.14);
}

canvas {
    border: 1px solid #000;
    cursor: default;
    display: block;
    margin: 0 auto;
    image-rendering: pixelated;
}

.canvas-viewport {
    width: min(760px, calc(100vw - 28px));
    height: min(760px, calc(100vh - var(--canvas-top-offset) - var(--bottom-bar-height) - 24px));
    height: min(760px, calc(100svh - var(--canvas-top-offset) - var(--bottom-bar-height) - env(safe-area-inset-bottom, 0px) - 24px));
    max-width: 100%;
    max-height: calc(100vh - var(--canvas-top-offset) - var(--bottom-bar-height) - 24px);
    max-height: calc(100svh - var(--canvas-top-offset) - var(--bottom-bar-height) - env(safe-area-inset-bottom, 0px) - 24px);
    overflow: auto;
    touch-action: none;
    background: transparent;
    line-height: 0;
    flex: 0 0 auto;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-left: auto;
    margin-right: auto;
}

/* ── Drawing on a phone: keep the PAGE still ───────────────────────────────────────────
   The browser's own gestures are the enemy here. touch-action:none on .canvas-viewport
   above already refuses scroll/pinch/double-tap-zoom on the canvas; these add the rest, and
   mobile-draw.js refuses Safari's non-standard gesture* events in JS (CSS cannot).
   overscroll-behavior stops a drag that reaches the edge turning into pull-to-refresh or a
   rubber-band; the user-select/touch-callout pair stops a long press selecting the canvas or
   opening the image menu mid-stroke. */
.canvas-viewport,
#canvas {
    overscroll-behavior: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
}

/* The cell the finger is over. position:fixed, so viewport scrolling needs no bookkeeping. */
.md-ring {
    position: fixed;
    z-index: 1250;
    display: none;
    pointer-events: none;
    box-sizing: border-box;
    border: 2px solid #ffffff;
    outline: 2px solid rgba(0, 0, 0, 0.75);
    border-radius: 2px;
}

/* The drawing bar: bottom of the screen, in thumb reach. z-index sits above the canvas but
   below the shell (1500) and every modal (2000), so an opened dialog covers it. */
.md-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 8px 8px calc(8px + env(safe-area-inset-bottom, 0px));
    background: rgba(255, 253, 244, 0.97);
    border-top: 1px solid rgba(var(--pix-ui-edge), 0.55);
    box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.12);
    -webkit-user-select: none;
    user-select: none;
    touch-action: manipulation; /* fast taps, and no double-tap page zoom on the controls */
}

.md-bar[hidden] {
    display: none;
}

.md-swatches {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    padding-bottom: 2px;
    scrollbar-width: none;
    touch-action: pan-x; /* the strip scrolls sideways; nothing else, no page zoom */
    overscroll-behavior-x: contain;
}

.md-swatches::-webkit-scrollbar {
    display: none;
}

.md-swatch {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    padding: 0;
    border: 2px solid rgba(0, 0, 0, 0.28);
    border-radius: 10px;
    cursor: pointer;
    background-clip: padding-box;
}

.md-swatch--on {
    border-color: #4a2570;
    box-shadow: 0 0 0 2px #ffffff inset, 0 0 0 3px #4a2570;
}

.md-swatch--more {
    background: #ffffff;
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1;
    color: #4a2570;
}

/* The native colour input must stay in the DOM to be clickable, but never shown. */
.md-color-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.md-tools {
    display: flex;
    gap: 6px;
}

.md-tool {
    flex: 1 1 0;
    min-width: 0;
    min-height: 48px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 4px 2px;
    border: 1px solid rgba(var(--pix-ui-edge), 0.55);
    border-radius: 10px;
    background: #ffffff;
    color: var(--pix-ui-ink);
    font: inherit;
    cursor: pointer;
}

.md-tool:disabled {
    opacity: 0.35;
}

.md-tool--on {
    background: #efe4fb;
    border-color: #9354d1;
    color: #3e2159;
    font-weight: 700;
}

.md-tool--done {
    background: #eaf3e2;
    border-color: var(--pix-ui-accent);
}

/* Tool icons are the app's own drawn art, the same files the desktop tool band uses — but
   on a plain button, not on a lilac flower. A fixed-height box keeps four very different
   drawings on one optical baseline; object-fit:contain does the rest. */
.md-tool-icon {
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    line-height: 1;
}

.md-tool-icon img {
    max-width: 30px;
    max-height: 28px;
    object-fit: contain;
    image-rendering: pixelated;
}


.md-tool-label {
    font-size: 0.62rem;
    line-height: 1.1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* Draw mode owns the bottom of the screen: give the canvas room so the bar never covers the
   rows being worked on. The viewport height formula subtracts --bottom-bar-height. */
body.md-drawing {
    --bottom-bar-height: 132px;
}

.viewer-draw-button.active {
    background: linear-gradient(180deg, rgba(247, 240, 255, 0.95) 0%, rgba(230, 214, 249, 0.9) 100%);
    border-color: rgba(150, 106, 205, 0.9);
    color: #3e2159;
    font-weight: 700;
}

.canvas-column {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    margin-top: 0;
    max-width: 100%;
    position: absolute;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    isolation: isolate;
}

.canvas-frame {
    --canvas-pan-gutter-x: clamp(56px, 12vw, 112px);
    --canvas-pan-gutter-y: clamp(56px, 12vw, 112px);
    display: block;
    width: max-content;
    position: relative;
    background: transparent;
    padding: var(--canvas-pan-gutter-y) var(--canvas-pan-gutter-x);
}

.canvas-frame::after {
    content: '';
    position: absolute;
    inset: var(--canvas-pan-gutter-y) var(--canvas-pan-gutter-x);
    border: 1px solid #000;
    box-sizing: border-box;
    pointer-events: none;
}

/* The canvas only gets its real CSS size from JS (updatePixelSizes): until then the
   900x900 default renders, then shrinks and recentres - a pure layout shift (CLS),
   counted even while the tab shell covers the viewer. Keep the frame invisible until
   mobile-init.js marks the body ready; hidden elements have no previous paint
   position, so the resize never counts as a shift. */
body:not(.pix-canvas-ready) .canvas-frame {
    visibility: hidden;
}

#canvas {
    border: none;
    margin: 0;
    transform-origin: top left;
}

#canvas.is-panning {
    cursor: grabbing;
}

.workspace {
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
    gap: 0;
    flex-wrap: nowrap;
    width: 100%;
    box-sizing: border-box;
    position: relative;
    height: calc(100vh - var(--canvas-top-offset) - var(--bottom-bar-height));
    height: calc(100svh - var(--canvas-top-offset) - var(--bottom-bar-height) - env(safe-area-inset-bottom, 0px));
    min-height: 0;
    overflow: hidden;
}

/* ── Viewer top bar (the old toolbar slot) ─────────────────────────────────────
   Shown only while the full-screen grid viewer is open; the rest of the time the
   shell (mobile-home.js tab bar + pages) covers the screen and the bar hides so
   its ResizeObserver measurement can't leak a stale --canvas-top-offset. */
body:not(.viewer-open) #viewerTopBar {
    display: none;
}

#toggleGridLines.active {
    background: var(--pix-ui-on);
    color: var(--pix-ui-on-ink);
    border-color: var(--pix-ui-on);
    text-shadow: none;
}

/* Cross-stitch layer: shown only for a pattern that has one (mobile-events.js), and pressed
   while the embroidery is visible. Same thread pink as the desktop layer buttons. */
#toggleCrossLayer.active {
    background: #a83b6b;
    color: #fff3f8;
    border-color: #a83b6b;
    text-shadow: none;
}

/* Two deliberate lines: view controls (Home, Grid, Cross stitch, Save PNG) on top,
   the two picture actions (Create Pattern, Draw) centred underneath. Both may still wrap
   on a very narrow phone rather than clipping; updateToolbarOffset() re-measures the bar
   height, so --canvas-top-offset follows however many lines it ends up with. */
#viewerTopBar .toolbar-row--1,
#viewerTopBar .toolbar-row--2 {
    flex-wrap: wrap;
}

/* Centred and roomier than row 1 (which keeps Home at the left edge, where a way out
   belongs): these two are the actions people came to the viewer for. */
#viewerTopBar .toolbar-row--2 {
    justify-content: center;
    gap: 10px;
}

/* .viewer-status-row / .mobile-status-info removed with the viewer's size+position readout. */

/* Floating fallback launchers from the shared feature layers: with the old toolbar
   anchor (#openTutorial) gone they would float over the shell at z 4000. The shell's
   tabs replace both (Profile tab = account, Home tab = the library), so hide them
   (important beats the inline styles those files set). */
#openAccount,
#openMyProjects {
    display: none !important;
}

.mobile-preview-hidden {
    position: fixed;
    left: -9999px;
    top: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.color-palette {
    width: 100%;
    display: grid;
    grid-template-columns: repeat(8, 30px);
    grid-auto-rows: 30px;
    justify-content: center;
    gap: 6px;
    max-height: min(55vh, 300px);
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-gutter: stable;
    padding: 10px 12px;
    box-sizing: border-box;
}

.color-palette[hidden] {
    display: none;
}

.palette-swatch {
    width: 30px;
    height: 30px;
    aspect-ratio: auto;
    box-sizing: border-box;
    border: 1px solid #444;
    border-radius: 4px;
    cursor: pointer;
    padding: 0;
    transition: transform 120ms ease, box-shadow 120ms ease;
}

.palette-swatch.selected {
    transform: translateY(-1px);
    box-shadow: 0 0 0 1px #ffffff, 0 0 0 3px #111111, 0 2px 6px rgba(0, 0, 0, 0.25);
}

/* Hold-to-remove: swatch shrinks over 1s while pressed in My Palette. */
.palette-swatch.removing {
    animation: paletteSwatchRemoving 1s linear forwards;
}

@keyframes paletteSwatchRemoving {
    to {
        transform: scale(0.3);
        opacity: 0.35;
    }
}

.palette-swatch.add-color-btn {
    position: relative;
    overflow: hidden;
    background-color: #f0f0f0;
    color: #444;
    font-size: 1.3rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border-style: dashed;
}

.add-color-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    border: 0;
    margin: 0;
    padding: 0;
}

.stitch-area-button {
    width: 100%;
    box-sizing: border-box;
    padding: 8px 10px;
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.9);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 10px;
    cursor: pointer;
}

.stitch-area-button[aria-pressed="true"] {
    background: var(--pix-ui-accent);
    color: #ffffff;
    border-color: var(--pix-ui-accent-dark);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
}

.stitch-area-button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* Floating colour-area inspector: blurs/dims the page behind a sharp card. */
.stitch-area-overlay {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 18px;
    background: rgba(24, 18, 32, 0.34);
    -webkit-backdrop-filter: blur(7px);
    backdrop-filter: blur(7px);
}

.stitch-area-overlay[hidden] {
    display: none;
}

.stitch-area-card {
    display: flex;
    align-items: center;
    gap: 16px;
    max-width: 92vw;
    max-height: 86vh;
    padding: 16px 18px;
    background: #ffffff;
    border-radius: 16px;
    box-shadow: 0 22px 60px rgba(0, 0, 0, 0.45);
}

.stitch-area-canvas-wrap {
    max-width: 55vw;
    max-height: 70vh;
    overflow: auto;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    background: #ffffff;
    -webkit-overflow-scrolling: touch;
}

.stitch-area-canvas {
    display: block;
    image-rendering: pixelated;
    background: #ffffff;
}

.stitch-area-zoom {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 2px;
}

.stitch-area-zoom-btn {
    width: 36px;
    height: 36px;
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1;
    color: var(--pix-ui-ink);
    background: #f0f0f0;
    border: 1px solid #cfcfcf;
    border-radius: 6px;
    cursor: pointer;
}

.stitch-area-zoom-label {
    min-width: 48px;
    text-align: center;
    font-size: 0.9rem;
    font-weight: 600;
    color: #555;
}

.stitch-area-info {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 96px;
}

.stitch-area-swatch-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.stitch-area-swatch {
    width: 20px;
    height: 20px;
    border-radius: 5px;
    border: 1px solid #ccc;
    box-sizing: border-box;
}

.stitch-area-hex {
    font: 13px/1 monospace;
    color: #555;
}

.stitch-area-stitches {
    font-size: 1.3rem;
    font-weight: 800;
    color: var(--pix-ui-ink);
}

.stitch-area-mass {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--pix-ui-accent-dark);
}

.stitch-area-mass:empty {
    display: none;
}

.stitch-area-toggle {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 4px;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--pix-ui-ink);
    cursor: pointer;
}

.stitch-area-toggle input {
    width: 18px;
    height: 18px;
    cursor: pointer;
}

.stitch-area-hint {
    margin-top: 6px;
    font-size: 0.72rem;
    color: #999;
}

/* "Load Palette": twice as wide as the "+" swatch (spans two grid columns). */
.palette-swatch.load-palette-btn {
    grid-column: span 2;
    width: auto;
    background-color: #f0f0f0;
    color: #444;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.1;
    padding: 0 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    border-style: dashed;
}

.add-color-hex {
    grid-column: 1 / -1;
    height: 34px;
    box-sizing: border-box;
    border: 1px solid #888;
    border-radius: 6px;
    font: 15px/1 monospace;
    text-align: center;
    padding: 0 8px;
}

.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
}

.modal-backdrop[hidden] {
    display: none;
}

.modal {
    width: min(92vw, 340px);
    background: #ffffff;
    border: 1px solid #c8c8c8;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.2);
    border-radius: 8px;
    padding: 16px;
    text-align: left;
}

/* Resize/Pixelise modal: single column on mobile, preview first, then the
   Size and Colours section cards. Doubled class so the width wins over the
   generic .modal rule regardless of source order. */
.modal.resize-pixelise-modal {
    width: min(94vw, 720px);
    min-width: 0; /* flex item on the backdrop: never grow past the set width */
    box-sizing: border-box; /* width includes the .modal padding, so 94vw truly fits */
    max-height: 90vh;
    overflow-y: auto;
}

/* New Project modal: same markup and ids as desktop's, adapted to a phone. It is a
   near-full-width sheet that scrolls, and every control is touch-sized — a 44px
   minimum on the two doors and on the actions row, which sticks to the bottom so
   Create stays reachable with the gauge section expanded and the keyboard up.
   Doubled class so the width beats the generic .modal rule whatever the source order. */
.modal.new-project-modal {
    width: min(94vw, 420px);
    min-width: 0;
    box-sizing: border-box;
    max-height: 88vh;
    overflow-y: auto;
    padding: 14px 14px 0;
}

.new-project-modal .modal-fields {
    gap: 10px;
}

/* Comfortable tap targets: the shared size/gauge inputs are sized for a mouse. */
.new-project-modal .modal-fields input[type="number"] {
    min-height: 40px;
    font-size: 16px; /* < 16px makes iOS Safari zoom the page on focus */
}

.new-project-modal .modal-checkbox {
    min-height: 44px;
    margin-top: 0;
}

.new-project-modal .modal-checkbox input {
    width: 22px;
    height: 22px;
    margin-left: auto;
}

/* Negative side margins so the sticky bar's background covers the modal's own
   padding too — otherwise fields scroll visibly past its edges. */
.new-project-modal .modal-actions {
    position: sticky;
    bottom: 0;
    margin: 14px -14px 0;
    padding: 10px 14px 14px;
    background: #ffffff;
    box-shadow: 0 -6px 10px -8px rgba(0, 0, 0, 0.35);
}

/* Only the phone-specific bits: full-width thumb-sized targets. The face, edge and the
   filled .modal-primary come from the shared modal-button rule further down. */
.new-project-modal .modal-actions button {
    flex: 1 1 0;
    min-height: 44px;
    font-size: 0.95rem;
}

#openPictureFields {
    display: grid;
    gap: 8px;
    margin-top: 2px;
}

#openPictureFields[hidden] {
    display: none;
}

/* The picture door at the top of the modal — the option most visitors want. */
.np-from-picture {
    min-height: 48px;
    padding: 12px;
    font-size: 1rem;
    font-weight: 600;
    color: #fff;
    background: #a667e4;
    border: 1px solid #9354d1;
    border-radius: 10px;
    cursor: pointer;
}

.np-divider {
    margin: 2px 0 0;
    font-size: 0.82rem;
    color: #777;
    text-align: center;
}

.pixelise-intro {
    margin: 0;
    font-size: 0.9rem;
    color: #444;
    line-height: 1.45;
}

.rp-columns {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    margin-top: 12px;
}

.rp-preview-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.rp-controls-col {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.rp-section {
    border: 1px solid #e2e2e2;
    border-radius: 8px;
    padding: 10px 12px 12px;
    background: rgba(255, 253, 246, 0.55);
}

.rp-section-title {
    margin: 0 0 8px;
    font-size: 0.76rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #6d6a58;
    padding-bottom: 4px;
    border-bottom: 1px solid #e4e0cf;
}

.rp-section-body {
    display: grid;
    gap: 8px;
}

.rp-section-body input[type="number"] {
    width: 100%;
    min-width: 0; /* inputs must not impose their intrinsic width on the grid */
    box-sizing: border-box;
    padding: 8px;
}

/* The colour controls kept their standalone-modal margins; the section grid
   already spaces them. */
.rp-section-body .pixelise-controls,
.rp-section-body .pixelise-bw-toggle,
.rp-section-body .pixelise-swap-panel,
.rp-section-body .pixelise-empty {
    margin-top: 0;
}

.rp-section-body .pixelise-swap-toggle {
    margin: 0 auto;
}

/* Collapsible option groups in the Share picture modal (native <details>). The
   summary reuses the .rp-section-title look; a chevron on the right rotates open. */
.ep-section > summary.rp-section-title {
    margin: 0;
    list-style: none;
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    border-bottom: none;
    padding-bottom: 0;
}

.ep-section > summary.rp-section-title::-webkit-details-marker {
    display: none;
}

.ep-section > summary.rp-section-title::after {
    content: "\25B8"; /* right-pointing triangle */
    font-size: 0.85rem;
    line-height: 1;
    opacity: 0.55;
    transition: transform 0.15s ease;
}

.ep-section[open] > summary.rp-section-title {
    margin-bottom: 8px;
    padding-bottom: 4px;
    border-bottom: 1px solid #e4e0cf;
}

.ep-section[open] > summary.rp-section-title::after {
    transform: rotate(90deg);
}

/* The rect-size fields live in this modal on mobile: Width and Height side by
   side. minmax(0, …) lets the tracks shrink below the inputs' intrinsic width. */
.resize-pixelise-modal #rectSizeFields {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto;
    grid-auto-flow: column;
    gap: 2px 10px;
    align-items: end;
}

.resize-pixelise-modal #rectSizeFields[hidden] {
    display: none;
}

.rp-size-readout {
    font-size: 0.95rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: #4a4a4a;
    text-align: center;
}

.rp-field-hint {
    margin: 0;
    font-size: 12px;
    color: #666;
}

/* Share picture (export) modal: single column on a phone — live preview
   first, then the option sections (rp-section chrome reused). Doubled class
   so the width wins over the generic .modal rule. */
.modal.export-picture-modal {
    width: min(94vw, 720px);
    min-width: 0;
    box-sizing: border-box;
    max-height: 90vh;
    overflow-y: auto;
}

.ep-columns {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    margin-top: 12px;
}

.ep-preview-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.ep-preview-box {
    width: 100%;
    height: 240px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #f1eee4;
    border: 1px solid #e2e2e2;
    border-radius: 8px;
    overflow: hidden;
}

.ep-preview-box canvas {
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
}

.ep-size-readout {
    font-size: 0.95rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: #4a4a4a;
    text-align: center;
}

.ep-controls-col {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ep-select {
    max-width: 100%;
    padding: 8px;
    border: 1px solid #c8c8c8;
    border-radius: 6px;
    background: #fff;
    font-size: 0.9rem;
}

.ep-field-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.ep-field-row label {
    font-size: 0.9rem;
    color: #444;
    flex: 0 0 auto;
}

.ep-field-row input[type="text"] {
    flex: 1 1 120px;
    min-width: 0;
    padding: 8px;
    border: 1px solid #c8c8c8;
    border-radius: 6px;
    font-size: 0.9rem;
}

.ep-field-row select {
    flex: 0 1 auto;
    min-width: 0;
}

.ep-logo-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ep-logo-btn {
    padding: 6px 12px;
    background: #fff;
    border: 1px solid #c8c8c8;
    border-radius: 6px;
    font-size: 0.85rem;
    cursor: pointer;
}

.ep-logo-preview {
    width: 32px;
    height: 32px;
    object-fit: contain;
    border: 1px solid #d8d0e6;
    border-radius: 6px;
    background:
        repeating-conic-gradient(#efece4 0% 25%, #faf8f2 0% 50%) 0 0 / 12px 12px;
}

.ep-logo-clear {
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid #c8c8c8;
    border-radius: 50%;
    background: #fff;
    color: #a4444d;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.ep-wm-note {
    margin: 2px 0 0;
    font-size: 0.74rem;
    color: #777;
    line-height: 1.35;
}

.ep-actions {
    margin-top: 12px;
}

/* The historic 1:1 export, promoted to a proper button under the intro so it's
   easy to reach. Full width on a phone; outlined accent style. */
.ep-original-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    margin: 6px 0 0;
    padding: 10px 14px;
    background: #f6f0ff;
    border: 1px solid #a667e4;
    border-radius: 999px;
    color: #5a3c86;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
}

.ep-original-btn::before {
    content: "⬇";
    font-size: 0.9em;
}

/* Import pixel art modal (Open Picture): single column on a phone — source
   pane with crop handles first, then counts + the rebuilt preview. Doubled
   class so the width wins over the generic .modal rule. */
.modal.pixel-art-import-modal {
    width: min(94vw, 720px);
    min-width: 0;
    box-sizing: border-box;
    max-height: 90vh;
    overflow-y: auto;
}

.pai-columns {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    margin-top: 12px;
}

.pai-source-col,
.pai-result-col {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.pai-source-canvas {
    width: 100%;
    height: min(46vh, 320px);
    border: 1px solid #e2e2e2;
    border-radius: 8px;
    background: #f5f3ec;
    touch-action: none; /* the crop handles own every pointer gesture */
}

.pai-zoom-controls {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

.pai-zoom-btn {
    width: 44px;
    height: 40px;
    padding: 0;
    border: 1px solid #d7cfe4;
    border-radius: 8px;
    background: #f6f2fb;
    color: #4a3d66;
    font-size: 24px;
    font-weight: 600;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
}

.pai-zoom-btn:disabled {
    opacity: 0.4;
}

.pai-crop-actions {
    display: flex;
    gap: 8px;
    justify-content: center;
    flex-wrap: wrap;
}

/* One line where it fits: the Gauge button belongs beside the size, so the inputs
   give up width first. It may still WRAP to a line of its own - the French label
   ("Echantillon") is twice the English one, and squeezing the counts to their
   44px minimum to keep it up there was the cramped result. */
.pai-count-fields {
    display: flex;
    align-items: center;
    gap: 6px;
    row-gap: 8px;
    flex-wrap: wrap;
}

.pai-count-fields label {
    font-size: 0.85rem;
    color: #555;
    white-space: nowrap;
}

.pai-count-fields input[type="number"] {
    width: auto;
    flex: 1 1 72px;
    min-width: 44px;
    box-sizing: border-box;
    padding: 8px;
}

/* The two count rows (stitches x rows / rings + magic circle) take turns, and the
   display:flex above beats the UA sheet's [hidden] rule - so the hidden one needs this. */
.pai-count-fields[hidden] {
    display: none;
}

.pai-count-x {
    color: #888;
}

.pai-status {
    font-size: 0.85rem;
    font-weight: 600;
    color: #3c7d46;
    text-align: center;
}

.pai-status.pai-status-none {
    color: #a15f22;
    font-weight: 500;
}

.pai-result-canvas {
    width: 100%;
    height: min(34vh, 240px);
    border: 1px solid #e2e2e2;
    border-radius: 8px;
    background: #ffffff;
    image-rendering: pixelated;
}

.pai-result-info {
    font-size: 0.92rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: #4a4a4a;
    text-align: center;
}

.pai-note {
    margin: 0;
    font-size: 12px;
    color: #666;
    line-height: 1.45;
}

.pai-colors-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* [hidden] alone loses to the display:flex above (same specificity, author wins),
   so the Colours/Threshold swap needs this attribute-qualified rule to hide. */
.pai-colors-row[hidden] {
    display: none;
}

.pai-colors-row label {
    font-size: 0.8rem;
    color: #555;
}

.pai-colors-row input[type="range"] {
    flex: 1 1 auto;
    min-width: 0;
}

.pai-colors-row .pixelise-count {
    min-width: 2.4em;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
    color: #4a4a4a;
}

.pai-palette {
    justify-content: center;
}

.pai-palette[hidden] {
    display: none;
}

.pixelise-controls {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
}

.pixelise-controls[hidden] {
    display: none;
}

.pixelise-controls label {
    font-weight: 600;
    font-size: 0.9rem;
}

.pixelise-controls input[type="range"] {
    flex: 1 1 auto;
    min-width: 0;
}

.pixelise-count {
    min-width: 3.5ch;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.pixelise-bw-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    cursor: pointer;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--pix-ui-ink);
}

.pixelise-bw-toggle input {
    width: auto;
    margin: 0;
    flex-shrink: 0;
}

/* Swap colours: a toggle under the preview opens a two-section panel. */
.pixelise-swap-toggle {
    display: block;
    margin: 12px auto 0;
    padding: 9px 18px;
    font-size: 0.86rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.92);
    border: 1px solid rgba(var(--pix-ui-edge), 0.7);
    border-radius: 999px;
    cursor: pointer;
}

.pixelise-swap-toggle[hidden] {
    display: none;
}

.pixelise-swap-toggle[aria-expanded="true"] {
    background: rgba(225, 230, 180, 0.95);
}

.pixelise-swap-panel {
    display: flex;
    align-items: stretch;
    justify-content: center;
    gap: 8px;
    margin-top: 10px;
    width: 100%;
}

.pixelise-swap-panel[hidden] {
    display: none;
}

.pixelise-swap-col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.pixelise-swap-coltitle {
    font-size: 0.74rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #555;
    text-align: center;
}

.pixelise-swap-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, 28px);
    grid-auto-rows: 28px;
    gap: 6px;
    justify-content: center;
    align-content: start;
    max-height: 168px;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 8px;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
    box-sizing: border-box;
}

.pixelise-swap-grid .palette-swatch {
    width: 28px;
    height: 28px;
}

.pixelise-swap-grid .palette-swatch.add-color-btn {
    font-size: 1.2rem;
}

.pixelise-swap-mid {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}

.pixelise-swap-btn {
    font-size: 1.2rem;
    line-height: 1;
    padding: 7px 11px;
    background: rgba(var(--pix-ui-face), 0.84);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 999px;
    cursor: pointer;
}

.pixelise-swap-btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* The colour input is opened by the "+" swatch; the control itself stays hidden. */
.pixelise-swap-picker {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    opacity: 0;
    pointer-events: none;
}

.pixelise-preview-canvas {
    width: 240px;
    max-width: 100%;
    aspect-ratio: 1 / 1;
    height: auto;
    background: #ffffff;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
}

.pixelise-palette-wrap {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.pixelise-palette-wrap[hidden] {
    display: none;
}

.pixelise-palette-hint {
    font-size: 0.78rem;
    color: #555;
    text-align: center;
    line-height: 1.3;
}

.pixelise-palette {
    width: 100%;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
}

#pixelisePalette {
    grid-template-columns: repeat(auto-fit, 30px);
}

.pixelise-empty {
    margin: 10px 0 0;
    font-size: 0.9rem;
    font-weight: 600;
    color: #8a6d1b;
    text-align: center;
}

.modal h2 {
    margin: 0 0 12px;
    font-size: 1.2rem;
}

.modal-fields {
    display: grid;
    gap: 8px;
}

.modal-checkbox {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
    margin-top: 4px;
    cursor: pointer;
}

.modal-checkbox input {
    width: auto;
    flex-shrink: 0;
}

#newProjectRatioFields {
    display: grid;
    gap: 8px;
    margin-top: 4px;
}

/* Standard mode: hook interval + ball band figures. */
#gaugeLabelFields {
    display: grid;
    gap: 6px;
}

.gauge-hint {
    margin: 0;
    font-size: 0.8em;
    color: #777;
}

/* Measured swatch + stitch/row count: the pixel ratio. */
#gaugeSwatchFields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px 16px;
}

.swatch-gauge-col {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 4px 8px;
}

.swatch-gauge-col-header {
    grid-column: 1 / -1;
    font-weight: 600;
    font-size: 0.85em;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #555;
    padding-bottom: 2px;
    border-bottom: 1px solid #ddd;
    margin-bottom: 2px;
}

.swatch-gauge-col label {
    white-space: nowrap;
}

.swatch-gauge-col input {
    width: 100%;
    min-width: 0;
}

.gauge-summary {
    margin: 6px 0 0;
    font-size: 0.82em;
    color: #555;
    white-space: pre-line;
}

#newProjectRatioFields[hidden] {
    display: none;
}

#resizeFields {
    display: grid;
    gap: 8px;
    margin-top: 6px;
}

#resizeFields[hidden] {
    display: none;
}

#rectSizeFields[hidden] {
    display: none;
}

#circularGridFields[hidden] {
    display: none;
}

.modal-fields input {
    width: 100%;
    box-sizing: border-box;
    padding: 6px 8px;
}

.modal-actions {
    margin-top: 14px;
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

.picture-warning-stats {
    margin-top: 8px;
    font-size: 0.85em;
    font-weight: 600;
    color: #8a6d1b;
}

/* Same rounded modal buttons as desktop (style.css) — a light face with a soft edge and one
   filled .modal-primary per row, from the chrome vars so a background design's buttons follow. */
.modal-actions button,
.pai-crop-actions button,
.pai-gauge-btn {
    padding: 8px 16px;
    font: inherit;
    font-size: 0.92rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--pix-ui-ink);
    background: #ffffff;
    border: 1px solid rgba(var(--pix-ui-edge), 0.45);
    border-radius: 8px;
    cursor: pointer;
}

.pai-crop-actions button,
.pai-gauge-btn {
    padding: 6px 12px;
    font-size: 0.85rem;
}

.modal-actions button:disabled,
.pai-crop-actions button:disabled,
.pai-gauge-btn:disabled {
    opacity: 0.55;
    cursor: default;
}

.modal-actions button.modal-primary {
    color: #fffdf2;
    font-weight: 700;
    background: var(--pix-ui-accent);
    border-color: var(--pix-ui-accent-dark);
}

/* Gauge (the shape of one stitch) in the Create-from-a-picture step. The button
   sits with the counts because it is the third thing that decides the grid, and it
   LIGHTS UP (.pai-gauge-on) when a gauge is in force - it used to be a line of text
   under the counts saying so, which crowded the step. Its modal opens ABOVE the
   import step (2100 over 2000) and is a sibling of it, never a child. */
.pai-gauge-btn {
    flex: 0 0 auto;
    margin-left: auto; /* the counts keep the left, the button takes the line's end */
    white-space: nowrap;
}

/* With the state line gone this is the ONLY sign a gauge is set, so it has to read
   as on at a glance: accent ink and a doubled edge (inset shadow, not border-width,
   which would nudge the row). */
.pai-gauge-btn.pai-gauge-on {
    background: rgba(var(--pix-ui-face), 0.9);
    border-color: var(--pix-ui-accent);
    color: var(--pix-ui-accent-dark);
    box-shadow: inset 0 0 0 1px var(--pix-ui-accent);
}


.pai-gauge-backdrop {
    z-index: 2100;
}

.modal.pai-gauge-modal {
    width: min(92vw, 420px);
    box-sizing: border-box;
}

#paiGaugeFields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px 16px;
}

.pai-gauge-error:empty {
    display: none;
}

.pai-gauge-error {
    margin: 6px 0 0;
    font-size: 0.82rem;
    font-weight: 600;
    color: #b3402f;
}

.feedback-modal {
    width: min(92vw, 520px);
    max-height: calc(100vh - 24px);
    overflow-y: auto;
}

.feedback-intro {
    margin: 0 0 10px;
    color: #3a3a3a;
}

.feedback-contact-list {
    list-style: none;
    margin: 0 0 4px;
    padding: 0;
    display: grid;
    gap: 8px;
}

.feedback-contact-list a {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
    border: 1px solid #d7d2e6;
    border-radius: 8px;
    background: #f6f4fc;
    text-decoration: none;
    color: inherit;
}

.feedback-contact-list a:active {
    border-color: #5f2dc7;
    background: #efeafb;
}

.feedback-contact-label {
    font-weight: 700;
    color: #5f2dc7;
}

.feedback-contact-value {
    font-size: 0.9rem;
    color: #3a3a3a;
    word-break: break-word;
}

.site-footer {
    display: none;
}

.mobile-footer-links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px 14px;
    padding: 6px 4px 2px;
    border-top: 1px solid rgba(var(--pix-ui-edge), 0.25);
    margin-top: 2px;
}

.mobile-footer-links a {
    font-size: 0.75rem;
    font-weight: 600;
    color: #5a5840;
    text-decoration: none;
}

.mobile-footer-links a:hover,
.mobile-footer-links a:focus-visible {
    text-decoration: underline;
}

.footer-copy {
    display: block;
    text-align: center;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--pix-ui-ink-soft);
    padding: 2px 4px 4px;
}

.site-footer a {
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--pix-ui-ink);
    text-decoration: none;
}

.site-footer a:hover,
.site-footer a:focus-visible {
    text-decoration: underline;
}

/* ─────────────────────────────────────────────
   TUTORIAL SLIDER
   ───────────────────────────────────────────── */

.tutorial-modal {
    width: min(94vw, 620px);
    max-height: calc(100vh - 24px);
    padding: clamp(12px, 2.4vw, 20px);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.tutorial-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.tutorial-header h2 {
    margin: 0;
    font-size: 1.2rem;
}

.tutorial-step-count {
    font-size: 0.82rem;
    font-weight: 600;
    color: #888;
    white-space: nowrap;
    flex-shrink: 0;
}

/* ── Slide container ── */

.tutorial-slides {
    position: relative;
    overflow-y: auto;
    overflow-x: hidden;
    min-height: 200px;
    padding-right: 2px;
}

.tutorial-slide {
    display: none;
    flex-direction: column;
    gap: 10px;
    animation: tutorialFadeIn 220ms ease;
}

.tutorial-slide.active {
    display: flex;
}

@keyframes tutorialFadeIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ── Image / media placeholder ── */

.tutorial-slide-media {
    width: 100%;
    aspect-ratio: 16 / 7;
    border-radius: 8px;
    background: linear-gradient(135deg, rgba(var(--pix-ui-face-2), 0.60) 0%, rgba(var(--pix-ui-face-2), 0.45) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.25);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
}

.tutorial-slide-media:empty {
    display: none;
}

.tutorial-slide-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
    display: block;
}

/* ── Slide text content ── */

.tutorial-content {
    display: grid;
    gap: 8px;
    color: #2f2f2f;
}

.tutorial-content h3 {
    margin: 0;
    font-size: 1rem;
    color: #1a1a14;
}

.tutorial-content p {
    margin: 0;
    line-height: 1.5;
}

.tutorial-content ul {
    margin: 0;
    padding-left: 18px;
    display: grid;
    gap: 5px;
    line-height: 1.45;
}

.tutorial-subsection {
    display: grid;
    gap: 6px;
    padding: 8px 10px;
    border: 1px solid rgba(var(--pix-ui-edge), 0.2);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.55);
}

.tutorial-subsection-title {
    margin: 0;
    font-size: 0.92rem;
    color: var(--pix-ui-ink);
    letter-spacing: 0.01em;
}

.tutorial-pattern-example {
    display: block;
    padding: 8px;
    border-radius: 8px;
    border: 1px solid rgba(var(--pix-ui-edge), 0.25);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.78) 0%, rgba(243, 248, 226, 0.72) 100%);
}

.tutorial-pattern-screenshot {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 6px;
}

.tutorial-pattern-logo-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 68px;
}

.tutorial-pattern-logo {
    width: 100%;
    max-width: 130px;
    height: auto;
    image-rendering: auto;
}

.tutorial-pattern-arrow {
    font-size: 1.25rem;
    font-weight: 700;
    color: #5f2dc7;
    line-height: 1;
}

/* Interactive recreation of two pattern rows (replaces the old screenshot). Stitch
   boxes and row-end boxes are real buttons; tapping toggles a red ✕, and finishing the
   row strikes the whole run through — mirroring the pattern window. Scrolls sideways on
   narrow screens so the boxes never have to shrink or wrap. */
.tutorial-pattern-demo {
    display: flex;
    flex-direction: column;
    gap: 2px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.tutorial-pattern-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.tutorial-pattern-rowlabel {
    min-width: 52px;
    text-align: right;
    font-size: 0.82rem;
    font-weight: 600;
    color: #2f2f2f;
    flex-shrink: 0;
}

.tutorial-pattern-stitches {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    gap: 4px;
    position: relative;
}

.tutorial-pattern-sbox,
.tutorial-pattern-end {
    height: 24px;
    min-width: 26px;
    padding: 0 5px;
    border: 1px solid #000;
    border-radius: 3px;
    font: 700 13px Arial, sans-serif;
    cursor: pointer;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    user-select: none;
}

.tutorial-pattern-end {
    margin-left: 6px;
    background: #ffffff;
    border-color: rgba(32, 32, 32, 0.6);
}

/* Red ✕ marks a stitch box or the row-end box as done (mirrors .sbox.checked). */
.tutorial-pattern-sbox.is-marked::before,
.tutorial-pattern-sbox.is-marked::after,
.tutorial-pattern-end.is-done::before,
.tutorial-pattern-end.is-done::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 15%;
    width: 70%;
    height: 2.5px;
    background: #d11f1f;
    border-radius: 2px;
    pointer-events: none;
}

.tutorial-pattern-sbox.is-marked::before,
.tutorial-pattern-end.is-done::before { transform: translateY(-50%) rotate(45deg); }
.tutorial-pattern-sbox.is-marked::after,
.tutorial-pattern-end.is-done::after { transform: translateY(-50%) rotate(-45deg); }

/* Finishing the row strikes the whole stitch run through, like the pattern window. */
.tutorial-pattern-row.is-done .tutorial-pattern-stitches::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 3px;
    background: #d11f1f;
    border-radius: 2px;
    transform: translateY(-50%);
    pointer-events: none;
    z-index: 2;
}

/* Decorative per-stitch strip between the two count rows (cells built by JS). */
.tutorial-pattern-mini {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-left: 60px;
    margin: 1px 0;
}

.tutorial-pattern-mini-line {
    display: flex;
    flex-direction: row;
    gap: 1px;
}

.tutorial-pattern-mini-cell {
    width: 9px;
    height: 7px;
    border-radius: 1px;
    flex-shrink: 0;
}

.tutorial-pattern-hint {
    margin: 8px 0 0;
    font-size: 0.82rem;
    font-weight: 600;
    color: #5f2dc7;
}

@media (max-width: 560px) {
    .tutorial-pattern-screenshot {
        max-height: 170px;
        object-fit: contain;
    }
}

.tutorial-swatch-picture {
    display: grid;
    gap: 6px;
    padding: 8px;
    border-radius: 8px;
    border: 1px solid rgba(var(--pix-ui-edge), 0.25);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.78) 0%, rgba(243, 248, 226, 0.72) 100%);
}

.tutorial-swatch-header {
    display: inline-block;
    justify-self: start;
    padding: 2px 8px;
    font-size: 0.74rem;
    font-weight: 700;
    color: var(--pix-ui-ink);
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.92) 0%, rgba(var(--pix-ui-face-2), 0.82) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.8);
    border-radius: 7px;
}

.tutorial-swatch-grid {
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: 10px;
    row-gap: 3px;
    font-size: 0.78rem;
    color: #2e2e2a;
}

.tutorial-swatch-grid span:nth-child(even) {
    font-weight: 700;
}

.tutorial-swatch-result {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--pix-ui-ink);
    padding-top: 4px;
    border-top: 1px solid rgba(var(--pix-ui-edge), 0.22);
}

.tutorial-pixel-tool-item {
    display: grid;
    grid-template-columns: max-content 1fr;
    align-items: start;
    column-gap: 10px;
}

.tutorial-menu-preview {
    display: flex;
    align-items: center;
    justify-content: center;
}

.tutorial-menu-button {
    display: inline-block;
    padding: 3px 8px;
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--pix-ui-ink);
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.92) 0%, rgba(var(--pix-ui-face-2), 0.82) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.8);
    border-radius: 8px;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    white-space: nowrap;
}

/* ── Mini lila tool buttons (tutorial slide 1) ── */

.tutorial-tool-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 4px;
}

.tutorial-tool-item {
    display: flex;
    align-items: center;
    gap: 10px;
    line-height: 1.4;
}

.tut-btn {
    position: relative;
    display: inline-block;
    width: 36px;
    height: 50px;
    background-size: 100% 100%;
    background-repeat: no-repeat;
    flex-shrink: 0;
    image-rendering: pixelated;
}

.tut-btn-img {
    position: absolute;
    left: 50%;
    object-fit: contain;
    image-rendering: pixelated;
    pointer-events: none;
    transform: translateX(-50%);
}

/* ── Navigation row ── */

.tutorial-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px solid rgba(var(--pix-ui-edge), 0.18);
    flex-shrink: 0;
}

.tutorial-nav-arrow {
    padding: 5px 14px;
    font-size: 1.1rem;
    font-weight: 700;
    line-height: 1;
    color: var(--pix-ui-ink);
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.88) 0%, rgba(var(--pix-ui-face-2), 0.78) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.8);
    border-radius: 10px;
    backdrop-filter: blur(1.5px);
    -webkit-backdrop-filter: blur(1.5px);
    cursor: pointer;
    transition: background 140ms ease, transform 120ms ease, opacity 140ms ease;
    min-width: 44px;
    text-align: center;
}

.tutorial-nav-arrow:hover:not(:disabled) {
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.96) 0%, rgba(var(--pix-ui-face-2), 0.9) 100%);
    transform: translateY(-2px);
}

.tutorial-nav-arrow:focus-visible {
    outline: 2px solid rgba(166, 103, 228, 0.65);
    outline-offset: 1px;
}

.tutorial-nav-arrow:active:not(:disabled) {
    transform: translateY(0);
}

.tutorial-nav-arrow:disabled {
    opacity: 0.35;
    cursor: default;
}

/* ── Progress dots ── */

.tutorial-dots {
    display: flex;
    gap: 8px;
    align-items: center;
    justify-content: center;
}

.tutorial-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    padding: 0;
    border: 2px solid rgba(var(--pix-ui-edge), 0.55);
    background: transparent;
    cursor: pointer;
    transition: background 200ms ease, border-color 200ms ease, transform 150ms ease;
    flex-shrink: 0;
}

.tutorial-dot:hover {
    border-color: rgba(var(--pix-ui-edge), 0.9);
    transform: scale(1.2);
}

.tutorial-dot.active {
    background: rgba(var(--pix-ui-edge), 0.85);
    border-color: rgba(var(--pix-ui-edge), 0.85);
    transform: scale(1.15);
}

.tutorial-dot:focus-visible {
    outline: 2px solid rgba(166, 103, 228, 0.65);
    outline-offset: 2px;
}

/* ─────────────────────────────────────────────
   EXAMPLE PATTERNS MODAL
   ───────────────────────────────────────────── */
.free-patterns-modal {
    width: min(92vw, 440px);
    max-height: min(90vh, 640px);
    display: flex;
    flex-direction: column;
}

/* Community gallery (Phase 3): let the tab panel scroll inside the fixed-height modal.
   community.js inline-styles the cards. */
.free-patterns-modal .community-panel {
    max-height: 62vh;
    overflow-y: auto;
    min-height: 0;
}
.free-patterns-modal .community-tabs {
    flex-wrap: wrap;
}

.free-patterns-grid {
    display: grid;
    grid-template-rows: repeat(2, auto);
    grid-auto-flow: column;
    gap: 12px;
    padding: 8px 4px 12px;
    overflow-x: auto;
    overflow-y: hidden;
    flex: 1 1 auto;
    min-height: 0;
}

.free-pattern-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 10px;
    border: 2px solid #e0e0e0;
    border-radius: 6px;
    background: #fafafa;
    cursor: pointer;
    font-size: 12px;
    color: #333;
    transition: border-color 0.15s, background 0.15s;
}

.free-pattern-item:hover {
    border-color: #a0a0a0;
    background: #f0f0f0;
}

.free-pattern-item.selected {
    border-color: #7b5ea7;
    background: #f3eeff;
    box-shadow: 0 0 0 2px #c4aee8;
}

.free-pattern-thumb {
    width: 64px;
    height: 64px;
    object-fit: contain;
    image-rendering: pixelated;
}

/* Wraps a single pattern button so the Instagram badge can be pinned to its corner. */
.free-pattern-cell {
    position: relative;
    display: flex;
}

/* Small Instagram link overlaid on the thumbnail (sibling of the button, not a child). */
.free-pattern-insta {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
    box-sizing: border-box;
    padding: 2px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    background: #ffffff;
    color: #e1306c;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
    z-index: 2;
}

.free-pattern-insta:active {
    color: #c13584;
}

.free-pattern-choose-picture {
    width: 88px;
    min-height: 94px;
    justify-content: center;
    border-style: dashed;
    border-color: #a0a0a0;
    background: #f5f5f5;
    color: #555;
}

.free-pattern-choose-icon {
    font-size: 36px;
    line-height: 1;
}

/* ─────────────────────────────────────────────
   RESPONSIVE LAYOUT
   ───────────────────────────────────────────── */


@media (max-width: 900px) {
    .site-footer {
        right: auto;
        left: 12px;
    }
}

@media (max-width: 760px) {
    :root {
        --mobile-tools-width: 156px;
        --mobile-palette-height: 0px;
    }

    .canvas-viewport {
        width: min(740px, calc(100vw - 14px));
    }

    .color-palette {
        grid-template-columns: repeat(6, 30px);
    }
}
/* ===== Language switcher ===== */
.lang-switcher-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 12px;
    padding: 0 4px;
    font-size: 14px;
}
.lang-switcher {
    flex: 1;
    padding: 5px 10px;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--pix-ui-ink);
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.88) 0%, rgba(var(--pix-ui-face-2), 0.78) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.8);
    background-clip: padding-box;
    border-radius: 10px;
    box-shadow: none;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
    cursor: pointer;
}

/* == Achievements (achievements.js): profile section, modals, congrats toast ============ */
/* Same system as style.css; the toast sits above the bottom tab bar. */
.ach-toast {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: calc(78px + env(safe-area-inset-bottom, 0px));
    z-index: 6500;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 15px 16px;
    background: linear-gradient(135deg, #fffdf6 0%, #f3e6fb 100%);
    border: 2px solid #c9a4e6;
    border-radius: 18px;
    box-shadow: 0 14px 40px rgba(90, 50, 140, 0.35);
    cursor: pointer;
    opacity: 0;
    transform: translateY(16px) scale(0.96);
    transition: opacity 0.25s ease, transform 0.25s ease;
}

.ach-toast.ach-toast-show {
    opacity: 1;
    transform: translateY(0) scale(1);
    animation: ach-toast-pop 0.55s cubic-bezier(0.18, 0.8, 0.3, 1.35) both,
               ach-toast-glow 1.5s ease-out 0.2s 3;
}

@keyframes ach-toast-pop {
    0%   { transform: translateY(16px) scale(0.9); }
    55%  { transform: translateY(-5px) scale(1.03); }
    100% { transform: translateY(0) scale(1); }
}

@keyframes ach-toast-glow {
    0%, 100% { box-shadow: 0 14px 40px rgba(90, 50, 140, 0.35), 0 0 0 0 rgba(157, 120, 214, 0.55); }
    50%      { box-shadow: 0 14px 40px rgba(90, 50, 140, 0.35), 0 0 0 12px rgba(157, 120, 214, 0); }
}

@media (prefers-reduced-motion: reduce) {
    .ach-toast.ach-toast-show { animation: none; }
}

.ach-toast-main { min-width: 0; }
.ach-toast-title { font-weight: 800; font-size: 15px; color: #6b4fae; letter-spacing: 0.2px; }
.ach-toast-title::before { content: "🎉 "; }
.ach-toast-name { font-size: 15px; font-weight: 700; color: #2c2c2c; margin-top: 1px; }
.ach-toast-sub { font-size: 12px; color: #8a7aa0; margin-top: 2px; }

.ach-toast-close {
    flex: 0 0 auto;
    align-self: flex-start;
    border: none;
    background: transparent;
    font-size: 15px;
    color: #999;
    cursor: pointer;
    padding: 4px;
    line-height: 1;
}

/* Above the tab shell (1500) and detail sheet (1900), below the sync modal (6100). */
.modal-backdrop.ach-backdrop { z-index: 6000; }
.modal-backdrop.ach-backdrop-group { z-index: 6050; }

/* Doubled selector so both stylesheets stay order-proof (see CLAUDE.md). */
.modal.achievements-modal {
    width: min(94vw, 560px);
    max-height: 86vh;
    overflow: auto;
    box-sizing: border-box;
}

.ach-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin: 0 0 8px;
}

.ach-modal-title { margin: 0; font-size: 18px; }

.ach-modal-close {
    border: none;
    background: transparent;
    font-size: 16px;
    color: #777;
    cursor: pointer;
    padding: 4px 6px;
    line-height: 1;
}

.ach-section-title { margin: 0 0 2px; font-size: 16px; }
.ach-section-intro { margin: 0 0 10px; font-size: 12px; color: #777; }

.ach-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    margin: 0 0 8px;
    background: #fff;
    border: 1px solid #e4ddf0;
    border-radius: 12px;
    text-align: left;
    font: inherit;
    color: inherit;
}

button.ach-row { cursor: pointer; }
button.ach-row:hover { border-color: #8f6fd6; background: #faf7ff; }
.ach-row.ach-static { cursor: default; }
.ach-row.ach-done { background: #f4fbf2; border-color: #cbe7c8; }

.ach-icon { position: relative; flex: 0 0 auto; line-height: 0; }
.ach-icon.ach-locked > :first-child { filter: grayscale(1); opacity: 0.5; }
.ach-lock { position: absolute; right: -5px; bottom: -5px; font-size: 14px; line-height: 1; }

.ach-main { flex: 1; min-width: 0; }
.ach-title { font-size: 13px; font-weight: 700; color: #333; }
.ach-count { font-weight: 600; color: #8f6fd6; }
.ach-desc { font-size: 12px; color: #666; margin-top: 1px; }
.ach-unlocks { font-size: 11px; font-style: italic; color: #a08cc8; margin-top: 1px; }

.ach-bar {
    height: 8px;
    background: #eee9f6;
    border-radius: 999px;
    overflow: hidden;
    margin-top: 5px;
}

.ach-bar-fill {
    height: 100%;
    background: linear-gradient(90deg, #8f6fd6, #4a90d9);
    border-radius: 999px;
    transition: width 0.3s ease;
}

.ach-row.ach-done .ach-bar-fill { background: linear-gradient(90deg, #6fbf68, #2e7d32); }

.ach-pct { font-size: 11px; color: #777; margin-top: 3px; }

/* ── Yarn meter (yarn-meter.js): card variant, mounted on the Home tab by mobile-home.js ── */
.yarn-meter-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.9) 0%, rgba(var(--pix-ui-face-2), 0.75) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.7);
    border-radius: 12px;
    color: var(--pix-ui-ink);
}

.yarn-meter-card .yarn-meter-label { font-weight: 700; font-size: 14px; }

.yarn-meter-card .yarn-meter-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    font-size: 14px;
}

.yarn-meter-card .yarn-meter-key { opacity: 0.75; }
.yarn-meter-card .yarn-meter-val { font-weight: 700; font-variant-numeric: tabular-nums; }

/* ═══ The minimal look (backgrounds/bg-minimal.js) ═══════════════════════════════════════
   The design ships no art, so both --pix-bg-* band images resolve to `none` and these two
   fixed strips would otherwise be invisible boxes. The DESKTOP half of this look rearranges
   the editor (see the long block at the end of style.css); there is nothing to rearrange
   here - the phone is already a tab shell with no tool band and no side panels - so all it
   does is flatten the two decorative bands into one quiet wash from the chrome, which is
   exactly what the look promises. The geometry (band heights, --canvas-top-offset and the
   pre-paint copy of its formula in mobile.html) is deliberately untouched. */

html[data-pix-layout="minimal"] .bg-brown {
    background-image: none;
    background-color: rgba(var(--pix-ui-edge), 0.13);
    border-bottom: 1px solid rgba(var(--pix-ui-edge), 0.32);
    box-sizing: border-box;
}

/* The vines strip carries no colour of its own here: with the brown band above it already
   marking the top of the page, a second wash under it would only be noise. */
html[data-pix-layout="minimal"] .bg-yellow {
    background-image: none;
}
/* Above the in-page pattern overlay (z 5000). The button that opens this dialog
   lives inside that overlay, so at the shared 2000 it would open behind it. */