/*!*****************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[12].use[2]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[14].oneOf[12].use[3]!./src/app/globals.css ***!
  \*****************************************************************************************************************************************************************************************************************************************************************/
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
:root { color-scheme: dark; }          /* native scrollbars & form controls render dark, not default light */
body {
  font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, sans-serif;
  color: #e7e7ea; background: #0f0f12;
}
/* the whole window: a document bar over a working area. min-height:0 on .app is what lets the rail and
   the chat log scroll instead of pushing the page taller than the viewport. */
.shell { display: flex; flex-direction: column; height: 100vh; }
.app { flex: 1 1; min-height: 0; display: flex; position: relative; }   /* the log overlays this row */

/* top bar — the document's own controls, and what the document is called */
.topbar { height: 44px; flex: none; display: flex; align-items: center; gap: 4px; padding: 0 10px;
          background: #141418; border-bottom: 1px solid #26262c; }
.brand { font-size: 13px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
         color: #8a8a93; padding: 0 8px 0 4px; }
.tsep { width: 1px; height: 20px; flex: none; background: #26262c; margin: 0 6px; }
.tgap { margin-left: auto; }   /* closing the file sits at the far end, away from the doing */
/* The version rides the wordmark as a subordinate: same line, smaller, lighter and dimmer, so it
   reads as a stamp on the name rather than a second word in it. `text-transform: none` undoes the
   bar's uppercase, which would otherwise capitalise the `v`. Tabular figures keep it from jittering. */
.brand .ver { font-size: 10px; font-weight: 400; letter-spacing: 0; text-transform: none;
              color: #55555e; font-feature-settings: "tnum"; font-variant-numeric: tabular-nums; }

/* dark scrollbar to match the chat (Chromium + Firefox) */
* { scrollbar-width: thin; scrollbar-color: #33333c transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: #2c2c34; border-radius: 6px; border: 2px solid transparent; background-clip: padding-box; }
*::-webkit-scrollbar-thumb:hover { background: #3a3a45; background-clip: padding-box; }

/* rail — the artwork's controls, no written label -------------------------------------------------
   Grouped the way the work is shaped: talk · transform · say · look. The last group is pushed to the
   foot by .rgap because it is the only one that changes nothing in the file; the document's own
   moves (import, export, undo, redo) live in the top bar instead. 48px wide: a 34px target with 7px
   either side, the same target size the floating toolbars used, so nothing got harder to hit. */
.rail { width: 48px; flex: none; display: flex; flex-direction: column; align-items: center;
        gap: 4px; padding: 8px 0; overflow-y: auto; overflow-x: hidden;
        border-right: 1px solid #26262c; background: #141418;
        /* above the log panel, which slides in from behind it. Without this the rail comes FIRST in the
           DOM and the panel therefore paints on top, so the sliding text crossed over the buttons. */
        position: relative; z-index: 10; }
/* Three states, and the arithmetic is worth writing down because they are not independent: a disabled
   button is the idle colour at 30% over the rail's #141418, so lifting idle lifts disabled with it.
   Channel values over that ground:

       idle #8a8a93 -> #c6c6d0     138 -> 198   the available tools, much brighter
       disabled (same colour, .3)   55 ->  73   carried up by idle alone, which is the small lift wanted
       hover #e7e7ea -> #f4f4f8    231 -> 244   still clearly above idle, so hover still reads as hover

   Raising idle without touching the opacity is what makes "a lot brighter when available, a little
   brighter when not" fall out of one edit instead of two that have to be kept in step. */
.rbtn { width: 34px; height: 34px; flex: none; border-radius: 8px; border: 0; background: transparent;
        color: #c6c6d0; display: flex; align-items: center; justify-content: center; cursor: pointer;
        padding: 0; }
.rbtn:hover:not(:disabled) { background: #22222a; color: #f4f4f8; }
.rbtn:disabled { opacity: .3; cursor: default; }
/* An engaged toggle is filled, not merely tinted — at 20px a colour-only change is easy to miss, and
   these three (conversation, before/after, white plate) each change what the whole screen shows. */
.rbtn.on { background: #2b6cff; color: #fff; }
.rbtn.on:hover:not(:disabled) { background: #4680ff; color: #fff; }
.rbtn:focus-visible, .rail .sw:focus-visible { outline: 2px solid #6f9bff; outline-offset: 2px; }
.rsep { width: 20px; height: 1px; flex: none; background: #26262c; margin: 4px 0; }
.rgap { margin-top: auto; }                       /* the view group sits at the foot of the rail */
/* The hover label. Fixed to the viewport (positioned by JS from the button's rect) so a scrolling
   rail never clips it, and instant — the browser's own tooltip delay is too slow to be the only
   label a control has. */
/* Two lines, not one: a tip that said everything on a single nowrap row drew a 70-character bar across
   the canvas the operator was pointing at. The NAME sets the width and never wraps; the detail sits
   under it, quieter, and wraps inside max-width. Same primary/secondary pattern as .flytxt below, so
   the two labelled surfaces read alike. */
.railtip { position: fixed; z-index: 20; pointer-events: none;
           background: #24242c; border: 1px solid #33333c; color: #e7e7ea; font-size: 12px;
           padding: 5px 9px; border-radius: 7px; max-width: 210px;
           box-shadow: 0 6px 20px #0008; }
.railtip.right { transform: translateY(-50%); }
.railtip.below { transform: translateX(-50%); }
.tipmain { display: block; white-space: nowrap; }
.tipsub { display: block; margin-top: 2px; font-size: 11px; font-style: italic;
          color: #8f8f9b; line-height: 1.3; }
/* The long-press variant menu. Labels are right HERE and nowhere else in the rail: two brushes that
   work by different machinery cannot be told apart by a glyph, and this is a menu, not a toolbar. */
.rfly { position: fixed; left: 56px; transform: translateY(-50%); z-index: 22; width: 232px;
        background: #16161c; border: 1px solid #2e2e38; border-radius: 10px; padding: 5px;
        box-shadow: 0 12px 34px #000a; }
.flyveil { position: fixed; inset: 0; z-index: 21; }   /* one click anywhere dismisses it */
.flyrow { display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px 8px; border: 0;
          border-radius: 7px; background: transparent; color: #c7c7cf; cursor: pointer; text-align: left; }
.flyrow:hover { background: #22222a; color: #e7e7ea; }
.flyrow.on { background: #2b6cff; color: #fff; }
.flytxt { display: flex; flex-direction: column; line-height: 1.25; }
.flytxt b { font-size: 12px; font-weight: 600; }
.flytxt i { font-size: 11px; font-style: normal; opacity: .65; }

/* A dialog of our own. The browser's confirm() cannot be styled, cannot carry a tick-box, and reads as
   a fault in the page rather than as a question the application is asking. Sits above the working veil
   (z-index 7) because it can only appear when nothing is running. */
.modal { position: fixed; inset: 0; z-index: 30; display: flex; align-items: center;
         justify-content: center; background: #06060999; }
.modal-card { width: 320px; padding: 18px; background: #16161c; border: 1px solid #2e2e38;
              border-radius: 12px; box-shadow: 0 18px 48px #000b; }
.modal-title { font-size: 15px; font-weight: 600; color: #e7e7ea; }
.modal-body { margin-top: 6px; font-size: 13px; color: #9a9aa6; line-height: 1.4; }
/* the tick-box is a quieter offer than the buttons: it changes a preference, it does not answer */
.modal-check { display: flex; align-items: center; gap: 8px; margin-top: 14px;
               font-size: 12px; color: #8f8f9b; cursor: pointer; }
.modal-check input { accent-color: #2b6cff; width: 14px; height: 14px; cursor: pointer; }
.modal-acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.mbtn { padding: 7px 14px; border-radius: 7px; border: 1px solid #33333c; background: transparent;
        color: #c6c6d0; font: inherit; font-size: 13px; cursor: pointer; }
.mbtn:hover { background: #22222a; color: #f4f4f8; }
/* the destructive answer is the one that is coloured, so the eye lands on what it is about to do */
.mbtn.danger { background: #b3372f; border-color: #b3372f; color: #fff; font-weight: 600; }
.mbtn.danger:hover { background: #c94339; }
.mbtn:focus-visible { outline: 2px solid #6f9bff; outline-offset: 2px; }

/* Dragging the artwork to the desktop is the other accidental save, and `draggable={false}` alone does
   not stop it everywhere. Same reason as the context menu: what leaves that way is the working copy. */
.viewer img, .viewer canvas { -webkit-user-drag: none; -webkit-user-select: none; -moz-user-select: none; user-select: none; }

/* the gesture being drawn, in screen space over the canvas. No fill: it must never hide the pixels
   the operator is aiming at. */
/* width/height are NOT redundant with inset:0 here. An <svg> is a REPLACED element, and for an
   absolutely positioned replaced box `width: auto` resolves to the INTRINSIC size — 300x150 — not to
   the inset rectangle. The layer was therefore a small box in the top-left corner, clipping away every
   stroke drawn outside it, which is why the ring (a plain div, explicitly sized) showed and the paint
   never did. */
.gesture { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 6; pointer-events: none; }
.gesture rect, .gesture polyline, .gesture circle {
  fill: none; stroke: #2b6cff; stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.gesture rect { stroke-dasharray: 5 4; }
.gesture polyline { stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; opacity: .9; }
/* The painted stroke wears the SELECTION's own colour and alpha (server HIGHLIGHT rgb(0,200,255) at
   130/255): what you paint is what you will get, so it must not be a different colour from it. */
/* The painted stroke carries no stroke-width here on purpose — the brush's width is set inline, and a
   rule here would outrank it (CSS beats presentation attributes AND would fight the inline style). */
.gesture polyline.paint { stroke: rgb(0, 200, 255); stroke-opacity: .51; opacity: 1;
                          stroke-linecap: round; stroke-linejoin: round; vector-effect: none; }
/* The brush ring IS the cursor (hence `cursor: none` below). Its own element rather than a shape in
   the gesture layer, because it must exist BEFORE the first movement — mounting it on the first
   mousemove, with the system cursor already hidden, left nothing on screen at all. It is moved by
   writing a transform, so travelling across the canvas costs no React render.
   Two rings, dark under light: a single colour vanishes on one of black or white artwork, and DTF
   files routinely contain both. z-index sits above the gesture layer so the stroke never covers it. */
.brushring { position: absolute; left: 0; top: 0; z-index: 8; pointer-events: none; border-radius: 50%;
             border: 1.25px solid #fff; box-shadow: 0 0 0 1.5px #0008, inset 0 0 0 1.5px #0008;
             opacity: 0; }
.brushring.on { opacity: 1; }

/* Choke belongs to the white plate: a flyout off the rail, alive only while that view is. It sits at
   the top of the canvas (clearing the top bar) — the foot of the canvas is taken by the size bar. */
.chokepop { position: fixed; left: 56px; top: 56px; z-index: 8;
            background: #191920; border: 1px solid #2a2a32; border-radius: 9px; padding: 7px 9px; }

/* The log — a panel the status bar opens. It carries no title of its own: the product is named once,
   in the top bar, and a second heading here would only repeat it.

   It OVERLAYS the working area rather than sitting in the row. That is what makes it animatable: as a
   flex column it resized the canvas on every frame of the transition, which reflows the viewer and
   re-fits the artwork — expensive, and visibly wrong. Overlaid, only transform and opacity change, both
   of which the compositor handles without touching layout.

   100ms, and only on open/close. This repo forbids CSS animation on the WORKING VEIL for a measured
   reason (a continuously animated overlay starves the CUDA op on WSL's shared GPU, 4.6s -> 45.7s); a
   one-shot transition on a panel is a different thing, but it is kept short and paid once for exactly
   that reason. `visibility` is delayed on the way out so the panel stays unfocusable while hidden. */
.chat { position: absolute; left: 48px; top: 0; bottom: 0; z-index: 9;
        width: 25%; min-width: 280px; display: flex; flex-direction: column;
        border-right: 1px solid #26262c; background: #141418;
        box-shadow: 8px 0 24px #0006;
        transition: transform .1s ease-out, opacity .1s ease-out, visibility 0s; }
.chat.closed { transform: translateX(-100%); opacity: 0; visibility: hidden;
               transition: transform .1s ease-in, opacity .1s ease-in, visibility 0s linear .1s; }
@media (prefers-reduced-motion: reduce) {
  .chat, .chat.closed { transition: none; }
}
/* The conversation reads as a SYSTEM TRANSCRIPT, not an SMS thread. No bubbles, and no
   left/right split — opposite alignment plus rounded fills is what makes a log look like phone
   messaging. Both roles run full-width from the same margin; the distinction is TYPOGRAPHIC: an
   issued command carries an accent rule and a marker, the reply is plain output beneath it.
   white-space: pre-line matters — the DTF report and other multi-line answers embed newlines that
   would otherwise collapse into one run-on line. */
.log { flex: 1 1; overflow-y: auto; padding: 16px 14px 18px; display: flex; flex-direction: column; gap: 6px; }
.msg { max-width: none; word-break: break-word; white-space: pre-line; }
.msg.user { color: #cfd6e6; border-left: 2px solid #2b6cff; padding-left: 10px; margin-top: 16px; }
.msg.user:first-child { margin-top: 0; }
.msg.user::before { content: "› "; color: #2b6cff; font-weight: 700; }
.msg.bot { color: #b9b9c2; padding-left: 12px; }
.msg .meta { font-size: 11px; color: #6f6f79; margin-top: 4px; font-feature-settings: "tnum"; font-variant-numeric: tabular-nums; }
/* the VLM image-warm chip: shown while the planner prefills the image (dev box ~30s); commands are fast
   after. No CSS keyframe animation — a compositing animation starves the shared WSL GPU (see page.tsx). */
.warmchip { display: flex; align-items: center; gap: 8px; margin: 0 12px; padding: 7px 11px;
  font-size: 12px; color: #c8b56a; background: #2a2820; border: 1px solid #4a4326; border-radius: 8px; }
.warmdot { width: 8px; height: 8px; border-radius: 50%; background: #c8b56a; flex: none; }

/* The one control that names itself, so it carries the SAME glyph as the rail's import button: the
   empty canvas teaches the icon here, and the operator meets it again in the bar afterwards. */
.upload { background: #2b6cff; color: #fff; border: 0; border-radius: 8px;
          padding: 9px 14px 9px 12px; cursor: pointer; font-weight: 600;
          display: inline-flex; align-items: center; gap: 8px; }

/* viewer — 3/4 */
/* viewer background is driven by html[data-bg] (set pre-paint in layout.tsx) so a reload never flashes. */
/* the base rule is the DEFAULT background, i.e. the LIGHT checker (see page.tsx / layout.tsx). */
.viewer { flex: 1 1; display: flex; align-items: center; justify-content: center;
          position: relative; overflow: hidden;
          background: #dedee4 repeating-conic-gradient(#f2f2f5 0 25%, #d0d0d7 0 50%) 0/26px 26px; }
html[data-bg="dark"] .viewer { background: #0f0f12 repeating-conic-gradient(#17171c 0 25%, #131317 0 50%) 0/26px 26px; }
html[data-bg="solid"] .viewer { background: var(--bg); }
/* white-underbase view: the canvas is opaque black off the ink, so fill the whole viewer black too — the
   image's black box merges in and the underbase reads across the panel instead of as a box around the art. */
.viewer.underbase { background: #000 !important; }
.viewer.grab { cursor: -webkit-grab; cursor: grab; }
.viewer.grab:active { cursor: -webkit-grabbing; cursor: grabbing; }
/* An armed tool means the drag draws instead of panning, and the cursor has to say so before the
   click. These MUST sit after .viewer.grab: same specificity, so source order decides, and declared
   earlier (they were) the pan cursor silently won and no tool cursor ever appeared. */
.viewer.aiming, .viewer.aiming.grab, .viewer.aiming.grab:active { cursor: crosshair; }
.viewer.painting, .viewer.painting.grab, .viewer.painting.grab:active { cursor: none; }
.pan { position: absolute; top: 0; left: 0; will-change: transform; }
.pan img, .pan canvas.white { display: block; width: 100%; height: 100%; }
.pan img.overlay { position: absolute; inset: 0; }
/* pixel-perfect when magnified: show the actual pixel grid instead of a bilinear blur */
.pan.crisp img, .pan.crisp canvas.white { image-rendering: pixelated; }
/* ...but keep the selection overlay SMOOTH when zoomed — its 1px mask-edge wobble reads as clean anti-aliased
   instead of hard pixel steps, while the artwork underneath stays crisp. */
.pan.crisp img.overlay { image-rendering: auto; }
.hint { color: #55555f; font-size: 15px; }
.upload-wrap { display: flex; flex-direction: column; align-items: center; gap: 14px; }
/* drag-and-drop upload */
.dropzone { position: absolute; inset: 10px; z-index: 5; display: flex; align-items: center; justify-content: center;
            border: 2px dashed #2b6cff; border-radius: 14px; background: #2b6cff1a; pointer-events: none; }
.dropzone span { color: #cfe0ff; font-size: 16px; font-weight: 600; }
.viewer.dropping { cursor: copy; }
/* working overlay — blocking veil during a /ground op. NO backdrop-filter, NO CSS animation: a
   compositing/animated overlay STARVES the CUDA op on WSL2's shared GPU (measured: object_pool 4.6s->45.7s).
   The only moving part is the timer/braille text, updated via textContent on a ref (CPU only). */
.working { position: absolute; inset: 0; z-index: 7; display: flex; align-items: center; justify-content: center;
           background: #0a0a0ccc; }
.working-card { display: flex; flex-direction: column; align-items: center; gap: 8px;
                padding: 18px 28px; background: #141418; border: 1px solid #26262c; border-radius: 12px; }
.working-msg { color: #e6e6ee; font-size: 15px; font-weight: 600; }
.working-tick { color: #8a8a97; font-size: 13px; font-feature-settings: "tnum"; font-variant-numeric: tabular-nums;
                font-family: ui-monospace, SFMono-Regular, monospace; min-height: 16px; }

/* before/after wipe compare — FULL PANEL: divider sweeps the whole viewer; images sit at the real view rect
   (same zoom/pan) so the wipe reads in place. Background honours the viewer's chosen colour (Esc / ⇋ / dbl-click to exit). */
/* The layer drags the IMAGE (it lets the press through to the viewer, which pans); only the divider
   drags the divider. So the cursor says "move the picture" here and "resize" on the divider alone. */
.compare { position: absolute; inset: 0; z-index: 4; -webkit-user-select: none; -moz-user-select: none; user-select: none; overflow: hidden;
           background: #0f0f12; }
.compare.bg-dark  { background: #0f0f12 repeating-conic-gradient(#17171c 0 25%, #131317 0 50%) 0/26px 26px; }
.compare.bg-light { background: #dedee4 repeating-conic-gradient(#f2f2f5 0 25%, #d0d0d7 0 50%) 0/26px 26px; }
.clayer { position: absolute; inset: 0; overflow: hidden; }
.clayer img { position: absolute; display: block; }
.clayer.crisp img { image-rendering: pixelated; }
/* 16px of grab area for a 2px line: the divider has to be catchable without aiming at it, and a
   2px pointer target is not. The line itself is drawn by ::before, centred in that band. */
.compare .cdiv { position: absolute; top: 0; bottom: 0; width: 16px; transform: translateX(-8px);
                 cursor: ew-resize; touch-action: none; }
.compare .cdiv::before { content: ""; position: absolute; top: 0; bottom: 0; left: 7px; width: 2px;
                         background: #fff; box-shadow: 0 0 6px #000a; }
/* Two separate things were off. line-height:1 — without it the glyph inherits body's 1.5, so its line
   box is 19.5px inside a 26px circle. And then an OPTICAL nudge, because flex centres the line box,
   not the ink within it: ⇋ anchors on the baseline and sits low in its em box. padding-bottom is the
   nudge — box-sizing:border-box keeps the circle at 26px and just trims the content box, lifting the
   glyph 1px. Tune padding-bottom, not the transform, which is doing the -50%/-50% positioning. */
.compare .chandle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
                    width: 26px; height: 26px; border-radius: 50%; background: #2b6cff; color: #fff;
                    display: flex; align-items: center; justify-content: center; font-size: 13px;
                    line-height: 1; padding-bottom: 2px; }
.compare .clbl { position: absolute; top: 8px; font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: 6px;
                 background: #000000a0; color: #e7e7ea; pointer-events: none; }
.compare .clbl.l { left: 8px; } .compare .clbl.r { right: 8px; }

/* background chooser, now in the rail: the presets and the native colour picker (DTF: preview the art
   on any garment colour). These three need no drawn glyph — a swatch already IS a picture of itself. */
.sw { width: 22px; height: 22px; flex: none; border-radius: 6px; border: 1px solid #ffffff22;
      cursor: pointer; padding: 0; position: relative; overflow: hidden; }
.sw.active { outline: 2px solid #2b6cff; outline-offset: 1px; border-color: transparent; }
.sw.checker-dark  { background: repeating-conic-gradient(#5a5a63 0 25%, #33333c 0 50%) 0/10px 10px; }
.sw.checker-light { background: repeating-conic-gradient(#f2f2f5 0 25%, #cfcfd6 0 50%) 0/10px 10px; }
/* active swatch ring driven by html[data-bg] (set pre-paint) so it never flashes on reload; covers the
   colour picker too (data-bg="solid"), whose swatch then shows the chosen colour via --bg. */
html[data-bg="dark"]  .sw.checker-dark,
html[data-bg="light"] .sw.checker-light,
html[data-bg="solid"] .sw.pick { outline: 2px solid #2b6cff; outline-offset: 1px; border-color: transparent; }
html[data-bg="solid"] .sw.pick { background: var(--bg); }
.sw.pick { background: conic-gradient(from 0deg, #f44, #ff4, #4f4, #4ff, #44f, #f4f, #f44); }
.sw.pick input[type="color"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0;
                               border: 0; padding: 0; cursor: pointer; }
/* choke amount — only mounted in underbase view (see page.tsx) */
.choke { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: #b8b8c0;
         white-space: nowrap; }
.choke input { width: 46px; background: #1e1e25; border: 1px solid #2a2a32; border-radius: 6px;
               padding: 2px 5px; color: #e7e7ea; outline: none; font-size: 11px; }
.choke input:focus { border-color: #2b6cff; }

/* status bar — what the file IS, along the foot of the window ------------------------------------
   A RECESSED surface: darker and cooler than the chrome above it, so it reads as the floor of the
   window rather than a third toolbar. 24px and 11px type — it is meant to be glanced at, never
   worked in; the one thing you can set from it opens a panel. */
.statusbar { height: 24px; flex: none; display: flex; align-items: center; justify-content: flex-end;
             gap: 0; padding: 0 6px; font-size: 11px; color: #7c7c86;
             background: #0b0b0f; border-top: 1px solid #22222a;
             font-feature-settings: "tnum";
             font-variant-numeric: tabular-nums; }
.statusbar .seg { padding: 0 9px; white-space: nowrap; }
/* The log is the only thing on this bar that is not a fact about the file, so it sits apart, at the
   opposite end — margin-right:auto pushes the readouts back to the right where they belong. */
.logwrap { position: relative; margin-right: auto; display: flex; }
.logwrap .act { display: flex; align-items: center; color: #8a8a93; }
.logwrap .act:hover, .logwrap .act.open { color: #fff; }
.logwrap .act svg { width: 16px; height: 16px; }
.logwrap.unread::after { content: ""; position: absolute; top: 2px; right: 5px; width: 6px; height: 6px;
                         border-radius: 50%; background: #2b6cff; box-shadow: 0 0 0 2px #0b0b0f; }
/* segments are told apart by a hairline, not by a bullet: at 11px a separator glyph reads as content */
.statusbar .seg + .seg, .statusbar .sizewrap + .dpi { box-shadow: inset 1px 0 0 #22222a; }
.statusbar .name { color: #b8b8c0; max-width: 34ch; overflow: hidden; text-overflow: ellipsis; }
.statusbar .dim { color: #55555f; }
/* the one live control on the bar: a readout you can press */
.sizewrap { position: relative; box-shadow: inset 1px 0 0 #22222a; }
.statusbar .act { background: transparent; border: 0; color: #d7d7de; font: inherit; cursor: pointer;
                  height: 24px; padding: 0 9px; }
.statusbar .act:hover, .statusbar .act.open { background: #1b1b22; color: #fff; }
.statusbar .act:focus-visible { outline: 2px solid #6f9bff; outline-offset: -2px; }
.statusbar .dpi { font-weight: 600; padding: 0 9px; }
.statusbar .dpi.ok { color: #6ee7a0; }
.statusbar .dpi.warn { color: #ffcf6a; }
.statusbar .dpi.bad { color: #ff8a8a; }
.statusbar .assumed { color: #b9a06a; margin-left: 6px; padding: 0 7px; border-radius: 5px; cursor: help;
                      border: 1px dashed #4a3f22; }
/* print-size editor, hung above the bar. Width is the only field, because it is the only INPUT: the
   height follows the aspect ratio and the density falls out of the pixel count — showing all three
   is what makes that relationship visible instead of something to work out in your head. */
.sizepop { position: absolute; bottom: 28px; right: 0; z-index: 12; width: 232px;
           background: #16161c; border: 1px solid #2e2e38; border-radius: 10px; padding: 10px 12px 11px;
           box-shadow: 0 10px 30px #000a; cursor: default; }
.sptitle { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
           color: #8a8a93; margin-bottom: 9px; }
.sprow { display: flex; align-items: center; gap: 8px; height: 26px; font-size: 12px; color: #d7d7de; }
.sprow .spk { flex: 1 1; color: #8a8a93; }
.sprow input { width: 62px; background: #1e1e25; border: 1px solid #33333c; border-radius: 6px;
               padding: 3px 7px; color: #e7e7ea; outline: none; font: inherit; text-align: right; }
.sprow input:focus { border-color: #2b6cff; }
/* the derived rows line up with the input's text, so the three numbers read as one column */
.sprow.ro .spv { width: 62px; text-align: right; padding-right: 7px; color: #b8b8c0; }
.sprow .spv.ok { color: #6ee7a0; } .sprow .spv.warn { color: #ffcf6a; } .sprow .spv.bad { color: #ff8a8a; }
.sprow .spu { width: 26px; color: #6f6f79; }
.spnote { margin-top: 8px; font-size: 11px; line-height: 1.45; color: #6f6f79; }

