/* The design frame (static/js/design/): the editor shared by the merch
   studio, the patch designer and the logo maker's studio. Everything here is
   prefixed df- and leans on no other stylesheet, so a host page's own rules
   (the portal's pp-/ap-, the tools' tl-/lg-, the shop's mc-) cannot reach in
   and the frame reads the same in every tool. docs/design-frame.md */

/* The attribute has to win over any display rule, the host's included: a
   hidden notice is hidden. (The merch studio's draft banner showed to
   everyone for a week because a portal rule beat `hidden`.) */
.df-shell [hidden], .df-notice[hidden] { display: none !important; }

/* --- the sandbox (/dashboard/design-sandbox/) ------------------------------ */

.df-sandbox { max-width: 1100px; margin: 0 auto; padding: 84px 1rem 2rem; }
.df-sandbox h1 { margin: 0 0 .4rem; }
.df-sandbox-lead { opacity: .8; max-width: 60ch; }
.df-sandbox-status { font-size: .9rem; opacity: .85; }

/* --- panels: the layers list and the inspector (static/js/design/panels.js) --- */

.df-empty { font-size: .82rem; opacity: .75; margin: 6px 0; }
.df-layer {
    display: flex; align-items: center; gap: 6px; width: 100%; padding: 4px 6px; box-sizing: border-box;
    border-radius: 8px; border: 1px solid rgba(255,255,255,.14); background: rgba(255,255,255,.03);
}
.df-layer.is-on { border-color: #fff; background: rgba(255,255,255,.08); }
.df-layer.is-hidden .df-layer-name { opacity: .5; text-decoration: line-through; }
.df-layer-pick {
    display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; padding: 2px;
    border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.df-layer-thumb { width: 48px; height: 48px; border-radius: 4px; flex: none; }
.df-layer-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.df-layer-tools { display: flex; gap: 2px; flex: none; }
.df-icon {
    width: 32px; height: 32px; border-radius: 6px; border: 1px solid transparent; background: none;
    color: inherit; cursor: pointer; opacity: .7; font-size: .8rem; padding: 0;
}
.df-icon:hover { opacity: 1; background: rgba(255,255,255,.08); }
.df-icon.is-on { opacity: 1; border-color: rgba(255,255,255,.4); }
.df-icon:disabled { opacity: .25; cursor: default; background: none; }
.df-icon:focus-visible, .df-btn:focus-visible, .df-layer-pick:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.df-label { display: grid; grid-template-columns: 80px 1fr; align-items: center; gap: 8px; margin: 6px 0; font-size: .9rem; }
.df-label--inks { align-items: start; }
.df-input, .df-select {
    width: 100%; min-width: 0; padding: 6px 8px; box-sizing: border-box; border-radius: 6px;
    border: 1px solid rgba(255,255,255,.2); background: rgba(0,0,0,.25); color: inherit; font: inherit; font-size: .9rem;
}
.df-input:focus, .df-select:focus { outline: none; border-color: #ff0000; box-shadow: 0 0 0 3px rgba(255,0,0,.22); }
/* Sliders: a thin track and a red thumb (the browser's are grey and blue). */
.df-range { -webkit-appearance: none; appearance: none; width: 100%; height: 22px; margin: 0; background: transparent; cursor: pointer; }
.df-range:focus { outline: none; }
.df-range::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: rgba(255,255,255,.2); }
.df-range::-webkit-slider-thumb {
    -webkit-appearance: none; width: 18px; height: 18px; margin-top: -7px; border-radius: 50%;
    background: #ff0000; border: 2px solid #fff; box-shadow: 0 0 0 2px rgba(0,0,0,.6); cursor: grab;
}
.df-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 2px rgba(0,0,0,.6), 0 0 0 3px rgba(255,0,0,.22); }
.df-range::-moz-range-track { height: 4px; border-radius: 2px; background: rgba(255,255,255,.2); }
.df-range::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: #ff0000; border: 2px solid #fff; cursor: grab; }
.df-range:disabled { opacity: .4; cursor: not-allowed; }
/* Size in centimetres, how sharp a picture prints, too small to stitch. */
.df-readouts { margin: 0 0 6px 88px; }
.df-readout { margin: 0 0 4px; font-size: .8rem; opacity: .75; }
.df-readout.is-warn { color: #ffb74d; opacity: 1; }
/* One-ink swatches: "its own colours" is a split dot. */
.df-inks { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.df-ink {
    width: 28px; height: 28px; border-radius: 50%; padding: 0; cursor: pointer;
    background: var(--ink, #fff); border: 1px solid rgba(255,255,255,.45);
}
.df-ink--own { background: conic-gradient(#e10600 0 25%, #f2c200 0 50%, #1b2340 0 75%, #f2f2f2 0); }
.df-ink[aria-pressed="true"] { box-shadow: 0 0 0 2px #111, 0 0 0 4px #fff; }
.df-ink:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.df-ink-custom { width: 30px; height: 26px; padding: 0; border: 1px solid rgba(255,255,255,.3); border-radius: 6px; background: none; cursor: pointer; }
.df-ink-custom.is-on { box-shadow: 0 0 0 2px #fff; }
.df-presets, .df-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.df-btn {
    display: inline-flex; align-items: center; gap: .4rem; padding: .45rem .8rem;
    border: 1px solid rgba(255,255,255,.22); border-radius: 8px; background: rgba(255,255,255,.04);
    color: inherit; font: inherit; font-size: .78rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; cursor: pointer;
}
.df-btn:hover { background: rgba(255,255,255,.1); }
.df-btn.is-on { border-color: #fff; background: rgba(255,255,255,.12); }
.df-btn--danger:hover { border-color: #ff8a65; color: #ff8a65; }
.df-btn:disabled { opacity: .4; cursor: default; }
@media (max-width: 500px) {
    .df-readouts { margin-left: 0; }
    .df-label { grid-template-columns: 1fr; }
}

/* --- the sandbox's little editor ------------------------------------------- */

.df-sandbox-row { display: grid; grid-template-columns: 400px minmax(0, 1fr); gap: 20px; align-items: start; margin-top: 1rem; }
.df-sandbox-layers { display: grid; gap: 6px; margin: 12px 0; }
.df-sandbox-inspector { padding: 10px; border-radius: 8px; background: rgba(255,255,255,.04); }
@media (max-width: 900px) { .df-sandbox-row { grid-template-columns: 1fr; } }

/* --- the shell (static/js/design/shell.js) ------------------------------------
   Laptop: rail | stage | inspector, the stage sticky under the site's fixed
   header. Narrower: the rail and the inspector stack beside the stage. Phone:
   the stage sticks at the top, the panels are sheets from a tab bar. The
   header's height is measured into --df-header by the shell. */

.df-shell { --df-header: 60px; --df-tabbar: 58px; position: relative; }
.df-shell-body {
    display: grid; grid-template-columns: 320px minmax(0, 1fr) 340px; gap: 18px; align-items: start;
    margin-bottom: 24px;
}
.df-rail, .df-inspector { min-width: 0; }
.df-panel { margin-bottom: 22px; }
.df-panel-title { margin-top: 0; }
.df-sheet-close { display: none; }
.df-stage { position: sticky; top: calc(var(--df-header) + 12px); min-width: 0; }
.df-stage-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 14px; margin-bottom: 8px; min-height: 32px; }
.df-stage-box {
    position: relative; width: 100%; aspect-ratio: 1 / 1; max-height: calc(100vh - var(--df-header) - 150px);
    min-height: 320px; background: #1f1f1f; border-radius: 12px; overflow: hidden;
}
.df-canvas { display: block; width: 100%; height: 100%; touch-action: pan-y; cursor: grab; }
.df-canvas:focus-visible { outline: 2px solid #fff; outline-offset: -3px; }
.df-stage-foot { margin-top: 8px; }
.df-status { margin: 0 0 6px; font-size: .88rem; opacity: .85; }
.df-status.is-error { color: #ff8a65; opacity: 1; }
.df-status:empty, .df-status[hidden] { display: none; }
.df-notice { margin-bottom: 14px; }
/* The floating toolbar by the selected layer. */
.df-toolbar {
    position: absolute; display: flex; gap: 2px; padding: 4px; border-radius: 10px;
    background: rgba(18,18,18,.96); border: 1px solid rgba(255,255,255,.22); box-shadow: 0 6px 20px rgba(0,0,0,.5);
    z-index: 5;
}
.df-toolbar[hidden] { display: none; }
.df-tool {
    width: 36px; height: 36px; border-radius: 8px; border: 1px solid transparent; background: none;
    color: #fff; font-size: .95rem; cursor: pointer; padding: 0;
}
.df-tool:hover { background: rgba(255,255,255,.1); }
.df-tool.is-on { border-color: rgba(255,255,255,.5); background: rgba(255,255,255,.12); }
.df-tool:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.df-tabbar, .df-scrim { display: none; }

@media (max-width: 1199px) {
    .df-shell-body { grid-template-columns: minmax(300px, 380px) minmax(0, 1fr); }
    .df-rail { grid-column: 1; grid-row: 1; }
    .df-inspector { grid-column: 1; grid-row: 2; }
    .df-stage { grid-column: 2; grid-row: 1 / span 2; }
}

@media (max-width: 899px) {
    .df-shell-body { display: block; padding-bottom: 12px; }
    /* The stage on top, under the fixed header, full-bleed over the portal's
       1rem side padding; the page scrolls under it. */
    .df-stage {
        position: sticky; top: var(--df-header); z-index: 30; margin: 0 -1rem; padding: 6px 1rem 8px;
        background: #0a0a0a; border-bottom: 1px solid #1f1f1f;
    }
    /* What is left above a sheet: the header, the stage bar (measured by
       shell.js into --df-stagebar: it wraps to two lines on some phones),
       the stage's own padding, the tab bar and a 46vh sheet taken off the
       screen, within 200-340px. A one-line bar gives the old 64px. In svh
       where the browser has it: Safari's 100vh is the screen with its bars
       hidden, so with them showing the sheet came up over the foot of the
       stage. */
    .df-stage-box {
        aspect-ratio: auto; min-height: 0; max-height: none;
        height: clamp(200px, calc(100vh - var(--df-header) - var(--df-tabbar) - 46vh - var(--df-stagebar, 40px) - 24px), 340px);
        height: clamp(200px, calc(100svh - var(--df-header) - var(--df-tabbar) - 46svh - var(--df-stagebar, 40px) - 24px), 340px);
    }
    /* iOS zooms the page into any field set under 16px when it is tapped,
       and leaves it zoomed: the logo maker's band name (14.9px) and the
       inspector's words (14.4px) did. `html` outranks the tools' own rules. */
    html .df-shell input[type="text"], html .df-shell input[type="number"], html .df-shell input[type="search"],
    html .df-shell input[type="email"], html .df-shell input[type="url"],
    html .df-shell select, html .df-shell textarea { font-size: 16px; }
    .df-stage-foot { margin-top: 6px; }
    /* The panels are sheets: hidden until their tab opens them. */
    .df-panel { display: none; margin: 0; }
    .df-panel.is-open {
        display: block; position: fixed; left: 0; right: 0;
        bottom: calc(var(--df-tabbar) + env(safe-area-inset-bottom, 0px));
        max-height: 46vh; max-height: 46svh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
        padding: 14px max(1rem, env(safe-area-inset-right, 0px)) 18px max(1rem, env(safe-area-inset-left, 0px));
        background: #121212; border-top: 1px solid #333; border-radius: 16px 16px 0 0;
        box-shadow: 0 -10px 30px rgba(0,0,0,.6); z-index: 1002;
    }
    .df-panel.is-open .df-sheet-close {
        display: flex; align-items: center; justify-content: center; position: absolute; top: 8px; right: 10px;
        width: 34px; height: 34px; border-radius: 50%; border: 0; background: rgba(255,255,255,.08); color: #fff;
        font-size: 1rem; cursor: pointer;
    }
    .df-scrim { display: block; position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 1001; }
    .df-scrim[hidden] { display: none; }
    html.df-sheet-open { overflow: hidden; }
    /* The stage stays above the scrim while a sheet is open: the canvas can
       still be tapped and dragged with a panel showing. */
    html.df-sheet-open .df-stage { z-index: 1002; }
    /* The tab bar, fixed at the bottom while the editor is on screen. */
    .df-tabbar {
        display: none; position: fixed; left: 0; right: 0; bottom: 0; height: var(--df-tabbar);
        padding: 0 env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px); box-sizing: content-box;
        background: rgba(10,10,10,.97); border-top: 1px solid #262626; z-index: 1003;
    }
    .df-tabbar.is-live { display: flex; }
    .df-tab {
        flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
        border: 0; background: none; color: #bbb; font: inherit; font-size: .6rem; font-weight: 700;
        letter-spacing: .05em; text-transform: uppercase; cursor: pointer; padding: 0 2px;
    }
    .df-tab i { font-size: 1.05rem; }
    .df-tab span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
    .df-tab.is-on { color: #fff; }
    .df-tab--primary { color: #fff; background: #ff0000; }
    .df-tab--primary.is-on { background: #c40000; }
    .df-tab:focus-visible { outline: 2px solid #fff; outline-offset: -3px; }
    /* 'pages' (shell.js phoneMode, the press kit editor): a panel fills
       the screen under the stage's bar rather than rising over the stage;
       the tab bar scrolls sideways so every label is readable. */
    .df-shell.df-pages .df-panel.is-open {
        position: static; max-height: none; overflow: visible; padding: 6px 0 18px;
        background: none; border: 0; border-radius: 0; box-shadow: none; z-index: auto;
    }
    .df-shell.df-pages .df-panel.is-open .df-sheet-close { display: none; }
    .df-shell.df-pages .df-scrim { display: none !important; }
    .df-shell.df-pages .df-tabbar { overflow-x: auto; justify-content: flex-start; scrollbar-width: none; }
    .df-shell.df-pages .df-tabbar::-webkit-scrollbar { display: none; }
    .df-shell.df-pages .df-tab { flex: 0 0 auto; min-width: 64px; padding: 0 10px; font-size: .75rem; letter-spacing: .02em; }
    /* The frame's own panels in a sheet. */
    .df-layer-tools { gap: 6px; }
    /* A fingertip's worth (44px) on a phone. */
    .df-icon { width: 44px; height: 44px; }
    .df-ink { width: 36px; height: 36px; }
}

/* A phone held sideways. The site header (89px, fixed), the stage and the
   tab bar came to more than the 375px or so of screen there is, so the foot
   of the canvas sat under the tab bar and a sheet covered the rest. Here
   the header scrolls away with the page (shell.js then measures it as 0, so
   the stage sticks to the top of the screen), and the stage is the height
   that is left: all of it between the top and the tab bar, or what shows
   above a sheet while one is open. :has() keeps this to pages with an
   editor on them; a browser without it keeps the old layout. */
@media (max-width: 899px) and (max-height: 500px) and (orientation: landscape) {
    html:has(.df-shell) .header { position: absolute; }
    html .df-shell .df-stage-box {
        height: clamp(80px, calc(100vh - var(--df-header) - var(--df-tabbar) - 46vh - var(--df-stagebar, 40px) - 24px), 340px);
        height: clamp(80px, calc(100svh - var(--df-header) - var(--df-tabbar) - 46svh - var(--df-stagebar, 40px) - 24px), 340px);
    }
    /* 72px, not the 24px above: with no sheet up the foot under the canvas
       (the patch maker's is 57px) has to fit over the tab bar too. */
    html:not(.df-sheet-open) .df-shell .df-stage-box {
        height: clamp(140px, calc(100vh - var(--df-header) - var(--df-tabbar) - var(--df-stagebar, 40px) - 72px), 340px);
        height: clamp(140px, calc(100svh - var(--df-header) - var(--df-tabbar) - var(--df-stagebar, 40px) - 72px), 340px);
    }
}

/* --- objects in an inline SVG (static/js/design/svgobjects.js) ------------
   The logo maker's letters: a selection box and corner handles drawn in the
   SVG itself, so they scale with it; the strokes stay one screen pixel. */
/* No touch-action here: Chrome ignores it on shapes inside an SVG, and where
   it is honoured it would stop a swipe on a letter scrolling the page. The
   root <svg> carries it instead (svgobjects.js, touchAction). */
.df-object { cursor: grab; }
.df-object:hover { filter: drop-shadow(0 0 5px rgba(255, 0, 0, .55)); }
.df-object.df-selected { filter: drop-shadow(0 0 2px #ff3b3b) drop-shadow(0 0 9px rgba(255, 0, 0, .65)); }
.df-overlay-box { fill: none; stroke: rgba(255,255,255,.9); stroke-width: 1.5; stroke-dasharray: 6 4; vector-effect: non-scaling-stroke; }
.df-overlay-handle { cursor: nwse-resize; touch-action: none; }
.df-overlay-handle[data-handle="ne"], .df-overlay-handle[data-handle="sw"] { cursor: nesw-resize; }
.df-overlay-face { fill: #fff; stroke: rgba(0,0,0,.6); stroke-width: 1; vector-effect: non-scaling-stroke; }

/* The rotate knob over an SVG object (svgobjects.js, 20 Oct 2026). */
.df-overlay-rotor line { stroke: #ff0000; stroke-width: 1; vector-effect: non-scaling-stroke; }
.df-overlay-rotor .df-overlay-rotor-face { fill: #fff; stroke: #ff0000; stroke-width: 1.5; vector-effect: non-scaling-stroke; cursor: grab; }

/* Several at once, the layers list, the zoom bar (20 Oct 2026, plans A1 and A3). */
.df-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.df-layer.is-dragging { opacity: .45; }
.df-layer.is-drop { box-shadow: inset 0 2px 0 #ff3b30; }
.df-layer-group { margin-left: auto; font-size: .7rem; color: #8a8a8a; padding-left: 6px; }
.df-layer-rename {
    flex: 1; min-width: 0; background: #0f0f0f; color: #eee; border: 1px solid #ff3b30; border-radius: 4px;
    padding: 6px 8px; font: inherit; font-size: .85rem;
}
.df-many { margin: 0 0 10px; color: #eee; font-weight: 600; }
.df-align { display: flex; flex-wrap: wrap; gap: 4px; }
.df-btn--small { padding: 4px 8px; font-size: .74rem; }
/* Words' outline row and the height in mm (plan A2). */
.df-outline { display: flex; align-items: center; gap: 8px; }
.df-outline .df-range { flex: 1; }
.df-input--mm { max-width: 7rem; }
.df-zoom { display: inline-flex; align-items: center; gap: 2px; margin-left: auto; }
.df-zoom-btn {
    background: #161616; border: 1px solid #333; color: #bbb; border-radius: 4px; min-width: 30px; height: 28px;
    padding: 0 6px; font: inherit; font-size: .75rem; cursor: pointer;
}
.df-zoom-btn:hover, .df-zoom-btn:focus-visible { border-color: #ff3b30; color: #fff; outline: none; }
.df-zoom-btn.is-on { border-color: #ff3b30; color: #fff; background: rgba(255,59,48,.12); }

/* A member's own colours after the surface's (plan A4, brand.js): a thin
   rule, then their swatches, squarer so they read as "yours". */
.df-inks-sep { width: 1px; height: 20px; background: rgba(255,255,255,.25); margin: 0 2px; }
.df-ink--brand { border-radius: 6px; }
/* Their saved logos as buttons (Compose on the logo page). */
.df-brand-logos { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px; }
.df-brand-logo {
    --ground: #1a1a1a;
    display: flex; align-items: center; justify-content: center; height: 56px; padding: 6px;
    border: 1px solid rgba(255,255,255,.18); border-radius: 8px; background: var(--ground); cursor: pointer;
}
.df-brand-logo img { max-width: 100%; max-height: 100%; display: block; }
.df-brand-logo:hover, .df-brand-logo:focus-visible { border-color: #ff3b30; outline: none; }

/* Starting layouts (plan A5, templates.js): a sketch of each, no fonts. */
.df-templates { margin: 0 0 14px; }
.df-templates-title { margin: 0 0 6px; font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; opacity: .8; }
.df-templates-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 8px; }
.df-template {
    display: flex; flex-direction: column; align-items: stretch; gap: 4px; padding: 6px;
    border: 1px solid rgba(255,255,255,.16); border-radius: 8px; background: rgba(255,255,255,.03);
    color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.df-template:hover, .df-template:focus-visible { border-color: #ff3b30; outline: none; background: rgba(255,59,48,.06); }
.df-template[aria-busy="true"] { opacity: .6; cursor: progress; }
.df-template-art { display: flex; gap: 4px; align-items: flex-start; justify-content: center; min-height: 48px; }
.df-template-sketch { width: 100%; max-width: 64px; max-height: 72px; height: auto; display: block; }
.df-template-label { font-size: .8rem; font-weight: 600; line-height: 1.2; }
.df-template-note { font-size: .72rem; opacity: .7; line-height: 1.25; }
.df-tsk-ground { fill: rgba(255,255,255,.08); stroke: rgba(255,255,255,.28); stroke-width: .8; }
.df-tsk-logo { fill: #ff3b30; opacity: .85; }
.df-tsk-line { fill: rgba(255,255,255,.75); }
.df-tsk-shape { fill: rgba(255,255,255,.4); }
