/* ============================================================
   PrintFlow — design system
   ------------------------------------------------------------
   TWO FAMILIES, TWO MODES, ONE SET OF COMPONENTS.

     Studio  warm paper and burnt orange. Soft layered depth,
             tactile controls. The default.
     Pulse   high-contrast blocks, hard shadow, rose brand.
     Obsidian  cinematic. A lit ground, hairline borders, one teal
             source; depth is the highlight on a panel's top edge.
     Cobalt  drafting. A graphite or cool-paper ground, crisp
             one-pixel edges, one cobalt line; nothing floats and
             nothing bounces.

   Both ship light and dark. The switch is one attribute on
   <html> (data-style / data-theme) and NOTHING below the token
   block knows which is on — a component reads --surface, never
   a colour. That is what let a second family land without
   touching a single component rule.

   The three rules the whole file obeys:
     1. Every colour is a token. A component never writes rgba().
        (That is why light mode used to break: a card painted
        rgba(255,255,255,.05) is invisible on white.)
     2. Text tokens are picked to clear WCAG AA (4.5:1) against
        the surface they sit on, in every family and both modes.
        Measured against the rendered page, not assumed.
     3. Motion is opacity and transform only. Animating width or
        top forces layout every frame, and a board of forty rows
        is exactly where that shows.
   ============================================================ */

/* v1.20.4 (P1-7): fonts live in fonts.css (v1.97.0: the local file only).
   An @import doesn't get the page's ?v=, so this one has its own. */
@import url("fonts.css?v=3");

/* ============================================================ tokens ====
   THE THEME MATRIX
   -----------------------------------------------------------------------
   One family in two modes (v1.89.0: Pulse, Obsidian and Cobalt removed --
   every visual change had to be made and checked four times), and
   components never know which mode is on: a component reads --surface;
   it does not read "warm paper" or "rose".

       :root                                    Studio  dark   (default)
       html[data-theme="light"]                 Studio  light

   STUDIO -- warm paper and burnt orange. A workshop, not a spreadsheet.
   Depth comes from two stacked shadows rather than a border, which is what
   makes a card feel lifted instead of drawn.
   ======================================================================== */

/* ------------------------------------------- shared, family-agnostic ---- */
:root {
  /* -- density. The customer funnel breathes; the ops console does not.
        One scale, two settings: the panels opt in via .dense on <body>. -- */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;
  --card-pad: 18px;
  --row-h: 38px;
  /* Where a panel that escapes the header (notifications, the appearance
     menu, toasts) has to start. It follows the bar rather than guessing. */
  --float-top: 68px;

  /* -- shape. Studio is rounder than the instrument panel it replaces:
        soft UI reads as touchable, and the customer side is touched. -- */
  --radius-lg: 18px;
  --radius-md: 14px;
  --radius-sm: 10px;
  --pill: 999px;
  /* A family differs in more than colour. These three are the whole of it:
     how heavy a card's edge is, what colour that edge is, and how round a
     button gets. Anything else a family wants, it expresses in tokens it
     already has. */
  --card-stroke: 1px;
  --card-line:   var(--line);
  --btn-radius:  var(--radius-md);
  /* A hard offset shadow has a direction, and direction flips with the
     script. Without this, Pulse's shadow falls on the wrong side of every
     card in Persian. */
  --hard-x: 4px;

  /* -- motion. Two durations and two curves, and nothing invents a third.
        --spring overshoots; use it only where something should feel like
        it has mass (a button press, a card arriving). --ease does not;
        use it for anything that must not draw attention to itself. -- */
  --t-fast: 130ms;
  --t:      210ms;
  --t-slow: 380ms;
  --ease:   cubic-bezier(0.22, 0.61, 0.36, 1);
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  --font: "Vazirmatn", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "JetBrains Mono", Menlo, Consolas, monospace;

  /* One name survives from the old system: 24-board.js reads var(--border)
     for the drop-target outline it draws in JS. */
  --border: var(--line);
}

/* ------------------------------------------------ STUDIO -- dark ------- */
/* Warm charcoal, not blue-black: a neutral-cool dark under a warm orange
   makes the orange look dirty. The whole ground is pulled toward the brand. */
:root,
html[data-style="studio"] {
  --bg-0:        #14110e;
  --bg-1:        #1e1a15;
  --surface:     #1e1a15;
  --surface-2:   #272018;
  --surface-3:   #332a20;
  --line:        #3a3126;
  --line-2:      #524534;
  --field:       #110e0b;

  --text-1:      #f6f0e8;
  --text-2:      #c3b5a4;
  --text-3:      #a2968a;

  /* Brand. Bright enough to carry white-on-orange at small sizes is not
     possible, so filled controls take INK text -- measured, not assumed. */
  --accent:      #f97316;
  --accent-ink:  #fdba74;   /* accent as TEXT on a dark surface */
  --accent-soft: rgba(249, 115, 22, 0.15);
  --accent-line: rgba(249, 115, 22, 0.55);
  --on-accent:   #1a0f05;

  --ok:          #4ade80;
  --live:        #60a5fa;
  --warn:        #fbbf24;
  --err:         #fb7185;
  --idle:        #a2968a;
  --ok-bg:       rgba(74, 222, 128, 0.13);
  --live-bg:     rgba(96, 165, 250, 0.13);
  --warn-bg:     rgba(251, 191, 36, 0.13);
  --err-solid:   #e11d48;
  --err-bg:      rgba(251, 113, 133, 0.13);
  --idle-bg:     rgba(162, 150, 138, 0.13);
  --ok-line:     rgba(74, 222, 128, 0.45);
  --live-line:   rgba(96, 165, 250, 0.45);
  --warn-line:   rgba(251, 191, 36, 0.45);
  --err-line:    rgba(251, 113, 133, 0.48);
  --idle-line:   rgba(162, 150, 138, 0.30);

  --scrim:     rgba(10, 7, 4, 0.80);
  /* Two stacked shadows: a tight one that seats the edge, a wide soft one
     that lifts it. One shadow alone reads as a drawn outline. */
  --shadow:    0 2px 6px rgba(0, 0, 0, 0.40), 0 18px 40px -14px rgba(0, 0, 0, 0.70);
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.35), 0 6px 16px -10px rgba(0, 0, 0, 0.55);
  --press:     inset 0 2px 6px rgba(0, 0, 0, 0.45);
  --btn-lift:  0 3px 0 #b8500f, 0 10px 22px -10px rgba(249, 115, 22, 0.60);
  /* a bar pinned to the BOTTOM casts upward, or it reads as unattached */
  --shadow-up: 0 -8px 28px -18px rgba(0, 0, 0, 0.65);
  --ring:      var(--accent);
  --hero:      linear-gradient(160deg, #2b1c0e 0%, #1e1a15 62%, #14110e 100%);
}

/* ------------------------------------------------ STUDIO -- light ------ */
/* Not an inversion. Every text and status colour is re-picked so it still
   clears 4.5:1 the other way round; a hue legible on #14110e is not
   legible on warm paper. */
html[data-theme="light"],
html[data-style="studio"][data-theme="light"] {
  --bg-0:        #faf7f2;
  --bg-1:        #ffffff;
  --surface:     #ffffff;
  --surface-2:   #f6f1e9;
  --surface-3:   #efe7db;
  --line:        #eadfd0;
  --line-2:      #d3c3ae;
  --field:       #ffffff;

  --text-1:      #201a14;
  --text-2:      #574c41;
  --text-3:      #6e6256;

  --accent:      #c2410c;
  --accent-ink:  #9a3412;
  --accent-soft: rgba(194, 65, 12, 0.09);
  --accent-line: rgba(194, 65, 12, 0.42);
  --on-accent:   #ffffff;

  /* One notch darker than the obvious 700-weights: these are read as TEXT
     on a tinted chip over warm paper, where the 700s measure ~4.1:1. */
  --ok:          #166534;
  --live:        #1d4ed8;
  --warn:        #92400e;
  --err:         #b91c1c;
  --idle:        #6e6256;
  --ok-bg:       rgba(22, 101, 52, 0.10);
  --live-bg:     rgba(29, 78, 216, 0.10);
  --warn-bg:     rgba(146, 64, 14, 0.10);
  --err-solid:   #c62828;
  --err-bg:      rgba(185, 28, 28, 0.09);
  --idle-bg:     rgba(110, 98, 86, 0.10);
  --ok-line:     rgba(22, 101, 52, 0.40);
  --live-line:   rgba(29, 78, 216, 0.40);
  --warn-line:   rgba(146, 64, 14, 0.40);
  --err-line:    rgba(185, 28, 28, 0.42);
  --idle-line:   rgba(110, 98, 86, 0.28);

  --scrim:     rgba(48, 34, 20, 0.42);
  --shadow:    0 2px 4px rgba(78, 54, 30, 0.06), 0 14px 32px -14px rgba(78, 54, 30, 0.22);
  --shadow-sm: 0 1px 2px rgba(78, 54, 30, 0.05), 0 5px 14px -8px rgba(78, 54, 30, 0.16);
  --press:     inset 0 2px 5px rgba(78, 54, 30, 0.16);
  --btn-lift:  0 3px 0 #9a3412, 0 10px 20px -10px rgba(194, 65, 12, 0.45);
  --shadow-up: 0 -8px 26px -18px rgba(78, 54, 30, 0.30);
  --ring:      var(--accent);
  --hero:      linear-gradient(160deg, #fff3e6 0%, #ffe8d2 58%, #ffdcbd 100%);
}

/* ======================================================= motion ========= */
/* The vocabulary the whole app animates with. Declared once so a new screen
   reaches for a named move instead of inventing a duration.

   Every one of these is opacity/transform only. Animating width, height or
   top forces layout on each frame, which is what makes a list of 40 order
   rows stutter on the shop-floor machine. */
@keyframes pf-rise   { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes pf-fall   { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@keyframes pf-pop    { from { opacity: 0; transform: scale(.94); }       to { opacity: 1; transform: none; } }
@keyframes pf-in-s   { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
@keyframes pf-sheen  { from { transform: translateX(-120%); }            to { transform: translateX(220%); } }
@keyframes pf-pulse  { 0%,100% { opacity: 1; } 50% { opacity: .45; } }
@keyframes pf-spin   { to { transform: rotate(360deg); } }
@keyframes pf-bob    { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }

/* Accessibility is not a mode here: motion carries meaning, so it is
   SHORTENED to near-instant rather than removed, and nothing that only
   decorates survives at all. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }

}

/* ============================================================== base ==== */
* { box-sizing: border-box; margin: 0; padding: 0; }

/* CRITICAL: [hidden] must always beat any display rule. Without this,
   .login-wrap { display:flex } wins and the login form renders on top of
   the panel it is supposed to replace. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--font);
  color: var(--text-1);
  background: var(--bg-0);
  min-height: 100vh;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--accent-soft); }

/* Numbers must not dance. Every place a figure can change in place —
   a price, a countdown, a percentage — gets fixed-width digits. */
.num,
.mono,
.tdy-code,
.brd-code,
.desk-price,
input[type="number"],
td,
th,
time { font-variant-numeric: tabular-nums; }

.num  { direction: ltr; unicode-bidi: embed; }
.mono { font-family: var(--font-mono); font-size: .88em; direction: ltr; unicode-bidi: embed; }

/* Mixed Persian/Latin lines (a code inside a sentence) keep their own
   direction instead of inheriting the paragraph's. */
.toast { unicode-bidi: plaintext; }

* { scrollbar-color: var(--line-2) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: var(--pill); border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: var(--text-3); background-clip: content-box; }
::-webkit-scrollbar-track { background: transparent; }

/* The old ambient orbs are gone on purpose: three 90px-blur layers behind
   every scroll bought atmosphere and cost a full-screen repaint. The hooks
   stay so the markup does not have to change. */
.bg-blobs { display: none; }

/* ============================================================ layout ==== */
.container { width: min(1240px, 94vw); margin-inline: auto; }
.wide-container { inline-size: min(1440px, 97vw); margin: var(--sp-3) auto; }

.topbar {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: var(--sp-4);
  min-height: 56px;
  padding: var(--sp-2) var(--sp-4);
  margin-bottom: var(--sp-3);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  border-radius: 0;
}
.topbar .brand {
  display: flex; align-items: center; gap: var(--sp-2);
  font-weight: 700; font-size: 1rem; letter-spacing: 0.2px;
  min-width: 0;
}
/* Whatever the brand block holds -- a name, or a name over a tagline -- it
   gives way before the bar does. The customer panel puts a strapline under
   the name, and on a phone that line was 24px wider than the box it sat in. */
.topbar .brand > :not(.logo):not(img):not(svg) {
  min-width: 0; overflow: hidden;
}
.topbar .brand > :not(.logo):not(img):not(svg) > * {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topbar .brand strong { white-space: nowrap; }
/* v1.6.3 — a brand image must never render at its natural SVG size and
   push the menu off the bar. */
.topbar .brand img, .topbar .brand svg, .topbar .brand .logo {
  width: 28px; height: 28px; flex: none;
  border-radius: var(--radius-sm); object-fit: contain;
}
.topbar .brand .logo { display: grid; place-items: center; font-size: 16px; background: var(--accent-soft); }
.topbar .spacer { flex: 1; }
.topbar-actions { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; margin-inline-start: auto; }

/* The service panel's bar spans the window; the customer panel's sits
   inside the reading column, so it keeps its corners. */
.container > .topbar { border-radius: 0 0 var(--radius-lg) var(--radius-lg); border: 1px solid var(--line); border-top: 0; }

.tab-view { animation: fadeIn var(--t) var(--ease); }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.service-body { min-height: 100vh; }

/* ============================================================= cards ==== */
.glass {
  background: var(--surface);
  border: var(--card-stroke) solid var(--card-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  position: relative;
  transition: box-shadow var(--t) var(--ease), transform var(--t) var(--ease);
}
/* A card inside a card must not repeat the same fill, or the boundary
   disappears; it steps up one surface instead. */
.glass .glass { background: var(--surface-2); }
.card { padding: var(--card-pad); }
.card + .card { margin-top: var(--sp-3); }

h1 { font-size: 1.5rem; font-weight: 700; line-height: 1.35; }
h2 { font-size: 1.2rem; font-weight: 700; line-height: 1.4; }
h3 { font-size: 1rem;   font-weight: 700; line-height: 1.45; }
h4 { font-size: .92rem; font-weight: 700; }
h5 { font-size: .85rem; font-weight: 700; }
.muted { color: var(--text-2); }
.small { font-size: 0.86rem; }
.tiny  { font-size: 0.76rem; color: var(--text-3); }
.sub   { font-size: 0.8rem; color: var(--text-2); }
.narrow { max-width: 640px; }

/* =========================================================== buttons ==== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font-family: inherit; font-size: 0.88rem; font-weight: 600;
  text-decoration: none;   /* an <a class="btn"> is a button, not a link */
  padding: 8px 16px; min-height: 34px;
  border-radius: var(--btn-radius);
  border: 1px solid var(--line-2);
  background: var(--surface-2);
  color: var(--text-1);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease),
              transform var(--t-fast) var(--spring),
              color var(--t-fast) var(--ease);
}
.btn:hover { background: var(--surface-3); border-color: var(--line-2); transform: translateY(-1px); }
.btn:active { background: var(--surface-2); transform: translateY(1px); box-shadow: var(--press);
  transition-duration: 40ms; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: none; }
/* The one control on the page that carries real weight, so it is the one
   that gets real thickness: a solid edge underneath that compresses when
   pressed. Nothing else in the system does this. */
.btn.primary {
  background: var(--accent); border-color: transparent; color: var(--on-accent);
  box-shadow: var(--btn-lift);
}
.btn.primary:hover { filter: brightness(1.06); transform: translateY(-2px); }
.btn.primary:active { transform: translateY(2px); box-shadow: var(--press); filter: none; }
.btn.danger { background: var(--err-bg); border-color: var(--err-line); color: var(--err); }
.btn.danger:hover { background: var(--err-bg); border-color: var(--err); }
.btn.ok { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.btn.ok:hover { border-color: var(--ok); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.btn.ghost:hover { background: var(--surface-2); color: var(--text-1); }
.btn.sm, .btn.small { padding: 4px 10px; min-height: 28px; font-size: 0.8rem; }
/* Touch: 44x44 is the floor on a pointer that is a finger, and the customer
   panel is all finger. Applied by pointer type, not by width -- a tablet at
   1024px is still a finger. */
@media (pointer: coarse) {
  .btn, .icon-btn { min-height: 44px; }
  .btn.sm, .btn.small { min-height: 38px; padding: 8px 14px; }
  .icon-btn { inline-size: 44px; block-size: 44px; }
}
.btn.wide { width: 100%; }
.btn.link { text-decoration: none; }

.icon-btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: none;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer; font-size: 15px; line-height: 1; color: var(--text-2);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.icon-btn:hover { background: var(--surface-2); color: var(--text-1); }
.icon-btn.active { background: var(--accent-soft); border-color: var(--accent-line); color: var(--text-1); }

/* ============================================================ inputs ==== */
label.field { display: block; margin-bottom: var(--sp-3); }
label.field > span { display: block; font-size: 0.8rem; color: var(--text-2); margin-bottom: 5px; }

input[type="text"], input[type="email"], input[type="password"], input[type="tel"],
input[type="number"], input[type="url"], input[type="search"],
input[type="date"], input[type="time"], input[type="datetime-local"],
select, textarea {
  width: 100%;
  font-family: inherit; font-size: 0.88rem;
  color: var(--text-1);
  background: var(--field);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  min-height: 34px;
  outline: none;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
input:hover, select:hover, textarea:hover { border-color: var(--text-3); }
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input:disabled, select:disabled, textarea:disabled { opacity: .55; cursor: not-allowed; }
textarea { min-height: 88px; resize: vertical; line-height: 1.6; }
select { appearance: none; padding-inline-end: 26px;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-3) 50%),
                    linear-gradient(135deg, var(--text-3) 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-position: right 12px center, right 7px center;
  background-repeat: no-repeat; }
[dir="rtl"] select { background-position: left 7px center, left 12px center;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-3) 50%),
                    linear-gradient(135deg, var(--text-3) 50%, transparent 50%); }
select option { background: var(--surface); color: var(--text-1); }

input[type="range"] { width: 100%; accent-color: var(--accent); min-height: 0; }
input[type="checkbox"], input[type="radio"] { width: 16px; height: 16px; min-height: 0; accent-color: var(--accent); }
input[type="color"] { padding: 2px; height: 34px; }

.toolbar { display: flex; align-items: center; gap: var(--sp-2); margin: var(--sp-3) 0; flex-wrap: wrap; }
.toolbar input[type="search"], .toolbar select { flex: 1 1 220px; max-width: 360px; }

/* toggle switch */
.switch { position: relative; display: inline-block; width: 40px; height: 22px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: absolute; inset: 0; border-radius: var(--pill); cursor: pointer;
  background: var(--surface-3); transition: background var(--t) var(--ease);
  border: 1px solid var(--line-2);
}
.switch .slider::before {
  content: ""; position: absolute; width: 16px; height: 16px; border-radius: 50%;
  background: var(--text-1); top: 2px; inset-inline-start: 2px;
  transition: transform var(--t) var(--ease);
}
.switch input:checked + .slider { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .slider::before { background: #fff; transform: translateX(18px); }
[dir="rtl"] .switch input:checked + .slider::before { transform: translateX(-18px); }

.check { display: flex; align-items: center; gap: var(--sp-2); cursor: pointer; font-size: .88rem; }
.check input { width: auto; }

/* ====================================================== badges & chips == */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 0.76rem; font-weight: 600; line-height: 1.6;
  padding: 2px 9px; border-radius: var(--pill);
  background: var(--idle-bg);
  border: 1px solid var(--idle-line);
  color: var(--text-2);
  white-space: nowrap;
}
.badge.ok   { color: var(--ok);     background: var(--ok-bg);   border-color: var(--ok-line); }
.badge.warn { color: var(--warn);   background: var(--warn-bg); border-color: var(--warn-line); }
.badge.err  { color: var(--err);    background: var(--err-bg);  border-color: var(--err-line); }
.badge.info { color: var(--accent-ink); background: var(--accent-soft); border-color: var(--accent-line); }

/* Machine states are the same four colours everywhere in the app. */
.badge.st-ready    { color: var(--ok);   background: var(--ok-bg);   border-color: var(--ok-line); }
.badge.st-printing { color: var(--live); background: var(--live-bg); border-color: var(--live-line); }
.badge.st-paused   { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-line); }
.badge.st-error    { color: var(--err);  background: var(--err-bg);  border-color: var(--err-line); }
.badge.st-offline  { color: var(--text-3); background: var(--idle-bg); border-color: var(--idle-line); }

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--pill);
  font-size: .78rem; white-space: nowrap;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: inherit;
}

.engine-chip { background: var(--accent-soft); border: 1px solid var(--accent-line);
  border-radius: var(--pill); padding: 2px 9px; font-size: .76rem; color: var(--accent-ink); }

/* ============================================================== tabs ==== */
.tabs { display: flex; gap: var(--sp-1); flex-wrap: wrap; }
.tab {
  padding: 6px 14px; border-radius: var(--radius-md); cursor: pointer;
  font-size: 0.88rem; font-weight: 600; color: var(--text-2);
  border: 1px solid transparent; background: transparent;
  font-family: inherit;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.tab:hover { color: var(--text-1); background: var(--surface-2); }
.tab.active { color: var(--text-1); background: var(--surface-3); border-color: var(--line-2); }

/* The main nav uses an underline instead of a pill: it is the one control
   that must read as "where am I", and an underline survives being crowded
   by thirteen admin tabs where a pill row does not. */
.main-tabs { display: flex; gap: 2px; flex-wrap: wrap; }
.main-tabs .tab {
  border: 0; border-bottom: 2px solid transparent; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: transparent; color: var(--text-2);
  padding: 8px 12px; cursor: pointer; font: inherit; font-weight: 600; font-size: .88rem;
}
.main-tabs .tab:hover { color: var(--text-1); background: var(--surface-2); }
.main-tabs .tab.active {
  color: var(--text-1); background: transparent;
  border-bottom-color: var(--accent); box-shadow: none;
}
.main-tabs.sub { border-bottom: 1px solid var(--line); margin-bottom: var(--sp-3); }

/* ============================================================= table ==== */
table.list, .data-table { width: 100%; border-collapse: collapse; font-size: .88rem; }
table.list th, .data-table th {
  text-align: start; color: var(--text-2); font-weight: 600; font-size: .76rem;
  text-transform: uppercase; letter-spacing: .04em;
  padding: 8px 10px; border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--surface); z-index: 1;
}
table.list td, .data-table td {
  padding: 8px 10px; border-bottom: 1px solid var(--line);
  height: var(--row-h);
}
table.list tr, .data-table tr { transition: background var(--t-fast) var(--ease); }
table.list tr:hover td, .data-table tr:hover td { background: var(--surface-2); }
table.list tr.clickable, .data-table tr.clickable { cursor: pointer; }
.row-actions { display: flex; gap: var(--sp-1); justify-content: flex-end; }
.admin-toolbar { display: flex; gap: var(--sp-2); margin: 0 0 var(--sp-3); flex-wrap: wrap; align-items: center; }
.admin-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--sp-3); margin: var(--sp-3) 0; align-items: end;
  padding: var(--sp-4); border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface-2);
}
.admin-form .field.wide, .admin-form .field.full { grid-column: 1 / -1; }
/* v1.89.0: group titles and the folded «more settings» box of an admin form */
.admin-form .form-h { grid-column: 1 / -1; margin: var(--sp-2) 0 0; font-size: .95rem; color: var(--text-1); }
.admin-form .form-h:first-child { margin-top: 0; }
.admin-form details.form-more { grid-column: 1 / -1; }
.admin-form details.form-more > summary { cursor: pointer; color: var(--text-2); font-size: .88rem; }
.admin-form details.form-more[open] {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--sp-3); align-items: end;
}
.admin-form details.form-more[open] > summary { grid-column: 1 / -1; }
#customer-modal details.form-more { margin: var(--sp-2) 0; }
#customer-modal details.form-more > summary { cursor: pointer; color: var(--text-2); }
#admin-tabs .tab-more { margin-inline-start: auto; }
/* v1.89.0: the health page, one light per check */
.health-list { display: grid; gap: var(--sp-2); }
.health-row {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); align-items: baseline;
  padding: var(--sp-3); border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface-2);
}
.health-row b { min-inline-size: 9em; }
.health-dot { flex: none; inline-size: 12px; block-size: 12px; border-radius: 50%; background: var(--ok); }
.health-dot.bad { background: var(--err); }
.health-dot.info { background: var(--idle); }

/* U-9: one empty state, everywhere. */
.empty {
  padding: var(--sp-6) var(--sp-4); text-align: center; color: var(--text-3);
  border: 1px dashed var(--line-2); border-radius: var(--radius-md); margin: var(--sp-2) 0;
  font-size: .88rem;
}
.empty::before { content: "—"; display: block; font-size: 1.4rem; margin-bottom: 6px; opacity: .6; }

/* =========================================================== kanban ===== */
.kanban { display: flex; gap: var(--sp-3); overflow-x: auto; padding-bottom: var(--sp-2); }

/* ============================================================ price ===== */
.price-hero { text-align: center; padding: var(--sp-5) var(--sp-4); }
.price-hero .amount { font-size: 2.2rem; font-weight: 800; color: var(--text-1); letter-spacing: -0.01em; }
.breakdown { font-size: .86rem; }
.breakdown .row { display: flex; justify-content: space-between; padding: 6px 2px; border-bottom: 1px solid var(--line); }
.breakdown .row.total { font-weight: 800; border-bottom: none; font-size: 1.02rem; color: var(--text-1); }

/* =================================================== toasts & callouts == */
#toasts {
  position: fixed; bottom: var(--sp-5); inset-inline-start: 50%;
  transform: translateX(-50%); z-index: 200;
  display: flex; flex-direction: column; gap: var(--sp-2); align-items: center;
}
[dir="rtl"] #toasts { transform: translateX(50%); }
.toast {
  padding: 10px 18px; border-radius: var(--radius-md);
  background: var(--surface-3);
  border: 1px solid var(--line-2);
  border-inline-start: 3px solid var(--text-3);
  box-shadow: var(--shadow);
  color: var(--text-1);
  font-size: .88rem;
  animation: toast-in var(--t) var(--ease);
  max-width: min(92vw, 560px);
}
.toast.ok  { border-inline-start-color: var(--ok); }
.toast.err, .toast.error { border-inline-start-color: var(--err); }
.toast.warn { border-inline-start-color: var(--warn); }
.toast.login-prompt { border-inline-start-color: var(--accent); font-weight: 600; text-align: center; line-height: 1.7; }
.toast-action { margin-inline-start: var(--sp-2); padding: 2px 9px; border-radius: var(--radius-sm);
  border: 1px solid currentColor; background: transparent; color: inherit; font: inherit; cursor: pointer; }
.toast-action:hover { opacity: .8; }
.error-text { color: var(--err); font-size: .88rem; margin: var(--sp-2) 0 0; text-align: center; }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }

/* A callout is a sentence with a stake in it: the colour is the stake, and
   it is carried on the inline edge so the text stays on a readable fill. */
.callout {
  border: 1px solid var(--warn-line);
  border-inline-start: 3px solid var(--warn);
  background: var(--warn-bg);
  border-radius: var(--radius-md);
  padding: var(--sp-3) var(--sp-4); margin: var(--sp-2) 0; font-size: .88rem;
}
.callout.ok  { border-color: var(--ok-line);  border-inline-start-color: var(--ok);  background: var(--ok-bg); }
.callout.err { border-color: var(--err-line); border-inline-start-color: var(--err); background: var(--err-bg); }
.callout.info { border-color: var(--accent-line); border-inline-start-color: var(--accent); background: var(--accent-soft); }
/* inline status text (e.g. #job-support-info) */
div.warn, p.warn { color: var(--warn); }
div.ok, p.ok { color: var(--ok); }
.ai-reasons { background: var(--accent-soft); border: 1px solid var(--accent-line);
  border-radius: var(--radius-md); padding: var(--sp-2) var(--sp-3); margin: var(--sp-2) 0; font-size: .86rem; }

/* =========================================================== modals ===== */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: var(--scrim);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  display: grid; place-items: center; padding: var(--sp-5);
}
.modal { width: min(760px, 96vw); max-height: 88vh; overflow-y: auto; }

/* Service panel overlay modals: hidden by default, shown as a full-screen
   overlay when JS adds .open via showModal()/hideModal().
   NOTE: this MUST be a dedicated class (.pf-modal). Reusing `.modal` here
   conflicted with the customer panel, where `.modal` is the *card* inside
   `.modal-backdrop` — it made the login/register dialogs invisible. */
.pf-modal { display: none; }
.pf-modal.open {
  display: grid; place-items: center;
  position: fixed; inset: 0; z-index: 100;
  background: var(--scrim);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  padding: var(--sp-5); overflow-y: auto;
}
.modal-card {
  width: min(980px, 94vw); max-height: 88vh; overflow: auto; position: relative;
  padding: var(--sp-5) var(--card-pad) var(--card-pad);
  box-shadow: var(--shadow);
}
.modal-card.narrow { width: min(480px, 94vw); }
.modal-card.large  { width: min(1100px, 96vw); }
/* Only the small × that is a direct child of a modal card is absolutely
   positioned. (The customer panel reuses .modal-close on a normal Cancel
   button inside forms — that one must stay in the flow.) */
.modal-card > .modal-close {
  position: absolute; top: var(--sp-2); inset-inline-end: var(--sp-2); z-index: 40;
  border: 1px solid transparent; background: transparent; color: var(--text-2);
  width: 30px; height: 30px; border-radius: var(--radius-sm); cursor: pointer; font-size: 1.05rem;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.modal-card > .modal-close:hover { background: var(--surface-3); color: var(--text-1); }
.modal-footer {
  position: sticky; bottom: -1px; display: flex; justify-content: flex-end;
  gap: var(--sp-2); margin-top: var(--sp-4); padding: var(--sp-2) 2px;
  border-top: 1px solid var(--line); background: var(--surface);
}
#form-modal-body { padding: var(--sp-1) 2px var(--sp-3); }
#form-modal-body h3 { margin-bottom: var(--sp-3); }

/* U-2: styled confirm dialog (also used by the unsaved-changes prompt) */
.confirm-overlay { z-index: 300; }
.confirm-card { padding: var(--sp-5); }
.confirm-msg { margin: 0; font-size: .95rem; line-height: 1.7; }
.confirm-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: flex-end; margin-top: var(--sp-4); }

/* ============================================================ login ===== */
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: var(--sp-5); }
.login-card {
  width: min(380px, 92vw); display: flex; flex-direction: column; gap: var(--sp-3);
  text-align: center; padding: var(--sp-6) var(--sp-5); box-shadow: var(--shadow);
}
.login-card .logo { width: 56px; height: 56px; margin: 0 auto; border-radius: var(--radius-md); }
.login-card h1 { font-size: 1.15rem; margin: 0; }
.login-card .field { text-align: start; }

/* ===================================== header tools & notifications ===== */
.hdr-tools { display: inline-flex; gap: var(--sp-1); align-items: center; }
.hdr-search {
  width: 0; opacity: 0; padding: 6px 8px; min-height: 30px;
  border-radius: var(--radius-sm); border: 1px solid var(--line-2);
  background: var(--field); color: inherit;
  transition: width var(--t) var(--ease), opacity var(--t-fast) var(--ease);
}
.hdr-search.open { width: 180px; opacity: 1; }
.notif-wrap { position: relative; display: inline-flex; }
.notif-count {
  position: absolute; top: -2px; inset-inline-end: -2px; min-width: 16px; height: 16px;
  border-radius: var(--pill); background: var(--err-solid); color: #fff; font-size: 10px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; padding: 0 4px;
}
.notif-count.has-unseen { box-shadow: 0 0 0 3px var(--err-bg); animation: notifPulse 1.6s infinite; }
@keyframes notifPulse { 50% { transform: scale(1.12); } }
/* 17.23 — the panel is fixed above the content, not clipped by the header */
.notif-panel {
  position: fixed; top: var(--float-top); inset-inline-end: 18px; width: 340px;
  max-height: 62vh; overflow-y: auto; z-index: 300;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: var(--sp-2);
  box-shadow: var(--shadow); text-align: start;
}
.notif-head { padding: 2px 6px var(--sp-2); border-bottom: 1px solid var(--line); margin-bottom: var(--sp-2); }
.notif-item {
  border-inline-start: 3px solid var(--idle);
  background: var(--surface-2);
  border-radius: var(--radius-sm); padding: 7px 9px; margin-bottom: 6px;
  cursor: pointer; transition: background var(--t-fast) var(--ease);
}
.notif-item:hover { background: var(--surface-3); }
.notif-item-top { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2); }
.notif-lock { opacity: .8; font-size: 12px; cursor: help; }
.notif-close { background: none; border: none; color: var(--text-2); font-size: 15px; cursor: pointer; padding: 0 2px; }
.notif-close:hover { color: var(--text-1); }
#notif-toasts { position: fixed; top: var(--float-top); inset-inline-end: 16px; z-index: 320;
  display: flex; flex-direction: column; gap: var(--sp-2); max-width: 320px; }
.notif-toast {
  border-inline-start: 3px solid var(--idle); background: var(--surface-3);
  border: 1px solid var(--line-2); border-radius: var(--radius-md);
  padding: var(--sp-2) var(--sp-3); cursor: pointer;
  box-shadow: var(--shadow); animation: notifIn var(--t) var(--ease);
}
@keyframes notifIn { from { transform: translateY(-6px); opacity: 0; } }
.nt-title { font-weight: 600; font-size: 13px; }
.nt-body { margin-top: 3px; font-size: .84rem; color: var(--text-2); }
.nt-time { margin-top: 3px; font-size: .74rem; color: var(--text-3); }

/* =========================================================== wizard ===== */
.steps { display: flex; gap: 2px; padding: var(--sp-2) var(--sp-3); overflow-x: auto; }
.step {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: var(--radius-md);
  font-size: .84rem; color: var(--text-2); white-space: nowrap;
}
.step .dot {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  font-size: .74rem; font-weight: 700;
  background: var(--surface-2); border: 1px solid var(--line-2);
}
.step.active { color: var(--text-1); background: var(--surface-2); }
.step.active .dot { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.step.done .dot { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
/* v1.49.0: the service panel's step shell reuses .steps/.step, but its steps
   are real buttons (clickable, dimmed while a step is not built yet). */
.steps button.step { background: none; border: 0; font: inherit; cursor: pointer; }
.steps button.step:disabled { opacity: .45; cursor: not-allowed; }
.steps button.step .hint { font-size: .7rem; color: var(--text-3); }

/* v1.49.0 restored-draft notice (43-wizard-draft.js) */
.wiz-draft {
  display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center;
  justify-content: space-between; padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-2);
  border-inline-start: 3px solid var(--warn);
}
.wiz-draft[hidden] { display: none; }

/* =========================================================== upload ===== */
.dropzone {
  border: 2px dashed var(--line-2);
  border-radius: var(--radius-lg);
  padding: var(--sp-6) var(--sp-5);
  text-align: center; cursor: pointer;
  background: var(--surface-2);
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease);
}
.dropzone:hover { border-color: var(--accent-line); }
.dropzone.dragover, .dropzone.drag { border-color: var(--accent); background: var(--accent-soft); }
.dropzone .icon { font-size: 36px; margin-bottom: var(--sp-2); }
.dropzone-hero { min-height: 240px; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 1rem; }
.dropzone-hero .icon { font-size: 48px; }
.upload-processing { display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-md); background: var(--accent-soft); }

/* =========================================================== viewer ===== */
.viewer-wrap {
  position: relative; border-radius: var(--radius-lg); overflow: hidden;
  background: radial-gradient(ellipse at 50% 30%, #1b2438 0%, #0b111c 72%);
  border: 1px solid var(--line);
}
html[data-theme="light"] .viewer-wrap { background: radial-gradient(ellipse at 50% 30%, #e5eaf4 0%, #ccd5e6 72%); }
.viewer-canvas { width: 100%; height: 400px; display: block; }
.viewer-hud {
  position: absolute; top: var(--sp-3); inset-inline-start: var(--sp-3);
  display: flex; flex-direction: column; gap: 6px; pointer-events: none;
}
.viewer-hud .badge { pointer-events: auto; }
.viewer-legend {
  position: absolute; bottom: var(--sp-3); inset-inline-start: var(--sp-3);
  display: flex; gap: var(--sp-3); font-size: .76rem; color: var(--text-2);
}
.viewer-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-inline-end: 5px; vertical-align: -1px; }
.viewer-toolbar { position: absolute; top: var(--sp-3); inset-inline-end: var(--sp-3); z-index: 6;
  display: flex; flex-direction: column; gap: var(--sp-1); }
.vt-btn {
  width: 30px; height: 30px; border-radius: var(--radius-sm); cursor: pointer;
  border: 1px solid var(--line-2); background: var(--surface); color: var(--text-1);
  font-size: 14px; line-height: 1; display: flex; align-items: center; justify-content: center;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.vt-btn:hover { background: var(--surface-3); }
.vt-btn.on { background: var(--accent-soft); border-color: var(--accent); }
.viewer-overlay {
  position: absolute; inset-inline-end: var(--sp-3); z-index: 6;
  background: var(--surface); border: 1px solid var(--line); color: var(--text-1);
  border-radius: var(--radius-sm); padding: 4px 9px; font-size: .8rem; direction: ltr;
}
.viewer-dims { bottom: var(--sp-3); }
.viewer-measure { bottom: 48px; color: var(--warn); }

/* ====================================================== settings UI ===== */
.settings-group { margin-bottom: 6px; }
.settings-group > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-md);
  background: var(--surface-2); font-weight: 600; font-size: .9rem;
}
.settings-group > summary::-webkit-details-marker { display: none; }
.settings-group > summary::after { content: "›"; margin-inline-start: auto; transform: rotate(90deg); transition: transform var(--t-fast) var(--ease); color: var(--text-3); }
.settings-group[open] > summary::after { transform: rotate(-90deg); }
.settings-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--sp-2); padding: var(--sp-3) 2px;
}
.setting-item { padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-md); background: var(--surface-2); border: 1px solid var(--line); }
.setting-item .k { font-size: .8rem; color: var(--text-2); margin-bottom: 5px; display: flex; justify-content: space-between; gap: 6px; }
.setting-item .k code { font-size: .68rem; color: var(--text-3); direction: ltr; }
.setting-item.changed { border-color: var(--accent); }

.downloads { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-2); }
.orders-toolbar { display: flex; gap: var(--sp-2); align-items: center; margin-bottom: var(--sp-3); flex-wrap: wrap; }

#delivery-estimate { margin-top: 5px; font-size: .92em; }

.colors-box { margin-top: var(--sp-2); }
.color-row { display: flex; align-items: center; gap: var(--sp-2); padding: 5px 0; border-bottom: 1px solid var(--line); }
.color-chip { width: 18px; height: 18px; border-radius: 5px; border: 1px solid var(--line-2); flex: none; }

/* v1.48.0: colour dropdown with the real Spoolman swatch next to each name */
.color-picker { position: relative; }
.cp-btn, .cp-opt {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%;
  padding: 6px 9px; border-radius: var(--radius-sm); cursor: pointer;
  background: var(--surface-2); color: inherit;
  border: 1px solid var(--line-2); text-align: start;
}
.cp-btn:hover, .cp-opt:hover { background: var(--surface-3); }
.cp-list {
  position: absolute; inset-inline-start: 0; top: calc(100% + 4px);
  z-index: 40; width: 100%; max-height: 240px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px; padding: 4px;
  border-radius: var(--radius-md);
  background: var(--surface); border: 1px solid var(--line-2);
  box-shadow: var(--shadow);
}
.cp-swatch { width: 16px; height: 16px; flex: 0 0 16px; border-radius: 4px; border: 1px solid var(--line-2); }

/* ====================================== skeletons, spinners, states ===== */
.skeleton {
  border-radius: var(--radius-md);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite;
}
@keyframes shimmer { to { background-position: -200% 0; } }
.spinner {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--accent);
  animation: spin .8s linear infinite; display: inline-block; vertical-align: middle;
}
@keyframes spin { to { transform: rotate(360deg); } }
#slice-stage { min-height: 1.2em; margin-top: 5px; text-align: center; }

/* ==================================================== choice cards ====== */
/* Every "pick one of these" surface in the app — orientation, material,
   colour, machine, mode, customer, preset — is the same object: a bordered
   tile that states its choice with a 2px accent border, never with a fill
   that would fight the status colours inside it. */
.orient-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--sp-2); }
.orient-card, .mat-card, .mach-card, .mode-card, .color-card, .cust-card {
  padding: var(--sp-3); border-radius: var(--radius-md); cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--line); color: inherit;
  text-align: start;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.orient-card:hover, .mat-card:hover, .mach-card:hover,
.mode-card:hover, .color-card:hover, .cust-card:hover { border-color: var(--line-2); background: var(--surface-3); }
.orient-card.selected, .mat-card.selected, .mach-card.selected,
.mode-card.selected, .color-card.selected, .cust-card.selected {
  border-color: var(--accent); border-width: 2px; background: var(--accent-soft);
}
.orient-card .score { font-size: 1.35rem; font-weight: 800; }

.check-card {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3); border-radius: var(--radius-md);
  border: 1px solid var(--line); background: var(--surface-2);
}
.check-card span { display: grid; gap: 2px; }
.check-card small { color: var(--text-2); }

/* ======================================================== utilities ===== */
.row-flex { display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); }
.mt-1 { margin-top: var(--sp-2); } .mt-2 { margin-top: var(--sp-4); } .mt-3 { margin-top: var(--sp-5); }
.mb-2 { margin-bottom: var(--sp-4); }
.hidden { display: none !important; }
.text-center { text-align: center; }
.text-start { text-align: start; }
.field.inline { display: flex; flex-direction: row; align-items: center; gap: 6px; }
.field.inline input { width: 80px; }
.qty-wrap { display: inline-flex; align-items: center; gap: var(--sp-1); }
.qty-wrap input { width: 60px; text-align: center; }
.qty-wrap .btn { min-width: 30px; padding-inline: 8px; }
.link-button { border: 0; background: none; color: var(--accent-ink); font: inherit; font-weight: 700; padding: 3px; cursor: pointer; }
.link-button:hover { text-decoration: underline; }
.auth-switch { text-align: center; margin-top: var(--sp-4); color: var(--text-2); font-size: .9rem; }
.busy { opacity: .6; pointer-events: none; }

/* ==================================== accessibility: pointer & focus ==== */
button, [role="button"], select, summary, a,
.tab, .modal-close, .dropzone, .switch, label.check,
input[type="checkbox"], input[type="radio"], input[type="file"] { cursor: pointer; }
button:disabled, .btn:disabled { cursor: not-allowed; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.btn:focus-visible, .tab:focus-visible, .modal-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.switch input:focus-visible + .slider { box-shadow: 0 0 0 3px var(--accent-soft); }
a { color: var(--accent-ink); transition: color var(--t-fast) var(--ease); }
summary { transition: color var(--t-fast) var(--ease); }

/* prefers-reduced-motion is handled once, in the motion layer at the top. */

/* ================================== customer panel: hub & spec popup ==== */
.welcome-services { list-style: none; padding: 0; margin: var(--sp-3) auto; max-width: 560px; text-align: start; }
.welcome-services li { position: relative; padding-inline-start: 22px; margin: 6px 0; font-size: .92rem; }
.welcome-services li::before { content: "\2714"; position: absolute; inset-inline-start: 0; color: var(--ok); }
.hub-toolbar { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-3); flex-wrap: wrap; }
.multi-table .thumb-canvas { width: 88px; height: 66px; border-radius: var(--radius-sm); background: var(--surface-2); }
.multi-table td { vertical-align: middle; }
.multi-summary { border-top: 1px solid var(--line); padding-top: var(--sp-3); margin-top: var(--sp-3); }
.pay-line { display: flex; gap: var(--sp-2); align-items: center; margin: 5px 0; }

.spec-popup { position: fixed; inset: 0; z-index: 90; overflow-y: auto; background: var(--bg-0); }
.spec-shell { max-width: 1280px; margin: 0 auto; padding: var(--sp-3) clamp(10px, 3vw, 32px) 48px; }
.spec-head { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); position: sticky; top: var(--sp-2); z-index: 5; }
.spec-head .steps { flex: 1; display: flex; margin: 0; }
.spec-head #spec-file-name { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: ltr; }
.spec-cols { display: grid; gap: var(--sp-4); margin-top: var(--sp-3); align-items: start;
  grid-template-columns: minmax(0, 1fr) minmax(340px, 460px); grid-template-areas:"form viewer"; }
.spec-main { grid-area: form; }
.spec-side { grid-area: viewer; position: sticky; top: 72px; }
.spec-side .viewer-wrap { margin-top: 0; }
.spec-main > .wizard-step { margin-top: 0; }
.spec-loading { padding: var(--sp-5); text-align: center; color: var(--text-2); }
body.spec-open { overflow: hidden; }

.prompt-chips { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin: var(--sp-2) 0 var(--sp-4); }
.prompt-chip {
  border: 1px solid var(--accent-line); background: var(--accent-soft); color: var(--text-1);
  border-radius: var(--pill); padding: 6px 12px; font: inherit; font-size: .82rem; cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.prompt-chip:hover { background: var(--accent-soft); border-color: var(--accent); }

.support-live { padding: var(--sp-3); border-radius: var(--radius-md); background: var(--err-bg); border: 1px solid var(--err-line); }
.postprocess-options { display: grid; gap: var(--sp-2); padding: var(--sp-4); border-radius: var(--radius-md);
  background: var(--accent-soft); border: 1px solid var(--accent-line); }
.postprocess-options .check-card:has(input:disabled) { background: var(--ok-bg); border-color: var(--ok-line); }
.login-required-notice {
  display: flex; gap: var(--sp-3); padding: var(--sp-4); margin-top: var(--sp-3);
  border-radius: var(--radius-md); background: var(--accent-soft);
  border: 1px solid var(--accent-line); line-height: 1.7;
}
.login-required-notice .login-notice-icon { font-size: 26px; }
.login-required-notice p { color: var(--text-2); font-size: .9rem; }

/* ------------------------------------------- v1.19.0 oversize notice ---- */
.oversize-note {
  display: flex; flex-direction: column; gap: 3px;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-md); line-height: 1.8;
  background: var(--warn-bg); border: 1px solid var(--warn-line); font-size: .86rem;
}
.oversize-note .ov-title { font-weight: 700; color: var(--warn); }
.oversize-note .ov-phone { font-weight: 700; direction: ltr; unicode-bidi: embed; letter-spacing: .5px; font-size: 1.05em; }
.multi-table tr.oversize-row td { vertical-align: middle; }

/* ================================ new-job desk: customers & parts ======= */
.cust-list { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0; }
.cust-card { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; min-width: 180px; padding: 7px 11px; }
.cust-new { margin: var(--sp-2) 0; padding: var(--sp-2) 0; }
.cust-chosen-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; padding: 5px 0; }
.cust-orders { margin-top: var(--sp-2); }

.part-card { display: flex; flex-direction: column; gap: 3px; padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-2); }
.part-card.active { border-color: var(--accent); border-width: 2px; }

.mat-head { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; justify-content: space-between; }
.mat-grid, .mach-grid, .mode-grid, .color-grid { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0; }
.mat-card, .mach-card { display: flex; flex-direction: column; gap: 1px; min-width: 160px; padding: 7px 10px; }
.mat-card.out, .mach-card.out { opacity: .5; }
.mach-card.auto { border-style: dashed; }
.mat-name, .mach-name, .mode-name, .color-name { font-weight: 600; }
.mat-sub, .mat-warn, .mach-sub, .mach-live, .mach-warn { font-size: .84em; }
.mat-sub, .mach-sub { color: var(--text-2); }
.mat-warn { color: var(--warn); }
.mach-warn { color: var(--err); }
.mach-live.ok { color: var(--ok); }
.mach-live.warn { color: var(--warn); }
.mach-live.bad { color: var(--err); }
.mach-live.muted { color: var(--text-3); }
.color-card { display: flex; gap: var(--sp-2); align-items: center; min-width: 165px; padding: 6px 9px; }
.color-card.out { opacity: .45; cursor: not-allowed; }
.color-text { display: flex; flex-direction: column; text-align: start; }
.color-stock { font-size: .82em; }
.color-stock.ok   { color: var(--ok); }
.color-stock.warn { color: var(--warn); }
.color-stock.bad  { color: var(--err); }
.color-stock.muted { color: var(--text-3); }
.swatch { width: 20px; height: 20px; border-radius: 50%; flex: 0 0 auto; border: 1px solid var(--line-2); }
.chip.any-color, .chip.preset { cursor: pointer; }
.chip.any-color.selected, .chip.preset.selected { border-color: var(--accent); border-width: 2px; background: var(--accent-soft); }
.mode-card { padding: 7px 12px; }
.preset-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.setup-services h5 { margin: var(--sp-2) 0 5px; }
.svc-row { display: flex; gap: var(--sp-2); align-items: center; padding: 2px 0; }
.svc-auto { font-size: .8em; color: var(--text-3); }

.pric-sub { font-size: .84em; color: var(--text-2); }
.pric-total { font-size: 1.08em; font-weight: 700; margin: 3px 0; }
.pric-total.agreed { color: var(--accent-ink); }
.pric-breakdown { margin: 3px 0; }
.pric-row { display: flex; justify-content: space-between; gap: var(--sp-3); font-size: .9em; }
.pric-key { color: var(--text-2); }
.pric-override { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--sp-2); }
.pric-override input { min-width: 160px; }

.sch-foot { display: flex; justify-content: space-between; gap: var(--sp-3); padding-top: var(--sp-2);
  font-weight: 700; border-top: 1px solid var(--line); }

/* review, commit */
.cmt-review { display: flex; flex-direction: column; gap: 3px; margin: var(--sp-2) 0; }
.cmt-row { display: flex; justify-content: space-between; gap: var(--sp-3); }
.cmt-key { color: var(--text-2); }
.cmt-val.mono { font-family: var(--font-mono); }
.cmt-row.cmt-total { font-weight: 700; font-size: 1.05em; }
.cmt-flags { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0; }
.cmt-code { font-size: 1.5em; font-weight: 700; letter-spacing: .04em; margin: 5px 0; }
.cmt-codes { display: flex; flex-direction: column; gap: 3px; margin: var(--sp-2) 0; }
.cmt-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }

/* ==================================================== the order drawer == */
/* A drawer, not a page: the operator keeps the list behind it and never loses
   their place in it. It slides in from the inline end, so it works unchanged
   in the RTL panel and in the LTR one. */
.odw { position: fixed; inset: 0; z-index: 70; }
.odw[hidden] { display: none; }
.odw-scrim { position: absolute; inset: 0; background: var(--scrim);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
.odw-panel {
  position: absolute; inset-block: 0; inset-inline-end: 0;
  width: min(560px, 100%); overflow-y: auto; padding: 0 0 2rem;
  background: var(--surface); box-shadow: var(--shadow);
  border-inline-start: 1px solid var(--line);
}
.odw-head {
  position: sticky; top: 0; z-index: 2; display: flex;
  align-items: center; justify-content: space-between; gap: .75rem;
  padding: .7rem var(--sp-4); background: var(--surface);
  border-block-end: 1px solid var(--line);
}
.odw-head h3 { margin: 0; font-size: 1rem; }
.odw-body { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
body.odw-open { overflow: hidden; }

.odw-facts { display: grid; grid-template-columns: 1fr 1fr; gap: 4px var(--sp-4); }
.odw-fact { display: flex; align-items: baseline; gap: 5px; min-width: 0; }
.odw-key { flex: 0 0 auto; font-size: .78rem; color: var(--text-3); }
.odw-val { min-width: 0; overflow-wrap: anywhere; }
.odw-fact.bad .odw-val { color: var(--err); }
.odw-fact.odw-total .odw-val { font-weight: 700; font-size: 1.05rem; }
.odw-note { margin: 5px 0 0; font-size: .84rem; color: var(--text-2); }

.odw-sec { border: 1px solid var(--line); border-radius: var(--radius-md); padding: var(--sp-3); background: var(--surface-2); }
.odw-sec-h { margin: 0 0 var(--sp-2); font-size: .88rem; color: var(--text-2); }
.odw-sec-b { display: flex; flex-direction: column; gap: var(--sp-2); }
.odw-empty { font-size: .84rem; color: var(--text-3); }
.odw-load { padding: var(--sp-5); text-align: center; color: var(--text-3); }

.odw-risk { border-radius: var(--radius-sm); padding: 6px var(--sp-2); font-size: .84rem; }
.odw-risk.ok  { background: var(--ok-bg);  color: var(--ok); }
.odw-risk.bad { background: var(--err-bg); color: var(--err); }
.odw-risk-row + .odw-risk-row { margin-block-start: 3px; }

.odw-item { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: var(--sp-2); background: var(--surface); }
.odw-item-head { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-block-end: 5px; }
.odw-item-name { font-weight: 600; overflow-wrap: anywhere; }
.odw-qty { color: var(--text-2); }
.odw-code { font-size: .78rem; color: var(--text-2); }
.odw-item-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 3px var(--sp-3); }
.odw-chip { display: inline-flex; align-items: center; gap: 4px; border-radius: var(--pill);
  padding: 1px 8px; font-size: .78rem; background: var(--surface-2); border: 1px solid var(--line); }
.odw-chip.warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.odw-dot { width: .7rem; height: .7rem; border-radius: 50%; border: 1px solid var(--line-2); }
.odw-warn { color: var(--warn); }
.odw-svc { display: flex; flex-wrap: wrap; gap: 4px; margin-block-start: 5px; }
.odw-price.agreed b { color: var(--accent-ink); }
.odw-strike { text-decoration: line-through; color: var(--text-3); font-size: .8rem; }
.odw-why { font-size: .78rem; color: var(--text-3); }
.odw-item-actions { display: flex; flex-wrap: wrap; gap: 4px; }
.odw-item-actions:empty { display: none; }

.odw-money { display: grid; grid-template-columns: 1fr 1fr; gap: 4px var(--sp-4); }
.odw-money .odw-total .odw-val { font-weight: 700; }
.odw-money .bad .odw-val { color: var(--err); }
.odw-pay { width: 100%; font-size: .82rem; margin-block-start: 6px; }
.odw-pay td.bad { color: var(--err); }
/* v1.71.0: recording money is one line -- amount, how, a note, the button. */
.oda-pay input[type="number"] { inline-size: 11ch; }
.oda-pay input[type="text"] { flex: 1 1 14ch; min-inline-size: 10ch; }

.odw-time { display: flex; flex-direction: column; gap: 4px; max-height: 15rem; overflow-y: auto; }
.odw-time-row { display: grid; grid-template-columns: auto 1fr auto; gap: 6px; font-size: .82rem; align-items: baseline; }
.odw-time-when { color: var(--text-3); }
.odw-time-who { color: var(--text-3); }
.odw-time-note { grid-column: 1 / -1; color: var(--text-2); }

.odw-actions { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.odw-actions:empty { display: none; }
.odw-act-row { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; width: 100%; }

/* §4 — the decision list on a customer's order. It sits above the warnings
   because it is not a warning: it is the work. */
.odw-review { display: flex; flex-direction: column; gap: 6px; padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-md); border: 1px solid var(--line); }
.odw-review.bad { border-color: var(--warn-line); background: var(--warn-bg); }
.odw-review.ok  { border-color: var(--ok-line);  background: var(--ok-bg); }
.odw-review-dot { margin-inline-end: var(--sp-2); color: var(--text-2); }

/* The two files a finished part leaves behind, offered together. */
.oda-outputs { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center;
  margin-block-start: 6px; }

/* §4 — booking a machine and a slot from inside the drawer. */
.oda-sched { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-block-start: 5px; }
.oda-sched select { min-inline-size: 12ch; }
.oda-sched p { flex-basis: 100%; margin: 0; }

/* §4 — the promised price beside the current one, and the two honest answers. */
.oda-gap { display: flex; flex-direction: column; gap: 3px; margin-block-start: 6px;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-md);
  border: 1px solid var(--warn-line); background: var(--warn-bg); }
.oda-gap-row { font-size: .9rem; }
.oda-gap-acts { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-block-start: 3px; }

/* ======================================================= today screen === */
/* Columns, not a kanban: nothing here is dragged, because nothing here is a
   state machine — the drawer moves work forward. Every edge is logical so
   the layout survives both writing directions. */
.tdy-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-2); }
.tdy-day { display: flex; flex-direction: column; line-height: 1.25; }
.tdy-day-main { font-size: 1.1rem; font-weight: 600; }
.tdy-day-alt { font-size: .76rem; color: var(--text-3); }
.tdy-sum { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 auto; }
.tdy-tools { display: flex; gap: 6px; margin-inline-start: auto; }
.tdy-chip {
  display: inline-flex; align-items: baseline; gap: 4px;
  padding: 2px 9px; border-radius: var(--pill); font-size: .8rem;
  background: var(--idle-bg); border: 1px solid var(--idle-line);
}
.tdy-chip b { font-size: .95rem; }
.tdy-chip.ok   { border-color: var(--ok-line);   color: var(--ok); }
.tdy-chip.go   { border-color: var(--accent-line); color: var(--accent-ink); }
.tdy-chip.live { border-color: var(--live-line); color: var(--live); }
.tdy-chip.warn { border-color: var(--warn-line); color: var(--warn); }
.tdy-chip.bad  { border-color: var(--err-line);  color: var(--err); }

.tdy-fleet, .tdy-alerts { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--sp-2); }
.tdy-fleet-sum { display: flex; flex-wrap: wrap; gap: 6px; }
.tdy-machine { display: flex; align-items: center; gap: var(--sp-2); font-size: .85rem; }
.tdy-mname { min-width: 9ch; color: var(--text-2); }
.tdy-dots { display: inline-flex; gap: 3px; }
.tdy-dot, .brd-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--idle); }
.tdy-dot.ready,    .brd-dot.ready    { background: var(--ok); }
.tdy-dot.printing, .brd-dot.printing { background: var(--live); }
.tdy-dot.paused,   .brd-dot.paused   { background: var(--warn); }
.tdy-dot.error,    .brd-dot.error    { background: var(--err); }
.tdy-dot.offline,  .brd-dot.offline, .brd-dot.unknown { background: var(--idle); opacity: .5; }
.tdy-free { color: var(--text-3); font-size: .8rem; }

/* The alert BAR. Eleven warnings as eleven full-width cards measured 933px
   -- the whole first screen -- and pushed the plan below the fold. The bar
   is a fixed height whatever happens: a screen that grows by 900px when
   something goes wrong rearranges itself exactly when you least want it to. */
.tdy-alert-bar {
  display: flex; align-items: center; gap: var(--sp-2);
  inline-size: 100%; text-align: start;
  padding: 7px var(--sp-3); border-radius: var(--radius-md);
  background: var(--surface); border: 1px solid var(--line);
  color: inherit; font: inherit; cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.tdy-alert-bar:hover { border-color: var(--line-2); background: var(--surface-2); }
.tdy-alert-cap { font-weight: 600; font-size: .88rem; }
.tdy-alert-more { margin-inline-start: auto; font-size: .8rem; color: var(--accent-ink); }
/* Expanded: a grid, capped, scrolling inside itself -- so "see all" can
   never take the screen back. */
.tdy-alert-list {
  display: grid; gap: 6px; margin-block-start: 6px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  max-block-size: min(38vh, 340px); overflow-y: auto;
}
.tdy-alert {
  padding: 6px var(--sp-2); border-radius: var(--radius-sm);
  border-inline-start: 3px solid var(--idle); background: var(--idle-bg);
}
.tdy-alert.bad  { border-inline-start-color: var(--err);  background: var(--err-bg); }
.tdy-alert.warn { border-inline-start-color: var(--warn); background: var(--warn-bg); }
.tdy-alert.live { border-inline-start-color: var(--live); background: var(--live-bg); }
.tdy-alert.clickable { cursor: pointer; }
.tdy-alert.clickable:hover { background: var(--surface-3); }
.tdy-alert-t { display: block; font-size: .88rem; }
.tdy-alert-b { font-size: .8rem; color: var(--text-2); margin-top: 2px; }
.tdy-alert-w { font-size: .72rem; color: var(--text-3); margin-top: 2px; }

/* v1.69.0 — the production board is a PIPELINE, so it is one row.
   It had the same fault "today" did, and worse: seven status columns in an
   auto-fit grid, five fitted and two wrapped 673px below the first row --
   and the two that wrapped were «آماده تحویل» and «نیاز به اصلاح», the two
   an operator most often drags INTO. A drop target you have to go looking
   for is a drop target that does not get used.

   Stages never wrap now. When they do not fit they scroll sideways, which
   is what every kanban does, and the page itself still never scrolls
   sideways -- it is the board that scrolls, inside its own box. */
.brd-cols {
  display: grid; gap: var(--sp-2);
  grid-auto-flow: column;
  grid-auto-columns: minmax(210px, 1fr);
  align-items: stretch;
  overflow-x: auto;
  padding-block-end: var(--sp-1);
  overscroll-behavior-x: contain;
}
.brd-cols > .brd-col { display: flex; flex-direction: column; min-block-size: 0; }
/* Each column scrolls inside itself, so one busy stage can never set the
   height of the whole board. */
.brd-col-b {
  overflow-y: auto; max-block-size: min(56vh, 520px);
  padding-inline-end: 2px;
}
@media (max-width: 620px) {
  .brd-col-b { max-block-size: none; }
}

/* v1.68.0 — "today", relaid out.
   The seven buckets used to share one auto-fit grid: five fitted and two
   wrapped, and because a grid row is as tall as its tallest cell, the
   wrapped pair sat 1036px below the first row as soon as one column filled
   up. The fix is not a smaller minmax -- it is admitting these were never
   one row of the same thing. */
.tdy-cols { display: flex; flex-direction: column; gap: var(--sp-3); }

/* The stages: new orders, then the four a part moves through (v1.71.0 put
   "new" in front). One row on a desktop, stretched so they are all the
   height of the tallest -- a column that grows downward past its neighbours
   is what made the old layout unreadable. */
.tdy-stages {
  display: grid; gap: var(--sp-2);
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: stretch;
}
/* Each body scrolls inside ITSELF. One busy column can no longer push the
   rest of the screen down; the operator scrolls the column, not the page. */
.tdy-stages .tdy-col { display: flex; flex-direction: column; min-block-size: 0; }
.tdy-stages .tdy-col-b {
  overflow-y: auto; max-block-size: min(46vh, 420px);
  padding-inline-end: 2px;
}

/* The three conditions. They are decisions, not stages, and they are usually
   empty -- so they get one compact row under a heading of their own rather
   than three more full-height columns pretending to be part of the pipeline. */
.tdy-dec-h {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: .82rem; font-weight: 600; color: var(--text-2);
  margin-block-start: var(--sp-1);
}
.tdy-dec-h::after {
  content: ""; flex: 1; block-size: 1px; background: var(--line);
}
.tdy-decisions {
  display: grid; gap: var(--sp-2);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: start;
}
.tdy-decision .tdy-col-b { max-block-size: min(30vh, 260px); overflow-y: auto; }
/* An empty condition bucket is a good-news box: it steps back rather than
   taking a full card's worth of attention. */
.tdy-decision:has(.tdy-empty) { background: transparent; opacity: .72; }

@media (max-width: 1360px) {
  .tdy-stages { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 980px) {
  .tdy-stages { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .tdy-decisions { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  .tdy-stages { grid-template-columns: 1fr; }
  .tdy-stages .tdy-col-b { max-block-size: none; }
}
.tdy-col, .brd-col {
  border-radius: var(--radius-md); padding: var(--sp-2);
  background: var(--surface); border: 1px solid var(--line);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.tdy-col.go   { border-color: var(--accent-line); }
.tdy-col.live { border-color: var(--live-line); }
.tdy-col.warn { border-color: var(--warn-line); }
.tdy-col.bad, .brd-col.bad { border-color: var(--err-line); }
.tdy-col-h, .brd-col-h {
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px; margin-bottom: 2px; font-weight: 600; font-size: .88rem;
}
.tdy-count, .brd-count {
  font-size: .76rem; padding: 0 7px; border-radius: var(--pill);
  background: var(--surface-3); color: var(--text-2);
}

.tdy-col-b, .brd-col-b { display: flex; flex-direction: column; gap: 6px; }

.tdy-card, .brd-card {
  border-radius: var(--radius-sm); padding: 6px var(--sp-2);
  background: var(--surface-2); border: 1px solid var(--line);
}
.tdy-card.clickable { cursor: pointer; }
.tdy-card.clickable:hover, .brd-card.clickable:hover { border-color: var(--accent-line); background: var(--surface-3); }
.tdy-card-head, .brd-card-head { display: flex; align-items: baseline; gap: 6px; justify-content: space-between; }
.tdy-card-name, .brd-name { font-size: .88rem; overflow-wrap: anywhere; }
.tdy-qty, .tdy-status, .brd-qty { font-size: .76rem; color: var(--text-3); white-space: nowrap; }
.tdy-card-meta, .tdy-card-foot, .brd-card-meta, .brd-card-foot {
  display: flex; flex-wrap: wrap; gap: 3px var(--sp-2);
  font-size: .76rem; color: var(--text-2); margin-top: 3px;
}
.tdy-card-meta .warn, .tdy-card-foot .warn, .tdy-when.warn, .tdy-mach.warn,
.brd-card-meta .warn, .brd-card-foot .warn { color: var(--warn); }
.tdy-code, .brd-code { color: var(--text-2); }
.tdy-pcode, .brd-pcode { font-family: var(--font-mono); font-size: .7rem; color: var(--text-3); }
.tdy-note, .brd-note {
  font-size: .75rem; margin-top: 3px; padding-top: 3px;
  border-top: 1px solid var(--line); color: var(--text-2);
}
.tdy-empty, .tdy-load, .brd-empty { font-size: .8rem; color: var(--text-3); padding: 5px 2px; }

/* =================================================== production board === */
/* The board must stay readable while something is dragged over it, so drop
   targets change border and background only — never size. A target that
   grows moves everything else and the card lands somewhere else. */
.brd-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-2); }
.brd-title { font-size: 1rem; font-weight: 600; }
.brd-modes { display: flex; gap: 3px; }
.brd-tools-right { display: flex; gap: 6px; margin-inline-start: auto; }
.brd-lanes { display: grid; gap: var(--sp-2); margin-bottom: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.brd-lane {
  border-radius: var(--radius-md); padding: var(--sp-2) var(--sp-3);
  background: var(--surface); border: 1px solid var(--line);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.brd-lane.off { opacity: .5; }
.brd-lane-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); font-weight: 600; }
.brd-lane-meta { display: flex; flex-wrap: wrap; gap: 3px var(--sp-2); font-size: .76rem; color: var(--text-2); margin-top: 3px; }
.brd-lane-meta .live { color: var(--live); }
.brd-dots { display: inline-flex; gap: 3px; }
.brd-col-b { min-block-size: 44px; }
.brd-card-order { border-inline-start: 3px solid var(--live); }

/* ==================================================== schedule lanes === */

/* ======================================================== wall view ==== */

/* ==================================================== the part card ==== */
/* The part card's overlay reuses the customer panel's spec-popup shell, so
   only what is genuinely new needs styling here. */
#part-card .spec-steps .step { cursor: pointer; }
#pc-main { display: flex; flex-direction: column; gap: var(--sp-2); }
#pc-main .mode-grid, #pc-main .mach-grid, #pc-main .mat-grid, #pc-main .color-grid { margin-block: 3px; }
#pc-viewer-canvas { inline-size: 100%; block-size: 100%; }
.pfc-empty, .pfc-refresh { color: var(--text-2); font-size: .88rem; }
.ai-details { margin-top: var(--sp-2); font-size: .86rem; color: var(--text-2); }

/* ====================================================== responsive ===== */
@media (max-width: 1100px) {
  .odw-panel { width: min(480px, 100%); }
}
@media (max-width: 900px) {
  /* the tab row drops onto its own line, so the bar is taller */
  :root { --float-top: 112px; }
  .main-tabs { order: 3; width: 100%; }
  .modal-card { max-height: 92vh; }

}
@media (max-width: 980px) {
  .spec-cols { grid-template-columns: 1fr; grid-template-areas: "viewer" "form"; }
  .spec-side { position: static; }
}
@media (max-width: 760px) {
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
  .viewer-canvas { height: 280px; }
  .card { padding: var(--sp-3); }
}
@media (max-width: 620px) {
  /* Wide content scrolls inside its own box; the page never scrolls sideways. */
  table.list, .data-table { display: block; overflow-x: auto; max-width: 100%; }
  /* A signed-in header carries a name, four links and four icons. On a phone
     that is wider than the screen, so the bar wraps onto a second line rather
     than dragging the whole page sideways with it. */
  .topbar { flex-wrap: wrap; gap: var(--sp-2); row-gap: var(--sp-1); }
  .topbar-actions { gap: 2px; }
  .odw-panel { width: 100%; }
  .odw-facts, .odw-item-grid, .odw-money { grid-template-columns: 1fr; }
  .tdy-tools, .brd-tools-right { margin-inline-start: 0; }
  .brd-lanes { grid-template-columns: 1fr; }

}
@media (max-width: 420px) {
  body { font-size: .95rem; }
  .topbar .brand [data-i18n="brand_sub"] { display: none; }
  .main-tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .main-tabs::-webkit-scrollbar { display: none; }
  .main-tabs .tab { white-space: nowrap; padding: 8px 10px; }
  .modal-card { width: 96vw; padding: var(--sp-5) var(--sp-3) var(--sp-3); }
}
@media (min-width: 1440px) {
  .container { max-width: 1320px; margin-inline: auto; }
}

/* v1.53.0 — the customer's order card (order-card.js).
   Logical properties only from here to the end of the file: this is the one
   screen that is read in Persian and in English, on a phone, by somebody who
   is not looking for a control panel. */
.pfc { padding-block: var(--sp-4); }
.pfc-head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.pfc-code { margin: 0; letter-spacing: .04em; }
.pfc-spacer { flex: 1 1 auto; }

.pfc-progress { margin-block-start: var(--sp-3); }
.pfc-bar { position: relative; block-size: 6px; border-radius: var(--pill);
  background: var(--surface-3); overflow: hidden; }
.pfc-bar-fill { display: block; block-size: 100%; border-radius: var(--pill);
  background: var(--accent); transition: inline-size .5s var(--ease); }

.pfc-steps { display: flex; gap: var(--sp-1); list-style: none; margin: var(--sp-2) 0 0;
  padding: 0; flex-wrap: wrap; }
.pfc-step { display: flex; align-items: center; gap: 6px; flex: 1 1 96px;
  min-inline-size: 96px; font-size: .82rem; color: var(--text-3); }
.pfc-step .pfc-dot { display: inline-flex; align-items: center; justify-content: center;
  inline-size: 18px; block-size: 18px; border-radius: 50%;
  border: 1.5px solid currentColor; font-size: .7rem; flex: 0 0 auto; }
.pfc-step.is-done { color: var(--ok); }
.pfc-step.is-done .pfc-dot { background: var(--ok-bg); }
.pfc-step.is-now { font-weight: 600; color: var(--accent-ink); }
.pfc-step.is-now .pfc-dot { box-shadow: 0 0 0 4px var(--accent-soft); }
.pfc-step.is-todo { color: var(--text-3); }

.pfc-headline { margin-block-start: var(--sp-3); }
.pfc-headline-text { margin: 0; }
.pfc-headline.is-action { border-inline-start: 3px solid var(--accent); padding-inline-start: var(--sp-2); }
.pfc-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-block-start: var(--sp-2); }
.pfc-delivery { margin-block-start: var(--sp-2); }

.pfc-items .pfc-item-name { word-break: break-word; }
.pfc-item-stage { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.pfc-mini { display: inline-block; inline-size: 54px; block-size: 4px;
  border-radius: var(--pill); background: var(--surface-3); overflow: hidden; }
.pfc-mini-fill { display: block; block-size: 100%; background: var(--accent); }

.pfc-total { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  margin-block-start: var(--sp-3); }
.pfc-total-num { font-size: 1.05rem; font-weight: 700; }
.pfc-tracking { letter-spacing: .04em; }
.pfc-note { margin-block-start: var(--sp-2); }
.pfc-timeline summary { cursor: pointer; }
.pfc-timeline-row { display: flex; gap: 6px; flex-wrap: wrap; margin-block-start: 3px; }
.pfc-exception .pfc-bar-fill { background: var(--warn); }

/* v1.54.0 — the customer inbox bell (customer-inbox.js). The bell sits in an
   RTL header on the Persian side and an LTR one in English. */
.pfi { position: relative; display: inline-flex; }
.pfi-bell { position: relative; }
.pfi-badge {
  position: absolute;
  inset-block-start: -2px;
  inset-inline-end: -2px;
  min-inline-size: 16px;
  padding-inline: 4px;
  border-radius: var(--pill);
  background: var(--err-solid);
  color: #fff;
  font-size: 10px;
  line-height: 16px;
  text-align: center;
}
.pfi-panel {
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-end: 0;
  z-index: 40;
  inline-size: min(360px, 86vw);
  padding: var(--sp-3);
  border-radius: var(--radius-lg);
  text-align: start;
  box-shadow: var(--shadow);
}
.pfi-panel-head { display: flex; align-items: center; gap: var(--sp-2); }
.pfi-spacer { flex: 1; }
.pfi-list { margin-block-start: var(--sp-2); max-block-size: 60vh; overflow-y: auto; }
.pfi-item {
  display: flex;
  gap: var(--sp-2);
  padding-block: var(--sp-2);
  border-block-start: 1px solid var(--line);
}
.pfi-item:first-child { border-block-start: 0; }
.pfi-dot {
  inline-size: 8px;
  block-size: 8px;
  margin-block-start: 6px;
  border-radius: 50%;
  background: transparent;
  flex: 0 0 auto;
}
.pfi-item.is-unread .pfi-dot { background: var(--accent); }
.pfi-item.is-unread .pfi-title { font-weight: 600; }
.pfi-body { flex: 1; }
.pfi-meta { display: flex; gap: var(--sp-2); margin-block-start: 3px; color: var(--text-3); font-size: .78rem; }
.pfi-link { text-decoration: underline; }
.pfi-empty { margin-block-start: var(--sp-2); color: var(--text-3); }
@media (max-width: 620px) {
  .pfi-panel { inline-size: min(320px, 92vw); }
}

/* v1.55.0 — board search. The same bar serves an RTL and an LTR panel. */
.brd-search { display: flex; align-items: center; gap: 6px; flex: 1 1 240px; }
.brd-search-input {
  flex: 1 1 auto; min-inline-size: 140px; padding: 6px var(--sp-2);
  border-radius: var(--radius-sm); border: 1px solid var(--line-2);
  background: var(--field); color: inherit; font: inherit; font-size: .88rem;
}
.brd-matches { white-space: nowrap; color: var(--text-2); font-size: .82rem; }
.brd-hint { flex-basis: 100%; margin-block-start: 6px; padding-inline-start: 2px; color: var(--text-3); font-size: .8rem; }

/* v1.56.0 — the A5 job sheet and the codes printed on it (27-sheet-qr.js).
   Printed sizes are in millimetres, because this block only ever exists on
   paper: 22mm is comfortably above the 15mm a phone camera needs at arm's
   length. Rows never split across pages, or half a code goes to the machine. */
#job-sheet { color: #000; background: #fff; padding: 8mm; font-size: 11pt; }
.sheet-head { display: flex; justify-content: space-between; align-items: baseline;
  border-bottom: 2px solid #000; padding-bottom: 4mm; }
.sheet-head h2 { margin: 0; font-size: 20pt; letter-spacing: .05em; }
.sheet-meta p { margin: 1mm 0; }
.sheet-urgent { font-weight: 700; border: 2px solid #000; display: inline-block; padding: 1mm 3mm; }
.sheet-table { width: 100%; border-collapse: collapse; margin-top: 4mm; }
.sheet-table th, .sheet-table td { border: 1px solid #000; padding: 2mm; text-align: start; }
.sheet-note { margin-top: 4mm; border-top: 1px dashed #000; padding-top: 2mm; }
.sheet-codes { margin-top: 6mm; font-family: monospace; font-size: 16pt;
  letter-spacing: .08em; word-spacing: .4em; }
/* The code block (27-sheet-qr.js). Printed sizes in millimetres, because this
   block only ever exists on paper: 22mm is comfortably above the 15mm a phone
   camera needs at arm's length. Rows never split across pages, or half a code
   goes to the machine. */
.sheet-qr-row { display: flex; align-items: center; gap: 4mm; margin-block: 3mm;
  break-inside: avoid; page-break-inside: avoid; }
.sheet-qr-row svg { inline-size: 22mm; block-size: 22mm; flex: 0 0 auto; }
.sheet-qr-box { min-inline-size: 0; }
.sheet-qr-code { font-family: monospace; font-size: 16pt; font-weight: 700; letter-spacing: .04em; }
.sheet-qr-hint { font-size: 9pt; opacity: .75; }

@page { size: A5; margin: 8mm; }
@media print { body * { visibility: hidden; } }

/* v1.59.0 — the customer's card says more (order-card.js).
   Everything here is one line of text or one small square: the card is read on
   a phone, in a hurry, usually standing up. */
.pfc-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding-block: 2px;
  padding-inline: var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-size: 0.78rem;
  white-space: nowrap;
}
.pfc-chip-any { border-style: dashed; color: var(--text-2); }
.pfc-swatch {
  inline-size: 10px;
  block-size: 10px;
  border-radius: 3px;
  border: 1px solid var(--line-2);
  flex: none;
}
.pfc-item-spec { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--sp-2); }
.pfc-mat { white-space: nowrap; }
.pfc-parts { margin-block: 6px 0; }
.pfc-next { margin-block: 2px 0; }
.pfc-paid { margin-block-start: 6px; }
.pfc-remaining { color: var(--warn); }
/* The handover block is the one thing on this card that is held up to another
   person, so it is the one thing allowed to be large. */
.pfc-handover {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-block-start: var(--sp-2);
  padding-block: var(--sp-2);
  padding-inline: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}
.pfc-handover svg {
  inline-size: 30mm;
  block-size: 30mm;
  background: #fff;
  padding: 4px;
  border-radius: 6px;
}
.pfc-handover-code {
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.04em;
}
@media (max-width: 620px) {
  .pfc-step { flex: 1 1 44%; min-inline-size: 0; }
  .pfc-step-label { font-size: .78rem; }
}
@media (max-width: 560px) {
  .pfc-handover svg { inline-size: 24mm; block-size: 24mm; }
  .pfc-handover-code { font-size: 1rem; }
}

/* v2 — the order desk (58-order-desk.js, SERVICE_WORKFLOW.md §2).
   One row per part: thumbnail, what it is and what was decided about it,
   quantity, price, actions. The chips under the name are the whole point of
   the table — they are how an operator sees, without opening anything, which
   part differs from the others. */
.desk-list { display: flex; flex-direction: column; gap: var(--sp-2); margin-block-start: var(--sp-3); }
.desk-row {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr) auto auto auto;
  gap: var(--sp-3); align-items: center;
  padding-block: var(--sp-2); padding-inline: var(--sp-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface-2);
}
.desk-row.desk-thead {
  border: 0; background: none; padding-block: 0;
  color: var(--text-3); font-size: 0.8rem;
}
.desk-thumb { inline-size: 96px; block-size: 72px; display: flex;
  align-items: center; justify-content: center; font-size: 1.5rem; }
.desk-thumb .thumb-canvas { inline-size: 96px; block-size: 72px; border-radius: var(--radius-sm); }
.desk-main { display: flex; flex-direction: column; gap: 5px; min-inline-size: 0; }
.desk-head { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline; }
.desk-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.desk-chips .chip { font-size: 0.78rem; padding-block: 2px; padding-inline: var(--sp-2); }
.desk-chips .chip.todo { border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn); }
.desk-chips .swatch.tiny {
  inline-size: 10px; block-size: 10px; border-radius: 50%;
  display: inline-block; margin-inline-end: 6px;
}
.desk-qty { display: flex; align-items: center; gap: var(--sp-1); }
.desk-qty input { inline-size: 60px; text-align: center; }
.desk-price { font-weight: 600; min-inline-size: 7ch; text-align: end; }
.desk-price.agreed { color: var(--accent-ink); }
.desk-actions { display: flex; flex-wrap: wrap; gap: 5px; justify-content: flex-end; }
.desk-toolbar { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-block-start: var(--sp-2); }
.desk-summary { display: flex; flex-wrap: wrap; gap: var(--sp-5); align-items: baseline; }
.desk-sum-money, .desk-sum-when { display: flex; flex-direction: column; gap: 2px; }
.desk-sum-key { font-size: 0.85rem; color: var(--text-2); }
.desk-sum-val { font-size: 1.4rem; font-weight: 700; }
.desk-sum-when .desk-sum-val { font-size: 1.1rem; }

@media (max-width: 760px) {
  .desk-row { grid-template-columns: 72px minmax(0, 1fr); }
  .desk-thumb, .desk-thumb .thumb-canvas { inline-size: 72px; block-size: 54px; }
  .desk-qty, .desk-price, .desk-actions { grid-column: 1 / -1; justify-content: flex-start; }
  .desk-price { text-align: start; }
  .desk-row.desk-thead { display: none; }
}

/* v1.62.0 — the operator's own project, on the order card. The dot beside a
   printer is information, never a gate: a file may be sent to a machine that
   is mid-job, because Moonraker simply queues it. */
.oda-send { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center;
  margin-block-start: 6px; }
.oda-send select { min-inline-size: 14ch; max-inline-size: 22ch; }
.opu-dot { inline-size: 10px; block-size: 10px; border-radius: 50%; flex: none;
  background: var(--idle); }
.opu-dot.free { background: var(--ok); }
.opu-dot.busy { background: var(--err); }
.oda-swap { display: flex; flex-direction: column; gap: 6px;
  margin-block-start: var(--sp-2); padding-block: var(--sp-2);
  padding-inline: var(--sp-3); border-radius: var(--radius-md);
  border: 1px solid var(--line); background: var(--surface-2); }
.oda-diff { display: flex; flex-direction: column; gap: 6px;
  margin-block-start: var(--sp-2); padding-block: var(--sp-2);
  padding-inline: var(--sp-3); border-radius: var(--radius-md);
  border: 1px solid var(--accent-line); background: var(--accent-soft); }
.oda-diff .data-table th, .oda-diff .data-table td { padding-block: 3px; }

/* v1.63.0 — the official sales invoice (24a-invoice.js).
   Black on white regardless of theme: this block only ever exists on paper,
   and an auditor's photocopier does not know about design tokens. Everything
   is in logical properties because the document is read right-to-left, and in
   millimetres because A4 is a physical object. */
#invoice-sheet { color: #000; background: #fff; padding: 10mm; font-size: 10.5pt; }
.inv-head { display: flex; align-items: flex-start; gap: 6mm;
  border-block-end: 2px solid #000; padding-block-end: 4mm; }
.inv-logo { block-size: 18mm; inline-size: auto; max-inline-size: 45mm; object-fit: contain; }
.inv-titles { flex: 1; min-inline-size: 0; }
.inv-title { margin: 0; font-size: 18pt; letter-spacing: .04em; }
.inv-sub { font-size: 11pt; }
.inv-serial { text-align: end; font-size: 10pt; line-height: 1.7; }
/* The serial is what an auditor matches against the ledger, so it is the one
   number on the page that is hard to miss. */
.inv-serial > :first-child { font-size: 13pt; font-weight: 700; }
.inv-order { opacity: .75; }
.inv-party { margin-block-start: 4mm; border: 1px solid #000; }
.inv-party-h { padding: 1.5mm 3mm; font-weight: 700; background: #eee;
  border-block-end: 1px solid #000; }
.inv-party-g { display: grid; grid-template-columns: repeat(3, 1fr); }
.inv-cell { display: flex; gap: 2mm; padding: 1.5mm 3mm; min-inline-size: 0;
  border-inline-end: 1px solid #bbb; border-block-start: 1px solid #eee; }
.inv-cell:nth-child(3n) { border-inline-end: 0; }
/* The address is the field that never fits in a third of the page. */
.inv-cell:last-child { grid-column: 1 / -1; border-inline-end: 0; }
.inv-k { opacity: .7; white-space: nowrap; }
.inv-v { font-weight: 600; overflow-wrap: anywhere; }
.inv-table { inline-size: 100%; border-collapse: collapse; margin-block-start: 4mm; }
.inv-table th, .inv-table td { border: 1px solid #000; padding: 2mm; text-align: start; }
.inv-table th { background: #eee; white-space: nowrap; }
.inv-table tr { break-inside: avoid; page-break-inside: avoid; }
.inv-num { text-align: center; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The summary is narrow and pinned to the inline end, the way every Iranian
   invoice reaches the payable figure. */
.inv-sum { margin-block-start: 3mm; margin-inline-start: auto; border-collapse: collapse;
  min-inline-size: 70mm; }
.inv-sum td { border: 1px solid #000; padding: 1.5mm 3mm; }
.inv-sum td:first-child { white-space: nowrap; }
.inv-total td { font-weight: 700; font-size: 12pt; background: #eee; }
.inv-words { margin-block-start: 3mm; padding: 2mm 3mm; border: 1px solid #000;
  font-weight: 600; }
.inv-feet { display: flex; gap: 6mm; margin-block-start: 6mm; }
.inv-sign { flex: 1; border: 1px solid #000; padding: 2mm 3mm 22mm;
  font-size: 9pt; break-inside: avoid; page-break-inside: avoid; }
@media (max-width: 700px) { .inv-party-g { grid-template-columns: 1fr; }
  .inv-cell { border-inline-end: 0; } }

/* Both sheets live in the page at once, so print the one that is on screen.
   Keying on [hidden] rather than a class means neither routine has to know
   the other exists. */
@media print {
  #job-sheet:not([hidden]), #job-sheet:not([hidden]) *,
  #invoice-sheet:not([hidden]), #invoice-sheet:not([hidden]) * { visibility: visible; }
  #job-sheet:not([hidden]), #invoice-sheet:not([hidden]) {
    position: absolute; inset: 0; display: block !important; }
}

/* v1.64.0 — audit. The urgency badge was drawn without a rule; and the §4
   edit row that came back to the order card (22-order-actions.js). */
.badge.urgent { background: var(--err-bg); color: var(--err); border-color: var(--err-line); }
.oda-edit { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
.oda-edit select { min-inline-size: 9rem; }
.oda-edit input[type="text"] { flex: 1 1 12rem; min-inline-size: 0; }

/* ============================================ v1.65.0 — the customer ====
   MOBILE IS THE REAL SIZE HERE.

   The operator sits at a 1920px screen all day. The customer is standing
   in a workshop holding a phone, one-handed, deciding whether to spend two
   million toman. Everything below is written for that person first and
   widened for desktop afterwards -- which is the opposite of how the rest
   of this file was built, and the reason the funnel used to feel like a
   control panel that had been squeezed.

   Three things follow from it:
     - The primary action is always reachable by a thumb. On a phone that
       means pinned to the bottom, above the home indicator, not at the end
       of a scroll nobody finishes.
     - Every step ARRIVES. A wizard that swaps content instantly makes the
       customer re-scan the whole screen to find what changed; a 380ms
       entrance tells them where to look for free.
     - Nothing is smaller than a fingertip. 44px, enforced by pointer type.
   ======================================================================== */

/* -------------------------------------------------------- the shell ---- */
.customer-body { background: var(--bg-0); }
/* Room for the pinned bar, plus the iOS home indicator. Without the inset
   the last card hides under the bar on exactly the devices people use. */
.customer-body .container { padding-block-end: calc(96px + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 861px) { .customer-body .container { padding-block-end: var(--sp-6); } }

/* -------------------------------------------------------- the hero ----- */
/* The one place in the app allowed a gradient: it is the first thing a
   customer sees and it has to say "this is a shop", not "this is a form". */
#welcome-hero, #hero-upload {
  background: var(--hero);
  border-color: transparent;
  position: relative;
  overflow: hidden;
}
#welcome-hero::after, #hero-upload::after {
  content: ""; position: absolute; inset-block-start: -50%; inset-inline-end: -20%;
  inline-size: 260px; block-size: 260px; border-radius: 50%;
  background: radial-gradient(circle, var(--accent-soft), transparent 70%);
  pointer-events: none;
}
#welcome-hero > *, #hero-upload > * { position: relative; }
#welcome-hero h2 { font-size: clamp(1.5rem, 6vw, 2.1rem); line-height: 1.3; letter-spacing: -.02em; }
.welcome-services li { animation: pf-in-s var(--t-slow) var(--ease) both; }
.welcome-services li:nth-child(1) { animation-delay: 60ms; }
.welcome-services li:nth-child(2) { animation-delay: 120ms; }
.welcome-services li:nth-child(3) { animation-delay: 180ms; }
.welcome-services li:nth-child(4) { animation-delay: 240ms; }
.welcome-services li:nth-child(5) { animation-delay: 300ms; }

/* ------------------------------------------------------ the dropzone --- */
/* The single most important control on the customer's first screen, so it
   is the one that reacts most: it breathes on hover and lifts on a drag. */
.dropzone {
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease),
              transform var(--t) var(--spring), box-shadow var(--t) var(--ease);
}
.dropzone:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.dropzone.drag {
  border-color: var(--accent); background: var(--accent-soft);
  transform: scale(1.015); box-shadow: var(--shadow);
}
.dropzone .icon { transition: transform var(--t) var(--spring); }
.dropzone:hover .icon { transform: translateY(-3px) scale(1.06); }
.dropzone.drag .icon { animation: pf-bob 1.1s var(--ease) infinite; }

/* ------------------------------------------------------- the stepper --- */
/* On a phone the labels do not fit, so the stepper becomes a progress bar
   with one label -- the step you are on. A row of five truncated words is
   not a stepper, it is noise. */
.steps { scrollbar-width: none; }
.steps::-webkit-scrollbar { display: none; }
.step .dot { transition: background var(--t) var(--ease), border-color var(--t) var(--ease),
             transform var(--t) var(--spring); }
.step.active .dot { transform: scale(1.18); }
/* A step that just completed gets one confirming beat, and only one. */
.step.done .dot { animation: pf-pop var(--t) var(--spring); }

@media (max-width: 860px) {
  .steps {
    position: sticky; inset-block-start: 0; z-index: 30;
    display: grid; grid-template-columns: 1fr; gap: var(--sp-1);
    background: var(--bg-0); padding-block: var(--sp-2);
    border-block-end: 1px solid var(--line);
  }
  /* the dots become one continuous track */
  .steps::before {
    content: ""; grid-row: 1; block-size: 5px; border-radius: var(--pill);
    background: var(--surface-3); overflow: hidden;
  }
  .step { grid-row: 2; display: none; font-size: .82rem; padding-inline: 0; background: none; }
  .step.active { display: flex; color: var(--text-1); }
  .step.active .dot { transform: none; }
}

/* --------------------------------------------- steps arrive, not swap -- */
/* The step's CONTENT animates, never the <section> itself. An element with a
   transform becomes the containing block for anything `position: fixed`
   inside it -- which silently un-pinned the bottom dock and left it floating
   in the middle of the card. Measured, not guessed: the dock came back at
   x:25 y:401 instead of spanning the bottom edge.

   Animating the children is also the better motion: the card does not slide
   as one slab, its rows arrive in sequence. */
.wizard-step > *:not(.row-flex) { animation: pf-rise var(--t-slow) var(--ease) both; }
.wizard-step > *:nth-child(2) { animation-delay: 40ms; }
.wizard-step > *:nth-child(3) { animation-delay: 80ms; }
.wizard-step > *:nth-child(4) { animation-delay: 120ms; }
.wizard-step > *:nth-child(n+5) { animation-delay: 150ms; }
.wizard-step[hidden] > *, .wizard-step.hidden > * { animation: none; }

/* ------------------------------------------------- the pinned action --- */
/* The step's OWN action row, pinned -- not a copy of it. `position: fixed`
   takes the row out of flow where it stands, so the buttons keep their
   identity, their ids and their listeners. A duplicated bar is a bar that
   eventually disagrees with the one it was copied from.

   Only the visible step's row pins, which is why this selector carries
   :not(.hidden): every other step still holds its row in the DOM. */
@media (max-width: 860px) {
  .customer-body .wizard-step:not(.hidden):not([hidden]) > .row-flex.mt-3 {
    position: fixed; inset-inline: 0; inset-block-end: 0; z-index: 60;
    display: flex; gap: var(--sp-2); align-items: center;
    padding: var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
    background: var(--surface); border-block-start: 1px solid var(--line);
    box-shadow: var(--shadow-up);
    animation: pf-rise var(--t) var(--ease) both;
  }
  .customer-body .wizard-step:not(.hidden) > .row-flex.mt-3 > .btn { flex: 1; min-height: 48px; font-size: .95rem; }
  .customer-body .wizard-step:not(.hidden) > .row-flex.mt-3 > .btn.ghost { flex: 0 0 auto; }
  /* the gate hint has to ride with the button it explains */
  .customer-body .wizard-step:not(.hidden) > .row-flex.mt-3 > .tiny {
    flex-basis: 100%; order: -1; margin-block-end: 2px; }

}

/* ------------------------------------------------------ price arrives -- */
/* The number the whole funnel exists to produce. It counts up rather than
   appearing, because a figure that lands is a figure that gets read. */
.pfc-total b, #price-box .big {
  font-variant-numeric: tabular-nums;
  animation: pf-pop var(--t) var(--spring) both;
}

/* -------------------------------------------------------- the viewer --- */
@media (max-width: 860px) {
  .viewer-canvas, #viewer-canvas { block-size: min(52vh, 340px); }
  .spec-cols { gap: var(--sp-3); }
}

/* ------------------------------------------------ parts list on a phone --- */
/* v1.71.0: the parts table was wider than a phone, so the one button a part
   needs -- "set print specs" -- sat 274px off-screen in a sideways-scrolling
   table. On a phone each row is a card: the picture and the name on top, the
   numbers labelled underneath, and the actions inside the card, full width. */
@media (max-width: 640px) {
  .customer-body .multi-table,
  .customer-body .multi-table tbody { display: block; inline-size: 100%; }
  .customer-body .multi-table tr:has(> th) { display: none; }
  .customer-body .multi-table tr {
    display: grid; grid-template-columns: 72px minmax(0, 1fr);
    gap: var(--sp-1) var(--sp-3); align-items: center;
    padding: var(--sp-3); margin-block-end: var(--sp-2);
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--radius-md);
  }
  .customer-body .multi-table td { display: block; padding: 0; border: 0; min-inline-size: 0; }
  .customer-body .multi-table td:nth-child(n+3) { grid-column: 1 / -1; }
  /* `.num` cells are LTR for their digits; the label/value line follows the
     page, so the label sits at the start in both languages. */
  .customer-body .multi-table td[data-label] {
    display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2);
    direction: inherit;
  }
  .customer-body .multi-table td[data-label]::before {
    content: attr(data-label); color: var(--text-3); font-size: .82rem;
  }
  .customer-body .multi-table td:last-child {
    display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-block-start: var(--sp-1);
  }
  .customer-body .multi-table td:last-child .btn { flex: 1 1 auto; min-block-size: 44px; }
  .customer-body .multi-table .thumb-canvas { inline-size: 72px; block-size: 54px; }
}

/* The order card's own parts table, same treatment: six columns on a 390px
   phone broke "mounting_bracket_L.stl" into "_L.s" / "tl". */
@media (max-width: 640px) {
  .pfc-items, .pfc-items tbody { display: block; inline-size: 100%; }
  .pfc-items thead { display: none; }
  .pfc-items tr {
    display: grid; gap: 2px var(--sp-2); padding: var(--sp-2) 0;
    border-block-end: 1px solid var(--line);
  }
  .pfc-items td { display: block; padding: 0; border: 0; }
  .pfc-items td.pfc-item-name { font-weight: 600; overflow-wrap: anywhere; word-break: normal; }
  .pfc-items td[data-label] {
    display: flex; justify-content: space-between; align-items: center;
    gap: var(--sp-2); direction: inherit;
  }
  .pfc-items td[data-label]::before { content: attr(data-label); color: var(--text-3); font-size: .82rem; }
}

/* --------------------------------------------------------- the cards --- */
/* Full-bleed on a phone: a 16px gutter on each side of a 375px screen is
   9% of the width spent on nothing. */
@media (max-width: 560px) {
  .customer-body .container { padding-inline: var(--sp-3); }
  .customer-body .card { padding: var(--sp-4) var(--sp-3); }
  .customer-body .glass { border-radius: var(--radius-md); }
  .multi-table .thumb-canvas { inline-size: 64px; block-size: 48px; }
}

/* ------------------------------------------------------- how to pay --- */
/* v1.71.0: the order is placed first and paid afterwards, so the card says
   how: the shop's card, the reference to write on the transfer, and the
   deadline after which the order is canceled by itself. */
.pfc-payto { display: grid; gap: var(--sp-1); margin-block-start: var(--sp-2);
  padding: var(--sp-3); border-radius: var(--radius-md);
  border: 1px solid var(--warn-line); background: var(--warn-bg); }
.pfc-payto .pay-line { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: space-between; }
.pfc-payto .pfc-due { color: var(--warn); font-weight: 600; }
/* v1.93.0: «پرداخت کردم» */
.pfc-payto .pfc-claim { font-weight: 600; }
.pfc-payto .pfc-claim.warn { color: var(--warn); }
.pfc-claim-form { display: grid; gap: var(--sp-2); }
/* v1.96.0 (n33): the "priced, never ordered" list above the customers */
.admin-unconverted { margin-block-end: var(--sp-4); }
.charge-rows { display: grid; gap: 2px; margin-block: var(--sp-2); }
.charge-rows .charge-row { display: flex; justify-content: space-between; gap: var(--sp-3); }
.charge-rows .charge-row.total { font-weight: 700; border-block-start: 1px solid var(--line); padding-block-start: var(--sp-1); }
.charge-note { font-size: .82rem; color: var(--text-2); margin: var(--sp-1) 0; }

/* ---------------------------------------------------- skeleton state --- */

/* ------------------------------------------------------ touch feedback - */
/* A tap with no reaction reads as a dead control. Hover does not exist on
   a phone, so the press state has to carry it. */
@media (pointer: coarse) {
  .btn:active, .icon-btn:active, .dropzone:active { transform: scale(.97); }
  .dropzone:hover { transform: none; box-shadow: none; }
}

/* ----------------------------------------------------------- icons ---- */
/* Stroke icons inherit currentColor and align to the text they sit beside,
   which is the whole reason they replaced the emoji: an emoji is a font
   glyph that renders a different shape per OS and ignores colour. */
.ic { inline-size: 18px; block-size: 18px; flex: none; display: block; }
.icon-btn .ic { inline-size: 19px; block-size: 19px; }
.logo-ic { inline-size: 22px; block-size: 22px; color: var(--accent); }
.brand .logo { display: inline-flex; align-items: center; }
.hero-mark {
  inline-size: 68px; block-size: 68px; margin-inline: auto; margin-block-end: var(--sp-3);
  display: grid; place-items: center; border-radius: var(--radius-lg);
  background: var(--accent-soft); color: var(--accent-ink);
  animation: pf-pop var(--t-slow) var(--spring) both;
}
.hero-mark .ic { inline-size: 34px; block-size: 34px; }
.dropzone .icon { display: grid; place-items: center; color: var(--accent-ink); }
.dropzone .icon .ic { inline-size: 32px; block-size: 32px; }

/* ------------------------------------------------ the header on a phone */
/* 56px of vertical space at the top of a 844px screen is 7% of everything
   the customer can see. It stays ONE row: the tracking link drops to its
   icon, the gaps tighten, and the brand keeps its name because that is the
   one thing the bar exists to say. */
@media (max-width: 560px) {
  .customer-body .topbar {
    flex-wrap: nowrap; gap: var(--sp-2); padding-inline: var(--sp-3);
    min-height: 52px; margin-block-end: var(--sp-2);
  }
  .customer-body .topbar .spacer { flex: 1 1 auto; min-inline-size: 0; }
  .customer-body .topbar > a.btn { padding-inline: 10px; }
  /* The auth pair is the one group allowed to keep its words: "sign in" and
     "register" are the whole point of a signed-out header. Everything else
     gives way to them. */
  .customer-body #auth-box { gap: var(--sp-1); flex-wrap: nowrap; flex: none; }
  .customer-body #auth-box .btn { padding-inline: 12px; font-size: .82rem; white-space: nowrap; }
  /* SIGNED IN is a different header. "Signed in as <name>" plus three links
     measured 453px on its own -- it cannot share a 390px row, and the rule
     above (written for the two signed-out buttons, and only ever tested
     signed out) forced it to try. The result widened the whole layout
     viewport to 714px: every signed-in customer got a page that scrolled
     sideways with the upload card off-screen.
     It takes its own row now, and the label gives way -- "my profile"
     carries the identity one tap away. Only the signed-in box holds an <a>,
     which is what tells the two states apart. */
  .customer-body .topbar:has(#auth-box a) { flex-wrap: wrap; row-gap: var(--sp-1); }
  .customer-body #auth-box:has(a) { flex: 1 1 100%; flex-wrap: wrap; justify-content: flex-start; }
  .customer-body #auth-box:has(a) > span.muted { display: none; }
  /* the mark stays, the wordmark goes: at 390px it was being clipped
     mid-word, which looks like a bug rather than a brand */
  .customer-body .topbar .brand { flex: none; gap: 0; }
  .customer-body .topbar .brand > div { display: none; }
}
@media (max-width: 400px) {
  /* below this the tracking link cannot keep its words without pushing the
     sign-in button off the row */
  .customer-body .topbar > a.btn[href="/track"] { font-size: 0; padding-inline: 12px; }
  .customer-body .topbar > a.btn[href="/track"]::before {
    content: ""; inline-size: 17px; block-size: 17px; display: block;
    background: currentColor; -webkit-mask: var(--ic-track) center/contain no-repeat;
    mask: var(--ic-track) center/contain no-repeat;
  }
}
:root {
  /* the one icon that has to exist as a mask rather than as markup, because
     it replaces text the template renders from a translation key */
  --ic-track: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.6-3.6"/></svg>');
}

.tdy-col-b { min-block-size: 44px; }

/* -------------------------------------- v1.69.0 — long admin lists ---- */
/* The audit log was an 8069px page and the knowledge base 5152px. Neither is
   wrong to be long -- a log is long -- but on both screens the filters and
   the "add" button sit at the TOP, so reading to the end meant scrolling the
   controls off. The list scrolls inside itself instead, and its header row
   stays pinned so a column never becomes anonymous halfway down. */
.admin-scroll {
  max-block-size: min(62vh, 620px);
  overflow: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
}
.admin-scroll .data-table { margin: 0; }
.admin-scroll .data-table th {
  position: sticky; inset-block-start: 0; z-index: 1;
  background: var(--surface-2);
  /* a sticky cell keeps its own background but loses the table border */
  box-shadow: inset 0 -1px 0 var(--line);
}
/* A short list should not look like a cropped one: no chrome until it
   actually scrolls. */
.admin-scroll:not(:has(.data-table tr:nth-child(12))) {
  border: 0; max-block-size: none; overflow: visible;
}

/* A textarea in a five-column grid makes its whole ROW as tall as itself, so
   the five short fields beside the AI prompt sat stranded at the top of a
   389px row with 300px of nothing under them. A long field takes the whole
   row -- which is also how you want to edit a prompt. */
.admin-form .field:has(textarea) { grid-column: 1 / -1; }

/* =================================================================== */
/* v1.72.0 — the agent chats (agent-chat.js, both panels)              */
/* v1.75.0 — rebuilt as an app: a history column, a thread read the    */
/* way the big assistants taught people to read one, a composer card   */
/* that waits mid-page until the first message, and motion that only   */
/* ever answers something the person did or something arriving.        */
/* Tokens only, logical properties only: every family, both modes,     */
/* RTL and LTR come from the variables above.                          */
/* =================================================================== */
html { --pfa-dir: 1; }
html[dir="rtl"] { --pfa-dir: -1; }

.pfa {
  container: pfa / inline-size;
  position: relative; min-block-size: 0; overflow: hidden;
  --pfa-col: 768px; --pfa-ava: 30px; --pfa-gap: 12px; --pfa-side: 272px;
  background: var(--bg-0); color: var(--text-1);
}
.pfa-app { position: absolute; inset: 0; display: grid; grid-template-columns: minmax(0, 1fr); }
.pfa-main {
  position: relative; display: flex; flex-direction: column;
  min-inline-size: 0; min-block-size: 0; overflow: hidden;
}

/* ---- header ---- */
.pfa-head {
  flex: none; display: flex; align-items: center; gap: 4px; box-sizing: border-box;
  min-block-size: 54px; padding: 8px 10px;
  border-block-end: 1px solid var(--line); background: var(--bg-0);
}
.pfa-title { flex: 1; display: flex; flex-direction: column; min-inline-size: 0; padding-inline: 4px; line-height: 1.4; }
.pfa-title strong { font-size: .95rem; }
.pfa-title strong, .pfa-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pfa-sub { font-size: .76rem; color: var(--text-3); }
.pfa-sub:empty { display: none; }
.pfa-sub.pfa-sub-live { color: var(--warn); font-weight: 600; }
.pfa-tools, .pfa-extra { display: flex; align-items: center; gap: 4px; }
.pfa-head .icon-btn { inline-size: 36px; block-size: 36px; }
.pfa-head .icon-btn .ic { inline-size: 20px; block-size: 20px; }

/* ---- the history: an overlay on a narrow host, a column on a wide one ---- */
.pfa-side {
  position: absolute; z-index: 5; inset-block: 0; inset-inline-start: 0;
  inline-size: min(300px, 86%); display: flex; flex-direction: column;
  background: var(--bg-1); border-inline-end: 1px solid var(--line); box-shadow: var(--shadow);
  transform: translateX(calc(-104% * var(--pfa-dir))); visibility: hidden;
  transition: transform .3s var(--spring), visibility 0s linear .3s;
}
.pfa.pfa-side-open .pfa-side { transform: none; visibility: visible; transition: transform .3s var(--spring); }
.pfa-scrim {
  position: absolute; inset: 0; z-index: 4; background: var(--scrim);
  opacity: 0; visibility: hidden; transition: opacity .25s var(--ease), visibility 0s linear .25s;
}
.pfa.pfa-side-open .pfa-scrim { opacity: 1; visibility: visible; transition: opacity .25s var(--ease); }
.pfa-side-head {
  flex: none; display: flex; align-items: center; gap: 6px; box-sizing: border-box;
  min-block-size: 54px; padding: 8px 10px; border-block-end: 1px solid var(--line);
}
.pfa-new {
  flex: 1; display: inline-flex; align-items: center; gap: 10px; min-block-size: 38px; padding: 0 12px;
  border-radius: var(--btn-radius); border: 1px solid var(--line-2); background: var(--surface);
  color: var(--text-1); font: inherit; font-size: .88rem; font-weight: 600; cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.pfa-new:hover { background: var(--surface-2); border-color: var(--accent-line); }
.pfa-new .ic { inline-size: 18px; block-size: 18px; color: var(--accent-ink); }
.pfa-side-body {
  flex: 1; min-block-size: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 1px; padding: 4px 8px 18px; box-sizing: border-box;
}
.pfa-side-extra:empty { display: none; }
.pfa-side-h { padding: 14px 8px 4px; font-size: .74rem; font-weight: 700; color: var(--text-2); }
.pfa-side-g { padding: 10px 8px 3px; font-size: .72rem; color: var(--text-3); }
.pfa-side-empty { margin: 4px 8px; font-size: .8rem; line-height: 1.85; color: var(--text-3); }
.pfa-conv-item, .pfa-order-item {
  position: relative; inline-size: 100%; box-sizing: border-box; padding: 8px 10px;
  border: 0; border-radius: var(--radius-md); background: transparent; color: var(--text-1);
  font: inherit; font-size: .86rem; text-align: start; cursor: pointer;
  transition: background var(--t-fast) var(--ease);
}
.pfa-conv-item { display: flex; flex-direction: column; gap: 3px; }
.pfa-conv-item:hover, .pfa-order-item:hover { background: var(--surface-2); }
.pfa-conv-item.on { background: var(--accent-soft); }
.pfa-conv-item.on::before {
  content: ""; position: absolute; inset-block: 9px; inset-inline-start: 0;
  inline-size: 3px; border-radius: 3px; background: var(--accent);
}
.pfa-conv-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pfa-conv-m { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: .72rem; color: var(--text-3); }
.pfa-conv-code { padding: 0 6px; border-radius: var(--radius-sm); border: 1px solid var(--line); color: var(--text-2); }
.pfa-conv-flag { color: var(--warn); font-weight: 600; }
.pfa-conv-busy { inline-size: 7px; block-size: 7px; border-radius: 50%; background: var(--accent); animation: pfa-blink 1s var(--ease) infinite; }
.pfa-order-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 2px 8px; }
.pfa-order-item .pfa-order-code { font-weight: 700; }
.pfa-order-item small {
  grid-column: 1 / -1; font-size: .74rem; color: var(--text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pfa-pill {
  padding: 1px 8px; border-radius: var(--pill); border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text-2); font-size: .7rem; font-weight: 700; white-space: nowrap;
}
.pfa-pill.warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--text-1); }
.pfa-pill.ok { background: var(--ok-bg); border-color: var(--ok-line); color: var(--text-1); }
.pfa-side-more {
  align-self: flex-start; margin: 2px 10px 0; padding: 0; border: 0; background: none;
  color: var(--accent-ink); font: inherit; font-size: .78rem; cursor: pointer; text-decoration: none;
}
.pfa-side-more:hover { text-decoration: underline; }

@container pfa (min-width: 760px) {
  .pfa-app { grid-template-columns: var(--pfa-side) minmax(0, 1fr); transition: grid-template-columns .32s var(--spring); }
  .pfa.pfa-side-closed .pfa-app { grid-template-columns: 0px minmax(0, 1fr); }
  .pfa-side {
    position: relative; inset: auto; z-index: auto; inline-size: auto; min-inline-size: 0;
    overflow: hidden; transform: none; visibility: visible; box-shadow: none; transition: none;
  }
  .pfa.pfa-side-closed .pfa-side { visibility: hidden; border-inline-end-color: transparent; transition: visibility 0s linear .32s; }
  .pfa-side-head, .pfa-side-body { inline-size: var(--pfa-side); }
  .pfa-scrim, .pfa-side-x { display: none; }
  .pfa-head { padding-inline: 14px; }
}

/* ---- the thread ---- */
.pfa-scroll {
  flex: 1 1 auto; min-block-size: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column;
}
.pfa-thread, .pfa-hero, .pfa .pfa-compose, .pfa-foot, .pfa-suggest {
  inline-size: min(100% - 28px, var(--pfa-col)); margin-inline: auto; box-sizing: border-box;
}
.pfa-thread { display: flex; flex-direction: column; gap: 20px; padding-block: 22px 30px; }
.pfa-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px var(--pfa-gap); min-inline-size: 0; }
.pfa-row.pfa-user { flex-direction: column; flex-wrap: nowrap; align-items: flex-end; gap: 6px; }
.pfa-row.pfa-note { justify-content: center; }
.pfa-bubble {
  max-inline-size: min(84%, 600px); box-sizing: border-box; padding: 10px 15px;
  border-radius: var(--radius-lg); background: var(--surface-2); border: 1px solid var(--line);
  line-height: 1.85; overflow-wrap: anywhere;
}
.pfa-user .pfa-bubble {
  background: var(--accent-soft); border-color: var(--accent-line);
  border-end-end-radius: var(--radius-sm);
}
.pfa-ghost { opacity: .78; animation: pfa-rise .22s var(--spring) both; }
.pfa-attached {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
  border-radius: var(--radius-md); background: var(--surface-2); border: 1px solid var(--line);
  font-size: .78rem; color: var(--text-2);
}
.pfa-attached .ic { inline-size: 15px; block-size: 15px; }
.pfa-ava {
  position: relative; flex: none; display: grid; place-items: center; box-sizing: border-box;
  inline-size: var(--pfa-ava); block-size: var(--pfa-ava); margin-block-start: 2px;
  border-radius: var(--radius-md); background: var(--accent-soft); border: 1px solid var(--accent-line);
  color: var(--accent-ink);
}
.pfa-ava .ic { inline-size: 17px; block-size: 17px; }
.pfa-ava-op { background: var(--warn-bg); border-color: var(--warn-line); color: var(--text-1); font-size: .8rem; font-weight: 800; }
.pfa-ava-busy::after {
  content: ""; position: absolute; inset: -4px; border-radius: inherit;
  border: 2px solid transparent; border-block-start-color: var(--accent);
  animation: pfa-spin .9s linear infinite;
}
.pfa-msg { flex: 1; min-inline-size: 0; font-size: .95rem; line-height: 1.95; overflow-wrap: anywhere; }
.pfa-author { font-size: .74rem; font-weight: 700; color: var(--text-2); margin-block-end: 2px; }
.pfa-text p, .pfa-plain { margin: 0; white-space: pre-wrap; }
.pfa-msg .pfa-text p + p { margin-block-start: .4em; }
.pfa-text .pfa-gap { block-size: 6px; }
.pfa-text .pfa-list { margin: 4px 0; padding-inline-start: 1.3em; }
.pfa-text .pfa-list li + li { margin-block-start: 2px; }
.pfa-ref { unicode-bidi: isolate; white-space: nowrap; }
button.pfa-ref {
  font: inherit; padding: 0; border: 0; background: none; color: var(--accent-ink);
  cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px;
}
.pfa-msg-tools {
  display: flex; gap: 2px; margin-block-start: 2px; margin-inline-start: -6px;
  opacity: 0; transition: opacity var(--t-fast) var(--ease);
}
.pfa-row:hover .pfa-msg-tools, .pfa-msg-tools:focus-within { opacity: 1; }
@media (hover: none) { .pfa-msg-tools { opacity: .85; } }
.pfa-msg-tools .icon-btn { inline-size: 30px; block-size: 30px; color: var(--text-3); }
.pfa-msg-tools .icon-btn .ic { inline-size: 16px; block-size: 16px; }
.pfa-copy.ok { color: var(--ok); }
.pfa-row > .pfa-deck { flex: 1 0 100%; display: flex; flex-direction: column; gap: 10px; min-inline-size: 0; }
.pfa-row:not(.pfa-user) > .pfa-deck { padding-inline-start: calc(var(--pfa-ava) + var(--pfa-gap)); box-sizing: border-box; }
/* The note's words only -- a note ROW also carries cards, which keep their own look. */
.pfa-note:not(.pfa-row) {
  max-inline-size: 92%; padding: 3px 12px; border-radius: var(--pill); background: var(--surface-2);
  text-align: center; font-size: .78rem; color: var(--text-3); white-space: pre-wrap;
}
.pfa-in { animation: pfa-rise .34s var(--spring) both; }
.pfa-w { animation: pfa-word .45s var(--ease) both; }

/* What the agent is doing now: the mark turns, the words shimmer. */
.pfa-typing { align-items: center; }
.pfa-shimmer {
  font-size: .88rem; color: var(--text-2);
  background: linear-gradient(90deg, var(--text-3) 20%, var(--text-1) 50%, var(--text-3) 80%);
  background-size: 220% 100%; -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; animation: pfa-shine 1.8s linear infinite;
}

/* ---- the empty page: the mark, a greeting, and what to ask ---- */
.pfa-hero { display: flex; flex-direction: column; align-items: center; gap: 10px; padding-block: 28px 16px; text-align: center; }
.pfa:not(.pfa-empty) .pfa-hero, .pfa:not(.pfa-empty) .pfa-suggest, .pfa.pfa-empty .pfa-thread { display: none; }
.pfa.pfa-empty .pfa-scroll { justify-content: center; }
.pfa-hero-mark {
  display: grid; place-items: center; inline-size: 56px; block-size: 56px; border-radius: var(--radius-lg);
  background: var(--accent-soft); border: 1px solid var(--accent-line); color: var(--accent-ink);
  animation: pfa-pop .55s var(--spring) both;
}
.pfa-hero-mark .ic { inline-size: 28px; block-size: 28px; }
.pfa-hero-title {
  margin: 4px 0 0; font-size: clamp(1.2rem, 1rem + 1.4cqi, 1.75rem); font-weight: 800;
  line-height: 1.5; animation: pfa-rise .5s var(--spring) .05s both;
}
.pfa-hero-lead { max-inline-size: 60ch; color: var(--text-2); font-size: .92rem; line-height: 1.95; animation: pfa-rise .5s var(--spring) .1s both; }
.pfa-suggest {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: 8px; margin-block-start: 12px;
}
.pfa-suggest-item {
  display: flex; align-items: center; gap: 10px; padding: 11px 14px; box-sizing: border-box;
  border-radius: var(--radius-lg); border: 1px solid var(--line); background: var(--surface);
  color: var(--text-1); font: inherit; font-size: .86rem; text-align: start; cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), transform .25s var(--spring);
  animation: pfa-rise .5s var(--spring) both; animation-delay: calc(.14s + var(--i, 0) * .05s);
}
.pfa-suggest-item:hover { border-color: var(--accent-line); background: var(--surface-2); transform: translateY(-1px); }
.pfa-suggest-item .ic { flex: none; inline-size: 18px; block-size: 18px; color: var(--accent-ink); }
/* A narrow host keeps the composer under the thumb and the ideas above it,
   two to a row and short; a wide one centres the greeting and the composer
   on the empty page. */
.pfa-suggest { margin-block: 0 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
.pfa-suggest .pfa-suggest-item { padding: 9px 10px; gap: 8px; font-size: .82rem; line-height: 1.5; }
@container pfa (max-width: 320px) { .pfa-suggest { grid-template-columns: minmax(0, 1fr); } }
@container pfa (max-width: 559px) {
  .pfa-hero { gap: 6px; padding-block: 18px 10px; }
  .pfa-hero-mark { inline-size: 44px; block-size: 44px; }
  .pfa-hero-mark .ic { inline-size: 22px; block-size: 22px; }
  .pfa-hero-lead { font-size: .86rem; line-height: 1.85; }
}
@container pfa (min-width: 560px) {
  .pfa-suggest { order: 1; margin-block: 14px 0; gap: 8px; }
  .pfa-suggest .pfa-suggest-item { padding: 11px 14px; font-size: .86rem; }
  .pfa-foot { order: 2; }
  .pfa.pfa-empty .pfa-scroll { flex: 0 1 auto; margin-block-start: auto; }
  .pfa.pfa-empty .pfa-dock { margin-block-end: auto; padding-block-end: max(6vh, 24px); }
}

/* ---- the composer ---- */
.pfa-dock {
  position: relative; z-index: 2; flex: none; display: flex; flex-direction: column;
  padding-block: 6px calc(10px + env(safe-area-inset-bottom, 0px));
}
.pfa-dock::before {
  content: ""; position: absolute; inset-inline: 0; inset-block-end: 100%; block-size: 26px;
  background: linear-gradient(to top, var(--bg-0), transparent); pointer-events: none;
}
.pfa.pfa-empty .pfa-dock::before { display: none; }
.pfa .pfa-compose {
  view-transition-name: pfa-compose;
  display: flex; flex-direction: column; gap: 4px; padding: 10px 10px 8px;
  border-radius: var(--radius-lg); border: 1px solid var(--line-2);
  background: var(--surface); box-shadow: var(--shadow);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.pfa .pfa-compose:focus-within { border-color: var(--accent-line); box-shadow: var(--shadow), 0 0 0 3px var(--accent-soft); }
.pfa .pfa-input {
  inline-size: 100%; box-sizing: border-box; min-block-size: 30px; max-block-size: 200px; resize: none;
  padding: 4px 6px; border: 0; outline: 0; background: transparent; color: var(--text-1);
  font: inherit; font-size: .97rem; line-height: 1.75;
}
.pfa .pfa-input::placeholder { color: var(--text-3); }
.pfa-bar { display: flex; align-items: center; gap: 2px; }
.pfa-bar-gap { flex: 1; }
.pfa-bar .icon-btn { inline-size: 36px; block-size: 36px; color: var(--text-2); }
.pfa-bar .icon-btn .ic { inline-size: 20px; block-size: 20px; }
.pfa .pfa-send {
  display: grid; place-items: center; inline-size: 38px; block-size: 38px; padding: 0;
  border: 0; border-radius: var(--btn-radius); background: var(--accent); color: var(--on-accent); cursor: pointer;
  transition: transform var(--t-fast) var(--spring), background var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.pfa .pfa-send .ic { inline-size: 20px; block-size: 20px; stroke-width: 2.2; }
.pfa .pfa-compose:not(.pfa-has-text):not(.pfa-has-files) .pfa-send { background: var(--surface-3); color: var(--text-3); }
.pfa .pfa-send:disabled { opacity: .5; cursor: default; }
.pfa .pfa-send:not(:disabled):active { transform: scale(.93); }
.pfa-mic.rec { color: var(--err); background: var(--err-bg); animation: pfa-ring 1.4s var(--ease) infinite; }
.pfa-rec-time { padding-inline: 4px; font-size: .82rem; font-weight: 700; color: var(--err); }
.pfa-hearing .pfa-input { opacity: .55; }
.pfa-attach-row { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 2px 4px; }
.pfa-file-chip {
  position: relative; overflow: hidden; display: inline-flex; align-items: center; gap: 7px;
  max-inline-size: 260px; padding-block: 5px; padding-inline: 10px 4px; box-sizing: border-box;
  border-radius: var(--radius-md); border: 1px solid var(--line); background: var(--surface-2);
  font-size: .8rem; animation: pfa-rise .25s var(--spring) both;
}
.pfa-file-chip > .ic { flex: none; inline-size: 16px; block-size: 16px; color: var(--text-2); }
.pfa-file-chip.ok > .ic { color: var(--ok); }
.pfa-file-chip.bad { border-color: var(--err-line); color: var(--err); }
.pfa-file-chip.bad > .ic { color: var(--err); }
.pfa-file-name { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pfa-file-stat { flex: none; font-size: .74rem; color: var(--text-3); }
.pfa-file-bar {
  position: absolute; inset-inline-start: 0; inset-block-end: 0; block-size: 2px;
  inline-size: var(--p, 0%); background: var(--accent); transition: inline-size .2s linear;
}
.pfa-file-x {
  flex: none; display: grid; place-items: center; inline-size: 24px; block-size: 24px; padding: 0;
  border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-3); cursor: pointer;
}
.pfa-file-x:hover { background: var(--surface-3); color: var(--text-1); }
.pfa-file-x .ic { inline-size: 14px; block-size: 14px; }
.pfa-foot { margin-block: 6px 0; text-align: center; font-size: .72rem; line-height: 1.7; color: var(--text-3); }
.pfa-jump {
  position: absolute; z-index: 3; inset-inline: 0; inset-block-start: -46px; margin-inline: auto;
  inline-size: 36px; block-size: 36px; border-radius: var(--pill);
  background: var(--surface); border: 1px solid var(--line-2); box-shadow: var(--shadow); color: var(--text-1);
  animation: pfa-rise .2s var(--spring) both;
}
.pfa-jump[hidden] { display: none; }
.pfa-jump.pfa-new-dot::after {
  content: ""; position: absolute; inset-block-start: -2px; inset-inline-end: -2px;
  inline-size: 10px; block-size: 10px; border-radius: 50%; background: var(--accent);
  border: 2px solid var(--bg-0);
}

/* ---- a file carried over the chat ---- */
.pfa-drop {
  position: absolute; inset: 0; z-index: 8; display: grid; place-items: center; padding: 16px;
  background: color-mix(in srgb, var(--bg-0) 80%, transparent);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .18s var(--ease), visibility 0s linear .18s;
}
.pfa.pfa-drag .pfa-drop { opacity: 1; visibility: visible; transition: opacity .18s var(--ease); }
.pfa-drop-box {
  display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 32px 40px;
  border: 2px dashed var(--accent); border-radius: var(--radius-lg); background: var(--surface);
  text-align: center; transform: scale(.96); transition: transform .25s var(--spring);
}
.pfa.pfa-drag .pfa-drop-box { transform: none; }
.pfa-drop-box .ic { inline-size: 34px; block-size: 34px; color: var(--accent-ink); animation: pfa-bob 1.1s var(--ease) infinite alternate; }
.pfa-drop-box span { font-size: .82rem; color: var(--text-2); }

/* ---- a page laid over the conversation (the customer's order) ---- */
.pfa-panel {
  position: absolute; z-index: 6; inset-block: 0; inset-inline-end: 0; inline-size: min(660px, 100%);
  display: flex; flex-direction: column; background: var(--surface);
  border-inline-start: 1px solid var(--line); box-shadow: var(--shadow);
  transform: translateX(calc(104% * var(--pfa-dir))); transition: transform .32s var(--spring);
}
.pfa-panel.on { transform: none; }
.pfa-panel-head {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-block-size: 54px; padding: 8px 14px; box-sizing: border-box; border-block-end: 1px solid var(--line);
}
.pfa-panel-body { flex: 1; min-block-size: 0; overflow-y: auto; padding: 4px 14px 16px; }
.pfa-panel-body > .pfc { margin-block-start: 10px; }
.pfa-panel-foot { flex: none; display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 14px; border-block-start: 1px solid var(--line); }
.pfa-panel-foot .btn { display: inline-flex; align-items: center; gap: 6px; }
.pfa-panel-foot .ic { inline-size: 16px; block-size: 16px; }

/* ---- cards ---- */
.pfa-card {
  inline-size: 100%; max-inline-size: 520px; box-sizing: border-box;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 12px 14px; display: flex; flex-direction: column; gap: 7px; box-shadow: var(--shadow-sm);
  animation: pfa-in var(--t, .25s) var(--ease, ease) both;
}
.pfa-k { font-size: .76rem; color: var(--text-3); }

.pfa-sum { font-weight: 700; }

.pfa-kv { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); font-size: .88rem; }
.pfa-kv .pfa-v { font-weight: 600; text-align: end; }
.pfa-kv.sum { border-block-start: 1px dashed var(--line); padding-block-start: 4px; }
.pfa-kv.total { border-block-start: 1px solid var(--line); padding-block-start: 6px; font-size: 1rem; }
.pfa-kv.total .pfa-v { font-size: 1.1rem; }
.pfa-kv.bad .pfa-v, .pfa-bad { color: var(--err); }
.pfa-warn { color: var(--warn); }

.pfa-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; margin-block-start: 4px; }
.pfa-pay-methods { display: flex; flex-direction: column; gap: 6px; margin-block-start: 4px; }
.pfa-radio { display: grid; grid-template-columns: auto 1fr; column-gap: 8px; align-items: center; cursor: pointer; }
.pfa-radio small { grid-column: 2; }
.pfa-status { color: var(--text-2); font-weight: 600; }
.pfa-decided { opacity: .78; }

.pfa-decided.pfa-done { border-color: var(--ok-line, var(--line)); }
.pfa-decided.pfa-failed { border-color: var(--err-line, var(--line)); }
.pfa-payval { display: inline-flex; align-items: center; gap: 6px; }

.pfa-orders, .pfa-order, .pfa-today, .pfa-stock { gap: 6px; }
.pfa-order-row, .pfa-line {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
  padding: 5px 0; border-block-end: 1px solid var(--line); color: inherit; text-decoration: none;
}
.pfa-order-row:last-child, .pfa-line:last-child { border-block-end: 0; }
.pfa-item { color: var(--text-2); }
.pfa-code {
  font: inherit; font-weight: 700; padding: 2px 8px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-1); cursor: pointer;
}
.pfa-code:disabled { cursor: default; }
.pfa-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.pfa-chip-stat {
  display: inline-flex; gap: 2px; align-items: baseline; padding: 3px 9px; border-radius: var(--pill);
  background: var(--surface-2); border: 1px solid var(--line); font-size: .8rem; color: var(--text-2);
}
.pfa-chip-stat.on { color: var(--text-1); border-color: var(--accent); }
.pfa-chip-stat.bad { color: var(--err); border-color: var(--err-line, var(--line)); }
.pfa-chip-stat.off { opacity: .55; }
.pfa-confirm { border-color: var(--accent); }
.pfa-handoff {
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-md);
  background: var(--warn-bg); border: 1px solid var(--warn-line); font-size: .86rem;
}
.pfa-generic { color: var(--text-2); }

/* ---- the customer: the whole page is the chat (v1.75.0) ---- */
/* The page arrives knowing its mode (body[data-mode], set by the server),
   so the order form never flashes up before the chat replaces it. */
.pfa-page { display: none; }
body[data-mode="chat"] { overflow: hidden; }
body[data-mode="chat"] .container {
  inline-size: 100%; max-inline-size: none; display: flex; flex-direction: column; block-size: 100dvh;
  padding: 0;
}
body[data-mode="chat"] .container > :not(.topbar):not(#pfa-page) { display: none; }
body[data-mode="chat"] .container > .topbar { border-radius: 0; border-inline: 0; margin: 0; }
body[data-mode="chat"] .pfa-page { display: block; flex: 1; min-block-size: 0; }

::view-transition-group(pfa-compose) { animation-duration: .42s; animation-timing-function: cubic-bezier(.2, .8, .2, 1); }

/* ---- the workshop: a launcher on every page, a panel that unfolds from it ---- */
.pfa-launch {
  position: fixed; z-index: 76; inset-block-end: calc(16px + env(safe-area-inset-bottom, 0px));
  inset-inline-start: 16px; display: inline-flex; align-items: center; justify-content: center;
  min-inline-size: 56px; block-size: 56px; padding-inline: 16px; box-sizing: border-box;
  border: 0; border-radius: var(--pill); background: var(--accent); color: var(--on-accent);
  font: inherit; font-weight: 700; cursor: pointer; box-shadow: var(--shadow);
  animation: pfa-launch-in .6s var(--spring) .35s both;
  transition: transform .25s var(--spring), box-shadow .25s var(--ease);
}
.pfa-launch:hover { transform: translateY(-2px); }
.pfa-launch:active { transform: scale(.96); }
.pfa-launch:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 3px; }
.pfa-launch-ic { display: grid; inline-size: 24px; block-size: 24px; }
.pfa-launch-ic .ic {
  grid-area: 1 / 1; inline-size: 24px; block-size: 24px;
  transition: transform .35s var(--spring), opacity .2s var(--ease);
}
.pfa-launch-ic .ic + .ic { opacity: 0; transform: rotate(-90deg) scale(.6); }
.pfa-launch.open .pfa-launch-ic .ic:first-child { opacity: 0; transform: rotate(90deg) scale(.6); }
.pfa-launch.open .pfa-launch-ic .ic + .ic { opacity: 1; transform: none; }
.pfa-launch-label {
  max-inline-size: 0; overflow: hidden; white-space: nowrap; opacity: 0;
  transition: max-inline-size .35s var(--spring), opacity .2s var(--ease), margin .35s var(--spring);
}
.pfa-launch:not(.open):hover .pfa-launch-label,
.pfa-launch:not(.open):focus-visible .pfa-launch-label { max-inline-size: 200px; opacity: 1; margin-inline-start: 10px; }
.pfa-launch .notif-count { inset-block-start: -4px; inset-inline-end: -4px; min-inline-size: 20px; block-size: 20px; font-size: 11px; }
/* Customers waiting for a person: a warm edge, and a ring that keeps going
   out from it where motion is welcome. */
.pfa-launch.waiting { box-shadow: var(--shadow), 0 0 0 2px var(--warn); }
.pfa-launch.waiting::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 0 0 var(--warn); animation: pfa-ring-warn 1.8s var(--ease) infinite;
}
/* An answer is being written with the panel shut: the mark breathes. */
.pfa-launch.busy .pfa-launch-ic { animation: pfa-breathe 1.2s var(--ease) infinite; }
/* ...and when it has arrived, a dot says so. */
.pfa-launch.unread::after {
  content: ""; position: absolute; inset-block-start: 3px; inset-inline-start: 3px;
  inline-size: 12px; block-size: 12px; border-radius: 50%;
  background: var(--ok); border: 2px solid var(--bg-0); animation: pfa-pop .4s var(--spring) both;
}
/* Above the order drawer when there is room for both side by side; under
   it on a narrower screen, where an order opened from the chat must win. */
.pfa-drawer {
  position: fixed; z-index: 75; inset-block: 12px 84px; inset-inline-start: 16px;
  inline-size: min(440px, calc(100vw - 32px)); display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg-0); border: 1px solid var(--line-2); border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  transform-origin: calc(50% - 50% * var(--pfa-dir)) 100%;
  animation: pfa-unfold .34s var(--spring) both;
  transition: inline-size .32s var(--spring);
}
.pfa-drawer.pfa-wide { inline-size: min(980px, calc(100vw - 32px)); }
.pfa-drawer.pfa-leaving { animation: pfa-fold .2s var(--ease) both; }
@media (max-width: 1100px) {
  .pfa-drawer { z-index: 65; }
  body:has(.odw:not([hidden])) .pfa-launch { display: none; }
}
@media (max-width: 600px) {
  .pfa-drawer, .pfa-drawer.pfa-wide { inset: 0; inline-size: auto; border-radius: 0; border: 0; }
  body.pfa-drawer-open .pfa-launch { display: none; }
}
.pfa-drawer .pfa-host { flex: 1; min-block-size: 0; }
.pfa-dhead {
  flex: none; display: flex; align-items: center; gap: 6px; padding: 8px 8px 0;
  border-block-end: 1px solid var(--line); background: var(--bg-0);
}
.pfa-tabs { flex: 1; display: flex; gap: 2px; min-inline-size: 0; }
.pfa-tabs .tab {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; margin-block-end: -1px;
  border: 0; border-block-end: 2px solid transparent; border-radius: 0; background: none;
  color: var(--text-2); font: inherit; font-size: .88rem; font-weight: 600; cursor: pointer;
}
.pfa-tabs .tab:hover { color: var(--text-1); }
.pfa-tabs .tab.active { color: var(--text-1); border-block-end-color: var(--accent); background: none; }
.pfa-dhead .icon-btn { inline-size: 34px; block-size: 34px; margin-block-end: 4px; }
.pfa-dhead .icon-btn .ic { inline-size: 19px; block-size: 19px; }
.pfa-inbox-wrap { flex: 1; min-block-size: 0; display: flex; flex-direction: column; }
.pfa-inbox { flex: 1; overflow-y: auto; padding: var(--sp-2) var(--sp-3); display: flex; flex-direction: column; gap: 6px; }
.pfa-conv {
  text-align: start; font: inherit; color: inherit; cursor: pointer; padding: 10px 12px;
  border-radius: var(--radius-md); border: 1px solid var(--line); background: var(--surface);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.pfa-conv:hover { border-color: var(--line-2); background: var(--surface-2); }
.pfa-conv.requested { border-color: var(--warn-line); background: var(--warn-bg); }
.pfa-conv-view { flex: 1; min-block-size: 0; display: flex; flex-direction: column; }
.pfa-conv-view > .pfa-head { min-block-size: 0; }
.pfa-list-wrap {
  flex: 1; min-block-size: 0; overflow-y: auto; padding: var(--sp-3);
  display: flex; flex-direction: column; gap: var(--sp-2);
}
.pfa-inbox-wrap .pfa-row { flex-direction: column; flex-wrap: nowrap; }
.pfa-inbox-wrap .pfa-row.pfa-user { align-items: flex-end; }
.pfa-inbox-wrap .pfa-operator .pfa-bubble { background: var(--warn-bg); border-color: var(--warn-line); }
.pfa-inbox-wrap .pfa-compose {
  display: flex; align-items: flex-end; gap: 6px; padding: var(--sp-2) var(--sp-3);
  border-block-start: 1px solid var(--line);
}
.pfa-inbox-wrap .pfa-input {
  flex: 1; min-inline-size: 0; resize: none; min-block-size: 42px; max-block-size: 140px;
  font: inherit; line-height: 1.6; padding: 8px 12px; border-radius: var(--radius-md);
  border: 1px solid var(--line-2); background: var(--surface-2); color: var(--text-1);
}
.pfa-badge {
  display: inline-block; padding: 1px 7px; border-radius: var(--pill); font-size: .72rem;
  background: var(--warn-bg); border: 1px solid var(--warn-line); color: var(--text-1);
}
.pfa-badge.on { background: var(--ok-bg); border-color: var(--ok-line, var(--line)); }

/* ---- motion ---- */
@keyframes pfa-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes pfa-rise { from { opacity: 0; transform: translateY(8px); } }
@keyframes pfa-word { from { opacity: 0; filter: blur(3px); } }
@keyframes pfa-pop { from { opacity: 0; transform: scale(.7); } }
@keyframes pfa-shine { to { background-position: -220% 0; } }
@keyframes pfa-spin { to { transform: rotate(360deg); } }
@keyframes pfa-blink { 50% { opacity: .25; } }
@keyframes pfa-bob { to { transform: translateY(-5px); } }
@keyframes pfa-breathe { 50% { transform: scale(.82); opacity: .7; } }
@keyframes pfa-ring { from { box-shadow: 0 0 0 0 var(--err-line); } to { box-shadow: 0 0 0 10px transparent; } }
@keyframes pfa-ring-warn { from { box-shadow: 0 0 0 0 var(--warn); opacity: .9; } to { box-shadow: 0 0 0 14px transparent; opacity: 0; } }
@keyframes pfa-unfold { from { opacity: 0; transform: translateY(12px) scale(.96); } }
@keyframes pfa-fold { to { opacity: 0; transform: translateY(12px) scale(.96); } }
@keyframes pfa-launch-in { from { opacity: 0; transform: translateY(18px) scale(.6) rotate(-12deg); } }
/* Reduced motion is the motion layer's job (top of this file): every
   animation here is cut to an instant there, and the reply's word-by-word
   reveal is skipped in agent-chat.js. */

/* ---- v1.76.0: the customer's consultation -------------------------- */
/* One switch in the header for the two ways to order. */
.pfm-switch {
  display: inline-flex; padding: 3px; gap: 2px; border-radius: var(--btn-radius);
  background: var(--surface-2); border: 1px solid var(--line);
}
.pfm-b {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border: 0;
  border-radius: calc(var(--btn-radius) - 2px); background: transparent; color: var(--text-2);
  font: inherit; font-size: .86rem; font-weight: 600; cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.pfm-b .ic { inline-size: 16px; block-size: 16px; }
.pfm-b:hover { color: var(--text-1); }
.pfm-b.on { background: var(--surface); color: var(--text-1); box-shadow: var(--shadow-sm); }
@media (max-width: 560px) {
  .pfm-b { padding: 5px 9px; }
  /* The chat lists the orders itself: on a phone the tracking link gives
     its place to the bell, and the header takes two rows instead of three. */
  body.customer-body[data-mode="chat"] .topbar > a.btn[href="/track"] { display: none; }
  body.customer-body[data-mode="chat"] .topbar { column-gap: 4px; }
}

/* The empty page's first move: an obvious place to put the file. */
.pfq-drop {
  display: flex; flex-direction: column; align-items: center; gap: 6px; inline-size: 100%;
  margin-block-start: 8px; padding: 22px 16px; box-sizing: border-box;
  border: 2px dashed var(--accent-line); border-radius: var(--radius-lg);
  background: var(--accent-soft); color: var(--text-1); font: inherit; cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), transform .25s var(--spring);
  animation: pfa-rise .5s var(--spring) .12s both;
}
.pfq-drop:hover { border-color: var(--accent); transform: translateY(-1px); }
.pfq-drop strong { font-size: .98rem; }
.pfq-drop span { font-size: .8rem; color: var(--text-2); }
.pfq-drop-ic {
  display: grid; place-items: center; inline-size: 42px; block-size: 42px;
  border-radius: var(--radius-md); background: var(--surface); color: var(--accent-ink);
}
.pfq-drop-ic .ic { inline-size: 22px; block-size: 22px; }

/* The cards. */
.pfa-card.pfq { max-inline-size: 660px; gap: 10px; }
.pfq-head { display: flex; align-items: center; gap: 10px; min-inline-size: 0; }
.pfq-title { display: flex; flex-direction: column; min-inline-size: 0; gap: 2px; }
.pfq-title strong { overflow-wrap: anywhere; }
.pfq-sub, .pfq-muted { font-size: .8rem; color: var(--text-2); }
.pfq-warn { font-size: .82rem; color: var(--warn); margin: 0; }
.pfq-label { font-size: .76rem; font-weight: 700; color: var(--text-2); }
.pfq-still { display: block; border-radius: var(--radius-sm); background: var(--surface-2); flex: none; }
.pfq-still-off { visibility: hidden; }
.pfq-stage {
  position: relative; display: grid; place-items: center; inline-size: 100%; padding: 6px;
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-2);
  cursor: zoom-in;
}
.pfq-stage .pfq-still { inline-size: min(100%, 300px) !important; block-size: auto !important; aspect-ratio: 300 / 190; background: transparent; }
.pfq-stage-btn {
  position: absolute; inset-block-end: 8px; inset-inline-end: 8px; display: inline-flex;
  align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--pill);
  background: var(--surface); border: 1px solid var(--line-2); font-size: .78rem; color: var(--text-1);
}
.pfq-stage-btn .ic { inline-size: 15px; block-size: 15px; }
.pfq-facts { margin: 0; padding-inline-start: 1.2em; font-size: .84rem; display: grid; gap: 2px; }
.pfq-orients { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 190px), 1fr)); gap: 8px; }
.pfq-orient {
  display: flex; flex-direction: column; gap: 6px; padding: 8px; text-align: start;
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface);
  color: var(--text-1); font: inherit; cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.pfq-orient:hover { border-color: var(--line-2); }
.pfq-orient.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.pfq-orient .pfq-still { align-self: center; background: transparent; }
.pfq-orient-t { display: flex; flex-direction: column; gap: 2px; font-size: .8rem; }
.pfq-orient-t strong { font-size: .86rem; }
.pfq-legend { display: flex; align-items: center; gap: 8px; margin: 0; font-size: .76rem; color: var(--text-2); }
.pfq-red { flex: none; inline-size: 12px; block-size: 12px; border-radius: 3px; background: var(--err); }

/* The part's settings: every row a choice, the price at the foot. */
.pfq-ctrls { display: grid; gap: 10px; transition: opacity var(--t-fast) var(--ease); }
.pfq-ctrls.pfq-busy { opacity: .55; pointer-events: none; }
.pfq-row { display: grid; grid-template-columns: 7.5em minmax(0, 1fr); align-items: start; gap: 4px 10px; }
.pfq-row > .pfq-label { padding-block-start: 7px; }
.pfq-row.pfq-need > .pfq-label { color: var(--warn); }
@media (max-width: 560px) { .pfq-row { grid-template-columns: minmax(0, 1fr); } .pfq-row > .pfq-label { padding-block-start: 0; } }
.pfq-seg { display: flex; flex-wrap: wrap; gap: 6px; }
.pfq-seg-b {
  display: inline-flex; align-items: center; gap: 6px; min-block-size: 32px; padding: 4px 11px;
  border: 1px solid var(--line); border-radius: var(--btn-radius); background: var(--surface-2);
  color: var(--text-1); font: inherit; font-size: .84rem; cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.pfq-seg-b:hover { border-color: var(--line-2); }
.pfq-seg-b.on { border-color: var(--accent); background: var(--accent-soft); font-weight: 700; }
.pfq-seg-b .ic { inline-size: 15px; block-size: 15px; }
.pfq-swatch { inline-size: 14px; block-size: 14px; border-radius: 50%; border: 1px solid var(--line-2); flex: none; }
.pfq-qty { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.pfq-step {
  inline-size: 32px; block-size: 32px; border: 1px solid var(--line); border-radius: var(--btn-radius);
  background: var(--surface-2); color: var(--text-1); font: inherit; font-size: 1.05rem; cursor: pointer;
}
.pfq-step:disabled { opacity: .45; cursor: default; }
/* Two classes: the global input[type="number"] rule (width: 100%) outranks one. */
.pfq-qty .pfq-qty-n {
  inline-size: 4.5em; block-size: 32px; min-block-size: 0; box-sizing: border-box; padding: 0 6px; text-align: center;
  border: 1px solid var(--line-2); border-radius: var(--btn-radius); background: var(--field);
  color: var(--text-1); font: inherit; font-weight: 700;
}
.pfq-check { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; cursor: pointer; font-size: .86rem; min-block-size: 32px; }
.pfq-note { grid-column: 2; font-size: .76rem; }
@media (max-width: 560px) { .pfq-note { grid-column: 1; } }
.pfq-extras { display: grid; gap: 4px; }
.pfq-spec {
  padding: 6px 10px; border-radius: var(--radius-sm); background: var(--surface-2);
  font-size: .82rem; color: var(--text-2);
}
.pfq-price { display: grid; gap: 4px; padding-block-start: 6px; border-block-start: 1px solid var(--line); }
.pfq-total { font-size: 1.45rem; font-weight: 800; }
.pfq-pricing {
  font-size: .9rem; color: var(--text-2);
  background: linear-gradient(90deg, var(--text-3) 20%, var(--text-1) 50%, var(--text-3) 80%);
  background-size: 220% 100%; -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; animation: pfa-shine 1.8s linear infinite;
}
.pfq-more summary { cursor: pointer; font-size: .82rem; color: var(--text-2); }
.pfq-more[open] { display: grid; gap: 4px; }
.pfa-card.pfq-folded { padding: 6px 10px; box-shadow: none; }
.pfq-fold {
  inline-size: 100%; padding: 2px 0; border: 0; background: transparent; color: var(--text-2);
  font: inherit; font-size: .8rem; text-align: start; cursor: pointer;
}
.pfq-fold:hover { color: var(--text-1); text-decoration: underline; }
.pfa-card.pfq-gone { opacity: .7; }

/* The options, side by side. */
.pfq-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr)); gap: 8px; }
.pfq-opt {
  display: flex; flex-direction: column; gap: 6px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.pfq-opt.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.pfq-opt-l { font-size: .8rem; font-weight: 800; color: var(--accent-ink); }
.pfq-opt .pfq-total { font-size: 1.2rem; }
.pfq-opt-spec { margin: 0; padding-inline-start: 1.1em; display: grid; gap: 2px; font-size: .8rem; }
.pfq-why { margin: 0; font-size: .8rem; color: var(--text-2); }
.pfq-opt .btn { margin-block-start: auto; justify-content: center; display: inline-flex; align-items: center; gap: 6px; }
.pfq-opt .btn .ic { inline-size: 15px; block-size: 15px; }
.pfa-card.pfq-retired { opacity: .55; }

/* Summary and the placed order. */
.pfq-line {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  padding-block: 6px; border-block-end: 1px solid var(--line);
}
.pfq-line-t { display: grid; gap: 2px; min-inline-size: 0; }
.pfq-note-in {
  inline-size: 100%; box-sizing: border-box; resize: vertical; padding: 8px 10px;
  border: 1px solid var(--line-2); border-radius: var(--radius-md); background: var(--field);
  color: var(--text-1); font: inherit; font-size: .88rem;
}
.pfq-done { display: flex; align-items: center; gap: 10px; }
.pfq-done > .ic {
  flex: none; inline-size: 34px; block-size: 34px; padding: 7px; box-sizing: border-box;
  border-radius: 50%; background: var(--ok-bg); color: var(--ok); border: 1px solid var(--ok-line);
}
.pfq-done .pfa-text p { font-weight: 800; font-size: 1.05rem; }
.pfq-payto { display: grid; gap: 4px; padding: 8px 10px; border-radius: var(--radius-md); background: var(--surface-2); }
.pfq .pfa-actions .btn { display: inline-flex; align-items: center; gap: 6px; }
.pfq .pfa-actions .btn .ic { inline-size: 15px; block-size: 15px; }

/* The 3D view in the side panel. */
.pfq-3d { display: grid; gap: 10px; padding-block: 10px; }
.pfq-viewer { position: relative; margin: 0; border-radius: var(--radius-md); overflow: hidden; }
.pfq-viewer .viewer-canvas { block-size: min(56vh, 460px); }
.pfq-go { justify-self: start; display: inline-flex; align-items: center; gap: 6px; }
.pfq-go .ic { inline-size: 16px; block-size: 16px; }
.pfq-ordered {
  justify-self: start; display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
  border: 1px solid var(--ok-line); border-radius: var(--pill); background: var(--ok-bg);
  color: var(--text-1); font: inherit; font-size: .8rem; font-weight: 700; cursor: pointer;
}
.pfq-ordered .ic { inline-size: 14px; block-size: 14px; color: var(--ok); }
.pfq-orient:disabled { cursor: default; }

/* ---- v1.77.0: the workshop assistant's own cards ------------------------ */
/* The day as a plan, the customers waiting, a reply the operator edits, a
   release that lists every step it will take. */
.pfa-plan { margin: 0; padding-inline-start: 1.3em; display: grid; gap: 8px; }
.pfa-plan li { display: grid; gap: 4px; }
.pfa-plan li > .btn, .pfa-chatrow > .btn { justify-self: start; }
.pfa-codes { display: flex; flex-wrap: wrap; gap: 4px; }
.pfa-codes .pfa-code { font-size: .78rem; padding: 1px 7px; }
.pfa-chatrow { display: grid; gap: 4px; padding: 8px 0; border-block-end: 1px solid var(--line); }
.pfa-chatrow:last-child { border-block-end: 0; }
.pfa-reply-text { inline-size: 100%; box-sizing: border-box; resize: vertical; min-block-size: 5.5em; }
.pfa-check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.pfa-steps { margin: 0; padding-inline-start: 1.2em; display: grid; gap: 3px; }
.pfa-extra { color: var(--text-1); font-weight: 600; }
.pfa-brief {
  display: grid; gap: 3px; padding: 8px 10px; margin-block-end: 8px;
  border-radius: var(--radius-md); background: var(--surface-2); border: 1px solid var(--line);
}
.pfa-conv-view > .pfa-brief { flex: none; margin: 8px 10px 0; max-block-size: 28vh; overflow: auto; }
.pfa-brief[hidden] { display: none; }

/* ---- v1.78.0: a customer chat that lives in Telegram ---- */
.pfa-badge.tg { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-ink); }
.pfa-tg { display: grid; gap: 10px; margin-block-start: 18px; }
.pfa-tg .callout { margin: 0; }
.pfa-tg-steps { margin: 0; padding-inline-start: 1.2em; display: grid; gap: 4px; }

/* v1.85.0: the staff panel's simple mode. Tools the owner switched off are
   marked adv-only and stay hidden until Admin -> advanced tools is on. */
.pf-simple .adv-only { display: none !important; }
.oda-steps { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.oda-steps .oda-step { min-height: 46px; padding: 10px 22px; font-size: 1rem; }
.oda-steps .oda-step.danger { margin-inline-start: auto; }
.odw-ask { margin-top: var(--sp-2); padding: var(--sp-2) var(--sp-3); border-radius: 10px;
  background: var(--accent-soft); border: 1px solid var(--accent-line); }
.odw-ask p { margin: 4px 0 0; white-space: pre-wrap; unicode-bidi: plaintext; }

/* v1.85.0: «قیمت سریع» */
.qp-card { display: grid; gap: var(--sp-3); }
.qp-card h2 { margin: 0; }
.qp-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--sp-3); }
.qp-result { display: grid; gap: var(--sp-2); }
.qp-price { font-size: 1.8rem; font-weight: 700; }
.qp-facts { display: flex; flex-wrap: wrap; gap: var(--sp-3); color: var(--text-2); }
.qp-text { inline-size: 100%; font: inherit; unicode-bidi: plaintext; }
