html {
    height: 100%;
}

body {
    font-family: Arial, sans-serif;
    text-align: center;
    margin: 0;
    height: 100%;
    min-height: 100vh;
    box-sizing: border-box;
    overflow: hidden;
    background: var(--pix-bg-page);
    padding-top: var(--canvas-top-offset);
}

:root {
    /* Background design (backgrounds.js). These are the SHIPPED values, and the only place
       they are written as literals: choosing a background sets the same custom properties
       inline on <html>, which wins, and clearing the choice removes them so these come back.
       The same var names are used by mobile.css and by both chart files' pattern documents,
       which is what makes a choice carry to the pattern page.
       The files live in the shipped design's own folder, assets/background/vines/, named
       after the slots like every other design's art. */
    --pix-bg-page: #b4def3;
    --pix-bg-brown: url('assets/background/vines/brown.png');
    /* ONE tile, repeated across the band. The shipped art is 960px wide = two curtains side
       by side, so the band shows them in turn; CSS repeats an image edge to edge and cannot
       alternate two of them, which is why this is one file and not two. */
    --pix-bg-vines: url('assets/background/vines/vines.png');
    --pix-bg-lilac: url('assets/background/vines/lilac.png');
    /* The tool BUTTON sprites. The buttons are no longer painted into the lilac band: each
       button box shows one of these sprite images (::before below), and the same image's
       alpha is the mask for the hover tint and the selected fill (::after) — so a design's
       active state always has the design's own shape, with no second PNG. Two alternating
       shapes for the nine tool buttons (a/b), plus the two small undo/redo blooms. */
    --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');
    /* ...and the same nine buttons addressed BY POSITION instead of by shape, which is what a
       design whose buttons are a series rather than a repeated shape ships (button-1.png is
       always the top one). Every tool button below reads its numbered var, so these defaults
       are where the shipped alternation of the two shapes is written down — the same order as
       backgrounds.js's NUMBERED_SHAPES, which resolves a design's art the same way. */
    --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);
    /* The colour a SELECTED tool flower is filled with (.background-lila-button.active).
       A theme value rather than art: see the ::after rule below. */
    --pix-bg-lila-active: #a667e4;
    /* ── The chrome: buttons, sliders, panels and readouts ───────────────────────────────
       The second half of a background design (backgrounds.js BUTTON_STYLES). These are the
       SHIPPED palette — a cream face with an olive edge, taken from the vines art — and the
       only place it is written as literals. A design names the style it wants and the engine
       sets these same vars inline, so the buttons stop clashing with art that is not vines.
       --pix-ui-face / -face-2 / -edge are RGB TRIPLES, not colours: every rule keeps its own
       alpha, so they are used as rgba(var(--pix-ui-face), 0.88) and rgb(var(--pix-ui-edge)).
       Every style is light-faced with dark ink, so these also dress the handful of buttons
       that sit inside white modal cards. */
    --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;
    /* The pressed look (Grid on, an active tab): the face inverts to these two. */
    --pix-ui-on: #2e2c1e;
    --pix-ui-on-ink: #f5f0d8;
    /* The edge at full strength: a filled toggle, a link, a small icon. */
    --pix-ui-accent: #7c8f1d;
    --pix-ui-accent-dark: #55691c;
    /* Set by updateUiScale() (JS). 1 = baseline/unchanged; grows on big screens
       to scale the whole desktop chrome up. Lengths below multiply by it. */
    --ui-scale: 1;
    --brown-band-height: calc(74px * var(--ui-scale));
    --canvas-below-brown: calc(19px * var(--ui-scale));
    --canvas-top-offset: calc(var(--brown-band-height) + var(--canvas-below-brown));
    /* Largest square the canvas may fill: the available viewport minus the side
       panels (tool panel on the left, preview panel on the right) and a little
       breathing room below. Drives both the CSS viewport box and, via
       clientWidth/Height, the JS bitmap sizing in updatePixelSizes(). */
    /* × --ui-scale so the grown side panels still clear the canvas. */
    --canvas-side-reserve: calc(340px * var(--ui-scale));
    --canvas-bottom-reserve: 24px;
    --canvas-max-size: min(
        calc(100vw - var(--canvas-side-reserve) * 2),
        calc(100vh - var(--canvas-top-offset) - var(--canvas-bottom-reserve))
    );
    --yellow-vines-top: calc(56px * var(--ui-scale));
    --yellow-vines-left: calc(209px * var(--ui-scale));
    /* The vines tile is drawn `auto <this>`, so this is what sets its scale: it must
       track the art's own height (960x380) or the drawing squashes. */
    --yellow-vines-height: calc(320px * var(--ui-scale));
    /* Intrinsic lilac image coordinates (its own pixel space) — NOT multiplied
       by --ui-scale; the panel's transform: scale() does the visual growing. */
    --lilac-image-offset-y: 60px;
    --lilac-image-offset-scaled: calc(var(--lilac-image-offset-y) * var(--lilac-panel-scale));
    --lilac-panel-width: 140px;
    --lilac-panel-left: -1px;
    --lilac-panel-scale: calc(1.5 * var(--ui-scale));
    --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);
}

* {
    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);
}

.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;
}

.left-vine-bg {
    position: fixed;
    left: 0;
    top: 0;
    pointer-events: none;
    z-index: 0;
    line-height: 0;
    transform: scale(1.5);
    transform-origin: top left;
}

.left-vine-panel {
    position: fixed;
    left: var(--lilac-panel-left);
    top: calc(var(--yellow-vines-top) - var(--lilac-image-offset-scaled));
    pointer-events: auto;
    z-index: 25;
    line-height: 0;
    transform: scale(var(--lilac-panel-scale));
    transform-origin: top left;
    width: 140px;
    height: calc(476px + var(--lilac-image-offset-y));
    background-image: var(--pix-bg-lilac);
    background-size: 140px 476px;
    background-repeat: no-repeat;
    background-attachment: local;
    background-position: 0 var(--lilac-image-offset-y);
    image-rendering: pixelated;
    max-height: calc((100vh - (var(--yellow-vines-top) - var(--lilac-image-offset-scaled)) - 8px) / var(--lilac-panel-scale));
    overflow-y: auto;
    overflow-x: hidden;
    direction: rtl;
}

.left-vine-panel > * {
    direction: ltr;
}

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

.left-vine-img {
    display: block;
    width: auto;
    height: auto;
    image-rendering: pixelated;
    pointer-events: none;
    user-select: none;
}

/* A tool button is its own little stack, independent of the band behind it:
     ::before   the button's FACE — the design's sprite image (--lila-btn picks which)
     ::after    the hover tint / selected fill, MASKED by that same sprite's alpha, so the
                highlight always has exactly the shape of the face it sits on — for any
                design, with no second PNG. ("lila 1/2 active.png" and the "lila 1/2.png"
                mask files are consequently unreferenced; kept on disk for now.)
   The sprites live in backgrounds land (--pix-bg-btn-*, defaults in :root above), which is
   what lets a background design reshape the buttons: it ships sprite PNGs the size of these
   boxes and backgrounds.js swaps the vars. The band PNG holds only the decor BEHIND the
   buttons — the shipped blooms were split out of it into the sprite files, losslessly. */
.background-lila-button {
    position: absolute;
    width: 50px;
    height: 70px;
    border: none;
    margin: 0;
    padding: 0;
    background: transparent;
    -webkit-appearance: none;
    appearance: none;
    cursor: pointer;
    pointer-events: auto;
    isolation: isolate;
    --lila-btn: var(--pix-bg-btn-a);
    /* Where an icon sits ON this button: the middle of the design's bloom, which belongs to
       the ART, not to this file — the vines flower head is centred on row 24.5 of its sprite,
       a moon disc on 28.5. tools/backgrounds.mjs measures it per sprite, each design file
       carries its values, and backgrounds.js delivers the current design's as
       --pix-bg-icon-a/-b/-undo/-redo; the fallbacks here are the SHIPPED sprites' measured
       values (mirror the engine's DEFAULT_ICON_CENTERS). Generic icon layers, like the
       profile page's .lila-tool-icon, centre themselves on --lila-icon-center; the editor's
       hand-tuned per-id .lila-btn-icon rules below were tuned ON the shipped art, so they
       keep their numbers and follow other designs through --lila-icon-shift (this anchor
       minus the shipped one — 0 on the shipped design by construction). */
    --lila-icon-ref: 24.5px;
    --lila-icon-center: var(--pix-bg-icon-a, 24.5px);
    --lila-icon-shift: calc(var(--lila-icon-center) - var(--lila-icon-ref));
    /* The room this design's shape offers an icon above and below that anchor (unitless px),
       measured by the tool. TWO numbers because a bloom is not symmetric: the shipped flower
       reaches 24.5 up and 45.5 down (its petals sit on a long stem), which is exactly why
       the tall icons below are tuned to hang low; a moon disc offers 24.5 each way. An icon
       that fits is left alone; one that would overflow shrinks just enough (see
       .lila-btn-icon). The fallbacks are the shipped sprites' own measurements. */
    --lila-icon-up: var(--pix-bg-icon-up-a, 24.5);
    --lila-icon-down: var(--pix-bg-icon-down-a, 45.5);
}

.background-lila-button::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image: var(--lila-btn);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    image-rendering: pixelated;
}

.background-lila-button::after {
    content: '';
    position: absolute;
    inset: 0;
    background-color: transparent;
    -webkit-mask-image: var(--lila-btn);
    mask-image: var(--lila-btn);
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    image-rendering: pixelated;
    z-index: 1;
    pointer-events: none;
}

/* Exact bounding boxes from pixel analysis of left-vine-1x.png. Each slot reads the sprite
   and the icon geometry of ITS OWN POSITION (--pix-bg-btn-1 .. -9), so a design may draw the
   nine buttons as a series instead of two alternating shapes; the a/b alternation the shipped
   art uses is written once, in the :root defaults of those vars. --lila-icon-shift needs no
   repeating, its calc() resolves against the cascaded --lila-icon-center on the same element,
   and the fallbacks are the shipped blooms' measured values (both shapes share them). */
.background-lila-button.lila-1 { left: 15px; top: 95px; --lila-btn: var(--pix-bg-btn-1); --lila-icon-center: var(--pix-bg-icon-1, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-1, 24.5); --lila-icon-down: var(--pix-bg-icon-down-1, 45.5); }
.background-lila-button.lila-2 { left: 65px; top: 155px; --lila-btn: var(--pix-bg-btn-2); --lila-icon-center: var(--pix-bg-icon-2, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-2, 24.5); --lila-icon-down: var(--pix-bg-icon-down-2, 45.5); }
.background-lila-button.lila-3 { left: 15px; top: 195px; --lila-btn: var(--pix-bg-btn-3); --lila-icon-center: var(--pix-bg-icon-3, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-3, 24.5); --lila-icon-down: var(--pix-bg-icon-down-3, 45.5); }
.background-lila-button.lila-4 { left: 75px; top: 245px; --lila-btn: var(--pix-bg-btn-4); --lila-icon-center: var(--pix-bg-icon-4, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-4, 24.5); --lila-icon-down: var(--pix-bg-icon-down-4, 45.5); }
.background-lila-button.lila-5 { left: 15px; top: 285px; --lila-btn: var(--pix-bg-btn-5); --lila-icon-center: var(--pix-bg-icon-5, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-5, 24.5); --lila-icon-down: var(--pix-bg-icon-down-5, 45.5); }
.background-lila-button.lila-6 { left: 85px; top: 335px; --lila-btn: var(--pix-bg-btn-6); --lila-icon-center: var(--pix-bg-icon-6, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-6, 24.5); --lila-icon-down: var(--pix-bg-icon-down-6, 45.5); }
.background-lila-button.lila-7 { left: 25px; top: 375px; --lila-btn: var(--pix-bg-btn-7); --lila-icon-center: var(--pix-bg-icon-7, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-7, 24.5); --lila-icon-down: var(--pix-bg-icon-down-7, 45.5); }
.background-lila-button.lila-8 { left: 85px; top: 415px; --lila-btn: var(--pix-bg-btn-8); --lila-icon-center: var(--pix-bg-icon-8, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-8, 24.5); --lila-icon-down: var(--pix-bg-icon-down-8, 45.5); }
.background-lila-button.lila-9 { left: 35px; top: 455px; --lila-btn: var(--pix-bg-btn-9); --lila-icon-center: var(--pix-bg-icon-9, 24.5px); --lila-icon-up: var(--pix-bg-icon-up-9, 24.5); --lila-icon-down: var(--pix-bg-icon-down-9, 45.5); }

/* Undo / redo sit in the two smaller lilacs (#a667e4) at the top-right of the
   band. They are momentary actions, not selectable tools, so they never take
   the .active state; their sprites are the two small blooms themselves, so the
   hover tint now has the right shape too. Boxes cover each ~35x49 bloom
   (image y + 60px background offset). */
.background-lila-button.lila-undo { left: 69px; top: 78px; width: 40px; height: 52px; --lila-btn: var(--pix-bg-btn-undo); --lila-icon-ref: 19px; --lila-icon-center: var(--pix-bg-icon-undo, 19px); --lila-icon-up: var(--pix-bg-icon-up-undo, 17); --lila-icon-down: var(--pix-bg-icon-down-undo, 32); }
.background-lila-button.lila-redo { left: 95px; top: 114px; width: 40px; height: 52px; --lila-btn: var(--pix-bg-btn-redo); --lila-icon-ref: 19px; --lila-icon-center: var(--pix-bg-icon-redo, 19px); --lila-icon-up: var(--pix-bg-icon-up-redo, 17); --lila-icon-down: var(--pix-bg-icon-down-redo, 32); }

#backgroundLilaUndoButton .lila-btn-icon,
#backgroundLilaRedoButton .lila-btn-icon {
    --lila-icon-rise: 10.5;
    --lila-icon-drop: 12.5;
    top: 8px;
    left: 50%;
    width: 24px;
    height: 24px;
}

/* Nudge the undo arrow a few px right so it sits centred on its bloom. */
#backgroundLilaUndoButton .lila-btn-icon { translate: calc(-50% + 3px); }

/* Undo/redo step counters — how many actions can be undone / redone. Small badge
   on the lilac (its own child span, since ::after is the lila active-image slot);
   shown by updateUndoRedoButtons only when the count is > 0. */
.lila-count {
    position: absolute;
    z-index: 3;
    top: 1px;
    right: 2px;
    min-width: 14px;
    height: 14px;
    padding: 0 2px;
    box-sizing: border-box;
    display: none;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    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;
}
.lila-count.show { display: flex; }

/* Undo/redo are disabled when their stack is empty (updateUndoRedoButtons). Unlike
   the tool lilas — which fade when disabled in circular mode via the later
   `.background-lila-button:disabled` rule — keep these at full strength so the
   arrow never looks faded/transparent; just make the button inert. Two classes
   here beat that one-class rule on specificity regardless of source order. */
.background-lila-button.lila-undo:disabled,
.background-lila-button.lila-redo:disabled {
    opacity: 1;
    filter: none;
    cursor: default;
    pointer-events: none;
}

.background-lila-button:hover,
.background-lila-button:focus-visible {
    outline: none;
}

.background-lila-button:hover::after,
.background-lila-button:focus-visible::after {
    background-color: rgba(255, 255, 255, 0.25);
}

.background-lila-button.active {
    box-shadow: none;
}

/* Selected: the sprite's silhouette filled with the design's own highlight colour.
   Declared after the hover rule so a hovered selected button stays filled. */
.background-lila-button.active::after {
    background-color: var(--pix-bg-lila-active);
}

/* A flower with no tool behind it (account-cloud.js fillEmptyLilaSlots). The band PNG holds
   only the decor now — each bloom is its button's own sprite — so a slot nothing renders is
   a gap in the band, which is what the profile page's five spare positions used to be. This
   draws the sprite and nothing else: no cursor, no hover tint, no selected fill, no hit area
   (which also keeps it out of the way of the tools around it). */
.background-lila-button.lila-decor {
    pointer-events: none;
    cursor: default;
}

/* The PROFILE page's tool flowers (account-cloud.js) carry their icon in a span rather than
   an <img class="lila-btn-icon">: it may be an emoji, a drawn PNG glyph or a composed avatar,
   at whatever size that tool asked for. So instead of a per-icon top offset it is centred —
   both ways — on the button's bloom anchor, and any icon of any size lands on the flower.
   Its label pill is .lila-tool-label, on the stem below. */
.lila-tool-icon {
    position: absolute;
    z-index: 2;
    left: 0;
    right: 0;
    top: var(--lila-icon-center, 24.5px);
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    pointer-events: none;
}

.lila-tool-label {
    position: absolute;
    z-index: 2;
    left: -7px;
    right: -7px;
    bottom: 2px;
    padding: 1px 2px;
    box-sizing: border-box;
    font-size: 8px;
    line-height: 1.2;
    font-weight: 700;
    text-align: center;
    color: var(--pix-ui-on);
    background: rgba(255, 255, 255, 0.8);
    border-radius: 5px;
    pointer-events: none;
}

/* A tool with no icon centres its label on the flower head instead of the stem. */
.lila-tool-label.lila-tool-label--alone {
    top: var(--lila-icon-center, 24.5px);
    bottom: auto;
    transform: translateY(-50%);
}

/* Each tool icon is hand-tuned ON the shipped blooms — its own box, angle and position. Two
   numbers in the per-id rules below let that tuning survive a DIFFERENT design's button
   shape, and they sit next to the art they describe:
     --lila-icon-rise  how far the icon's DRAWN art reaches above the bloom anchor, on screen
     --lila-icon-drop  ...and below it.
   They are not derivable from the box: `object-fit: contain` letterboxes each PNG by its own
   aspect (the hook's 65x430 art in a 22x58 box renders ~6x55), and the rotation widens the
   result. They were measured off the real PNGs; re-measure if an icon's art, box or angle
   changes (tools/backgrounds.mjs's header points at the method).
   From those, two things follow. An icon that would reach past what the design's shape
   offers (--lila-icon-up / --lila-icon-down) shrinks by the worst of the two ratios, so one
   that already fits keeps its tuned size and the shipped design shrinks nothing at all, by
   construction. And because a scale pulls the art toward its own box centre rather than
   toward the bloom, margin-top adds back the difference, keeping the anchor point still — a
   shrinking icon stays ON the flower instead of sliding down it. (That same margin-top also
   carries --lila-icon-shift, which follows a design whose bloom sits elsewhere in its
   sprite.)
   Holding the anchor is right up to the moment an icon has to shrink: the constraint that
   forced it is one-sided, so the shrunken art ends up flush against that edge of the shape
   with all the freed room left on the other. --lila-icon-recentre then puts it in the middle
   of what the shape offers. It is what the moon and planet buttons needed for the hook and
   the line, the only two icons a disc makes shrink.
   Two deliberate mechanics: the shrink is the standalone `scale` property, NOT part of
   `transform`, because cosmetics.js writes an inline transform on some reward icons and it
   would silently win; and the horizontal centring is the standalone `translate`, because
   `transform` is applied BEFORE `scale` and a translateX(-50%) in there would itself be
   scaled, drifting a shrunken icon off centre. transform-origin is deliberately left at its
   default: it would move the rotation pivot too, which would shift every rotated icon on the
   shipped design. */
.lila-btn-icon {
    position: absolute;
    z-index: 2;
    --lila-icon-rise: 8.5;
    --lila-icon-drop: 23.5;
    --lila-icon-s: min(1, calc(var(--lila-icon-up, 24.5) / var(--lila-icon-rise)),
                          calc(var(--lila-icon-down, 45.5) / var(--lila-icon-drop)));
    /* Half the difference between the slack left above the shrunken art and the slack left
       below it: add it and the art sits in the MIDDLE of the room instead of flush against
       whichever edge forced the shrink. Applied only when the icon actually shrank, via a
       0-or-1 gate (any real shrink is far more than 0.1%), because on a design that fits the
       icon the tuned position is the point - the shipped flower deliberately hangs its icons
       low into the stem, and centring them in the bloom would undo that. Zero on the shipped
       design by construction, since nothing shrinks there. */
    --lila-icon-fit: calc((var(--lila-icon-down, 45.5) - var(--lila-icon-s) * var(--lila-icon-drop) -
                           var(--lila-icon-up, 24.5) + var(--lila-icon-s) * var(--lila-icon-rise)) * 0.5);
    --lila-icon-recentre: calc(var(--lila-icon-fit) * clamp(0, (1 - var(--lila-icon-s)) * 1000, 1));
    top: 16px;
    left: 50%;
    translate: -50%;
    scale: var(--lila-icon-s);
    width: 32px;
    height: 32px;
    object-fit: contain;
    image-rendering: pixelated;
    pointer-events: none;
    margin-top: calc(var(--lila-icon-shift, 0px) +
        (var(--lila-icon-rise) - var(--lila-icon-drop)) * (1 - var(--lila-icon-s)) * 0.5px +
        var(--lila-icon-recentre) * 1px);
}

/* rise/drop = how far the DRAWN art reaches above and below the bloom anchor, measured off
   each PNG through its box, letterboxing and rotation. See .lila-btn-icon. */
#backgroundLila1Button .lila-btn-icon {
    --lila-icon-rise: 21.1;
    --lila-icon-drop: 34.9;
    top: 6px;
    left: 50%;
    width: 50px;
    height: 50px;
    transform: rotate(-21deg);
}

#backgroundLila2Button .lila-btn-icon {
    --lila-icon-rise: 13.4;
    --lila-icon-drop: 20.4;
    top: 13px;
    left: 47%;
    width: 30px;
    height: 30px;
    transform: rotate(-8deg);
}

#backgroundLila3Button .lila-btn-icon {
    --lila-icon-rise: 14.5;
    --lila-icon-drop: 15.5;
    top: 10px;
    left: 51%;
    width: 30px;
    height: 30px;
}

/* The line art is a bare diagonal stroke on a square canvas, so a box shaped like the old
   tall-thin one letterboxed it into a third of its former length. A 50px square gives
   `contain` the room to draw the stroke at its old size (44.0px tall against 44.6), and the
   art is already 27deg off vertical, so it only needs -2deg to land at the 25deg the button
   used to show - not the -12deg the old, shallower drawing wanted. */
#backgroundLila4Button .lila-btn-icon {
    --lila-icon-rise: 16.5;
    --lila-icon-drop: 27.5;
    top: 5px;
    left: 48%;
    width: 50px;
    height: 50px;
    transform: rotate(-2deg);
}

#backgroundLila5Button .lila-btn-icon {
    --lila-icon-rise: 18.6;
    --lila-icon-drop: 21.1;
    top: 5px;
    left: 49%;
    width: 41px;
    height: 41px;
}

#backgroundLila6Button .lila-btn-icon {
    --lila-icon-rise: 16.3;
    --lila-icon-drop: 20.5;
    top: 8px;
    left: 49%;
    width: 37px;
    height: 37px;
}

#backgroundLila7Button .lila-btn-icon {
    --lila-icon-rise: 18.1;
    --lila-icon-drop: 21.1;
    top: 8px;
    left: 48%;
    width: 36px;
    height: 36px;
    transform: rotate(-8deg);
}

#backgroundLila8Button .lila-btn-icon {
    --lila-icon-rise: 17.8;
    --lila-icon-drop: 20.6;
    top: 9px;
    left: 48%;
    width: 34px;
    height: 34px;
    transform: rotate(-10deg);
}

#backgroundLila9Button .lila-btn-icon {
    --lila-icon-rise: 15.9;
    --lila-icon-drop: 18.9;
    top: 8px;
    left: 50%;
    width: 36px;
    height: 36px;
}

.toolbar {
    position: fixed;
    top: calc(5px * var(--ui-scale));
    left: 0;
    right: 0;
    height: calc(50px * var(--ui-scale));
    background-color: transparent;
    border-bottom: none;
    margin-top: 0;
    margin-bottom: 20px;
    display: flex;
    align-items: center;
    gap: calc(10px * var(--ui-scale));
    padding: 0 8px;
    z-index: 40;
}

.toolbar > button,
.toolbar .file-menu > button,
.toolbar .reference-menu > button,
.toolbar .chart-menu > button,
.toolbar .tutorial-menu > button {
    padding: calc(7px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-size: calc(0.96rem * var(--ui-scale));
    font-weight: 600;
    letter-spacing: 0.01em;
    transform: translateY(calc(-4px * var(--ui-scale)));
    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 > button:hover,
.toolbar .file-menu > button:hover,
.toolbar .reference-menu > button:hover,
.toolbar .chart-menu > button:hover,
.toolbar .tutorial-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 > button:focus-visible,
.toolbar .file-menu > button:focus-visible,
.toolbar .reference-menu > button:focus-visible,
.toolbar .chart-menu > button:focus-visible,
.toolbar .tutorial-menu > button:focus-visible {
    outline: 2px solid rgba(166, 103, 228, 0.65);
    outline-offset: 1px;
}

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

/* Disabled toolbar buttons (circular mode hides inapplicable controls) */
.toolbar > 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;
}

.dropdown button:disabled {
    opacity: 0.38;
    cursor: not-allowed;
    color: #b0aa9a;
}

.dropdown button:disabled:hover {
    background: transparent;
}

.background-lila-button:disabled {
    opacity: 0.28;
    filter: saturate(0.2) brightness(0.85);
    pointer-events: none;
}

/* This is where the toolbar's right-hand cluster starts (tutorial, pack, Instagram, Ko-fi,
   language). It is NOT auto-margined here: #yarnMeter sits just before it and its own auto
   margins do the right-anchoring, while also reserving the space the meter occupies. The
   fallbacks below put the auto margin back for the cases where the meter is not in the row. */
.tutorial-menu {
    position: relative;
}
/* Meter never mounted (yarn-meter.js absent or failed): nothing else would push this right. */
.toolbar:not(:has(#yarnMeter)) .tutorial-menu {
    margin-left: auto;
}

.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);
}

.preview-reference-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 220px;
    box-sizing: border-box;
    justify-content: flex-start;
    padding: 2px 0;
    cursor: pointer;
    white-space: nowrap;
    font-size: 0.9rem;
}

.preview-reference-toggle[hidden] {
    display: none;
}

.preview-reference-toggle input {
    margin: 0;
}

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

.canvas-viewport {
    width: var(--canvas-max-size);
    height: var(--canvas-max-size);
    max-width: 100%;
    max-height: calc(100vh - var(--canvas-top-offset) - 24px);
    overflow: auto;
    touch-action: pinch-zoom pan-x pan-y;
    background: transparent;
    line-height: 0;
    flex: 0 0 auto;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-left: auto;
    margin-right: auto;
}

.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%);
}

/* Fallback zoom buttons parked in the gutter just OUTSIDE the canvas top-left
   corner, so they never cover the art. canvas-column is the positioning context
   (always position:absolute on this desktop-only page — the app redirects to
   mobile.html below 900px); right:100% pins them to the canvas's left edge.
   z-index sits above the lilac tool panel (35) so they stay clickable on the
   narrowest desktop widths where the gutter is tight. */
.canvas-zoom-controls {
    position: absolute;
    top: 0;
    right: 100%;
    margin-right: 10px;
    z-index: 36;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

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

.canvas-zoom-btn {
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1;
    color: #5a3d6b;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid rgba(90, 61, 107, 0.35);
    border-radius: 8px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    -webkit-user-select: none;
    user-select: none;
    touch-action: manipulation;
}

.canvas-zoom-btn:hover {
    background: #ffffff;
    border-color: rgba(90, 61, 107, 0.6);
}

.canvas-zoom-btn:active {
    transform: translateY(1px);
}

.canvas-zoom-btn:disabled {
    opacity: 0.4;
    cursor: default;
    box-shadow: none;
}

.project-status-bar {
    width: 220px;
    align-self: center;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 2px;
    padding: 5px 7px;
    margin-left: 0;
    box-sizing: border-box;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.82);
    border: 1px solid rgba(var(--pix-ui-edge), 0.7);
    border-radius: 10px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.15;
}

.project-status-bar span {
    display: inline-block;
    width: auto;
    max-width: 100%;
    white-space: nowrap;
    background: rgba(var(--pix-ui-face), 0.84);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 999px;
    padding: 1px 6px;
}

.stitch-area-button {
    margin-top: 4px;
    width: 100%;
    box-sizing: border-box;
    padding: 4px 8px;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.84);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 999px;
    cursor: pointer;
    transition: background 120ms ease, box-shadow 120ms ease;
}

.stitch-area-button:hover {
    background: rgba(var(--pix-ui-face-2), 0.95);
}

.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: 24px;
    background: rgba(24, 18, 32, 0.34);
    -webkit-backdrop-filter: blur(7px);
    backdrop-filter: blur(7px);
    cursor: pointer;
}

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

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

.stitch-area-canvas-wrap {
    max-width: 60vw;
    max-height: 78vh;
    overflow: auto;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    background: #ffffff;
}

.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: 28px;
    height: 28px;
    font-size: 1.1rem;
    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-btn:hover {
    background: #e3e6cf;
}

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

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

.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;
}

/* The Stitch per Area pill and its "?" sit on one row; the pill flexes, the "?" is fixed. */
.stitch-area-button-row {
    display: flex;
    gap: 4px;
    width: 100%;
    margin-top: 4px;
}

.stitch-area-button-row .stitch-area-button {
    flex: 1;
    width: auto;
    margin-top: 0;
}

/* "?" next to the Stitch per Area button: opens the note on what the tool measures. */
.stitch-area-help {
    flex: 0 0 auto;
    width: 26px;
    padding: 0;
    font: 700 14px/1 sans-serif;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.84);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 999px;
    cursor: pointer;
}

.stitch-area-help:hover {
    background: rgba(var(--pix-ui-face-2), 0.95);
}

.stitch-area-stitches {
    font-size: 1.35rem;
    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.85rem;
    font-weight: 600;
    color: var(--pix-ui-ink);
    cursor: pointer;
}

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

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

.canvas-frame {
    display: block;
    width: max-content;
    position: relative;
    background: #ffffff;
    contain: layout;
}

/* 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).
   Keep the frame invisible until the initial render 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-frame::after {
    content: '';
    position: absolute;
    inset: 0;
    border: 1px solid #000;
    box-sizing: border-box;
    pointer-events: none;
}

#canvas {
    border: none;
    margin: 0;
    transform-origin: top left;
    width: var(--canvas-max-size);
    height: var(--canvas-max-size);
}

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

#examplePatternsBar {
    position: fixed;
    top: calc(55px * var(--ui-scale));
    left: 0;
    right: 0;
    height: calc(34px * var(--ui-scale));
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 39;
    pointer-events: none;
}

#examplePatternsBar > button,
#examplePatternsBar > .project-menu > button {
    pointer-events: auto;
    padding: calc(7px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-size: calc(0.96rem * var(--ui-scale));
    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;
    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, color 140ms ease;
}

#examplePatternsBar > button:hover,
#examplePatternsBar > .project-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);
}

#examplePatternsBar > button:focus-visible,
#examplePatternsBar > .project-menu > button:focus-visible {
    outline: 2px solid rgba(166, 103, 228, 0.65);
    outline-offset: 1px;
}

#examplePatternsBar > button:active,
#examplePatternsBar > .project-menu > button:active {
    border-color: rgba(var(--pix-ui-edge), 0.92);
}

/* Every button that carries two labels prints the full one; the minimal look's block at the
   end of this file swaps them, because it is the only layout short of the width. Both are
   translated, so the swap costs no string. */
.bar-label-min {
    display: none;
}

/* The merged "Projects" menu in the bar above the canvas: markup that exists for the minimal
   look only (index.html), so it draws nothing anywhere else. The bar is pointer-events: none
   with its children set back to auto, and the wrapper is a child now, so it needs both that
   and the positioning its dropdown hangs off. */
#projectBarMenu {
    display: none;
    position: relative;
    pointer-events: auto;
}

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

.tool-panel {
    position: fixed;
    left: calc(var(--lilac-panel-left) + (var(--lilac-panel-width) * var(--lilac-panel-scale)) - 40px * var(--ui-scale));
    top: calc(var(--yellow-vines-top) + 18px * var(--ui-scale));
    transform: scale(var(--ui-scale));
    transform-origin: left top;
    z-index: 35;
    pointer-events: auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    width: auto;
    flex: none;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
}

.tool-panel button {
    min-height: 34px;
}

#toggleGridLines {
    padding: 5px 11px;
    font-size: 0.85rem;
    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.75);
    border-radius: 8px;
    cursor: pointer;
    white-space: nowrap;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
    transition: background 140ms ease, border-color 140ms ease;
}

#toggleGridLines:hover {
    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);
}

#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 ──────────────────────────────────────────────────────
   The Cross-Stitch switch sits in the lilac band, above the pencil flower, and is styled to
   stay quiet next to them: a small flat pill, not another tool. Once it is on, it and the
   frame around the canvas share one thread pink, so "what I draw now is embroidery, not
   crochet" is a single colour you learn once. (Hiding the layer is the "Hide Cross Stitch"
   checkbox under the preview, styled by .preview-reference-toggle like its Hide Reference
   twin, and it only appears once there is something to hide.)

   Symmetry is its neighbour below, drawn as the same pill because the two are the same kind
   of thing: small band controls that are not tools.

   The pair is COMPACT because the strip it stands in is, and the strip is bounded on all four
   sides by things that paint over it. In the band's own units (the panel is drawn at 1.5x, so
   a label measured on screen is half again as long as it is here): the brown tile band is
   fixed at z 26 against this panel's 25, so it covers everything above y 72; the pencil flower
   starts at y 95; and the undo bloom sits at x 69 from y 78 down. That leaves 23px of height
   and 65px of width for two controls, which is why the padding is thin and the labels are set
   at 0.5rem. It is enough: "Point de Croix", the longest of the four labels at 54px, has 3px
   to spare. It did NOT fit the single 64px pill that used to be here, and a centred label that
   overflows is cut at BOTH ends, which is what the ellipsis in the middle of the band was.
   Anything drawn outside that box goes under a bloom or under the brown band. */
.cross-stitch-toggle,
.symmetry-toggle {
    position: absolute;
    left: 4px;
    width: 65px;
    padding: 0 3px;
    font-family: inherit;
    font-size: 0.5rem;
    font-weight: 600;
    letter-spacing: 0;
    line-height: 1.1;
    color: #5c4468;
    background: rgba(255, 253, 250, 0.72);
    border: 1px solid rgba(124, 90, 140, 0.4);
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}

.cross-stitch-toggle { top: 72.6px; }
.symmetry-toggle { top: 84px; }

.cross-stitch-toggle:hover:not(:disabled),
.symmetry-toggle:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.92);
    border-color: rgba(124, 90, 140, 0.75);
}

/* Lit while the mode is on: the cross-stitch switch is on the embroidery layer, the symmetry
   one is drawing live. Both are modes you can leave running, so both have to say so from
   outside the panel they were set in. */
.cross-stitch-toggle.active,
.symmetry-toggle.active {
    background: #a83b6b;
    color: #fff3f8;
    border-color: #a83b6b;
}

.cross-stitch-toggle:disabled,
.symmetry-toggle:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

body.cross-layer-active #canvas {
    box-shadow: 0 0 0 3px #a83b6b;
}

#pencilTool.pencil-icon-btn {
    width: 56px;
    height: 76px;
    padding: 4px;
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
}

#pencilTool .tool-icon {
    width: 24px;
    height: 64px;
    object-fit: contain;
    image-rendering: pixelated;
    display: block;
}

.tool-panel button.icon-btn {
    width: 56px;
    height: 56px;
    padding: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
}

.tool-panel button.icon-btn .tool-icon {
    width: 44px;
    height: 44px;
    object-fit: contain;
    image-rendering: pixelated;
    display: block;
}

.tool-panel label {
    display: none;
}

.tool-panel input[type="range"]:disabled {
    opacity: 0.35;
    filter: saturate(0.2);
    pointer-events: none;
}

.tool-panel input[type="range"] {
    width: 10px;
    height: 136px;
    accent-color: var(--pix-bg-lila-active);
    -webkit-appearance: none;
    appearance: none;
    writing-mode: vertical-lr;
    direction: rtl;
    border-radius: 999px;
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    background: linear-gradient(90deg, rgba(var(--pix-ui-face), 0.95) 0%, rgba(var(--pix-ui-face-2), 0.85) 100%);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12);
}

#toolSizeValue {
    order: -1;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.84);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 999px;
    padding: 1px 6px;
    min-width: 0;
    text-align: center;
}

.tool-panel input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1px solid rgba(var(--pix-ui-edge), 0.9);
    background: var(--pix-bg-lila-active);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
    cursor: pointer;
}

.tool-panel input[type="range"]::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1px solid rgba(var(--pix-ui-edge), 0.9);
    background: var(--pix-bg-lila-active);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
    cursor: pointer;
}

.tool-panel button.active {
    background: #222222;
    color: #ffffff;
}

.selection-actions {
    position: absolute;
    z-index: 20;
    display: flex;
    align-items: center;
    background: rgba(255, 255, 255, 0.96);
    border: 1px solid #888;
    border-radius: 6px;
    padding: 4px;
    gap: 6px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

.selection-actions[hidden] {
    display: none;
}

.selection-actions button {
    width: auto;
}

.preview-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    width: 286px;
    flex-shrink: 0;
    margin-top: 0;
    margin-left: auto;
    margin-right: 24px;
    flex: 0 0 286px;
    padding-right: 16px;
    padding-left: 18px;
    box-sizing: border-box;
    position: relative;
    z-index: 5;
    /* Scale with the rest of the chrome, pinned to the top-right so it stays at
       the right edge; the layout footprint stays 286px and --canvas-side-reserve
       (also × --ui-scale) keeps the grown panel clear of the canvas. max-height
       is divided by the scale so the scaled panel still fits the viewport. */
    transform: scale(var(--ui-scale));
    transform-origin: top right;
    max-height: calc((100vh - var(--canvas-top-offset) - 8px) / var(--ui-scale));
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-gutter: stable;
}

.preview-panel h3 {
    margin: 0;
    padding: 7px 14px;
    font-size: 0.86rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: var(--pix-ui-ink);
    text-transform: uppercase;
    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;
}

#previewCanvas {
    width: 220px;
    height: 220px;
    border: 1px solid #000;
    box-sizing: border-box;
    background: #ffffff;
    image-rendering: pixelated;
}

.preview-header-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 220px; /* match the preview canvas so the row aligns to its borders */
    max-width: 100%;
    height: 40px; /* fixed so the larger signed-in avatar can't grow the column height */
    overflow: visible; /* let the top-pinned avatar spill into the 8px gap above the preview */
}

/* Matches the Preview header pill (.preview-panel h3). The "My Projects" button that
   project-ui.js injects into the preview header uses this look. */
.preview-projects-button {
    padding: 7px 14px;
    font-size: 0.86rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    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;
    cursor: pointer;
    white-space: nowrap;
    transition: background 120ms ease, box-shadow 120ms ease;
}

.preview-projects-button:hover {
    background: linear-gradient(180deg, rgba(var(--pix-ui-face-2), 0.95) 0%, rgba(225, 230, 180, 0.9) 100%);
}

/* Account launcher (account-cloud.js). On desktop it floats (.preview-account-floating,
   position + placement set by JS) centered in the gap above the preview: signed in = just
   the user's avatar; signed out = an accent lilac pill so first-time visitors notice sign-in.
   The mobile toolbar fallback (no .preview-account-floating) keeps a compact avatar + label. */
.preview-account-button {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 5px 12px;
    border-radius: 12px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--pix-ui-ink);
    font-weight: 700;
    font-size: 0.8rem;
    line-height: 1.1;
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
}

/* Signed in: just the avatar, no pill chrome (a round hit area with a soft focus/hover ring). */
.preview-account-button.signed-in {
    flex-direction: column;
    gap: 0;
    padding: 0;
    border: none;
    background: transparent;
    border-radius: 50%;
}

.preview-account-button.signed-in:hover {
    background: transparent;
    box-shadow: 0 0 0 3px rgba(166, 103, 228, 0.35);
}

/* Desktop: floats centered in the gap above the preview. JS sets position:fixed + left/top
   (the gap centre); this centres the box on that point. */
.preview-account-floating {
    transform: translate(-50%, -50%);
}

.preview-account-button.signed-out {
    flex-direction: row;
    gap: 6px;
    padding: 8px 20px;
    color: #fff;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: linear-gradient(180deg, #b98be0 0%, #a061d6 100%);
    border-color: rgba(120, 70, 180, 0.9);
    box-shadow: 0 2px 7px rgba(120, 70, 180, 0.38);
}

.preview-account-button.signed-out:hover {
    background: linear-gradient(180deg, #c39cea 0%, #ab6ee0 100%);
    border-color: rgba(120, 70, 180, 1);
}

.preview-account-button:focus-visible {
    outline: 2px solid rgba(166, 103, 228, 0.7);
    outline-offset: 2px;
}

/* Resize/Pixelise modal: preview column on the left, Size + Colours section
   cards on the right; stacks to one column on narrow viewports. Doubled class
   so the width beats the generic .modal rule, which comes later in this file. */
.modal.resize-pixelise-modal {
    width: min(96vw, 780px);
    min-width: 0; /* flex item on the backdrop: never grow past the set width */
    box-sizing: border-box; /* width includes the .modal padding, so 96vw truly fits */
    max-height: 92vh;
    overflow-y: auto;
}

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

.rp-columns {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    margin-top: 12px;
}

.rp-preview-col {
    flex: 0 0 auto;
    width: 264px;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    position: sticky;
    top: 0;
}

.rp-controls-col {
    flex: 1 1 auto;
    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.72rem;
    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: 6px 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);
}

/* Adopted from the New Project modal: put 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.9rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: #4a4a4a;
    text-align: center;
}

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

@media (max-width: 700px) {
    .rp-columns {
        flex-direction: column;
        align-items: stretch;
    }

    .rp-preview-col {
        width: 100%;
        position: static;
    }
}

/* Symmetry modal: the same shape as Resize/Pixelise (live preview left, choices right) and
   it borrows that modal's .rp-section chrome deliberately, so the two read as one family of
   "change the whole picture" dialogs. Doubled class so the width beats the generic .modal
   rule below. */
.modal.symmetry-modal {
    width: min(96vw, 620px);
    min-width: 0;
    box-sizing: border-box;
    max-height: 92vh;
    overflow-y: auto;
}

.sym-columns {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    margin-top: 12px;
}

.sym-preview-col {
    flex: 0 0 auto;
    width: 244px;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

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

.symmetry-preview-canvas {
    width: 240px;
    height: 240px;
    max-width: 100%;
    border: 1px solid rgba(var(--pix-ui-edge), 0.5);
    border-radius: 6px;
    background: #ffffff;
    image-rendering: pixelated;
}

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

.sym-section-title {
    margin: 0 0 8px;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #6d6a58;
}

/* The five axes as one wrapping row of tiles: the drawing says which line the picture is
   folded along, and the word underneath names it. */
.sym-axis-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.sym-axis-btn {
    flex: 1 1 76px;
    min-width: 68px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 6px 4px;
    font-family: inherit;
    font-size: 11px;
    line-height: 1.15;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.85);
    border: 1px solid rgba(var(--pix-ui-edge), 0.55);
    border-radius: 8px;
    cursor: pointer;
}

.sym-axis-btn:hover:not(:disabled) {
    background: rgba(var(--pix-ui-face), 1);
    border-color: rgba(var(--pix-ui-edge), 0.95);
}

.sym-axis-btn[aria-checked="true"] {
    background: var(--pix-ui-accent);
    border-color: var(--pix-ui-accent-dark);
    color: var(--pix-ui-on-ink);
}

.sym-axis-btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

.sym-axis-glyph {
    width: 30px;
    height: 30px;
    display: block;
}

.sym-glyph-frame {
    fill: none;
    stroke: currentColor;
    stroke-width: 1;
    opacity: 0.45;
    stroke-dasharray: 2.5 2;
}

.sym-glyph-line {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
}

.sym-glyph-dot {
    fill: currentColor;
    stroke: none;
}

.sym-axis-label {
    text-align: center;
}

/* Drawing live is a MODE, not one more parameter of the fold above it, so it is given its
   own tinted block at the bottom rather than a third section that looks like the other two.
   Its state is visible from outside this modal: the band pill stays lit and the editor
   canvas draws the fold. */
/* What the chosen axis does, under the tiles. It was a tickbox's hint while the mode had a
   switch of its own; the axis is the switch now, so it is simply the note. */
.sym-live-hint {
    margin: 10px 0 0;
    font-size: 11px;
    line-height: 1.35;
    color: var(--pix-ui-ink-soft);
}

.sym-note {
    margin: 8px 0 0;
    font-size: 12px;
    color: #8a5a2a;
}

@media (max-width: 620px) {
    .sym-columns {
        flex-direction: column;
        align-items: stretch;
    }

    .sym-preview-col {
        width: 100%;
    }
}

/* Share picture (export) modal: live preview on the left, option sections on
   the right (reuses the rp-section chrome). Doubled class so the width beats
   the generic .modal rule below. */
.modal.export-picture-modal {
    width: min(96vw, 820px);
    min-width: 0;
    box-sizing: border-box;
    max-height: 92vh;
    overflow-y: auto;
}

.ep-columns {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    margin-top: 12px;
}

.ep-preview-col {
    flex: 0 0 auto;
    width: 300px;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.ep-preview-box {
    width: 100%;
    height: 340px;
    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.9rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: #4a4a4a;
    text-align: center;
}

.ep-controls-col {
    flex: 1 1 auto;
    min-width: 0;
    display: grid;
    gap: 10px;
}

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

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

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

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

.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: 4px 10px;
    background: #fff;
    border: 1px solid #c8c8c8;
    border-radius: 6px;
    font-size: 0.85rem;
    cursor: pointer;
}

.ep-logo-btn:hover {
    border-color: #a667e4;
    color: #5a3c86;
}

.ep-logo-preview {
    width: 30px;
    height: 30px;
    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: 24px;
    height: 24px;
    padding: 0;
    border: 1px solid #c8c8c8;
    border-radius: 50%;
    background: #fff;
    color: #a4444d;
    font-size: 0.95rem;
    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 without hunting. Outlined accent style keeps it distinct from
   the primary Download PNG action in the footer. */
.ep-original-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 4px 0 0;
    padding: 7px 14px;
    background: #f6f0ff;
    border: 1px solid #a667e4;
    border-radius: 999px;
    color: #5a3c86;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

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

.ep-original-btn:hover,
.ep-original-btn:focus-visible {
    background: #ede0ff;
    border-color: #8f4fd0;
    color: #452e6a;
}

@media (max-width: 760px) {
    .ep-columns {
        flex-direction: column;
        align-items: stretch;
    }

    .ep-preview-col {
        width: 100%;
    }

    .ep-preview-box {
        height: 260px;
    }
}

/* Import pixel art modal (Open Picture): source pane with crop handles on the
   left, counts + rebuilt preview on the right; stacks on narrow viewports.
   Doubled class so the width beats the generic .modal rule below. */
.modal.pixel-art-import-modal {
    width: min(96vw, 780px);
    min-width: 0;
    box-sizing: border-box;
    max-height: 92vh;
    overflow-y: auto;
}

.pai-columns {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    margin-top: 12px;
}

.pai-source-col {
    flex: 1 1 55%;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.pai-result-col {
    flex: 1 1 45%;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.pai-source-canvas {
    width: 100%;
    height: 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: 4px;
    justify-content: flex-end;
    margin-top: -2px;
}

.pai-zoom-btn {
    width: 34px;
    height: 30px;
    padding: 0;
    border: 1px solid #d7cfe4;
    border-radius: 6px;
    background: #f6f2fb;
    color: #4a3d66;
    font-size: 20px;
    font-weight: 600;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.pai-zoom-btn:hover:not(:disabled) {
    background: #ede4f8;
}

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

.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.8rem;
    color: #555;
    white-space: nowrap;
}

.pai-count-fields input[type="number"] {
    width: auto;
    flex: 1 1 64px;
    min-width: 44px;
    box-sizing: border-box;
    padding: 6px 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.82rem;
    font-weight: 600;
    color: #3c7d46;
}

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

.pai-result-canvas {
    width: 100%;
    height: 220px;
    border: 1px solid #e2e2e2;
    border-radius: 8px;
    background: #ffffff;
    image-rendering: pixelated;
}

.pai-result-info {
    font-size: 0.9rem;
    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.4;
}

.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;
}

@media (max-width: 700px) {
    .pai-columns {
        flex-direction: column;
        align-items: stretch;
    }
}

.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.85rem;
}

.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.85rem;
    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: 7px 16px;
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.9);
    border: 1px solid rgba(var(--pix-ui-edge), 0.7);
    border-radius: 999px;
    cursor: pointer;
    transition: background 120ms ease;
}

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

.pixelise-swap-toggle:hover {
    background: rgba(var(--pix-ui-face-2), 0.95);
}

.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.7rem;
    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, 24px);
    grid-auto-rows: 24px;
    gap: 5px;
    justify-content: center;
    align-content: start;
    max-height: 150px;
    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: 24px;
    height: 24px;
}

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

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

.pixelise-swap-btn {
    font-size: 1.1rem;
    line-height: 1;
    padding: 6px 9px;
    background: rgba(var(--pix-ui-face), 0.84);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 999px;
    cursor: pointer;
    transition: background 120ms ease;
}

.pixelise-swap-btn:hover:not(:disabled) {
    background: rgba(var(--pix-ui-face-2), 0.95);
}

.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: 260px;
    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.72rem;
    color: #555;
    text-align: center;
    line-height: 1.3;
}

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

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

.palette-panel {
    width: 244px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    align-items: center;
}

.palette-panel h4 {
    display: none;
}

.palette-tabs {
    position: relative;
    display: flex;
    width: 100%;
    gap: 0;
    border-bottom: 2px solid #555;
}

.palette-tab {
    flex: 1;
    background: rgba(var(--pix-ui-face), 0.92);
    border: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    padding: 4px 6px;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    color: var(--pix-ui-ink);
}

.palette-tab.active {
    color: #111;
    border-bottom-color: #111;
}

/* The "?" beside the My Palette tab: the same round help chip Stitch per Area carries. It sits
   OVER the strip's right end instead of being laid out in it. The two tabs share the panel's
   fixed 244px, so a third flex item takes its width off them and pulls their faces in from the
   strip's own edges, uncovering the band behind. The tab it lands on keeps room with padding. */
.palette-help {
    position: absolute;
    right: 3px;
    top: 50%;
    transform: translateY(-50%);
    width: 19px;
    height: 19px;
    padding: 0;
    font: 700 12px/1 sans-serif;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face-2), 0.95);
    border: 1px solid rgba(var(--pix-ui-edge), 0.65);
    border-radius: 999px;
    cursor: pointer;
}

.palette-help:hover {
    background: var(--pix-ui-accent);
    color: #fffdf2;
}

/* Room for it on the tab it overlays (the second one, My Palette). */
.palette-tabs .palette-tab + .palette-tab {
    padding-right: 24px;
}

/* A control named inside a help note, drawn as the thing it names rather than left as prose:
   "+", Load Palette, Swap Color, My Palette. Built by markPaletteHelpText from [[...]] markers
   in the i18n string, so a translation moves the marker with the word. */
.help-chip {
    display: inline-block;
    padding: 0 5px;
    border: 1px solid rgba(var(--pix-ui-edge), 0.55);
    border-radius: 5px;
    background: rgba(var(--pix-ui-face-2), 0.95);
    color: var(--pix-ui-ink);
    font-size: 0.9em;
    font-weight: 700;
    white-space: nowrap;
}

.color-palette {
    width: 100%;
    display: grid;
    grid-template-columns: repeat(6, 30px);
    grid-auto-rows: 30px;
    justify-content: center;
    gap: 6px;
    max-height: 218px;
    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);
}

/* The colour a Swap Color is about to replace, while it waits for the one to put in its
   place. Same ring as .selected in the accent colour, and it must stay AFTER that rule:
   equal specificity, so the later one wins when a swatch is both. */
.palette-swatch.swap-source {
    transform: translateY(-1px);
    box-shadow: 0 0 0 1px #ffffff, 0 0 0 3px var(--pix-ui-accent-dark), 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;
}

/* "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;
}

/* "Swap Color": the same two-column pill as Load Palette while it waits to be started. Armed,
   its label becomes the step it is on ("Pick the colour to replace"), which needs the row. */
.palette-swatch.swap-color-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;
}

.palette-swatch.swap-color-btn.armed {
    grid-column: 1 / -1;
    background-color: rgba(var(--pix-ui-face-2), 0.95);
    color: var(--pix-ui-ink);
    border-style: solid;
    border-color: var(--pix-ui-accent-dark);
}

/* "Delete Color": the fill tool's blank option. It is inserted as the palette's first child,
   so it takes the whole first row on its own and the colours start one row lower. The
   checkerboard says "no colour", the same thing an empty stitch is. */
.palette-swatch.delete-color-btn {
    grid-column: 1 / -1;
    width: auto;
    padding: 0 6px;
    background-color: rgba(var(--pix-ui-face), 0.92);
    background-image:
        linear-gradient(45deg, rgba(var(--pix-ui-edge), 0.2) 25%, transparent 25%, transparent 75%, rgba(var(--pix-ui-edge), 0.2) 75%),
        linear-gradient(45deg, rgba(var(--pix-ui-edge), 0.2) 25%, transparent 25%, transparent 75%, rgba(var(--pix-ui-edge), 0.2) 75%);
    background-size: 12px 12px;
    background-position: 0 0, 6px 6px;
    color: var(--pix-ui-ink);
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.1;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    border-style: dashed;
}

.palette-swatch.delete-color-btn.selected {
    border-style: solid;
    border-color: var(--pix-ui-accent-dark);
}

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

.confirm-color-btn {
    font-size: 1rem;
    font-weight: 700;
    border-style: solid;
}

.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;
}

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

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

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

.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;
}

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

#openPictureFields[hidden] {
    display: none;
}

/* Mosaic's own door, in the picture door's place. Same shape, so the modal does not move
   when the tick changes which of the two is showing. */
#mosaicProjectFields {
    display: grid;
    gap: 8px;
    margin-top: 6px;
}

#mosaicProjectFields[hidden] {
    display: none;
}

/* The picture door at the top of the New Project modal. */
.np-from-picture {
    padding: 10px 12px;
    font-size: 0.95rem;
    font-weight: 600;
    color: #fff;
    background: #a667e4;
    border: 1px solid #9354d1;
    border-radius: 8px;
    cursor: pointer;
}

.np-from-picture:hover {
    background: #9354d1;
}

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

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

#resizeFields[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;
}

/* Modal buttons wear the rounded style the newer, JS-built modals already use (see
   project-ui.js's save-before-pattern modal): a light face with a soft edge, and ONE filled
   primary per row (.modal-primary). Before this the markup modals — New Project, Pixelise,
   Create from a picture — kept the plain UA button and read as a different app. Colours come
   from the chrome vars, so a background design's buttons follow (never the olive literals). */
.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;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}

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

.modal-actions button:hover:not(:disabled),
.pai-crop-actions button:hover:not(:disabled),
.pai-gauge-btn:hover:not(:disabled) {
    background: rgba(var(--pix-ui-face), 0.7);
    border-color: rgba(var(--pix-ui-edge), 0.8);
}

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

/* The one action the modal is there for. */
.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;
}

.modal-actions button.modal-primary:hover:not(:disabled) {
    background: var(--pix-ui-accent-dark);
    border-color: var(--pix-ui-accent-dark);
}

.free-patterns-modal {
    width: min(92vw, 440px);
}

/* Community gallery (Phase 3): community.js inline-styles the cards; this only widens the
   examples modal for the gallery grid and lets it scroll. Doubled selector beats the late
   generic .modal width rule (see CLAUDE.md CSS gotcha). */
.modal.free-patterns-modal {
    /* Wide enough for 6 gallery cards per row (6*168 + 5*12 gap = 1068px of grid,
       + 16px padding each side + scrollbar). border-box so the declared width is the
       whole box (like .resize-pixelise-modal) and 96vw keeps the margin on narrow screens. */
    box-sizing: border-box;
    width: min(96vw, 1180px);
}
/* Start this modal near the top of the canvas (just below the toolbar) rather than
   vertically centred, so the wide gallery has room to grow downward. */
#freePatternsModal {
    align-items: flex-start;
    padding-top: 60px;
}
.free-patterns-modal .community-panel {
    /* Cap so the now top-aligned, taller modal still fits short viewports (chrome ~360px). */
    max-height: min(58vh, calc(100vh - 360px));
    overflow-y: auto;
}
.free-patterns-modal .community-tabs {
    flex-wrap: wrap;
}

.free-patterns-welcome {
    margin: 0 0 12px;
    font-size: 0.9rem;
    color: #444;
    line-height: 1.5;
    text-align: center;
}

.free-patterns-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: center;
    padding: 8px 0 12px;
}

.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: 20px;
    height: 20px;
    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);
    transition: transform 0.12s ease, color 0.12s ease;
    z-index: 2;
}

.free-pattern-insta:hover,
.free-pattern-insta:focus-visible {
    transform: scale(1.12);
    color: #c13584;
}

.premium-prompt-message {
    margin: 0;
    white-space: pre-line;
    color: #343019;
    line-height: 1.45;
}

.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:hover {
    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 {
    position: fixed;
    left: auto;
    right: 12px;
    bottom: 10px;
    transform: none;
    z-index: 120;
    /* Four links, two by two: a 2-column grid rather than a wrapping flex row, so the
       bubble keeps its shape whatever the language does to the word widths. */
    display: grid;
    grid-template-columns: repeat(2, auto);
    justify-content: center;
    justify-items: center;
    align-items: center;
    gap: 3px 12px;
    max-width: 200px;
    padding: 5px 12px;
    border: 1px solid rgba(var(--pix-ui-edge), 0.7);
    border-radius: 999px;
    background: rgba(var(--pix-ui-face), 0.96);
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(2px);
}

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

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

.footer-copy {
    flex-basis: 100%;
    grid-column: 1 / -1;
    text-align: center;
    font-size: 0.56rem;
    font-weight: 600;
    color: var(--pix-ui-ink-soft);
    margin-top: 1px;
}

/* 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;
}

/* ─────────────────────────────────────────────
   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;
}

/* A tour button placed directly in a subsection grid must not stretch full-width. */
.tutorial-subsection > .tutorial-help-tour {
    justify-self: start;
}

.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). The
   stitch boxes and row-end boxes are real buttons; clicking toggles a red ✕, and
   finishing the row strikes the whole run through — mirroring the pattern window. */
/* Demo boxes on the left, the "learn how to read this pattern" link on the right. */
.tutorial-pattern-demo-row {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
}

.tutorial-pattern-read {
    flex: 1 1 150px;
    min-width: 140px;
    margin: 0;
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1.4;
    color: #2f2f2f;
}

.tutorial-pattern-read a {
    color: #8a4fc4;
    font-weight: 700;
    text-decoration: underline;
}

.tutorial-pattern-read a:hover {
    color: #6f3aa0;
}

.tutorial-pattern-demo {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

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

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

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

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

.tutorial-pattern-sbox:hover,
.tutorial-pattern-end:hover {
    filter: brightness(0.92);
}

.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: 74px;
    margin: 2px 0;
}

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

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

.tutorial-pattern-hint {
    margin: 10px 0 0;
    font-size: 0.9rem;
    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;
}

/* ── Contents grid (tutorial front page) ── */

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

.tutorial-toc-item {
    width: 100%;
    display: grid;
    grid-template-columns: max-content 1fr max-content;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    text-align: left;
    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.55);
    border-radius: 10px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, transform 0.1s;
}

.tutorial-toc-item:hover {
    border-color: rgba(var(--pix-ui-edge), 0.9);
    transform: translateX(2px);
}

.tutorial-toc-icon {
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.tutorial-toc-img {
    max-width: 30px;
    max-height: 32px;
    width: auto;
    height: auto;
    image-rendering: pixelated;
}

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

.tutorial-toc-title {
    font-weight: 700;
    font-size: 0.92rem;
    color: var(--pix-ui-ink);
}

.tutorial-toc-blurb {
    font-size: 0.78rem;
    color: #6b6857;
    line-height: 1.35;
}

.tutorial-toc-chevron {
    font-size: 1.2rem;
    color: var(--pix-ui-accent);
    font-weight: 700;
}

/* Per-section help row: "Try the interactive tour" + Instagram link */
/* Help bar shown at the top of each tutorial section: two matching chips
   (Instagram link + interactive tour) sitting above the section body. */
.tutorial-help-row {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 10px;
    margin: 2px 0 18px;
    padding-bottom: 16px;
    border-bottom: 1px solid rgba(var(--pix-ui-edge), 0.22);
}

.tutorial-section-ig,
.tutorial-help-tour {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin: 0;
    padding: 7px 14px;
    border: 1px solid transparent;
    border-radius: 999px;
    font: inherit;
    font-size: 0.82rem;
    font-weight: 700;
    line-height: 1.1;
    text-decoration: none;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}

.tutorial-section-ig::before,
.tutorial-help-tour::before {
    content: "";
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
}

/* Instagram chip — purple accent matching the brand link colour. */
.tutorial-section-ig {
    color: #8a4fc4;
    background: linear-gradient(180deg, rgba(249, 241, 255, 0.95) 0%, rgba(243, 230, 252, 0.9) 100%);
    border-color: rgba(138, 79, 196, 0.45);
}

.tutorial-section-ig::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a4fc4' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='2' width='20' height='20' rx='5.5'/%3E%3Ccircle cx='12' cy='12' r='4'/%3E%3Ccircle cx='17.5' cy='6.5' r='1.1' fill='%238a4fc4' stroke='none'/%3E%3C/svg%3E");
}

.tutorial-section-ig:hover {
    color: #6f3aa0;
    background: rgba(243, 230, 252, 0.95);
    border-color: rgba(138, 79, 196, 0.8);
}

/* Interactive tour chip — olive accent matching the app theme. */
.tutorial-help-tour {
    color: #4a5410;
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.95) 0%, rgba(var(--pix-ui-face-2), 0.9) 100%);
    border-color: rgba(var(--pix-ui-edge), 0.7);
}

.tutorial-help-tour::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234a5410' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolygon points='16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76'/%3E%3C/svg%3E");
}

.tutorial-help-tour:hover {
    border-color: rgba(var(--pix-ui-edge), 1);
    background: rgba(var(--pix-ui-face-2), 0.95);
}

.tutorial-jump {
    color: var(--pix-ui-accent);
    font-weight: 600;
    text-decoration: underline;
    cursor: pointer;
}

.tutorial-jump:hover {
    color: #4a5410;
}

.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%);
}

/* ── Tapestry crochet demo (slide 4) ── */

.tut-tapestry-demo {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px;
    background: rgba(255,255,255,0.7);
    border: 1px solid rgba(var(--pix-ui-edge),0.2);
    border-radius: 8px;
    margin-bottom: 8px;
}

.tut-tapestry-row {
    display: flex;
    align-items: center;
    gap: 3px;
}

.tut-row-num {
    font-size: 0.72rem;
    color: #888;
    width: 14px;
    text-align: right;
    flex-shrink: 0;
    margin-right: 4px;
}

.tut-cell {
    width: 14px;
    height: 14px;
    border-radius: 2px;
    flex-shrink: 0;
    border: 1px solid rgba(0,0,0,0.08);
}

.tut-row-check {
    width: 16px;
    height: 16px;
    border: 1.5px solid #bbb;
    border-radius: 3px;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.7rem;
    color: #888;
    flex-shrink: 0;
    margin-left: 4px;
}

.tut-row-check-done {
    border-color: rgba(var(--pix-ui-edge),0.6);
    color: rgba(var(--pix-ui-edge),0.9);
}

.tut-tapestry-done {
    opacity: 0.45;
}

.tut-tapestry-active {
    background: rgba(var(--pix-ui-edge),0.08);
    border-radius: 4px;
    padding: 2px 4px;
    margin: 0 -4px;
}

/* ── Show Time example screenshot (slide 8) ── */

.tutorial-showtime-img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 8px;
    border: 1px solid rgba(var(--pix-ui-edge),0.2);
    margin-bottom: 8px;
}

/* ── 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;
}

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

/* ── Large screens: 1280px+ (default, already works well) ── */

/* ── Medium screens: 900px – 1279px ── */
@media (max-width: 0px) {
    .modal-backdrop {
        align-items: flex-start;
        padding: 8px;
        overflow-y: auto;
    }

    .tutorial-modal {
        width: 100%;
        max-height: calc(100vh - 16px);
        border-radius: 10px;
    }

    .tutorial-header {
        margin-bottom: 10px;
    }

    .tutorial-header h2 {
        font-size: 1.05rem;
    }

    .tutorial-slides {
        min-height: 160px;
    }

    .tutorial-content {
        gap: 6px;
    }

    .tutorial-subsection {
        padding: 7px 8px;
    }

    .tutorial-nav {
        margin-top: 12px;
        padding-top: 10px;
    }

    .tutorial-nav-arrow {
        min-width: 40px;
        padding: 4px 10px;
    }

    .canvas-viewport {
        width: clamp(400px, calc(100vw - 340px), 760px);
        height: clamp(400px, calc(100vw - 340px), 760px);
    }

    .canvas-column {
        width: clamp(400px, calc(100vw - 340px), 760px);
    }

    .tool-panel {
        left: clamp(130px, calc(var(--lilac-panel-left) + (var(--lilac-panel-width) * var(--lilac-panel-scale)) - 30px), calc(var(--lilac-panel-left) + (var(--lilac-panel-width) * var(--lilac-panel-scale)) - 6px));
    }
}

/* ── Small/tablet screens: up to 1099px ── */
@media (max-width: 0px) {
    :root {
        --lilac-image-offset-scaled: 52.8px;
        --lilac-panel-left: 1px;
        --lilac-panel-scale: 0.88;
    }

    body {
        margin: 10px 8px;
    }

    /* Scale down vine decoration */
    .left-vine-bg,
    .left-vine-panel {
        transform: scale(var(--lilac-panel-scale));
    }

    .toolbar {
        flex-wrap: wrap;
        height: auto;
        gap: 6px;
        margin-top: 4px;
        margin-bottom: 12px;
        padding: 6px 8px;
    }

    .toolbar > button,
    .toolbar .file-menu > button,
    .toolbar .reference-menu > button,
    .toolbar .chart-menu > button,
    .toolbar .tutorial-menu > button {
        padding: 5px 10px;
        font-size: 0.85rem;
        transform: none;
    }

    .toolbar > button:hover,
    .toolbar .file-menu > button:hover,
    .toolbar .reference-menu > button:hover,
    .toolbar .chart-menu > button:hover,
    .toolbar .tutorial-menu > button:hover {
        transform: none;
    }

    .toolbar > button:active,
    .toolbar .file-menu > button:active,
    .toolbar .reference-menu > button:active,
    .toolbar .chart-menu > button:active,
    .toolbar .tutorial-menu > button:active {
        transform: none;
    }

    /* Stack workspace vertically */
    .workspace {
        flex-direction: column;
        align-items: center;
        min-height: unset;
        gap: 12px;
    }

    .canvas-column {
        position: static;
        transform: none;
        width: min(100%, calc(100vw - 24px));
        left: unset;
        top: unset;
    }

    .canvas-viewport {
        width: min(100%, calc(100vw - 24px));
        height: min(70vh, 600px);
        margin-left: 0;
        margin-right: 0;
    }

    /* Tool panel: horizontal strip above canvas */
    .tool-panel {
        position: static;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        align-items: center;
        width: 100%;
        order: -1;
        gap: 8px;
    }

    .tool-panel input[type="range"] {
        writing-mode: horizontal-tb;
        direction: ltr;
        -webkit-appearance: none;
        appearance: none;
        width: 120px;
        height: 10px;
    }

    /* Preview panel: move below canvas */
    .preview-panel {
        margin-top: 8px;
        width: 100%;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        align-items: flex-start;
        gap: 12px;
    }

    #previewCanvas {
        width: 160px;
        height: 160px;
    }

    .palette-panel {
        width: 244px;
    }
}

/* ── Mobile screens: up to 599px ── */
@media (max-width: 0px) {
    :root {
        --lilac-image-offset-scaled: 42px;
        --lilac-panel-left: -3px;
        --lilac-panel-scale: 0.7;
    }

    body {
        margin: 6px 4px;
    }

    /* Keep lilac controls visible but scaled on narrow screens */
    .left-vine-bg,
    .left-vine-panel {
        transform: scale(var(--lilac-panel-scale));
    }

    .toolbar {
        gap: 4px;
        padding: 4px;
        margin-bottom: 8px;
    }

    .toolbar > button,
    .toolbar .file-menu > button,
    .toolbar .reference-menu > button,
    .toolbar .chart-menu > button,
    .toolbar .tutorial-menu > button {
        padding: 4px 8px;
        font-size: 0.8rem;
    }

    .canvas-viewport {
        width: 100%;
        height: min(62vh, 460px);
    }

    .canvas-column {
        width: 100%;
    }

    #previewCanvas {
        width: 120px;
        height: 120px;
    }

    .color-palette {
        grid-template-columns: repeat(5, 1fr);
        max-height: 160px;
    }
}

/* ── Short screens: keep controls and canvas inside viewport height ── */
@media (max-height: 0px) {
    :root {
        --lilac-image-offset-scaled: 37.2px;
        --lilac-panel-left: -5px;
        --lilac-panel-scale: 0.62;
    }

    .left-vine-bg,
    .left-vine-panel {
        transform: scale(var(--lilac-panel-scale));
    }

    .canvas-viewport {
        height: min(60vh, 500px);
        max-height: min(60vh, 500px);
    }

    .preview-panel {
        margin-top: 10px;
    }
}

@media (max-width: 900px) {
    .site-footer {
        right: auto;
        left: 12px;
    }
}
/* ===== Instagram / Ko-fi / Supporter Pack buttons ===== */
/* One shape for the three "somewhere else to go" controls at the end of the toolbar. The pack
   button joins them here rather than copying the numbers, so the cluster cannot drift apart. */
#instagramButton,
#kofiButton,
#openPremiumPack {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: calc(36px * var(--ui-scale));
    height: calc(36px * var(--ui-scale));
    padding: 0;
    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: 999px;
    text-decoration: none;
    color: var(--pix-ui-ink);
    box-shadow: none;
    backdrop-filter: blur(1.5px);
    -webkit-backdrop-filter: blur(1.5px);
    transition: background 0.15s, border-color 0.15s;
    transform: translateY(calc(-4px * var(--ui-scale)));
}
#instagramButton:hover,
#kofiButton:hover {
    background: linear-gradient(180deg, rgba(255, 220, 180, 0.95) 0%, rgba(230, 160, 80, 0.85) 100%);
    border-color: rgba(180, 100, 20, 0.9);
}
#instagramButton svg,
#kofiButton svg,
#openPremiumPack svg {
    fill: currentColor;
    display: block;
}

/* ===== Premium Pack button (premium-pack.js) ===== */
/* Sits in the Instagram / Ko-fi cluster, i.e. the "support the site" end of the toolbar and
   not among the drawing controls: the pack is optional and must never read as a tool that is
   missing. It used to be a labelled lilac PILL; since 2026-08-07 it is the same 36px icon
   button as its two neighbours, because the pack is not on sale and a wide labelled control
   is a promise the site cannot keep yet. It keeps the lilac accent so the cluster still reads
   as three different destinations, and it keeps its tooltip and aria-label, which is now the
   only place the words "Supporter Pack" appear in the toolbar. premium-pack.js hides it
   entirely once the account has the pack. Shape rules are shared with the two social buttons
   in the selector list just above, and this block MUST stay after them: both selectors weigh
   one id, so the later one wins and the shared rule sets background, border and colour. */
#openPremiumPack {
    border-color: rgba(123, 63, 181, 0.75);
    background: linear-gradient(180deg, rgba(250, 240, 255, 0.92) 0%, rgba(232, 213, 248, 0.85) 100%);
    color: #5a2f86;
}
/* An author `display` beats the UA sheet's [hidden] rule, so the JS hide needs this back.
   The shared rule above sets `display: inline-flex`, hence this. */
#openPremiumPack[hidden] {
    display: none;
}
#openPremiumPack:hover {
    border-color: rgba(123, 63, 181, 0.95);
    background: linear-gradient(180deg, rgba(253, 248, 255, 0.98) 0%, rgba(240, 226, 252, 0.94) 100%);
    color: #4a2570;
}


/* ===== Language switcher ===== */
.lang-switcher {
    padding: calc(7px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-size: calc(0.96rem * var(--ui-scale));
    font-weight: 600;
    letter-spacing: 0.01em;
    transform: translateY(calc(-4px * var(--ui-scale)));
    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);
    cursor: pointer;
    align-self: center;
}

/* ===== Interactive guided tour ===== */
/* The tour layers sit ABOVE every surface a FAQ flow can spotlight into — the My
   Projects modal and account modal (z 6000), the profile page (5500) — but below the
   in-app dialogs (pixAlert/pixConfirm, z 9000) so a prompt a real click opens stays
   usable over a live tour. */
.tour-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    z-index: 6800;
    display: none;
}

/* During a spotlight step the overlay becomes an invisible full-screen click-blocker
   with a hole punched at the highlighted target (see setOverlayHole). The visible
   dimming still comes from the spotlight's box-shadow, so only the highlighted control
   — and the tour bubble — remain clickable while the tour is live. */
.tour-overlay--cutout {
    background: transparent;
}

#tourSpotlight {
    position: fixed;
    z-index: 6801;
    border-radius: 8px;
    pointer-events: none;
    box-shadow: 0 0 0 3px rgba(166, 103, 228, 0.9),
                0 0 0 9999px rgba(0, 0, 0, 0.55);
    transition: left 0.25s ease, top 0.25s ease, width 0.25s ease, height 0.25s ease;
    display: none;
}

#tourBubble {
    position: fixed;
    z-index: 6802;
    width: min(90vw, 320px);
    background: #ffffff;
    border: 1px solid #c8c8c8;
    border-radius: 10px;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25);
    padding: 16px 18px 14px;
    text-align: left;
    pointer-events: auto;
}

.tour-skip {
    position: absolute;
    top: 6px;
    right: 8px;
    width: 26px;
    height: 26px;
    line-height: 1;
    padding: 0;
    border: none;
    background: transparent;
    color: #888;
    font-size: 20px;
    cursor: pointer;
    border-radius: 6px;
}

.tour-skip:hover {
    background: #f0f0f0;
    color: #333;
}

.tour-bubble-step {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: #a667e4;
    margin-bottom: 4px;
}

.tour-bubble-title {
    margin: 0 0 6px;
    font-size: 1.05rem;
    color: var(--pix-ui-ink);
}

.tour-bubble-body {
    margin: 0 0 12px;
    font-size: 0.9rem;
    line-height: 1.5;
    color: #444;
}

.tour-tool-label {
    position: fixed;
    z-index: 6802;
    pointer-events: none;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--pix-ui-ink);
    background: #ffffff;
    border: 1px solid #d9ccf2;
    border-radius: 999px;
    padding: 2px 10px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    white-space: nowrap;
}

.tour-more {
    margin: 0 0 12px;
    font-size: 0.84rem;
    line-height: 1.5;
    color: #555;
}

.tour-more[hidden] {
    display: none;
}

/* One-line "click the highlighted control" cue shown on click-guided action steps. */
.tour-click-hint {
    margin: 0 0 12px;
    font-size: 0.84rem;
    line-height: 1.5;
    font-weight: 600;
    color: #7b5ea7;
}

.tour-click-hint[hidden] {
    display: none;
}

.tour-link {
    color: #7b5ea7;
    text-decoration: underline;
    cursor: pointer;
}

.tour-link:hover {
    color: #6b4f97;
}

.tour-bubble-nav {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

.tour-bubble-nav button {
    padding: 6px 14px;
    font-size: 0.88rem;
    font-weight: 600;
    border-radius: 8px;
    cursor: pointer;
    border: 1px solid #c8c8c8;
    background: #f7f7f7;
    color: #333;
}

.tour-bubble-nav .tour-back[hidden],
.tour-bubble-nav .tour-next[hidden],
.tour-bubble-nav .tour-stop[hidden],
.tour-bubble-nav .tour-skip-step[hidden],
.tour-bubble-nav .tour-skip-text[hidden] {
    display: none;
}

.tour-bubble-nav .tour-back:hover,
.tour-bubble-nav .tour-stop:hover {
    background: #efefef;
}

.tour-bubble-nav .tour-skip-text,
.tour-bubble-nav .tour-skip-step {
    border-color: transparent;
    background: transparent;
    color: #888;
}

.tour-bubble-nav .tour-skip-text:hover,
.tour-bubble-nav .tour-skip-step:hover {
    background: #f0f0f0;
    color: #555;
}

.tour-bubble-nav .tour-next {
    border-color: #7b5ea7;
    background: #7b5ea7;
    color: #ffffff;
}

.tour-bubble-nav .tour-next:hover {
    background: #6b4f97;
}

/* Arrow pointing from the bubble toward the highlighted element. */
.tour-arrow {
    position: absolute;
    width: 14px;
    height: 14px;
    background: #ffffff;
    border: 1px solid #c8c8c8;
    transform: rotate(45deg);
}

#tourBubble[data-placement="bottom"] .tour-arrow {
    top: -8px;
    border-right: none;
    border-bottom: none;
}

#tourBubble[data-placement="top"] .tour-arrow {
    bottom: -8px;
    border-left: none;
    border-top: none;
}

#tourBubble[data-placement="right"] .tour-arrow {
    left: -8px;
    border-top: none;
    border-right: none;
}

#tourBubble[data-placement="left"] .tour-arrow {
    right: -8px;
    border-bottom: none;
    border-left: none;
}

#tourBubble[data-placement="center"] .tour-arrow {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    #tourSpotlight {
        transition: none;
    }
}

/* Bubble parts the FAQ runs hide (no per-step title / no counter on one-step flows). */
.tour-bubble-step[hidden],
.tour-bubble-title[hidden] {
    display: none;
}

/* ===== FAQ guide ("How do I…?") — the reworked #tutorialModal ===== */
/* Doubled selector: the generic `.modal` width rule sits late in this file and would
   win over a single class at equal specificity (see the CSS gotcha in CLAUDE.md). */
.modal.faq-modal {
    width: min(94vw, 880px);
    max-height: min(88vh, 760px);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
}

.faq-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-shrink: 0;
}

.faq-header h2 {
    margin: 0;
    font-size: 1.3rem;
    color: var(--pix-ui-ink);
}

.faq-close {
    border: none;
    background: transparent;
    font-size: 24px;
    line-height: 1;
    padding: 2px 10px;
    border-radius: 8px;
    color: var(--pix-ui-ink-soft);
    cursor: pointer;
}

.faq-close:hover {
    background: rgba(var(--pix-ui-edge), 0.14);
    color: var(--pix-ui-ink);
}

.faq-intro {
    margin: 4px 0 12px;
    font-size: 0.9rem;
    color: #5c5a48;
    flex-shrink: 0;
}

/* The scroll host for both pages (featured questions / all sections). */
.faq-list {
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
    padding-right: 6px;
}

/* First page: a compact grid of the most-asked questions, then one card per section. */
.faq-featured-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 2px 24px;
    margin-bottom: 18px;
}

/* The written guide (a PDF), at the very top of the guide's first page. It is a LINK, not
   a question, so it is drawn as a filled lilac banner rather than as one of the pale cards
   below it: everything under it teaches one click, this one hands over the whole book. It
   exists only in a language that has an edition written (see PixlaineGuide in
   shared-utils.js), so most readers never see this rule. */
.faq-guide-card {
    display: flex;
    align-items: center;
    gap: 13px;
    text-decoration: none;
    color: var(--pix-ui-ink);
    background: linear-gradient(135deg, #f7ecff 0%, #efe0fb 100%);
    border: 1px solid rgba(166, 103, 228, 0.55);
    border-left: 4px solid var(--pix-ui-accent);
    border-radius: 14px;
    padding: 12px 14px;
    margin: 0 0 18px;
    box-shadow: 0 3px 12px rgba(90, 50, 140, 0.10);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.faq-guide-card:hover {
    box-shadow: 0 9px 22px rgba(90, 50, 140, 0.18);
    transform: translateY(-1px);
}

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

/* The mark sits on its own cream tile, like the section cards' thumbnails below: the
   Pixlaine logo is dark line work and disappears straight onto the lilac banner. */
.faq-guide-icon {
    flex: 0 0 auto;
    width: 52px;
    height: 52px;
    object-fit: contain;
    background: #fffdf6;
    border: 1px solid rgba(var(--pix-ui-edge), 0.28);
    border-radius: 11px;
    padding: 6px;
    box-shadow: 0 1px 4px rgba(90, 50, 140, 0.10);
}

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

.faq-guide-title {
    font-weight: 700;
    font-size: 1rem;
    color: #5a3480;
}

.faq-guide-sub {
    font-size: 0.82rem;
    color: #5c5a48;
    line-height: 1.3;
}

/* Small label introducing the section cards. */
.faq-cards-heading {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--pix-ui-accent);
    margin: 0 0 10px 2px;
}

/* The five section cards wrap into as many rows as needed (two on a wide modal). */
.faq-section-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 12px;
}

/* One section card: a picture, the section title and a short description, tinted
   lilac so the row of cards reads as its own distinct area. */
.faq-section-card {
    display: flex;
    align-items: center;
    gap: 13px;
    text-align: left;
    cursor: pointer;
    font: inherit;
    color: var(--pix-ui-ink);
    background: linear-gradient(135deg, #fffdf6 0%, #f3e6fb 100%);
    border: 1px solid rgba(166, 103, 228, 0.40);
    border-radius: 14px;
    padding: 12px 14px;
    box-shadow: 0 3px 12px rgba(90, 50, 140, 0.09);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.faq-section-card:hover {
    box-shadow: 0 9px 22px rgba(90, 50, 140, 0.17);
    transform: translateY(-1px);
}

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

/* The picture: crisp pixel art / icon on a small cream tile. */
.faq-section-card-thumb {
    flex: 0 0 auto;
    width: 52px;
    height: 52px;
    object-fit: contain;
    image-rendering: pixelated;
    background: #fffdf6;
    border: 1px solid rgba(var(--pix-ui-edge), 0.28);
    border-radius: 11px;
    padding: 6px;
    box-shadow: 0 1px 4px rgba(90, 50, 140, 0.10);
}

.faq-section-card-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.faq-section-card-title {
    font-weight: 700;
    font-size: 1rem;
    color: #5a3480;
}

.faq-section-card-sub {
    font-size: 0.82rem;
    color: #5c5a48;
    line-height: 1.3;
}

.faq-section-card-chevron {
    margin-left: auto;
    color: #a667e4;
    font-weight: 700;
    font-size: 1.4rem;
    flex: 0 0 auto;
}

/* Section page: back link + that section's title + its list of questions. */
.faq-back {
    display: inline-flex;
    align-items: center;
    background: transparent;
    border: none;
    cursor: pointer;
    font: inherit;
    font-weight: 700;
    color: var(--pix-ui-accent);
    padding: 4px 6px;
    margin-bottom: 8px;
}

.faq-back:hover {
    color: #5c6b16;
}

.faq-section-page-title {
    margin: 2px 0 10px;
    font-size: 1.15rem;
    color: var(--pix-ui-ink);
}

.faq-section-questions {
    column-width: 330px;
    column-gap: 28px;
}

.faq-q {
    display: flex;
    width: 100%;
    align-items: center;
    gap: 8px;
    text-align: left;
    background: transparent;
    border: none;
    border-radius: 8px;
    padding: 6px 8px;
    font: inherit;
    font-size: 0.92rem;
    line-height: 1.35;
    color: var(--pix-ui-ink);
    cursor: pointer;
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
}

.faq-q:hover {
    background: rgba(var(--pix-ui-edge), 0.13);
}

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

.faq-q-label {
    flex: 1 1 auto;
}

.faq-q-chevron {
    color: #a667e4;
    font-weight: 700;
    font-size: 1.05rem;
    flex: 0 0 auto;
}

/* == Achievements (achievements.js): profile section, modals, congrats toast ============ */
.ach-toast {
    position: fixed;
    left: 0;
    right: 0;
    margin: 0 auto;
    bottom: 24px;
    z-index: 6500;
    display: flex;
    align-items: center;
    gap: 14px;
    width: fit-content;
    max-width: min(92vw, 400px);
    padding: 16px 18px;
    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.94);
    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.85); }
    55%  { transform: translateY(-5px) scale(1.05); }
    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 profile page (5500), below the sync-conflict modal (6100). */
.modal-backdrop.ach-backdrop { z-index: 6000; }
.modal-backdrop.ach-backdrop-group { z-index: 6050; }

/* Doubled selector: outranks the generic late .modal width rule (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): "you / everyone have crocheted this much yarn" ──────── */
/* Desktop mounts the toolbar variant into #yarnMeter, injected after the Reference menu.
   Not a button, so it echoes the toolbar buttons' cream/olive chip without their hover. */
/* IN the flex row, between the left button group and the right cluster, with symmetric auto
   margins so the free space splits evenly either side of it.
   It used to be absolutely centred on the toolbar, which put it at the true middle of the
   screen but took it OUT of the flow -- so it reserved no space and the neighbours could not
   see it. The right cluster is anchored to the right edge, so every label added to it (the
   Supporter Pack pill) or every longer translation (French "Comment faire ?") grew that
   cluster LEFTWARDS until it ran straight over the meter. A width media query cannot fix that
   class of bug either, since --ui-scale resizes every button independently of the viewport.
   Being in the flow makes the overlap structurally impossible; the cost is that the meter is
   centred between the two clusters rather than on the screen, which is only a visible
   difference when they are very uneven. */
#yarnMeter {
    display: flex;
    align-items: center;
    margin-left: auto;
    margin-right: auto;
}

.yarn-meter-toolbar {
    display: flex;
    align-items: center;
    gap: calc(8px * var(--ui-scale));
    padding: calc(6px * var(--ui-scale)) calc(12px * var(--ui-scale));
    font-size: calc(0.86rem * var(--ui-scale));
    transform: translateY(calc(-4px * var(--ui-scale)));
    color: var(--pix-ui-ink);
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 0.7) 0%, rgba(var(--pix-ui-face-2), 0.6) 100%);
    border: 1px solid rgba(var(--pix-ui-edge), 0.55);
    border-radius: 10px;
    white-space: nowrap;
    cursor: default;
}

.yarn-meter-toolbar .yarn-meter-row { display: inline-flex; align-items: baseline; gap: 5px; }
.yarn-meter-toolbar .yarn-meter-key { opacity: 0.72; }
.yarn-meter-toolbar .yarn-meter-val { font-weight: 700; font-variant-numeric: tabular-nums; }
.yarn-meter-toolbar .yarn-meter-lead { font-weight: 600; }

/* The toolbar is tight on narrow desktops; the meter is the first thing that may go.
   With it gone the right cluster loses what was pushing it right, so .tutorial-menu takes
   the auto margin back over. */
@media (max-width: 1100px) {
    #yarnMeter { display: none; }
    .tutorial-menu { margin-left: auto; }
}

/* ═══ Mosaic crochet drawing mode (script-mosaic.js) ═════════════════════════════════════
   A mode, not a tool: while body carries .mosaic-mode the editor is narrowed to what mosaic
   crochet can work. The list is deliberately SHORT - the palette panel stays, because it is
   where a new colour is chosen before being drawn onto the row guide, and so does
   Resize/Pixelise, whose SIZE half a mosaic goes through unchanged. */

/* Embroidery is not crocheted, so a mosaic pattern would never show it. */
body.mosaic-mode .cross-stitch-toggle {
    display: none;
}

/* Resize/Pixelise keeps its Size half and loses its COLOURS half: a mosaic is already exactly
   two colours, so there is nothing to reduce - and a reduction, black & white above all, would
   leave the grid holding a colour the mode refuses to draw with. applyResizePixelise skips the
   remap in the mode too, so this is what it looks like rather than what enforces it. */
body.mosaic-mode #rpColoursSection,
body.mosaic-mode #pixelisePaletteWrap {
    display: none;
}

/* The row guide: one band per row of the picture, in the colour that row is worked in. Every
   number that places it comes from JS (mosaicLayoutColumn), measured off the canvas's own
   rect so the bands stay level with the stitches through a zoom and a pan - which is why
   there is no `left`/`top` here. overflow:hidden is the clip: the strip inside is as tall as
   the whole picture and slides under this box as the viewport scrolls.
   Narrow on purpose: the desktop gutter between the canvas viewport and the preview panel is
   about 30px, and 24 + a 6px gap is what fits it. z-index matches the zoom buttons on the
   other side, for the same reason - the lilac panel (35) must not bury it. */
.mosaic-column {
    position: absolute;
    z-index: 36;
    width: 24px;
    overflow: hidden;
    border-radius: 3px;
    /* An outline rather than a border: a border is inside the box and would push the bands
       a pixel off the rows they belong to. */
    box-shadow: 0 0 0 1px rgba(var(--pix-ui-edge), 0.5);
    cursor: pointer;
}

.mosaic-column[hidden] { display: none; }

.mosaic-column-canvas {
    position: absolute;
    left: 0;
    width: 100%;
    display: block;
    /* The generic `canvas` rule above gives every canvas a 1px black border and auto side
       margins. Both are for the picture; here a border would make the strip two pixels
       taller than the height JS gives it and push every band a pixel off its row. */
    border: 0;
    margin: 0;
    /* The bands are a nearest-neighbour upscale of a 1 x rows bitmap - the same one the
       picture itself goes through, which is what lands them ON the stitch rows. */
    image-rendering: pixelated;
}

/* Inert = no tool here takes a colour from the guide (anything but the hook and the pipette).
   Shown rather than silently swallowed: a control that does nothing should look like it does
   nothing. It still READS as the row guide, so it only goes half pale. */
.mosaic-column.inert {
    cursor: default;
}

.mosaic-column.inert .mosaic-column-canvas {
    opacity: 0.5;
}

/* One pill floating at the TOP of the picture: what a shape made from a single click is
   waiting for (the line's second end) or what can still be done with it (drag it, and
   anything else puts it down). Same shape as the mosaic pills below, in the app's own chrome
   because it is a direction and not a fault. Never in the flow, so it cannot move the canvas,
   and never clickable - the answer to it is always given on the picture itself. */
.shape-prompt {
    position: absolute;
    left: 50%;
    top: 10px;
    transform: translateX(-50%);
    z-index: 37;
    margin: 0;
    max-width: 88%;
    padding: 5px 12px;
    box-sizing: border-box;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.35;
    text-align: center;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.97);
    border: 1px solid rgba(var(--pix-ui-edge), 0.6);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.14);
    pointer-events: none;
}

.shape-prompt[hidden] { display: none; }

/* One pill floating under the picture, over the empty part of the viewport: the standing
   count of stitches that cannot be worked, and - in .nudge - the passing word that answers a
   click the two-colour lock refused. Never in the flow, so it cannot move the canvas. */
.mosaic-warning {
    position: absolute;
    left: 50%;
    bottom: 10px;
    transform: translateX(-50%);
    z-index: 37;
    margin: 0;
    max-width: 88%;
    padding: 5px 12px;
    box-sizing: border-box;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.35;
    text-align: center;
    color: #7a1414;
    background: rgba(255, 236, 236, 0.96);
    border: 1px solid rgba(214, 40, 40, 0.5);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.14);
    pointer-events: none;
}

/* Not a fault, just a direction: same pill, the app's own chrome colours. */
.mosaic-warning.nudge {
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.97);
    border-color: rgba(var(--pix-ui-edge), 0.6);
}

.mosaic-warning[hidden] { display: none; }

/* The standing note about an imported picture, riding just above the count pill. Deliberately
   NOT the warning's red: it is an observation about what was brought in, not a fault in it -
   the marks and the count below already say what cannot be worked. */
.mosaic-unlikely {
    position: absolute;
    left: 50%;
    bottom: 44px;
    transform: translateX(-50%);
    z-index: 37;
    margin: 0;
    max-width: 88%;
    padding: 5px 12px;
    box-sizing: border-box;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.35;
    text-align: center;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.97);
    border: 1px solid rgba(var(--pix-ui-edge), 0.6);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.14);
    pointer-events: none;
}

.mosaic-unlikely[hidden] { display: none; }

/* The "Start from a project" picker. The generic .modal rule sits late in this file, so the
   width needs the doubled selector to outrank it (and box-sizing, since .modal is
   content-box). */
.modal.mosaic-project-modal {
    width: min(94vw, 560px);
    box-sizing: border-box;
}

.mosaic-project-note {
    margin: 0 0 10px;
    font-size: 0.82rem;
    line-height: 1.4;
    color: #666;
}

.mosaic-project-list {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-content: flex-start;
    max-height: min(52vh, 420px);
    overflow-y: auto;
    padding: 2px;
}

.mosaic-project-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: 118px;
    padding: 8px;
    background: #fafafa;
    border: 1px solid #e2e2e2;
    border-radius: 10px;
    cursor: pointer;
    text-align: center;
    font: inherit;
}

.mosaic-project-card:hover {
    border-color: #a667e4;
    background: #fff;
}

.mosaic-project-thumb {
    width: 100%;
    height: 86px;
    object-fit: contain;
    /* Thumbs are one pixel per stitch, so they scale UP; pixelated keeps that crisp. */
    image-rendering: pixelated;
    background: #f0f0f0;
    border-radius: 6px;
}

.mosaic-project-thumb--empty {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    color: #bbb;
}

.mosaic-project-name {
    font-size: 12px;
    font-weight: 600;
    word-break: break-word;
}

.mosaic-project-meta {
    font-size: 11px;
    color: #777;
}

.mosaic-project-message {
    flex-basis: 100%;
    margin: 0;
    padding: 10px 2px;
    font-size: 0.85rem;
    color: #777;
}

/* The Fix button, on the picture's top-right corner (placed by mosaicLayoutFix, hence no
   `right`/`top` here). It exists only while there is something to fix, so it is allowed to
   sit over the picture: it is the answer to the marks underneath it. */
.mosaic-fix {
    position: absolute;
    z-index: 38;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    color: #7a1414;
    background: rgba(255, 236, 236, 0.97);
    border: 1px solid rgba(214, 40, 40, 0.55);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
}

.mosaic-fix:hover {
    background: #fff;
    border-color: #d62828;
}

.mosaic-fix[hidden] { display: none; }

/* The same ring-and-slash the overlay draws over an impossible stitch, in the same red, so
   the button points at the marks by wearing one. Drawn rather than written: no font glyph is
   that mark on every machine, and an emoji would be a different drawing beside its own. */
.mosaic-fix-mark {
    position: relative;
    flex: none;
    width: 12px;
    height: 12px;
    border: 2px solid #d62828;
    border-radius: 50%;
    box-sizing: border-box;
}

.mosaic-fix-mark::after {
    content: '';
    position: absolute;
    left: 50%;
    top: -2px;
    bottom: -2px;
    width: 2px;
    margin-left: -1px;
    background: #d62828;
    /* The overlay's slash runs bottom-left to top-right; so does this one. */
    transform: rotate(45deg);
}

/* ═══ The minimal look (backgrounds/bg-minimal.js) ═══════════════════════════════════════
   The one design that rearranges the editor instead of only repainting it. It ships no art
   at all - every --pix-bg-* image resolves to `none` - so without this block the three band
   elements would be invisible boxes and the eleven tool buttons flat rectangles scattered
   down an empty strip at the positions the shipped flowers occupy. The engine writes
   data-pix-layout="minimal" onto <html> when it is the chosen look; nothing else knows.

   Three columns and one row of buttons, and it is paid for by the yarn meter:
     top      .bg-brown, flattened. Both upper rows merge into it: the meter goes and the
              Pattern Gallery / New Project / My Projects / Row by Row buttons move up into
              the gap it leaves, still centred on the screen, at the toolbar's own size. Four
              buttons plus two menus is more than that line holds in French, so this look also
              shortens two labels and folds New Project + My Projects into one Projects menu
              (see "One line, not two" below, and the breakpoint under it - below 1360px the
              two toolbar clusters would still reach them, so they drop back to their own
              line).
     left     .left-vine-panel, flattened, its eleven absolutely-positioned tool blooms
              re-laid as a two-column grid of small squares wearing the app's own chrome.
              Grid + the size slider sit at the foot of it.
     right    .bg-yellow, flattened and stood on end, under the preview panel.
   Every colour here comes from the chrome (--pix-ui-*), which is what the Buttons row in the
   picker sets - so this design needs no colourways of its own beyond a light page and a dark
   one, which is exactly what bg-minimal.js registers. */

html[data-pix-layout="minimal"] {
    /* One flat wash and one line, both from the chrome's edge colour: a mid-tone in every
       style, so the same two values read as a quiet column over a pale page AND over a
       near-black one. That is what lets the light and the dark variant share this block. */
    --pix-min-fill: rgba(var(--pix-ui-edge), 0.16);
    --pix-min-line: rgba(var(--pix-ui-edge), 0.42);
    /* The left column's own pixel space: it scales itself (transform below), so these are
       unscaled. Two 44px buttons and the gap between them, plus the padding either side. */
    --pix-min-btn: 44px;
    --pix-min-gap: 8px;
    --pix-min-pad: 10px;
    --pix-min-col-left: calc(var(--pix-min-btn) * 2 + var(--pix-min-gap) + var(--pix-min-pad) * 2);
    /* The right column holds the 286px preview panel with a little air either side. */
    --pix-min-col-right: calc(300px * var(--ui-scale));

    /* One toolbar row instead of two, so the band carrying it shrinks and the canvas gets
       the difference. */
    --brown-band-height: calc(58px * var(--ui-scale));
    --canvas-below-brown: calc(10px * var(--ui-scale));
    /* Symmetric, so the canvas stays centred on the screen: the right column is the wider of
       the two, and the left one then simply has room to spare. */
    --canvas-side-reserve: calc(306px * var(--ui-scale));
    /* There is no vines band here, and saying so is what keeps account-cloud.js's public
       profile page right: it computes its top padding from these two, as an INLINE style, so
       no rule of ours could reach it - but a var it reads can. "At the top offset, no
       height" is both the truth and the number that page needs (it starts just under the top
       band instead of 400px down an empty one). Every other reader of the two - the vines
       band itself, the left panel, the tool panel - is overridden outright below. */
    --yellow-vines-top: var(--canvas-top-offset);
    --yellow-vines-height: 0px;
}

/* ── The three columns ─────────────────────────────────────────────────────────────────── */

html[data-pix-layout="minimal"] .bg-brown {
    background-image: none;
    background-color: var(--pix-min-fill);
    border-bottom: 1px solid var(--pix-min-line);
    box-sizing: border-box;
}

/* The vines band, stood on end at the other side of the screen. Reusing it rather than
   adding an element keeps this design out of the HTML: it is a stylesheet and a short
   registration, nothing more. */
html[data-pix-layout="minimal"] .bg-yellow {
    /* Flush with the bottom edge of the top band, not with the canvas offset: the offset
       leaves --canvas-below-brown of air under the band so the drawing never touches it, and
       a column starting there would float with a stripe of page showing between the two. The
       three columns are meant to read as one frame. */
    top: var(--brown-band-height);
    bottom: 0;
    left: auto;
    right: 0;
    width: var(--pix-min-col-right);
    height: auto;
    background-image: none;
    background-color: var(--pix-min-fill);
    border-left: 1px solid var(--pix-min-line);
    box-sizing: border-box;
}

html[data-pix-layout="minimal"] .left-vine-panel {
    left: 0;
    /* Flush with the top band, same as the right column - see .bg-yellow above. */
    top: var(--brown-band-height);
    /* The panel scales itself and everything in it is unscaled px, the way .tool-panel and
       .preview-panel work; dividing by the scale makes the VISUAL height the viewport's. */
    height: calc((100vh - var(--brown-band-height)) / var(--ui-scale));
    max-height: none;
    width: var(--pix-min-col-left);
    transform: scale(var(--ui-scale));
    transform-origin: top left;
    box-sizing: border-box;
    padding: var(--pix-min-pad);
    background-image: none;
    background-color: var(--pix-min-fill);
    border-right: 1px solid var(--pix-min-line);
    /* The shipped panel is a positioning context for eleven absolute blooms, laid out
       right-to-left so its scrollbar sits on the outside. Here it is an ordinary grid. */
    direction: ltr;
    line-height: normal;
    display: grid;
    grid-template-columns: repeat(2, var(--pix-min-btn));
    gap: var(--pix-min-gap);
    justify-content: center;
    align-content: start;
}

/* ── The tool buttons ──────────────────────────────────────────────────────────────────── */

/* Two to a line, wearing the same face as every other button in the app: there is no bloom
   to sit on, and a design with no art of its own has no business inventing a button style. */
html[data-pix-layout="minimal"] .background-lila-button {
    /* relative, NOT static: the icon inside is position:absolute, and with the button out of
       the way its containing block becomes the fixed PANEL - which parks all eleven icons on
       top of each other in the middle of the column. `inset: auto` then has to come with it,
       because the per-slot rules place each button on the band with left/top and those turn
       into RELATIVE offsets the moment it stops being absolute - scattering the grid. */
    position: relative;
    inset: auto;
    width: var(--pix-min-btn);
    height: var(--pix-min-btn);
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    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.75);
    border-radius: 10px;
    transition: background 140ms ease, border-color 140ms ease;
}

/* Move is the odd one out of eleven: nine tools pair up, then undo and redo pair up, and
   this is what stops a hole opening beside it. It also fences the two actions off from the
   tools, which is worth having anyway. */
html[data-pix-layout="minimal"] .background-lila-button.lila-9 {
    grid-column: 1 / -1;
    width: auto;
}

/* The sprite face and its alpha mask - the whole mechanism the shipped buttons are drawn by.
   There is no sprite here, so both layers go and the states below are plain CSS. */
html[data-pix-layout="minimal"] .background-lila-button::before,
html[data-pix-layout="minimal"] .background-lila-button::after {
    display: none;
}

html[data-pix-layout="minimal"] .background-lila-button:hover:not(:disabled) {
    background: linear-gradient(180deg, rgba(var(--pix-ui-face), 1) 0%, rgba(var(--pix-ui-face-2), 0.94) 100%);
    border-color: rgba(var(--pix-ui-edge), 0.98);
}

html[data-pix-layout="minimal"] .background-lila-button:focus-visible {
    outline: 2px solid var(--pix-ui-accent);
    outline-offset: 2px;
}

/* Selected. Deliberately a TINT plus a heavier edge rather than the dark pressed face the
   rest of the app uses: the icons are fixed-colour pixel art, so the face under them has to
   stay in the same range in both states, or the selected tool is the one whose glyph stops
   reading. */
html[data-pix-layout="minimal"] .background-lila-button.active {
    background: rgba(var(--pix-ui-edge), 0.3);
    border: 2px solid var(--pix-ui-accent);
    box-shadow: none;
}

/* The shipped rule keeps undo/redo at full strength when disabled, so their arrow never
   looks faded on the bloom. A flat button with nothing behind it has to say so itself. */
html[data-pix-layout="minimal"] .background-lila-button:disabled {
    opacity: 0.4;
    filter: none;
}

/* Every icon centred in its square, upright and one size. !important is doing two jobs here
   and neither is avoidable: the rules that place these on the shipped bloom are id-based
   (#backgroundLila4Button .lila-btn-icon) and so outrank any class selector, and cosmetics.js
   writes the same properties INLINE on a user-drawn icon. */
html[data-pix-layout="minimal"] .lila-btn-icon {
    top: 50% !important;
    left: 50% !important;
    width: 26px !important;
    height: 26px !important;
    margin-top: 0 !important;
    transform: none !important;
    translate: -50% -50% !important;
    scale: 1 !important;
    object-fit: contain;
}

/* The undo/redo step counters, moved off the face onto the corner of it. */
html[data-pix-layout="minimal"] .lila-count {
    top: -5px;
    right: -5px;
}

/* The cross-stitch switch and its Symmetry neighbour keep their job - small toggles above
   the tools, not tools - but take the full width of the column, the only place they fit at
   this size, so the button grid below starts under both of them. */
html[data-pix-layout="minimal"] .cross-stitch-toggle,
html[data-pix-layout="minimal"] .symmetry-toggle {
    position: static;
    grid-column: 1 / -1;
    width: auto;
    padding: 3px 6px;
    margin-bottom: 2px;
    font-size: 0.56rem;
    color: var(--pix-ui-ink);
    background: rgba(var(--pix-ui-face), 0.85);
    border-color: rgba(var(--pix-ui-edge), 0.6);
}

html[data-pix-layout="minimal"] .cross-stitch-toggle:hover:not(:disabled),
html[data-pix-layout="minimal"] .symmetry-toggle:hover:not(:disabled) {
    background: rgba(var(--pix-ui-face), 1);
    border-color: rgba(var(--pix-ui-edge), 0.95);
}

/* The lit state has to be restated here: the rule above is an attribute AND a class, so it
   outranks the plain .active one and would leave a switched-on mode looking switched off -
   which for drawing live means the pencil quietly mirrors with nothing on screen saying so. */
html[data-pix-layout="minimal"] .cross-stitch-toggle.active,
html[data-pix-layout="minimal"] .symmetry-toggle.active {
    background: #a83b6b;
    border-color: #a83b6b;
    color: #fff3f8;
}

/* ── Grid + the size slider, at the foot of the left column ────────────────────────────── */

/* Anchored to the BOTTOM rather than measured off the button grid: that grid's height
   depends on --ui-scale and on whether the cross-stitch switch is showing, and a number
   derived from those would be wrong the moment either changed. */
html[data-pix-layout="minimal"] .tool-panel {
    left: 0;
    top: auto;
    bottom: calc(12px * var(--ui-scale));
    width: var(--pix-min-col-left);
    box-sizing: border-box;
    padding: 0 var(--pix-min-pad);
    align-items: center;
    gap: 5px;
    transform: scale(var(--ui-scale));
    transform-origin: left bottom;
}

/* The accent follows the CHROME here, not the design's own selected-bloom colour: this look
   has no blooms, and the Buttons row is where its colour comes from. */
html[data-pix-layout="minimal"] .tool-panel input[type="range"] {
    accent-color: var(--pix-ui-accent);
    height: 110px;
}

html[data-pix-layout="minimal"] .tool-panel input[type="range"]::-webkit-slider-thumb {
    background: var(--pix-ui-accent);
}

html[data-pix-layout="minimal"] .tool-panel input[type="range"]::-moz-range-thumb {
    background: var(--pix-ui-accent);
}

/* ── The top row ───────────────────────────────────────────────────────────────────────── */

/* The meter is what pays for the merge: it sits in the middle of the toolbar's flow, which
   is exactly where the three buttons below it are going. With it gone the right-hand cluster
   loses what was pushing it right, so it takes the auto margin back - the same fallback the
   shipped layout keeps for a page where the meter never mounted, restated because the
   element is still in the DOM here and that rule's :has() would still find it. */
html[data-pix-layout="minimal"] #yarnMeter {
    display: none;
}

html[data-pix-layout="minimal"] .tutorial-menu {
    margin-left: auto;
}

html[data-pix-layout="minimal"] #examplePatternsBar {
    top: calc(5px * var(--ui-scale));
    height: calc(50px * var(--ui-scale));
}

/* The bar already carries the toolbar's own padding and font size, so lifting these the same
   4px the toolbar lifts its buttons is the whole of it: same box, same baseline, one row of
   buttons rather than two sizes sharing a line. It costs width, which is what the breakpoint
   below is for. The Projects menu is lifted as a WRAPPER, not through its button, so its
   dropdown travels with the trigger instead of hanging 4px below it. */
html[data-pix-layout="minimal"] #examplePatternsBar > button,
html[data-pix-layout="minimal"] #examplePatternsBar > #projectBarMenu {
    transform: translateY(calc(-4px * var(--ui-scale)));
}

/* Merging the rows put #examplePatternsBar (z 39, top 5px, 50px tall) INSIDE .toolbar's own
   box (z 40, top 5px, 50px tall, full width). The toolbar is transparent to the eye across
   its empty middle - the yarn meter that used to fill it is hidden here - but it was never
   transparent to the MOUSE, so it swallowed every click aimed at the four buttons showing
   through it. (Shipped that way: the merged row has looked right and been dead since the look
   went out.) The bar cannot simply be raised above it - that would put its stacking context
   over the toolbar's own dropdowns - so the toolbar gives the clicks back the same way the bar
   already does: none on the container, auto on the children, which reaches their dropdowns by
   inheritance. Nothing else changes, paint order least of all. */
html[data-pix-layout="minimal"] .toolbar {
    pointer-events: none;
}

html[data-pix-layout="minimal"] .toolbar > * {
    pointer-events: auto;
}

/* ── One line, not two ─────────────────────────────────────────────────────────────────── */

/* Merging the two upper rows (above) put the bar's four buttons on the toolbar's line; what
   follows is what makes them FIT there in FRENCH, which is where this is decided - the
   English labels were never the problem - so the merge holds down to an ordinary laptop
   rather than only on a wide screen.

   Pattern Gallery and How do I ? print their short label instead of their full one. Both
   strings are in i18n.js and both are in the markup - this only chooses which shows - so a
   translator never meets a label that exists in CSS. (In English the short form of How do I ?
   is the same words; the row it is on is the French one that had to give.) */
html[data-pix-layout="minimal"] .bar-label-full {
    display: none;
}

html[data-pix-layout="minimal"] .bar-label-min {
    display: inline;
}

/* ...and New Project + My Projects become one "Projects" menu. They are two buttons wide and
   one dropdown narrow, which is the bigger half of the saving. Both originals stay in the
   DOM: #openMyProjects is built by project-ui.js and #openPictureBarButton is what the tour
   and the FAQ flows point at, so the menu's own entries simply forward to them
   (script-events.js) rather than taking their jobs over. */
html[data-pix-layout="minimal"] #projectBarMenu {
    display: block;
}

html[data-pix-layout="minimal"] #examplePatternsBar > #openPictureBarButton,
html[data-pix-layout="minimal"] #examplePatternsBar > #openMyProjects {
    display: none;
}

/* ── The right column ──────────────────────────────────────────────────────────────────── */

/* Pulled in from the 24px the shipped layout leaves, so the panel sits centred in its column
   rather than against the screen edge. */
html[data-pix-layout="minimal"] .preview-panel {
    margin-right: calc(7px * var(--ui-scale));
}

/* The spacer the floating account launcher is centred in: account-cloud.js measures the gap
   between the toolbar and the preview CANVAS and sizes the avatar to it, so this row is the
   only thing standing between a rearranged top band and an avatar a different size from the
   one every other look shows. It absorbs the whole difference: 40px (the shipped row) plus
   93px (the shipped --brown-band-height + --canvas-below-brown, scaled as they are) minus
   whatever offset this look is actually running at - so the preview canvas lands exactly
   where it always does, at either of our two band heights, without a second number to keep. */
html[data-pix-layout="minimal"] .preview-header-row {
    height: calc(40px + 93px * var(--ui-scale) - var(--canvas-top-offset));
}

/* ── Narrow desktops ───────────────────────────────────────────────────────────────────── */

/* --ui-scale is 1 at and below 1920px wide, so the toolbar's two clusters are a fixed width
   here and the middle is simply what is left of the window. Measured in French, the longer of
   the two languages and so the one that decides this: the centre used to reach the left-hand
   cluster at ~1520px, and the merged Projects menu plus the two short labels take about 200px
   off it, so the one-line arrangement now holds to ~1300. Below 1360 - a little clearance,
   since the number depends on the font the browser actually has - they go back to a line of
   their own and the top band grows to hold both rows, which is the shipped arrangement minus
   the art. A window narrower than that is not a broken layout, just a two-row one. */
@media (max-width: 1360px) {
    html[data-pix-layout="minimal"] {
        --brown-band-height: calc(90px * var(--ui-scale));
    }
    html[data-pix-layout="minimal"] #examplePatternsBar {
        top: calc(55px * var(--ui-scale));
        height: calc(34px * var(--ui-scale));
    }
    html[data-pix-layout="minimal"] #examplePatternsBar > button,
    html[data-pix-layout="minimal"] #examplePatternsBar > #projectBarMenu {
        transform: none;
    }
}

/* ── The other two surfaces that borrow the tool band ──────────────────────────────────── */

/* account-cloud.js builds the signed-in PROFILE page's tool menu out of the same band and the
   same button class, and cosmetics.js's Customize panel previews the editor's band by CLONING
   it. Both inherit everything above, which is right - they are the same buttons - but each
   needs one thing said about it. */

/* The profile page has at most six tools where the band has eleven slots, and fills the rest
   with inert sprites so the drawn band reads as a band rather than a half-picked bouquet.
   There is no drawn band here and so nothing to fill: without this they are empty squares. */
html[data-pix-layout="minimal"] .background-lila-button.lila-decor {
    display: none;
}

/* ...and with the fillers gone, a profile tool is an icon AND a written label, which does not
   fit a 44px square. That column goes to ONE tile per line instead, wide enough to read.
   :has(.lila-tool-icon) is what tells the two bands apart: a profile tool carries its icon in
   a span (it may be an emoji, a glyph or an avatar), the editor's in an <img class=
   "lila-btn-icon">. */
html[data-pix-layout="minimal"] .left-vine-panel:has(.lila-tool-icon) {
    grid-template-columns: minmax(0, 1fr);
    width: calc(var(--pix-min-col-left) + 22px);
}

html[data-pix-layout="minimal"] .left-vine-panel:has(.lila-tool-icon) .background-lila-button {
    width: auto;
    height: 54px;
}

/* Centred on the tile rather than on a bloom anchor that no longer exists, and high enough to
   leave the label its line. */
html[data-pix-layout="minimal"] .lila-tool-icon {
    top: 21px;
}

html[data-pix-layout="minimal"] .lila-tool-label {
    left: 2px;
    right: 2px;
    bottom: 3px;
    background: none;
    color: var(--pix-ui-ink);
}

/* A tool drawn with no icon at all still centres its label on the tile. */
html[data-pix-layout="minimal"] .lila-tool-label.lila-tool-label--alone {
    top: 50%;
}

/* The Customize panel's band preview is a clone of the real panel, re-flowed inline
   (position: relative, transform: none - cosmetics.js buildBandPreview). It is the only
   .left-vine-panel that is not a fixed full-height column, and it must not keep that height
   or the preview is a screenful of empty strip. Being inline-styled is exactly what makes it
   the clone, so [style] is what tells it apart. */
html[data-pix-layout="minimal"] .left-vine-panel[style] {
    height: auto;
}
