/* ═══════════════════════════════════════════════════
   JOB CARD PORTAL — Production CSS
   Alpha Style | v2.0
═══════════════════════════════════════════════════ */

/* ── Design Tokens ───────────────────────────────── */
:root {
  --bg:            #edf3fb;
  --panel:         #ffffff;
  --panel-soft:    #f5f8fd;
  --ink:           #0d1f35;
  --ink-2:         #1e3a5f;
  --muted:         #5a7190;
  --muted-2:       #8aa3be;
  --line:          #dce8f5;
  --line-strong:   #c4d8ee;
  --brand:         #0f2742;
  --brand-2:       #1557a0;
  --brand-3:       #1e72d1;
  --brand-soft:    #e6f0fb;
  --accent:        #e8a100;
  --accent-soft:   #fff7e0;
  --danger:        #c94040;
  --danger-soft:   #fdeaea;
  --success:       #1f8a55;
  --success-soft:  #e6f7ef;
  --warning:       #c57a00;
  --warning-soft:  #fff3d6;

  --radius-xs:  8px;
  --radius-sm:  12px;
  --radius-md:  18px;
  --radius-lg:  24px;
  --radius-xl:  32px;

  --shadow-sm:  0 2px 8px rgba(15, 39, 66, 0.06);
  --shadow-md:  0 8px 28px rgba(15, 39, 66, 0.10);
  --shadow-lg:  0 20px 60px rgba(15, 39, 66, 0.12);
  --shadow-top: 0 -4px 20px rgba(15, 39, 66, 0.06);

  --sidebar-w:  280px;
  --topbar-h:   84px;

  --font: "Manrope", "Segoe UI", system-ui, sans-serif;
}

/* ── Reset & Base ────────────────────────────────── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink);
  background:
    radial-gradient(ellipse 80% 50% at 110% -10%, rgba(21, 87, 160, 0.13) 0%, transparent 60%),
    radial-gradient(ellipse 60% 40% at -10% 120%, rgba(21, 87, 160, 0.07) 0%, transparent 60%),
    linear-gradient(170deg, #eaf3fc 0%, #f4f9ff 55%, #eef5fb 100%);
  min-height: 100vh;
  min-height: 100dvh; /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

/* Some browsers render the native <option> popup list with the OS default
   font rather than the author font-family inherited by <select> — set it
   explicitly so dropdown lists match the rest of the UI's typography. */
select, option {
  font-family: var(--font);
}

button {
  cursor: pointer;
}

/* ── Utility ─────────────────────────────────────── */
.hidden { display: none !important; }

.eyebrow {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--brand-3);
  margin-bottom: 6px;
}

.muted {
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.65;
}

/* ════════════════════════════════════════════════════
   AUTH / LOGIN
════════════════════════════════════════════════════ */
.auth-shell {
  min-height: 100vh;
  min-height: 100dvh; /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  display: grid;
  place-items: center;
  padding: 24px;
  position: relative;
}

.auth-shell::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 55% 45% at 15% 85%, rgba(21, 87, 160, 0.09) 0%, transparent 60%),
    radial-gradient(ellipse 40% 55% at 90% 10%, rgba(14, 38, 68, 0.08) 0%, transparent 60%);
  pointer-events: none;
}

.auth-card {
  position: relative;
  z-index: 1;
  width: min(480px, 100%);
  padding: 40px 36px 36px;
  border-radius: var(--radius-xl);
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid rgba(220, 232, 245, 0.9);
  box-shadow: var(--shadow-lg), 0 1px 0 rgba(255,255,255,0.9) inset;
  backdrop-filter: blur(20px);
}

.auth-logo {
  display: flex;
  justify-content: center;
  margin-bottom: 18px;
}

.auth-logo-img {
  display: block;
  height: 90px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}

.auth-form {
  display: grid;
  gap: 16px;
}

.auth-hint {
  margin: 0;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  background: var(--brand-soft);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-2);
  border: 1px solid #c4d8f5;
}

.auth-error {
  display: none;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  background: #fef2f2;
  border: 1.5px solid rgba(201, 64, 64, 0.35);
  color: #7f1d1d;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.45;
}

.auth-error:not(:empty) {
  display: flex;
}

.auth-error::before {
  content: "⚠";
  flex-shrink: 0;
  font-size: 14px;
  color: var(--danger);
}

.auth-submit {
  margin-top: 4px;
  width: 100%;
  padding: 14px 20px;
  font-size: 0.95rem;
}

/* ════════════════════════════════════════════════════
   PORTAL TOPBAR
════════════════════════════════════════════════════ */
.portal-shell { min-height: 100vh; min-height: 100dvh; }

.portal-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 28px;
  height: var(--topbar-h);
  overflow: hidden;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 100;
}

/* Groups the hamburger + brand together so .portal-topbar's own
   justify-content:space-between only spreads THIS group apart from
   .topbar-right — without this wrapper, space-between treated the
   hamburger, brand, and topbar-right as three separate items and stretched
   a huge gap between the hamburger and the brand icon instead of keeping
   them together on the left. */
.topbar-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

/* Centers the logo within the same width — and the same starting edge —
   as the sidebar column directly below it (desktop only; the mobile
   breakpoint further down hides .brand-block entirely, so this never
   applies there). The negative margin cancels .portal-topbar's own 28px
   left padding, which otherwise shifts this whole box 28px right of the
   sidebar's true left edge (the sidebar itself has no such padding), so
   centering "within topbar-left" and centering "within the sidebar
   column" were two different zones — this makes them the same one. */
@media (min-width: 1001px) {
  .topbar-left {
    width: var(--sidebar-w);
    margin-left: -28px;
    justify-content: center;
  }
}

/* Hamburger button that opens the mobile nav drawer — only ever shown at
   the mobile breakpoint (see @media max-width:1000px below); on desktop the
   full sidebar is always visible, so this stays hidden and out of layout. */
.mobile-nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
}

.mobile-nav-toggle span {
  display: block;
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
}

.mobile-nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(8, 24, 44, 0.45);
  z-index: 590;
}

body.mobile-nav-open .mobile-nav-backdrop {
  display: block;
}

.brand-block {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.brand-icon {
  display: block;
  height: 76px;
  width: auto;
  max-width: 220px;
  object-fit: contain;
  flex-shrink: 0;
  margin: 0 14px;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.user-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border-radius: var(--radius-md);
  background: var(--panel-soft);
  border: 1px solid var(--line);
  position: relative;
  cursor: pointer;
}

.user-chip-menu {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 190px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: 6px;
  z-index: 50;
}

.user-chip-menu.open {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.user-chip-menu-item {
  display: none;
  text-align: left;
  padding: 9px 12px;
  border: none;
  background: transparent;
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

.user-chip-menu-item.visible {
  display: block;
}

.user-chip-menu-item:hover {
  background: var(--brand-soft);
  color: var(--brand-2);
}

.user-chip-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--brand-2) 0%, var(--brand-3) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 12px;
  font-weight: 800;
  flex-shrink: 0;
}

.user-chip-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.user-chip-name {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-chip-role {
  font-size: 11px;
  font-weight: 600;
  color: var(--brand-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.job-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  border: 1px solid rgba(232, 161, 0, 0.25);
}

.job-chip-label {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--warning);
}

.job-chip-value {
  font-size: 13px;
  font-weight: 800;
  color: var(--brand);
  letter-spacing: -0.01em;
}

/* ── Topbar logout button ──────────────────────── */
.topbar-logout-btn {
  border: 1.5px solid var(--line-strong);
  padding: 9px 22px;
  border-radius: var(--radius-md);
  background: #fff;
  color: var(--ink);
  font-size: 0.88rem;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.18s ease;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
}

.topbar-logout-btn:hover {
  background: var(--danger-soft);
  border-color: rgba(201, 64, 64, 0.3);
  color: var(--danger);
  transform: translateY(-1px);
}

/* ════════════════════════════════════════════════════
   PORTAL MAIN LAYOUT
════════════════════════════════════════════════════ */
.portal-main {
  max-width: 1600px;
  margin: 0 auto;
  padding: 24px 24px 48px;
}

.view-panel {
  display: none;
}

.view-panel.active {
  display: block;
}

/* Forces the wizard panel to lay out off-screen (not display:none) so
   html2canvas can capture .preview-sheet for on-demand PDF regeneration
   without visually disrupting whichever screen is actually on-screen. */
.view-panel.pdf-capture-mode {
  display: block !important;
  position: fixed;
  top: 0;
  left: -10000px;
  z-index: -1;
}

/* Forces one specific wizard step section capturable off-screen regardless
   of which step is actually active — lets the combined PDF capture both the
   Job Card (step 8) and the Cutting Report (step 7) in one pass without
   changing which step the user is actually looking at. */
.step.pdf-force-capturable {
  display: block !important;
  position: fixed;
  top: 0;
  left: -10000px;
  z-index: -1;
}

/* ════════════════════════════════════════════════════
   BUTTONS
════════════════════════════════════════════════════ */
.primary-btn,
.secondary-btn,
.ghost-btn,
.icon-btn,
.mini-btn,
.danger-btn {
  border: none;
  font-weight: 700;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  transition: all 0.18s ease;
  white-space: nowrap;
  letter-spacing: -0.01em;
}

.primary-btn {
  padding: 12px 22px;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 60%, var(--brand-3) 100%);
  color: #fff;
  font-size: 0.9rem;
  box-shadow: 0 6px 20px rgba(15, 39, 66, 0.22);
}

.primary-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 28px rgba(15, 39, 66, 0.28);
}

.primary-btn:active {
  transform: translateY(0);
}

.secondary-btn {
  padding: 11px 20px;
  border-radius: var(--radius-md);
  background: #fff;
  color: var(--ink);
  font-size: 0.9rem;
  border: 1.5px solid var(--line-strong);
  box-shadow: var(--shadow-sm);
}

.secondary-btn:hover {
  background: var(--brand-soft);
  border-color: rgba(21, 87, 160, 0.3);
  color: var(--brand-2);
  transform: translateY(-1px);
}

.ghost-btn {
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  background: var(--brand-soft);
  color: var(--brand-2);
  font-size: 0.875rem;
  border: 1px solid rgba(21, 87, 160, 0.15);
}

.ghost-btn:hover {
  background: rgba(21, 87, 160, 0.12);
  transform: translateY(-1px);
}

.icon-btn {
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 0.82rem;
  border: 1px solid rgba(201, 64, 64, 0.15);
}

.icon-btn:hover {
  background: rgba(201, 64, 64, 0.15);
  transform: translateY(-1px);
}

.mini-btn {
  padding: 7px 14px;
  border-radius: var(--radius-xs);
  background: var(--brand-soft);
  color: var(--brand-2);
  font-size: 0.8rem;
  border: 1px solid rgba(21, 87, 160, 0.15);
}

.mini-btn:hover {
  background: rgba(21, 87, 160, 0.12);
  transform: translateY(-1px);
}

.mini-btn.danger-btn {
  background: var(--danger-soft);
  color: var(--danger);
  border-color: rgba(201, 64, 64, 0.15);
}

.mini-btn.danger-btn:hover {
  background: rgba(201, 64, 64, 0.15);
}

/* Focus-visible accessibility rings */
.primary-btn:focus-visible,
.secondary-btn:focus-visible,
.ghost-btn:focus-visible,
.mini-btn:focus-visible,
.topbar-logout-btn:focus-visible {
  outline: 3px solid rgba(30, 114, 209, 0.55);
  outline-offset: 2px;
}

/* Edit mode indicator banner */
.edit-mode-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  background: linear-gradient(95deg, #fffbeb 0%, #fff7d6 100%);
  border: 1.5px solid rgba(232, 161, 0, 0.35);
  font-size: 13px;
  font-weight: 600;
  color: #92400e;
  margin-bottom: 16px;
}

.edit-mode-banner .edit-mode-icon {
  font-size: 16px;
  flex-shrink: 0;
}

/* ════════════════════════════════════════════════════
   FORM ELEMENTS
════════════════════════════════════════════════════ */
.field {
  display: grid;
  gap: 7px;
}

.field > span,
.field > label > span {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-2);
  letter-spacing: 0.01em;
}

.field-hint {
  font-size: 11.5px;
  color: var(--muted);
  margin-top: -2px;
}

input[type="text"],
input[type="password"],
input[type="date"],
input[type="number"],
input[type="email"],
input[type="file"],
select,
textarea {
  width: 100%;
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--line);
  background: #fff;
  color: var(--ink);
  font-size: 0.9rem;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
  appearance: none;
  -webkit-appearance: none;
}

select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%235a7190' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 18px;
  padding-right: 36px;
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--brand-3);
  box-shadow: 0 0 0 3.5px rgba(30, 114, 209, 0.13);
}

input[readonly] {
  background: var(--panel-soft);
  color: var(--muted);
}

input[type="file"] {
  padding: 11px 14px;
  cursor: pointer;
}

input[type="range"] {
  width: 100%;
  padding: 0;
  border: none;
  background: none;
  accent-color: var(--brand-3);
  cursor: pointer;
}

.grid {
  display: grid;
  gap: 16px;
}

.grid.two {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* ════════════════════════════════════════════════════
   CARDS & PANELS
════════════════════════════════════════════════════ */
.screen-card,
.wizard-card {
  background: rgba(255, 255, 255, 0.97);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-md);
}

.screen-card {
  padding: 24px;
}

.screen-card:not(.active) {
  display: none;
}

.screen-card + .screen-card {
  margin-top: 20px;
}

/* ════════════════════════════════════════════════════
   PAGE HEADERS
════════════════════════════════════════════════════ */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

/* The title block used to size to its own (short) text, leaving a large,
   awkward-looking dead gap before the pill/button on the right at wide
   desktop widths. Letting it grow into the row's available space keeps the
   header feeling like one balanced unit instead of two islands with empty
   space between them. */
.page-header > div:first-child {
  flex: 1;
  min-width: 0;
}

.page-header h2 {
  font-size: 1.6rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink);
  line-height: 1.15;
}

.page-header .muted {
  margin-top: 4px;
}

.page-header.inner {
  margin-bottom: 0;
}

.section-heading {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.section-heading h3 {
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.section-heading .muted {
  margin-top: 3px;
}

.section-heading.compact {
  align-items: start;
}

.inline-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

.panel-note {
  display: inline-flex;
  align-items: center;
  padding: 8px 14px;
  border-radius: 999px;
  background: var(--brand-soft);
  color: var(--brand-2);
  font-size: 12.5px;
  font-weight: 600;
  border: 1px solid rgba(21, 87, 160, 0.12);
}

/* ════════════════════════════════════════════════════
   ADMIN LAYOUT
   The sidebar lives outside both view-panels (see index.html) so it stays
   visible whether the admin is looking at the Admin panel or the Job Card
   wizard — it only ever shows at all for an admin session (body.is-admin,
   toggled in JS right after login/session-restore), never for an employee.
════════════════════════════════════════════════════ */
.app-shell-layout {
  display: block;
}

.admin-sidebar {
  display: none;
}

/* ── Admin Sidebar (sizing/appearance — only visible once body.is-admin
   sets display:flex below; these properties are inert until then) ───── */
.admin-sidebar {
  position: sticky;
  top: calc(var(--topbar-h) + 24px);
  max-height: calc(100vh - var(--topbar-h) - 48px);
  max-height: calc(100dvh - var(--topbar-h) - 48px); /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  overflow-y: auto;
  overflow-x: hidden;
  border-radius: var(--radius-xl);
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-md);
  flex-direction: column;

  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

.admin-sidebar::-webkit-scrollbar { width: 4px; }
.admin-sidebar::-webkit-scrollbar-track { background: transparent; }
.admin-sidebar::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }

/* Hidden on desktop, where the topbar right above already shows the same
   logo + company name — shown only inside the mobile drawer (see @media
   max-width:1000px below), which has no topbar brand of its own visible
   once the drawer covers it, so it needs its own header. */
.admin-sidebar-head {
  display: none;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}

.admin-sidebar-head .sidebar-brand {
  display: flex;
  align-items: center;
  justify-content: center;
}

.sidebar-brand-icon {
  display: block;
  height: 52px;
  width: auto;
  max-width: 120px;
  object-fit: contain;
  flex-shrink: 0;
}

/* ── Sidebar Nav ─────────────────────────────────── */
.admin-nav {
  display: flex;
  flex-direction: column;
  padding: 8px 0;
  gap: 0;
  flex: 1;
}

.admin-nav-btn {
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  text-align: left;
  padding: 13px 18px 13px 20px;
  cursor: pointer;
  transition: background 0.15s ease;
  position: relative;
  border-left: 3px solid transparent;
}

.admin-nav-btn:hover {
  background: var(--panel-soft);
}

.admin-nav-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #eef4fb;
  color: var(--brand-2);
  flex-shrink: 0;
  transition: background 0.16s ease, color 0.16s ease;
}

.admin-nav-icon svg {
  width: 22px;
  height: 22px;
}

.admin-nav-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.admin-nav-copy strong {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.16s ease;
}

.admin-nav-copy span {
  display: block;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.admin-nav-btn.active {
  background: linear-gradient(95deg, rgba(30, 114, 209, 0.1) 0%, rgba(21, 87, 160, 0.05) 100%);
  box-shadow: inset 0 0 0 1px rgba(30, 114, 209, 0.15);
}

.admin-nav-btn.active::before {
  transform: translateY(-50%) scaleY(1);
}

.admin-nav-btn.active .admin-nav-icon {
  background: linear-gradient(135deg, var(--brand-2) 0%, var(--brand-3) 100%);
  color: #fff;
  box-shadow: 0 3px 8px rgba(21, 87, 160, 0.25);
}

.admin-nav-btn.active .admin-nav-copy strong {
  color: var(--brand-2);
}

.admin-nav-btn.active .admin-nav-copy span {
  color: rgba(21, 87, 160, 0.65);
}

/* ── Sidebar Footer ──────────────────────────────── */
.admin-sidebar-foot {
  margin: 8px 10px 12px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--panel-soft) 0%, #eef5fb 100%);
  border: 1px solid var(--line);
  flex-shrink: 0;
}

.sidebar-foot-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(21, 87, 160, 0.1);
  color: var(--brand-2);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 8px;
}

.admin-sidebar-foot strong {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 4px;
  letter-spacing: -0.01em;
}

.admin-sidebar-foot p {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.5;
}

/* ════════════════════════════════════════════════════
   ADMIN CONTENT AREA
════════════════════════════════════════════════════ */
.admin-content {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* ── Overview / Stats ────────────────────────────── */
.admin-overview {
  padding: 24px 26px;
  border-radius: var(--radius-xl);
  background: linear-gradient(120deg, rgba(255, 255, 255, 0.98) 0%, rgba(240, 248, 255, 0.9) 100%);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-md);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 20px;
  align-items: center;
}

.admin-overview-copy h2 {
  font-size: clamp(1.6rem, 2.5vw, 2.2rem);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1.1;
  color: var(--ink);
  margin-bottom: 6px;
}

.admin-overview-copy .muted {
  max-width: 500px;
  font-size: 0.875rem;
}

.admin-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

.admin-stat-card {
  padding: 16px 18px;
  border-radius: var(--radius-lg);
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
  text-align: center;
  min-width: 100px;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.admin-stat-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.admin-stat-card .stat-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted-2);
  margin-bottom: 6px;
}

.admin-stat-card strong {
  display: block;
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--brand);
  margin-bottom: 5px;
}

.admin-stat-card .stat-desc {
  font-size: 11px;
  color: var(--muted);
  line-height: 1.4;
}

/* ── Admin Panels ────────────────────────────────── */
.admin-panel {
  display: none;
}

.admin-panel.active {
  display: block;
}

.admin-panel-header {
  margin-bottom: 16px;
}

.admin-panel-header h2 {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink);
  margin-bottom: 4px;
}

/* ════════════════════════════════════════════════════
   TOOLBAR / SEARCH
════════════════════════════════════════════════════ */
.toolbar-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 200px auto;
  gap: 10px;
  margin-bottom: 16px;
  align-items: start;
}

.toolbar-grid.admin-toolbar {
  grid-template-columns: minmax(0, 1fr) auto 200px;
}

/* ════════════════════════════════════════════════════
   DATE RANGE FILTER (Job Card list — filter by Issue Date)
════════════════════════════════════════════════════ */
.date-range-filter {
  position: relative;
}

.date-range-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 100%;
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--line);
  background: #fff;
  color: var(--ink);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.18s ease;
}

.date-range-btn:hover {
  border-color: var(--brand-3);
}

.date-range-btn svg {
  color: var(--muted-2);
  flex-shrink: 0;
}

.date-range-filter.has-value .date-range-btn {
  border-color: var(--brand-2);
  color: var(--brand-2);
  background: var(--brand-soft);
}

.date-range-popover {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 40;
  width: max(320px, 100%);
  padding: 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: #fff;
  box-shadow: var(--shadow-lg);
}

.date-range-presets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-bottom: 12px;
}

.date-range-presets button {
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--panel-soft);
  color: var(--ink);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.date-range-presets button:hover {
  background: var(--brand-soft);
  border-color: var(--brand-3);
}

.date-range-custom {
  display: grid;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

.date-range-custom label {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
}

.date-range-custom input[type="text"] {
  margin-top: 4px;
  padding: 9px 10px;
  font-size: 0.85rem;
}

.date-range-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.date-range-actions button {
  padding: 8px 14px;
  font-size: 0.82rem;
}

/* ════════════════════════════════════════════════════
   DATA TABLE
════════════════════════════════════════════════════ */
.table-shell {
  overflow: auto;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: #fff;
  box-shadow: var(--shadow-sm);
}

table {
  width: 100%;
  border-collapse: collapse;
  min-width: 700px;
}

th {
  padding: 13px 16px;
  text-align: left;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  background: linear-gradient(180deg, var(--panel-soft) 0%, #eef5fb 100%);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

td {
  padding: 13px 16px;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: middle;
  font-size: 0.88rem;
  color: var(--ink);
}

tbody tr:hover {
  background: #f8fcff;
}

/* The cutting report is a fixed printable template — it must look identical
   whether or not the mouse happens to be hovering a row when a PDF is
   captured, so it's excluded from the generic row-hover highlight. */
.cutting-report-table tbody tr:hover {
  background: inherit;
}

tbody tr:last-child td {
  border-bottom: none;
}

.actions-col {
  min-width: 230px;
}

/* Address/Email/GST truncation is handled in JS now (renderClampableCell —
   see .clamp-text/.clamp-toggle below), which actually clips the DOM text
   rather than relying on CSS text-overflow. A CSS-only max-width/ellipsis
   approach was tried here first but doesn't work at all in an auto-layout
   table (the browser sizes columns to fit content BEFORE overflow rules
   ever apply, so nothing was ever actually clipped), and forcing
   white-space:nowrap on the cell fights the JS clamp's own inline-block
   wrapping besides. */

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

.row-actions .mini-btn {
  white-space: nowrap;
}

/* ── Tags / Pills ────────────────────────────────── */
.tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.tag.high  { background: var(--danger-soft);  color: var(--danger);  }
.tag.medium{ background: var(--warning-soft); color: var(--warning); }
.tag.low   { background: var(--success-soft); color: var(--success); }
.tag.default { background: var(--brand-soft); color: var(--brand-2); }

.permission-cell { font-size: 0.82rem; font-weight: 700; }
.permission-yes  { color: var(--success); }
.permission-no   { color: var(--danger);  }

.empty-state td {
  padding: 40px 16px;
  text-align: center;
  color: var(--muted);
  font-weight: 600;
  font-size: 0.88rem;
}

/* ════════════════════════════════════════════════════
   EMPLOYEE / JOB CARD WIZARD
════════════════════════════════════════════════════ */
.step-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.step-pill {
  border: 1.5px solid var(--line);
  background: #fff;
  color: var(--muted);
  border-radius: 999px;
  padding: 5px 14px;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.18s ease;
  white-space: nowrap;
}

.step-pill:hover {
  border-color: var(--brand-3);
  color: var(--brand-2);
  background: var(--brand-soft);
}

.step-pill.active {
  background: linear-gradient(135deg, var(--accent) 0%, #f7a800 100%);
  color: #fff;
  border-color: transparent;
  box-shadow: 0 3px 10px rgba(232, 161, 0, 0.35);
}

.wizard-progress {
  height: 4px;
  background: var(--line);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  overflow: hidden;
}

.wizard-progress-bar {
  height: 100%;
  width: 14.28%;
  background: linear-gradient(90deg, var(--accent) 0%, #ef7342 100%);
  transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

form {
  padding: 22px 24px;
}

.step {
  display: none;
}

.step.active {
  display: block;
}

.wizard-actions {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 24px 20px;
  border-top: 1px solid var(--line);
  background: var(--panel-soft);
}

/* ════════════════════════════════════════════════════
   PARTY / FACTORY SELECT
════════════════════════════════════════════════════ */
/* Flexbox, not CSS Grid — grid's fr-unit/implicit-track sizing has known
   inconsistencies on older or budget Android WebViews, which showed up here
   as a stray empty box next to the dropdown on a real device. Flexbox has
   much more mature, uniform support across low-end mobile browsers. */
.option-select-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

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

.option-select-row .ghost-btn {
  flex: 0 0 auto;
}

/* ════════════════════════════════════════════════════
   DATE PICKER (Issue Date / Due Date)
   Plain text inputs (DD/MM/YYYY) with a calendar dropdown attached —
   native <input type="date"> can't be forced into DD/MM/YYYY display in
   Chrome, so typing stays the source of truth and this popup is just a
   faster way to fill it in.
════════════════════════════════════════════════════ */
.date-input-wrap {
  position: relative;
}

.date-input-wrap input {
  padding-right: 42px;
}

.date-picker-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  background: transparent;
  color: var(--muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.date-picker-toggle:hover {
  background: var(--brand-soft);
  color: var(--brand-2);
}

.password-input-wrap {
  position: relative;
}

.password-input-wrap input {
  padding-right: 42px;
}

.password-toggle-btn {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  background: transparent;
  color: var(--muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.password-toggle-btn:hover {
  background: var(--brand-soft);
  color: var(--brand-2);
}

.date-picker-panel {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 40;
  width: 280px;
  padding: 14px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  font-size: 0.85rem;
}

.date-picker-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.date-picker-header .date-picker-month {
  font-weight: 700;
  color: var(--ink);
}

.date-picker-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--line);
  background: #fff;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--brand-2);
  transition: background 0.15s ease;
}

.date-picker-nav:hover {
  background: var(--brand-soft);
}

.date-picker-weekdays,
.date-picker-days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  text-align: center;
}

.date-picker-weekdays span {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  padding: 4px 0;
}

.date-picker-day {
  border: none;
  background: transparent;
  padding: 7px 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--ink);
  font-size: 0.85rem;
}

.date-picker-day:hover {
  background: var(--brand-soft);
}

.date-picker-day.is-outside-month {
  color: var(--muted-2);
  opacity: 0.55;
}

.date-picker-day.is-today {
  border: 1.5px solid var(--accent);
}

.date-picker-day.is-selected {
  background: var(--brand);
  color: #fff;
  font-weight: 700;
}

.date-picker-footer {
  display: flex;
  justify-content: center;
  margin-top: 10px;
}

.date-picker-today-btn {
  border: none;
  background: transparent;
  color: var(--brand-2);
  font-weight: 700;
  font-size: 12.5px;
  cursor: pointer;
  padding: 4px 8px;
}

.date-picker-today-btn:hover {
  text-decoration: underline;
}

/* ════════════════════════════════════════════════════
   BOXED PANELS (Size/Fabric rows)
════════════════════════════════════════════════════ */
.boxed-panel {
  padding: 18px;
  border-radius: var(--radius-lg);
  border: 1.5px solid var(--line);
  background: var(--panel-soft);
}

.table-head,
.table-row {
  display: grid;
  gap: 12px;
  align-items: center;
}

.table-head {
  padding: 0 4px 10px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  margin-bottom: 10px;
}

.table-row {
  margin-bottom: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  border: 1.5px solid var(--line);
  background: #fff;
  transition: border-color 0.15s ease;
}

.table-row:hover {
  border-color: var(--line-strong);
}

.table-row:last-child {
  margin-bottom: 12px;
}

.size-grid   { grid-template-columns: 1.4fr 1fr 100px; }
.fabric-grid { grid-template-columns: 0.8fr 0.7fr 1.6fr 100px; }

.row-cell {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Only shown once the row collapses to a single column (see the 520px
   breakpoint) — on desktop/tablet the grid columns line up with .table-head
   above them instead, so an inline label would just be a redundant echo. */
.row-cell-label {
  display: none;
}

/* ════════════════════════════════════════════════════
   RICH TEXT EDITOR
════════════════════════════════════════════════════ */
.editor-shell {
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1.5px solid var(--line);
  background: #fff;
  transition: border-color 0.18s ease;
}

.editor-shell:focus-within {
  border-color: var(--brand-3);
  box-shadow: 0 0 0 3.5px rgba(30, 114, 209, 0.13);
}

.editor-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: var(--panel-soft);
  border-bottom: 1px solid var(--line);
}

.editor-toolbar-group {
  display: inline-flex;
  gap: 4px;
}

.editor-toolbar-divider {
  width: 1px;
  align-self: stretch;
  min-height: 22px;
  background: var(--line);
  margin: 0 2px;
}

.editor-toolbar button {
  border: 1.5px solid var(--line);
  background: #fff;
  border-radius: var(--radius-xs);
  min-width: 30px;
  height: 30px;
  padding: 0 10px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s ease;
}

.editor-toolbar button:hover {
  background: var(--brand-soft);
  border-color: rgba(21, 87, 160, 0.25);
  color: var(--brand-2);
}

.editor-toolbar button.is-active {
  background: var(--brand-2);
  border-color: var(--brand-2);
  color: #fff;
}

.editor-select {
  height: 30px;
  padding: 0 8px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-xs);
  background: #fff;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}

.color-picker {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px;
  border-radius: var(--radius-xs);
  border: 1.5px solid var(--line);
  background: #fff;
  font-size: 13px;
  font-weight: 600;
}

.color-picker input[type="color"] {
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.editor {
  min-height: 200px;
  padding: 14px 16px;
  outline: none;
  line-height: 1.75;
  font-size: 0.9rem;
}

/* The global `* { margin:0; padding:0; }` reset also zeroes out <ul>/<ol>'s
   marker indent, which otherwise pushes the bullet/number itself flush
   against the container's edge with no gap before the text. Applies to both
   the editable box and the read-only preview/PDF, since both render the
   same sanitized HTML. */
.editor ul, .editor ol,
.preview-description ul, .preview-description ol {
  padding-left: 1.5em;
  margin: 0.5em 0;
}

.editor li, .preview-description li {
  margin: 0.2em 0;
}

/* ════════════════════════════════════════════════════
   IMAGE UPLOAD CARDS
════════════════════════════════════════════════════ */
.cards-grid { align-items: start; }

.upload-card {
  padding: 18px;
  border-radius: var(--radius-lg);
  border: 1.5px solid var(--line);
  background: var(--panel-soft);
}

.upload-header h4 {
  font-size: 1rem;
  font-weight: 800;
  color: var(--ink);
  margin-bottom: 4px;
}

.upload-header p {
  margin: 0 0 14px;
  color: var(--muted);
  font-size: 0.85rem;
}

.image-stage {
  height: 260px;
  margin-bottom: 12px;
  border-radius: var(--radius-md);
  border: 2px dashed var(--line-strong);
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  position: relative;
  transition: border-color 0.15s ease;
  cursor: pointer;
}

.image-stage:hover,
.image-stage.dragover {
  border-color: var(--brand-3);
  background: rgba(13, 31, 53, 0.02);
}

/* Product photos display as a square tile (see .photo-card), unlike the
   rectangular Main Label/Hang Tag frame — the editor stage matches that
   aspect ratio so dragging/zooming on screen reflects the actual crop. */
#productPhotoStage {
  height: auto;
  aspect-ratio: 1 / 1;
}

.image-placeholder {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted-2);
}

.image-stage img {
  display: none;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #fff;
  position: absolute;
  inset: 0;
}

.image-stage.has-image img {
  display: block;
}

.image-stage.has-image .image-placeholder {
  display: none;
}

.image-stage.has-image {
  cursor: grab;
  touch-action: none;
}

.image-stage.has-image.dragging {
  cursor: grabbing;
}

.image-drag-hint {
  display: none;
  position: absolute;
  bottom: 8px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(15, 39, 66, 0.72);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 999px;
  pointer-events: none;
  z-index: 2;
}

.image-stage.has-image .image-drag-hint {
  display: block;
}

/* Corner crop-marks reinforce that this frame is the crop boundary — only
   what's inside these marks is kept when the image is baked/saved. */
.image-stage.has-image::before,
.image-stage.has-image::after {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  pointer-events: none;
  z-index: 2;
  border-color: var(--brand-3);
  border-style: solid;
}

.image-stage.has-image::before {
  top: 6px;
  left: 6px;
  border-width: 2px 0 0 2px;
}

.image-stage.has-image::after {
  bottom: 6px;
  right: 6px;
  border-width: 0 2px 2px 0;
}

.image-stage-remove {
  display: none;
  z-index: 3;
}

.image-stage.has-image .image-stage-remove {
  display: flex;
}

.image-controls {
  display: grid;
  gap: 10px;
}

.button-row {
  display: flex;
  gap: 10px;
}

.button-row .secondary-btn {
  flex: 1;
}

/* ════════════════════════════════════════════════════
   PHOTO GRID
════════════════════════════════════════════════════ */
.photo-grid,
.preview-photo-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
}

.photo-card,
.preview-photo {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-md);
  border: 2px dashed var(--line-strong);
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  color: var(--muted-2);
  font-size: 20px;
  font-weight: 300;
  transition: border-color 0.15s ease;
  position: relative;
  cursor: pointer;
}

.photo-card:hover { border-color: var(--brand-3); }

.photo-remove-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: none;
  background: rgba(15, 39, 66, 0.72);
  color: #fff;
  font-size: 15px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 2;
}

.photo-remove-btn:hover {
  background: var(--danger);
}

.photo-card img,
.preview-photo img,
.image-stage img,
.preview-image-box img,
.preview-photo img {
  max-width: none;
}

.photo-card img,
.preview-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ════════════════════════════════════════════════════
   JOB CARD PREVIEW SHEET
════════════════════════════════════════════════════ */
/* The wizard card around it is a normal responsive container, but the A4
   page itself must not shrink to fit inside it — that's what was making the
   preview look like a squashed little widget instead of a real page. This
   wrapper lets the page render at true size and simply scrolls (both axes)
   when the viewport is narrower than 210mm, instead of compressing it. */
.a4-preview-scroll {
  width: 100%;
  overflow: auto;
  background: var(--panel-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 24px;
}

.a4-preview-center {
  width: max-content;
  margin: 0 auto;
}

.preview-sheet {
  /* Real A4 width, and a MINIMUM height (not a fixed height) — a fixed
     height combined with overflow:hidden below would silently clip any job
     whose content runs longer than one page, which is a real regression we
     hit and fixed before. Short content still reads as a full A4 page;
     long content simply grows taller instead of losing content. */
  width: 210mm;
  min-height: 297mm;
  background: #fff;
  border-radius: var(--radius-lg);
  border: 1.5px solid var(--line);
  box-shadow: var(--shadow-sm);
}

.preview-header {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 20px 24px 18px;
  border-bottom: 2px solid var(--line);
  background: #fff;
  /* .preview-sheet no longer clips via overflow:hidden (a fixed height +
     hidden overflow would silently cut off long content), so the header
     rounds its own top corners directly to keep the card's rounded look. */
  border-top-left-radius: calc(var(--radius-lg) - 2px);
  border-top-right-radius: calc(var(--radius-lg) - 2px);
}

.preview-brand {
  justify-self: start;
}

.preview-logo {
  display: block;
  height: 72px;
  width: auto;
}

.preview-brand-name {
  margin-top: 2px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
}

.preview-title {
  justify-self: center;
  font-size: 2.2rem;
  font-weight: 800;
  color: var(--ink);
  letter-spacing: -0.02em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

.preview-job {
  justify-self: end;
  text-align: right;
}

.preview-job span {
  display: block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--muted);
  margin-bottom: 4px;
}

.preview-job strong {
  display: block;
  font-family: "IBM Plex Mono", monospace;
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0.01em;
  margin-bottom: 8px;
}

.priority-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  background: var(--danger-soft);
  color: var(--danger);
}

.priority-pill::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}

.priority-pill.medium { background: var(--warning-soft); color: var(--warning); }
.priority-pill.low    { background: var(--success-soft); color: var(--success); }

/* A single-line strip (Party | Factory | Issued | Due) with thin vertical
   dividers between fields, instead of four separate boxes stacked 2-by-2 —
   the box grid used a full row of vertical space just for these 4 labels,
   which pushed a long Product Description further down the page and
   toward the multi-page pagination edge cases below. */
.preview-meta {
  display: flex;
  border-bottom: 1px solid var(--line);
  padding: 12px 24px;
}

.preview-meta > div {
  flex: 1;
  min-width: 0;
  padding: 0 20px;
  border-left: 1px solid var(--line);
}

.preview-meta > div:first-child {
  padding-left: 0;
  border-left: none;
}

.preview-meta span {
  display: block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted-2);
  margin-bottom: 4px;
}

.preview-meta strong {
  display: block;
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#previewIssued,
#previewDue {
  font-family: "IBM Plex Mono", monospace;
  font-weight: 600;
}

.preview-meta .due-text { color: var(--danger); }

.preview-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 240px;
  border-bottom: 1px solid var(--line);
}

.preview-left { padding: 20px 22px; }

.preview-right {
  padding: 16px 18px;
  border-left: 1px solid var(--line);
  background: var(--panel-soft);
}

.preview-block { margin-bottom: 20px; }

.preview-block h4 {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted-2);
  margin-bottom: 10px;
}

/* Each size (plus Total/Average) gets its own equal-width bordered box,
   rather than one continuous colored table — matches the plain, boxed
   reference design and drops the same solid-color fill removed from the
   header above. */
.preview-size-boxes {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 6px;
}

.preview-size-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 10px 4px;
  text-align: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
}

.preview-size-box-label {
  font-size: max(8px, calc(10px * var(--size-scale, 1)));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.preview-size-box-value {
  font-family: "IBM Plex Mono", monospace;
  font-size: max(10px, calc(1.2rem * var(--size-scale, 1)));
  font-weight: 700;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.preview-description {
  line-height: 1.75;
  font-size: 0.88rem;
}

.preview-description h3 {
  font-size: 1.1rem;
  font-weight: 800;
  color: var(--ink);
  margin-bottom: 6px;
}

/* Cutting Report — Step 7's own standalone document with its own PDF
   download (not part of the job card preview/PDF), styled after the
   factory's physical cutting-report form (yellow background, heavy
   borders) so the on-screen preview matches the printed PDF exactly. */
.cutting-report-panel {
  background: #fff;
}

/* Locked to the real A4 ratio (595.28 : 841.89 pt) so the on-screen box is
   proportioned exactly like the exported page — the table then stretches
   via flex to fill that box top-to-bottom, so row height responds to
   however tall the page actually is instead of a fixed padding guess that
   leaves a dead zone below a short table.

   Fixed width, not max-width — same reasoning as .preview-sheet: shrinking
   to fit a narrow viewport (via max-width) turned this into an unreadably
   tiny miniature on a phone instead of a real, legible page. Wrapped in
   the same .a4-preview-scroll/.a4-preview-center pair the job card preview
   uses, so a narrow screen scrolls to see it at full size instead of
   squashing it. */
#cuttingReportPrintable {
  aspect-ratio: 595.28 / 841.89;
  width: 210mm;
  display: flex;
  flex-direction: column;
  background: #fff;
  padding: 16px;
  border-radius: var(--radius-sm);
}

.cutting-report-title-bar {
  flex-shrink: 0;
  border: 2px solid #1a1a1a;
  border-radius: var(--radius-sm);
  background: #fff;
  color: #1a1a1a;
  font-weight: 800;
  font-size: 1.1rem;
  letter-spacing: 0.03em;
  text-align: center;
  text-transform: uppercase;
  padding: 12px;
  margin-bottom: 14px;
}

.cutting-report-table-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* The table's columns need real minimum widths to stay readable, so on
     narrow screens it can end up wider than this wrapper — scroll
     horizontally to reach every column instead of clipping them
     permanently (overflow-y stays hidden purely to mask the rounded
     corners against the table underneath, matching .preview-header's
     corner-radius approach elsewhere). */
  overflow-x: auto;
  overflow-y: hidden;
  border-radius: var(--radius-sm);
  border: 2px solid #1a1a1a;
}

.cutting-report-table {
  width: 100%;
  height: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  background: #fff;
  font-size: 0.82rem;
}

.cutting-report-table th,
.cutting-report-table td {
  border: 1.5px solid #1a1a1a;
  padding: 8px 10px;
  text-align: center;
  vertical-align: middle;
  color: #1a1a1a;
  white-space: nowrap;
  background: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cutting-report-table thead th {
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.78rem;
  padding: 14px 8px;
  white-space: normal;
}

/* Each size column's header is two stacked cells — its name (a <th> in
   #cuttingReportHeadRow) and its ratio (a <td> in
   #cuttingReportHeadBottomRow right below it) — which need to read as one
   evenly-split box, matching the equal top/bottom split used for the
   Total/Average and size cells in the body. Without a matching explicit
   height they rendered unevenly: the name row inherited thead th's taller
   14px padding while the ratio row fell back to the generic 8px cell
   padding, so the two halves visibly didn't match. */
#cuttingReportHeadRow th:not([rowspan]),
#cuttingReportHeadBottomRow td {
  height: 26px;
  padding: 8px;
  font-size: 0.78rem;
}

/* Blank spacer row between the header and the first data row, with no
   vertical rulings — matches the physical form's own separation, which
   uses a border-less gap instead of an unbroken grid line straight through
   from header to body. */
.cutting-report-table tr.header-separator td {
  padding: 4px 0;
  border-left: none;
  border-right: none;
}

/* MTR/MPP/Fabric Name are hand-typed free text with no length limit — a
   long value (or a single unbroken word with no spaces to wrap on, e.g. a
   design code) needs to break mid-word and wrap onto a second line inside
   its own cell instead of overflowing past the column border into the size
   columns next to it. text-overflow:ellipsis (the base rule) would clip it
   instead, silently hiding real data. */
.cutting-report-table td:nth-child(2),
.cutting-report-table td:nth-child(3),
.cutting-report-table td:nth-child(4) {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: anywhere;
  word-break: break-word;
  font-size: 0.76rem;
  line-height: 1.25;
}

.cutting-report-table tbody td.cutting-report-swatch {
  font-size: 0.72rem;
  font-weight: 400;
  color: rgba(26, 26, 26, 0.45);
  white-space: normal;
  /* The physical form left-aligns this instructional label (it starts right
     at the cell's left edge) instead of centering it like the other
     columns. It's a hand-writing prompt, not real data, so it's styled to
     recede like placeholder text rather than compete with actual entries. */
  text-align: left;
  padding-left: 12px;
}

/* Each fabric row is two real <tr>s (top/bottom) with the Fabric Cut / MTR /
   Fabric Name / Total cells rowspan-ned across both — a blank top half for
   a hand-written layer/bundle count, and a bottom half showing the
   computed piece count once fabric data exists. Top and bottom now split
   the row exactly evenly (equal height AND equal padding); an earlier
   ~1:2 split read as visually inconsistent with the header's own
   size-name/ratio split directly above it, which is (and needs to stay)
   an even 50/50 box. */
.cutting-report-table tr.cutting-report-row-top td,
.cutting-report-table tr.cutting-report-row-bottom td {
  height: 30px;
  padding: 12px 10px;
}

.cutting-report-table tbody td:nth-child(2),
.cutting-report-table tbody td:nth-child(3) {
  font-weight: 700;
}

.cutting-report-table tfoot td {
  font-weight: 800;
  background: #f5f5f5;
  /* Chromium doesn't give tfoot rows a share of the table's stretched
     height the way it does tbody rows under height:100% — without an
     explicit padding boost the TOTAL row renders far shorter than every
     other row (measured ~32px vs ~106px+ for data rows). */
  padding: 26px 10px;
}

/* The form restates "TOTAL" in the final column's footer cell instead of a
   computed sum, faded relative to the main TOTAL label. */
.cutting-report-table td.cutting-report-total-echo {
  color: rgba(26, 26, 26, 0.45);
}

.preview-image-box {
  height: 160px;
  border-radius: var(--radius-sm);
  border: 1.5px dashed var(--line-strong);
  background: #fff;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 10px;
}

.preview-image-box img {
  display: none;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #fff;
}

.preview-image-box img.has-image {
  display: block;
}

.image-box-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  color: var(--muted-2);
}

.image-box-placeholder small {
  font-size: 11px;
  font-weight: 600;
}

.preview-image-box:has(img.has-image) .image-box-placeholder {
  display: none;
}

.preview-photos {
  padding: 16px 22px 22px;
  margin-bottom: 0;
}

.preview-footer {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 22px 16px;
  border-top: 1px solid var(--line);
  color: var(--muted-2);
  font-size: 12px;
  font-weight: 600;
  background: var(--panel-soft);
}

/* ════════════════════════════════════════════════════
   DIALOGS / MODALS
════════════════════════════════════════════════════ */
dialog {
  border: none;
  background: transparent;
  padding: 0;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  max-height: calc(100vh - 64px);
  max-height: calc(100dvh - 64px); /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  max-width: calc(100vw - 32px);
  overflow: visible;
}

dialog::backdrop {
  background: rgba(8, 24, 44, 0.52);
  backdrop-filter: blur(4px);
}

.dialog-card {
  width: min(480px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  max-height: calc(100dvh - 64px); /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  overflow-y: auto;
  padding: 22px 24px;
  border-radius: var(--radius-xl);
  background: #fff;
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--line);
}

.dialog-card h3 {
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink);
  margin-bottom: 20px;
}

.avatar-upload {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 0 auto 14px;
  cursor: pointer;
  width: fit-content;
}

.avatar-preview-wrap {
  position: relative;
}

.avatar-preview {
  width: 88px;
  height: 88px;
  border-radius: 50%;
  border: 2px solid var(--line-strong);
  background: var(--brand-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 38px;
  color: var(--brand-2);
  overflow: hidden;
  transition: border-color 0.15s ease;
}

.avatar-upload:hover .avatar-preview {
  border-color: var(--brand-3);
}

.avatar-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-edit-badge {
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--brand-2);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  border: 2.5px solid #fff;
  box-shadow: 0 3px 8px rgba(15, 39, 66, 0.28);
}

.avatar-upload-hint {
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-2);
}

.link-btn {
  display: block;
  margin: -14px auto 18px;
  padding: 0;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--danger);
  cursor: pointer;
}

.link-btn:hover {
  text-decoration: underline;
}

/* A long Address/Email/GST value, clamped to a short preview with a
   "more"/"less" toggle (see renderClampableCell in app.js). inline-block on
   the text span is the key fix here — without it, an unbroken long value
   wider than the cell got hard-wrapped by the browser mid-word, and the
   toggle button (a separate inline sibling) landed wherever that wrap
   happened to fall, visually splicing "more" into the middle of the word
   instead of appearing cleanly after it. As an atomic inline-block box, the
   whole span wraps internally on its own but the button always follows it,
   never interleaves inside it. */
.clamp-text {
  display: inline-block;
  max-width: 100%;
  overflow-wrap: anywhere;
  vertical-align: top;
}

.clamp-toggle {
  display: inline;
  margin-left: 4px;
  padding: 0;
  border: none;
  background: none;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-2);
  cursor: pointer;
  text-decoration: underline;
  white-space: nowrap;
}

.table-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--panel-soft);
  overflow: hidden;
  margin-right: 8px;
  font-size: 14px;
  vertical-align: middle;
}

.table-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.color-swatch {
  display: inline-block;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--line);
  vertical-align: middle;
}

/* Name column with an avatar should never wrap — a long name (e.g. "System
   Admin") breaking to two lines makes that row taller than its neighbours
   and throws off the whole table's vertical alignment. */
#userTableBody td:first-child,
#employeeTableBody td:first-child {
  white-space: nowrap;
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

/* ════════════════════════════════════════════════════
   TOGGLE / CHECKBOX GRID
════════════════════════════════════════════════════ */
.toggle-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-bottom: 4px;
}

.toggle-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  border: 1.5px solid var(--line);
  background: var(--panel-soft);
  cursor: pointer;
  transition: all 0.15s ease;
  font-size: 13px;
  font-weight: 600;
}

.toggle-card:hover {
  border-color: var(--brand-3);
  background: var(--brand-soft);
}

.toggle-card input[type="checkbox"] {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  accent-color: var(--brand-2);
  cursor: pointer;
}

.permission-section-label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin: 16px 0 8px;
}

/* Category + action checkbox tree for the Add/Edit User permission form —
   one bordered box per feature (Job Card/User/Party/Factory/Priority),
   a bold category checkbox up top, its specific actions (Add/Edit/
   Delete/Active-Deactivate) indented below. The category checkbox is a
   "select all in this box" convenience toggle wired in app.js, not its
   own separate permission. */
.permission-groups {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  gap: 8px;
  margin-bottom: 8px;
}

.permission-group {
  border: 1.5px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel-soft);
  padding: 10px 12px;
}

.permission-group-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 800;
  color: var(--ink);
  cursor: pointer;
  padding-bottom: 8px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--line);
}

.permission-group-title input[type="checkbox"] {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  accent-color: var(--brand-2);
  cursor: pointer;
}

.permission-group-items {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.permission-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted-2);
  cursor: pointer;
}

.permission-item input[type="checkbox"] {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  accent-color: var(--brand-2);
  cursor: pointer;
}

/* ════════════════════════════════════════════════════
   RESPONSIVE
════════════════════════════════════════════════════ */
@media (max-width: 1200px) {
  :root { --sidebar-w: 230px; }

  .admin-overview {
    grid-template-columns: 1fr;
  }

  .admin-stats {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 1000px) {
  .portal-topbar {
    padding: 0 14px;
    gap: 10px;
  }

  /* Keeps the logo and the avatar/logout together on one row instead of
     wrapping onto a second line — the full company name + a name/role
     text stack + a logout button together don't fit a phone's width, so
     the less essential text (company subtitle, active user's name/role)
     gives way; the icon, avatar, and Logout button (what you actually need
     to tap) stay put. */
  .mobile-nav-toggle {
    display: flex;
  }

  /* The logo already reappears in the drawer's own header once it's open
     (see .admin-sidebar-head) — showing it a second time in the topbar
     next to the hamburger just crowds an already-tight mobile width. */
  .brand-block {
    display: none;
  }

  .user-chip-info {
    display: none;
  }

  .user-chip {
    padding: 6px;
  }

  .topbar-right {
    gap: 8px;
  }

  .app-shell-layout {
    grid-template-columns: 1fr;
  }

  /* Off-canvas drawer instead of an in-flow icon grid — closed by default
     (translated fully off the left edge), slid into view only once
     body.mobile-nav-open is set by the hamburger button. Freed from normal
     document flow, it no longer pushes the actual page content down. */
  .admin-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: min(280px, 82vw);
    height: 100vh;
    height: 100dvh;
    max-height: none;
    border-radius: 0;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    z-index: 600;
  }

  body.mobile-nav-open .admin-sidebar {
    transform: translateX(0);
  }

  .admin-nav {
    flex-direction: column;
    padding: 0 0 8px;
    gap: 0;
  }

  .admin-nav-btn {
    min-width: 0;
    padding: 13px 18px;
  }

  .admin-sidebar-head { display: block; padding: 10px 16px; }
  .admin-sidebar-foot { margin: 0 10px 10px; }

  .toolbar-grid,
  .toolbar-grid.admin-toolbar,
  .grid.two {
    grid-template-columns: 1fr;
  }

  .admin-overview {
    grid-template-columns: 1fr;
  }

  /* .preview-main/.preview-meta are NOT collapsed here even on a narrow
     viewport — .preview-sheet is always a fixed 210mm (A4) box regardless
     of screen size (see .preview-sheet below), so it always has room for
     the desktop 2-column layout. Media queries key off the viewport, not
     the container, so without this exclusion, viewing or generating a PDF
     from a phone forced this fixed-width sheet into the narrow-screen
     single-column stack anyway — producing a much taller capture with a
     completely different page layout than the same job viewed on desktop,
     and consequently a differently (and badly) paginated PDF. */
}

@media (max-width: 768px) {
  .portal-main,
  form {
    padding: 16px;
  }

  .screen-card {
    padding: 16px;
  }

  .size-grid {
    grid-template-columns: 1fr 1fr;
  }

  /* .fabric-grid keeps its 4-column base definition here (not squeezed to
     2 columns like .size-grid) — it has 4 real fields (MTR/MPP/Fabric
     Name/Action), so a blind 2-column collapse would leave 2 of them in
     unaligned implicit grid tracks instead of matching the header row. */

  .photo-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  /* .preview-photo-grid stays at 5 columns even here — it's inside
     .preview-sheet, which is always a fixed A4 width regardless of the
     viewport (see the .preview-main/.preview-meta note above for why). */

  .admin-stats {
    grid-template-columns: repeat(3, 1fr);
  }

  .toggle-grid {
    grid-template-columns: 1fr;
  }

  .topbar-right {
    flex-wrap: wrap;
    gap: 8px;
  }
}

@media (max-width: 520px) {
  .auth-card { padding: 24px 20px; }

  /* The Party/Factory dropdown plus its +Add/Edit buttons don't fit
     comfortably on one line at this width — put the dropdown on its own
     full-width row, then +Add and Edit share the row below it side by
     side (not each stacked as a separate full-width row, which wasted
     vertical space for no reason). */
  .option-select-row {
    flex-wrap: wrap;
  }

  .option-select-row select {
    flex-basis: 100%;
  }

  .option-select-row .ghost-btn {
    flex: 1 1 calc(50% - 4px);
  }

  .admin-stats {
    grid-template-columns: 1fr;
  }

  /* Keep Size/Fabric rows as one compact row each (matching the desktop
     table look) instead of stacking every field on its own line — a
     stacked layout was 3x taller than needed and read as "not compact"
     on real devices. The Remove button shrinks to an icon-only circle
     (see .remove-row below) to make room. */
  .size-grid   { grid-template-columns: 1.3fr 0.85fr 34px; gap: 6px; }
  .fabric-grid { grid-template-columns: 0.6fr 0.5fr 1fr 34px; gap: 6px; }

  .table-head.size-grid,
  .table-head.fabric-grid {
    padding-bottom: 6px;
    margin-bottom: 6px;
    font-size: 9px;
  }

  .table-row.size-grid,
  .table-row.fabric-grid {
    padding: 8px;
    margin-bottom: 8px;
  }

  .remove-row {
    font-size: 0;
    padding: 0;
    width: 34px;
    height: 34px;
    min-width: 34px;
    border-radius: 50%;
  }

  .remove-row::after {
    content: "×";
    font-size: 20px;
    line-height: 1;
  }

  .size-grid input,
  .fabric-grid input {
    padding: 9px 10px;
    font-size: 0.82rem;
  }

  /* The editor toolbar's 15+ controls can't all fit on a ~350px-wide
     screen at full size (they don't even all fit on this line at desktop
     width without wrapping once or twice) — shrink every control and swap
     the wordiest buttons (List/Link/Color) down to icon-only so it wraps
     to 2 rows instead of 4-5. */
  .editor-toolbar {
    padding: 6px 8px;
    gap: 4px;
  }

  .editor-toolbar-group {
    gap: 2px;
  }

  .editor-toolbar-divider {
    display: none;
  }

  .editor-toolbar button,
  .editor-select {
    height: 26px;
    min-width: 24px;
    font-size: 11px;
    padding: 0 5px;
  }

  #editorFormatBlock {
    max-width: 78px;
    padding: 0 20px 0 6px;
    background-position: right 6px center;
    background-size: 14px;
  }

  .editor-toolbar button[data-command="insertUnorderedList"],
  .editor-toolbar button[data-command="insertOrderedList"],
  #editorLinkBtn {
    font-size: 0;
    padding: 0;
    width: 28px;
  }

  .editor-toolbar button[data-command="insertUnorderedList"]::after {
    content: "•";
    font-size: 16px;
  }

  .editor-toolbar button[data-command="insertOrderedList"]::after {
    content: "1.";
    font-size: 12px;
  }

  #editorLinkBtn::after {
    content: "🔗";
    font-size: 13px;
  }

  .color-picker {
    padding: 3px 5px;
    gap: 4px;
  }

  .color-picker span {
    display: none;
  }

  .color-picker input[type="color"] {
    width: 18px;
    height: 18px;
  }

  /* .row-cell-label stays hidden — the column header row above already
     labels each field now that rows no longer stack to 1 column. */

  /* Size/Fabric panels were double-boxed on mobile — the outer .screen-card
     plus this inner .boxed-panel border+background, both with their own
     padding, ate space and read as two nested cards instead of one compact
     block. Drop the inner box; the individual .table-row cards underneath
     already give each row its own visible border. */
  .boxed-panel {
    padding: 0;
    border: none;
    background: transparent;
    border-radius: 0;
  }

  .photo-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .wizard-actions {
    flex-wrap: wrap;
    padding: 14px 16px 18px;
  }

  .wizard-actions button {
    flex: 1 1 100%;
  }

  #employeeTabs {
    flex-wrap: wrap;
  }

  .page-header {
    flex-direction: column;
    align-items: flex-start !important;
    gap: 10px;
  }

  .user-chip-info {
    display: none;
  }

  .user-chip-menu {
    right: -8px;
  }

  .avatar-upload {
    margin-left: auto;
    margin-right: auto;
  }

  /* .preview-header/.preview-job/.preview-footer/.cutting-report-title-bar
     are NOT collapsed here even on a narrow viewport, for the same reason
     .preview-main/.preview-meta/.preview-photo-grid aren't (see the note
     near .preview-sheet below): .preview-sheet and #cuttingReportPrintable
     are always a fixed 210mm (A4) box regardless of screen size, so they
     always have the room these rules assumed a phone screen didn't have.
     An earlier version of this file stacked them at this breakpoint, which
     made the on-screen mobile preview AND the PDF generated from a phone
     (a flattened capture of this same markup) look different from — and
     visually broken compared to — the desktop version. */

  /* The admin nav now lives in the off-canvas drawer (see @media max-width:
     1000px) at every mobile width, including this one — its own width is
     capped at min(280px, 82vw) regardless of how narrow the phone is, which
     is plenty of room for icon + full label side by side like desktop, so
     unlike the old in-flow icon grid this no longer needs its own
     narrower-screen fallback layout. */

  /* Every data table (Job Cards / Employees / User Access) becomes a
     column of self-labeled cards instead of a wide table — no horizontal
     scroll needed at all, and every column's full value stays visible
     (each row's own td::before echoes that column's header as a label,
     since <thead> itself is hidden here). */
  .table-shell table {
    min-width: 0;
  }

  .table-shell thead {
    display: none;
  }

  .table-shell table,
  .table-shell tbody,
  .table-shell tr {
    display: block;
    width: 100%;
  }

  .table-shell tr:not(.empty-state) {
    margin-bottom: 12px;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
  }

  .table-shell tr:not(.empty-state):last-child {
    margin-bottom: 0;
  }

  .table-shell td {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding: 6px 0;
    border: none !important;
    text-align: right;
    overflow-wrap: anywhere;
  }

  .table-shell td::before {
    content: attr(data-label);
    font-weight: 700;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    text-align: left;
    flex-shrink: 0;
  }

  .table-shell td[data-label="Actions"] {
    flex-direction: column;
    align-items: stretch;
  }

  .table-shell td[data-label="Actions"]::before {
    margin-bottom: 6px;
  }

  .table-shell td[data-label="Actions"] .row-actions {
    flex-wrap: wrap;
    justify-content: flex-end;
  }

  .table-shell tr.empty-state {
    padding: 16px;
  }

  .table-shell tr.empty-state td {
    display: block;
    text-align: center;
  }

  .table-shell tr.empty-state td::before {
    content: none;
  }

  /* Shrinks the Edit/Delete footprint in every admin table so less of the
     row needs to scroll into view to reach them. */
  .row-actions .mini-btn {
    padding: 6px 10px;
    font-size: 0.75rem;
  }

  /* The subtitle line and "Employee mode" badge are purely descriptive —
     on a phone they push the actually-useful Job Cards table further down
     the screen than the extra context is worth. */
  .page-header .muted,
  .panel-note {
    display: none;
  }

  .page-header {
    margin-bottom: 12px;
  }
}

/* ════════════════════════════════════════════════════
   MODERN DASHBOARD LAYOUT & WIZARD POLISH
   (Sidebar attached to left/top and hidden step pills)
   ════════════════════════════════════════════════════ */

/* Hide step pills, wizard progress bar, and step eyebrows as requested.
   Scoped to .section-heading (the "Step N of 8" labels) rather than every
   .eyebrow under .step, since other .eyebrow text inside a .step (e.g.
   inside .preview-meta) must stay visible. */
.step-pills,
.wizard-progress,
.section-heading .eyebrow {
  display: none !important;
}

/* Let the admin layout span full screen width and height — this whole
   group is keyed off body.is-admin (toggled in JS right after login/session
   restore, see setAuthenticatedUser), NOT which view-panel is active, so
   the sidebar+full-bleed layout stays exactly the same whether an admin is
   looking at the Admin panel or the Job Card wizard. An employee session
   never gets this class, so .admin-sidebar (display:none by default) never
   appears for them regardless of which view-panel they're on. */
body.is-admin .portal-main {
  max-width: 100% !important;
  padding: 0 !important;
  margin: 0 !important;
}

body.is-admin .app-shell-layout {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  gap: 0 !important;
  min-height: calc(100vh - var(--topbar-h));
  min-height: calc(100dvh - var(--topbar-h)); /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  align-items: stretch;
}

body.is-admin .admin-sidebar {
  display: flex;
  flex-direction: column;
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  height: calc(100dvh - var(--topbar-h)); /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  max-height: calc(100vh - var(--topbar-h));
  max-height: calc(100dvh - var(--topbar-h)); /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  border-radius: 0 !important;
  border-top: none !important;
  border-left: none !important;
  border-bottom: none !important;
  border-right: 1px solid var(--line) !important;
  box-shadow: none !important;
  margin: 0 !important;
}

/* The scrollable column next to the sidebar — holds whichever view-panel
   is currently active (employee wizard or admin panels). Replaces what
   used to be .admin-content's job when the sidebar only ever wrapped the
   admin panels; .admin-content below now only needs its own padding. */
body.is-admin .app-shell-content {
  min-width: 0;
  max-height: calc(100vh - var(--topbar-h));
  max-height: calc(100dvh - var(--topbar-h)); /* dvh overrides vh above on browsers that support it — accounts for mobile browser chrome (address bar) that 100vh doesn't */
  overflow-y: auto;
}

body.is-admin .admin-content {
  padding: 32px 40px 48px;
}

/* .admin-content (above) only wraps the actual admin-panel sections — the
   Job Card wizard lives in the employee view-panel, a separate sibling
   inside .app-shell-content that gets none of that padding. That's
   invisible for a real employee session (.portal-main still has its own
   default padding there), but for an admin — whose .portal-main padding is
   forced to 0 above, to let the sidebar sit flush and full-bleed — it left
   the wizard's own header and content flush against the sidebar with no
   breathing room at all, while every other admin screen had proper
   padding. Match .admin-content's padding here so the wizard looks
   consistent with the rest of the admin experience. */
body.is-admin [data-view-panel="employee"] {
  padding: 32px 40px 48px;
}

/* The rules above are unscoped (no media query) and use a higher-
   specificity body.is-admin selector, so they silently overrode the
   @media (max-width: 1000px) sidebar-collapse rules above at every
   viewport — including phones, where a fixed 230px sidebar left only ~140px
   for actual content. This block wins the same way (same selector prefix,
   declared later) to restore the single-column collapse on small screens. */
@media (max-width: 1000px) {
  body.is-admin .app-shell-layout {
    grid-template-columns: 1fr !important;
    min-height: 0 !important;
  }

  /* Off-canvas drawer, same as the unscoped .admin-sidebar mobile rule
     above — restated here with !important because body.is-admin's own
     unscoped position:sticky/height rules (declared earlier, no media
     query) otherwise win on specificity at every viewport, including
     phones. */
  body.is-admin .admin-sidebar {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    width: min(280px, 82vw) !important;
    height: 100vh !important;
    height: 100dvh !important;
    max-height: none !important;
    border-right: none !important;
    border-bottom: none !important;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }

  body.mobile-nav-open .admin-sidebar {
    transform: translateX(0) !important;
  }

  body.is-admin .app-shell-content {
    max-height: none !important;
    overflow-y: visible !important;
  }

  body.is-admin .admin-content {
    padding: 20px !important;
  }

  /* Matches .admin-content's own mobile reduction just above — without
     this, the wizard (reached via "+ Create Job Card") kept its desktop
     32px/40px padding while every other admin screen dropped to 20px, and
     that padding stacked with #employeeCreateScreen's own fixed 32px
     padding below (also !important) to leave barely 200px of a 390px
     phone screen for the actual form — the Fabric Details row's MTR/MPP
     inputs were rendered too narrow to show more than one clipped digit. */
  body.is-admin [data-view-panel="employee"] {
    padding: 20px !important;
  }
}

/* Center employee/client view card nicely since main padding was removed */
#employeeCreateScreen {
  max-width: 1200px;
  margin: 24px auto !important;
  padding: 32px !important;
}

/* #employeeCreateScreen above hardcodes its padding/margin with
   !important on an ID selector — the same specificity as the override
   below, so source order (this rule coming later) is what makes the
   mobile version win, not the media query alone. Without this, the
   wizard's own card stayed at a fixed 32px padding + 24px auto margin on
   every screen size, stacking with the admin shell's own mobile-reduced
   padding to leave barely half a 390px phone screen for the actual form
   — the Fabric Details step's MTR/MPP inputs were too narrow to show
   more than one clipped digit. */
@media (max-width: 768px) {
  #employeeCreateScreen {
    padding: 16px !important;
    margin: 12px auto !important;
  }
}

/* Contenteditable Placeholder */
.editor:empty::before {
  content: attr(placeholder);
  color: var(--muted-2);
  font-style: normal;
  cursor: text;
  pointer-events: none;
  display: block;
}

