
  :root{
    /* ---- Shopify Polaris-inspired tokens ---- */
    --navy:#303030; --navy2:#1a1a1a; --ink:#303030; --muted:#616161; --muted2:#8a8a8a;
    --pink:#303030; --pink2:#4a4a4a; --pink-dark:#303030; --pink-deep:#1a1a1a;
    --bg:#f1f1f1;
    --card:#ffffff; --line:#e3e3e3; --line2:#ebebeb;
    --green:#0c5132; --green-dk:#0a4327;
    --amber:#8a6116; --red:#8e1f0b; --blue:#00527c;
    --accent:#005bd3;            /* Polaris focus / link blue */
    --topnav:#1a1a1a;            /* global dark bar */
    --topnav-field:#303030;
    --radius:12px; --radius-sm:8px; --radius-lg:16px;
    --shadow-xs:0 1px 0 rgba(0,0,0,.05);
    --shadow:0 1px 0 rgba(0,0,0,.05);
    --shadow-md:0 4px 12px rgba(0,0,0,.10);
    --shadow-lg:0 26px 80px rgba(0,0,0,.22);
    --shadow-glow:0 0 0 3px rgba(0,91,211,.25);
    --btn-shadow:0 -1px 0 0 #b5b5b5 inset, 0 0 0 1px rgba(0,0,0,.10) inset, 0 .5px 0 1.5px #fff inset;
    --btn-shadow-dark:0 -1px 0 0 #000 inset, 0 0 0 1px rgba(255,255,255,.10) inset, 0 .5px 0 1.5px rgba(255,255,255,.22) inset;
    --grad:#303030;
    --grad-deep:#1a1a1a;
    --grad-navy:#f1f1f1;
    --grad-mesh:none;
    --ease:cubic-bezier(.22,.9,.32,1.16);
    --ease-soft:cubic-bezier(.4,0,.2,1);
  }
  *{box-sizing:border-box; margin:0; padding:0}
  html,body{height:100%}
  body{font-family:Inter,-apple-system,BlinkMacSystemFont,"San Francisco","Segoe UI",Roboto,"Helvetica Neue",sans-serif; background:var(--bg); color:var(--ink); font-size:13px; line-height:1.45; -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; letter-spacing:normal}
  body::before{content:none}
  button{font-family:inherit; cursor:pointer}
  :focus-visible{outline:2px solid var(--accent); outline-offset:1px; border-radius:6px}
  @media (prefers-reduced-motion: reduce){*{animation-duration:.01ms!important; transition-duration:.01ms!important}}
  input,select,textarea{font-family:inherit; font-size:13px; color:var(--ink)}
  ::-webkit-scrollbar{width:10px; height:10px}
  ::-webkit-scrollbar-thumb{background:#c9c9c9; border-radius:20px; border:2.5px solid transparent; background-clip:content-box}
  ::-webkit-scrollbar-thumb:hover{background:#adadad; background-clip:content-box}
  ::-webkit-scrollbar-track{background:transparent}

  /* ================= GLOBAL DARK TOP BAR (Shopify admin) ================= */
  .globalbar{position:sticky; top:0; z-index:50; background:var(--topnav); height:56px; display:flex; align-items:center; gap:16px; padding:0 12px 0 16px}
  .globalbar .gb-brand{display:flex; align-items:center; gap:9px; color:#e3e3e3; font-size:13.5px; font-weight:650; white-space:nowrap}
  .globalbar .gb-brand .gb-mark{width:26px; height:26px; border-radius:7px; background:#1a1a1a; border:1px solid #4a4a4a; display:flex; align-items:center; justify-content:center; font-size:14px}
  .globalbar .gb-search{flex:1; max-width:520px; margin:0 auto; position:relative}
  .globalbar .gb-field{display:flex; align-items:center; min-height:32px; border-radius:8px; background:var(--topnav-field); border:1px solid #616161; padding:0 8px 0 34px; gap:6px; transition:.15s}
  .globalbar .gb-field:hover{background:#3a3a3a}
  .globalbar .gb-field:focus-within{background:#3a3a3a; border-color:#8a8a8a; box-shadow:0 0 0 2px rgba(255,255,255,.18)}
  .globalbar .gb-search input{flex:1; min-width:0; width:auto; height:30px; border:none; border-radius:0; background:transparent; color:#e3e3e3; padding:0 4px 0 0; font-size:13px; outline:none; box-shadow:none}
  .globalbar .gb-search input::placeholder{color:#b5b5b5}
  .globalbar .gb-search input:hover,.globalbar .gb-search input:focus{background:transparent; border:none; box-shadow:none}
  .globalbar .gb-field:focus-within .gb-kbd{opacity:0; pointer-events:none}
  .gb-chip{display:inline-flex; align-items:center; gap:2px; height:22px; padding:0 2px 0 8px; border-radius:6px; background:#4a4a4a; color:#f1f1f1; font-size:12px; font-weight:650; flex:none; white-space:nowrap}
  .gb-chip .gb-chip-x{border:none; background:transparent; color:#c9c9c9; width:18px; height:18px; border-radius:4px; display:inline-flex; align-items:center; justify-content:center; cursor:pointer; padding:0; line-height:1}
  .gb-chip .gb-chip-x:hover{background:#616161; color:#fff}
  .gb-pop{display:none; position:absolute; top:calc(100% + 8px); left:50%; transform:translateX(-50%); width:min(560px,94vw); max-height:min(72vh,560px); overflow:auto; background:#fff; color:var(--ink); border-radius:12px; box-shadow:0 18px 48px rgba(0,0,0,.32); z-index:80; padding:6px}
  .gb-pop.show{display:block; animation:drop .12s var(--ease-soft)}
  .gb-sec{font-size:11.5px; font-weight:650; color:var(--muted); padding:8px 10px 4px}
  .gb-row{display:flex; align-items:flex-start; gap:10px; width:100%; text-align:left; border:none; background:transparent; padding:8px 10px; border-radius:8px; cursor:pointer; color:inherit; font:inherit}
  .gb-row:hover,.gb-row.on{background:#f1f1f1}
  .gb-row .gb-ico{width:28px; height:28px; border-radius:8px; background:#f1f1f1; color:#4a4a4a; display:flex; align-items:center; justify-content:center; flex:none}
  .gb-row.on .gb-ico,.gb-row:hover .gb-ico{background:#e3e3e3}
  .gb-row .gb-body{min-width:0; flex:1}
  .gb-row .gb-t{font-size:13px; font-weight:650; display:flex; flex-wrap:wrap; gap:6px; align-items:center}
  .gb-row .gb-s{font-size:12px; color:var(--muted); margin-top:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .gb-more{width:100%; border:none; background:none; padding:10px; font-size:13px; font-weight:650; color:var(--accent); cursor:pointer; border-radius:8px; font-family:inherit}
  .gb-more:hover,.gb-more.on{background:#f1f6fd}
  .gb-empty{padding:22px 14px; text-align:center; color:var(--muted); font-size:13px}
  .globalbar .gb-search::before{content:""; position:absolute; left:11px; top:9px; width:14px; height:14px; opacity:.7;
    background:currentColor; color:#b5b5b5;
    -webkit-mask:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Ccircle cx='9' cy='9' r='5'/%3E%3Cpath d='M12.8 12.8 16.5 16.5'/%3E%3C/svg%3E") center/contain no-repeat;
    mask:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Ccircle cx='9' cy='9' r='5'/%3E%3Cpath d='M12.8 12.8 16.5 16.5'/%3E%3C/svg%3E") center/contain no-repeat}
  .globalbar .gb-kbd{position:absolute; right:8px; top:7px; height:18px; padding:0 6px; border-radius:5px; background:#4a4a4a; color:#b5b5b5; font-size:10.5px; font-family:inherit; font-weight:600; display:flex; align-items:center; letter-spacing:.2px; pointer-events:none}
  .globalbar .gb-right{display:flex; align-items:center; gap:6px; margin-left:auto}
  .globalbar .gb-btn{width:32px; height:32px; border-radius:8px; border:none; background:transparent; color:#e3e3e3; font-size:14px; display:flex; align-items:center; justify-content:center; transition:.14s}
  
  
  .jwr-items-table{table-layout:fixed;width:100%}
  .jwr-items-table th,.jwr-items-table td{vertical-align:middle}
  .jwr-items-table th:nth-child(1),.jwr-items-table td:nth-child(1){width:52px}
  .jwr-items-table th:nth-child(2),.jwr-items-table td:nth-child(2){width:22%}
  .jwr-items-table th:nth-child(3),.jwr-items-table td:nth-child(3),
  .jwr-items-table th:nth-child(4),.jwr-items-table td:nth-child(4){width:110px;text-align:center}
  .jwr-items-table th:nth-child(5),.jwr-items-table td:nth-child(5),
  .jwr-items-table th:nth-child(6),.jwr-items-table td:nth-child(6),
  .jwr-items-table th:nth-child(7),.jwr-items-table td:nth-child(7){width:112px;text-align:center}
  .jwr-items-table th:nth-child(8),.jwr-items-table td:nth-child(8){width:96px;text-align:center}
  .jwr-items-table th:nth-child(9),.jwr-items-table td:nth-child(9){width:150px}
  .jwr-items-table th:nth-child(10),.jwr-items-table td:nth-child(10){width:44px}
  .jwr-items-table td:nth-child(5) .inp,
  .jwr-items-table td:nth-child(6) .inp,
  .jwr-items-table td:nth-child(8) .inp{width:100%;max-width:100%}

  .open-stock-table{table-layout:fixed;width:100%}
  .open-stock-table th,.open-stock-table td{vertical-align:middle}
  .open-stock-table th:nth-child(1),.open-stock-table td:nth-child(1){width:56px}
  .open-stock-table th:nth-child(2),.open-stock-table td:nth-child(2){width:18%}
  .open-stock-table th:nth-child(3),.open-stock-table td:nth-child(3){width:26%}
  .open-stock-table th:nth-child(4),.open-stock-table td:nth-child(4),
  .open-stock-table th:nth-child(5),.open-stock-table td:nth-child(5),
  .open-stock-table th:nth-child(6),.open-stock-table td:nth-child(6){width:120px;text-align:center}
  .open-stock-table th:nth-child(7),.open-stock-table td:nth-child(7){width:100px;text-align:center}
  .open-stock-table th:nth-child(8),.open-stock-table td:nth-child(8){width:44px}
  .open-stock-table td:nth-child(4) .inp,
  .open-stock-table td:nth-child(5) .inp,
  .open-stock-table td:nth-child(7) .inp{width:100%;max-width:100%}

.globalbar .gb-btn{position:relative}
.globalbar .gb-btn:hover{background:#303030}
.globalbar .gb-btn .gb-badge{position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 4px;border-radius:99px;background:#dc2626;color:#fff;font-size:10px;font-weight:700;line-height:16px;text-align:center}
.qa-notif-item .qa-notif-meta{font-size:11px;color:#6b7280;margin-top:2px}
.qa-notif-item .qa-notif-type{font-weight:600;font-size:13px;color:#111827}
  .globalbar .gb-avatar{display:flex; align-items:center; gap:8px; height:32px; padding:0 8px 0 4px; border-radius:8px; background:#303030; color:#e3e3e3; font-size:12.5px; font-weight:600; border:none; transition:.14s}
  .globalbar .gb-avatar:hover{background:#3f3f3f}
  .globalbar .gb-avatar .av{width:24px; height:24px; border-radius:6px; background:#36fba1; color:#0c5132; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700}
  @media(max-width:920px){.globalbar .gb-brand span{display:none} .globalbar{padding:0 8px}}

  /* ===== hamburger ===== */
  .gb-burger{width:32px; height:32px; border-radius:8px; border:none; background:transparent; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; flex:none; transition:background .14s}
  .gb-burger:hover{background:#303030}
  .gb-burger span{display:block; width:16px; height:1.8px; border-radius:2px; background:#e3e3e3; transition:transform .22s var(--ease-soft), opacity .18s}
  body.nav-collapsed .gb-burger span:nth-child(1){transform:translateY(5.8px) rotate(45deg)}
  body.nav-collapsed .gb-burger span:nth-child(2){opacity:0}
  body.nav-collapsed .gb-burger span:nth-child(3){transform:translateY(-5.8px) rotate(-45deg)}

  /* ===== collapsed rail (desktop) ===== */
  .sidebar{transition:width .2s var(--ease-soft), min-width .2s var(--ease-soft), transform .22s var(--ease-soft)}
  body.nav-collapsed .sidebar{width:0; min-width:0; padding-left:0; padding-right:0; overflow:hidden}
  body.nav-collapsed .sidebar > *{opacity:0; pointer-events:none; transition:opacity .1s}

  /* ===== mobile drawer ===== */
  .nav-scrim{display:none; position:fixed; inset:56px 0 0; background:rgba(0,0,0,.45); z-index:29; animation:overlayIn .16s var(--ease-soft)}

  .app{display:flex; min-height:calc(100vh - 56px); position:relative; z-index:1; align-items:flex-start}

  /* ================= SIDEBAR (light Polaris nav) ================= */
  .sidebar{width:232px; min-width:232px; background:var(--bg); color:var(--ink); display:flex; flex-direction:column; position:sticky; top:56px; height:calc(100vh - 56px); z-index:30; overflow:hidden; padding:8px 8px 0; border-right:1px solid transparent}
  .sidebar::after{content:none}
  .brand{padding:8px 8px 10px; border-bottom:none; position:relative}
  .brand .logo{display:flex; align-items:center; gap:10px; padding:6px 8px; border-radius:8px; transition:.14s}
  .brand .logo:hover{background:#e3e3e3}
  .brand .logo .mark{width:30px;height:30px;border-radius:8px;background:#303030;display:flex;align-items:center;justify-content:center;font-size:15px;color:#fff;flex-shrink:0;box-shadow:none; position:relative}
  .brand .logo .mark::after{content:none}
  .brand h1{font-size:13.5px; letter-spacing:normal; color:var(--ink); font-weight:650}
  .brand p{font-size:11.5px; color:var(--muted); margin-top:1px; letter-spacing:normal}
  .nav{padding:2px 0 10px; flex:1; overflow-y:auto; position:relative}
  .nav .nav-sec{font-size:11px; text-transform:none; letter-spacing:normal; color:var(--muted); padding:14px 8px 4px; font-weight:650}
  .nav .nav-sec:first-child{padding-top:2px}.nav-group-toggle{width:100%;border:none;background:transparent;display:flex;align-items:center;justify-content:space-between;cursor:pointer}.nav-group-toggle .arr{font-size:12px;transition:transform .15s}.nav-group-toggle.closed .arr{transform:rotate(-90deg)}.nav-group-items.closed{display:none}
  .nav-item{display:flex; align-items:center; gap:10px; padding:6px 8px; border-radius:8px; color:var(--ink); text-decoration:none; font-size:13px; margin-bottom:1px; width:100%; background:none; border:none; text-align:left; transition:background .12s var(--ease-soft); position:relative; font-weight:550; min-height:32px}
  .nav-item:hover{background:#e3e3e3; color:var(--ink); transform:none}
  .nav-item.active{background:#e3e3e3; color:var(--ink); font-weight:650; box-shadow:none}
  .nav-item.active::before{content:""; position:absolute; left:-8px; top:50%; transform:translateY(-50%); width:3px; height:20px; background:#303030; border-radius:0 3px 3px 0; opacity:1}
  .nav-item .ico{width:20px; height:20px; display:flex; align-items:center; justify-content:center; flex:none; color:#4a4a4a}
  .nav-item.active .ico{color:#303030}
  .nav-item .nl{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  svg.i{flex:none; display:block; overflow:visible}
  .btn svg.i{margin-right:-1px}
  .nav-item .cnt{margin-left:auto; background:#e3e3e3; color:var(--muted); border-radius:8px; padding:1px 7px; font-size:11.5px; font-weight:650; box-shadow:inset 0 0 0 1px rgba(0,0,0,.06)}
  .nav-item.active .cnt{background:#fff; color:var(--ink)}
  .side-foot{padding:12px 18px 16px; border-top:1px solid #dcdcdc; font-size:11.5px; color:var(--muted); line-height:1.5; position:relative; margin:0 -8px}
  .side-foot .dot{display:inline-block; width:7px; height:7px; border-radius:50%; background:#29845a; margin-right:7px; box-shadow:0 0 0 2.5px rgba(41,132,90,.18)}
  .side-foot .sf-sub{margin-top:3px; padding-left:14px; color:#8a8a8a; font-size:11px}

  /* ================= MAIN / PAGE HEADER ================= */
  .main{flex:1; min-width:0; display:flex; flex-direction:column; background:var(--bg)}
  .topbar{background:transparent; backdrop-filter:none; -webkit-backdrop-filter:none; border-bottom:none; padding:20px 24px 4px; display:flex; align-items:center; gap:16px; position:static; z-index:20; box-shadow:none; max-width:none; width:100%; margin:0}
  .topbar h2{font-size:20px; font-weight:650; letter-spacing:normal; color:var(--ink); line-height:1.3; display:flex; align-items:center; gap:8px}
  .backlink{width:28px; height:28px; border-radius:8px; background:#fff; box-shadow:var(--btn-shadow); border:none; color:var(--ink); display:inline-flex; align-items:center; justify-content:center; flex:none; transition:background .12s}
  .backlink:hover{background:#f1f1f1}
  .topbar .sub{font-size:12.5px; color:var(--muted); margin-top:2px}
  .topbar .spacer{flex:1}
  .topbar .actions{display:flex; gap:8px; flex-wrap:wrap}
  .content{padding:16px 24px 60px; max-width:none; width:100%; margin:0; animation:fadeUp .2s var(--ease-soft)}
  
  @media(min-width:1500px){.topbar{padding:20px 32px 4px} .content{padding:16px 32px 60px} .msgbox{padding:0 32px}}
  @keyframes fadeUp{from{opacity:0; transform:translateY(4px)}to{opacity:1; transform:none}}

  /* ================= BUTTONS (Polaris) ================= */
  .btn{display:inline-flex; align-items:center; justify-content:center; gap:6px; border:none; border-radius:8px; padding:0 12px; min-height:32px; font-size:13px; font-weight:600; transition:background .12s var(--ease-soft), box-shadow .12s; white-space:nowrap; letter-spacing:normal; line-height:1}
  .btn-primary{background:#303030; color:#fff; box-shadow:var(--btn-shadow-dark)}
  .btn-primary:hover{background:#1a1a1a; transform:none; filter:none}
  .btn-primary:active{background:#000; transform:none}
  .btn-primary:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
  .btn-ghost{background:#fff; color:var(--ink); border:none; box-shadow:var(--btn-shadow)}
  .btn-ghost:hover{background:#f7f7f7; border-color:transparent; transform:none; box-shadow:var(--btn-shadow)}
  .btn-ghost:active{background:#f0f0f0}
  .btn-danger{background:#fff; color:#8e1f0b; box-shadow:var(--btn-shadow)}
  .btn-danger:hover{background:#fdf3f1; transform:none}
  .btn-sm{padding:0 10px; min-height:28px; font-size:12.5px; border-radius:8px}
  .btn-icon{background:none; border:none; border-radius:8px; width:28px; height:28px; font-size:13px; color:var(--muted); transition:.12s var(--ease-soft); display:inline-flex; align-items:center; justify-content:center}
  .btn-icon:hover{background:#f1f1f1; color:var(--ink); transform:none}
  .btn-icon.danger{background:none; color:#8e1f0b; border-radius:8px}
  .btn-icon.danger:hover{background:#8e1f0b; color:#fff}
  .btn-icon.danger svg.i{stroke:none}
  .btn-icon.danger[disabled], .btn-icon.danger[disabled]:hover{background:none; color:#b5b5b5}
  .btn-icon.recv{background:#e6f6ec; color:#0c5132}
  .btn-icon.recv:hover{background:#cdfee1}

  /* ================= CARDS / STATS ================= */
  .stats{display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:12px; margin-bottom:16px}
  .stat{background:var(--card); border:none; border-radius:var(--radius); padding:14px 16px; box-shadow:0 0 0 1px rgba(0,0,0,.06), var(--shadow); position:relative; overflow:hidden; transition:box-shadow .15s var(--ease-soft)}
  .stat::before{content:none}
  .stat:hover{transform:none; box-shadow:0 0 0 1px rgba(0,0,0,.10), var(--shadow-md)}
  .stat .lbl{font-size:12px; text-transform:none; letter-spacing:normal; color:var(--muted); font-weight:600}
  .stat .val{font-size:22px; font-weight:650; margin-top:6px; color:var(--ink); font-variant-numeric:tabular-nums; letter-spacing:-.2px}
  .stat .val small{font-size:12px; font-weight:550; color:var(--muted)}
  .stat .extra{font-size:12px; color:var(--muted); margin-top:4px}
  .stat .chip{position:absolute; right:12px; top:12px; width:30px; height:30px; border-radius:8px; display:flex; align-items:center; justify-content:center; font-size:15px; background:#f1f1f1; color:#4a4a4a; transition:.14s}
  .stat.clickable{cursor:pointer}
  .stat.clickable:hover .chip{background:#e3e3e3; color:#303030}
  .stat.clickable:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
  .stat .lbl{padding-right:36px}
  /* thin progress bar */
  .prog{height:4px; border-radius:99px; background:#e3e3e3; overflow:hidden; margin-top:9px}
  .prog span{display:block; height:100%; border-radius:99px; background:#303030; transition:width .5s var(--ease-soft)}
  .prog.green span{background:#29845a}
  .stat:hover .chip{transform:none}
  .stat.pink .val{color:var(--ink)}
  .stat.pink .chip{background:#f1f1f1}
  .stat.green .val{color:#0c5132}
  .stat.green .chip{background:#cdfee1}

  /* ================= PIPELINE ================= */
  .flow-card{background:var(--card); border:none; border-radius:var(--radius); padding:16px; box-shadow:0 0 0 1px rgba(0,0,0,.06), var(--shadow); margin-bottom:16px}
  .flow-card .t{font-size:13.5px; font-weight:650}
  .flow-card .s{font-size:12.5px; color:var(--muted); margin-top:2px}
  .flow{display:flex; align-items:stretch; gap:8px; flex-wrap:wrap; margin-top:14px}
  .flow .step{flex:1; min-width:108px; background:#fff; border:1px solid var(--line); border-radius:10px; padding:10px 12px; cursor:pointer; transition:.14s var(--ease-soft); position:relative}
  .flow .step:hover{border-color:#b5b5b5; transform:none; box-shadow:var(--shadow-md); background:#fafafa}
  .flow .step .n{font-size:18px; font-weight:650; color:var(--ink); font-variant-numeric:tabular-nums}
  .flow .step .t{font-size:12px; color:var(--muted); margin-top:2px; font-weight:550; letter-spacing:normal}
  .flow .step .ic{float:right; color:#8a8a8a; display:flex}
  .flow .step:hover .ic{color:#303030}
  .flow .arrow{display:flex; align-items:center; color:#c9c9c9; padding:0 2px; transform:rotate(-90deg)}
  @media(max-width:920px){.flow .arrow{display:none}}
  .modcell{display:inline-flex; align-items:center; gap:7px; color:var(--ink); font-weight:550}
  .modcell svg.i{color:#616161}

  /* ================= TABLES / INDEX ================= */
  .panel{background:var(--card); border:none; border-radius:var(--radius); box-shadow:0 0 0 1px rgba(0,0,0,.06), var(--shadow); overflow:hidden}

  .dpop{position:fixed; top:0; left:0; z-index:210; display:none; width:284px; padding:12px;
        background:#fff; border:1px solid var(--line); border-radius:12px; box-shadow:0 12px 32px rgba(0,0,0,.22)}
  .dpop.show{display:block; animation:drop .12s var(--ease-soft)}
  .dpop-head{display:flex; align-items:center; gap:6px; margin-bottom:8px}
  .dpop-title{flex:1; text-align:center; font-size:13.5px; font-weight:650; color:var(--ink)}
  .dpop-nav{width:28px; height:28px; border:none; border-radius:8px; background:transparent; color:var(--ink);
            display:flex; align-items:center; justify-content:center; transition:background .12s}
  .dpop-nav:hover{background:#f1f1f1}
  .dpop-next svg{transform:rotate(180deg)}
  .dpop-dow{display:grid; grid-template-columns:repeat(7,1fr); gap:2px; margin-bottom:2px}
  .dpop-dow span{text-align:center; font-size:11.5px; font-weight:650; color:var(--muted); padding:4px 0}
  .dpop-grid{display:grid; grid-template-columns:repeat(7,1fr); gap:2px}
  .dpc{height:34px; border:none; border-radius:8px; background:transparent; font-size:13px; font-weight:550;
       color:var(--ink); display:flex; align-items:center; justify-content:center; transition:background .1s}
  .dpc:hover{background:#f1f1f1}
  .dpc-mute{pointer-events:none}
  .dpc-today{box-shadow:inset 0 0 0 1px var(--line); font-weight:700}
  .dpc-sel{background:#303030; color:#fff; font-weight:700}
  .dpc-sel:hover{background:#1a1a1a}
  .dpop-foot{display:flex; justify-content:space-between; gap:8px; margin-top:10px; padding-top:10px; border-top:1px solid var(--line)}

  /* ===== Shopify-style report date range ===== */
  .rdp-trig{display:inline-flex; align-items:center; gap:8px; height:32px; padding:0 10px 0 8px; border:none; border-radius:8px;
            background:#fff; box-shadow:var(--btn-shadow); font-size:13px; font-weight:650; color:var(--ink)}
  .rdp-trig:hover{background:#f7f7f7}
  .rdp-trig .rdp-calic{display:inline-flex; color:#616161}
  .rdp-chip{display:inline-flex; align-items:center; height:32px; padding:0 12px; border-radius:8px; background:#fff;
            box-shadow:var(--btn-shadow); font-size:13px; font-weight:550; color:var(--ink)}
  .rdp-pop{position:fixed; z-index:220; display:none; background:#fff; border:1px solid var(--line); border-radius:14px;
           box-shadow:0 18px 48px rgba(0,0,0,.22); overflow:hidden; min-width:min(760px,94vw)}
  .rdp-pop.show{display:flex; animation:drop .12s var(--ease-soft)}
  .rdp-side{width:168px; flex:none; padding:8px; border-right:1px solid var(--line); background:#fafafa}
  .rdp-si{display:block; width:100%; text-align:left; border:none; background:transparent; border-radius:8px;
          padding:8px 10px; font-size:13px; font-weight:550; color:var(--ink); margin-bottom:1px}
  .rdp-si:hover{background:#f1f1f1}
  .rdp-si.on{background:#e3e3e3; font-weight:650; box-shadow:var(--btn-shadow)}
  .rdp-main{flex:1; min-width:0; display:flex; flex-direction:column; background:#fff}
  .rdp-dates{display:flex; align-items:center; gap:10px; padding:12px 16px 6px}
  .rdp-date{flex:1; min-width:0}
  .rdp-date .inp{cursor:text; background:#fff}
  .rdp-date.on .inp{border-color:var(--accent); box-shadow:0 0 0 2px var(--accent)}
  .rdp-arrow{color:var(--muted); flex:none; font-size:16px; font-weight:650}
  .rdp-lastbar{display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding:12px 16px 4px}
  .rdp-lastbar .inp{width:auto; min-width:72px}
  .rdp-n{width:64px!important; min-width:64px!important; text-align:center}
  .rdp-check{display:inline-flex; align-items:center; gap:6px; font-size:13px; font-weight:550; margin-left:8px; cursor:pointer}
  .rdp-pills{display:flex; flex-wrap:wrap; gap:6px}
  .rdp-pill{height:28px; padding:0 10px; border:none; border-radius:8px; background:#f1f1f1; font-size:12.5px; font-weight:600}
  .rdp-pill.on{background:#303030; color:#fff}
  .rdp-cals{display:flex; gap:8px; padding:8px 16px 12px; flex-wrap:wrap}
  .rdp-cal{width:280px; min-width:260px; flex:1}
  .rdp-cal-h{display:flex; align-items:center; justify-content:center; gap:6px; margin-bottom:6px; font-size:13.5px; font-weight:650}
  .rdp-cal-h .dpop-nav{position:static}
  .rdp-hint{font-size:12.5px; color:var(--muted); padding:4px 16px 8px}
  .rdp-foot{display:flex; align-items:center; justify-content:space-between; gap:10px; padding:10px 16px;
            border-top:1px solid var(--line); background:#fff}
  .rdp-foot .rdp-sum{font-size:13px; font-weight:550; color:var(--muted)}
  .dpc-in{background:#f1f1f1}
  .dpc-in.dpc-sel, .dpc-sel.dpc-in{background:#303030; color:#fff}
  @media(max-width:820px){
    .rdp-pop.show{flex-direction:column; max-height:86vh; overflow:auto}
    .rdp-side{width:auto; border-right:none; border-bottom:1px solid var(--line); display:flex; flex-wrap:wrap; gap:4px}
    .rdp-si{width:auto}
  }

  /* ===== Masters: tabbed layout ===== */
  .masters-wrap{display:grid; grid-template-columns:232px minmax(0,1fr); gap:16px; align-items:start}
  .mnav{background:#fff; border-radius:12px; box-shadow:0 0 0 1px rgba(0,0,0,.06), var(--shadow); padding:8px; position:sticky; top:16px}
  .mnav-sec{font-size:11px; font-weight:650; color:var(--muted); text-transform:uppercase; letter-spacing:.4px; padding:12px 8px 4px}
  .mnav-sec:first-child{padding-top:4px}
  .mnav-item{display:flex; align-items:center; gap:9px; width:100%; border:none; background:none; text-align:left;
             padding:7px 8px; border-radius:8px; font-size:13px; font-weight:550; color:var(--ink); min-height:32px; transition:background .12s}
  .mnav-item:hover{background:#f1f1f1}
  .mnav-item.active{background:#f1f1f1; font-weight:650; box-shadow:var(--btn-shadow)}
  .mnav-item .mn-ic{width:20px; height:20px; display:flex; align-items:center; justify-content:center; color:#616161; flex:none}
  .mnav-item.active .mn-ic{color:#303030}
  .mnav-item .mn-l{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .mnav-item .cnt{font-size:11.5px; font-weight:650; color:var(--muted); background:#ebebeb; border-radius:6px; padding:0 6px; min-width:20px; text-align:center}
  .mnav-item.active .cnt{background:#fff; color:var(--ink)}
  .mpanel{padding:18px 20px 20px}
  .mhead{display:flex; align-items:flex-start; gap:12px; margin-bottom:14px}
  .mhead h3{font-size:15px; font-weight:650; display:flex; align-items:center; gap:8px}
  .mhead h3 .c{font-size:12px; font-weight:650; color:var(--muted); background:#f1f1f1; border-radius:8px; padding:1px 8px}
  .mhead p{font-size:12.5px; color:var(--muted); margin-top:3px; line-height:1.5}
  .mtools{display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-bottom:12px}
  .mtools .add-row .inp{min-width:160px}
  .msearch{max-width:260px; flex:0 1 260px}
  .mlist{max-height:none; border-radius:10px}
  .mlist .master-item{padding:10px 12px}
  #inward-items tr.row-over td{background:#fff5f4}
  #inward-items tr.row-over input[data-f="recQty"]{border-color:#8e1f0b; box-shadow:0 0 0 2px rgba(142,31,11,.18)}
  #inward-items .tdr{color:var(--ink)}
  .segbar{display:inline-flex; gap:4px; padding:3px; background:#f1f1f1; border-radius:10px}
  .seg{display:inline-flex; align-items:center; gap:6px; border:none; background:transparent; border-radius:8px;
       padding:6px 14px; font-size:13px; font-weight:600; color:var(--muted); transition:.12s}
  .seg:hover{color:var(--ink)}
  .seg.on{background:#fff; color:var(--ink); box-shadow:var(--btn-shadow)}
  .seg.locked{cursor:not-allowed; opacity:.55}
  .seg.locked:hover{color:var(--muted)}
  .seg.on.locked{opacity:1; color:var(--ink)}
  .seg svg.i{color:currentColor}
  #jw-issues td:nth-child(3), #jw-extras td:nth-child(3){min-width:130px}
  #jw-issues td:nth-child(4), #jw-extras td:nth-child(4){min-width:170px}
  #jw-issues td:nth-child(5), #jw-extras td:nth-child(5){min-width:150px}
  #jw-issues td:nth-child(3), #jw-extras td:nth-child(3){min-width:150px}
  #jw-issues .po-ref-cell, #jw-extras .po-ref-cell{min-width:120px}
  #jw-issues .uom-cell, #jw-extras .uom-cell{min-width:0; width:74px}
  #jw-issues .pod-cell, #jw-extras .pod-cell{min-width:150px}
  #jw-issues .uom-cell .inp, #jw-extras .uom-cell .inp{width:74px; text-align:center; padding-left:6px; padding-right:6px}
  #inward-items td:nth-child(2), #pr-items td:nth-child(2){min-width:140px}
  #inward-items td:nth-child(3), #pr-items td:nth-child(3){min-width:210px}
  #pr-items td.srcol, #pr-table th.srcol{text-align:center; width:52px}
  #pr-items td{vertical-align:middle}
  #pr-items td.num{font-variant-numeric:tabular-nums}
  #pr-items td:nth-child(6) .inp.num{width:96px; min-width:88px}
  #pr-items td:nth-child(8) .inp.num{width:96px; min-width:88px}
  .cellhint{font-size:10.5px; line-height:1.3; margin-top:3px; min-height:12px; color:var(--muted)}
  .cellhint.ok{color:#0c5132}
  .cellhint.bad{color:#8e1f0b}
  .inp.inp-bad{border-color:#8e1f0b; box-shadow:0 0 0 2px rgba(142,31,11,.15)}
  /* required-field highlight applied on save */
  .cbox.cbox-bad .cbox-input, .mcbox.cbox-bad .mcbox-input{border-color:#8e1f0b; box-shadow:0 0 0 2px rgba(142,31,11,.15)}
  .field.field-bad > .lbl, .field.field-bad > label.lbl{color:#8e1f0b; font-weight:650}
  .field.field-bad > .lbl::after{content:' \2022'; font-weight:800; font-size:15px; line-height:0; vertical-align:middle}
  td.cell-bad{background:#fff5f4}
  .req-flash{animation:reqFlash .5s var(--ease-soft) 1}
  @keyframes reqFlash{0%{box-shadow:0 0 0 0 rgba(142,31,11,.45)}70%{box-shadow:0 0 0 6px rgba(142,31,11,0)}100%{box-shadow:0 0 0 0 rgba(142,31,11,0)}}
  #pt-billtos td{min-width:130px; vertical-align:top}
  #pt-billtos .bt-addr{min-width:210px}
  #pt-billtos td:nth-child(2){min-width:170px}
  #pt-billtos td:last-child{min-width:0}
  .intot-wrap{max-width:420px; margin-left:auto}
  .inward-bottom-grid{display:flex;flex-direction:column;gap:20px;width:100%;margin-top:24px}
  .inward-bottom-grid.single{display:block;width:100%;margin-top:24px}
  .inward-bottom-grid .sec{margin-top:0}
  .inward-attachments-panel{width:100%;min-width:0;margin-top:0}
  .inward-attachments-panel .table-wrap{width:100%;max-width:100%;overflow-x:auto}
  .inward-attachments-panel table.mini-table{width:100%;min-width:600px;table-layout:auto}
  .inward-attachments-panel table.mini-table th:first-child,
  .inward-attachments-panel table.mini-table td:first-child{width:48px;min-width:48px;text-align:center}
  .inward-attachments-panel table.mini-table th:nth-child(2),
  .inward-attachments-panel table.mini-table td:nth-child(2){width:auto;min-width:220px}
  .inward-attachments-panel table.mini-table td:nth-child(2) .inp{width:100%}
  .inward-attachments-panel table.mini-table th:nth-child(3),
  .inward-attachments-panel table.mini-table td:nth-child(3){width:210px;min-width:190px}
  .inward-attachments-panel table.mini-table th:nth-child(4),
  .inward-attachments-panel table.mini-table td:nth-child(4){width:110px;min-width:100px;text-align:center}
  .inward-attachments-panel table.mini-table th:last-child,
  .inward-attachments-panel table.mini-table td:last-child{width:90px;min-width:90px;text-align:right}
  .inward-summary-row{width:100%;display:flex;justify-content:flex-end}
  .inward-summary-panel{width:100%;max-width:420px;min-width:0;margin-top:0}
  .inward-summary-panel .intot-wrap{width:100%;max-width:420px;margin-left:auto}
  @media(max-width:1050px){.inward-bottom-grid,.inward-bottom-grid.single{width:100%}.inward-attachments-panel,.inward-summary-panel{width:100%}}
  .grid-even5{display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:0 14px}
  .grid-even5 .inp, .grid-even5 .inp.num{width:100%}
  @media(max-width:1100px){.grid-even5{grid-template-columns:repeat(3,minmax(0,1fr))}}
  @media(max-width:760px){.grid-even5{grid-template-columns:1fr}}
  .intot{width:100%; border-collapse:collapse; border:1px solid var(--line); border-radius:10px; overflow:hidden; min-width:0}
  .intot th{background:#fff; text-align:left; font-size:12.5px; font-weight:600; color:var(--ink);
            padding:9px 14px; border-bottom:1px solid var(--line2); text-transform:none; letter-spacing:normal; position:static}
  .intot td{text-align:right; font-size:12.5px; padding:9px 14px; border-bottom:1px solid var(--line2); font-variant-numeric:tabular-nums}
  .intot tr:last-child th, .intot tr:last-child td{border-bottom:none}
  .intot tr.grand th, .intot tr.grand td{font-weight:700; background:#f7f7f7}
  .att-cell{position:relative;min-width:180px}.att-upload-box{width:164px;min-height:108px;margin:0}.att-preview-cell{width:90px;white-space:nowrap}
  .att-none{font-size:12.5px; color:var(--muted)}
  .att-has{display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:550; color:#0c5132}
  .att-file{display:block; margin-top:6px; font-size:12px; max-width:240px}
  #inward-items .tdr .sub-line{font-weight:550}
  .master-item .inuse{display:inline-block; margin-left:8px; font-size:11px; font-weight:600; color:var(--muted);
                      background:#f1f1f1; border-radius:6px; padding:1px 7px; vertical-align:middle}
  .master-item.mi-locked{background:#fcfcfc}
  .btn-icon[disabled]{opacity:.38; cursor:not-allowed}
  .btn-icon[disabled]:hover{background:none; color:var(--muted)}
  @media(max-width:920px){
    .masters-wrap{grid-template-columns:1fr}
    .mnav{position:static; display:flex; flex-wrap:wrap; gap:4px}
    .mnav-sec{width:100%; padding:6px 8px 2px}
    .mnav-item{width:auto}
    .mnav-item .cnt{display:none}
  }

  /* ===== index tabs (saved views) ===== */
  .tabs{display:flex; gap:2px; padding:6px 8px; border-bottom:1px solid var(--line); overflow-x:auto; scrollbar-width:none; background:#fff}
  .tabs::-webkit-scrollbar{display:none}
  .tab{border:none; background:none; border-radius:8px; padding:5px 10px; font-size:13px; font-weight:550; color:var(--muted); white-space:nowrap; display:inline-flex; align-items:center; gap:6px; transition:background .12s, color .12s; position:relative}
  .tab:hover{background:#f1f1f1; color:var(--ink)}
  .tab.active{background:#f1f1f1; color:var(--ink); font-weight:650; box-shadow:var(--btn-shadow)}
  .tab .tc{font-size:11.5px; font-weight:600; color:var(--muted); background:#ebebeb; border-radius:6px; padding:0 5px; min-width:18px; text-align:center}
  .tab.active .tc{background:#e3e3e3; color:#303030}

  .rowcount{font-size:12.5px; color:var(--muted); font-weight:550; margin-left:auto; white-space:nowrap}
  .pager{display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:10px 12px; border-top:1px solid var(--line); background:#fff}
  .pager-nav{display:flex; align-items:center; gap:4px; margin-left:auto}
  .pager-num{min-width:28px; height:28px; border:none; background:transparent; border-radius:8px; font-weight:600; font-size:13px; color:var(--ink)}
  .pager-num:hover{background:#f1f1f1}
  .pager-num.on{background:#f1f1f1; box-shadow:var(--btn-shadow)}
  .pager-gap{color:var(--muted); padding:0 4px; font-size:13px}
  .pager .inp{width:auto; min-width:108px}
  .pager .btn[disabled]{opacity:.38; cursor:not-allowed}

  /* ===== bulk action bar ===== */
  .bulkbar{display:flex; align-items:center; gap:10px; padding:8px 12px; border-bottom:1px solid var(--line); background:#f1f1f1; font-size:13px; animation:slideDown .14s var(--ease-soft)}
  .bulkbar .bsp{flex:1}
  .bulkbar .cbox{min-width:150px}
  @keyframes slideDown{from{opacity:0; transform:translateY(-4px)}to{opacity:1; transform:none}}

  /* ===== checkbox ===== */
  .cbx{display:inline-flex; align-items:center; cursor:pointer}
  .cbx input{appearance:none; -webkit-appearance:none; width:17px; height:17px; border-radius:5px; border:1.5px solid #8a8a8a; background:#fff; cursor:pointer; position:relative; transition:.12s; margin:0}
  .cbx input:hover{border-color:#616161}
  .cbx input:checked{background:#303030; border-color:#303030}
  .cbx input:checked::after{content:""; position:absolute; left:5px; top:1.5px; width:4px; height:9px; border:solid #fff; border-width:0 2px 2px 0; transform:rotate(42deg)}
  .cbx input:focus-visible{outline:2px solid var(--accent); outline-offset:1px}

  .filter-bar{display:flex; gap:8px; padding:10px 12px; border-bottom:1px solid var(--line); flex-wrap:wrap; align-items:center; background:#fff}
  .filter-bar .cbox{min-width:150px}
  .search{flex:1; min-width:200px; position:relative}
  .search .inp{width:100%; padding-left:34px; background:#fff}
  .search::before{content:none}
  .search > svg.i{position:absolute; left:10px; top:50%; transform:translateY(-50%); color:var(--muted); pointer-events:none; z-index:1}
  .table-wrap{overflow-x:auto}
  table{width:100%; border-collapse:collapse; min-width:780px}
  th{background:#f7f7f7; font-size:12px; text-transform:none; letter-spacing:normal; color:var(--muted); text-align:left; padding:8px 12px; border-bottom:1px solid var(--line); white-space:nowrap; font-weight:650; position:sticky; top:0; z-index:2}
  .list-panel{display:flex; flex-direction:column; max-height:calc(100vh - 158px)}
  .list-panel .index-stick{flex:none; background:#fff; z-index:6}
  .list-panel .table-wrap{flex:1 1 auto; min-height:0; overflow:auto}
  .list-panel .table-wrap th{position:sticky; top:0; z-index:4; background:#f7f7f7; box-shadow:0 1px 0 var(--line)}
  .list-panel .pager{flex:none}
  .mpanel{display:flex; flex-direction:column; max-height:calc(100vh - 158px); overflow:hidden}
  .mpanel.menu-editor-host,.mpanel.user-editor-host{max-height:calc(100vh - 120px);overflow:auto;display:block}
  .mpanel .mhead, .mpanel .mtools{flex:none; background:#fff}
  .mpanel .mlist, .mpanel .master-list{flex:1 1 auto; min-height:120px; overflow:auto; max-height:none}
  .mpanel .pager{flex:none}
  .mnav{max-height:calc(100vh - 158px); overflow:auto}
  #im-list .table-wrap, #pt-list .table-wrap, #rep-body .table-wrap{
    max-height:min(62vh, calc(100vh - 260px)); overflow:auto}
  #im-list th, #pt-list th, #rep-body th{position:sticky; top:0; z-index:3; background:#f7f7f7; box-shadow:0 1px 0 var(--line)}
  td{padding:10px 12px; border-bottom:1px solid var(--line2); vertical-align:middle; font-size:13px}
  .xfilter-row th{padding:5px 6px;background:#fff;position:sticky;top:33px;z-index:3}
  .xfilter-row input{width:100%;min-width:64px;height:26px;border:1px solid #c9c9c9;border-radius:5px;padding:3px 7px;font:inherit;font-size:11.5px;background:#fff;color:var(--ink);outline:none}
  .xfilter-row input:focus{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
  .xfilter-row input::placeholder{color:#9a9a9a}
  th.sortable{cursor:pointer; user-select:none; transition:background .1s, color .1s}
  th.sortable:hover{background:#f1f1f1; color:var(--ink)}
  th.sortable.on{color:var(--ink)}
  th .thin{display:inline-flex; align-items:center; gap:4px}
  th.num .thin{justify-content:flex-end; width:100%}
  th .sarr{display:inline-flex; color:var(--ink)}
  th.selcol, td.selcol{width:38px; padding-right:0; padding-left:14px}
  tbody tr{transition:background .1s var(--ease-soft); cursor:pointer}
  tbody tr:hover td{background:#fafafa}
  tbody tr.sel td{background:#f1f6fd}
  tbody tr.sel:hover td{background:#e8f1fc}
  tbody tr:focus-visible{outline:2px solid var(--accent); outline-offset:-2px}
  td .badge{vertical-align:middle}
  tbody tr:last-child td{border-bottom:none}
  td.num, th.num{text-align:right; font-variant-numeric:tabular-nums}
  th.srcol, td.srcol{width:52px; min-width:46px; max-width:64px; text-align:center; font-variant-numeric:tabular-nums; color:var(--muted); font-weight:650; white-space:nowrap; padding-left:10px; padding-right:6px}
  .master-item .mi-sr{flex:0 0 32px; width:32px; color:var(--muted); font-weight:650; font-variant-numeric:tabular-nums; font-size:12.5px}
  .master-item .mi-id{flex:0 0 94px!important;width:94px;min-width:94px!important;color:var(--muted);font-weight:650;font-size:12px;white-space:nowrap}
  .master-status-cell{flex:0 0 130px!important;display:flex;align-items:center;gap:8px}.master-status-sw{flex:none}.master-status-label{font-size:11.5px;font-weight:650}.master-status-label.on{color:#0c5132}.master-status-label.off{color:#8e1f0b}
  .master-list-head{display:grid;grid-template-columns:32px 94px minmax(0,1fr) 130px 72px;gap:8px;padding:7px 12px;background:#f7f7f7;border:1px solid var(--line);border-bottom:none;border-radius:10px 10px 0 0;font-size:11.5px;font-weight:650;color:var(--muted)}
  .master-list-head + .master-list{border-radius:0 0 10px 10px}
  .sub-line{font-size:12px; color:var(--muted); margin-top:1px}
  .actions-cell{white-space:nowrap; text-align:right}
  .empty{text-align:center; padding:56px 24px; color:var(--muted); display:flex; flex-direction:column; align-items:center}
  .empty .big{width:56px; height:56px; border-radius:14px; background:#f1f1f1; color:#616161; display:flex; align-items:center; justify-content:center; margin-bottom:14px; filter:none; opacity:1}
  .empty h3{font-size:14.5px; font-weight:650; color:var(--ink); margin-bottom:5px}
  .empty p{margin-bottom:16px; font-size:13px; max-width:420px; line-height:1.5}

  /* ================= INPUTS ================= */
  .inp{background:#fff; border:1px solid #8a8a8a; border-top-color:#5c5c5c; border-radius:8px; padding:6px 12px; min-height:32px; outline:none; transition:box-shadow .12s var(--ease-soft), border-color .12s; width:100%}
  .inp:hover{border-color:#616161}
  .inp:focus{border-color:var(--accent); box-shadow:0 0 0 2px var(--accent)}
  .inp::placeholder{color:#8a8a8a}
  /* ===== date field + calendar popover ===== */
  .mini-table td .inp[data-date]{min-width:132px}
  .inp[data-date]{padding-left:34px; cursor:pointer; background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAyMCAyMCcgZmlsbD0nbm9uZScgc3Ryb2tlPScjNjE2MTYxJyBzdHJva2Utd2lkdGg9JzEuNScgc3Ryb2tlLWxpbmVjYXA9J3JvdW5kJyBzdHJva2UtbGluZWpvaW49J3JvdW5kJz48cmVjdCB4PSczLjI1JyB5PSc0Ljc1JyB3aWR0aD0nMTMuNScgaGVpZ2h0PScxMicgcng9JzInLz48cGF0aCBkPSdNMy4yNSA4LjI1aDEzLjVNNyAzLjV2Mi41TTEzIDMuNXYyLjUnLz48L3N2Zz4=); background-repeat:no-repeat; background-position:10px center; background-size:16px 16px}
  .inp[data-date]:disabled{cursor:not-allowed}

  .inp-sm{padding:4px 10px; min-height:28px; font-size:12.5px; width:auto}
  .inp.num{width:96px}
  .lbl{display:block; font-size:12.5px; font-weight:550; color:var(--ink); text-transform:none; letter-spacing:normal; margin:0 0 4px}
  .field{margin-bottom:14px}
  .grid2{display:grid; grid-template-columns:1fr 1fr; gap:0 14px}
  .grid3{display:grid; grid-template-columns:1fr 1fr 1fr; gap:0 14px}
  .grid-auto{display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:0 14px}
  /* fixed-width row: fields keep a sane width instead of stretching edge to edge */
  /* inline validation hints under a field */
  .fhint{font-size:11.5px; margin-top:4px; min-height:14px; color:var(--muted)}
  .fhint.ok{color:#0c5132}
  .fhint.bad{color:#8e1f0b}
  /* hide native spinners — the app validates input itself */
  input[type=number]::-webkit-outer-spin-button,
  input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none; margin:0}
  input[type=number]{-moz-appearance:textfield; appearance:textfield}
  .grid-fixed{display:grid; grid-template-columns:repeat(auto-fill,240px); gap:0 14px; justify-content:start}
  .grid-fixed.wide-last > .field:last-child{grid-column:span 2}
  .grid-fixed.in-jwr > .inj-no{order:1}
  .grid-fixed.in-jwr > .inj-date{order:2}
  .grid-fixed.in-jwr > .inj-jw{order:3}
  .grid-fixed.in-jwr > .inj-ven{order:4}
  .grid-fixed.in-jwr > .inj-wh{order:5}
  .grid-fixed.in-jwr > .inj-ch{order:6}
  .grid-fixed.in-jwr > .inj-by{order:7}
  tr.jw-dgrp{cursor:default}
  tr.jw-dgrp td, tbody tr.jw-dgrp:hover td{background:#f1f1f1; font-weight:650; font-size:12.5px; color:var(--ink); padding:7px 12px; border-bottom:1px solid var(--line)}
  tr.jw-dgrp .jw-dgrp-cell{display:flex; align-items:center; gap:12px; flex-wrap:wrap}
  tr.jw-dgrp .jw-dgrp-n{margin-left:auto; font-weight:550; color:var(--muted); font-size:12px}
  tr.jw-dgrp .jw-dgrp-dc{background:#fff; border-radius:8px; padding:4px 10px; box-shadow:var(--btn-shadow); font-size:12.5px; font-weight:550}
  tr.jw-dgrp .jw-dgrp-dc b{font-weight:700}
  tr.jw-dgrp .jw-dgrp-pod{min-width:160px}
  tr.jw-dgrp .pod-file{max-width:150px}
  /* Date/DC group is displayed inside the item rows instead of consuming a
     separate full-width row. It spans every line belonging to that DC. */
  td.jw-merged-dispatch{min-width:164px; width:164px; padding:10px 12px; vertical-align:middle; text-align:center;
    background:#f7f7f7; border-right:1px solid var(--line); border-bottom:1px solid var(--line)}
  tbody tr:hover td.jw-merged-dispatch{background:#f7f7f7}
  .jw-merge-date{display:flex; align-items:center; justify-content:center; gap:7px; font-size:12.5px; white-space:nowrap}
  .jw-merge-date svg{color:var(--muted)}
  .jw-merge-dc{display:inline-flex; align-items:center; justify-content:center; margin-top:9px; padding:4px 8px;
    border-radius:8px; background:#fff; box-shadow:var(--btn-shadow); font-size:12px; white-space:nowrap}
  .jw-merge-lines{margin-top:10px; text-align:center; font-size:11.5px; color:var(--muted); white-space:nowrap}
  .dc-select-summary{display:flex; gap:10px; flex-wrap:wrap; margin-bottom:14px}
  .dc-select-summary .stock-chip{margin:0}
  .dc-pod-status{display:inline-flex; align-items:center; gap:5px; font-size:12px; font-weight:600}
  .dc-pod-status.ok{color:#0c5132}.dc-pod-status.miss{color:#8a6116}
  .audit-change{font-size:11.5px;line-height:1.4;padding:2px 0;border-bottom:1px dotted #e3e3e3;max-width:560px;white-space:normal;word-break:break-word}.audit-change:last-child{border-bottom:none}
  .audit-note{min-width:260px;max-width:420px;white-space:normal;line-height:1.45;background:#fafafa;color:#303030}
  .sf-builder{background:#eef2f6;border:1px solid #cfd8e3;border-radius:12px;overflow:hidden;min-height:calc(100vh - 180px)}.sf-builder-top{height:58px;background:#fff;border-bottom:1px solid #cfd8e3;display:flex;align-items:center;gap:8px;padding:0 12px}.sf-builder-title{min-width:0;flex:1}.sf-builder-title b{display:block;font-size:14px}.sf-builder-title span{font-size:11px;color:#65758b}.sf-builder-title input{border:none;background:transparent;font-weight:700;font-size:14px;width:min(280px,100%);outline:none;padding:2px 0}.sf-builder-title input:focus{border-bottom:1px solid #0176d3}.sf-builder-body{display:grid;grid-template-columns:310px minmax(0,1fr);min-height:calc(100vh - 238px)}.sf-builder-side{background:#fff;border-right:1px solid #cfd8e3;overflow:auto;max-height:calc(100vh - 180px)}.sf-builder-tabs{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid #d8e0e8}.sf-builder-tabs button{height:42px;border:none;background:#fff;font-weight:650;color:#526174;border-bottom:3px solid transparent}.sf-builder-tabs button.on{color:#0176d3;border-bottom-color:#0176d3}.sf-side-body{padding:12px}.sf-side-section{margin-top:14px}.sf-side-section h4{font-size:11px;color:#526174;text-transform:uppercase;letter-spacing:.45px;margin:0 0 7px;display:flex;justify-content:space-between}.sf-drop-zone{border:1px dashed #9eafc1;background:#f8fafc;border-radius:7px;min-height:42px;padding:6px}.sf-chip{display:flex;align-items:center;gap:5px;background:#eaf5fe;color:#014486;border:1px solid #b8d8f2;border-radius:5px;padding:5px 7px;margin:3px;font-size:11.5px}.sf-chip span{flex:1}.sf-chip button{border:none;background:transparent;color:#526174;padding:0 2px}.sf-fields{max-height:260px;overflow:auto;border:1px solid #e0e6ed;border-radius:7px}.sf-field{display:flex;align-items:center;gap:7px;padding:7px 9px;border-bottom:1px solid #eef1f4;font-size:11.5px}.sf-field:last-child{border-bottom:none}.sf-field:hover{background:#f3f8fc}.sf-report-canvas{min-width:0;padding:12px;overflow:auto}.sf-preview-head{display:flex;align-items:center;gap:8px;background:#fff;border:1px solid #d5dde6;border-bottom:none;border-radius:8px 8px 0 0;padding:9px 11px}.sf-preview-head b{flex:1}.sf-preview-head span{font-size:11px;color:#65758b}.sf-report-table{background:#fff;border:1px solid #d5dde6;border-radius:0 0 8px 8px;overflow:auto;max-height:calc(100vh - 340px)}.sf-report-table table{min-width:900px}.sf-report-table th{background:#f4f6f9;border-right:1px solid #d8e0e8}.sf-report-table td{border-right:1px solid #edf0f3}.sf-filter-card{border:1px solid #d8e0e8;border-radius:7px;padding:8px;margin-bottom:7px;background:#fff}.sf-filter-card b{font-size:11.5px}.sf-filter-card div{font-size:10.5px;color:#65758b;margin-top:2px}.sf-builder .btn-brand{background:#0176d3;color:#fff;box-shadow:none}.sf-builder .btn-brand:hover{background:#0b5cab}.sf-format-pills{display:flex;background:#eef2f6;border-radius:7px;padding:3px}.sf-format-pills button{border:none;background:transparent;border-radius:5px;padding:5px 8px;font-size:11px}.sf-format-pills button.on{background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.15)}.sf-unsaved{color:#b85d00;font-size:10.5px}.sf-proto-badge{background:#ede9fe;color:#6d28d9;border-radius:5px;padding:3px 7px;font-size:10px;font-weight:700}.sf-builder-chart{background:#fff;border:1px solid #d5dde6;border-bottom:none;padding:10px;border-radius:8px 8px 0 0}@media(max-width:980px){.sf-builder-body{grid-template-columns:1fr}.sf-builder-side{max-height:none;border-right:none;border-bottom:1px solid #cfd8e3}.sf-report-canvas{padding:8px}}
  .reporting-shell{display:grid;grid-template-columns:300px minmax(0,1fr);gap:14px;align-items:start}.reporting-main{min-width:0;grid-column:2}.reporting-customize{grid-column:1;grid-row:1}.reporting-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 12px;background:#fff;border-bottom:1px solid var(--line)}.reporting-toolbar .report-name{min-width:190px;max-width:280px}.reporting-toolbar .inp{width:auto}.reporting-customize{background:#fff;border-radius:12px;box-shadow:0 0 0 1px rgba(0,0,0,.06);position:sticky;top:72px;max-height:calc(100vh - 100px);overflow:auto}.reporting-customize h3{font-size:14px;padding:14px;border-bottom:1px solid var(--line)}.pivot-section{padding:10px 12px;border-bottom:1px solid var(--line)}.pivot-section h4{font-size:11.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);margin:0 0 8px}.pivot-option{display:flex;align-items:center;gap:8px;padding:6px 4px;font-size:12.5px;cursor:pointer}.pivot-option input{width:16px;height:16px}.pivot-table th{position:sticky;top:0}.pivot-table td.pivot-break{font-weight:600}.pivot-indent{display:inline-block}.pivot-total td{font-weight:700;background:#f1f6fd}.pivot-head{display:flex;align-items:center;gap:5px;justify-content:space-between}.pivot-menu{border:none;background:transparent;color:var(--muted);width:20px;height:20px;border-radius:4px}.pivot-menu:hover{background:#e3e3e3;color:var(--ink)}.pivot-empty{padding:55px 20px;text-align:center;color:var(--muted)}.saved-report-bar{display:flex;gap:6px;align-items:center}.pivot-filter-chip{display:inline-flex;align-items:center;gap:4px;background:#e8f1fc;color:#00527c;border-radius:6px;padding:2px 7px;font-size:11px;margin:2px}.reporting-summary{display:flex;gap:8px;padding:8px 12px;border-bottom:1px solid var(--line);flex-wrap:wrap}.reporting-summary span{font-size:11.5px;color:var(--muted)}.sf-report-home-head{display:flex;align-items:center;gap:12px;margin-bottom:14px}.sf-report-home-head>div{flex:1}.report-type-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.report-type-card{border:1px solid var(--line);border-radius:10px;background:#fff;padding:16px;text-align:left;min-height:120px}.report-type-card:hover{border-color:#8aa7c4;box-shadow:0 4px 14px rgba(0,82,124,.1)}.report-type-card b{display:block;font-size:14px;margin-bottom:5px}.report-type-card span{font-size:12px;color:var(--muted)}.pivot-chart{padding:14px;border-bottom:1px solid var(--line);background:#fff}.pivot-chart h4{font-size:12px;margin-bottom:10px}.pivot-bars{display:flex;flex-direction:column;gap:7px}.pivot-bar-row{display:grid;grid-template-columns:150px 1fr 90px;gap:8px;align-items:center;font-size:11.5px}.pivot-bar-track{height:18px;background:#eef2f6;border-radius:4px;overflow:hidden}.pivot-bar-fill{height:100%;background:#0b74b5;border-radius:4px}.pivot-donut-wrap{display:flex;align-items:center;gap:20px}.pivot-donut{width:150px;height:150px;border-radius:50%;position:relative}.pivot-donut::after{content:"";position:absolute;inset:34px;border-radius:50%;background:#fff}.pivot-legend{display:grid;gap:5px;font-size:11.5px}.pivot-legend i{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:6px}@media(max-width:1050px){.reporting-shell{grid-template-columns:1fr}.reporting-main,.reporting-customize{grid-column:1;grid-row:auto}.reporting-customize{position:static;max-height:none}.report-type-grid{grid-template-columns:1fr 1fr}}
  .auth-login{position:fixed;inset:0;z-index:999;background:#fff;display:grid;grid-template-columns:minmax(0,2.8fr) minmax(450px,1fr);overflow:hidden}
  .auth-visual{position:relative;min-width:0;margin:28px 28px 48px;background:#eef1f4;border-radius:10px;overflow:visible;box-shadow:0 12px 34px rgba(18,55,91,.13)}
  .auth-visual>img{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:cover;object-position:center;border-radius:10px;transition:opacity .35s ease}
  .auth-slide-dots{position:absolute;left:50%;bottom:-31px;z-index:2;transform:translateX(-50%);display:flex;gap:8px;padding:6px 9px;border-radius:99px;background:#d9e0e8;box-shadow:0 2px 8px rgba(18,55,91,.14)}.auth-slide-dots:empty{display:none}.auth-slide-dots button{width:8px;height:8px;border:none;border-radius:50%;background:#9aa8b7;padding:0}.auth-slide-dots button.on{width:22px;border-radius:8px;background:#fff;box-shadow:0 0 0 1px #9aa8b7 inset}
  .auth-side{background:#fff;display:flex;align-items:center;justify-content:center;padding:36px 26px;border-left:1px solid #e7edf3}
  .auth-login-card{width:min(430px,100%);background:#fff;border:none;border-radius:16px;box-shadow:0 20px 55px rgba(18,55,91,.12);padding:38px}
  .auth-login-card .login-eyebrow{display:inline-flex;align-items:center;height:26px;padding:0 10px;border-radius:6px;background:#eef5fb;color:#00527c;font-size:10.5px;font-weight:750;letter-spacing:.8px;text-transform:uppercase;margin-bottom:17px}
  .auth-login-card h2{font-size:26px;line-height:1.2;margin:0 0 6px;color:#17212b}.auth-login-card>p{color:#6b7280;margin:0 0 24px}.auth-login-card .field{margin-bottom:16px}.auth-login-card .inp{min-height:40px;border-color:#c8d0d9}.auth-login-card .trial{font-size:11.5px;color:#4b5563;background:#f8fafc;border:1px solid #e5eaf0;border-radius:8px;padding:8px 10px;margin-bottom:18px;text-align:left}.auth-login-card .login-submit{min-height:42px;background:#086da8}.auth-login-card .login-submit:hover{background:#005985}
  @media(max-width:860px){.auth-login{grid-template-columns:1fr}.auth-visual{display:none}.auth-side{padding:20px}.auth-login-card{max-width:440px;box-shadow:0 14px 40px rgba(18,55,91,.14)}}
  .user-access-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px;margin-top:8px}.user-access-grid label{display:flex;align-items:center;gap:7px;padding:7px 9px;border:1px solid var(--line);border-radius:7px;font-size:12px}
  .perm-table{width:100%;min-width:760px;table-layout:fixed}.perm-table th:first-child,.perm-table td:first-child{width:36%;text-align:left}.perm-table th:not(:first-child),.perm-table td:not(:first-child){width:12.8%;text-align:center;padding-left:6px;padding-right:6px}.perm-table .cbx{justify-content:center}.perm-table td:last-child .btn{margin:0 auto}
  .login-slide-thumb{width:150px;height:82px;object-fit:cover;border:1px solid var(--line);border-radius:8px;background:#e8eef5;display:block}.login-thumb-replace{position:relative;display:inline-block;cursor:pointer}.login-thumb-replace input{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}.login-thumb-replace span{position:absolute;left:50%;bottom:5px;transform:translateX(-50%);background:rgba(0,0,0,.72);color:#fff;border-radius:5px;padding:3px 8px;font-size:10px;font-weight:650;pointer-events:none}.login-thumb-replace:hover img{filter:brightness(.85)}
  .shop-menu-page{max-width:780px;margin:0 auto}.shop-menu-head{display:flex;align-items:center;gap:10px;margin-bottom:12px}.shop-menu-head>div{flex:1}.shop-menu-name{background:#fff;border:1px solid var(--line);border-radius:10px;padding:13px 14px;margin-bottom:14px}.shop-menu-name label{font-size:12px;font-weight:650;display:block;margin-bottom:6px}.shop-menu-card{background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px}.shop-menu-card>h4{font-size:14px;margin:0 0 12px}.shop-menu-group{border:1px solid #dfe3e8;border-radius:8px;margin-bottom:8px;overflow:hidden;background:#fff}.shop-menu-group.dragging{opacity:.45;outline:2px dashed #6aa9d8}.shop-menu-parent{display:flex;align-items:center;gap:9px;padding:11px 12px;background:#fafafa;font-size:13px;font-weight:650}.shop-menu-parent .drag{color:#8a8a8a;letter-spacing:-2px}.shop-menu-parent .chev{border:none;background:transparent;width:22px}.shop-menu-parent .count{margin-left:auto;color:var(--muted);font-size:11px}.shop-menu-children{padding:6px 10px 8px 38px;border-top:1px solid #e8ebef}.shop-menu-group.closed .shop-menu-children{display:none}.shop-menu-group.closed .chev{transform:rotate(-90deg)}.shop-menu-child{display:grid;grid-template-columns:24px minmax(0,1fr) 104px;align-items:center;gap:7px;padding:9px 8px;border:1px solid #e1e5ea;border-radius:7px;margin:5px 0;background:#fff;cursor:grab}.shop-menu-child:hover{background:#f8fafc;border-color:#bcc9d6}.shop-menu-child.dragging{opacity:.4}.shop-menu-child .handle{color:#8a8a8a}.shop-menu-child small{display:block;color:var(--muted);margin-top:2px}.shop-menu-add{width:100%;border:1px dashed #b9c4cf;background:#fff;color:#005bd3;border-radius:7px;padding:9px;text-align:left;margin-top:8px}.shop-menu-save{display:flex;justify-content:flex-end;margin-top:12px}
  @media(max-width:760px){.user-access-grid{grid-template-columns:1fr 1fr}}
  td.jw-merged-pod{vertical-align:middle; text-align:center; min-width:178px; width:178px; padding:10px 12px;
    background:#fafafa; border-left:1px solid var(--line); border-right:1px solid var(--line)}
  tbody tr:hover td.jw-merged-pod{background:#fafafa}
  td.jw-merged-pod .pod-thumb{display:block; width:max-content; margin:0 auto 6px}
  td.jw-merged-pod .pod-file{display:block; max-width:156px; margin:4px auto 0}
  td.jw-merged-pod .sub-line{max-width:156px; margin:5px auto 0; white-space:normal; overflow-wrap:anywhere; text-align:center}
  .grp-toggle{display:inline-flex; align-items:center; gap:8px; height:32px; padding:0 12px 0 8px; background:#fff; box-shadow:var(--btn-shadow); border-radius:8px; font-size:13px; font-weight:650; color:var(--ink)}
  .sw{width:36px; height:20px; border:none; border-radius:99px; background:#c9c9c9; position:relative; padding:0; flex:none}
  .sw.on{background:#29845a}
  .sw-knob{position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.25); transition:transform .15s}
  .sw.on .sw-knob{transform:translateX(16px)}
  .grp-by{display:inline-flex; align-items:center; gap:8px}
  .grp-by .mcbox{min-width:210px; max-width:280px}
  .grp-by-l{font-size:12.5px; font-weight:650; color:var(--muted)}
  tr.rep-grp td{background:#f1f6fd; font-weight:650; cursor:default}
  tbody tr.rep-grp:hover td{background:#e8f0fb}
  tr.rep-grp.d1 td{background:#f6f8fb; padding-left:28px}
  tr.rep-grp.d2 td{padding-left:44px}
  tr.rep-grp .rep-g-n{font-weight:550; color:var(--muted); margin-left:8px}
  @media(max-width:760px){.grid2,.grid3,.grid-auto{grid-template-columns:1fr}}
  .hint{font-size:12px; color:var(--muted); margin-top:8px; display:block; line-height:1.6}

  /* ===== Shopify-like rich text editor (PO Terms) ===== */
  .rte{border:1px solid #8a8a8a; border-top-color:#5c5c5c; border-radius:8px; background:#fff; overflow:hidden}
  .rte:focus-within{border-color:var(--accent); box-shadow:0 0 0 2px var(--accent)}
  .rte-bar{display:flex; align-items:center; gap:2px; flex-wrap:wrap; padding:6px 8px; border-bottom:1px solid var(--line); background:#fafafa}
  .rte-sep{width:1px; height:18px; background:var(--line); margin:0 5px; flex:none}
  .rte-sel{height:28px; border:none; background:transparent; font:inherit; font-size:13px; font-weight:550; padding:0 6px; border-radius:6px; color:var(--ink); cursor:pointer}
  .rte-sel:hover{background:#e3e3e3}
  .rte-btn{width:28px; height:28px; border:none; background:transparent; border-radius:6px; color:var(--ink); display:inline-flex; align-items:center; justify-content:center; flex:none; padding:0}
  .rte-btn:hover{background:#e3e3e3}
  .rte-btn.on{background:#e3e3e3; box-shadow:var(--btn-shadow)}
  .rte-btn b,.rte-btn i,.rte-btn u,.rte-btn s{font-size:13px; line-height:1; font-family:Georgia,serif}
  .rte-body{min-height:168px; max-height:420px; overflow:auto; padding:12px 14px; outline:none; line-height:1.55; font-size:13.5px; color:var(--ink)}
  .rte-body:empty:before{content:attr(data-ph); color:#8a8a8a; pointer-events:none}
  .rte-body p{margin:0 0 8px}
  .rte-body p:last-child{margin-bottom:0}
  .rte-body h2{font-size:18px; margin:0 0 8px}
  .rte-body h3{font-size:15px; margin:0 0 8px}
  .rte-body blockquote{margin:0 0 8px; padding-left:12px; border-left:3px solid #c9c9c9; color:var(--muted)}
  .rte-body ul,.rte-body ol{margin:6px 0 8px; padding-left:22px}
  .rte-body li{margin:2px 0}
  .rte-body a{color:var(--accent)}
  .rte.code .rte-body{font-family:ui-monospace,Menlo,Consolas,monospace; font-size:12.5px; white-space:pre-wrap; background:#f7f7f7}

  /* ================= COMBOBOX ================= */
  .cbox{position:relative}
  .cbox .cbox-input{padding-right:30px; cursor:pointer; background:#fff}
  .cbox .cbox-caret{position:absolute; right:9px; top:50%; transform:translateY(-50%); display:inline-flex; align-items:center; color:var(--muted); pointer-events:none; transition:color .14s}
  .cbox .cbox-caret svg.i{display:block; width:16px; height:16px; stroke-width:1.6}
  .cbox.open .cbox-caret{color:var(--ink)}
  .cbox-list{position:fixed; top:0; left:0; width:auto; min-width:180px; background:#fff; border:1px solid var(--line); border-radius:10px; box-shadow:0 10px 28px rgba(0,0,0,.20); max-height:250px; overflow-y:auto; z-index:200; display:none; padding:4px; animation:drop .12s var(--ease-soft)}
  @keyframes drop{from{opacity:0; transform:translateY(-4px)}to{opacity:1; transform:none}}
  .cbox.open .cbox-list{display:block}
  .cbox.cbox-ro{pointer-events:none;opacity:.72}.cbox.cbox-ro .cbox-input{background:#f4f4f4;color:#6b6b6b;-webkit-text-fill-color:#6b6b6b}
  .cbox-list::-webkit-scrollbar{width:8px}
  .cbox-opt{padding:7px 10px; border-radius:8px; cursor:pointer; font-size:13px; display:flex; align-items:center; gap:8px; transition:background .1s}
  .cbox-opt:hover,.cbox-opt.active{background:#f1f1f1; color:var(--ink)}
  .cbox-opt .tick{margin-left:auto; color:#303030; font-weight:700; font-size:11px}
  .cbox-opt .hint-l{font-size:11.5px; color:var(--muted); margin-left:auto}
  .cbox-search{display:flex; align-items:center; gap:6px; padding:6px 10px 8px; margin-bottom:4px; border-bottom:1px solid var(--line); font-size:11.5px; color:var(--muted); font-weight:550}
  .cbox-search svg.i{color:var(--muted); flex:none}
  .cbox-empty{padding:12px; font-size:12.5px; color:var(--muted); text-align:center}
  .cbox-add{padding:8px 10px; border-radius:8px; cursor:pointer; font-size:12.5px; font-weight:600; color:var(--accent); border-top:1px solid var(--line); margin-top:4px; display:flex; align-items:center; gap:6px}
  .cbox-add:hover{background:#f1f6fd}
  .cbox.compact .cbox-input{padding:4px 26px 4px 10px; min-height:28px; font-size:12.5px}
  td .cbox{max-width:150px}
  td .cbox .cbox-input{border-color:transparent; background:transparent; font-weight:550}
  td .cbox .cbox-input:hover{border-color:#8a8a8a; background:#fff}
  .cbox.compact .cbox-caret svg.i{width:14px; height:14px}
  td .cbox .cbox-caret{opacity:0; transition:opacity .12s}
  tbody tr:hover td .cbox .cbox-caret, td .cbox.open .cbox-caret{opacity:1}
  td .cbox.open .cbox-input{border-color:var(--accent); background:#fff}
  .cbox.compact .cbox-list{font-size:12.5px}

  /* ================= BADGES (Polaris) ================= */
  .badge{display:inline-flex; align-items:center; gap:5px; padding:2px 8px; border-radius:8px; font-size:12px; font-weight:550; line-height:1.4; white-space:nowrap; background:#e3e3e3; color:#303030}
  .badge::before{content:""; width:6px; height:6px; border-radius:50%; background:currentColor; opacity:.85; flex:none}
  .b-green{background:#cdfee1; color:#0c5132}
  .b-amber{background:#ffd6a4; color:#5e4200}
  .b-red{background:#ffd7d4; color:#8e1f0b}
  .b-blue{background:#d3eafd; color:#00527c}
  .b-gray{background:#e3e3e3; color:#4a4a4a}
  .b-pink{background:#ebd7fd; color:#5700d1}
  .b-yellow{background:#fff2a8; color:#6b5200}
  .b-purple{background:#e6dcff; color:#4c1d95}
  .b-teal{background:#c9f3ec; color:#075e54}

  /* ================= MODAL ================= */
  .overlay{position:fixed; inset:0; background:rgba(0,0,0,.55); display:none; align-items:flex-start; justify-content:center; padding:48px 18px; z-index:60; overflow-y:auto; backdrop-filter:none; -webkit-backdrop-filter:none}
  .overlay.show{display:flex; animation:overlayIn .14s var(--ease-soft)}
  @keyframes overlayIn{from{opacity:0}to{opacity:1}}
  .modal{background:#fff; border-radius:14px; width:100%; max-width:min(1100px,94vw); box-shadow:var(--shadow-lg); animation:pop .16s var(--ease-soft); overflow:hidden}
  .modal.modal-wide{max-width:min(1400px,96vw)}
  .modal.modal-xl{max-width:96vw}
  .modal.modal-item{max-width:min(1100px,84vw);height:auto;max-height:78vh;display:flex;flex-direction:column}
  .modal.modal-item .modal-head,.modal.modal-item .modal-foot{flex:none}
  .modal.modal-item .modal-body{flex:0 1 auto;min-height:0;max-height:calc(78vh - 118px);overflow:auto}
  @media(max-width:900px){.modal.modal-item{max-width:96vw;max-height:88vh}.modal.modal-item .modal-body{max-height:calc(88vh - 118px)}}
  .modal.modal-sm{max-width:min(460px,94vw)}
  .dlg-msg{font-size:13.5px; line-height:1.55; color:var(--ink)}
  .dlg-msg .dlg-strong{font-weight:650}
  .dlg-note{margin-top:10px; font-size:12.5px; color:var(--muted); background:#f7f7f7; border-radius:8px; padding:8px 10px}
  .dlg-field{margin-top:4px}
  .dlg-field label{display:block; font-size:12px; font-weight:600; color:var(--muted); margin-bottom:5px}
  .dlg-field + .dlg-field{margin-top:12px}
  .btn-critical{background:#8e1f0b; color:#fff; box-shadow:var(--btn-shadow-dark)}
  .btn-critical:hover{background:#7a1a09}
  @keyframes pop{from{transform:translateY(10px) scale(.99); opacity:0}to{transform:none; opacity:1}}
  .modal-head{display:flex; align-items:center; gap:12px; padding:14px 20px; border-bottom:1px solid var(--line); position:sticky; top:0; background:#fff; backdrop-filter:none; z-index:5}
  .modal-head::before{content:none}
  .modal-head h3{font-size:15px; font-weight:650; flex:1; letter-spacing:normal}
  .modal-head .tag{font-size:12px; font-weight:550; color:#303030; background:#e3e3e3; padding:2px 8px; border-radius:8px; margin-right:8px}
  .modal-body{padding:20px; background:#fff}
  .modal-foot{display:flex; justify-content:flex-end; gap:8px; padding:14px 20px; border-top:1px solid var(--line); background:#fff; position:sticky; bottom:0}
  /* form section rhythm */
  .entry-page .sec{padding-bottom:8px; border-bottom:1px solid var(--line)}
  .entry-page .sec::after{display:none}
  .inp:disabled,.inp[readonly]{background-color:#fafafa; color:var(--muted)}
  /* date fields stay white and keep their calendar icon even though they're readonly */
  .inp[data-date][readonly]{background-color:#fff; color:var(--ink)}
  .inp[data-date]:disabled{background-color:#fafafa; color:var(--muted)}
  .field:last-child{margin-bottom:0}
  .sec{font-size:13px; font-weight:650; text-transform:none; letter-spacing:normal; color:var(--ink); margin:22px 0 10px; display:flex; align-items:center; gap:10px}
  .sec:first-child{margin-top:0}
  .sec::after{content:""; flex:1; height:1px; background:var(--line)}
  .mini-table th{padding:7px 10px; font-size:11.5px}
  .mini-table td{padding:6px 8px}
  .mini-table .inp{padding:4px 10px; min-height:28px; font-size:12.5px}
  /* date cells keep room for the calendar glyph at the smaller mini-table scale */
  .mini-table .inp[data-date]{padding-left:30px; background-position:8px center; background-size:15px 15px}
  /* readable widths for the PO / entry item grids */
  .mini-table td .cbox{max-width:none}
  .mini-table td .cbox .cbox-input{border-color:#8a8a8a; background:#fff}
  .mini-table th, .mini-table td{white-space:nowrap}
  #po-items td:nth-child(2){min-width:150px}   /* Classification */
  #po-items td:nth-child(3){min-width:230px}   /* Item Name (with code) */
  #po-items td:nth-child(4){min-width:110px}   /* HSN No */
  #po-items td:nth-child(5){min-width:80px}    /* Unit */
  /* auto-filled, read-only cells read as data, not inputs */
  #po-items td .inp:disabled{background:#f7f7f7; border-color:#e3e3e3; color:#4a4a4a; -webkit-text-fill-color:#4a4a4a; opacity:1; cursor:not-allowed}
  #po-items td input.num, #po-items td .inp-sm{min-width:78px}
  .tot-row td{font-weight:650; background:#f7f7f7; border-top:1px solid var(--line); font-variant-numeric:tabular-nums}

  /* ================= CONTEXTUAL SAVE BAR ================= */
  .savebar{position:fixed; top:64px; left:50%; transform:translateX(-50%) translateY(-140%); z-index:70; width:min(1100px, calc(100vw - 32px)); transition:transform .22s var(--ease); pointer-events:none}
  .savebar.show{transform:translateX(-50%) translateY(0); pointer-events:auto}
  .sb-in{background:#303030; border-radius:10px; padding:8px 8px 8px 16px; display:flex; align-items:center; gap:10px; box-shadow:0 6px 20px rgba(0,0,0,.34); color:#e3e3e3; font-size:13px}
  .sb-msg{font-weight:550; color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  .sb-dot{width:8px; height:8px; border-radius:50%; background:#ffb800; flex:none; box-shadow:0 0 0 3px rgba(255,184,0,.2)}
  .sb-sp{flex:1}
  .sb-in .btn-ghost{background:transparent; color:#e3e3e3; box-shadow:0 0 0 1px #616161 inset}
  .sb-in .btn-ghost:hover{background:#4a4a4a; box-shadow:0 0 0 1px #8a8a8a inset}
  .sb-in .btn-primary{background:#fff; color:#303030; box-shadow:none}
  .sb-in .btn-primary:hover{background:#e3e3e3}
  body.has-savebar .globalbar{background:#1a1a1a}
  @media(max-width:920px){.savebar{top:auto; bottom:12px; transform:translateX(-50%) translateY(180%)}}
  body.has-savebar .topbar{padding-top:64px}

  /* ================= TOASTS (Polaris dark pill) ================= */
  .toast-wrap{position:fixed; bottom:24px; left:50%; transform:translateX(-50%); top:auto; right:auto; z-index:120; display:flex; flex-direction:column; gap:8px; pointer-events:none; align-items:center}
  .toast{background:#303030; color:#fff; padding:10px 16px; border-radius:8px; font-size:13px; font-weight:550; box-shadow:0 4px 12px rgba(0,0,0,.28); display:flex; gap:8px; align-items:center; animation:toastIn .18s var(--ease-soft); max-width:420px; border-left:none}
  .toast .ti{display:flex; color:#36fba1; flex:none}
  .toast.red .ti{color:#ffd7d4}
  .tundo{background:transparent; border:none; color:#fff; font-weight:650; font-size:13px; text-decoration:underline; padding:2px 4px; margin-left:6px; border-radius:6px}
  .tundo:hover{background:rgba(255,255,255,.14); text-decoration:none}
  .toast.green{background:#303030}
  .toast.red{background:#8e1f0b}
  @keyframes toastIn{from{opacity:0; transform:translateY(10px)}to{opacity:1; transform:none}}
  .toast.out{opacity:0; transform:translateY(10px); transition:.24s}

  /* ================= MASTERS ================= */
  .master-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(258px,1fr)); gap:12px}
  .master-box{background:var(--card); border:none; border-radius:var(--radius); padding:16px; box-shadow:0 0 0 1px rgba(0,0,0,.06), var(--shadow); transition:box-shadow .15s var(--ease-soft)}
  .im-thumb{width:28px; height:28px; border-radius:6px; object-fit:cover; vertical-align:middle; margin-right:6px; border:1px solid var(--line)}
  .im-image-col{width:64px}.im-list-image{width:42px;height:42px;border:1px solid var(--line);border-radius:7px;background:#fff;padding:2px;overflow:hidden}.im-list-image img{width:100%;height:100%;object-fit:cover;border-radius:5px;display:block}.im-list-image:hover{border-color:var(--accent);box-shadow:0 0 0 2px rgba(0,91,211,.16)}.im-no-image{display:inline-flex;width:42px;height:42px;align-items:center;justify-content:center;background:#f1f1f1;border-radius:7px;color:#9a9a9a}
  .item-image-viewer{position:fixed;inset:0;z-index:500;background:rgba(0,0,0,.72);display:flex;align-items:center;justify-content:center;padding:28px}.item-image-viewer-card{max-width:min(900px,92vw);max-height:90vh;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 22px 70px rgba(0,0,0,.4)}.item-image-viewer-head{display:flex;align-items:center;gap:10px;padding:10px 14px;border-bottom:1px solid var(--line)}.item-image-viewer-head b{flex:1}.item-image-viewer-body{padding:14px;display:flex;justify-content:center;background:#f7f7f7}.item-image-viewer-body img{max-width:100%;max-height:calc(90vh - 75px);object-fit:contain;display:block}
  .im-thumb-empty{display:inline-flex; align-items:center; justify-content:center; background:#f1f1f1; font-size:13px}
  .im-thumb-lg{width:54px; height:54px; border-radius:8px; object-fit:cover; border:1px solid var(--line); margin-left:10px}
  .radio-row{display:flex; gap:16px; align-items:center; height:34px; font-size:13px; color:var(--ink)}
  .radio-row label{display:flex; align-items:center; gap:6px; cursor:pointer; font-weight:550}
  .im-equal-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
  .im-equal-grid .field{min-width:0}.im-equal-grid .inp,.im-equal-grid .cbox{width:100%;max-width:none}
  .im-status-toggle{display:flex;align-items:center;gap:10px;min-height:34px}.im-status-toggle b{font-size:12.5px}
  @media(max-width:1000px){.im-equal-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
  @media(max-width:680px){.im-equal-grid{grid-template-columns:1fr}}
  .img-upload-row{display:flex; align-items:center; gap:10px; height:34px}
  .file-btn{position:relative; overflow:hidden; display:inline-flex}
  .file-btn input[type=file]{position:absolute; inset:0; opacity:0; cursor:pointer; width:100%}
  .master-box:hover{box-shadow:0 0 0 1px rgba(0,0,0,.10), var(--shadow-md); transform:none}
  .master-box h4{font-size:13.5px; font-weight:650; margin-bottom:10px; display:flex; align-items:center; gap:8px}
  .master-box h4 .mh-ic{width:26px; height:26px; border-radius:7px; background:#f1f1f1; color:#4a4a4a; display:flex; align-items:center; justify-content:center; flex:none}
  .master-item.mi-empty{color:var(--muted); font-style:normal; justify-content:center; padding:16px 12px; font-size:12.5px}
  .master-box h4 .c{margin-left:auto; font-size:11.5px; background:#e3e3e3; color:var(--muted); padding:1px 8px; border-radius:8px; font-weight:650}
  .master-list{max-height:232px; overflow-y:auto; border:1px solid var(--line); border-radius:10px; margin-bottom:10px}
  .master-item{display:flex; align-items:center; justify-content:space-between; padding:8px 12px; border-bottom:1px solid var(--line2); font-size:13px; gap:8px}
  .master-item:last-child{border-bottom:none}
  .master-item .mi-acts{display:inline-flex; gap:2px; flex:0 0 auto; margin-left:auto}
  .master-item > span:not(.mi-sr):not(.mi-id):not(.mi-acts):not(.master-status-cell){flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:6px; flex-wrap:wrap}
  .mtools{align-items:flex-end}
  .mtools .add-row{align-items:center}
  .mtools .search.msearch{flex:0 0 240px}
  .rc-szhint{text-transform:none; font-weight:550; color:var(--muted); font-size:11px}
  .rc-table .rc-stcol, .rc-table .rc-stname{width:210px; min-width:210px; max-width:210px}
  .rc-stname{font-size:12.5px; line-height:1.35}
  .rc-stname b{display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .rc-szcell{min-width:260px}
  /* let the size column soak up all spare width so the fixed columns stay tight */
  .rc-table th:nth-child(4), .rc-table td:nth-child(4){width:100%}
  /* tighter Date / Style / UOM so the size boxes get the room */
  .rc-table .rc-dcol, .rc-table td:nth-child(2){width:112px; min-width:112px; padding-left:6px; padding-right:4px}
  .rc-table td:nth-child(2) .inp,
  .rc-table.mini-table td .inp[data-date]{width:112px; min-width:112px; padding-left:24px; padding-right:4px; background-position:5px center; background-size:14px 14px}
  .jw-stname{font-size:12.5px}
  .rc-addbar{display:flex; flex-wrap:wrap; gap:8px; margin-top:10px}
  .rc-table .rc-ucol, .rc-table .rc-uom{width:46px; min-width:46px; padding-left:4px; padding-right:4px}
  .rc-table .rc-sumunit{white-space:nowrap}
  .rc-table .rc-uom{color:var(--muted); font-size:12.5px}
  .rc-table .rc-qccol, .rc-table .rc-qc{width:104px; min-width:104px}
  .rc-table .rc-whcol, .rc-table .rc-wh{width:150px; min-width:150px}
  .rc-table .rc-qc .cbox-input, .rc-table .rc-wh .cbox-input{padding-right:26px}
  .rc-table tr.rc-locked .inp{background:#f4f4f4; color:#5a5a5a; -webkit-text-fill-color:#5a5a5a}
  .rc-table .cbox-ro{pointer-events:none; opacity:.75}
  .rc-table .actions-cell{white-space:nowrap}
  .rc-sz{display:flex; flex-wrap:wrap; gap:4px}
  .rc-sz .szbox{margin-right:2px}
  .rc-sz .szin{width:58px}
  .szbal{font-size:10.5px; color:var(--muted); font-weight:600; line-height:1.2}
  .rc-sz-tot{display:inline-block; margin-top:5px; font-size:11.5px; font-weight:700; color:var(--ink)}
  .rc-table .rc-hist td{background:#fafafa}
  .rc-table .rc-hist .inp{background:#fff}
  .rc-table .rc-sumbody{border-top:2px solid var(--line)}
  .rc-table .rc-sum td{padding-top:7px; padding-bottom:7px; border-bottom:none}
  .rc-table .rc-sum .rc-lbl{font-weight:600; color:var(--ink)}
  .rc-table .rc-sum .num b{font-variant-numeric:tabular-nums}
  .rc-table .rc-total-row td{border-top:1px solid var(--line); background:#f7f7f7}
  .rc-table .rc-diff-row td{border-top:1px solid var(--line); font-weight:700}
  .rc-table .rc-diff-row .rc-lbl{font-weight:700}
  .style-cell{min-width:300px}
  .style-cell .cbox-input{text-overflow:ellipsis}
  .mcbox-opt.mcbox-lock{color:var(--muted); cursor:not-allowed; background:#fafafa}
  .mcbox-opt.mcbox-lock:hover{background:#f4f4f4}
  .mcbox-opt.mcbox-lock .tick svg{vertical-align:-2px}
  .dpc.dpc-off{color:#c9c9c9; cursor:not-allowed; background:none}
  .dpc.dpc-off:hover{background:none}
  /* rows locked because downstream entries already consume them */
  tr.row-locked td, tr.jw-edit-locked td{background:#fafafa}
  tr.row-locked .inp, tr.jw-edit-locked .inp{background:#f4f4f4; border-color:#e3e3e3; color:#6b6b6b; -webkit-text-fill-color:#6b6b6b; cursor:not-allowed}
  tr.row-locked .cbox, tr.row-locked .mcbox, tr.jw-edit-locked .cbox, tr.jw-edit-locked .mcbox{pointer-events:none; opacity:.85}
  tr.row-locked .cbox-caret, tr.jw-edit-locked .cbox-caret{opacity:.4}
  tr.jw-edit-locked .jw-row-edit{opacity:1; color:var(--accent); background:#fff; box-shadow:var(--btn-shadow)}
  tr.jw-edit-locked .jw-row-edit:hover{background:#f1f6fd}
  .btn-icon[disabled]{opacity:.35; cursor:not-allowed}
  .btn-icon[disabled]:hover{background:none; color:var(--muted)}
  .lock-note{display:inline-flex; align-items:center; gap:6px; margin:10px 0 0; padding:8px 12px; border-radius:8px;
             background:#fff8e6; color:#5a4300; font-size:12.5px; font-weight:550}
  .po-add-item-bar{display:flex;width:100%;margin-top:10px;clear:both}
  .master-item .meta{font-size:11.5px; color:var(--muted); font-weight:550}
  .add-row{display:flex; gap:8px; flex-wrap:wrap}
  .add-row .inp{flex:1; min-width:112px}
  .add-row .btn{flex-shrink:0}
  .section-title{font-size:15px; font-weight:650; margin:24px 0 10px; display:flex; align-items:center; gap:8px; letter-spacing:normal}
  .section-title .c{font-size:12px; background:#e3e3e3; color:#303030; padding:1px 8px; border-radius:8px; font-weight:650}
  .recent tbody tr{cursor:pointer}
  .key-line{font-size:12px; color:var(--muted)}
  .recent td{font-size:12.5px}
  .demo-bar{display:flex; gap:8px; flex-wrap:wrap; margin-top:12px}
  .footnote{margin-top:20px; font-size:12.5px; color:var(--muted); line-height:1.7; background:#fff; border:none; border-radius:12px; padding:0; box-shadow:0 0 0 1px rgba(0,0,0,.06)}
  .footnote summary{cursor:pointer; padding:12px 16px; font-weight:650; color:var(--ink); font-size:13px; list-style:none; display:flex; align-items:center; gap:8px; border-radius:12px}
  .footnote summary::-webkit-details-marker{display:none}
  .footnote summary::before{content:""; width:0; height:0; border-left:5px solid #616161; border-top:4px solid transparent; border-bottom:4px solid transparent; transition:transform .15s}
  .footnote[open] summary::before{transform:rotate(90deg)}
  .footnote summary:hover{background:#fafafa}
  .footnote .fn-body{padding:0 16px 16px; border-top:1px solid var(--line); padding-top:12px}
  .warn{background:#fff5ea; border:none; border-left:4px solid #ffb800; color:#5e4200; border-radius:12px; padding:12px 16px; font-size:12.5px; margin-bottom:16px; line-height:1.55; box-shadow:0 0 0 1px rgba(0,0,0,.05)}
  .stock-chip{background:#fff; border:1px solid var(--line); color:var(--ink); border-radius:10px; padding:8px 12px; font-size:12.5px; font-weight:550; display:flex; gap:14px; flex-wrap:wrap; align-items:center; margin-bottom:4px}
  .stock-chip b{color:#0c5132}
  .load-msg{display:flex; align-items:center; justify-content:center; gap:10px; padding:64px 20px; color:var(--muted); font-size:13.5px; font-weight:550}
  .load-msg .spin{width:20px;height:20px;border-radius:50%;border:2.5px solid #e3e3e3;border-top-color:#303030;animation:rot .8s linear infinite}
  @keyframes rot{to{transform:rotate(360deg)}}
  .js-err{display:none; background:#ffd7d4; border:none; color:#8e1f0b; border-radius:12px; padding:12px 14px; font-size:12.5px; line-height:1.65; margin:0 0 14px; font-weight:550; white-space:pre-wrap; word-break:break-word; box-shadow:0 0 0 1px rgba(0,0,0,.05)}
  .js-err b{display:block; margin-bottom:4px}
  .msgbox{max-width:none; width:100%; margin:0; padding:0 24px}
  .entry-page{max-width:none; margin:0; background:var(--card); border:none; border-radius:var(--radius); padding:20px; box-shadow:0 0 0 1px rgba(0,0,0,.06), var(--shadow)}
  
  .entry-page .sec:first-child{margin-top:0}
  .entry-page.view-only .cbox,.entry-page.view-only .mcbox,.entry-page.view-only .file-btn{pointer-events:none}.entry-page.view-only{background:#fafafa}
  .sizes-cell{white-space:nowrap}
  .szbox{display:inline-flex; flex-direction:column; align-items:center; gap:2px; margin-right:6px}
  .szlbl{font-size:11px; font-weight:600; color:var(--muted); letter-spacing:normal}
  .szin{width:54px; padding:4px; min-height:28px; font-size:12.5px; text-align:center}
  .sz-inputs{margin-top:6px; display:flex; flex-wrap:wrap; gap:3px}
  .cons-cell{min-width:212px}
  .cons-total{font-size:11.5px; font-weight:650; color:#0c5132; margin-top:4px; white-space:nowrap}

  /* ================= MULTI-SELECT ================= */
  .mcbox{position:relative; min-width:152px; max-width:222px}
  /* form-level multiselects (e.g. PO Style) fill their field instead of the
     narrow width used by the in-table size pickers */
  .field > .mcbox{max-width:none; width:100%}
  .mcbox .mcbox-input{padding-right:28px; cursor:pointer; text-align:left; background:#fff}
  /* the caret was only positioned for .cbox — anchor it inside .mcbox too */
  .mcbox .cbox-caret{position:absolute; right:9px; top:50%; transform:translateY(-50%); display:inline-flex; align-items:center; color:var(--muted); pointer-events:none; transition:color .14s}
  .mcbox .cbox-caret svg.i{display:block; width:16px; height:16px; stroke-width:1.6}
  .mcbox-list{position:fixed; top:0; left:0; min-width:180px; width:max-content; max-width:280px; background:#fff; border:1px solid var(--line); border-radius:10px; box-shadow:0 10px 28px rgba(0,0,0,.20); max-height:232px; overflow-y:auto; z-index:200; display:none; padding:4px; animation:drop .12s var(--ease-soft)}
  .mcbox.open .mcbox-list{display:block}
  .mcbox.disabled{pointer-events:none;opacity:.72}.mcbox.disabled .mcbox-input{background:#f4f4f4;color:#6b6b6b;-webkit-text-fill-color:#6b6b6b}
  .mcbox.open .cbox-caret{color:var(--ink)}
  .mcbox-opt{padding:7px 10px; border-radius:8px; cursor:pointer; font-size:13px; display:flex; align-items:center; gap:8px; transition:background .1s}
  .mcbox-opt:hover{background:#f1f1f1; color:var(--ink)}
  .mcbox-opt .tick{margin-left:auto; color:#303030; font-weight:700; font-size:11px}
  .mcbox-hint{font-size:11.5px; color:var(--muted); padding:6px 10px; border-bottom:1px solid var(--line); margin-bottom:4px}

  /* ================= POD UPLOAD ================= */
  .pod-cell{min-width:176px}
  .pod-upload-box{position:relative; width:164px; min-height:104px; margin:0 auto; border:1px solid var(--line);
    border-radius:8px; background:#fff; display:flex; flex-direction:column; align-items:center; justify-content:center;
    padding:10px; overflow:hidden; transition:border-color .14s, background .14s, box-shadow .14s}
  .pod-upload-box:hover{border-color:#8a8a8a; background:#fafafa; box-shadow:0 1px 3px rgba(0,0,0,.07)}
  .pod-upload-box.inp-bad{border-color:#8e1f0b;box-shadow:0 0 0 2px rgba(142,31,11,.15)}
  .pod-upload-box.empty{cursor:pointer}
  .pod-upload-box.empty input[type=file]{position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:pointer}
  .pod-upload-plus{font-size:18px; line-height:1; font-weight:500; color:#217346; margin-bottom:7px}
  .pod-upload-title{font-size:12.5px; font-weight:550; color:var(--muted)}
  .pod-upload-help{font-size:10.5px; color:#8a8a8a; margin-top:4px; text-align:center}
  .pod-preview-img{width:76px; height:54px; object-fit:cover; border-radius:6px; border:1px solid var(--line); display:block}
  .pod-preview-pdf{width:76px; height:54px; border-radius:6px; background:#f1f1f1; border:1px solid var(--line);
    color:#8e1f0b; font-size:12px; font-weight:800; display:flex; align-items:center; justify-content:center; letter-spacing:.5px}
  .pod-upload-name{width:100%; margin-top:6px; font-size:10.5px; color:var(--muted); text-align:center;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  .pod-upload-actions{position:absolute; top:5px; right:5px; z-index:3; display:flex; gap:4px}
  .pod-upload-actions button{width:22px; height:22px; border:1px solid var(--line); border-radius:6px; background:#fff;
    display:flex; align-items:center; justify-content:center; padding:0; box-shadow:0 1px 3px rgba(0,0,0,.12)}
  .pod-upload-actions .pod-view{color:var(--accent)}
  .pod-upload-actions .pod-del{color:#8e1f0b}
  .pod-replace{position:relative; margin-top:6px; color:var(--accent); font-size:11px; font-weight:600; cursor:pointer}
  .pod-replace input[type=file]{position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:pointer}
  .invoice-gate-upload{width:100%;min-height:150px;margin-top:6px}.invoice-gate-upload input[type=file]{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}.invoice-gate-upload .invoice-preview img{width:92px;height:68px;object-fit:cover;border-radius:7px;border:1px solid var(--line)}.invoice-gate-upload .invoice-preview .pdf{width:92px;height:68px;display:flex;align-items:center;justify-content:center;background:#f1f1f1;color:#8e1f0b;border-radius:7px;font-weight:800;border:1px solid var(--line)}.invoice-gate-delete{position:absolute;right:8px;top:8px;z-index:4;width:26px;height:26px;border:1px solid var(--line);border-radius:7px;background:#fff;color:#8e1f0b;display:none;align-items:center;justify-content:center;padding:0;box-shadow:0 2px 8px rgba(0,0,0,.14)}
  @media(max-width:920px){.msgbox{padding:0 16px}}

  @media(max-width:920px){
    .app{flex-direction:row; min-height:auto}
    .grid-fixed{grid-template-columns:1fr}
    /* sidebar becomes an off-canvas drawer */
    .sidebar{position:fixed; top:56px; left:0; bottom:0; width:264px; min-width:264px; height:auto;
             background:#f1f1f1; box-shadow:4px 0 24px rgba(0,0,0,.18); z-index:30;
             transform:translateX(-100%); padding:8px 8px 0}
    body.nav-open .sidebar{transform:none}
    body.nav-open .nav-scrim{display:block}
    body.nav-collapsed .sidebar{width:264px; min-width:264px; padding:8px 8px 0}
    body.nav-collapsed .sidebar > *{opacity:1; pointer-events:auto}
    .main{width:100%; min-width:0}
    .nav{display:block; padding:2px 0 10px}
    .nav .nav-sec{display:block}
    .nav-item{width:100%; padding:8px 10px; font-size:13.5px; min-height:38px}
    .nav-item .cnt{display:inline-block}
    .brand{padding:8px 8px 10px}
    .side-foot{display:block}
    .content{padding:12px}
    .topbar{padding:14px 12px 4px; flex-wrap:wrap}
    .entry-page{padding:16px}
    .tabs{padding:6px}
    .rowcount{display:none}
    th.selcol, td.selcol{padding-left:10px}
    .modal{border-radius:14px}
  }
  @media print{
    .sidebar,.topbar .actions,.filter-bar,.no-print,aside,.globalbar{display:none!important}
    .main{margin:0}
    body{background:#fff}
    body::before{display:none}
    .panel{box-shadow:none; border:none}
    .content{padding:0}
  }

  /* ================= COSTING MODULE (Excel-style sheet) ================= */
  .split-grid{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:0 16px; align-items:start}
  .split-col{min-width:0}
  .split-grid table{min-width:0!important}
  @media(max-width:1100px){.split-grid{grid-template-columns:1fr}}
  .xl-sheet{border:1px solid #dfe1e5; border-radius:10px; overflow:hidden; background:#fff}
  .xl-sheet-head{background:#eceef1; border-bottom:1px solid #dfe1e5; padding:5px 12px; font-size:10.5px; font-weight:800; letter-spacing:.8px; color:#4a4f57; text-transform:uppercase; display:flex; align-items:center; gap:8px}
  .xl-sheet-head:not(:first-child){border-top:1px solid #dfe1e5}
  .xl-tip{margin-left:auto; font-size:10px; font-weight:600; letter-spacing:.2px; color:#7a8089; text-transform:none; white-space:nowrap}
  .xl-grid{display:grid; grid-template-columns:118px minmax(0,1fr) 108px minmax(0,1fr)}
  @media(max-width:760px){
    .xl-grid{grid-template-columns:110px minmax(0,1fr)}
    .xl-grid .xl-cell:nth-child(n){border-right:1px solid #e6e8ec}
    .xl-cell:nth-child(2n){border-right:none}
    .xl-grid > :nth-last-child(-n+2){border-bottom:none}
  }
  .xl-cell{border-right:1px solid #e6e8ec; border-bottom:1px solid #e6e8ec; min-height:30px; background:#fff; min-width:0}
  .xl-cell:nth-child(4n){border-right:none}
  .xl-grid > :nth-last-child(-n+4){border-bottom:none}
  .xl-cell.xl-lbl{background:#f7f8fa; padding:0 10px; display:flex; align-items:center; font-size:11.5px; font-weight:650; color:#3f4348; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  .xl-cell.xl-val{padding:0; display:flex; align-items:center; min-width:0; overflow:hidden}
  .xl-cell.xl-val:hover{background:#fbfcff}
  .xl-cell.xl-empty{pointer-events:none; background:#fbfbfc}
  .xl-grid input.xl-in, .xl-table input.xl-in, .xl-table input.inp{
    border:none; border-radius:0; background:transparent; width:100%; min-height:30px; padding:4px 10px;
    box-shadow:none; outline:none; font-size:12.5px;
  }
  .xl-grid input.xl-in:focus, .xl-table input.xl-in:focus, .xl-table input.inp:focus{
    background:#fff; box-shadow:inset 0 0 0 2px #217346;
  }
  input.xl-in.num, .xl-table input.inp.num{text-align:right; font-variant-numeric:tabular-nums}
  .xl-in::placeholder, .xl-table input.inp::placeholder{color:#a9aeb6}
  table.xl-table{border-collapse:collapse; min-width:0; width:100%}
  .xl-table th{background:#eceef1; font-size:10.5px; font-weight:750; color:#4a4f57; padding:4px 10px; border-right:1px solid #dfe1e5; border-bottom:1px solid #dfe1e5; text-transform:uppercase; letter-spacing:.6px; text-align:left}
  .xl-table th.num{text-align:right}
  .xl-table th:last-child{border-right:none}
  .xl-table td{padding:0; border-right:1px solid #e6e8ec; border-bottom:1px solid #e6e8ec; vertical-align:middle; background:#fff; position:relative}
  .xl-table td:last-child{border-right:none}
  .xl-table tr:last-child td{border-bottom:none}
  .xl-table td.xl-lblc{background:#f7f8fa; padding:0 10px; font-size:12px; font-weight:600; color:#3f4348; white-space:nowrap}
  .xl-table tr.totalrow td{background:#eef1f6}
  .xl-table tr.totalrow td.xl-lblc{font-weight:750; color:#22262b}
  .xl-table tr.totalrow input.xl-in, .xl-table tr.totalrow input.inp{font-weight:700}
  .xl-table input.xl-in[readonly], .xl-table input.inp[readonly]{background:#f4f5f7; color:#6b7280; -webkit-text-fill-color:#6b7280; cursor:default; font-weight:600}
  .xl-table tr.totalrow input.xl-in[readonly], .xl-table tr.totalrow input.inp[readonly]{background:#e9ecf2; color:#3c4149; -webkit-text-fill-color:#3c4149; font-weight:750}
  .mini-table.xl-table td{padding:0}
  .mini-table.xl-table th{padding:4px 10px; font-size:10.5px; font-weight:750; letter-spacing:.6px; text-transform:uppercase}
  .mini-table.xl-table .inp{min-height:30px; padding:4px 10px; font-size:12.5px}
  td.fill-hl{background:#dff0e7!important}
  .fh{position:absolute; right:-1px; bottom:-1px; width:10px; height:10px; background:#217346; border:1.5px solid #fff; border-radius:2px; z-index:6; cursor:crosshair; box-shadow:0 0 0 1px rgba(0,0,0,.12)}
  body.filling, body.filling *{user-select:none!important; -webkit-user-select:none!important}
  body.filling{cursor:crosshair!important}
  .xl-table td.sr-cell{text-align:center; vertical-align:middle; background:#f7f8fa; color:#7a8089; font-weight:700; font-size:12px; width:52px; min-width:52px; padding:0}
  .xl-table th.c-sr{text-align:center}
  .xl-table td.xl-act{text-align:center; width:44px; background:#fff}
  .bom-foot{display:flex; align-items:center; justify-content:space-between; gap:8px; margin-top:8px}
  .bom-hint{font-size:10.5px; color:#7a8089; font-weight:600}
  .xl-cell.xl-imgcell{min-height:78px; position:relative; align-items:center; justify-content:center}
  .xl-cell.xl-imgcell input[type=file]{position:absolute; inset:0; opacity:0; cursor:pointer; z-index:3}
  .xl-imgcell .imgph{font-size:11.5px; color:#8a8f98; pointer-events:none; display:inline-flex; align-items:center; gap:6px}
  .xl-imgcell .imgph::before{content:"+"; font-size:15px; font-weight:700; color:#217346; line-height:1}
  .xl-imgcell img.imgprev{position:relative; z-index:2; max-height:64px; max-width:calc(100% - 30px); object-fit:contain; border-radius:6px; display:block; background:#fff; box-shadow:0 0 0 1px #e6e8ec}
  .xl-imgcell .imgclear{position:absolute; top:3px; right:3px; z-index:4; width:18px; height:18px; border-radius:5px; border:none; background:#fff; box-shadow:0 0 0 1px rgba(0,0,0,.14); color:#8e1f0b; font-size:10px; display:none; align-items:center; justify-content:center; line-height:1; padding:0}
  .xl-imgcell .imgclear:hover{background:#fdf3f1}
  .mpos{color:#0c5132;font-weight:650}
  .mneg{color:#8e1f0b;font-weight:650}
  /* BOM: compact so both 50% panels fit; Particulars + Cost stay readable */
  table.cost-bom{min-width:0!important; width:100%; table-layout:fixed}
  table.cost-bom th{white-space:normal; line-height:1.15; padding:5px 5px}
  table.cost-bom td{white-space:nowrap}
  table.cost-bom th.srcol, table.cost-bom td.srcol, table.cost-bom td.sr-cell{width:40px; min-width:40px; max-width:40px; padding-left:4px; padding-right:2px}
  table.cost-bom th:nth-child(2), table.cost-bom td:nth-child(2){width:auto; min-width:0; white-space:normal}
  table.cost-bom td:nth-child(2) .inp{width:100%; min-width:0}
  table.cost-bom th:nth-child(3), table.cost-bom td:nth-child(3),
  table.cost-bom th:nth-child(5), table.cost-bom td:nth-child(5){width:58px; min-width:52px}
  table.cost-bom th:nth-child(4), table.cost-bom td:nth-child(4){width:86px; min-width:78px}
  table.cost-bom th:nth-child(6), table.cost-bom td:nth-child(6){width:62px; min-width:56px}
  table.cost-bom th:nth-child(7), table.cost-bom td:nth-child(7){width:84px; min-width:76px}
  table.cost-bom td:nth-child(7) .inp{width:100%; min-width:0; text-align:right; font-weight:650}
  table.cost-bom th:nth-child(8), table.cost-bom td:nth-child(8),
  table.cost-bom td.xl-act{width:32px; min-width:32px}
  table.cost-bom .inp.num{width:100%; min-width:0; max-width:none}
  table.cost-bom .inp{padding-left:6px; padding-right:6px}
  .split-col .table-wrap{overflow-x:auto}
  /* Excel-like column resize on costing tables */
  .xl-table th{position:relative}
  .col-grip{position:absolute; right:-3px; top:0; width:8px; height:100%; cursor:col-resize; z-index:5; user-select:none}
  .col-grip:hover, .col-grip.on{background:linear-gradient(90deg, transparent 3px, #217346 3px, #217346 5px, transparent 5px)}
  body.col-resizing, body.col-resizing *{cursor:col-resize!important; user-select:none!important}
  .cost-print-img{max-width:160px; max-height:120px; object-fit:contain; border:1px solid #ccc; border-radius:4px; background:#fff}
  .xl-cell.xl-val .cbox{width:100%; max-width:none}
  .xl-cell.xl-val .cbox .cbox-input{border:none; border-radius:0; background:transparent; min-height:30px; box-shadow:none; padding:4px 28px 4px 10px}
  .xl-cell.xl-val .cbox .cbox-input:focus{background:#fff; box-shadow:inset 0 0 0 2px #217346; border-color:transparent}
  .xl-cell.xl-val .cbox .cbox-caret{right:8px}
  table.cost-bom td .cbox{max-width:none; width:100%}
  table.cost-bom td .cbox .cbox-input{border:none; background:transparent; min-height:30px; box-shadow:none; padding:4px 26px 4px 6px; font-weight:550}
  table.cost-bom td .cbox .cbox-input:focus{background:#fff; box-shadow:inset 0 0 0 2px #217346}
  table.cost-bom td .cbox .cbox-caret{opacity:1}


  /* ================= MOBILE-FIRST UX REFINEMENTS ================= */
  @media(max-width:920px){
    body{font-size:13px;overflow-x:hidden}
    .globalbar{height:52px;padding:0 8px;gap:8px}
    .globalbar .gb-brand{display:none}
    .globalbar .gb-search{max-width:none;margin:0;min-width:0}
    .globalbar .gb-field{min-height:36px}
    .globalbar .gb-right{gap:2px}
    .globalbar .gb-avatar{width:36px;padding:0;justify-content:center}
    .globalbar .gb-avatar>span:last-child{display:none}
    .globalbar .gb-btn{width:34px;height:34px}
    .app{min-height:calc(100vh - 52px)}
    .sidebar{top:52px;width:min(286px,86vw);min-width:min(286px,86vw)}
    body.nav-collapsed .sidebar{width:min(286px,86vw);min-width:min(286px,86vw)}
    .nav-scrim{inset:52px 0 0}
    .topbar{padding:12px 12px 4px;gap:9px;align-items:flex-start}
    .topbar h2{font-size:18px}
    .topbar .sub{font-size:11.5px;line-height:1.35}
    .topbar .actions{width:100%;gap:6px;order:3}
    .topbar .actions .btn{flex:1 1 auto;min-height:36px;padding:0 10px}
    .content{padding:10px 10px 56px}
    .msgbox{padding:0 10px}
    .entry-page{padding:13px;border-radius:10px}
    .entry-page .sec{margin-top:18px}
    .grid-fixed,.grid2,.grid3,.grid-auto,.grid-even5,.xl-grid{grid-template-columns:1fr!important}
    .grid-fixed.wide-last>.field:last-child{grid-column:auto}
    .field{margin-bottom:11px}
    .lbl{font-size:12px}
    .inp,.cbox-input,.mcbox-input,select.inp,textarea.inp{min-height:38px;font-size:16px}
    .inp-sm,.mini-table .inp{font-size:14px;min-height:34px}
    .btn{min-height:36px}
    .btn-sm{min-height:34px}
    .stats{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
    .stat{padding:12px}.stat .val{font-size:19px}
    .flow-card{padding:12px}.flow{display:grid;grid-template-columns:1fr 1fr;gap:8px}
    .flow .step{min-width:0}.flow .arrow{display:none}
    .panel{border-radius:10px}
    .table-wrap{width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-inline:contain;border-radius:8px}
    .table-wrap table{min-width:760px}
    .mini-table{min-width:720px}
    .table-wrap::-webkit-scrollbar{height:7px}
    .table-wrap::-webkit-scrollbar-thumb{background:#aeb7c1;border-radius:9px}
    th{font-size:11.5px;padding:8px 9px}
    td{padding:9px;font-size:12.5px}
    th.srcol,td.srcol{position:sticky;left:0;z-index:5;background:#f7f7f7;box-shadow:1px 0 0 var(--line)}
    tbody td.srcol{background:#fff}
    tbody tr:hover td.srcol{background:#fafafa}
    .actions-cell{position:sticky;right:0;background:#fff;z-index:4;box-shadow:-1px 0 0 var(--line)}
    th.actions-cell,th.no-print:last-child{right:0;z-index:6}
    .filter-bar{padding:8px;gap:7px}
    .filter-bar .search{flex:1 1 100%;min-width:0}
    .filter-bar>.btn,.filter-bar>span{flex:0 1 auto}
    .tabs{padding:5px;gap:4px}.tab{min-height:34px;padding:6px 10px}
    .pager{padding:8px;gap:6px}.pager .rowcount{display:block;flex:1 1 100%;margin:0}.pager-nav{margin-left:0}.pager select.inp{margin-left:auto;font-size:13px;width:auto;min-height:34px}
    .masters-wrap{display:block}
    .mnav{position:sticky;top:52px;z-index:16;display:flex!important;flex-wrap:nowrap!important;gap:5px;overflow-x:auto;max-height:none;padding:7px;margin-bottom:10px;border-radius:9px;scrollbar-width:none}
    .mnav::-webkit-scrollbar{display:none}
    .mnav-sec{display:none!important}
    .mnav-item{flex:0 0 auto;width:auto!important;min-height:36px;padding:7px 10px;box-shadow:0 0 0 1px var(--line)}
    .mnav-item .cnt{display:inline-block!important}
    .mpanel{display:block;max-height:none;overflow:visible;padding:14px!important}
    .mpanel .mlist,.mpanel .master-list{max-height:48vh;overflow:auto}
    .mhead{gap:8px;flex-wrap:wrap}.mhead>div:last-child{margin-left:0!important;width:100%}.mhead .btn{width:100%}
    .mtools{display:flex;align-items:stretch;gap:7px}.mtools .add-row{flex:1 1 100%}.mtools .search.msearch{flex:1 1 100%;max-width:none}.mtools>.btn{flex:1 1 auto}
    .master-list-head{grid-template-columns:30px 80px minmax(150px,1fr) 112px 62px;min-width:560px}
    .master-list{min-width:560px}
    .master-box{padding:12px}
    .inward-bottom-grid,.inward-bottom-grid.single{grid-template-columns:1fr!important;gap:12px;justify-content:stretch}
    .inward-attachments-panel,.inward-summary-panel{width:100%}
    .intot-wrap{max-width:none!important;width:100%}
    .overlay{padding:10px;align-items:flex-start}
    .modal,.modal.modal-wide,.modal.modal-xl,.modal.modal-item{max-width:100%;width:100%;max-height:calc(100vh - 20px);border-radius:12px}
    .modal.modal-item{height:auto}
    .modal-body{padding:14px;max-height:calc(100vh - 132px)!important;overflow:auto}
    .modal-head,.modal-foot{padding:12px 14px}
    .modal-foot .btn{flex:1}
    .dpop{max-width:calc(100vw - 16px)}
    .rdp-pop{min-width:calc(100vw - 16px)!important;max-width:calc(100vw - 16px)}
    .rdp-cals{display:block}.rdp-cal{width:100%;min-width:0}.rdp-cal+.rdp-cal{margin-top:12px}
    .savebar{width:calc(100vw - 16px)}.sb-in{padding:8px;flex-wrap:wrap}.sb-msg{flex:1}.sb-sp{display:none}
    .pod-upload-box,.att-upload-box{width:150px;min-height:105px}
    td.jw-merged-dispatch{min-width:145px;width:145px}
    td.jw-merged-pod{min-width:164px;width:164px}
    .sz-inputs{flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px}
    .reporting-shell,.sf-builder-body{grid-template-columns:1fr!important}
    .reporting-main,.reporting-customize,.sf-builder-side,.sf-report-canvas{grid-column:1!important;grid-row:auto!important}
    .reporting-customize,.sf-builder-side{position:static;max-height:none;border-right:none;border-bottom:1px solid var(--line)}
    .reporting-toolbar,.sf-builder-top{height:auto;flex-wrap:wrap;padding:8px}
    .reporting-toolbar>*{flex:1 1 auto}.reporting-toolbar .report-name{max-width:none}
    .sf-builder-title{flex:1 1 100%}.sf-format-pills{width:100%;overflow-x:auto}.sf-builder-top>.btn,.sf-builder-top>select{flex:1 1 auto}
    .sf-report-canvas{padding:8px}.sf-report-table{max-height:none}
    .report-type-grid{grid-template-columns:1fr!important}
    .perm-table{min-width:680px}
    .user-access-grid{grid-template-columns:1fr 1fr}
    .menu-manage-grid{grid-template-columns:1fr}.shop-menu-page{max-width:none}.shop-menu-head{flex-wrap:wrap}.shop-menu-head>div{flex:1 1 100%}.shop-menu-head>.btn{flex:1}
    .shop-menu-children{padding-left:16px}.shop-menu-child{grid-template-columns:22px minmax(150px,1fr) 104px}
    .login-slide-thumb{width:115px;height:68px}
    .item-image-viewer{padding:10px}.item-image-viewer-card{max-width:100%;max-height:96vh}
    .toast-wrap{bottom:12px;width:calc(100vw - 20px)}.toast{max-width:100%;width:auto}
  }
  @media(max-width:560px){
    .globalbar .gb-right .gb-btn{display:none}
    .globalbar .gb-kbd{display:none}
    .stats{grid-template-columns:1fr 1fr}.stat .extra{font-size:11px}
    .flow{grid-template-columns:1fr}
    .topbar .actions{display:grid;grid-template-columns:1fr 1fr}.topbar .actions>*{width:100%}
    .content{padding-left:8px;padding-right:8px}
    .entry-page{padding:11px}
    .user-access-grid{grid-template-columns:1fr}
    .radio-row{height:auto;flex-wrap:wrap}
    .segbar{width:100%;overflow-x:auto}.seg{flex:0 0 auto;padding:7px 10px}
    .img-upload-row{height:auto;flex-wrap:wrap}
    .auth-side{padding:12px}.auth-login-card{padding:24px 20px;border-radius:12px}.auth-login-card h2{font-size:22px}
    .modal-foot{position:sticky;bottom:0}
  }


/* --- Next Style Block --- */

.side-foot,.side-foot .sf-sub,.auth-login-card .sub-line{color:#4a4a4a!important}.sf-builder [onclick^="sfFeature("]{display:none!important}.perm-table{min-width:1180px!important;table-layout:auto!important}
.b-amber{background:#fef3c7!important;color:#92400e!important;border:1px solid #fde68a}

<style id="open-stock-align">
.open-stock-table{table-layout:fixed!important;width:100%!important;border-collapse:collapse}
.open-stock-table col.c-sr{width:56px}
.open-stock-table col.c-cls{width:18%}
.open-stock-table col.c-name{width:24%}
.open-stock-table col.c-qty,.open-stock-table col.c-rate,.open-stock-table col.c-val{width:118px}
.open-stock-table col.c-unit{width:110px}
.open-stock-table col.c-act{width:44px}
.open-stock-table th,.open-stock-table td{text-align:left!important;vertical-align:middle}
.open-stock-table th.num,.open-stock-table td.num{text-align:left!important}
.open-stock-table td .inp,
.open-stock-table td .inp.num,
.open-stock-table td .cbox{width:100%!important;max-width:100%!important;box-sizing:border-box}
.open-stock-table td.open-line-value,.open-stock-table td[data-val]{padding-right:10px}


/* --- Next Style Block --- */


.jwr-items-table{table-layout:fixed!important;width:100%!important;border-collapse:collapse}
.jwr-items-table col.j-sr{width:52px}
.jwr-items-table col.j-name{width:22%}
.jwr-items-table col.j-iss,.jwr-items-table col.j-alr{width:108px}
.jwr-items-table col.j-qty,.jwr-items-table col.j-rate,.jwr-items-table col.j-val{width:112px}
.jwr-items-table col.j-unit{width:100px}
.jwr-items-table col.j-type{width:150px}
.jwr-items-table col.j-act{width:44px}
.jwr-items-table th,.jwr-items-table td{text-align:left!important;vertical-align:middle}
.jwr-items-table th.num,.jwr-items-table td.num{text-align:left!important}
.jwr-items-table td .inp,
.jwr-items-table td .inp.num,
.jwr-items-table td .cbox{width:100%!important;max-width:100%!important;box-sizing:border-box}


/* --- Next Style Block --- */


.po-inward-table{table-layout:fixed!important;width:100%!important;border-collapse:collapse}
.po-inward-table col.p-sr{width:52px}
.po-inward-table col.p-cls{width:16%}
.po-inward-table col.p-name{width:20%}
.po-inward-table col.p-poq,.po-inward-table col.p-till{width:108px}
.po-inward-table col.p-qty,.po-inward-table col.p-rate,.po-inward-table col.p-val{width:110px}
.po-inward-table col.p-unit{width:96px}
.po-inward-table col.p-act{width:44px}
.po-inward-table th,.po-inward-table td{text-align:left!important;vertical-align:middle}
.po-inward-table th.num,.po-inward-table td.num{text-align:left!important}
.po-inward-table td .inp,
.po-inward-table td .inp.num{width:100%!important;max-width:100%!important;box-sizing:border-box}


/* --- Next Style Block --- */


.ob-wrap{max-width:1500px;width:100%}
.ob-notice{display:flex;gap:10px;align-items:flex-start;background:#fff;border:1px solid #e3e3e3;border-left:4px solid #005bd3;border-radius:10px;padding:12px 14px;margin-bottom:14px;color:#303030}
.ob-layout{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:14px;align-items:start}
.ob-card{background:#fff;border-radius:12px;box-shadow:0 0 0 1px rgba(0,0,0,.06),0 1px 0 rgba(0,0,0,.05);padding:18px;margin-bottom:14px;width:100%;box-sizing:border-box}
.ob-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:14px;width:100%}
.ob-title{display:flex;align-items:center;gap:9px}
.ob-num{width:26px;height:26px;border-radius:7px;background:#e3e3e3;display:grid;place-items:center;font-size:11px;font-weight:750}
.ob-help{color:#616161;font-size:12px;margin:4px 0 0 35px}
.ob-mini{color:#616161;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;margin:14px 0 8px}
.ob-check{display:flex;gap:8px;flex-wrap:wrap}
.ob-check label{display:inline-flex;align-items:center;gap:7px;border:1px solid #e3e3e3;border-radius:8px;padding:7px 10px;background:#fff;font-size:12px;font-weight:600}
.ob-repeat{border:1px solid #e3e3e3;border-radius:10px;padding:14px;margin-bottom:10px;background:#fff;width:100%;box-sizing:border-box}
.ob-repeat.ob-locked{background:#fafafa;border-color:#dcdcdc}
.ob-repeat-h{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;width:100%}
.ob-badge{background:#e3e3e3;color:#616161;border-radius:6px;padding:2px 7px;font-size:10px;font-weight:800}
.ob-full-address{width:100%!important;min-width:100%!important;max-width:100%!important;box-sizing:border-box;resize:vertical}
.grid-fixed.wide-last > .field:last-child{grid-column:1 / -1; width:100%; max-width:none}
.grid-fixed.wide-last > .field:last-child textarea{width:100%!important;max-width:100%!important;min-width:100%!important}
.ob-inline{display:flex;gap:8px;align-items:center}
.ob-inline .inp{flex:1}
.ob-ver{min-height:16px;font-size:11px;font-weight:700;margin-top:4px}
.ob-ver.ok{color:#0c5132}.ob-ver.warn{color:#8a6116}.ob-ver.err{color:#8e1f0b}
.ob-gstin{text-transform:uppercase;letter-spacing:.6px;font-weight:700}
.ob-side{position:sticky;top:12px;display:grid;gap:12px}
.ob-sum-row{display:flex;justify-content:space-between;gap:10px;color:#616161;font-size:12px;margin:6px 0}
.ob-sum-row b{color:#303030}
.ob-prog{height:5px;background:#e3e3e3;border-radius:99px;overflow:hidden;margin:8px 0}
.ob-prog>span{display:block;height:100%;background:#303030;width:0}
.ob-tip{background:#fafafa;border:1px solid #e3e3e3;border-radius:10px;padding:10px;font-size:12px;color:#616161;margin-top:8px}
.ob-foot{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;padding-top:4px}
.ob-list-row{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:center;padding:10px 0;border-bottom:1px solid #eee}
.gst-panel{background:#fff;border-radius:12px;box-shadow:0 0 0 1px rgba(0,0,0,.06),0 1px 0 rgba(0,0,0,.05);padding:18px 20px;margin-bottom:14px}
.gst-panel h3{margin:0 0 4px;font-size:15px}
.gst-panel .sub{color:#616161;font-size:12px;margin-bottom:12px}
.gst-rule{height:1px;background:#e3e3e3;margin:10px 0 14px}
.gst-segbar{display:inline-flex;gap:3px;padding:3px;border-radius:8px;background:#f1f1f1;height:34px}
.gst-seg{border:0;background:transparent;border-radius:6px;padding:0 13px;color:#616161;font-weight:600;cursor:pointer}
.gst-seg.on{background:#fff;color:#303030;box-shadow:0 1px 0 rgba(0,0,0,.05)}
.gst-tog{border:1px solid #e3e3e3;border-radius:9px;padding:10px;background:#fff;min-height:70px;display:flex;gap:9px;align-items:flex-start}
.gst-sw{width:32px;height:19px;border:0;border-radius:99px;background:#c9c9c9;position:relative;flex:none;margin-top:1px;padding:0;cursor:pointer}
.gst-sw span{position:absolute;left:2px;top:2px;width:15px;height:15px;background:#fff;border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.25);transition:transform .16s}
.gst-sw.on{background:#29845a}.gst-sw.on span{transform:translateX(13px)}
.gst-tog strong{display:block;font-size:11.5px}.gst-tog small{display:block;color:#616161;font-size:10.5px;margin-top:2px}
.gst-status{display:flex;align-items:center;gap:10px;border:1px solid #e3e3e3;border-radius:9px;padding:8px 10px;background:#fff;color:#616161;font-size:11.5px}
.gst-pill{display:inline-flex;align-items:center;gap:6px;background:#cdfee1;color:#0c5132;border-radius:7px;padding:3px 8px;font-weight:650;font-size:11px}
.gst-preview{display:none;margin-top:14px;border:1px solid #e3e3e3;border-radius:10px;overflow:hidden}
.gst-preview.show{display:block}
.gst-preview-h{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;background:#f7f7f7;border-bottom:1px solid #e3e3e3;font-size:12px}
.gst-preview-b{display:grid;grid-template-columns:1fr 1fr}
.gst-col{padding:12px}.gst-col+.gst-col{border-left:1px solid #e3e3e3}
.gst-kv{display:grid;grid-template-columns:120px minmax(0,1fr);gap:5px 8px;font-size:11.5px}
.gst-kv dt{color:#616161}.gst-kv dd{margin:0;font-weight:600;overflow-wrap:anywhere}
.gst-raw{margin:0;max-height:220px;overflow:auto;background:#1e1e1e;color:#d4d4d4;padding:10px;border-radius:7px;font:11px/1.5 ui-monospace,Consolas,monospace;white-space:pre-wrap}
.gst-lock{margin-top:12px;padding:9px 11px;background:#fff2c2;color:#5e4200;border-radius:8px;font-size:11px}
.gst-val{font-size:10.5px;min-height:15px;margin-top:4px;font-weight:600}
.gst-val.ok{color:#0c5132}.gst-val.bad{color:#8e1f0b}.gst-val.warn{color:#8a6116}
@media(max-width:1100px){.ob-layout,.gst-preview-b{grid-template-columns:1fr}.ob-side{position:static}.gst-col+.gst-col{border-left:0;border-top:1px solid #e3e3e3}}


/* --- Next Style Block --- */

' +
    ':root{--ink:#172033;--muted:#667085;--line:#cfd6df;--soft:#f4f7fa;--brand:#173f67;--brand2:#eaf1f8}' +
    '*{box-sizing:border-box}body{font-family:Arial,Helvetica,sans-serif;font-size:10px;color:var(--ink);margin:0;padding:14px;background:#e9edf2}' +
    '.bar{display:flex;gap:8px;justify-content:flex-end;max-width:960px;margin:0 auto 10px;position:sticky;top:8px;z-index:2}.bar button{font:inherit;font-weight:700;padding:8px 17px;border:1px solid #b7c0cb;border-radius:6px;background:#fff;cursor:pointer}.bar .p{background:var(--brand);color:#fff;border-color:var(--brand)}' +
    '.sheet{max-width:960px;margin:0 auto;background:#fff;box-shadow:0 3px 18px rgba(16,24,40,.14);border-top:7px solid var(--brand);padding:0 22px 16px}' +
    '.mast{display:grid;grid-template-columns:1fr 260px;gap:22px;padding:17px 0 13px;border-bottom:2px solid var(--brand)}.company h1{font-size:18px;letter-spacing:.3px;margin:0 0 5px;color:var(--brand)}.company p{margin:1px 0;line-height:1.35;color:#344054}.company .gst{display:inline-block;margin-top:5px;padding:3px 7px;border-radius:4px;background:var(--brand2);font-weight:700}.pohead{text-align:right}.pohead .eyebrow{font-size:9px;letter-spacing:1.4px;text-transform:uppercase;color:var(--muted)}.pohead h2{font-size:22px;letter-spacing:1px;margin:3px 0;color:var(--brand)}.pohead .pono{font-size:15px;font-weight:800}.status{display:inline-block;margin-top:5px;padding:3px 9px;border:1px solid #99a8b8;border-radius:12px;font-size:9px;font-weight:700;text-transform:uppercase}' +
    '.meta{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);border-radius:6px;overflow:hidden;margin:13px 0}.meta>div{padding:7px 9px;min-height:43px}.meta>div+div{border-left:1px solid var(--line)}.meta span,.label{display:block;font-size:8px;text-transform:uppercase;letter-spacing:.55px;color:var(--muted);margin-bottom:3px}.meta b{font-size:10.5px}' +
    '.parties{display:grid;grid-template-columns:1.15fr 1fr 1fr;gap:8px;margin-bottom:12px}.card{border:1px solid var(--line);border-radius:6px;overflow:hidden}.card h3{font-size:8.5px;text-transform:uppercase;letter-spacing:.7px;margin:0;padding:5px 8px;background:var(--brand2);color:var(--brand)}.card .body{padding:7px 9px;min-height:88px}.card b{font-size:10.5px}.card p{margin:3px 0;line-height:1.35;color:#344054}.mini{margin-top:5px;font-size:8.8px;line-height:1.45}.mini strong{color:var(--muted);font-weight:600}' +
    '.commercial{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:12px}.term{border-left:3px solid #91a7bd;background:var(--soft);padding:6px 8px;min-height:42px}.term b{display:block;margin-top:2px}' +
    'table{border-collapse:collapse;width:100%}.items{margin-bottom:10px}.items th{background:var(--brand);color:#fff;font-size:8.5px;letter-spacing:.25px;padding:6px 5px;text-align:left;border:1px solid var(--brand)}.items td{border:1px solid var(--line);padding:6px 5px;vertical-align:top}.items tbody tr:nth-child(even){background:#fafbfd}.items .rt,.rt{text-align:right}.items .ctr,.ctr{text-align:center}.items .sub{font-size:8.5px;color:var(--muted);margin-top:2px}.tot td{font-weight:800;background:var(--brand2);border-color:#aab9c8}' +
    '.finance{display:grid;grid-template-columns:1.55fr .75fr;gap:10px;margin:10px 0}.taxbox,.totalbox{border:1px solid var(--line);border-radius:6px;overflow:hidden}.boxhead{padding:5px 8px;background:var(--soft);font-weight:800;text-transform:uppercase;font-size:8.5px;letter-spacing:.5px}.hsn th,.hsn td{border:1px solid var(--line);padding:4px;font-size:8.3px}.hsn th{background:#f8fafc}.sum td{padding:4px 8px}.sum .lbl{text-align:right;color:#475467}.sum .amt{text-align:right;width:95px;font-weight:700}.sum .grand td{background:var(--brand);color:#fff;font-size:12px;font-weight:800;padding:7px 8px}' +
    '.words{border:1px solid #aab9c8;background:var(--brand2);padding:6px 9px;border-radius:5px;font-size:9px;font-weight:700;margin-bottom:8px}.remark{padding:6px 9px;border-left:3px solid #d5a021;background:#fff9e8;margin-bottom:8px}' +
    '.terms{border:1px solid var(--line);border-radius:6px;padding:8px 10px;margin-top:8px}.terms h4,.terms h2,.terms h3{margin:0 0 5px;font-size:10px;color:var(--brand)}.terms ol,.terms ul{columns:2;column-gap:26px;margin:0;padding-left:16px}.terms li{break-inside:avoid;margin-bottom:3px;line-height:1.3;font-size:8.5px}.terms p{margin:0 0 4px;font-size:8.5px}.terms a{color:inherit}' +
    '.sign{display:grid;grid-template-columns:1fr 1fr 1fr;margin-top:12px;border:1px solid var(--line);border-radius:6px;overflow:hidden}.sign>div{min-height:76px;padding:7px 9px;text-align:center}.sign>div+div{border-left:1px solid var(--line)}.sign b{font-size:9px}.sign span{display:block;height:42px;border-bottom:1px solid #98a2b3;margin-bottom:4px}.foot{text-align:center;color:var(--muted);font-size:8px;margin-top:9px;letter-spacing:.35px}' +
    '@page{size:A4 portrait;margin:7mm}@media print{body{padding:0;background:#fff}.bar{display:none}.sheet{max-width:none;box-shadow:none;padding:0 8mm 5mm;border-top-width:5px}.items tr,.finance,.sign,.card{break-inside:avoid}}' +
    '

/* --- Next Style Block --- */

<fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><color rgb="FFFFFFFF"/><sz val="11"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF303030"/></patternFill></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf/></cellStyleXfs><cellXfs count="2"><xf/><xf fontId="1" fillId="2" applyFont="1" applyFill="1"/></cellXfs></styleSheet>';
  downloadBytes(xzip(files),'All_Masters_'+today()+'.xlsx');toast('All Masters exported to Excel','green');
}

function masterExportData(k){
  if(k==='usermanagement')return {name:'User Management',headers:['User ID','Full Name','Username','Role','Status','Visible Modules','Permission Model'],rows:(db.users||[]).map(function(u){const mods=u.role==='Admin'?'All modules':Object.keys((u.permissions&&u.permissions.modules)||{}).filter(function(m){return u.permissions.modules[m].view;}).map(function(m){return (MODULES[m]||{}).label||m;}).join(', ')||((u.modules||[]).join(', '));return [u.id||'',u.name||'',u.username||'',u.role||'',u.status||'Active',mods,u.permissions?'Detailed per module':'Legacy role access'];})};
  if(k==='styles')return {name:'Style Master',headers:['ID','Internal ID','Portal ID','Description','Status'],rows:(db.masters.styles||[]).map(function(x){return[x.id||'',x.internal||'',x.portal||'',x.desc||'',x.active===false?'Inactive':'Active'];})};
  if(k==='items')return {name:'Item Master',headers:['ID','Item Name','Category','Classification','Quality','Description','UOM','Width','HSN','GST %','Status'],rows:(db.masters.items||[]).map(function(x){return[x.code||'',x.name||'',x.category||'',x.classification||'',x.quality||'',x.description||'',x.unit||'',x.width||'',x.hsn||'',x.gst==null?'':x.gst,x.active===false?'Inactive':'Active'];})};
  if(k==='partyrecords')return {name:'Party onboarding',headers:['ID','Legal Name','Short Name','Business Type','GST No','PAN','City','State','Status'],rows:(db.masters.parties||[]).map(function(x){return[x.code||'',x.legalName||'',x.shortName||'',partyTypes(x).join(', '),x.gstNo||'',x.pan||'',x.city||'',x.state||'',x.status||'Active'];})};
  if(k==='loginpage'){const c=db.masters.loginSettings||{};return {name:'Login Page',headers:['Slide ID','Slide Name','Fit','Position','Status','Interval Seconds'],rows:(c.slides||[]).map(function(x){return[x.id,x.name,x.fit,x.position,x.active===false?'Inactive':'Active',c.interval||6];})};}
  if(k==='menumaster'){const rows=[];mainMenuEntries().forEach(function(x,i){rows.push(['Main Menu',x.section,x.l,x.target||x.k,i+1]);});masterMenuEntries().forEach(function(x,i){rows.push(['Masters Sub Menu',x.section,x.l,x.target||x.k,i+1]);});return {name:'Menu Management',headers:['Menu Area','Parent Menu','Display Label','Module or Master','Order'],rows:rows};}
  const d=mastersDefs().find(function(x){return x.k===k;}),rows=(db.masters[k]||[]).map(function(v){return[masterIdFor(k,v),String(v),masterIsActive(k,v)?'Active':'Inactive',MASTER_PARENT[k]?parentOf(k,v):'',k==='warehouses'?warehouseAddr(v):''];});return {name:d?d.title:k,headers:['ID','Master Value','Status','Parent','Additional Details'],rows:rows};
}
function exportSingleMasterExcel(k){const sh=masterExportData(k),sheetName=String(sh.name||'Master').replace(/[\\\/?*\[\]:]/g,' ').slice(0,31),files={};files['[Content_Types].xml']='<?xml version="1.0" encoding="UTF-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>';files['_rels/.rels']='<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>';files['xl/workbook.xml']='<?xml version="1.0" encoding="UTF-8"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="'+xesc(sheetName)+'" sheetId="1" r:id="rId1"/></sheets></workbook>';files['xl/_rels/workbook.xml.rels']='<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>';files['xl/styles.xml']='<?xml version="1.0" encoding="UTF-8"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><color rgb="FFFFFFFF"/><sz val="11"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF303030"/></patternFill></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf/></cellStyleXfs><cellXfs count="2"><xf/><xf fontId="1" fillId="2" applyFont="1" applyFill="1"/></cellXfs></styleSheet>';files['xl/worksheets/sheet1.xml']=xsheet(sh.headers,sh.rows,[]);downloadBytes(xzip(files),String(sh.name).replace(/[^A-Za-z0-9_-]/g,'_')+'_'+today()+'.xlsx');toast(sh.name+' exported to Excel','green');}

const ITEM_XLSX_HEADERS=['Category','Classification','Item Name','Quality','Description','UOM','Width / Dimensions','HSN Code','GST %','Status'];
let ITEM_IMPORT_ERRORS=[];
function xesc(v){return String(v==null?'':v).replace(/[&<>"']/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&apos;'}[c];});}
function xcol(n){let s='';while(n){n--;s=String.fromCharCode(65+n%26)+s;n=Math.floor(n/26);}return s;}
function xcell(ref,v,style){return '<c r="'+ref+'" t="inlineStr"'+(style?' s="'+style+'"':'')+'><is><t xml:space="preserve">'+xesc(v)+'</t></is></c>';}
function xsheet(headers,rows,validations,headerStyles){
  let xml='<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetViews><sheetView workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews><cols>';
  headers.forEach(function(h,i){const w=Math.max(12,Math.min(34,String(h).length+5));xml+='<col min="'+(i+1)+'" max="'+(i+1)+'" width="'+w+'" customWidth="1"/>';});
  xml+='</cols><sheetData><row r="1">'+headers.map(function(h,i){return xcell(xcol(i+1)+'1',h,headerStyles&&headerStyles[i]!=null?headerStyles[i]:1);}).join('')+'</row>';
  rows.forEach(function(row,ri){xml+='<row r="'+(ri+2)+'">'+headers.map(function(_,ci){return xcell(xcol(ci+1)+(ri+2),row[ci]==null?'':row[ci],0);}).join('')+'</row>';});
  xml+='</sheetData><autoFilter ref="A1:'+xcol(headers.length)+'1"/>';
  if(validations&&validations.length){xml+='<dataValidations count="'+validations.length+'">'+validations.map(function(v){return '<dataValidation type="list" allowBlank="1" showErrorMessage="1" errorTitle="Invalid master value" error="Select a value from the latest Master list" sqref="'+v.range+'"><formula1>'+xesc(v.formula||v.name)+'</formula1></dataValidation>';}).join('')+'</dataValidations>';}
  return xml+'</worksheet>';
}
function xcrc(bytes){let c=0xffffffff;for(let i=0;i<bytes.length;i++){c^=bytes[i];for(let k=0;k<8;k++)c=(c>>>1)^((c&1)?0xedb88320:0);}return (c^0xffffffff)>>>0;}
function xu16(a,o,v){a[o]=v&255;a[o+1]=(v>>>8)&255;}function xu32(a,o,v){xu16(a,o,v);xu16(a,o+2,v>>>16);}
function xconcat(parts){const n=parts.reduce(function(a,p){return a+p.length;},0),out=new Uint8Array(n);let o=0;parts.forEach(function(p){out.set(p,o);o+=p.length;});return out;}
function xzip(files){
  const enc=new TextEncoder(),locals=[],centrals=[];let offset=0;
  Object.keys(files).forEach(function(name){const nb=enc.encode(name),data=enc.encode(files[name]),crc=xcrc(data),lh=new Uint8Array(30+nb.length);xu32(lh,0,0x04034b50);xu16(lh,4,20);xu16(lh,6,0);xu16(lh,8,0);xu32(lh,14,crc);xu32(lh,18,data.length);xu32(lh,22,data.length);xu16(lh,26,nb.length);lh.set(nb,30);locals.push(lh,data);
    const ch=new Uint8Array(46+nb.length);xu32(ch,0,0x02014b50);xu16(ch,4,20);xu16(ch,6,20);xu16(ch,8,0);xu16(ch,10,0);xu32(ch,16,crc);xu32(ch,20,data.length);xu32(ch,24,data.length);xu16(ch,28,nb.length);xu32(ch,42,offset);ch.set(nb,46);centrals.push(ch);offset+=lh.length+data.length;});
  const central=xconcat(centrals),end=new Uint8Array(22);xu32(end,0,0x06054b50);xu16(end,8,centrals.length);xu16(end,10,centrals.length);xu32(end,12,central.length);xu32(end,16,offset);return xconcat(locals.concat([central,end]));
}
function activeMasterRows(k){return (db.masters[k]||[]).filter(function(v){return masterIsActive(k,v);}).slice().reverse();}
function itemWorkbookRows(){
  const rows=[];for(let i=0;i<50;i++)rows.push(new Array(ITEM_XLSX_HEADERS.length).fill(''));return rows;
}
function xlsxRangeName(v){return 'CAT_'+String(v||'').toUpperCase().replace(/[^A-Z0-9_]/g,'_').replace(/_+/g,'_').replace(/^_|_$/g,'');}
function buildItemXlsx(rows,errorMode){
  const cats=activeMasterRows('categories'),allClass=activeMasterRows('classifications');
  const masters=[cats,allClass,activeMasterRows('qualities'),activeMasterRows('uoms'),activeMasterRows('gstRates'),['Active','Inactive']];
  const mh=['Categories','Classifications','Qualities','UOMs','GSTRates','Statuses'];
  const dependent=[];
  cats.forEach(function(cat){
    const values=allClass.filter(function(cls){return String(parentOf('classifications',cls)||'').trim().toLowerCase()===String(cat).trim().toLowerCase();});
    const rangeName=xlsxRangeName(cat);masters.push(values);mh.push('Classification — '+cat);dependent.push({cat:cat,name:rangeName,index:masters.length-1});
  });
  const max=Math.max.apply(null,masters.map(function(a){return Math.max(1,a.length);}));const mr=[];for(let i=0;i<max;i++)mr.push(masters.map(function(a){return a[i]||'';}));
  const headers=errorMode?ITEM_XLSX_HEADERS.concat(['Import Error']):ITEM_XLSX_HEADERS;
  const classFormula='INDIRECT("CAT_"&SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(UPPER($A2)," ","_"),"-","_"),"/","_"),"&","_"))';
  const vals=[{range:'A2:A500',name:'Categories'},{range:'B2:B500',formula:classFormula},{range:'D2:D500',name:'Qualities'},{range:'F2:F500',name:'UOMs'},{range:'I2:I500',name:'GSTRates'},{range:'J2:J500',name:'Statuses'}];
  let names=['Categories','Classifications','Qualities','UOMs','GSTRates','Statuses'].map(function(n,i){const last=Math.max(2,masters[i].length+1);return '<definedName name="'+n+'">Masters!$'+xcol(i+1)+'$2:$'+xcol(i+1)+'$'+last+'</definedName>';}).join('');
  dependent.forEach(function(d){const last=Math.max(2,masters[d.index].length+1);names+='<definedName name="'+d.name+'">Masters!$'+xcol(d.index+1)+'$2:$'+xcol(d.index+1)+'$'+last+'</definedName>';});
  const files={'[Content_Types].xml':'<?xml version="1.0" encoding="UTF-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>',
  '_rels/.rels':'<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>',
  'xl/workbook.xml':'<?xml version="1.0" encoding="UTF-8"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="Item Template" sheetId="1" r:id="rId1"/><sheet name="Masters" sheetId="2" state="hidden" r:id="rId2"/></sheets><definedNames>'+names+'</definedNames></workbook>',
  'xl/_rels/workbook.xml.rels':'<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>',
  'xl/styles.xml':'<?xml version="1.0" encoding="UTF-8"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><color rgb="FFFFFFFF"/><sz val="11"/><name val="Calibri"/></font></fonts><fills count="4"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF303030"/><bgColor indexed="64"/></patternFill></fill><fill><patternFill patternType="solid"><fgColor rgb="FFFFE699"/><bgColor indexed="64"/></patternFill></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf/></cellStyleXfs><cellXfs count="3"><xf fontId="0" fillId="0" borderId="0"/><xf fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1"/><xf fontId="0" fillId="3" borderId="0" applyFill="1"/></cellXfs></styleSheet>',
  'xl/worksheets/sheet1.xml':xsheet(headers,rows,vals,headers.map(function(_,i){return [2,4,6,7].indexOf(i)>-1||i>=ITEM_XLSX_HEADERS.length?2:1;})),'xl/worksheets/sheet2.xml':xsheet(mh,mr,[])};return xzip(files);
}
function downloadBytes(bytes,name){const blob=new Blob([bytes],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}),a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(function(){URL.revokeObjectURL(a.href);},1000);}
function exportItemExcelTemplate(){downloadBytes(buildItemXlsx(itemWorkbookRows(),false),'Item_Master_Template_'+today()+'.xlsx');toast('Excel template exported — yellow headers require manual input','green');}
function triggerItemExcelImport(){if(!userCanMaster('items','create'))return toast('Item import access denied','red');const el=$('item-excel-file');if(el){el.value='';el.click();}}
async function unzipEntry(buf,want){
  const a=new Uint8Array(buf),dv=new DataView(buf);let eocd=-1;for(let i=a.length-22;i>=0&&i>a.length-66000;i--){if(dv.getUint32(i,true)===0x06054b50){eocd=i;break;}}if(eocd<0)throw new Error('Invalid XLSX file');let count=dv.getUint16(eocd+10,true),pos=dv.getUint32(eocd+16,true);
  for(let z=0;z<count;z++){if(dv.getUint32(pos,true)!==0x02014b50)break;const method=dv.getUint16(pos+10,true),cs=dv.getUint32(pos+20,true),nl=dv.getUint16(pos+28,true),el=dv.getUint16(pos+30,true),cl=dv.getUint16(pos+32,true),off=dv.getUint32(pos+42,true),name=new TextDecoder().decode(a.slice(pos+46,pos+46+nl));if(name===want){const lnl=dv.getUint16(off+26,true),lel=dv.getUint16(off+28,true),data=a.slice(off+30+lnl+lel,off+30+lnl+lel+cs);if(method===0)return new TextDecoder().decode(data);if(method===8&&typeof DecompressionStream!=='undefined'){const stream=new Blob([data]).stream().pipeThrough(new DecompressionStream('deflate-raw'));return new TextDecoder().decode(await new Response(stream).arrayBuffer());}throw new Error('Compressed XLSX is not supported by this browser');}pos+=46+nl+el+cl;}throw new Error('Item Template sheet not found');
}
function parseShared(xml){if(!xml)return[];const d=new DOMParser().parseFromString(xml,'application/xml');return Array.from(d.getElementsByTagName('si')).map(function(si){return Array.from(si.getElementsByTagName('t')).map(function(t){return t.textContent;}).join('');});}
async function readItemXlsx(file){const buf=await file.arrayBuffer();let shared='';try{shared=await unzipEntry(buf,'xl/sharedStrings.xml');}catch(e){}const ss=parseShared(shared),xml=await unzipEntry(buf,'xl/worksheets/sheet1.xml'),doc=new DOMParser().parseFromString(xml,'application/xml'),out=[];Array.from(doc.getElementsByTagName('row')).forEach(function(r,ri){if(ri===0)return;const row=new Array(ITEM_XLSX_HEADERS.length).fill('');Array.from(r.getElementsByTagName('c')).forEach(function(c){const ref=c.getAttribute('r')||'',letters=(/^[A-Z]+/.exec(ref)||[''])[0];let col=0;for(const ch of letters)col=col*26+ch.charCodeAt(0)-64;col--;if(col<0||col>=row.length)return;const t=c.getAttribute('t');let v='';if(t==='inlineStr'){const n=c.getElementsByTagName('t')[0];v=n?n.textContent:'';}else{const n=c.getElementsByTagName('v')[0];v=n?n.textContent:'';if(t==='s')v=ss[+v]||'';}row[col]=String(v).trim();});if(row.some(Boolean))out.push(row);});return out;}
function itemMasterAllowed(k,v){
  if(k==='gstRates'){
    const n=Number(String(v==null?'':v).trim());if(!isFinite(n))return false;
    return (db.masters[k]||[]).some(function(x){return masterIsActive(k,x)&&Math.abs(Number(x)-n)<0.000001;});
  }
  return (db.masters[k]||[]).some(function(x){return masterIsActive(k,x)&&String(x).trim().toLowerCase()===String(v).trim().toLowerCase();});
}
async function importItemExcel(inp){
  const file=inp.files&&inp.files[0];if(!file)return;let rows;try{rows=await readItemXlsx(file);}catch(e){toast('Could not read Excel file: '+e.message,'red');return;}
  const errors=[],valid=[],seen={};
  rows.forEach(function(r,idx){
    const err=[],cat=r[0],cls=r[1],name=r[2],quality=r[3],desc=r[4],uom=r[5],width=r[6],hsn=r[7],gst=r[8],status=r[9]||'Active';
    if(!name)err.push('Item Name is required');
    if(!cat||!itemMasterAllowed('categories',cat))err.push('Invalid/inactive Category');
    if(!cls||!itemMasterAllowed('classifications',cls))err.push('Invalid/inactive Classification');
    if(cat&&cls){const allowed=childValues('classifications',cat).some(function(v){return masterIsActive('classifications',v)&&String(v).trim().toLowerCase()===String(cls).trim().toLowerCase();});if(!allowed)err.push('Classification does not belong to selected Category');}
    if(quality&&!itemMasterAllowed('qualities',quality))err.push('Invalid/inactive Quality');
    if(!uom||!itemMasterAllowed('uoms',uom))err.push('Invalid/inactive UOM');
    if(!/^\d+$/.test(hsn))err.push('HSN Code must contain numbers only');
    if(!itemMasterAllowed('gstRates',gst))err.push('Invalid/inactive GST %');
    if(!/^(active|deactive|inactive)$/i.test(status))err.push('Status must be Active or Inactive');
    const key=String(name||'').toLowerCase();if(key&&seen[key])err.push('Duplicate Item Name in file');if(key)seen[key]=1;
    const clash=(db.masters.items||[]).find(function(x){return String(x.name).toLowerCase()===key;});if(clash)err.push('Item Name already exists as '+clash.code);
    if(err.length)errors.push(r.concat([err.join('; ')]));
    else valid.push({name:name,category:cat,classification:cls,quality:quality,description:desc,unit:uom,width:width,hsn:hsn,gst:pf(gst),active:/^active$/i.test(status)});
  });
  valid.forEach(function(data){db.seq.item=pf(db.seq.item)+1;data.id=uid();data.code='ITM-'+String(db.seq.item).padStart(4,'0');data.image='';db.masters.items.push(data);});
  if(valid.length){saveDB();renderMasters();}
  if(errors.length){
    ITEM_IMPORT_ERRORS=errors;
    openDialog({title:'Item import completed with errors',message:'<b>'+valid.length+'</b> valid row(s) were imported successfully.<br><b>'+errors.length+'</b> invalid row(s) were rejected.',note:'Download the error file containing only rejected rows, correct the Import Error column, and import that file again.',confirmText:'Download error file',cancelText:'Close',onConfirm:function(){downloadItemImportErrors();}});
    if(valid.length)toast(valid.length+' valid item row(s) imported; '+errors.length+' rejected','green');
    return;
  }
  if(valid.length)toast(valid.length+' new item row(s) imported successfully','green');
  else toast('No item rows found in the Excel file','red');
}
function downloadItemImportErrors(){if(!ITEM_IMPORT_ERRORS.length)return;downloadBytes(buildItemXlsx(ITEM_IMPORT_ERRORS,true),'Item_Master_Import_Errors_'+today()+'.xlsx');}

/* ---------------- Item Master (full page) ---------------- */
let imEditId = null;
let imState = { q: '', page: 1, size: 10 };
function renderItemMaster() {
  setTimeout(dpSyncAll, 0);
  $('content').innerHTML =
    '<div class="entry-page" style="max-width:1560px">' + itemMasterFormHtml(imEditId ? db.masters.items.find(i => i.id === imEditId) : null) + '</div>' +
    '<div class="section-title" style="margin-top:26px">📦 All Items <span class="c">' + db.masters.items.length + '</span></div>' +
    '<div class="filter-bar"><input class="inp" id="im-search" placeholder="🔍 Search items…" value="' + esc(imState.q) + '" oninput="imSearch(this.value)" style="max-width:320px"></div>' +
    itemMasterListHtml();
  afterRenderItemForm();
}
function imSearch(v) { imState.q = v; imState.page = 1; const b=$('im-clear-filters');if(b)b.disabled=!String(v||'').trim(); renderItemListOnly(); }
function renderItemListOnly() { const el = document.getElementById('im-list'); if (el) { el.outerHTML = itemMasterListHtml(); setTimeout(function(){attachExcelHeaderFilters($('m-body') || document);},0); } }
function itemMasterListHtml() {
  const q = imState.q.toLowerCase().trim();
  let rows = db.masters.items.slice();
  if (q) rows = rows.filter(it => (it.name + ' ' + it.code + ' ' + (it.category||'') + ' ' + (it.hsn||'')).toLowerCase().includes(q));
  rows.sort((a, b) => String(b.code || b.id || '').localeCompare(String(a.code || a.id || ''), undefined, { numeric:true, sensitivity:'base' }));
  const pg = slicePage(rows, imState);
  if (!pg.total) return '<div id="im-list" class="panel empty"><div class="big">📦</div><p>No items found.</p></div>';
  return '<div id="im-list"><div class="table-wrap"><table class="recent"><thead><tr><th class="srcol">Sr. No.</th><th>Image</th><th>Item</th><th>ID</th><th>Category</th><th>Classification</th><th>UOM</th><th>HSN</th><th class="num">GST %</th><th>Status</th><th class="num no-print">Actions</th></tr></thead><tbody data-sr-start="' + pg.from + '">' +
    pg.rows.map((it, i) =>
      '<tr>' +
        '<td class="srcol">' + (pg.from + i) + '</td>' +
        '<td class="im-image-col">' + (it.image ? '<button type="button" class="im-list-image" title="View image" onclick="event.stopPropagation();viewItemImage(\'' + it.id + '\')"><img src="' + it.image + '" alt="' + esc(it.name) + '"></button>' : '<span class="im-no-image">—</span>') + '</td>' +
        '<td><b>' + esc(it.name) + '</b>' + (it.colorName ? '<div class="sub-line">🎨 ' + esc(it.colorName) + '</div>' : '') + '</td>' +
        '<td>' + esc(it.code) + '</td>' +
        '<td>' + esc(it.category || '—') + '</td>' +
        '<td>' + esc(it.classification || '—') + '</td>' +
        '<td>' + esc(it.unit || '—') + '</td>' +
        '<td>' + esc(it.hsn || '—') + '</td>' +
        '<td class="num">' + ((it.gst !== '' && it.gst != null) ? fmtNum(it.gst) + '%' : '—') + '</td>' +
        '<td onclick="event.stopPropagation()"><button type="button" class="sw master-status-sw' + (it.active !== false ? ' on' : '') + '" onclick="toggleItemRecordStatus(\'' + it.id + '\')"><span class="sw-knob"></span></button> <span class="master-status-label ' + (it.active !== false ? 'on">Active' : 'off">Inactive') + '</span></td>' +
        '<td class="actions-cell no-print">' +
          '<button class="btn-icon" title="Edit" onclick="editItemMaster(\'' + it.id + '\')">' + ic('pencil',15) + '</button>' +
          ((itemUsedIn(it.name).length)
            ? '<button class="btn-icon" disabled title="Cannot delete — used in ' + esc(itemUsedIn(it.name).join(', ')) + '">' + ic('lock',15) + '</button>'
            : '<button class="btn-icon danger" title="Delete" onclick="deleteItemMasterRow(\'' + it.id + '\')">' + ic('trash',15) + '</button>') +
        '</td>' +
      '</tr>'
    ).join('') + '</tbody></table></div>' + pagerBar(pg, 'item', 'items') + '</div>';
}
function closeItemImageViewer(){const el=$('itemImageViewer');if(el)el.remove();}
function viewItemImage(id){const it=(db.masters.items||[]).find(function(x){return x.id===id;});if(!it||!it.image)return;closeItemImageViewer();const el=document.createElement('div');el.id='itemImageViewer';el.className='item-image-viewer';el.innerHTML='<div class="item-image-viewer-card" onclick="event.stopPropagation()"><div class="item-image-viewer-head"><b>'+esc(it.name)+(it.code?' · '+esc(it.code):'')+'</b><button class="btn-icon" onclick="closeItemImageViewer()">'+ic('x',16)+'</button></div><div class="item-image-viewer-body"><img src="'+it.image+'" alt="'+esc(it.name)+'"></div></div>';el.onclick=closeItemImageViewer;document.body.appendChild(el);}
function toggleItemRecordStatus(id){const it=db.masters.items.find(function(x){return x.id===id;});if(!it)return;const old=it.active!==false;it.active=!old;saveDB();toast(it.name+' marked '+(it.active?'Active':'Inactive'),'green');if(cur==='masters')renderMasters();else renderItemMaster();}
function editItemMaster(id) {
  if (cur === 'masters' && mTab === 'items') { openItemMasterPopup(id); return; }
  imEditId = id; renderItemMaster(); window.scrollTo({ top: 0, behavior: 'smooth' });
}
function cancelEditItem() { imEditId = null; renderItemMaster(); }
function deleteItemMasterRow(id) {
  const it = db.masters.items.find(i => i.id === id); if (!it) return;
  // v5: deletion is allowed even when the item is in use. The confirmation
  // dialog still shows a warning so the user knows downstream entries will
  // have a dangling reference.
  const used = itemUsedIn(it.name);
  const warnNote = used.length
    ? '<div style="background:#fff5ea;border-left:4px solid #ffb800;color:#5e4200"><b>Item in use:</b> ' + esc(used.join(', ')) + ' reference this item and will have a dangling reference after deletion.</div>'
    : '';
  confirmDelete({ kind: 'item', name: it.name + (it.code ? ' (' + it.code + ')' : ''), bodyHtml: warnNote, confirmText: 'Delete permanently' }, function () {
    db.masters.items = db.masters.items.filter(i => i.id !== id);
    if (imEditId === id) imEditId = null;
    saveDB(); toast('Item removed');
    if (cur === 'masters' && mTab === 'items') renderMasters(); else renderItemMaster();
  });
}
function itemMasterFormHtml(it, popup) {
  it = it || {};
  return '<div class="sec">' + (it.id ? 'Edit Item — ' + esc(it.name) : 'New Item') + '</div>' +
  '<div class="grid-auto im-equal-grid">' +
    field('Category *', cbox('im-category', { opts: db.masters.categories, value: it.category || '', f:'category', placeholder:'Select Category', addMaster:'categories', addText:'Item Categories', free:true, onSet: function (v) { imCategoryChanged(v); } })) +
    field('Classification *', cbox('im-classification', { opts: childValues('classifications', it.category), value: it.classification || '', f:'classification', placeholder:'-Classification-', free:true })) +
    field('Item Name *', '<input class="inp" id="im-name" value="' + esc(it.name || '') + '" placeholder="Item name">') +
    field('Quality', cbox('im-quality', { opts: db.masters.qualities, value: it.quality || '', f:'quality', placeholder:'--Quality--', addMaster:'qualities', addText:'Quality Grades', free:true })) +

    field('Description / Count Construction', '<input class="inp" id="im-desc" value="' + esc(it.description || '') + '" placeholder="e.g. 30s combed cotton">') +
    field('UOM *', cbox('im-uom', { opts: db.masters.uoms, value: it.unit || '', f:'unit', placeholder:'--UOM--', addMaster:'uoms', addText:'UOM', free:false })) +
    field('Width / Dimensions', '<input class="inp" id="im-width" value="' + esc(it.width || '') + '" placeholder="e.g. 44 inch">') +
    field('HSN Code *', '<input class="inp" id="im-hsn" data-numeric maxlength="8" value="' + esc(it.hsn || '') + '" placeholder="e.g. 52081290" inputmode="numeric">') +
    field('GST % *', cbox('im-gst-cb', { opts: db.masters.gstRates, value: (it.gst === '' || it.gst == null) ? '' : String(it.gst), f:'gst', placeholder:'--GST %--', addMaster:'gstRates', addText:'GST %', free:false })) +

    field('Item Image', '<div class="img-upload-row"><label class="btn btn-ghost btn-sm file-btn">Choose file<input type="file" accept="image/*" id="im-image-file" onchange="onItemImageFile(this)"></label><span id="im-image-name" class="sub-line">' + (it.image ? 'Image attached' : 'No file chosen') + '</span>' + (it.image ? '<img src="' + it.image + '" id="im-image-preview" class="im-thumb-lg">' : '<img id="im-image-preview" class="im-thumb-lg" style="display:none">') + '<button type="button" class="btn-icon danger" id="im-image-delete" title="Delete image" onclick="clearItemImage()" style="' + (it.image ? 'display:inline-flex' : 'display:none') + '">' + ic('trash',14) + '</button></div>') +
  '</div>' +
  (popup ? '' : '<div style="margin-top:8px;display:flex;gap:10px">' +
    '<button class="btn btn-primary" onclick="saveItemMasterForm()">💾 ' + (it.id ? 'Update Item' : 'Submit') + '</button>' +
    (it.id ? '<button class="btn btn-ghost" onclick="cancelEditItem()">Cancel Edit</button>' : '') +
  '</div>');
}
let imImageData = '';
function toggleItemActive() {
  const val = $('im-active-value'), sw = $('im-active-switch'), lab = $('im-active-label');
  if (!val || !sw) return;
  const on = val.value !== '1';
  val.value = on ? '1' : '0';
  sw.classList.toggle('on', on);
  sw.setAttribute('aria-pressed', String(on));
  if (lab) lab.textContent = on ? 'Active' : 'Inactive';
}
function clearItemImage(){imImageData='';const prev=$('im-image-preview'),name=$('im-image-name'),del=$('im-image-delete'),file=$('im-image-file');if(prev){prev.removeAttribute('src');prev.style.display='none';}if(name)name.textContent='No file chosen';if(del)del.style.display='none';if(file)file.value='';toast('Item image removed — save the Item to apply','green');}
function onItemImageFile(inp) {
  const f = inp.files && inp.files[0]; if (!f) return;
  $('im-image-name').textContent = f.name;
  const reader = new FileReader();
  reader.onload = e => {
    const img = new Image();
    img.onload = () => {
      const max = 500; let w = img.width, h = img.height;
      if (w > max) { h = Math.round(h * max / w); w = max; }
      const cv = document.createElement('canvas'); cv.width = w; cv.height = h;
      cv.getContext('2d').drawImage(img, 0, 0, w, h);
      imImageData = cv.toDataURL('image/jpeg', 0.8);
      const prev = $('im-image-preview'); if (prev) { prev.src = imImageData; prev.style.display = 'inline-block'; } const del=$('im-image-delete');if(del)del.style.display='inline-flex';
    };
    img.src = e.target.result;
  };
  reader.readAsDataURL(f);
}
function afterRenderItemForm() {
  imImageData = imEditId ? ((db.masters.items.find(i => i.id === imEditId) || {}).image || '') : '';
}
function imCategoryChanged(cat) {
  const c = CBOX['im-classification']; if (!c) return;
  c.opts = childValues('classifications', cat).map(function (v) { return { v: v, l: v }; });
  c.val = ''; c.typed = false;
  const inp = qs('[data-cb-input="im-classification"]'); if (inp) inp.value = '';
  renderCboxList('im-classification');
}
function saveItemMasterForm() {
  clearReqMarks();
  if (!cboxGet('im-category')) return reqBad('im-category', 'Category is required');
  if (!cboxGet('im-classification')) return reqBad('im-classification', 'Classification is required');
  const name = ($('im-name').value || '').trim();
  if (!name) return reqBad('im-name', 'Item Name is required');
  if (!cboxGet('im-uom')) return reqBad('im-uom', 'UOM is required');
  const hsn = (($('im-hsn') || {}).value || '').trim();
  if (!hsn) return reqBad('im-hsn', 'HSN Code is required');
  if (!/^\d+$/.test(hsn)) return reqBad('im-hsn', 'HSN Code must be numbers only');
  if (!cboxGet('im-gst-cb') && cboxGet('im-gst-cb') !== '0') return reqBad('im-gst-cb', 'GST % is required');
  const prev = imEditId ? (db.masters.items.find(i => i.id === imEditId) || {}) : {};
  const activeEl = $('im-active-value');
  const activeVal = activeEl ? activeEl.value : (prev.active === false ? '0' : '1');
  const gst = pf(cboxGet('im-gst-cb'));
  const data = {
    name, category: cboxGet('im-category'), classification: cboxGet('im-classification'),
    materialType: prev.materialType || '',
    quality: cboxGet('im-quality'), description: $('im-desc').value.trim(),
    unit: cboxGet('im-uom'), width: $('im-width').value.trim(), hsn: $('im-hsn').value.trim(),
    gst: gst, image: imImageData,
    active: activeVal === '1',
    /* retired fields — preserved so existing records / exports stay intact */
    hasColor: !!prev.hasColor, sizes: Array.isArray(prev.sizes) ? prev.sizes.slice() : [],
    colorName: prev.colorName || '', moq: prev.moq ?? '',
    itemRate: prev.itemRate ?? '', itemMRP: prev.itemMRP ?? '',
    cgst: prev.cgst ?? '', sgst: prev.sgst ?? '', igst: prev.igst ?? '',
    purchaseRate: prev.purchaseRate ?? '', saleRate: prev.saleRate ?? ''
  };
  const nameClash = db.masters.items.some(function (i) { return i.id !== imEditId && String(i.name).toLowerCase() === name.toLowerCase(); });
  if (nameClash) return reqBad('im-name', '\u201c' + name + '\u201d already exists in Item Master \u2014 duplicate names are not allowed');
  if (imEditId) {
    const ex = db.masters.items.find(i => i.id === imEditId);
    if (!ex.code) { db.seq.item = (db.seq.item || 0) + 1; ex.code = 'ITM-' + String(db.seq.item).padStart(4, '0'); }
    Object.assign(ex, data);
    saveDB(); toast('Item updated — ' + name, 'green'); imEditId = null;
    if (MASTER_RECORD_POPUP === 'item') { MASTER_RECORD_POPUP = ''; refreshMasterRecords('item'); } else renderItemMaster();
  } else {
    db.seq.item = (db.seq.item || 0) + 1;
    data.id = uid(); data.code = 'ITM-' + String(db.seq.item).padStart(4, '0');
    db.masters.items.push(data);
    saveDB(); toast('Item added — ' + name, 'green');
    if (MASTER_RECORD_POPUP === 'item') { MASTER_RECORD_POPUP = ''; refreshMasterRecords('item'); } else renderItemMaster();
  }
}
function exportItemsCSV() {
  if (!db.masters.items.length) return toast('Nothing to export', 'red');
  const cols = ['code','name','category','classification','quality','unit','width','hsn','gst','active'];
  const header = cols.join(',');
  const lines = db.masters.items.map(it => cols.map(c => csvEsc(String(it[c] ?? ''))).join(','));
  const csv = header + '\n' + lines.join('\n');
  try {
    const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = 'items_' + today() + '.csv';
    document.body.appendChild(a); a.click(); a.remove();
    toast('CSV exported — ' + db.masters.items.length + ' items', 'green');
  } catch (e) { toast('Download blocked in preview — open the file in a browser to export'); }
}

/* ---------------- Tables ---------------- */
function statusOpts(module) {
  return [{ v:'', l:'All statuses' }].concat(STATUSES[module].map(s => ({ v: s, l: s })));
}
function setStatus(m, id, v) {
  const r = db[m].find(x => x.id === id); if (!r) return;
  if(!userCanRecord(m,r,'edit'))return toast('Status update access denied','red');
  r.status = v; saveDB(); toast('Status → ' + v, 'green');
  if (m === 'inward' && r.poRef) updatePOStatus(r.poRef);
  render();
}
function delEntry(m, id) {
  const r = db[m].find(x => x.id === id); if (!r) return;
  if(!userCanRecord(m,r,'delete'))return toast('Delete access denied','red');
  // v5: posted documents (Approved / Completed / Received / Closed) cannot
  // be deleted. Use the Cancel button on the form to mark the record as a
  // audit-only entry without removing it from history.
  const blocked = deleteBlockReason(m, r);
  if (blocked) return toast('\u201c' + (r.no || 'This entry') + '\u201d \u2014 ' + blocked, 'red');
  confirmDelete({
    kind: MODULES[m] ? MODULES[m].label + ' entry' : 'record',
    name: r.no || 'this record',
    confirmText: 'Delete permanently'
  }, function () {
    const idx = db[m].findIndex(x => x.id === id);
    if (idx < 0) return;
    const rec = db[m][idx];
    db[m] = db[m].filter(x => x.id !== id);
    if (m === 'processinward' && rec && rec.outwardRef) {
      const out = (db.processoutward || []).find(function (x) { return x.no === rec.outwardRef; });
      if (out && typeof updateProcessOutwardStatus === 'function') updateProcessOutwardStatus(out);
    }
    saveDB();
    toastUndo(rec.no + ' deleted', () => {
      db[m].splice(idx, 0, rec);
      if (m === 'processinward' && rec && rec.outwardRef) {
        const out = (db.processoutward || []).find(function (x) { return x.no === rec.outwardRef; });
        if (out && typeof updateProcessOutwardStatus === 'function') updateProcessOutwardStatus(out);
      }
      saveDB(); toast(rec.no + ' restored', 'green'); render();
    });
    render();
  });
}
function closeStatusCell(m, r) {
  r = r || {};
  // v5: prepend warning pill(s) so user can see flagged records
  let warn = '';
  // v5: only show warning badges if the warning is still valid based on current data
  if (m === 'po' && r.zeroRateWarning && (r.items||[]).some(i => pf(i.rate) <= 0)) warn += '<span class="badge b-amber" style="margin-right:4px" title="This PO has one or more zero-rate items">⚠ Zero rate</span>';
  if (m === 'inward' && r.overReceiptWarning && (r.items||[]).length) {
    const po = (db.po||[]).find(p => p.no === r.poRef);
    if (po) {
      const prior = {};
      (db.inward||[]).forEach(rr => { if (rr.poRef === r.poRef && !/cancel|reject/i.test(rr.status||'')) (rr.items||[]).forEach(i => { const k=lcName(bareItemName(i.name)); prior[k]=(prior[k]||0)+pf(i.recQty); }); });
      const stillOver = (r.items||[]).some(i => { const k=lcName(bareItemName(i.name)); const ordered=(po.items||[]).filter(p=>lcName(bareItemName(p.name))===k).reduce((a,p)=>a+pf(p.qty),0); return ordered && prior[k] > ordered; });
      if (stillOver) warn += '<span class="badge b-amber" style="margin-right:4px" title="This inward exceeds the related PO\'s ordered quantity">⚠ Over-receipt</span>';
    }
  }
  if (m === 'inward' && r.poNotApprovedWarning) warn += '<span class="badge b-amber" style="margin-right:4px" title="This inward was created against a PO that was not yet approved (' + esc(r.poNotApprovedWarning.status) + ')">⚠ PO ' + esc(r.poNotApprovedWarning.status) + '</span>';
  if (m === 'jobworkreturn' && r.overBalanceWarning) warn += '<span class="badge b-amber" style="margin-right:4px" title="This return exceeds the issued balance for one or more items">⚠ Over-return</span>';
  if (m === 'jobwork' && r.fgOverReceiptWarning) {
    // v5: only show if receipts are STILL over the order
    const order = {};
    (r.garments||[]).forEach(g => { order[qaStyleKey(g.desc||g.style)] = pf(g.orderQty) || Object.values(g.sizes||{}).reduce((a,n)=>a+pf(n),0); });
    const prior = {};
    (r.receipts||[]).forEach(rx => { if(!/reject|fail/i.test(rx.qc||'')){ const k=qaStyleKey(rx.desc); prior[k]=(prior[k]||0)+pf(rx.qty); } });
    const stillOver = Object.keys(order).some(k => prior[k] > order[k]);
    if (stillOver) warn += '<span class="badge b-amber" style="margin-right:4px" title="Finished-goods receipts exceed the order qty for one or more styles">⚠ FG over-order</span>';
  }
  // v5: show rejection reason for rejected POs
  if (m === 'po' && r.status === 'Rejected' && r.rejectReason) {
    const reasonEsc = esc(r.rejectReason);
    const who = r.rejectedBy ? ' by ' + esc(r.rejectedBy) : '';
    warn += '<span class="badge b-red" style="margin-right:4px;cursor:help" title="Rejection reason' + who + ': ' + reasonEsc + '">✗ Rejected</span>';
  }
  if(!userCan(m,'edit'))return warn + badge(r.status||'—');
  if (r.status === 'Closed') {
    return warn + '<button type="button" class="btn btn-ghost btn-sm" title="Reopen" onclick="event.stopPropagation();reopenRecord(\'' + m + '\',\'' + r.id + '\')">Reopen</button>';
  }
  if (r.status === 'Cancelled') return warn + '<span class="badge b-red">Cancelled</span>';
  if (r.status === 'Rejected') {
    // avoid double badge: the "warn" prefix already added the Rejected pill, return just warn
    return warn;
  }
  const lab = m === 'po' ? 'Close PO' : (m === 'jobwork' ? 'Close Job Work' : (m === 'processoutward' ? 'Close Process PO' : 'Close Sample'));
  const why = (m === 'jobwork') ? jwCloseBlockReason(r) : '';
  const title = why || lab;
  return warn + '<button type="button" class="btn btn-ghost btn-sm" title="' + esc(title) + '" onclick="event.stopPropagation();closeRecord(\'' + m + '\',\'' + r.id + '\')">' + lab + '</button>';
}
function closeRecordNote(m) {
  if (m === 'po') return 'No further inward can be booked against this PO until it is reopened.';
  if (m === 'jobwork') return 'No further material issues or finished-goods receipts can be booked against this job work until it is reopened.';
  if (m === 'sample') return 'No further material issues or sample receipts can be booked against this sample until it is reopened.';
  if (m === 'processoutward') return 'No further Process Inward receipts can be booked against this Process PO / Outward until it is reopened.';
  return 'No further transactions can be booked against this entry until it is reopened.';
}
function jwLineHasPod(line) {
  return !!(line && line.pod);
}
function jwCloseBlockReason(r) {
  r = r || {};
  function missingDates(list, label) {
    const dates = {};
    (list || []).forEach(function (i) {
      if (!String(i.item || '').trim()) return;
      const d = String(i.date || '').trim() || 'no date';
      if (!dates[d]) dates[d] = false;
      if (i.pod) dates[d] = true;
    });
    return Object.keys(dates).filter(function (d) { return !dates[d]; }).map(function (d) {
      return label + ' ' + (d === 'no date' ? 'no date' : (dpToDisplay(d) || d));
    });
  }
  const miss = missingDates(r.issues, 'Issues').concat(missingDates(r.extras, 'Extras'));
  const usedI = (r.issues || []).some(function (i) { return String(i.item || '').trim(); });
  const usedX = (r.extras || []).some(function (i) { return String(i.item || '').trim(); });
  if (!usedI && !usedX) return 'Issue material and upload POD for every date group before closing';
  if (miss.length) return 'Close is allowed only after one POD is uploaded per date. Missing: ' + miss.join('; ');
  return '';
}
function closeRecord(m, id) {
  const r = db[m].find(function (x) { return x.id === id; }); if (!r) return;
  if (r.status === 'Closed') return toast(r.no + ' is already closed');
  if (r.status === 'Cancelled') return toast(r.no + ' is cancelled', 'red');
  if (m === 'jobwork') {
    const why = jwCloseBlockReason(r);
    if (why) return toast(why, 'red');
  }
  const label = (MODULES[m] && MODULES[m].label) || 'entry';
  openDialog({
    title: 'Close ' + label,
    message: 'Close <span class="dlg-strong">' + esc(r.no) + '</span>?',
    note: closeRecordNote(m),
    confirmText: 'Close',
    onConfirm: function () {
      r.closedFrom = r.status || '';
      r.status = 'Closed';
      r.closeDate = today();
      saveDB();
      toast(r.no + ' closed', 'green');
      render();
    }
  });
}
function reopenRecord(m, id) {
  const r = db[m].find(function (x) { return x.id === id; }); if (!r) return;
  if (r.status !== 'Closed') return toast(r.no + ' is not closed');
  const label = (MODULES[m] && MODULES[m].label) || 'entry';
  openDialog({
    title: 'Reopen ' + label,
    message: 'Reopen <span class="dlg-strong">' + esc(r.no) + '</span> so it can be transacted again?',
    confirmText: 'Reopen',
    onConfirm: function () {
      const from = r.closedFrom;
      delete r.closedFrom;
      r.closeDate = '';
      if (m === 'po') {
        r.status = (from && from !== 'Closed') ? from : 'Draft';
        updatePOStatus(r.no);
      } else if (m === 'processoutward') {
        r.status = (from && from !== 'Closed' && from !== 'Cancelled') ? from : 'Open';
        updateProcessOutwardStatus(r);
      } else {
        r.status = (from && from !== 'Closed' && from !== 'Cancelled') ? from : 'Issued';
        r.status = jwAutoStatus(r);
      }
      saveDB();
      toast(r.no + ' reopened', 'green');
      render();
    }
  });
}

function recSearchText(r) {
  if (!r) return '';
  const skip = { pod:1, podName:1, styleImage:1, image:1, id:1, createdAt:1 };
  function walk(o, d) {
    if (o == null || d > 2) return '';
    const t = typeof o;
    if (t !== 'object') return String(o);
    if (Array.isArray(o)) {
      let s = '';
      for (let i = 0; i < o.length; i++) s += ' ' + walk(o[i], d + 1);
      return s;
    }
    let s = '';
    for (const k in o) {
      if (skip[k]) continue;
      const v = o[k];
      if (v && typeof v === 'object') s += ' ' + walk(v, d + 1);
      else if (v != null && v !== '') s += ' ' + v;
    }
    return s;
  }
  return walk(r, 0).toLowerCase();
}
function slicePage(rows, st) {
  rows = rows || [];
  st = st || {};
  const size = Math.max(10, parseInt(st.size, 10) || 25);
  const total = rows.length;
  const pages = Math.max(1, Math.ceil(total / size) || 1);
  let page = parseInt(st.page, 10) || 1;
  if (page > pages) page = pages;
  if (page < 1) page = 1;
  st.page = page;
  st.size = size;
  const start = (page - 1) * size;
  return {
    rows: rows.slice(start, start + size),
    page: page, pages: pages, size: size, total: total,
    from: total ? start + 1 : 0,
    to: Math.min(start + size, total)
  };
}
function pagerBar(pg, kind, key) {
  pg = pg || { page:1, pages:1, size:50, total:0, from:0, to:0 };
  key = key || '';
  const go = function (p) { return "goPage('" + kind + "','" + key + "'," + p + ")"; };
  const sizes = [10, 25, 50, 100, 200];
  let nums = '';
  const pages = pg.pages, cur = pg.page;
  if (pages > 1) {
    let a = Math.max(1, cur - 2), b = Math.min(pages, a + 4);
    a = Math.max(1, b - 4);
    if (a > 1) nums += '<button type="button" class="pager-num" onclick="' + go(1) + '">1</button>';
    if (a > 2) nums += '<span class="pager-gap">…</span>';
    for (let i = a; i <= b; i++) {
      nums += '<button type="button" class="pager-num' + (i === cur ? ' on' : '') + '" onclick="' + go(i) + '">' + i + '</button>';
    }
    if (b < pages - 1) nums += '<span class="pager-gap">…</span>';
    if (b < pages) nums += '<button type="button" class="pager-num" onclick="' + go(pages) + '">' + pages + '</button>';
  }
  return '<div class="pager">' +
    '<span class="rowcount" style="margin-left:0">' + (pg.total ? ('Showing ' + pg.from + '–' + pg.to + ' of ' + pg.total) : '0 records') + '</span>' +
    '<div class="pager-nav">' +
      (pages > 1 ? ('<button type="button" class="btn btn-ghost btn-sm" ' + (cur <= 1 ? 'disabled' : 'onclick="' + go(cur - 1) + '"') + '>Prev</button>' + nums +
        '<button type="button" class="btn btn-ghost btn-sm" ' + (cur >= pages ? 'disabled' : 'onclick="' + go(cur + 1) + '"') + '>Next</button>') : '') +
    '</div>' +
    '<select class="inp" title="Rows per page" onchange="goPageSize(\'' + kind + '\',\'' + key + '\',+this.value)">' +
      sizes.map(function (n) { return '<option value="' + n + '"' + (n === pg.size ? ' selected' : '') + '>' + n + ' / page</option>'; }).join('') +
    '</select></div>';
}
function goPage(kind, key, p) {
  p = parseInt(p, 10) || 1;
  if (kind === 'list') { fState[key].page = p; renderTable(key); }
  else if (kind === 'master') { mPage = p; refreshMasterBody(); }
  else if (kind === 'item') { imState.page = p; renderItemListOnly(); }
  else if (kind === 'party') { ptState.page = p; const el = $('pt-list'); if (el) el.innerHTML = partyListHtml(); }
  else if (kind === 'rep') { repState.page = p; renderReportBody(); }
  else if (kind === 'audit') { auditState.page = p; renderAuditTrail(); }
}
function goPageSize(kind, key, n) {
  n = parseInt(n, 10) || 25;
  if (kind === 'list') { fState[key].size = n; fState[key].page = 1; renderTable(key); }
  else if (kind === 'master') { mSize = n; mPage = 1; refreshMasterBody(); }
  else if (kind === 'item') { imState.size = n; imState.page = 1; renderItemListOnly(); }
  else if (kind === 'party') { ptState.size = n; ptState.page = 1; const el = $('pt-list'); if (el) el.innerHTML = partyListHtml(); }
  else if (kind === 'rep') { repState.size = n; repState.page = 1; renderReportBody(); }
  else if (kind === 'audit') { auditState.size = n; auditState.page = 1; renderAuditTrail(); }
}
function refreshMasterBody() {
  const el = $('m-body'); if (!el) { renderMasters(); return; }
  el.innerHTML = masterPanelHtml();
  setTimeout(function () { alignTables(); renumberSr(); attachExcelHeaderFilters(el); }, 0);
}
function listTabs(m) {
  if (m === 'inward') {
    return [
      { v:'', l:'All' },
      { v:'PO Receipt', l:'Against PO' },
      { v:'Opening Stock', l:'Opening Stock' },
      { v:'Job Work Return', l:'Job Work Return' }
    ];
  }
  if (m === 'po') {
    // v5: role-aware PO tabs — managers/admins see "Pending Approval" tab
    const u = authUser();
    const isApprover = u && (u.role==='Admin' || u.role==='Manager' || (u.permissions&&u.permissions.modules&&u.permissions.modules.po&&u.permissions.modules.po.approve));
    if (isApprover) {
      return [
        { v:'Pending', l:'Pending My Approval' },
        { v:'', l:'All' },
        { v:'Draft', l:'Draft' },
        { v:'Submitted', l:'Submitted' },
        { v:'Approved', l:'Approved' }
      ];
    } else {
      // Junior user (Store/Operator) tabs — show workflow states clearly
      return [
        { v:'MyDrafts', l:'My Drafts' },
        { v:'MyPending', l:'Awaiting Approval' },
        { v:'MyApproved', l:'Approved' },
        { v:'MyRejected', l:'Rejected (needs fix)' },
        { v:'', l:'All' }
      ];
    }
  }
  if (m === 'jobwork' || m === 'sample' || m === 'processoutward') {
    return [{ v:'', l:'All' }, { v:'Open', l:'Open' }, { v:'Close', l:'Close' }];
  }
  return [{ v:'', l:'All' }].concat((STATUSES[m] || []).map(function (s) { return { v:s, l:s }; }));
}
function tabCount(m, v) {
  const all = db[m] || [];
  if (!v) return m === 'inward' ? all.filter(function(r){return userCanInward(r.entryType||'PO Receipt','view');}).length : all.length;
  if (m === 'inward') {
    return all.filter(function (r) {
      const type = r.entryType || 'PO Receipt';
      return userCanInward(type,'view') && type === v;
    }).length;
  }
  if (v === 'Open') return all.filter(function (r) { return r.status !== 'Closed' && r.status !== 'Cancelled'; }).length;
  if (v === 'Close') return all.filter(function (r) { return r.status === 'Closed'; }).length;
  if (v === 'Pending') return all.filter(function (r) { return r.status === 'Submitted'; }).length;
  if (v === 'MyDrafts') return all.filter(function (r) { return r.status === 'Draft'; }).length;
  if (v === 'MyPending') return all.filter(function (r) { return r.status === 'Submitted'; }).length;
  if (v === 'MyApproved') return all.filter(function (r) { return r.status === 'Approved' || r.status === 'Received' || r.status === 'Closed'; }).length;
  if (v === 'MyRejected') return all.filter(function (r) { return r.status === 'Rejected'; }).length;
  return all.filter(function (r) { return r.status === v; }).length;
}
function setListTab(m, v) {
  fState[m].st = v;
  fState[m].page = 1;
  renderTable(m);
}
function pagedList(m) { return slicePage(filtered(m), fState[m]); }

function filtered(m) {
  const f = fState[m];
  let rows = db[m].slice().sort(function (a, b) {
    const ac=pf(a.createdAt),bc=pf(b.createdAt);
    if(ac&&bc&&ac!==bc)return bc-ac;
    if(String(a.date||'')!==String(b.date||''))return String(b.date||'').localeCompare(String(a.date||''));
    const an=parseInt((/([0-9]+)$/.exec(String(a.no||''))||[])[1],10)||0;
    const bn=parseInt((/([0-9]+)$/.exec(String(b.no||''))||[])[1],10)||0;
    return bn-an;
  });
  if(m==='inward')rows=rows.filter(function(r){return userCanInward(r.entryType||'PO Receipt','view');});
  if (f.q) { const q = f.q.toLowerCase(); rows = rows.filter(function (r) { return recSearchText(r).indexOf(q) > -1; }); }
  if (m === 'inward' && f.st) {
    rows = rows.filter(function (r) { return (r.entryType || 'PO Receipt') === f.st; });
  } else if (f.st === 'Open') rows = rows.filter(function (r) { return r.status !== 'Closed' && r.status !== 'Cancelled'; });
  else if (f.st === 'Close') rows = rows.filter(function (r) { return r.status === 'Closed'; });
  else if (f.st === 'Pending') rows = rows.filter(function (r) { return r.status === 'Submitted'; });
  else if (f.st === 'MyDrafts') rows = rows.filter(function (r) { return r.status === 'Draft'; });
  else if (f.st === 'MyPending') rows = rows.filter(function (r) { return r.status === 'Submitted'; });
  else if (f.st === 'MyApproved') rows = rows.filter(function (r) { return r.status === 'Approved' || r.status === 'Received' || r.status === 'Closed'; });
  else if (f.st === 'MyRejected') rows = rows.filter(function (r) { return r.status === 'Rejected'; });
  else if (f.st) rows = rows.filter(r => r.status === f.st);
  if (f.sort) {
    const dir = f.dir === 'desc' ? -1 : 1;
    rows.sort((a, b) => {
      const x = sortKeyVal(m, a, f.sort), y = sortKeyVal(m, b, f.sort);
      return x < y ? -dir : x > y ? dir : 0;
    });
  }
  return rows;
}

function poApprovalCell(r) {
  const canApprove = (typeof window.qaCanApprove === 'function') ? window.qaCanApprove('po') : ((authUser() && authUser().role === 'Admin') || false);
  const canEdit = userCanRecord('po', r, 'edit');
  const btnStyle = 'padding:4px 10px;font-size:11px;font-weight:600;border-radius:5px;border:1px solid #cbd5e1;background:#fff;cursor:pointer;line-height:1.2;margin:0 2px';
  let html = '<div class="po-approval-cell" style="display:flex;flex-wrap:wrap;gap:4px;align-items:center">';
  if (canEdit && (r.status === 'Draft' || r.status === 'Rejected') && !canApprove) {
    html += '<button style="' + btnStyle + ';color:#fff;background:#2563eb;border-color:#1d4ed8" title="Submit for approval" onclick="event.stopPropagation();window.qaSetStatus(\'po\',\'' + r.id + '\',\'Submitted\')">' + (r.status === 'Rejected' ? 'Re-submit' : 'Submit') + '</button>';
  }
  if (canApprove) {
    if (r.status === 'Draft') {
      html += '<button style="' + btnStyle + ';color:#fff;background:#2563eb;border-color:#1d4ed8" title="Submit for approval" onclick="event.stopPropagation();window.qaSetStatus(\'po\',\'' + r.id + '\',\'Submitted\')">Submit</button>';
    } else if (r.status === 'Submitted') {
      html += '<button style="' + btnStyle + ';color:#fff;background:#16a34a;border-color:#15803d" title="Approve PO" onclick="event.stopPropagation();window.qaSetStatus(\'po\',\'' + r.id + '\',\'Approved\')">Approve</button>';
      html += '<button style="' + btnStyle + ';color:#fff;background:#dc2626;border-color:#b91c1c" title="Reject PO" onclick="event.stopPropagation();window.qaSetStatus(\'po\',\'' + r.id + '\',\'Rejected\')">Reject</button>';
    } else if (r.status === 'Rejected') {
      html += '<button style="' + btnStyle + ';color:#1e293b;background:#f1f5f9" title="Re-open as Draft" onclick="event.stopPropagation();window.qaSetStatus(\'po\',\'' + r.id + '\',\'Draft\')">Re-open</button>';
    }
  }
  if (html.indexOf('<button') < 0) html += '<span class="muted" style="color:#94a3b8">—</span>';
  html += '</div>';
  return html;
}
function statusCell(m, id, val) {
  const rec=(db[m]||[]).find(function(x){return x.id===id;});
  if(!userCanRecord(m,rec,'edit'))return badge(val);
  return cbox('st_' + m + '_' + id, { opts: STATUSES[m], value: val, free: false, compact: true, placeholder: val, onSet: v => setStatus(m, id, v) });
}
/* ---------- index table state: sorting + selection ---------- */
const selState = {};
ORDER.forEach(m => selState[m] = {});
function sortKeyVal(m, r, key) {
  const c = (TABLE_COLS[m] || []).find(x => x.h === key);
  if (!c) return '';
  const raw = String(c.v(r)).replace(/<[^>]*>/g, '').replace(/[₹,\s]/g, '');
  const n = parseFloat(raw);
  return (raw !== '' && !isNaN(n) && /^[-\d.]+$/.test(raw)) ? n : raw.toLowerCase();
}
function toggleSort(m, key) {
  const f = fState[m];
  if (f.sort === key) f.dir = (f.dir === 'asc' ? 'desc' : 'asc');
  else { f.sort = key; f.dir = 'asc'; }
  f.page = 1;
  renderTable(m);
}
function selAll(m, on) {
  const rows = filtered(m);
  selState[m] = {};
  if (on) rows.forEach(r => selState[m][r.id] = true);
  renderTable(m);
}
function selOne(m, id, on) { if (on) selState[m][id] = true; else delete selState[m][id]; renderTable(m); }
function selCount(m) { return Object.keys(selState[m] || {}).length; }
function bulkDelete(m) {
  let ids = Object.keys(selState[m] || {});
  if (!ids.length) return;
  const blockedRows = db[m].filter(function (x) { return ids.includes(x.id) && deleteBlockReason(m, x); });
  if (blockedRows.length) {
    ids = ids.filter(function (i) { return !blockedRows.some(function (b) { return b.id === i; }); });
    toast(blockedRows.length + ' entr' + (blockedRows.length > 1 ? 'ies are' : 'y is') + ' locked by later documents and will be skipped', 'red');
    if (!ids.length) return;
  }
  openDialog({
    title: 'Delete ' + ids.length + ' ' + MODULES[m].label + ' entr' + (ids.length > 1 ? 'ies' : 'y'),
    message: 'Are you sure you want to delete <span class="dlg-strong">' + ids.length + '</span> selected record' + (ids.length > 1 ? 's' : '') + '? This action cannot be undone.',
    confirmText: 'Delete', danger: true,
    onConfirm: function () {
      const removed = db[m].filter(x => ids.includes(x.id));
      db[m] = db[m].filter(x => !ids.includes(x.id));
      selState[m] = {}; saveDB();
      toastUndo(ids.length + ' entries deleted', () => { db[m] = db[m].concat(removed); saveDB(); render(); });
      render();
    }
  });
}
function bulkStatus(m, v) {
  const ids = Object.keys(selState[m] || {});
  if (!ids.length || !v) return;
  db[m].forEach(r => { if (ids.includes(r.id)) r.status = v; });
  saveDB(); selState[m] = {}; toast('Status updated for ' + ids.length + ' entries', 'green'); render();
}
function clearFilters(m) { fState[m].q = ''; fState[m].st = ''; fState[m].page = 1; renderTable(m); }

/* just the <tbody> rows — shared by renderTable() and tableSearch() */
function tableBodyHtml(m) {
  const pg = pagedList(m);
  const rows = pg.rows;
  const total = db[m].length;
  const cols = TABLE_COLS[m];
  if (!pg.total) {
    const filteredOut = total > 0;
    return '<tr><td colspan="' + (cols.length + 2) + '"><div class="empty">' +
      '<div class="big">' + ic(MODULES[m].icon, 38) + '</div>' +
      (filteredOut
        ? '<h3>No ' + esc(MODULES[m].label) + ' entries found</h3><p>Try changing the search term or status filter.</p>' +
          '<button class="btn btn-ghost" onclick="clearFilters(\'' + m + '\')">Clear filters</button>'
        : '<h3>Create your first ' + esc(MODULES[m].label) + ' entry</h3><p>' + MODULES[m].sub + '</p>' +
          '<button class="btn btn-primary" onclick="openForm(\'' + m + '\')">' + ic('plus',16) + 'Create ' + esc(MODULES[m].label) + '</button>') +
      '</div></td></tr>';
  }
  let body = '';
  rows.forEach((r, ri) => {
    const tds = cols.map(c => c.v(r));
    let extra = '';
    const delBlock = deleteBlockReason(m, r);
    const canEditRow=userCanRecord(m,r,'edit'),canDeleteRow=userCanRecord(m,r,'delete');
    const srNo = pg.from + ri;
    if (m === 'po') {
      extra += '<button class="btn-icon" title="Print purchase order" onclick="event.stopPropagation();printPO(\'' + r.id + '\')">' + ic('print',15) + '</button>';
    }
    if (m === 'costing') {
      extra += '<button class="btn-icon" title="Repeat costing" onclick="event.stopPropagation();repeatCosting(\'' + r.id + '\')">' + ic('copy',15) + '</button>';
      extra += '<button class="btn-icon" title="Print costing" onclick="event.stopPropagation();printCosting(\'' + r.id + '\')">' + ic('print',15) + '</button>';
    }
    if (m === 'jobwork' && userCan(m,'create')) {
      extra += '<button class="btn-icon" title="Duplicate Job Work" onclick="event.stopPropagation();duplicateJobWork(\'' + r.id + '\')">' + ic('copy',15) + '</button>';
    }
    if (m === 'jobwork' || m === 'sample') {
      /* always available — fully received entries still need their
         receipts opened for review / correction */
      const rcvd = jwTotalReceived(r), ordq = jwTotalOrder(r);
      const done = ordq > 0 && rcvd >= ordq;
      const what = m === 'sample' ? 'samples' : 'finished goods';
      if (r.status !== 'Closed' && userCan(m,'edit')) {
        extra += '<button class="btn-icon recv" title="' +
          (done ? 'Received ' + fmtNum(rcvd) + ' of ' + fmtNum(ordq) + ' — open to review or edit receipts'
                : 'Receive ' + what + ' (' + fmtNum(rcvd) + ' of ' + fmtNum(ordq) + ' received)') +
          '" onclick="event.stopPropagation();openReceive(\'' + r.id + '\',\'' + m + '\')">' + ic('receive',15) + '</button>';
      }
    }
    body += '<tr onclick="openForm(\'' + m + '\',\'' + r.id + '\')">' +
      '<td class="srcol">' + srNo + '</td>' +
      tds.map((t, i) => '<td' + (cols[i].cls ? ' class="' + cols[i].cls + '"' : '') +
        (cols[i].stop || cols[i].h === 'Status' ? ' onclick="event.stopPropagation()"' : '') + '>' + t + '</td>').join('') +
      '<td class="actions-cell no-print" onclick="event.stopPropagation()">' + extra +
      '<button class="btn-icon" title="' + (canEditRow?'Edit':'View') + '" onclick="openForm(\'' + m + '\',\'' + r.id + '\')">' + ic(canEditRow?'pencil':'view',15) + '</button>' +
      (canDeleteRow
        ? (delBlock
            ? '<button class="btn-icon" disabled title="' + esc(delBlock) + '">' + ic('lock',15) + '</button>'
            : '<button class="btn-icon danger" title="Delete' + (delBlock ? ' — Warning: ' + esc(delBlock) : '') + '" onclick="delEntry(\'' + m + '\',\'' + r.id + '\')">' + ic('trash',15) + '</button>')
        : '') +
      '</td></tr>';
  });
  return body;
}
function renderTable(m) {
  setTimeout(dpSyncAll, 0);
  const f = fState[m];
  const rows = filtered(m);
  const total = db[m].length;

  /* --- saved-view tabs --- */
  const tabs = listTabs(m);
  const tabBar = tabs.length > 1 ? ('<div class="tabs">' + tabs.map(t => {
      return '<button class="tab' + (f.st === t.v ? ' active' : '') + '" onclick="setListTab(\'' + m + '\',\'' + t.v + '\')">' +
             esc(t.l) + '<span class="tc">' + tabCount(m, t.v) + '</span></button>';
    }).join('') + '</div>') : '';

  /* --- filter row --- */
  const filterBar =
    '<div class="filter-bar">' +
      '<div class="search">' + ic('search',16) +
        '<input class="inp" id="tblq_' + m + '" placeholder="Search ' + esc(MODULES[m].label) + '" value="' + esc(f.q) + '" oninput="tableSearch(\'' + m + '\',this.value)"></div>' +
      '<span id="tblclr_' + m + '">' + (f.q || f.st ? '<button class="btn btn-ghost btn-sm" onclick="clearFilters(\'' + m + '\')">Clear filters</button>' : '') + '</span>' +
      '<span class="rowcount" id="tblcount_' + m + '">' + rows.length + ' of ' + total + '</span>' +
    '</div>';

  /* row selection was removed — no bulk bar */
  const bulkBar = '';

  const cols = TABLE_COLS[m];
  const head = '<tr>' + srTh() +
    cols.map(c => {
      const on = f.sort === c.h;
      const arrow = on ? '<span class="sarr">' + ic(f.dir === 'asc' ? 'sortAsc' : 'sortDesc', 12) + '</span>' : '';
      return '<th class="' + (c.cls === 'num' ? 'num ' : '') + 'sortable' + (on ? ' on' : '') + '" onclick="toggleSort(\'' + m + '\',\'' + c.h.replace(/'/g, "\\'") + '\')">' +
             '<span class="thin">' + c.h + arrow + '</span></th>';
    }).join('') +
    '<th class="num no-print">Actions</th></tr>';

  const pg = pagedList(m);
  const body = tableBodyHtml(m);
  $('content').innerHTML = '<div class="panel list-panel">' +
    '<div class="index-stick">' + tabBar + (bulkBar || filterBar) + '</div>' +
    '<div class="table-wrap"><table><thead>' + head + '</thead><tbody id="tblbody_' + m + '" data-sr-start="' + pg.from + '">' + body + '</tbody></table></div>' +
    pagerBar(pg, 'list', m) + '</div>';
}
/* Typing in the index search must NOT rebuild the panel — that would destroy
   the focused input after every keystroke. Only the rows + counters refresh. */
function tableSearch(m, v) {
  fState[m].q = v;
  fState[m].page = 1;
  renderTable(m);
  const inp = $('tblq_' + m);
  if (inp) { inp.focus(); try { inp.setSelectionRange(inp.value.length, inp.value.length); } catch (e) {} }
}
function bulkExport(m) { exportCSV(m); }

const TABLE_COLS = {
  po: [
    { h:'PO No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'Supplier', v:r => esc(r.supplier) },
    { h:'Category', v:r => esc(r.category) },
    { h:'Qty', v:r => fmtNum(sumBy(r.items,'qty')), cls:'num' },
    { h:'Amount', v:r => fmtINR(sumItemAmt(r.items)), cls:'num' },
    { h:'PO status', v:r => closeStatusCell('po', r), stop:true },
    { h:'PO Approval', v:r => poApprovalCell(r), stop:true }
  ],
  inward: [
    { h:'Inward No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'Type', v:r => r.entryType === 'Opening Stock'
        ? '<span class="badge b-blue">Opening</span>'
        : r.entryType === 'Job Work Return'
          ? '<span class="badge b-pink">JW Return</span>'
          : '<span class="badge b-gray">PO</span>' },
    { h:'PO Ref', v:r => esc(r.poRef) || '—' },
    { h:'Supplier', v:r => esc(r.supplier) },
    { h:'Recv Qty', v:r => fmtNum(sumBy(r.items,'recQty')), cls:'num' }
  ],
  jobwork: [
    { h:'JW No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'Job Worker / Vendor', v:r => esc(r.vendor) },
    { h:'Finished Styles', v:r => { const gs = jwGarments(r).filter(x => x.desc); if (!gs.length) return '—'; const f = gs[0]; return '<b>' + esc(styleLabel(f.desc)) + '</b>' + (f.colour ? '<div class="sub-line">🎨 ' + esc(f.colour) + '</div>' : '') + (f.sizes && Object.keys(f.sizes).length ? '<div class="sub-line">📐 ' + esc(Object.entries(f.sizes).map(([s, q]) => s + ':' + q).join(' ')) + '</div>' : '') + (gs.length > 1 ? '<div class="sub-line">+' + (gs.length - 1) + ' more style' + (gs.length > 2 ? 's' : '') + '</div>' : ''); } },
    { h:'Fabric Issued', v:r => fmtNum(jwFabricTotal(r)) + ' ' + esc(jwFabricUnit(r)), cls:'num' },
    { h:'Trims Issued', v:r => { const t = jwTrimSummary(r); if (!t.length) return '—'; return '<span class="badge b-blue">' + t.length + ' item' + (t.length > 1 ? 's' : '') + '</span><div class="sub-line">' + esc(t.map(x => x.name + ': ' + fmtNum(x.qty) + (x.unit ? ' ' + x.unit : '')).join(', ').slice(0, 48)) + '</div>'; } },
    { h:'Order Qty', v:r => pf(jwTotalOrder(r)) ? fmtNum(jwTotalOrder(r)) : '—', cls:'num' },
    { h:'Charges', v:r => fmtINR(jwTotalCharges(r)), cls:'num' },
    { h:'Received', v:r => '<b>' + fmtNum(jwTotalReceived(r)) + '</b>' + (pf(jwTotalOrder(r)) > 0 ? ' / ' + fmtNum(jwTotalOrder(r)) : ''), cls:'num' },
    { h:'Job Work status', v:r => closeStatusCell('jobwork', r), stop:true }
  ],
  processoutward: [
    { h:'Outward No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'Process', v:r => processTypeBadge(r.processType) },
    { h:'Processor', v:r => esc(r.processor || '—') },
    { h:'Job Work Ref', v:r => esc(r.jwRef || '—') },
    { h:'Style', v:r => { const list=(r.items||[]).map(function(it){return styleLabel(it.style)||it.style;}).filter(Boolean); return list.length ? '<b>'+esc(list[0])+'</b>'+(list.length>1?'<div class="sub-line">+'+(list.length-1)+' more</div>':'') : '—'; } },
    { h:'Outward Qty', v:r => fmtNum((r.items || []).reduce(function(a,it){return a+pf(it.qty);},0)), cls:'num' },
    { h:'Process PO status', v:r => closeStatusCell('processoutward', r), stop:true }
  ],
  processinward: [
    { h:'Inward No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'Outward Ref', v:r => esc(r.outwardRef || '—') },
    { h:'Process', v:r => processTypeBadge(r.processType) },
    { h:'Processor', v:r => esc(r.processor || '—') },
    { h:'Warehouse', v:r => esc(r.warehouse || '—') },
    { h:'Received Qty', v:r => fmtNum((r.items || []).reduce(function(a,it){return a+pf(it.qty);},0)), cls:'num' },
    { h:'Status', v:r => statusCell('processinward', r.id, r.status) }
  ],
  purchasereturn: [
    { h:'Return No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'PO Ref', v:r => esc(r.poRef) || '—' },
    { h:'Inward No', v:r => esc(r.inwardNo) || '—' },
    { h:'Supplier', v:r => esc(r.supplier) || '—' },
    { h:'Items', v:r => (r.items || []).length, cls:'num' },
    { h:'Return Qty', v:r => fmtNum(sumBy(r.items,'retQty')), cls:'num' },
    { h:'Value', v:r => fmtINR(prTotal(r)), cls:'num' }
  ],
  jobworkreturn: [
    { h:'Return No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'JW Ref', v:r => esc(r.jwRef) || '—' },
    { h:'Job Worker', v:r => esc(r.vendor) || '—' },
    { h:'Warehouse', v:r => esc(r.warehouse) || '—' },
    { h:'Items', v:r => (r.items || []).length, cls:'num' },
    { h:'Return Qty', v:r => fmtNum(sumBy(r.items,'retQty')), cls:'num' },
    { h:'Status', v:r => statusCell('jobworkreturn', r.id, r.status) }
  ],
  sample: [
    { h:'Sample No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'Job Worker / Vendor', v:r => esc(r.vendor) },
    { h:'Style', v:r => { const gs = jwGarments(r).filter(x => x.desc); if (!gs.length) return '—'; const f = gs[0];
        return '<b>' + esc(styleLabel(f.desc)) + '</b>' +
          (f.sizes && Object.keys(f.sizes).length ? '<div class="sub-line">📐 ' + esc(Object.entries(f.sizes).map(([s, q]) => s + ':' + q).join(' ')) + '</div>' : '') +
          (gs.length > 1 ? '<div class="sub-line">+' + (gs.length - 1) + ' more</div>' : ''); } },
    { h:'Material Issued', v:r => { const t = jwTrimSummary({ issues: r.extras || [] }); if (!t.length) return '—';
        return '<span class="badge b-blue">' + t.length + ' item' + (t.length > 1 ? 's' : '') + '</span>'; } },
    { h:'Sample Qty', v:r => pf(jwTotalOrder(r)) ? fmtNum(jwTotalOrder(r)) : '—', cls:'num' },
    { h:'Charges', v:r => fmtINR(jwTotalCharges(r)), cls:'num' },
    { h:'Received', v:r => '<b>' + fmtNum(jwTotalReceived(r)) + '</b>' + (pf(jwTotalOrder(r)) > 0 ? ' / ' + fmtNum(jwTotalOrder(r)) : ''), cls:'num' },
    { h:'Sample status', v:r => closeStatusCell('sample', r), stop:true }
  ],
  sale: [
    { h:'Sale No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Date', v:r => esc(dpToDisplay(r.date) || r.date) },
    { h:'Customer', v:r => esc(r.customer) },
    { h:'Channel', v:r => esc(r.channel) },
    { h:'Qty', v:r => fmtNum(sumBy(r.items,'qty')), cls:'num' },
    { h:'Amount', v:r => fmtINR(sumBy(r.items,'amount')), cls:'num' },
    { h:'Payment', v:r => badge(r.payStatus) + (r.payMode ? '<div class="sub-line">' + esc(r.payMode) + '</div>' : '') }
  ],
  costing: [
    { h:'Costing No', v:r => '<b>' + esc(r.no) + '</b>' },
    { h:'Brand', v:r => esc((r.order && r.order.brand) || '—') },
    { h:'Label', v:r => esc((r.order && r.order.label) || '—') },
    { h:'Style', v:r => esc((r.order && r.order.style) || '—') },
    { h:'Description', v:r => esc((r.order && r.order.styleDescription) || '—') },
    { h:'Order Qty', v:r => fmtNum((r.order && r.order.totalQty) || 0), cls:'num' },
    { h:'FOB', v:r => fmtINR((r.order && r.order.fob) || 0), cls:'num' },
    { h:'Total Prod. Cost', v:r => fmtINR((r.totals && r.totals.totalProduction) || 0), cls:'num' },
    { h:'Margin', v:r => { const mgn = (r.totals && r.totals.margin) || 0; return '<span class="' + (mgn < 0 ? 'mneg' : 'mpos') + '">' + fmtINR(mgn) + '</span>'; }, cls:'num' },
    { h:'CMP', v:r => fmtINR((r.totals && r.totals.cmp) || 0), cls:'num' }
  ]
};

/* ---------------- Entry helpers ---------------- */
function field(lbl, inner, cls) { return '<div class="field' + (cls ? ' ' + cls : '') + '"><label class="lbl">' + lbl + '</label>' + inner + '</div>'; }
function srTh() { return '<th class="srcol">Sr. No.</th>'; }
function srTd() { return '<td class="srcol" data-sr></td>'; }
function renumberSr(root) {
  const scope = (root && root.querySelectorAll) ? root : document;
  const bodies = (root && root.tagName === 'TBODY') ? [root] : qsa('tbody', scope);
  bodies.forEach(function (tb) {
    if (tb.classList.contains('rc-sumbody')) return;
    let n = 0;
    const start = parseInt(tb.getAttribute('data-sr-start'), 10);
    if (start > 0) n = start - 1;
    qsa('tr', tb).forEach(function (tr) {
      if (tr.classList.contains('rc-sum') || tr.classList.contains('tot-row') || tr.classList.contains('jw-dgrp')) return;
      const cell = qs('td.srcol, td[data-sr]', tr);
      if (!cell) return;
      n += 1;
      cell.textContent = n;
    });
  });
}

let formCtx = null;
let PENDING_INWARD_INVOICE=null;
function clearInwardInvoiceGate(e){if(e){e.preventDefault();e.stopPropagation();}PENDING_INWARD_INVOICE=null;const file=$('invoice-gate-file'),preview=$('invoice-gate-preview'),title=$('invoice-gate-title'),help=$('invoice-gate-help'),msg=$('invoice-gate-msg'),del=$('invoice-gate-delete');if(file)file.value='';if(preview)preview.innerHTML='<span class="pod-upload-plus">＋</span>';if(title)title.textContent='Upload Invoice';if(help)help.textContent='Image or PDF · max 5 MB';if(msg){msg.textContent='Invoice removed';msg.className='fhint bad';}if(del)del.style.display='none';}
function onInwardInvoiceGateFile(inp){const f=inp.files&&inp.files[0],msg=$('invoice-gate-msg');PENDING_INWARD_INVOICE=null;if(!f)return;const resetPreview=$('invoice-gate-preview'),resetTitle=$('invoice-gate-title'),resetHelp=$('invoice-gate-help');if(resetPreview)resetPreview.innerHTML='<span class="pod-upload-plus">＋</span>';if(resetTitle)resetTitle.textContent='Upload Invoice';if(resetHelp)resetHelp.textContent='Image or PDF · max 5 MB';const ok=/^image\//i.test(f.type)||/pdf/i.test(f.type)||/\.(pdf|png|jpe?g|webp)$/i.test(f.name);if(!ok){if(msg){msg.textContent='Upload a PDF or image invoice.';msg.className='fhint bad';}inp.value='';return;}if(f.size>5*1024*1024){if(msg){msg.textContent='Invoice file must be 5 MB or smaller.';msg.className='fhint bad';}inp.value='';return;}const rd=new FileReader();rd.onload=function(e){PENDING_INWARD_INVOICE={name:'Supplier Invoice',fileName:f.name,data:e.target.result};const preview=$('invoice-gate-preview'),title=$('invoice-gate-title'),help=$('invoice-gate-help'),del=$('invoice-gate-delete'),isPdf=/pdf/i.test(f.type)||/\.pdf$/i.test(f.name);if(preview)preview.innerHTML=isPdf?'<div class="pdf">PDF</div>':'<img src="'+e.target.result+'" alt="Invoice preview">';if(title)title.textContent=f.name;if(help)help.textContent='Invoice attached · click to replace';if(del)del.style.display='inline-flex';if(msg){msg.textContent='Ready to continue';msg.className='fhint ok';}};rd.readAsDataURL(f);}
function openInwardTypeForm(type){closeDialog();pageView={kind:'form',m:'inward',id:null,inwardType:type};render();}
function requestAgainstPoInvoice(){PENDING_INWARD_INVOICE=null;const alternatives=[];if(userCanInward('Opening Stock','create'))alternatives.push('<button type="button" class="btn btn-ghost btn-sm" onclick="PENDING_INWARD_INVOICE=null;openInwardTypeForm(\'Opening Stock\')">Opening Stock instead</button>');if(userCanInward('Job Work Return','create'))alternatives.push('<button type="button" class="btn btn-ghost btn-sm" onclick="PENDING_INWARD_INVOICE=null;openInwardTypeForm(\'Job Work Return\')">Job Work Return instead</button>');openDialog({title:'Against PO — Invoice Required',message:'Attach the supplier invoice before creating an Against PO Inward entry.',bodyHtml:'<div class="dlg-field"><label>Supplier Invoice *</label><label class="pod-upload-box empty invoice-gate-upload" title="Upload supplier invoice"><button type="button" class="invoice-gate-delete" id="invoice-gate-delete" title="Delete invoice" onclick="clearInwardInvoiceGate(event)">'+ic('trash',13)+'</button><input type="file" id="invoice-gate-file" accept="image/*,application/pdf,.pdf" onchange="onInwardInvoiceGateFile(this)"><span class="invoice-preview" id="invoice-gate-preview"><span class="pod-upload-plus">＋</span></span><span class="pod-upload-title" id="invoice-gate-title">Upload Invoice</span><span class="pod-upload-help" id="invoice-gate-help">Image or PDF · max 5 MB</span></label><div class="fhint" id="invoice-gate-msg"></div></div>'+(alternatives.length?'<div style="display:flex;gap:7px;flex-wrap:wrap;margin-top:14px">'+alternatives.join('')+'</div>':''),confirmText:'Continue to Against PO',onConfirm:function(){if(!PENDING_INWARD_INVOICE){reqBad('invoice-gate-file','Supplier Invoice is mandatory for Against PO Inward');return false;}openInwardTypeForm('PO Receipt');}});}
function beginNewInward(){if(userCanInward('PO Receipt','create'))return requestAgainstPoInvoice();if(userCanInward('Opening Stock','create'))return openInwardTypeForm('Opening Stock');if(userCanInward('Job Work Return','create'))return openInwardTypeForm('Job Work Return');toast('Create access denied','red');}
function openForm(m, id) {
  if(!id&&m==='inward')return beginNewInward();
  if(id){const rec=(db[m]||[]).find(function(x){return x.id===id;});const subtype=m==='inward'?((rec&&rec.entryType)||'PO Receipt'):'';if(m==='inward'?!userCanInward(subtype,'view'):!userCan(m,'view'))return toast('View access denied','red');const editable=m==='inward'?userCanInward(subtype,'edit'):userCan(m,'edit');pageView={kind:'form',m:m,id:id,viewOnly:!editable};}
  else{if(m==='inward'){const any=['PO Receipt','Opening Stock','Job Work Return'].some(function(t){return userCanInward(t,'create');});if(!any)return toast('Create access denied','red');}else if(!userCan(m,'create'))return toast('Create access denied','red');pageView={kind:'form',m:m,id:null};}render();
}
function duplicateJobWork(id) {
  const src = (db.jobwork || []).find(function (x) { return x.id === id; });
  if (!src) return toast('Job Work not found', 'red');
  cur = 'jobwork';
  pageView = { kind:'form', m:'jobwork', id:null, duplicateFrom:id };
  render();
  toast('Duplicated ' + (src.no || 'Job Work') + ' — review and save to generate a new JW number', 'green');
}
function saveForm() {
  const { m, id } = pageView;
  if(!userCan(m,id?'edit':'create'))return toast('Save access denied','red');
  if (id && (m === 'po' || m === 'jobwork' || m === 'sample')) {
    const rec = (db[m] || []).find(function (x) { return x.id === id; });
    if (rec && rec.status === 'Closed') return toast((rec.no || MODULES[m].label) + ' is closed — reopen it to edit', 'red');
  }
  if (m === 'po') savePO(id);
  else if (m === 'inward') saveInward(id);
  else if (m === 'jobwork') saveJobWork(id);
  else if (m === 'processoutward') saveProcessOutward(id);
  else if (m === 'processinward') saveProcessInward(id);
  else if (m === 'sample') saveSample(id);
  else if (m === 'purchasereturn') savePurchaseReturn(id);
  else if (m === 'jobworkreturn') saveJobWorkReturn(id);
  else if (m === 'sale') saveSale(id);
  else if (m === 'costing') saveCosting(id);
}

/* ---------- mini-table helpers ---------- */
function addRow(tbodyId, fn) { $(tbodyId).insertAdjacentHTML('beforeend', fn()); dpSyncAll($(tbodyId)); renumberSr($(tbodyId)); }
function removeRow(btn) {
  const tb = btn.closest('tbody');
  const row = btn.closest('tr');
  if (row && row.classList.contains('row-locked') && row.dataset.jwEditing !== '1') return toast('This line is locked by a later entry and cannot be removed', 'red');
  if (tb && (tb.id === 'jw-issues' || tb.id === 'jw-extras')) {
    const returnBlock = jwItemReturnBlockReason(rowItemName(row));
    if (returnBlock) return toast(returnBlock, 'red');
    if (jwRowHasPod(row) && row.dataset.jwEditing !== '1') return toast('Cannot delete — POD already uploaded for this item', 'red');
  }
  // v5: allow deletion of the last row in JW issues/extras and most tables
  // (only PO and Sale keep a minimum-one-row rule)
  if (tb && (tb.id === 'po-items' || tb.id === 'sale-items') && tb.querySelectorAll('tr').length <= 1) return toast('At least one row needed', 'red');
  btn.closest('tr').remove();
  renumberSr(tb);
  recalc(tb.id.replace('-items',''));
}
/* the item cell displays "Name (ITM-0001)" — store the bare name */
function bareItemName(v) {
  const s = String(v == null ? '' : v).trim();
  const m = /^(.*?)\s*\([A-Za-z]{2,}-\d+\)$/.exec(s);
  return m ? m[1].trim() : s;
}
function gatherRows(tbodyId, keys) {
  const rows = [];
  qsa('tr', $(tbodyId)).forEach(tr => {
    const o = {};
    qsa('[data-f]', tr).forEach(inp => {
      if (inp.dataset.cbInput) { o[inp.dataset.f] = cboxGet(inp.dataset.cbInput); return; }
      if (inp.dataset.cb) { o[inp.dataset.f] = cboxGet(inp.dataset.cb); return; }
      if (inp.hasAttribute('data-date')) { o[inp.dataset.f] = dval(inp); return; }
      const t = inp.type;
      o[inp.dataset.f] = (inp.tagName === 'SELECT' || t === 'text' || t === 'hidden' || t === 'date' || t === 'datetime-local') ? inp.value.trim() : (inp.value === '' ? 0 : parseFloat(inp.value));
    });
    if (keys.some(k => o[k])) rows.push(o);
  });
  return rows;
}
function recalc(m) { if (m === 'po') calcPO(); else if (m === 'sale') calcSale(); }

/* ================= PO ================= */
function poItemRow(it) {
  it = it || {};
  const nameId = rid('poin'), unitId = null, clsId = rid('pocl');
  return '<tr>' +
    srTd() +
    '<td>' + cbox(clsId, { opts: db.masters.classifications, value: it.classification || '', f:'classification', placeholder:'Classification', addMaster:'classifications', addText:'Classifications', free:true, compact:true, onSet: function (v) { poClassChanged(clsId, nameId, v); } }) + '</td>' +
    '<td>' + cbox(nameId, { opts: itemOptsFor(it.classification), value: it.name || '', display: itemDisplay(it.name), f:'name', placeholder:'Item / Fabric', addMaster:'items', addText:'Item Master', free:true, onSet: v => { const tr = qs('[data-cb-input="' + nameId + '"]').closest('tr'); if (poDupCheck(tr, v)) return; poApplyItemDefaults(tr, v, unitId, clsId); calcPO(); } }) +
      '<input type="text" data-f="code" value="' + esc(it.code || '') + '" style="display:none" tabindex="-1" aria-hidden="true"></td>' +
    '<td><input class="inp" data-f="hsn" value="' + esc(it.hsn || '') + '" placeholder="Auto" disabled></td>' +
    '<td><input class="inp" data-f="unit" value="' + esc(it.unit || '') + '" placeholder="Auto" disabled></td>' +
    '<td class="num" data-stock style="color:var(--muted);font-weight:600">' + fmtNum(itemStockQty(it.name)) + '</td>' +
    '<td><input class="inp num" type="number" min="0" data-f="qty" value="' + (it.qty ?? '') + '" oninput="calcPO()"></td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="rate" value="' + (it.rate ?? '') + '" oninput="calcPO()"></td>' +
    '<td><input class="inp num inp-sm" type="number" data-f="cgstPct" value="' + (it.cgstPct ?? '') + '" placeholder="Auto" disabled></td>' +
    '<td class="num" data-camt style="color:var(--muted)">0</td>' +
    '<td><input class="inp num inp-sm" type="number" data-f="sgstPct" value="' + (it.sgstPct ?? '') + '" placeholder="Auto" disabled></td>' +
    '<td class="num" data-samt style="color:var(--muted)">0</td>' +
    '<td><input class="inp num inp-sm" type="number" data-f="igstPct" value="' + (it.igstPct ?? '') + '" placeholder="Auto" disabled></td>' +
    '<td class="num" data-iamt style="color:var(--muted)">0</td>' +
    '<td class="num" data-amt style="font-weight:700">0</td>' +
    '<td class="num" data-total style="font-weight:800;color:var(--pink-dark)">0</td>' +
    '<td><button type="button" class="btn-icon danger" onclick="removeRow(this);calcPO()">' + ic('trash', 15) + '</button></td>' +
  '</tr>';
}
/* classification picked → restrict the item list on that row */
function poClassChanged(clsId, nameId, cls) {
  const c = CBOX[nameId]; if (!c) return;
  c.opts = itemOptsFor(cls).map(normalizeOpt);
  const stillValid = c.opts.some(function (o) { return o.v === c.val; });
  if (!stillValid) {
    c.val = ''; c.typed = false;
    const inp = qs('[data-cb-input="' + nameId + '"]');
    if (inp) {
      inp.value = '';
      const tr = inp.closest('tr');
      if (tr) {
        ['hsn','unit','code'].forEach(function (f) { const el = qs('[data-f="' + f + '"]', tr); if (el) el.value = ''; });
        const st = qs('[data-stock]', tr); if (st) st.textContent = '0';
      }
    }
    calcPO();
  }
  renderCboxList(nameId);
}
/* PO must never list the same item twice */
function poDupCheck(tr, name) {
  const nm = lcName(bareItemName(name));
  if (!nm || !tr) return false;
  const tb = tr.parentNode; if (!tb) return false;
  let dup = false;
  qsa('tr', tb).forEach(function (other) {
    if (other === tr || dup) return;
    if (lcName(rowItemName(other)) === nm) dup = true;
  });
  if (!dup) return false;
  toast('Duplicate item \u2014 ' + bareItemName(name) + ' is already on this purchase order. Update that line\u2019s quantity instead.', 'red');
  const el = qs('[data-f="name"]', tr);
  if (el && el.dataset.cbInput) { cboxSet(el.dataset.cbInput, ''); el.value = ''; }
  ['hsn','unit','code','cgstPct','sgstPct','igstPct','qty','rate'].forEach(function (f) {
    const x = qs('[data-f="' + f + '"]', tr); if (x) x.value = '';
  });
  const st = qs('[data-stock]', tr); if (st) st.textContent = '0';
  calcPO();
  return true;
}
/* '' when the line's GST matches Item Master, else the message */
function poGstMismatch(i) {
  const im = itemByName(bareItemName(i.name));
  if (!im) return '';
  const master = pf(im.gst);
  const line = pf(i.cgstPct) + pf(i.sgstPct) + pf(i.igstPct);
  if (Math.abs(master - line) < 0.005) return '';
  return 'GST mismatch — ' + bareItemName(i.name) + ' is configured at ' + fmtNum(master) +
         '% in Item Master but this line totals ' + fmtNum(line) + '%. Re-select the item to reload the correct rate.';
}
/* recompute every line's GST from Item Master + the PO's GST type */
function poSyncGstFromMaster() {
  const gstType = String((($('f-gsttype') || {}).value) || '').toUpperCase();
  const hasGstType = !!gstType;
  const splitGst = gstType.indexOf('CGST') > -1;
  let fixed = 0;
  qsa('#po-items tr').forEach(function (tr) {
    if (tr.classList.contains('row-locked')) return;
    const nm = rowItemName(tr); if (!nm) return;
    const im = itemByName(nm); if (!im) return;
    const g = pf(im.gst);
    const set = function (f, v) { const el = qs('[data-f="' + f + '"]', tr); if (el && String(el.value) !== String(v)) { el.value = v; fixed++; } };
    set('cgstPct', hasGstType && splitGst ? g / 2 : '');
    set('sgstPct', hasGstType && splitGst ? g / 2 : '');
    set('igstPct', hasGstType && !splitGst ? g : '');
  });
  if (fixed) calcPO();
  return fixed;
}
function poApplyItemDefaults(tr, name, unitId, clsId) {
  if (!tr) return;
  const it = itemByName(name);
  const set = (sel, val) => { const el = qs(sel, tr); if (el) el.value = (val != null && val !== '') ? val : ''; };
  set('[data-f="code"]', it ? it.code : '');
  if (clsId && it && it.classification) cboxSet(clsId, it.classification);
  set('[data-f="hsn"]', it ? it.hsn : '');
  set('[data-f="unit"]', it ? (it.unit || itemUnit(name)) : '');
  /* Item Master keeps ONE GST % — split it per the PO's GST type */
  const gstPct = it ? pf(it.gst) : 0;
  const gstType = String((($('f-gsttype') || {}).value) || '').toUpperCase();
  const hasGstType = !!gstType;
  const splitGst = gstType.indexOf('CGST') > -1;
  set('[data-f="cgstPct"]', it && hasGstType && splitGst ? gstPct / 2 : '');
  set('[data-f="sgstPct"]', it && hasGstType && splitGst ? gstPct / 2 : '');
  set('[data-f="igstPct"]', it && hasGstType && !splitGst ? gstPct : '');
  const stockTd = qs('[data-stock]', tr); if (stockTd) stockTd.textContent = fmtNum(itemStockQty(name));
}

/* =====================================================================
   RICH TEXT EDITOR — Shopify-style toolbar for PO Terms & Conditions
   ===================================================================== */
function termsToHtml(raw) {
  const t = String(raw == null ? '' : raw);
  if (!t) return DEFAULT_PO_TERMS;
  if (/<[a-z][\s\S]*>/i.test(t)) return t;
  return t.split(/\n+/).map(function (line) {
    const s = line.trim(); if (!s) return '';
    return '<p>' + esc(s) + '</p>';
  }).join('') || DEFAULT_PO_TERMS;
}
function rteSanitize(html) {
  const d = document.createElement('div');
  d.innerHTML = String(html == null ? '' : html);
  qsa('*', d).forEach(function (el) {
    if (/^(SCRIPT|STYLE|IFRAME|OBJECT|EMBED|LINK|META)$/.test(el.tagName)) { el.remove(); return; }
    Array.prototype.slice.call(el.attributes || []).forEach(function (a) {
      if (/^on/i.test(a.name) || a.name === 'srcdoc') el.removeAttribute(a.name);
      if ((a.name === 'href' || a.name === 'src') && /^\s*javascript:/i.test(a.value)) el.removeAttribute(a.name);
    });
  });
  return d.innerHTML;
}
function rteSvg(d) {
  return '<svg class="i" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">' + d + '</svg>';
}
function rteHtml(html) {
  const body = rteSanitize(termsToHtml(html));
  const btn = function (cmd, title, inner) {
    return '<button type="button" class="rte-btn" data-cmd="' + cmd + '" title="' + title + '" onclick="rteCmd(\'' + cmd + '\')">' + inner + '</button>';
  };
  return '<div class="rte" id="f-terms-rte">' +
    '<div class="rte-bar">' +
      '<select class="rte-sel" title="Paragraph" onchange="rteBlock(this.value)">' +
        '<option value="p">Paragraph</option>' +
        '<option value="h2">Heading</option>' +
        '<option value="h3">Subheading</option>' +
        '<option value="blockquote">Quote</option>' +
      '</select>' +
      '<span class="rte-sep"></span>' +
      btn('bold', 'Bold', '<b>B</b>') +
      btn('italic', 'Italic', '<i>I</i>') +
      btn('underline', 'Underline', '<u>U</u>') +
      '<span class="rte-sep"></span>' +
      btn('justifyLeft', 'Align left', rteSvg('<path d="M3.5 5.5h13M3.5 10h9M3.5 14.5h13"/>')) +
      btn('justifyCenter', 'Align center', rteSvg('<path d="M3.5 5.5h13M5.5 10h9M3.5 14.5h13"/>')) +
      btn('justifyRight', 'Align right', rteSvg('<path d="M3.5 5.5h13M7.5 10h9M3.5 14.5h13"/>')) +
      '<span class="rte-sep"></span>' +
      btn('insertUnorderedList', 'Bulleted list', rteSvg('<path d="M7 5.5h9.5M7 10h9.5M7 14.5h9.5"/><circle cx="4.2" cy="5.5" r="1"/><circle cx="4.2" cy="10" r="1"/><circle cx="4.2" cy="14.5" r="1"/>')) +
      btn('insertOrderedList', 'Numbered list', rteSvg('<path d="M7.5 5.5h9M7.5 10h9M7.5 14.5h9"/><path d="M3.2 4.2h2.2v4H3.6M3.2 12.2h2.6M3.2 14.8h2.6"/>')) +
      '<span class="rte-sep"></span>' +
      '<button type="button" class="rte-btn" title="Insert link" onclick="rteLink()">' + rteSvg('<path d="M8.2 11.8 11.8 8.2"/><path d="M7.2 9.2 5.8 10.6a2.4 2.4 0 0 0 3.4 3.4l1.4-1.4M12.8 10.8l1.4-1.4a2.4 2.4 0 0 0-3.4-3.4L9.4 7.4"/>') + '</button>' +
      btn('strikeThrough', 'Strikethrough', '<s>S</s>') +
      '<button type="button" class="rte-btn" title="Clear formatting" onclick="rteCmd(\'removeFormat\')">' + rteSvg('<path d="M5.5 5.5 14.5 14.5M14.5 5.5 5.5 14.5"/>') + '</button>' +
      '<span class="rte-sep"></span>' +
      '<button type="button" class="rte-btn" id="rte-code-btn" title="Show HTML" onclick="rteToggleCode()">&lt;/&gt;</button>' +
    '</div>' +
    '<div class="rte-body" id="f-terms" contenteditable="true" spellcheck="true" data-ph="Write terms and conditions…">' + body + '</div>' +
  '</div>';
}
function rteCmd(cmd, val) {
  const body = $('f-terms'); if (!body) return;
  if ($('f-terms-rte') && $('f-terms-rte').classList.contains('code')) return;
  body.focus();
  try { document.execCommand(cmd, false, val == null ? null : val); } catch (e) {}
  rteSyncState();
}
function rteBlock(tag) {
  const body = $('f-terms'); if (!body) return;
  body.focus();
  try { document.execCommand('formatBlock', false, tag); } catch (e) {}
  rteSyncState();
}
function rteLink() {
  const body = $('f-terms'); if (!body) return;
  openDialog({
    title: 'Insert link',
    bodyHtml: '<div class="dlg-field"><label>URL</label><input class="inp" id="rte-url" placeholder="https://"></div>',
    confirmText: 'Insert',
    after: function () { const el = $('rte-url'); if (el) el.focus(); },
    onConfirm: function () {
      const url = (($('rte-url') || {}).value || '').trim();
      if (!url) return false;
      rteCmd('createLink', url);
    }
  });
}
function rteToggleCode() {
  const rte = $('f-terms-rte'), body = $('f-terms'); if (!rte || !body) return;
  if (rte.classList.contains('code')) {
    body.innerHTML = rteSanitize(body.innerText || body.textContent || '');
    rte.classList.remove('code');
    const b = $('rte-code-btn'); if (b) { b.classList.remove('on'); b.title = 'Show HTML'; }
  } else {
    body.textContent = body.innerHTML;
    rte.classList.add('code');
    const b = $('rte-code-btn'); if (b) { b.classList.add('on'); b.title = 'Show formatted'; }
  }
}
function rteGet() {
  const body = $('f-terms'); if (!body) return '';
  const rte = $('f-terms-rte');
  if (rte && rte.classList.contains('code')) return rteSanitize(body.innerText || body.textContent || '');
  return rteSanitize(body.innerHTML || '');
}
function rteSyncState() {
  const bar = qs('#f-terms-rte .rte-bar'); if (!bar) return;
  qsa('.rte-btn[data-cmd]', bar).forEach(function (btn) {
    let on = false;
    try { on = document.queryCommandState(btn.dataset.cmd); } catch (e) {}
    btn.classList.toggle('on', !!on);
  });
}
function bindRte() {
  const body = $('f-terms'); if (!body || body._rteBound) return;
  body._rteBound = true;
  body.addEventListener('keyup', rteSyncState);
  body.addEventListener('mouseup', rteSyncState);
  body.addEventListener('paste', function (e) {
    if ($('f-terms-rte') && $('f-terms-rte').classList.contains('code')) return;
    e.preventDefault();
    const text = ((e.clipboardData || window.clipboardData).getData('text/plain') || '');
    try { document.execCommand('insertText', false, text); } catch (err) {}
  });
}
function poTermsPrintHtml(r) {
  const html = rteSanitize(termsToHtml((r && r.terms) || DEFAULT_PO_TERMS));
  return '<div class="terms"><h4>AGREED TERMS &amp; CONDITIONS</h4><div class="rte-print">' + html + '</div></div>';
}

function formPO(rec) {
  const r = rec || {};
  const isEdit = !!(rec && rec.id);
  return '<div class="sec">Purchase Order</div><div class="grid-fixed">' +
    (isEdit ? field('PO No', '<input class="inp" id="f-no" value="' + esc(r.no || '') + '" disabled>') : '') +
    field('PO Date *', '<input class="inp" type="text" data-date autocomplete="off" disabled id="f-date" value="' + esc(r.date || today()) + '" title="Purchase orders are always dated today">') +
    field('Supplier *', cbox('po-supplier', { opts: supplierPartyOptions(), value: r.supplier || '', f:'supplier', placeholder:'Type to search…', free:true, onSet: v => onPoSupplierChange(v) })) +
    field('Supplier Trade Name *', cbox('po-trade', { opts: partyTradeOptions(r.supplier), value: r.supplierTrade || '', f:'supplierTrade', placeholder:'Select trade / branch', free:true, onSet: v => onPoTradeChange(v) })) +
    field('Ship To *', cbox('po-shipto', { opts: partyOptions(), value: r.shipTo || '', f:'shipTo', placeholder:'Select party…', free:true })) +
    field('GST Type', '<input class="inp" id="f-gsttype" value="' + esc(r.gstType || '') + '" placeholder="Auto after Supplier Trade Name" disabled title="Auto-filled from Supplier Trade Name"><div class="fhint" id="f-gsttype-hint">Select Supplier Trade Name to determine GST Type</div>') +
    field('Buyer *', cbox('po-buyer', { opts: buyerPartyOptions(), value: r.buyer || '', f:'buyer', placeholder:'Type to search…', free:true })) +
    field('Style (Internal ID) *', mcbox('po-styles', { opts: styleOpts(),
      selected: Array.isArray(r.styles) ? r.styles.slice() : (r.styles ? [r.styles] : []),
      placeholder: 'Select one or more styles…' })) +
  '</div>' +
  '<div class="sec">Items</div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' +
    srTh() + '<th>Classification *</th><th>Item Name *</th><th>HSN No</th><th>Unit</th><th>Stock</th><th>Quantity</th><th>Rate</th>' +
    '<th>CGST%</th><th>CAMT</th><th>SGST%</th><th>SAMT</th><th>IGST%</th><th>IAMT</th><th>Amount</th><th>Total Amount</th><th></th>' +
  '</tr></thead>' +
  '<tbody id="po-items">' + (r.items && r.items.length ? r.items.map(poItemRow).join('') : poItemRow()) + '</tbody></table></div>' +
  '<div class="po-add-item-bar"><button class="btn btn-ghost btn-sm" onclick="addRow(\'po-items\',poItemRow);calcPO()">＋ Add item</button></div>' +
  '<div class="grid-fixed" style="margin-top:18px">' +
    field('Total Qty', '<input class="inp" id="po-sum-qty" value="0" disabled>') +
    field('Gross Amount', '<input class="inp" id="po-sum-gross" value="₹0" disabled>') +
    field('GST Amount', '<input class="inp" id="po-sum-gst" value="₹0" disabled>') +
    field('Net Amount', '<input class="inp" id="po-sum-net" value="₹0" disabled>') +
    field('Delivery Date *', '<input class="inp" type="text" data-date autocomplete="off" readonly id="f-delivery-date" value="' + esc(r.deliveryDate || '') + '">') +
    field('Raised For *', cbox('po-raisedfor', { opts: db.masters.raisedForList, value: r.raisedFor || '', f:'raisedFor', placeholder:'Select raised for', addMaster:'raisedForList', addText:'Raised For', free:true })) +
    field('Qtn. / P.I. No.', '<input class="inp" id="f-qtn-no" value="' + esc(r.qtnNo || '') + '">') +
    field('Qtn. / P.I. No. – Date', '<input class="inp" type="text" data-date autocomplete="off" readonly id="f-qtn-date" value="' + esc(r.qtnDate || '') + '">') +
    field('Payment Terms', cbox('po-payterms', { opts: db.masters.paymentTermsList, value: r.payTerms || '', f:'payTerms', placeholder:'Select payment terms', addMaster:'paymentTermsList', addText:'Payment Terms', free:true })) +
    field('Freight Charges', cbox('po-freightcharges', { opts: db.masters.freightChargesList, value: r.freightCharges || '', f:'freightCharges', placeholder:'--Select--', addMaster:'freightChargesList', addText:'Freight Charges', free:true })) +
    field('P &amp; F', '<input class="inp" id="f-pnf" value="' + esc(r.pnf === '' || r.pnf == null ? 'Nil' : r.pnf) + '" placeholder="Nil" oninput="calcPO()" onfocus="if(this.value===\'Nil\')this.select()">') +
    field('Transit Insurance', cbox('po-transitins', { opts: TRANSIT_INSURANCE_OPTS, value: r.transitInsurance || '', f:'transitInsurance', placeholder:'--Select--', free:false })) +
    field('Mode of Transportation', cbox('po-transport', { opts: db.masters.transportModes, value: r.transportMode || '', f:'transportMode', placeholder:'--Select--', addMaster:'transportModes', addText:'Transport Modes', free:true })) +
    field('Narration', '<input class="inp" id="f-narration" value="' + esc(r.narration || '') + '">') +
  '</div>' +
  '<div class="sec">Terms and Conditions</div>' + rteHtml(r.terms || DEFAULT_PO_TERMS);
}
function calcPO() {
  const tb = $('po-items'); if (!tb) return;
  let tq = 0, gross = 0, gstAmt = 0, freightAmt = 0;
  qsa('tr', tb).forEach((tr, idx) => {
    const srEl = qs('[data-sr]', tr); if (srEl) srEl.textContent = idx + 1;
    const q = pf(qs('[data-f="qty"]', tr) ? qs('[data-f="qty"]', tr).value : 0);
    const rt = pf(qs('[data-f="rate"]', tr) ? qs('[data-f="rate"]', tr).value : 0);
    const cgstPct = pf(qs('[data-f="cgstPct"]', tr) ? qs('[data-f="cgstPct"]', tr).value : 0);
    const sgstPct = pf(qs('[data-f="sgstPct"]', tr) ? qs('[data-f="sgstPct"]', tr).value : 0);
    const igstPct = pf(qs('[data-f="igstPct"]', tr) ? qs('[data-f="igstPct"]', tr).value : 0);
    const amount = q * rt;
    const camt = amount * cgstPct / 100, samt = amount * sgstPct / 100, iamt = amount * igstPct / 100;
    const total = amount + camt + samt + iamt;
    const put = (sel, val) => { const el = qs(sel, tr); if (el) el.textContent = fmtNum(val); };
    put('[data-camt]', camt); put('[data-samt]', samt); put('[data-iamt]', iamt);
    put('[data-amt]', amount); put('[data-total]', total);
    tq += q; gross += amount; gstAmt += camt + samt + iamt;
  });
  freightAmt = pnfValue($('f-pnf') ? $('f-pnf').value : 0);
  const net = gross + gstAmt + freightAmt;
  if ($('po-sum-qty')) $('po-sum-qty').value = fmtNum(tq);
  if ($('po-sum-gross')) $('po-sum-gross').value = fmtINR(gross);
  if ($('po-sum-gst')) $('po-sum-gst').value = fmtINR(gstAmt);
  if ($('po-sum-freight')) $('po-sum-freight').value = fmtINR(freightAmt);
  if ($('po-sum-net')) $('po-sum-net').value = fmtINR(net);
}
/* "Nil" (or blank) means zero P&F */
function pnfValue(v) {
  const s = String(v == null ? '' : v).trim();
  if (!s || /^nil{1,2}$/i.test(s)) return 0;
  return pf(s);
}
/* mirror the supplier's GST type onto the PO and re-split every line's GST */
/* supplier changed → refill its trades, clear the old pick, refresh GST */
function onPoSupplierChange(supplier) {
  const c = CBOX['po-trade'];
  if (c) {
    c.opts = partyTradeOptions(supplier);
    c.val = ''; c.display = null; c.typed = false;
    const inp = qs('[data-cb-input="po-trade"]');
    if (inp) inp.value = '';
    renderCboxList('po-trade');
  }
  applyPoGstType(supplier);
  onPoTradeChange('');
}
/* a trade carries its own GST registration, so its state drives the tax split */
function onPoTradeChange(tradeName) {
  const t = partyTradeByName(cboxGet('po-supplier'), tradeName);
  const gstType = (t && t.gstType) ? t.gstType : '';
  const el = $('f-gsttype'); if (el) el.value = gstType;
  const hint = $('f-gsttype-hint');
  if (hint) {
    hint.textContent = t && gstType ? ('Auto from Supplier Trade Name' + (t.state ? ' — ' + t.state : '')) : 'Select a Supplier Trade Name with State / GST Type configured';
    hint.className = t && gstType ? 'fhint ok' : 'fhint';
  }
  qsa('#po-items tr').forEach(function (tr) {
    const nm = qs('[data-f="name"]', tr);
    if (nm && nm.value) poApplyItemDefaults(tr, cboxGet(nm.dataset.cbInput) || nm.value, null, null);
  });
  calcPO();
}
function onPoGstTypeChange(v) {
  /* retained for compatibility; GST Type is controlled by Supplier Trade */
  const el = $('f-gsttype'); if (el) el.value = v || '';
  calcPO();
}
function applyPoGstType() {
  const trade = cboxGet('po-trade');
  if (trade) { onPoTradeChange(trade); return; }
  const el = $('f-gsttype'); if (el) el.value = '';
  const hint = $('f-gsttype-hint');
  if (hint) { hint.textContent = 'Select Supplier Trade Name to determine GST Type'; hint.className = 'fhint'; }
  poSyncGstFromMaster();
  calcPO();
}
function poCategoryFromItems(items, prev) {
  const cls = [...new Set((items || []).map(i => (i.classification || '').trim()).filter(Boolean))];
  if (cls.length === 1) return cls[0];
  if (cls.length > 1) return 'Combined';
  return prev ? (prev.category || '') : '';
}
function savePO(id) {
  clearReqMarks();
  /* validate in the same order the fields appear on screen */
  if (!cboxGet('po-supplier')) return reqBad('po-supplier', 'Supplier is required');
  if (!cboxGet('po-trade')) return reqBad('po-trade', 'Supplier Trade Name is required');
  if (!(($('f-gsttype') || {}).value || '').trim()) return reqBad('f-gsttype', 'GST Type could not be determined — configure State / GST Type on the selected Supplier Trade Name');
  if (!cboxGet('po-shipto')) return reqBad('po-shipto', 'Ship To is required');
  if (!cboxGet('po-buyer')) return reqBad('po-buyer', 'Buyer is required');
  if (!mcboxGet('po-styles').length) return reqBad('po-styles', 'Style (Internal ID) is required — pick at least one style');
  const items = gatherRows('po-items', ['name']).map(i => {
    const amt = pf(i.qty) * pf(i.rate);
    i.camt = amt * pf(i.cgstPct) / 100; i.samt = amt * pf(i.sgstPct) / 100; i.iamt = amt * pf(i.igstPct) / 100;
    i.discount = 0;
    i.amount = amt;
    i.totalAmount = amt + i.camt + i.samt + i.iamt;
    return i;
  });
  if (!requireRowFields('po-items', [
        { f:'classification', label:'Classification' },
        { f:'name',           label:'Item Name' }
      ], 'item row')) return false;
  if (!items.length) return reqBadCell('po-items', 0, 'name', 'Add at least one item');
  const seenPo = {};
  for (let k = 0; k < items.length; k++) {
    const key = lcName(bareItemName(items[k].name));
    if (key && seenPo[key]) return reqBadCell('po-items', k, 'name', 'Duplicate item \u2014 ' + bareItemName(items[k].name) + ' appears more than once. Keep a single line per item.');
    seenPo[key] = 1;
  }
  const zeroIdx = items.findIndex(function (i) { return pf(i.qty) <= 0; });
  if (zeroIdx > -1) return reqBadCell('po-items', zeroIdx, 'qty', 'Quantity must be more than zero — ' + (items[zeroIdx].name || 'item'));
  /* line GST must equal the rate configured on Item Master */
  for (let k = 0; k < items.length; k++) {
    const gerr = poGstMismatch(items[k]);
    if (gerr) return reqBadCell('po-items', k, 'name', gerr);
  }
  if (!cboxGet('po-raisedfor')) return reqBad('po-raisedfor', 'Raised For is required');
  if (!dval('f-delivery-date')) return reqBad('f-delivery-date', 'Delivery Date is required');
  const poDateVal = dval('f-date') || today();
  if (dval('f-delivery-date') < poDateVal)
    return reqBad('f-delivery-date', 'Delivery Date must be the same as or later than the PO Date (' + dpToDisplay(poDateVal) + ')');
  const prev = id ? db.po.find(r => r.id === id) : null;
  const partyLocked = !!(prev && poHasInward(prev.no));
  const data = {
    firm: prev ? (prev.firm || '') : (db.masters.firms[0] || ''), date: dval('f-date') || today(), supplier: partyLocked ? prev.supplier : cboxGet('po-supplier'),
    billTo: prev ? (prev.billTo || '') : '', shipTo: cboxGet('po-shipto'), gstType: (($('f-gsttype') || {}).value) || '',
    supplierTrade: partyLocked ? prev.supplierTrade : cboxGet('po-trade'),
    poType: prev ? (prev.poType || '') : '', buyer: cboxGet('po-buyer'),
    styles: mcboxGet('po-styles'),
    bom: prev && Array.isArray(prev.bom) ? prev.bom.slice() : [], bomType: prev ? (prev.bomType || '') : '',
    classification: prev ? (prev.classification || '') : '',
    category: poCategoryFromItems(items, prev),
    items, status: prev ? (prev.status || 'Draft') : 'Draft',
    deliveryAddress: prev ? (prev.deliveryAddress || '') : '', deliveryDate: dval('f-delivery-date'),
    closeDate: prev ? (prev.closeDate || '') : '', raisedFor: cboxGet('po-raisedfor'),
    qtnNo: $('f-qtn-no').value.trim(), qtnDate: dval('f-qtn-date'),
    payTerms: cboxGet('po-payterms'), freightCharges: cboxGet('po-freightcharges'),
    pnf: pnfValue($('f-pnf').value), transitInsurance: cboxGet('po-transitins'),
    transportMode: cboxGet('po-transport'), narration: $('f-narration').value.trim(),
    terms: rteGet()
  };
  if (id) Object.assign(db.po.find(r => r.id === id), data);
  else { data.id = uid(); data.no = nextNo('po','PO'); data.createdAt = Date.now(); db.po.push(data); }
  const poNoSaved = id ? ((db.po.find(r => r.id === id) || {}).no || '') : data.no;
  saveDB(); pageView = null; toast('PO saved — ' + poNoSaved, 'green'); render();
}

/* ================= INWARD ================= */
function poOptions() {
  return db.po.filter(p => !isInactiveDoc(p)).sort((a, b) => b.date < a.date ? -1 : 1)
    .map(p => ({ v: p.no, l: p.no + ' · ' + (p.supplier || '') + ' (' + (p.category || '') + ')' }));
}
/* qty already received for this PO + item across earlier inwards (excluding the row being edited) */
function inwardReceivedSoFar(poRef, itemName, colour, excludeId) {
  if (!poRef || !itemName) return 0;
  const nm = String(itemName).trim().toLowerCase();
  const cl = String(colour == null ? '' : colour).trim().toLowerCase();
  let tot = 0;
  db.inward.forEach(function (r) {
    if (r.poRef !== poRef) return;
    if (excludeId && r.id === excludeId) return;
    (r.items || []).forEach(function (i) {
      if (String(i.name || '').trim().toLowerCase() !== nm) return;
      const ic = String(i.color || i.colour || '').trim().toLowerCase();
      if (cl && ic && ic !== cl) return;
      tot += pf(i.recQty);
    });
  });
  return tot;
}
/* =====================================================================
   DOWNSTREAM-USE LOCKS
   A record (or a single item line) can no longer be deleted once a later
   document already consumes it.
   ===================================================================== */
function lcName(v) { return String(v == null ? '' : v).trim().toLowerCase(); }

/* --- PO level: is there any Inward booked against this PO? --- */
function poInwardEntries(poNo) {
  if (!poNo) return [];
  const no = String(poNo).trim();
  return db.inward.filter(function (r) { return String(r.poRef || '').trim() === no && r.status !== 'Rejected'; });
}
function poHasInward(poNo) { return poInwardEntries(poNo).length > 0; }
/* --- PO item level: has this specific item been received on this PO? --- */
function poItemHasInward(poNo, itemName, itemCode) {
  const nm = lcName(bareItemName(itemName));
  const cd = lcName(itemCode || '');
  if (!poNo || (!nm && !cd)) return false;
  return poInwardEntries(poNo).some(function (r) {
    return (r.items || []).some(function (i) {
      if (pf(i.recQty) <= 0) return false;
      const inN = lcName(bareItemName(i.name));
      const inC = lcName(i.code || '');
      if (nm && inN && nm === inN) return true;
      if (cd && inC && cd === inC) return true;
      return false;
    });
  });
}
/* the reference other documents use to point back at an inward entry:
   PO receipt -> the PO no, Opening Stock -> OS-####, Job Work Return -> JWR-#### */
function inwardRef(rec) {
  if (!rec) return '';
  if (rec.poRef) return rec.poRef;
  if (rec.entryType === 'Opening Stock') return osRefFor(rec.no);
  if (rec.entryType === 'Job Work Return') return osRefFor(rec.no).replace('OS-', 'JWR-');
  return '';
}
/* --- Inward level: is any outward document consuming this stock? --- */
function issueUsesInward(s, inwardNo) {
  return String(inwardNoForIssue(s) || '') === String(inwardNo || '');
}
function prUsesInward(r, inwardNo) {
  if (!r || !inwardNo) return false;
  if (r.inwardNo) return String(r.inwardNo) === String(inwardNo);
  return false;
}
function inwardOutwardUsers(rec) {
  const no = rec && rec.no; if (!no) return [];
  const out = [];
  function scan(list, label) {
    (list || []).forEach(function (j) {
      const hit = (j.issues || []).concat(j.extras || []).some(function (s) {
        return issueUsesInward(s, no) && pf(s.actualQty != null ? s.actualQty : s.qty) > 0;
      });
      if (hit) out.push(label + ' ' + j.no);
    });
  }
  scan(db.jobwork, 'Job Work');
  scan(db.sample, 'Sample');
  db.purchasereturn.forEach(function (r) {
    if (!prUsesInward(r, no)) return;
    if ((r.items || []).some(function (i) { return pf(i.retQty) > 0; })) out.push('Purchase Return ' + r.no);
  });
  return out;
}
function inwardHasOutward(rec) { return inwardOutwardUsers(rec).length > 0; }
/* --- Inward item level: is this specific item consumed downstream? --- */
function inwardItemHasOutward(rec, itemName) {
  const no = rec && rec.no, nm = lcName(itemName);
  if (!no || !nm) return false;
  const inOut = function (list) {
    return (list || []).some(function (j) {
      return (j.issues || []).concat(j.extras || []).some(function (s) {
        return issueUsesInward(s, no) && lcName(s.item) === nm && pf(s.actualQty != null ? s.actualQty : s.qty) > 0;
      });
    });
  };
  if (inOut(db.jobwork) || inOut(db.sample)) return true;
  return db.purchasereturn.some(function (r) {
    return prUsesInward(r, no) && (r.items || []).some(function (i) { return lcName(i.name) === nm && pf(i.retQty) > 0; });
  });
}
/* why a whole record cannot be deleted — '' when it can */
function deleteBlockReason(m, r) {
  if (m === 'po' && poHasInward(r.no)) {
    const n = poInwardEntries(r.no).length;
    return n + ' Inward entr' + (n > 1 ? 'ies are' : 'y is') + ' booked against this PO';
  }
  if (m === 'inward') {
    const users = inwardOutwardUsers(r);
    if (users.length) return 'Inward ' + (r.no || '') + ' is already used in ' + users.slice(0, 3).join(', ') + (users.length > 3 ? ' +' + (users.length - 3) + ' more' : '');
  }
  if (m === 'jobwork') {
    const recv = jwTotalReceived(r);
    if (recv > 0) return 'Finished goods already received (' + fmtNum(recv) + ') — delete the Receive FG entries first';
    const direct = (db.jobworkreturn || []).filter(function (x) { return x.jwRef === r.no; });
    const inwardReturns = (db.inward || []).filter(function (x) { return x.entryType === 'Job Work Return' && x.jwRef === r.no; });
    const n = direct.length + inwardReturns.length;
    if (n) return n + ' Job Work Return entr' + (n > 1 ? 'ies are' : 'y is') + ' booked against this Job Work';
  }
  if (m === 'sample') {
    const recv = jwTotalReceived(r);
    if (recv > 0) return 'Samples already received (' + fmtNum(recv) + ') — delete the receive entries first';
  }
  if (m === 'processoutward') {
    const n = (db.processinward || []).filter(function (x) { return x.outwardRef === r.no && !/cancel/i.test(x.status || ''); }).length;
    if (n) return n + ' Process Inward entr' + (n > 1 ? 'ies are' : 'y is') + ' booked against this outward';
  }
  return '';
}
/* grey out + disable every input in a row that must not change */
function lockTableRow(tr, why) {
  if (!tr) return;
  tr.classList.add('row-locked');
  tr.title = why || 'Locked — used by a later entry';
  qsa('input,select,textarea,button', tr).forEach(function (el) {
    // v5: keep the Edit pencil button enabled so user can unlock the row to delete
    if (el.classList && (el.classList.contains('jw-row-edit') || el.classList.contains('po-row-edit'))) {
      el.disabled = false;
      return;
    }
    el.disabled = true;
    if (el.tagName === 'BUTTON') el.title = why || 'Cannot delete — used by a later entry';
  });
}
function rowItemName(tr) {
  const el = qs('[data-f="name"]', tr) || qs('[data-f="item"]', tr);
  if (!el) return '';
  let v = '';
  if (el.dataset.cbInput) v = cboxGet(el.dataset.cbInput) || el.value;
  else if (el.dataset.cb) v = cboxGet(el.dataset.cb) || el.value;
  else v = el.value;
  return bareItemName(v);
}
/* PO form: lock every item line already received on this PO */
function poLockReceivedRows() {
  let poNo = '';
  if (formCtx && formCtx.id) {
    const rec = db.po.find(function (p) { return p.id === formCtx.id; });
    if (rec) poNo = rec.no || '';
  }
  if (!poNo && $('f-no')) poNo = ($('f-no').value || '').trim();
  if (!poNo) return;
  if(poHasInward(poNo)){
    ['po-supplier','po-trade'].forEach(function(id){const box=qs('[data-cb="'+id+'"]'),inp=qs('[data-cb-input="'+id+'"]');if(box){box.classList.add('cbox-ro');box.title='Locked — Inward is already booked against this PO';}if(inp)inp.disabled=true;});
  }
  let locked = 0;
  qsa('#po-items tr').forEach(function (tr) {
    const nm = rowItemName(tr);
    const codeEl = qs('[data-f="code"]', tr);
    const code = codeEl ? String(codeEl.value || '').trim() : '';
    if (!nm && !code) return;
    if (poItemHasInward(poNo, nm, code)) {
      lockTableRow(tr, 'Inward already exists for this item — delete is disabled');
      const del = qs('button.btn-icon.danger, button.btn-icon', tr);
      if (del) { del.disabled = true; del.title = 'Cannot delete — inward exists for this item'; }
      locked++;
    }
  });
  showLockNote('po-items', locked, locked + ' item line' + (locked > 1 ? 's are' : ' is') + ' locked — inward is already booked against this item.');
}
/* Inward form: lock every item line already consumed by an outward document */
function inwardLockConsumedRows() {
  const rec = (formCtx && formCtx.id) ? db.inward.find(function (r) { return r.id === formCtx.id; }) : null;
  if (!rec) return;
  let locked = 0;
  qsa('#inward-items tr').forEach(function (tr) {
    const nm = rowItemName(tr);
    if (nm && inwardItemHasOutward(rec, nm)) { lockTableRow(tr, 'Inward ' + rec.no + ' — this item is already used on a later entry and cannot be changed or deleted'); locked++; }
  });
  showLockNote('inward-items', locked, locked + ' item line' + (locked > 1 ? 's are' : ' is') + ' locked — Inward ' + rec.no + ' is already used (Job Work / Sample / Purchase Return).');
}
function showLockNote(tbodyId, count, msg) {
  const tb = $(tbodyId); if (!tb) return;
  const wrap = tb.closest('.table-wrap'); if (!wrap) return;
  const old = wrap.parentNode.querySelector('.lock-note[data-for="' + tbodyId + '"]');
  if (old) old.remove();
  if (!count) return;
  wrap.insertAdjacentHTML('afterend', '<div class="lock-note" data-for="' + tbodyId + '">' + ic('lock', 15) + '<span>' + esc(msg) + '</span></div>');
}

/* warn (never block) when a line is received beyond the PO quantity */
function inwardCheckQty(el) {
  const tr = el.closest('tr'); if (!tr) return;
  const poQty = pf(tr.dataset.poqty);
  const prev = pf(tr.dataset.recvd);
  const now = pf(el.value);
  const over = (prev + now) - poQty;
  const bad = poQty > 0 && over > 0;
  tr.classList.toggle('row-over', bad);
  if (bad && !tr.dataset.warned) {
    tr.dataset.warned = '1';
    const nm = tr.querySelector('[data-f="name"]');
    toast('Over receipt on ' + ((nm && nm.value) || 'this item') + ' — PO ' + fmtNum(poQty) +
          ', already received ' + fmtNum(prev) + ', entering ' + fmtNum(now) +
          ' (excess ' + fmtNum(over) + ')', 'red');
  }
  if (!bad) delete tr.dataset.warned;
}
/* GST split follows the supplier's GST type, same rule as the PO */
function inwardGstMode() {
  const po = db.po.find(p => p.no === (CBOX['in-po'] ? CBOX['in-po'].val : ''));
  let t = po ? (po.gstType || '') : '';
  if (!t) t = partyGstType($('f-supplier') ? $('f-supplier').value : '') || 'IGST';
  return String(t).toUpperCase().indexOf('CGST') > -1 ? 'split' : 'igst';
}
function calcInward() {
  const tb = $('inward-items'); if (!tb) return;
  let tq = 0, amt = 0, cgst = 0, sgst = 0, igst = 0;
  const split = inwardGstMode() === 'split';
  qsa('tr', tb).forEach(function (tr) {
    const q = pf((qs('[data-f="recQty"]', tr) || {}).value);
    const rt = pf((qs('[data-f="poRate"]', tr) || {}).value);
    const line = q * rt;
    const cell = qs('.open-line-value', tr) || qs('[data-val]', tr); if (cell) cell.textContent = fmtNum(line);
    const gp = pf(tr.dataset.gst);
    if (split) { cgst += line * (gp / 2) / 100; sgst += line * (gp / 2) / 100; }
    else igst += line * gp / 100;
    tq += q; amt += line;
  });
  const fr = pf(($('in-freight') || {}).value);
  const frGst = pf(cboxGet('in-freight-gst-cb'));
  if (split) { cgst += fr * (frGst / 2) / 100; sgst += fr * (frGst / 2) / 100; }
  else igst += fr * frGst / 100;

  const before = amt + fr;
  const after = before + cgst + sgst + igst;
  const grand = Math.round(after);
  const round = grand - after;

  const put = function (id, v) { const el = $(id); if (el) el.textContent = fmtNum(v.toFixed ? +v.toFixed(2) : v); };
  if ($('in-sum-qty')) $('in-sum-qty').value = fmtNum(tq);
  if ($('in-sum-amt')) $('in-sum-amt').value = fmtINR(+amt.toFixed(2));
  put('in-t-freight', +fr.toFixed(2));
  put('in-t-before', +before.toFixed(2));
  put('in-t-sgst', +sgst.toFixed(2));
  put('in-t-cgst', +cgst.toFixed(2));
  put('in-t-igst', +igst.toFixed(2));
  put('in-t-after', +after.toFixed(2));
  put('in-t-round', +round.toFixed(2));
  put('in-t-grand', grand);
  renumberSr($('inward-items'));
}
function inwardAttRow(a) {
  a = a || {};
  const id = rid('inatt'), mandatory = /^supplier invoice$/i.test(String(a.name || '').trim());
  if (a.data) ATTDATA[id] = { data: a.data, fileName: a.fileName || '', locked: mandatory };
  else if (mandatory) ATTDATA[id] = { data:'', fileName:'', locked:true };
  return '<tr' + (mandatory ? ' data-mandatory-invoice="1"' : '') + '>' +
    srTd() +
    '<td><input class="inp" data-f="attName" value="' + esc(a.name || '') + '" placeholder="e.g. Invoice"' + (mandatory ? ' disabled title="Supplier Invoice is mandatory"' : '') + '></td>' +
    '<td class="att-cell" data-att="' + id + '">' + attCellHtml(id) + '</td>' +
    '<td class="att-preview-cell"><button type="button" class="btn btn-ghost btn-sm"' + ((ATTDATA[id] && ATTDATA[id].data) ? ' onclick="viewInwardAttachment(\'' + id + '\')"' : ' disabled') + '>' + ic('view', 13) + 'Preview</button></td>' +
    '<td class="actions-cell">' +
      '<button type="button" class="btn-icon" title="Add row" onclick="addRow(\'inward-atts\', inwardAttRow)">' + ic('plus', 15) + '</button>' +
      (mandatory ? '<button type="button" class="btn-icon danger" disabled title="Supplier Invoice cannot be deleted">' + ic('trash', 15) + '</button>' : '<button type="button" class="btn-icon danger" title="Remove" onclick="removeRow(this)">' + ic('trash', 15) + '</button>') +
    '</td></tr>';
}
const ATTDATA = {};
function attCellHtml(id) {
  const a = ATTDATA[id], accept=' accept="image/*,application/pdf,.pdf"', change=' onchange="onAttFile(this,\'' + id + '\')"';
  if(!(a&&a.data))return '<label class="pod-upload-box empty att-upload-box"><input type="file" data-f="attFile" data-attid="'+id+'"'+accept+change+'><span class="pod-upload-plus">＋</span><span class="pod-upload-title">Upload File</span><span class="pod-upload-help">Image or PDF · max 5 MB</span></label>';
  const pdf=/pdf/i.test(a.fileName||'')||/^data:application\/pdf/i.test(a.data),img=/^data:image\//i.test(a.data),preview=pdf?'<div class="pod-preview-pdf">PDF</div>':img?'<img class="pod-preview-img" src="'+a.data+'" alt="Attachment">':'<div class="pod-preview-pdf">FILE</div>';
  return '<div class="pod-upload-box has att-upload-box">'+(a.locked?'':'<div class="pod-upload-actions"><button type="button" class="pod-del" title="Remove file" onclick="event.stopPropagation();clearAtt(\''+id+'\')">'+ic('trash',11)+'</button></div>')+preview+'<div class="pod-upload-name" title="'+esc(a.fileName||'file')+'">'+esc(a.fileName||'file')+'</div>'+(a.locked?'':'<label class="pod-replace">Replace<input type="file" data-f="attFile" data-attid="'+id+'"'+accept+change+'></label>')+'</div>';
}
function syncAttachmentPreviewButton(id){const cell=qs('[data-att="'+id+'"]'),btn=cell&&cell.closest('tr')?qs('.att-preview-cell .btn',cell.closest('tr')):null,a=ATTDATA[id];if(btn){btn.disabled=!(a&&a.data);btn.onclick=(a&&a.data)?function(){viewInwardAttachment(id);}:null;}}
function viewInwardAttachment(id){const a=ATTDATA[id];if(!a||!a.data)return;const pdf=/pdf/i.test(a.fileName||'')||/^data:application\/pdf/i.test(a.data);if(pdf){const w=window.open('about:blank','_blank');if(!w)return toast('Allow pop-ups to preview the attachment','red');const obj=podObjectUrl(a.data);try{w.location.replace(obj.url);}catch(e){w.location.href=obj.url;}if(obj.revoke)setTimeout(function(){URL.revokeObjectURL(obj.url);},300000);return;}const w=window.open('','_blank');if(!w)return toast('Allow pop-ups to preview the attachment','red');w.document.write('<html><body style="margin:0;background:#1a1a1a;display:flex;align-items:center;justify-content:center;min-height:100vh"><img src="'+a.data+'" style="max-width:95vw;max-height:95vh"></body></html>');}
function clearAtt(id) {
  if(ATTDATA[id]&&ATTDATA[id].locked)return toast('Supplier Invoice is mandatory and cannot be removed','red');
  delete ATTDATA[id];
  const cell = qs('[data-att="' + id + '"]'); if (cell) cell.innerHTML = attCellHtml(id);syncAttachmentPreviewButton(id);
}
function onAttFile(inp, id) {
  const f = inp.files && inp.files[0]; if (!f) return;
  const valid=/^image\//i.test(f.type)||/pdf/i.test(f.type)||/\.(pdf|png|jpe?g|webp)$/i.test(f.name);if(!valid){inp.value='';return toast('Upload an image or PDF attachment','red');}
  if (f.size > 5*1024*1024) return toast('File too large — keep attachments under 5 MB', 'red');
  const reader = new FileReader();
  reader.onload = function (e) {
    const locked=!!(ATTDATA[id]&&ATTDATA[id].locked);ATTDATA[id] = { data: e.target.result, fileName: f.name, locked:locked };
    const cell = qs('[data-att="' + id + '"]'); if (cell) cell.innerHTML = attCellHtml(id);syncAttachmentPreviewButton(id);
    toast('Attached: ' + f.name, 'green');
  };
  reader.readAsDataURL(f);
}
function validateInwardAttachments(){const rows=qsa('#inward-atts tr');for(let i=0;i<rows.length;i++){const tr=rows[i],nameEl=qs('[data-f="attName"]',tr),name=String((nameEl&&nameEl.value)||'').trim(),cell=qs('[data-att]',tr),a=cell?ATTDATA[cell.dataset.att]:null,hasFile=!!(a&&a.data);if(hasFile&&!name)return reqBad(nameEl,'Attachment Name is required when a file is uploaded — row '+(i+1));if(name&&!hasFile)return reqBad(qs('.att-upload-box',tr)||cell,'Attachment file is required when Attachment Name is entered — row '+(i+1));}return true;}
function gatherAttachments() {
  const out = [];
  qsa('tr', $('inward-atts')).forEach(function (tr) {
    const nm = (qs('[data-f="attName"]', tr) || {}).value || '';
    const cell = qs('[data-att]', tr);
    const id = cell ? cell.dataset.att : '';
    const a = ATTDATA[id];
    if (nm.trim() || (a && a.data)) out.push({ name: nm.trim(), data: a ? a.data : '', fileName: a ? a.fileName : '' });
  });
  return out;
}
/* flip the Inward form between a PO receipt and an opening-stock entry */
function setInwardType(t) {
  const action=(pageView&&pageView.id)?'edit':'create';if(!userCanInward(t,action))return toast(action.charAt(0).toUpperCase()+action.slice(1)+' access denied for '+t,'red');
  const el = $('f-entrytype'); if (!el) return;
  if(t==='PO Receipt'&&el.value!=='PO Receipt'&&!(pageView&&pageView.id)){return requestAgainstPoInvoice();}
  if (el.value === t) return;
  el.value = t;
  const opening = (t === 'Opening Stock');
  const jwret = (t === 'Job Work Return');
  /* keep whatever is already typed, just re-render the form in the new mode */
  const rec = (pageView && pageView.id) ? db.inward.find(function (x) { return x.id === pageView.id; }) : null;
  const draft = Object.assign({}, rec || {}, {
    entryType: t,
    date: dval('f-date') || today(),
    receivedBy: ($('f-by') || {}).value || '',
    warehouse: cboxGet('in-warehouse'),
    items: []
  });
  $('content').innerHTML = '<div class="entry-page">' + formInward(draft) + '</div>';
  dpSyncAll(); calcInward();
  toast(opening ? 'Opening stock mode — enter physical stock on hand'
    : jwret ? 'Job work return mode — material coming back from the job worker'
    : 'PO receipt mode', 'green');
}
/* a free-entry row: pick any item, type the quantity you physically hold */

function openingStockHeaders(){ return ['Item Code','Opening Qty','Rate']; }
function exportOpeningStockTemplate(){
  const headers=openingStockHeaders();
  const items=(db.masters.items||[]).filter(function(it){return it.active!==false;});
  const rows=items.length
    ? items.map(function(it){return [it.code||it.id||'', '', ''];})
    : [['','','']];
  const sheets=[
    {name:'Opening Stock',headers:headers,rows:rows},
    {name:'Instructions',headers:['Field','Required','Notes'],rows:[
      ['Item Code','Yes','Item Master ID, e.g. ITM-0012'],
      ['Opening Qty','Yes','Physical quantity on hand — must be greater than 0'],
      ['Rate','Yes','Rate per unit — 0 allowed'],
      ['Tip','','Fill Opening Qty and Rate only. Classification, Item Name and Unit come from Item Master on upload.']
    ]}
  ];
  let content='<?xml version="1.0" encoding="UTF-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>';
  let wb='<?xml version="1.0" encoding="UTF-8"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets>';
  let rels='<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
  const files={};
  sheets.forEach(function(sh,i){
    const n=i+1;
    content+='<Override PartName="/xl/worksheets/sheet'+n+'.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
    wb+='<sheet name="'+xesc(sh.name)+'" sheetId="'+n+'" r:id="rId'+n+'"/>';
    rels+='<Relationship Id="rId'+n+'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet'+n+'.xml"/>';
    files['xl/worksheets/sheet'+n+'.xml']=xsheet(sh.headers,sh.rows,[]);
  });
  content+='</Types>';wb+='</sheets></workbook>';
  rels+='<Relationship Id="rId'+(sheets.length+1)+'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>';
  files['[Content_Types].xml']=content;
  files['_rels/.rels']='<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>';
  files['xl/workbook.xml']=wb;
  files['xl/_rels/workbook.xml.rels']=rels;
  files['xl/styles.xml']='<?xml version="1.0" encoding="UTF-8"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><color rgb="FFFFFFFF"/><sz val="11"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF303030"/></patternFill></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf/></cellStyleXfs><cellXfs count="2"><xf/><xf fontId="1" fillId="2" applyFont="1" applyFill="1"/></cellXfs></styleSheet>';
  downloadBytes(xzip(files),'Opening_Stock_Template_'+today()+'.xlsx');
  toast('Opening Stock Excel template downloaded — fill Qty & Rate, then upload','green');
}
function triggerOpeningStockImport(){
  const inp=$('open-xlsx-file'); if(!inp) return;
  inp.value=''; inp.click();
}
function resolveOpeningItem(codeOrName){
  const raw=String(codeOrName||'').trim(); if(!raw) return null;
  const items=db.masters.items||[];
  const lc=raw.toLowerCase();
  return items.find(function(i){return String(i.code||'').toLowerCase()===lc;}) ||
    items.find(function(i){return String(i.id||'').toLowerCase()===lc;}) ||
    itemByName(raw) ||
    items.find(function(i){return String(i.name||'').toLowerCase()===lc;}) ||
    null;
}
async function readOpeningStockXlsx(file){
  const buf=await file.arrayBuffer();
  let shared='';
  try{ shared=await unzipEntry(buf,'xl/sharedStrings.xml'); }catch(e){}
  const ss=parseShared(shared);
  const xml=await unzipEntry(buf,'xl/worksheets/sheet1.xml');
  const doc=new DOMParser().parseFromString(xml,'application/xml');
  const out=[];
  Array.from(doc.getElementsByTagName('row')).forEach(function(r,ri){
    if(ri===0) return;
    const row=new Array(3).fill('');
    Array.from(r.getElementsByTagName('c')).forEach(function(c){
      const ref=c.getAttribute('r')||'', letters=(/^[A-Z]+/.exec(ref)||[''])[0];
      let col=0; for(const ch of letters) col=col*26+ch.charCodeAt(0)-64; col--;
      if(col<0||col>=row.length) return;
      const t=c.getAttribute('t'); let v='';
      if(t==='inlineStr'){ const n=c.getElementsByTagName('t')[0]; v=n?n.textContent:''; }
      else { const n=c.getElementsByTagName('v')[0]; v=n?n.textContent:''; if(t==='s') v=ss[+v]||''; }
      row[col]=String(v).trim();
    });
    if(row.some(Boolean)) out.push(row);
  });
  return out;
}
async function importOpeningStockExcel(inp){
  const file=inp.files&&inp.files[0]; if(!file) return;
  let rows;
  try{ rows=await readOpeningStockXlsx(file); }
  catch(e){ toast('Could not read Excel file: '+(e.message||e),'red'); inp.value=''; return; }
  const errors=[], loaded=[];
  const seen={};
  rows.forEach(function(r, idx){
    const code=r[0], qty=pf(r[1]), rate=pf(r[2]);
    const line=idx+2;
    if(!String(code||'').trim() && qty<=0 && rate<=0) return;
    const it=resolveOpeningItem(code);
    if(!it){ errors.push('Row '+line+': Item Code not found in Item Master — '+ (code||'(blank)')); return; }
    if(!(qty>0)){ errors.push('Row '+line+': Opening Qty must be greater than 0 for '+ (it.code||it.name)); return; }
    if(rate<0){ errors.push('Row '+line+': Rate cannot be negative for '+(it.code||it.name)); return; }
    const key=String(it.id||it.code||it.name).toLowerCase();
    if(seen[key]){ errors.push('Row '+line+': duplicate Item Code '+(it.code||it.name)); return; }
    seen[key]=1;
    loaded.push({
      classification: it.classification || '',
      name: it.name,
      recQty: qty,
      poRate: rate,
      unit: it.unit || '',
      gst: it.gst
    });
  });
  if(!loaded.length){
    toast(errors.length ? errors.slice(0,3).join(' | ') : 'No valid opening stock rows found','red');
    inp.value=''; return;
  }
  const tb=$('inward-items');
  if(tb) tb.innerHTML=loaded.map(function(it){ return openItemRow(it); }).join('');
  if(typeof dpSyncAll==='function') dpSyncAll();
  loaded.forEach(function(){});
  if(typeof calcInward==='function') calcInward();
  qsa('#inward-items [data-f="recQty"]').forEach(function(el){ if(typeof calcOpeningValue==='function') calcOpeningValue(el); });
  toast('Uploaded '+loaded.length+' opening stock item'+(loaded.length>1?'s':'')+(errors.length?' — '+errors.length+' row(s) skipped':'') ,'green');
  if(errors.length) toast(errors.slice(0,4).join(' | ')+(errors.length>4?' …':''),'amber');
  inp.value='';
}

function openItemRow(it) {
  it = it || {};
  const clsId = rid('opcl'), itemId = rid('opit');
  const im = itemByName(it.name);
  const gstPct = pf(it.gst) || (im ? pf(im.gst) : 0);
  return '<tr data-poqty="0" data-recvd="0" data-gst="' + gstPct + '">' +
    srTd() +
    '<td>' + cbox(clsId, { opts: db.masters.classifications, value: it.classification || (im ? im.classification : '') || '', f:'classification', placeholder:'Classification', free:true, onSet: function () { jwClassChanged(clsId, itemId); } }) + '</td>' +
    '<td>' + cbox(itemId, { opts: itemOptsFor(it.classification || (im ? im.classification : '') || ''), value: it.name || '', f:'name', placeholder:'Select item', free:true, onSet: function (v) { openRowItemPicked(qs('[data-cb-input="' + itemId + '"]'), v); } }) +
      '<input type="text" data-f="specs" value="' + esc(it.specs || '') + '" style="display:none" tabindex="-1" aria-hidden="true">' +
      '<input type="text" data-f="color" value="' + esc(it.color || '') + '" style="display:none" tabindex="-1" aria-hidden="true"></td>' +
    '<td class="num"><input class="inp num" type="number" min="0" step="0.01" data-f="recQty" value="' + (it.recQty ?? '') + '" placeholder="0" oninput="calcOpeningValue(this)" onchange="calcOpeningValue(this)"></td>' +
    '<td class="num"><input class="inp num" type="number" min="0" step="0.01" data-f="poRate" value="' + (it.poRate ?? '') + '" placeholder="0" oninput="calcOpeningValue(this)" onchange="calcOpeningValue(this)">' +
      '<input type="text" data-f="gst" value="' + gstPct + '" style="display:none" tabindex="-1" aria-hidden="true">' +
'</td>' +
    '<td class="num open-line-value" data-val style="font-weight:650">' + fmtNum(pf(it.recQty) * pf(it.poRate)) + '</td>' +
    '<td class="uom-cell"><input class="inp" data-f="unit" value="' + esc(it.unit || '') + '" placeholder="Auto" disabled></td>' +
    '<td><button type="button" class="btn-icon danger" onclick="removeRow(this);calcInward()">' + ic('trash',15) + '</button></td>' +
  '</tr>';
}
/* Opening Stock value updates immediately as Opening Qty × Rate. Keeping this
   row-level calculation separate makes the Value cell responsive even before
   the complete Inward totals are recalculated. */
function calcOpeningValue(el) {
  const tr = el && el.closest ? el.closest('tr') : null;
  if (tr) {
    const qty = pf((qs('[data-f="recQty"]', tr) || {}).value);
    const rate = pf((qs('[data-f="poRate"]', tr) || {}).value);
    const valueCell = qs('.open-line-value', tr) || qs('[data-val]', tr);
    if (valueCell) valueCell.textContent = fmtNum(qty * rate);
  }
  calcInward();
}
/* one line per item in an opening-stock entry */
function openDupCheck(tr, name) {
  const nm = String(name || '').trim().toLowerCase();
  if (!nm) return false;
  const tb = tr.parentNode; if (!tb) return false;
  let dup = false;
  qsa('tr', tb).forEach(function (other) {
    if (other === tr || dup) return;
    const el = qs('[data-f="name"]', other);
    const v = el ? String(el.dataset.cbInput ? cboxGet(el.dataset.cbInput) : el.value || '').trim().toLowerCase() : '';
    if (v === nm) dup = true;
  });
  if (!dup) return false;
  toast('Already added — ' + name + ' is on another line. Update that quantity instead.', 'red');
  const el = qs('[data-f="name"]', tr);
  if (el && el.dataset.cbInput) { cboxSet(el.dataset.cbInput, ''); el.value = ''; }
  const u = qs('[data-f="unit"]', tr); if (u) u.value = '';
  const q = qs('[data-f="recQty"]', tr); if (q) q.value = '';
  const r = qs('[data-f="poRate"]', tr); if (r) r.value = '';
  calcInward();
  return true;
}
function openRowItemPicked(anyEl, name) {
  const tr = anyEl && anyEl.closest ? anyEl.closest('tr') : null; if (!tr) return;
  if (openDupCheck(tr, name)) return;
  const im = itemByName(name);
  const u = qs('[data-f="unit"]', tr); if (u) u.value = im ? (im.unit || '') : '';
  const g = qs('[data-f="gst"]', tr); if (g) g.value = im ? pf(im.gst) : 0;
  tr.dataset.gst = im ? pf(im.gst) : 0;
  const rate = qs('[data-f="poRate"]', tr);
  if (rate && !pf(rate.value) && im && pf(im.purchaseRate)) rate.value = im.purchaseRate;
  calcInward();
}
function inwardItemRow(it, poQty, excludeId) {
  it = it || {};
  const poRef = (typeof CBOX !== 'undefined' && CBOX['in-po']) ? CBOX['in-po'].val : '';
  const done = inwardReceivedSoFar(poRef, it.name, it.color || it.colour, excludeId);
  const bal = Math.max(0, pf(poQty) - done);
  const im = itemByName(it.name);
  const gstPct = pf(it.gst) || (im ? pf(im.gst) : 0);
  return '<tr data-poqty="' + pf(poQty) + '" data-recvd="' + done + '" data-gst="' + gstPct + '">' +
    srTd() +
    '<td><input class="inp" data-f="classification" value="' + esc(it.classification || (im ? im.classification : '') || '') + '" placeholder="—" disabled></td>' +
    '<td><input class="inp" data-f="name" value="' + esc(itemDisplay(it.name) || it.name || '') + '" placeholder="From PO" disabled>' +
      '<input type="text" data-f="specs" value="' + esc(it.specs || '') + '" style="display:none" tabindex="-1" aria-hidden="true">' +
      '<input type="text" data-f="color" value="' + esc(it.color || '') + '" style="display:none" tabindex="-1" aria-hidden="true"></td>' +
    '<td class="num" style="color:var(--muted);font-weight:600">' + fmtNum(poQty) + '</td>' +
    '<td class="num tdr"><b>' + fmtNum(done) + '</b>' +
      (pf(poQty) > 0 ? '<div class="sub-line">balance ' + fmtNum(bal) + '</div>' : '') + '</td>' +
    '<td><input class="inp num" type="number" min="0" data-f="recQty" value="' + (it.recQty ?? '') + '" placeholder="0" oninput="inwardCheckQty(this);calcInward()"></td>' +
    '<td><input class="inp num" type="number" data-f="poRate" value="' + (it.poRate ?? '') + '" placeholder="Auto" disabled>' +
      '<input type="text" data-f="gst" value="' + gstPct + '" style="display:none" tabindex="-1" aria-hidden="true">' +
'</td>' +
    '<td class="num" data-val style="font-weight:650">0</td>' +
    '<td><input class="inp" data-f="unit" value="' + esc(it.unit || '') + '" placeholder="—" disabled></td>' +
    '<td><button type="button" class="btn-icon danger" onclick="removeRow(this)">' + ic('trash', 15) + '</button></td>' +
  '</tr>';
}
function formInward(rec) {
  const r = rec || {};
  const isEdit = !!(rec && rec.id);
  const opening = r.entryType === 'Opening Stock';
  const jwret   = r.entryType === 'Job Work Return';
  const poMode  = !opening && !jwret;
  return '<div class="sec" style="display:flex;align-items:center;gap:10px;flex-wrap:wrap">Material Inward' +
    (opening
      ? '<span style="margin-left:auto;display:flex;gap:8px;align-items:center">' +
          '<button type="button" class="btn btn-ghost btn-sm" onclick="exportOpeningStockTemplate()">⬇ Excel template</button>' +
          '<button type="button" class="btn btn-ghost btn-sm" onclick="triggerOpeningStockImport()">⬆ Upload Excel</button>' +
          '<input type="file" id="open-xlsx-file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" style="display:none" onchange="importOpeningStockExcel(this)">' +
        '</span>'
      : '') +
  '</div>' +
  '<div class="field" style="max-width:720px">' +
    '<label class="lbl">Entry Type</label>' +
    '<div class="segbar">' +
      '<button type="button" class="seg' + (poMode ? ' on' : '') + (isEdit ? ' locked' : '') + '"' + ((isEdit||!userCanInward('PO Receipt','create')) ? ' disabled title="Entry type unavailable for this user"' : ' onclick="setInwardType(\'PO Receipt\')"') + '>' + ic('inbox',15) + 'Against PO</button>' +
      '<button type="button" class="seg' + (opening ? ' on' : '') + (isEdit ? ' locked' : '') + '"' + ((isEdit||!userCanInward('Opening Stock','create')) ? ' disabled title="Entry type unavailable for this user"' : ' onclick="setInwardType(\'Opening Stock\')"') + '>' + ic('box',15) + 'Opening Stock</button>' +
      '<button type="button" class="seg' + (jwret ? ' on' : '') + (isEdit ? ' locked' : '') + '"' + ((isEdit||!userCanInward('Job Work Return','create')) ? ' disabled title="Entry type unavailable for this user"' : ' onclick="setInwardType(\'Job Work Return\')"') + '>' + ic('receive',15) + 'Job Work Return</button>' +
    '</div>' +
    '<input type="hidden" id="f-entrytype" value="' + esc(r.entryType || 'PO Receipt') + '">' +
    '<div class="fhint" id="f-entrytype-hint">' + (isEdit ? 'Entry type is fixed once the record is saved. ' : '') + (opening
      ? 'Physical stock already on hand — no PO, no supplier invoice.'
      : jwret
        ? 'Material coming back from a job worker into your warehouse.'
        : 'Goods received against a purchase order.') + '</div>' +
  '</div>' +
  '<div class="grid-fixed' + (jwret ? ' in-jwr' : '') + '">' +
    (isEdit ? field('Inward No', '<input class="inp" id="f-no" value="' + esc(r.no || '') + '" disabled>', 'inj-no') : '') +
    field('Date *', '<input class="inp" type="text" data-date autocomplete="off" readonly disabled id="f-date" value="' + esc(r.date || today()) + '">', 'inj-date') +
    '<div class="field po-only"' + (poMode ? '' : ' style="display:none"') + '><label class="lbl">PO Reference *</label>' +
      cbox('in-po', { opts: poOptions(), value: r.poRef || '', f:'poRef', placeholder:'Search PO…', free:false, onSet: onPOSelect }) + '</div>' +
    '<div class="field po-only"' + (poMode ? '' : ' style="display:none"') + '><label class="lbl">Supplier</label>' +
      '<input class="inp" id="f-supplier" value="' + esc(r.supplier || '') + '" placeholder="Auto from PO" disabled></div>' +
    '<div class="field po-only"' + (poMode ? '' : ' style="display:none"') + '><label class="lbl">Invoice No. *</label>' +
      '<input class="inp" id="f-invno" value="' + esc(r.invoiceNo || '') + '" placeholder="Supplier invoice no."></div>' +
    '<div class="field po-only"' + (poMode ? '' : ' style="display:none"') + '><label class="lbl">Invoice Date *</label>' +
      '<input class="inp" type="text" data-date autocomplete="off" readonly id="f-invdate" value="' + esc(r.invoiceDate || '') + '"></div>' +
    (poMode ? field('Warehouse *', cbox('in-warehouse', { opts: db.masters.warehouses, value: r.warehouse || '', f:'warehouse', placeholder:'Select warehouse', addMaster:'warehouses', addText:'Warehouse', free:true }), 'inj-wh') : '') +
    '<div class="field po-only"' + (poMode ? '' : ' style="display:none"') + '><label class="lbl">Vehicle No</label>' +
      '<input class="inp" id="f-vehicle" value="' + esc(r.vehicle || '') + '"></div>' +
    (opening ? '' : field('Received By', '<input class="inp" id="f-by" value="' + esc(r.receivedBy || '') + '">', 'inj-by')) +
    (poMode ? '' : field('Warehouse *', cbox('in-warehouse', { opts: db.masters.warehouses, value: r.warehouse || '', f:'warehouse', placeholder:'Select warehouse', addMaster:'warehouses', addText:'Warehouse', free:true }), 'inj-wh')) +
    '<div class="field open-only"' + (opening ? '' : ' style="display:none"') + '><label class="lbl">Remarks</label>' +
      '<input class="inp" id="f-openremark" value="' + esc(r.openingRemark || '') + '" placeholder="e.g. Stock as on go-live"></div>' +
    '<div class="field jwr-only inj-jw"' + (jwret ? '' : ' style="display:none"') + '><label class="lbl">Job Work No. *</label>' +
      cbox('in-jw', { opts: jwOptions(), value: r.jwRef || '', f:'jwRef', placeholder:'Search Job Work…', free:false, onSet: onInwardJWSelect }) + '</div>' +
    '<div class="field jwr-only inj-ven"' + (jwret ? '' : ' style="display:none"') + '><label class="lbl">Job Worker</label>' +
      '<input class="inp" id="f-jwvendor" value="' + esc(r.vendor || '') + '" placeholder="Auto from Job Work" disabled></div>' +
    '<div class="field jwr-only inj-ch"' + (jwret ? '' : ' style="display:none"') + '><label class="lbl">Challan No</label>' +
      '<input class="inp" id="f-jwchallan" value="' + esc(r.challan || '') + '" placeholder="Job worker challan"></div>' +
  '</div>' +
  (opening
    ? '<div class="sec">Opening Stock Items</div>'
    : jwret
      ? '<div class="sec">Material Returned by Job Worker</div>'
      : '<div class="sec">Items Received (PO qty for reference)</div>') +
  (jwret
    ? '<div class="table-wrap"><table class="mini-table jwr-items-table">' +
        '<colgroup><col class="j-sr"><col class="j-name"><col class="j-iss"><col class="j-alr"><col class="j-qty"><col class="j-rate"><col class="j-val"><col class="j-unit"><col class="j-type"><col class="j-act"></colgroup>' +
        '<thead><tr>' +
        srTh() + '<th>Item Name</th><th>Issued</th><th>Already Returned</th>' +
        '<th>Return Qty</th><th>Rate</th><th>Value</th><th>Unit</th><th>Return Type</th><th></th>' +
      '</tr></thead><tbody id="inward-items">' +
        ((r.items && r.items.length) ? r.items.map(it => jwrItemRow(it, r.id)).join('') : jwrItemRow()) +
      '</tbody></table></div>'
    : '<div class="table-wrap"><table class="mini-table' + (opening ? ' open-stock-table' : ' po-inward-table') + '">' +
      (opening
        ? '<colgroup><col class="c-sr"><col class="c-cls"><col class="c-name"><col class="c-qty"><col class="c-rate"><col class="c-val"><col class="c-unit"><col class="c-act"></colgroup>'
        : '<colgroup><col class="p-sr"><col class="p-cls"><col class="p-name"><col class="p-poq"><col class="p-till"><col class="p-qty"><col class="p-rate"><col class="p-val"><col class="p-unit"><col class="p-act"></colgroup>') +
      '<thead><tr>' + srTh() + '<th>Classification</th><th>Item Name</th>' +
      (opening ? '' : '<th>PO Qty</th><th>Till Date Received</th>') +
      '<th>' + (opening ? 'Opening Qty' : 'Recv Qty') + '</th>' +
      '<th>' + (opening ? 'Rate' : 'PO Rate') + '</th>' +
      '<th>Value</th><th>Unit</th><th></th></tr></thead>' +
    '<tbody id="inward-items">' +
      (r.items && r.items.length
        ? r.items.map(it => opening ? openItemRow(it) : inwardItemRow(it, it.poQty, r.id)).join('')
        : (opening ? openItemRow() : inwardItemRow())) +
    '</tbody></table></div>') +
  (opening
    ? '<button class="btn btn-ghost btn-sm" style="margin-top:10px" onclick="addRow(\'inward-items\', openItemRow)">' + ic('plus',15) + 'Add item</button>'
    : '') +

  '<div class="grid-even5" style="margin-top:18px">' +
    field('Total Quantity', '<input class="inp" id="in-sum-qty" value="0" disabled>') +
    field('Total Amount', '<input class="inp" id="in-sum-amt" value="₹0" disabled>') +
    (poMode
      ? field('Freight Charges', '<input class="inp num" type="number" min="0" step="0.01" id="in-freight" value="' + esc(r.freight ?? 0) + '" oninput="calcInward()">') +
        field('Freight GST%', cbox('in-freight-gst-cb', { opts: db.masters.gstRates, value: (r.freightGst === '' || r.freightGst == null) ? '0' : String(r.freightGst), f:'freightGst', placeholder:'--GST %--', addMaster:'gstRates', addText:'GST %', free:false, onSet: function () { calcInward(); } })) +
        field('Freight HSN Code', '<input class="inp" id="in-freight-hsn" inputmode="numeric" pattern="[0-9]*" maxlength="12" value="' + esc(r.freightHsn || '') + '" oninput="this.value=this.value.replace(/[^0-9]/g,\'\')" placeholder="Numbers only">')
      : '') +
  '</div>' +

  '<div class="inward-bottom-grid' + (poMode ? '' : ' single') + '"><section class="inward-attachments-panel">' +
  '<div class="sec">Attachments</div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' + srTh() + '<th>Attachment Name</th><th>Attachment</th><th>Preview</th><th class="num">Add/Remove</th></tr></thead>' +
  '<tbody id="inward-atts">' + ((r.attachments && r.attachments.length) ? r.attachments.map(inwardAttRow).join('') : inwardAttRow()) + '</tbody></table></div></section>' +

  (poMode
    ? '<section class="inward-summary-panel"><div class="sec">Summary</div>' +
      '<div class="intot-wrap"><table class="intot">' +
        '<tr><th>Freight Charges</th><td class="num" id="in-t-freight">0.00</td></tr>' +
        '<tr><th>Total (Before Tax)</th><td class="num" id="in-t-before">0.00</td></tr>' +
        '<tr><th>SGST</th><td class="num" id="in-t-sgst">0.00</td></tr>' +
        '<tr><th>CGST</th><td class="num" id="in-t-cgst">0.00</td></tr>' +
        '<tr><th>IGST</th><td class="num" id="in-t-igst">0.00</td></tr>' +
        '<tr><th>Total (After Tax)</th><td class="num" id="in-t-after">0.00</td></tr>' +
        '<tr><th>Round Off</th><td class="num" id="in-t-round">0.00</td></tr>' +
        '<tr class="grand"><th>Grand Total</th><td class="num" id="in-t-grand">0.00</td></tr>' +
      '</table></div></section>'
    : '') +
  '</div>';
}
function onPOSelect(poNo) {
  const po = db.po.find(p => p.no === poNo);
  const sup = $('f-supplier');
  const excl = (pageView && pageView.kind === 'form' && pageView.m === 'inward') ? pageView.id : null;
  if (!po) { if (sup) sup.value = ''; $('inward-items').innerHTML = inwardItemRow(); dpSyncAll(); calcInward(); return; }
  // Block further inward entry until the PO is Approved (or already in receipt).
  if (po && !['Approved','Partially Received','Received'].includes(po.status)) {
    toast('Inward blocked: PO ' + po.no + ' is "' + (po.status || 'Draft') + '" — approve the PO before booking inward.', 'red');
    if (typeof cboxSet === 'function') cboxSet('in-po', '');
    if (sup) sup.value = '';
    $('inward-items').innerHTML = inwardItemRow();
    dpSyncAll(); calcInward();
    return;
  }
  if (sup) sup.value = po.supplier || '';
  $('inward-items').innerHTML = po.items.map(i => inwardItemRow(
    { name: i.name, classification: i.classification, specs: i.specs, color: i.color, recQty: 0, unit: i.unit || 'm',
      poRate: i.rate, gst: pf(i.cgstPct) + pf(i.sgstPct) + pf(i.igstPct) }, i.qty, excl)).join('') || inwardItemRow();
  dpSyncAll(); calcInward(); inwardLockConsumedRows();
}
function saveInward(id) {
  clearReqMarks();
  const entryType = (($('f-entrytype') || {}).value) || 'PO Receipt';
  if(!userCanInward(entryType,id?'edit':'create'))return toast((id?'Edit':'Create')+' access denied for '+entryType,'red');
  const opening = entryType === 'Opening Stock';
  const jwret = entryType === 'Job Work Return';
  const poMode = !opening && !jwret;
  const items = gatherRows('inward-items', ['name']).map(function (i) { i.name = bareItemName(i.name); return i; })
    .filter(function (i) { return pf(i.recQty) > 0; });
  if(!validateInwardAttachments())return false;
  const attachments=gatherAttachments();
  if(poMode&&!attachments.some(function(a){return a&&a.data&&(/invoice/i.test(a.name||'')||/invoice/i.test(a.fileName||''));}))return reqBad(qs('#inward-atts input[type=file]'),'Supplier Invoice attachment is mandatory for Against PO Inward');
  if (jwret && !cboxGet('in-jw')) return reqBad('in-jw', 'Job Work number is required');
  if (poMode && !cboxGet('in-po')) return reqBad('in-po', 'PO Reference is required');
  if (poMode) {
    const poHit = db.po.find(function (p) { return p.no === cboxGet('in-po'); });
    if (poHit && isInactiveDoc(poHit)) return reqBad('in-po', 'PO ' + poHit.no + ' is ' + String(poHit.status).toLowerCase() + ' — reopen it to book inward');
    if (poHit && !['Approved','Partially Received','Received'].includes(poHit.status)) {
      return reqBad('in-po', 'PO ' + poHit.no + ' is "' + (poHit.status || 'Draft') + '" — approve the PO before booking inward');
    }
  }
  if (poMode) {
    if (!($('f-invno').value || '').trim()) return reqBad('f-invno', 'Invoice No. is required');
    if (!dval('f-invdate')) return reqBad('f-invdate', 'Invoice Date is required');
    const poDateRec = db.po.find(function (p) { return p.no === cboxGet('in-po'); });
    if (poDateRec && poDateRec.date && dval('f-invdate') < poDateRec.date) {
      return reqBad('f-invdate', 'Invoice Date must be the same as or later than the PO Date (' + dpToDisplay(poDateRec.date) + ')');
    }
  }
  if (!cboxGet('in-warehouse')) return reqBad('in-warehouse', 'Warehouse is required');
  if (poMode) {
    const invNo = ($('f-invno').value || '').trim().toLowerCase();
    const poNo = cboxGet('in-po');
    const clash = db.inward.find(function (r) {
      return r.id !== id && r.poRef === poNo && String(r.invoiceNo || '').trim().toLowerCase() === invNo;
    });
    if (clash) return reqBad('f-invno', 'Invoice ' + ($('f-invno').value || '').trim() + ' is already recorded against ' + poNo + ' in ' + clash.no + ' — duplicate invoice numbers are not allowed');
  }
  const zeroIn = items.find(function (i) { return pf(i.recQty) <= 0; });
  if (zeroIn) return reqBadCell('inward-items', reqFirstEmptyRow('inward-items', 'recQty'), 'recQty', 'Quantity must be more than zero — ' + (zeroIn.name || 'item'));
  if (!items.length) return reqBadCell('inward-items', 0, opening ? 'name' : 'recQty', (opening || jwret) ? 'Enter a quantity for at least one item' : 'Add at least one item row');
  if (opening) {
    const seen = {};
    for (let k = 0; k < items.length; k++) {
      const key = String(items[k].name || '').trim().toLowerCase();
      if (key && seen[key]) return reqBadCell('inward-items', k, 'name', 'Duplicate item — ' + items[k].name + ' appears more than once');
      seen[key] = 1;
    }
  }
  const prev = id ? db.inward.find(r => r.id === id) : null;
  const data = {
    entryType: entryType,
    openingRemark: opening ? ((($('f-openremark') || {}).value) || '').trim() : '',
    jwRef: jwret ? cboxGet('in-jw') : '',
    vendor: jwret ? (($('f-jwvendor') || {}).value || '').trim() : '',
    challan: jwret ? (($('f-jwchallan') || {}).value || '').trim() : '',
    date: dval('f-date') || today(), poRef: (opening || jwret) ? '' : cboxGet('in-po'),
    grn: prev ? (prev.grn || '') : '',
    invoiceNo: (opening || jwret) ? '' : (($('f-invno') || {}).value || '').trim(),
    invoiceDate: (opening || jwret) ? '' : dval('f-invdate'),
    supplier: opening ? 'Opening Stock'
      : jwret ? ((($('f-jwvendor') || {}).value || '').trim() || 'Job Work Return')
      : (($('f-supplier') || {}).value || '').trim(),
    vehicle: (opening || jwret) ? '' : (($('f-vehicle') || {}).value || '').trim(),
    bin: prev ? (prev.bin || '') : '',
    receivedBy: opening ? '' : String((($('f-by') || {}).value) || '').trim(),
    warehouse: cboxGet('in-warehouse'),
    freight: poMode ? pf(($('in-freight') || {}).value) : 0,
    freightGst: poMode ? pf(cboxGet('in-freight-gst-cb')) : 0,
    freightHsn: poMode ? ((($('in-freight-hsn') || {}).value) || '').trim() : '',
    attachments: attachments,
    items, status: prev ? (prev.status || ((opening || jwret) ? 'Completed' : 'Completed')) : ((opening || jwret) ? 'Completed' : 'Completed')
  };
  if (id) {
    const ex = db.inward.find(r => r.id === id);
    Object.assign(ex, data);
    if (opening) ex.poRef = osRefFor(ex.no);              // IN-0003 -> OS-0003
    if (jwret) ex.poRef = osRefFor(ex.no).replace('OS-', 'JWR-');
  } else {
    data.id = uid();
    data.no = nextNo('inward','IN');          // one continuous series for all inwards
    if (opening) data.poRef = osRefFor(data.no);          // IN-0003 -> OS-0003
    if (jwret) data.poRef = osRefFor(data.no).replace('OS-', 'JWR-');   // IN-0003 -> JWR-0003
    data.createdAt = Date.now();
    db.inward.push(data);
  }
  saveDB(); if (data.poRef && !opening && !jwret) updatePOStatus(data.poRef);
  pageView = null;PENDING_INWARD_INVOICE=null;
  toast(opening ? 'Opening stock saved' : jwret ? 'Job work return saved' : 'Inward saved', 'green');
  render();
}
function updatePOStatus(poNo) {
  const po = db.po.find(p => p.no === poNo); if (!po || isInactiveDoc(po)) return;
  const inws = db.inward.filter(i => i.poRef === poNo && i.status !== 'Rejected');
  let allDone = true, any = false;
  po.items.forEach(it => {
    const got = inws.reduce((a, i) => a + sumBy(i.items.filter(x => x.name === it.name), 'recQty'), 0);
    if (got <= 0) allDone = false; else any = true;
    if (got < pf(it.qty)) allDone = false;
  });
  if (allDone && any) po.status = 'Received';
  else if (any) po.status = 'Partially Received';
  saveDB();
}


/* =====================================================================
   PROCESS PO / OUTWARD + PROCESS INWARD
   Finished garment styles move size-wise through Embroidery / Washing /
   Printing. Outward and inward use separate document series; inward may be
   booked in several partial receipts.
   ===================================================================== */
const PROCESS_TYPES = ['Embroidery','Washing','Printing'];
function processTypeAbbr(type) {
  const clean = String(type || '').toUpperCase().replace(/[^A-Z0-9]/g, '');
  return (clean.slice(0, 3) || 'PRO').padEnd(3, 'X');
}
function nextProcessOutwardNo(type) {
  const abbr = processTypeAbbr(type);
  let max = pf(db.seq.processoutward);
  (db.processoutward || []).forEach(function (r) {
    const m = /^[A-Z0-9]{3}-(\d+)$/.exec(String(r.no || ''));
    if (m) max = Math.max(max, parseInt(m[1], 10) || 0);
  });
  db.seq.processoutward = max + 1;
  return abbr + '-' + String(db.seq.processoutward).padStart(5, '0');
}
function processVendorOptions() {
  const out = [], seen = {};
  function add(v, l) { v = String(v || '').trim(); if (!v || seen[v.toLowerCase()]) return; seen[v.toLowerCase()] = 1; out.push({ v:v, l:l || v }); }
  vendorPartyOptions().forEach(function (o) { add(o.v, o.l); });
  supplierPartyOptions().forEach(function (o) { add(o.v, o.l); });
  return out;
}
function processOutwardOptions() {
  return (db.processoutward || []).filter(function (r) { return !['Completed','Cancelled','Closed'].includes(r.status); })
    .sort(function (a,b) { return String(b.no).localeCompare(String(a.no), undefined, {numeric:true}); })
    .map(function (r) { return { v:r.no, l:r.no + ' · ' + (r.processType || '') + ' · ' + (r.processor || '') }; });
}
function processSizeHtml(values, pending, mode) {
  values = values || {}; pending = pending || {};
  return '<div class="sz-inputs process-sizes">' + (db.masters.sizes || []).map(function (sz) {
    const val = values[sz] != null ? values[sz] : '';
    const max = pending[sz] != null ? pf(pending[sz]) : '';
    return '<span class="szbox"><span class="szlbl">' + esc(sz) + '</span>' +
      '<input class="inp inp-sm szin" type="number" min="0" step="1" data-psz="' + esc(sz) + '" value="' + esc(val) + '"' +
      (mode === 'in' && max !== '' ? ' max="' + max + '" title="Pending ' + fmtNum(max) + '"' : '') +
      ' oninput="' + (mode === 'in' ? 'calcProcessInward()' : 'calcProcessOutward()') + '">' +
      (mode === 'in' ? '<span class="szbal">bal ' + fmtNum(max) + '</span>' : '') + '</span>';
  }).join('') + '</div>';
}
function processOutwardRow(it) {
  it = it || {}; const style = it.style || '';
  return '<tr>' + srTd() +
    '<td><b>' + esc(styleLabel(style) || style || '—') + '</b><input type="hidden" data-f="style" value="' + esc(style) + '"></td>' +
    '<td class="sizes-cell">' + processSizeHtml(it.sizes || {}, {}, 'out') + '</td>' +
    '<td class="num" data-ptotal>0</td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="rate" value="' + (it.rate ?? '') + '" oninput="calcProcessOutward()"></td>' +
    '<td class="num" data-pamount>0</td>' +
    '<td><button type="button" class="btn-icon danger" onclick="removeRow(this);calcProcessOutward()">' + ic('trash',14) + '</button></td></tr>';
}
function processRowsFromJW(jwNo) {
  const jw = (db.jobwork || []).find(function (x) { return x.no === jwNo; });
  if (!jw) return [];
  return (jw.garments || []).filter(function (g) { return g.style || g.desc; }).map(function (g) {
    return { style:g.style || g.desc, sizes:Object.assign({}, g.sizes || {}), rate:0 };
  });
}
function onProcessJWSelect(jwNo) {
  const tb = $('pro-items'); if (!tb) return;
  const rows = processRowsFromJW(jwNo);
  tb.innerHTML = rows.map(processOutwardRow).join('') || processOutwardRow();
  calcProcessOutward();
}
function formProcessOutward(rec) {
  const r = rec || {};
  const rows = (r.items && r.items.length) ? r.items : processRowsFromJW(r.jwRef || '');
  return '<div class="sec">Process PO / Outward</div><div class="grid-fixed">' +
    (r.id ? field('Process Outward No.', '<input class="inp" value="' + esc(r.no || '') + '" disabled>') : '') +
    field('Date *','<input class="inp" data-date readonly disabled id="f-date" value="' + esc(r.date || today()) + '">') +
    field('Process Type *', cbox('pro-type',{opts:db.masters.processTypes || PROCESS_TYPES,value:r.processType || '',placeholder:'Select Process Type'})) +
    field('Processor / Vendor *', cbox('pro-vendor',{opts:processVendorOptions(),value:r.processor || '',placeholder:'Select processor'})) +
    field('Source Job Work *', cbox('pro-jw',{opts:(db.jobwork||[]).map(function(j){return {v:j.no,l:j.no+' · '+(j.vendor||'')};}),value:r.jwRef || '',placeholder:'Select Job Work',onSet:onProcessJWSelect})) +
    field('Expected Return Date','<input class="inp" data-date readonly id="pro-expected" value="' + esc(r.expectedDate || '') + '">') +
    field('Status', cbox('pro-status',{opts:STATUSES.processoutward,value:r.status || 'Open'})) +
    field('Remarks','<input class="inp" id="pro-remarks" value="' + esc(r.remarks || '') + '">') +
  '</div><div class="sec">Style / Size-wise Quantity Sent</div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' + srTh() + '<th>Style</th><th>Size-wise Outward Qty</th><th class="num">Total Qty</th><th class="num">Rate / pc</th><th class="num">Amount</th><th></th></tr></thead>' +
  '<tbody id="pro-items">' + (rows.length ? rows.map(processOutwardRow).join('') : processOutwardRow()) + '</tbody>' +
  '<tfoot><tr class="tot-row"><td></td><td>Total</td><td></td><td class="num" id="pro-total-qty">0</td><td></td><td class="num" id="pro-total-amt">₹0</td><td></td></tr></tfoot></table></div>' +
  '<div class="hint">Select a source Job Work to load its styles and size quantities. Adjust the quantities being sent for the selected process.</div>';
}
function processRowData(tr) {
  const style = (qs('[data-f="style"]',tr)||{}).value || '';
  const sizes = {}; let qty = 0;
  qsa('[data-psz]',tr).forEach(function (i) { const n=pf(i.value); if(n>0){sizes[i.dataset.psz]=n;qty+=n;} });
  return { style:style, sizes:sizes, qty:qty, rate:pf((qs('[data-f="rate"]',tr)||{}).value) };
}
function calcProcessOutward() {
  let tq=0,ta=0; qsa('#pro-items tr').forEach(function(tr){const x=processRowData(tr),a=x.qty*x.rate;tq+=x.qty;ta+=a;const q=qs('[data-ptotal]',tr),am=qs('[data-pamount]',tr);if(q)q.textContent=fmtNum(x.qty);if(am)am.textContent=fmtNum(a);});
  if($('pro-total-qty'))$('pro-total-qty').textContent=fmtNum(tq); if($('pro-total-amt'))$('pro-total-amt').textContent=fmtINR(ta); renumberSr($('pro-items'));
}
function afterRenderProcessOutward(){calcProcessOutward();}
function saveProcessOutward(id) {
  clearReqMarks(); const prev=id?(db.processoutward||[]).find(function(x){return x.id===id;}):null;
  if(!cboxGet('pro-type'))return reqBad('pro-type','Process Type is required');
  if(!cboxGet('pro-vendor'))return reqBad('pro-vendor','Processor / Vendor is required');
  if(!cboxGet('pro-jw'))return reqBad('pro-jw','Source Job Work is required');
  const items=qsa('#pro-items tr').map(processRowData).filter(function(x){return x.style&&x.qty>0;});
  if(!items.length)return reqBad(qs('#pro-items [data-psz]'),'Enter at least one size-wise outward quantity');
  const data={date:dval('f-date')||today(),processType:cboxGet('pro-type'),processor:cboxGet('pro-vendor'),jwRef:cboxGet('pro-jw'),expectedDate:dval('pro-expected'),remarks:($('pro-remarks').value||'').trim(),items:items,status:cboxGet('pro-status')||'Open'};
  if(prev)Object.assign(prev,data);else{data.id=uid();data.no=nextProcessOutwardNo(data.processType);data.createdAt=Date.now();db.processoutward.push(data);}
  updateProcessOutwardStatus(prev||data);saveDB();pageView=null;toast('Process Outward saved — '+(prev?prev.no:data.no),'green');render();
}
function processReceivedMap(outNo, excludeId) {
  const map={}; (db.processinward||[]).forEach(function(r){if(r.outwardRef!==outNo||r.id===excludeId||r.status==='Cancelled')return;(r.items||[]).forEach(function(it){Object.keys(it.sizes||{}).forEach(function(sz){const k=it.style+'|'+sz;map[k]=(map[k]||0)+pf(it.sizes[sz]);});});}); return map;
}
function processInwardRow(outItem, saved, outNo, excludeId) {
  outItem=outItem||{};saved=saved||{};const got=processReceivedMap(outNo,excludeId),pending={};
  (db.masters.sizes||[]).forEach(function(sz){pending[sz]=Math.max(0,pf((outItem.sizes||{})[sz])-pf(got[outItem.style+'|'+sz]));});
  return '<tr>'+srTd()+'<td><b>'+esc(styleLabel(outItem.style)||outItem.style)+'</b><input type="hidden" data-f="style" value="'+esc(outItem.style||'')+'"></td>'+
    '<td class="sizes-cell">'+processSizeHtml(saved.sizes||{},pending,'in')+'</td><td class="num" data-pri-total>0</td><td class="num" data-pri-pending>'+fmtNum(Object.keys(pending).reduce(function(a,k){return a+pending[k];},0))+'</td></tr>';
}
function onProcessOutwardSelect(no) {
  const out=(db.processoutward||[]).find(function(x){return x.no===no;}),tb=$('pri-items');if(!out||!tb)return;
  if($('pri-type'))$('pri-type').value=out.processType||'';if($('pri-vendor'))$('pri-vendor').value=out.processor||'';
  tb.innerHTML=(out.items||[]).map(function(it){return processInwardRow(it,{},no,null);}).join('');calcProcessInward();
}
function formProcessInward(rec) {
  const r=rec||{},out=(db.processoutward||[]).find(function(x){return x.no===r.outwardRef;});
  const rows=out?(out.items||[]):[];
  return '<div class="sec">Process Inward</div><div class="grid-fixed">'+
    (r.id?field('Process Inward No.','<input class="inp" value="'+esc(r.no||'')+'" disabled>'):'')+
    field('Date *','<input class="inp" data-date readonly disabled id="f-date" value="'+esc(r.date||today())+'">')+
    field('Process Outward Ref *',cbox('pri-out',{opts:processOutwardOptions(),value:r.outwardRef||'',placeholder:'Select Process Outward',onSet:onProcessOutwardSelect}))+
    field('Process Type','<input class="inp" id="pri-type" value="'+esc((out&&out.processType)||r.processType||'')+'" disabled>')+
    field('Processor / Vendor','<input class="inp" id="pri-vendor" value="'+esc((out&&out.processor)||r.processor||'')+'" disabled>')+
    field('Warehouse *',cbox('pri-wh',{opts:db.masters.warehouses,value:r.warehouse||'',placeholder:'Select warehouse'}))+
    field('Status',cbox('pri-status',{opts:STATUSES.processinward,value:r.status||'Received'}))+
    field('Remarks','<input class="inp" id="pri-remarks" value="'+esc(r.remarks||'')+'">')+
  '</div><div class="sec">Processed Garments Received — Partial Receipt</div><div class="table-wrap"><table class="mini-table"><thead><tr>'+srTh()+'<th>Style</th><th>Receive Now (size-wise)</th><th class="num">Receive Qty</th><th class="num">Pending Before Receipt</th></tr></thead><tbody id="pri-items">'+
    (rows.length?rows.map(function(it){const sv=(r.items||[]).find(function(x){return x.style===it.style;})||{};return processInwardRow(it,sv,r.outwardRef,r.id);}).join(''):'')+
    '</tbody><tfoot><tr class="tot-row"><td></td><td>Total</td><td></td><td class="num" id="pri-total">0</td><td></td></tr></tfoot></table></div>'+
    '<div class="hint">Multiple Process Inward entries may be saved against the same Process Outward until all size quantities are received.</div>';
}
function calcProcessInward(){let t=0;qsa('#pri-items tr').forEach(function(tr){const x=processRowData(tr);t+=x.qty;const c=qs('[data-pri-total]',tr);if(c)c.textContent=fmtNum(x.qty);});if($('pri-total'))$('pri-total').textContent=fmtNum(t);renumberSr($('pri-items'));}
function afterRenderProcessInward(){calcProcessInward();}
function updateProcessOutwardStatus(out){if(!out)return;const sent=(out.items||[]).reduce(function(a,it){return a+Object.keys(it.sizes||{}).reduce(function(x,k){return x+pf(it.sizes[k]);},0);},0);const got=(db.processinward||[]).filter(function(r){return r.outwardRef===out.no&&r.status!=='Cancelled';}).reduce(function(a,r){return a+(r.items||[]).reduce(function(x,it){return x+Object.keys(it.sizes||{}).reduce(function(y,k){return y+pf(it.sizes[k]);},0);},0);},0);out.status=got<=0?'Open':(got>=sent?'Completed':'Partially Received');}
function saveProcessInward(id){clearReqMarks();const prev=id?(db.processinward||[]).find(function(x){return x.id===id;}):null,no=cboxGet('pri-out'),out=(db.processoutward||[]).find(function(x){return x.no===no;});if(!out)return reqBad('pri-out','Process Outward reference is required');if(!cboxGet('pri-wh'))return reqBad('pri-wh','Warehouse is required');const over=qsa('#pri-items [data-psz]').find(function(el){return el.max!==''&&pf(el.value)>pf(el.max);});if(over)return reqBad(over,'Received quantity cannot exceed the pending size quantity ('+fmtNum(pf(over.max))+')');const items=qsa('#pri-items tr').map(processRowData).filter(function(x){return x.style&&x.qty>0;});if(!items.length)return reqBad(qs('#pri-items [data-psz]'),'Enter received quantity for at least one size');const data={date:dval('f-date')||today(),outwardRef:no,processType:out.processType,processor:out.processor,warehouse:cboxGet('pri-wh'),remarks:($('pri-remarks').value||'').trim(),items:items,status:cboxGet('pri-status')||'Received'};if(prev)Object.assign(prev,data);else{data.id=uid();data.no=nextNo('processinward','PRI');data.createdAt=Date.now();db.processinward.push(data);}updateProcessOutwardStatus(out);saveDB();pageView=null;toast('Process Inward saved — '+(prev?prev.no:data.no),'green');render();}

/* ================= JOB WORK (multiple material issues → job-worker vendor → finished goods) ================= */
const JW_EXTRA_REASONS = ['Wastage','Rework','Shortage','Damage','Top-up','Sample','Other'];
function jwLinesByDate(arr) {
  return (arr || []).slice().sort(function (a, b) {
    const da = String(a.date || ''), db = String(b.date || '');
    if (da === db) return 0;
    if (!da) return 1;
    if (!db) return -1;
    return da < db ? -1 : 1;
  });
}
function jwLastRowDate(tbodyId) {
  const rows = qsa('#' + tbodyId + ' tr').filter(function (tr) { return !tr.classList.contains('jw-dgrp'); });
  for (let i = rows.length - 1; i >= 0; i--) {
    const d = dval(qs('[data-f="date"]', rows[i]));
    if (d) return d;
  }
  return today();
}
function jwPodIdFor(tbodyId, dateIso) {
  const sampleScope = pageView && pageView.m === 'sample' && tbodyId === 'jw-extras';
  const scope = sampleScope ? 'sample-' + tbodyId : tbodyId;
  return 'grp_' + scope + '_' + String(dateIso || 'none').replace(/[^0-9A-Za-z]/g, '');
}
function jwSeedGroupPod(tbodyId, it) {
  it = it || {};
  if (!it.pod || !it.date) return;
  const id = jwPodIdFor(tbodyId, it.date);
  if (!PODDATA[id]) PODDATA[id] = { pod: it.pod, podName: it.podName || 'POD', kind: it.podKind || (/pdf/i.test(it.podName || '') || /^data:application\/pdf/i.test(it.pod) ? 'pdf' : 'image') };
}
function jwApplyGroupMeta(tbodyId, tr, row) {
  if (!row) return;
  const d = row.date || dval(qs('[data-f="date"]', tr)) || '';
  const dcEl = qs('[data-f="dcNo"]', tr);
  row.dcNo = dcEl ? String(dcEl.value || '').trim() : (row.dcNo || '');
  const lineInp = qs('[data-pod]', tr);
  const linePd = lineInp ? PODDATA[lineInp.dataset.pod] : null;
  const pd = (linePd && linePd.pod) ? linePd : PODDATA[jwPodIdFor(tbodyId, d)];
  if (pd && pd.pod) { row.pod = pd.pod; row.podName = pd.podName; row.podKind = pd.kind || ''; }
}
function jwRowGroupHasPod(tr) {
  const tb = tr && tr.closest ? tr.closest('#jw-issues, #jw-extras') : null;
  if (!tb) return false;
  const d = dval(qs('[data-f="date"]', tr)) || '';
  const pd = PODDATA[jwPodIdFor(tb.id, d)];
  return !!(pd && pd.pod);
}
function jwGroupRowsByDate(tbodyId) {
  const tb = $(tbodyId); if (!tb) return;

  /* Restore the editable row cells before rebuilding the visual groups. */
  qsa('tr.jw-dgrp', tb).forEach(function (tr) { tr.remove(); });
  qsa('.jw-merged-dispatch', tb).forEach(function (td) { td.remove(); });
  const rows = qsa('tr', tb).filter(function (tr) { return !tr.classList.contains('jw-dgrp'); });
  rows.forEach(function (tr) {
    const dateEl = qs('[data-f="date"]', tr), dcEl = qs('[data-f="dcNo"]', tr);
    const dateTd = dateEl && dateEl.closest ? dateEl.closest('td') : null;
    const dcTd = dcEl && dcEl.closest ? dcEl.closest('td') : null;
    const podTd = qs('[data-jw-group-pod]', tr);
    if (dateTd) dateTd.style.display = '';
    if (dcTd) dcTd.style.display = '';
    if (podTd) {
      podTd.style.display = '';
      podTd.rowSpan = 1;
      podTd.classList.remove('jw-merged-pod');
    }
  });
  if (!rows.length) return;

  const keyed = rows.map(function (tr, i) {
    return { tr: tr, i: i, d: dval(qs('[data-f="date"]', tr)) || '' };
  });
  keyed.sort(function (a, b) {
    if (a.d === b.d) return a.i - b.i;
    if (!a.d) return 1;
    if (!b.d) return -1;
    return a.d < b.d ? -1 : 1;
  });

  /* One issue date creates one DC. All lines in that DC share one POD. */
  const dcByDate = {};
  keyed.forEach(function (k) {
    const el = qs('[data-f="dcNo"]', k.tr);
    const v = el ? String(el.value || '').trim() : '';
    if (k.d && v && !dcByDate[k.d]) dcByDate[k.d] = v;
  });
  keyed.forEach(function (k) {
    if (!k.d) return;
    /* A new DC number is intentionally not consumed while the form is being
       created. Existing saved rows keep their number; blank rows show Pending
       and receive the next sequence only inside saveJobWork(). */
    const el = qs('[data-f="dcNo"]', k.tr);
    if (el && dcByDate[k.d]) el.value = dcByDate[k.d];
  });

  const groups = [];
  keyed.forEach(function (k) {
    let g = groups.length ? groups[groups.length - 1] : null;
    if (!g || g.date !== k.d) {
      g = { date: k.d, dc: k.d ? (dcByDate[k.d] || '') : '', rows: [] };
      groups.push(g);
    }
    g.rows.push(k.tr);
  });

  groups.forEach(function (g) {
    const podId = jwPodIdFor(tbodyId, g.date);
    g.rows.forEach(function (tr, index) {
      const dateEl = qs('[data-f="date"]', tr), dcEl = qs('[data-f="dcNo"]', tr);
      const dateTd = dateEl && dateEl.closest ? dateEl.closest('td') : null;
      const dcTd = dcEl && dcEl.closest ? dcEl.closest('td') : null;

      if (g.date) {
        /* Move the old full-width date/DC block into the table body and merge
           it vertically across all material lines for this DC. The original
           inputs stay in the DOM (hidden) so saving and stock logic are unchanged. */
        if (dateTd) dateTd.style.display = 'none';
        if (dcTd) dcTd.style.display = 'none';
        if (index === 0) {
          const merged = document.createElement('td');
          merged.className = 'jw-merged-dispatch';
          merged.rowSpan = g.rows.length;
          merged.colSpan = 2;
          merged.innerHTML =
            '<div class="jw-merge-date">' + ic('inbox', 13) + '<b>' + esc(dpToDisplay(g.date) || g.date) + '</b></div>' +
            '<div class="jw-merge-dc">' + ((pageView && pageView.m === 'sample') ? 'Sample DC No. ' : 'DC No. ') + '<b>' + esc(g.dc || ((pageView && pageView.m === 'sample') ? 'SAM — generated on Save' : 'Pending — generated on Save')) + '</b></div>' +
            '<div class="jw-merge-lines">' + g.rows.length + (g.rows.length === 1 ? ' line' : ' lines') + '</div>';
          const sr = qs('td.srcol', tr);
          if (sr && sr.parentNode) sr.parentNode.insertBefore(merged, sr.nextSibling);
          else tr.insertBefore(merged, tr.firstChild);
        }
      }

      const podCell = qs('[data-jw-group-pod]', tr);
      if (podCell) {
        if (!g.date) {
          podCell.innerHTML = '<span class="att-none">Select date</span>';
        } else if (index === 0) {
          /* One vertically merged POD cell per DC, matching the merged Date/DC
             treatment. The upload, preview and remove actions stay centered. */
          podCell.rowSpan = g.rows.length;
          podCell.classList.add('jw-merged-pod');
          podCell.innerHTML = podCellHtml(podId);
        } else {
          podCell.style.display = 'none';
          podCell.innerHTML = '';
        }
      }
      tb.appendChild(tr);
    });
  });
  renumberSr(tb);
}
function addJwIssue() {
  addRow('jw-issues', function () { return jwIssueRow({ date: today() }); });
  afterRenderIssues();
}
function addJwExtra() {
  addRow('jw-extras', function () { return jwExtraRow({ date: today() }); });
  afterRenderExtras();
}
function jwExtraRow(it) {
  it = it || {};
  const itemId = rid('jxi'), unitId = rid('jxu'), rsnId = rid('jxr'), poId = rid('jxpo'), clsId = rid('jxcl');
  jwSeedGroupPod('jw-extras', it);
  const podId = jwPodIdFor('jw-extras', it.date || '');
  return '<tr data-jw-saved="' + (it._saved ? '1' : '0') + '">' +
    srTd() +
    '<td><input class="inp" type="text" data-date autocomplete="off" readonly disabled data-f="date" value="' + esc(it.date || today()) + '"></td>' +
    '<td><input class="inp" data-f="dcNo" value="' + esc(it.dcNo || '') + '" disabled title="System generated — one DC No. per date"></td>' +
    '<td>' + cbox(clsId, { opts: db.masters.classifications, value: it.classification || (itemByName(it.item) || {}).classification || '', f:'classification', placeholder:'Classification', free:true, onSet: function () { jwClassChanged(clsId, itemId); } }) + '</td>' +
    '<td>' + cbox(itemId, { opts: itemOptsFor(it.classification || (itemByName(it.item) || {}).classification || ''), value: it.item || '', f:'item', placeholder:'Fabric / Trim', free:true, onSet: function (v) { const el = qs('[data-cb-input="' + itemId + '"]'); jwSetUnit(el, v); jwRowRefresh(el); calcJWExtras(); } }) +
      '</td>' +
    '<td>' + cbox(poId, { opts: inwardOptionsForItem(it.item), value: inwardNoForIssue(it), f:'inwardNo', placeholder:'Select Inward', free:false, onSet: function () { jwSyncPoRef(qs('[data-cb-input="' + poId + '"]').closest('tr')); jwRowRefresh(qs('[data-cb-input="' + poId + '"]')); } }) + '</td>' +
    '<td class="po-ref-cell"><input class="inp" data-f="poRef" value="' + esc(it.poRef || poRefFromInward(inwardNoForIssue(it))) + '" placeholder="Auto" disabled></td>' +
    '<td class="num" data-postock style="color:var(--muted);font-weight:600">' + fmtNum(inwardItemStock(inwardNoForIssue(it), it.item, stockExcept())) + '</td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="qty" value="' + (it.qty ?? '') + '" placeholder="0" oninput="jwCheckActual(this)" title="Actually issued — cannot exceed stock"></td>' +
    '<td class="uom-cell"><input class="inp" data-f="unit" value="' + esc(it.unit || itemUnit(it.item) || '') + '" placeholder="Auto" disabled></td>' +
    '<td><input class="inp" data-f="reason" value="' + esc(it.reason || '') + '" placeholder="Reason *" oninput="jwReasonCheck(this)"></td>' +
    '<td class="pod-cell" data-jw-group-pod>' + ((it.date || '') ? podCellHtml(podId) : '<span class="att-none">Select date</span>') + '</td>' +
    '<td class="actions-cell">' +
      (it._saved ? '<button type="button" class="btn-icon jw-row-edit" title="Edit this saved row" onclick="jwEditIssueRow(this)">' + ic('pencil', 15) + '</button>' : '') +
      '<button type="button" class="btn-icon danger jw-row-delete" title="Remove this row" onclick="removeRow(this);calcJWExtras();jwGroupRowsByDate(\'jw-extras\');jwRefreshSharedStock()">' + ic('trash', 15) + '</button></td>' +
  '</tr>';
}
function calcJWExtras() {
  const tb = $('jw-extras'); if (!tb) return;
  const map = {};
  qsa('tr', tb).forEach(function (tr) {
    const itEl = qs('[data-f="item"]', tr);
    const item = itEl ? (itEl.dataset.cbInput ? cboxGet(itEl.dataset.cbInput) : itEl.value) : '';
    const q = pf((qs('[data-f="qty"]', tr) || {}).value);
    if (!item || q <= 0) return;
    const unEl = qs('[data-f="unit"]', tr);
    const un = unEl ? (unEl.dataset.cbInput ? cboxGet(unEl.dataset.cbInput) : unEl.value) : '';
    const k = item + '|' + un;
    map[k] = (map[k] || 0) + q;
  });
  const parts = Object.keys(map).map(function (k) {
    const p = k.split('|');
    return esc(p[0]) + ': <b>' + fmtNum(map[k]) + '</b>' + (p[1] ? ' ' + esc(p[1]) : '');
  });
  const el = $('jw-extra-totals');
  if (el) el.innerHTML = parts.length ? parts.join(' &nbsp;·&nbsp; ') : '—';
}
function afterRenderExtras() {
  dpSyncAll($('jw-extras'));
  calcJWExtras();
  qsa('#jw-extras tr').forEach(function (tr) {
    if (tr.classList.contains('jw-dgrp')) return;
    jwRowRefresh(qs('[data-f="item"]', tr) || tr);
  });
  jwGroupRowsByDate('jw-extras');
  jwLockPodRows();
  jwLockSavedRows('jw-extras');
}
function inwardByNo(no) {
  const n = String(no || '').trim();
  if (!n) return null;
  return db.inward.find(function (r) { return r.no === n; }) || null;
}
/* Inward documents that received this item (PO receipt, opening stock, JW return) */
function inwardOptionsForItem(itemName, skipTr) {
  const nm = lcName(itemName);
  if (!nm) return [];
  return db.inward
    .filter(function (r) {
      const hasItem = (r.items || []).some(function (i) { return lcName(i.name) === nm && pf(i.recQty) > 0; });
      return hasItem && inwardItemStock(r.no, itemName, stockExcept(skipTr)) > 0;
    })
    .sort(function (a, b) { return (b.date || '') < (a.date || '') ? -1 : 1; })
    .map(function (r) {
      const extra = r.entryType === 'Opening Stock' ? 'Opening Stock'
        : r.entryType === 'Job Work Return' ? ('JW Return' + (r.vendor ? ' \u00b7 ' + r.vendor : ''))
        : ((r.poRef ? r.poRef + ' \u00b7 ' : '') + (r.supplier || ''));
      const available = inwardItemStock(r.no, itemName, stockExcept(skipTr));
      return { v: r.no, l: r.no + (extra ? ' \u00b7 ' + extra : '') + ' \u00b7 Stock ' + fmtNum(available) };
    });
}
function poRefFromInward(inwardNo) {
  const r = inwardByNo(inwardNo);
  if (!r) return inwardNo || '';
  return r.poRef || inwardRef(r) || r.no;
}
/* map a saved issue row onto an Inward No. (new field, or first matching inward for a legacy poRef) */
function inwardNoForIssue(it) {
  it = it || {};
  if (it.inwardNo) return it.inwardNo;
  if (it.poRef && it.item) {
    const hit = db.inward.find(function (r) {
      const ref = r.poRef || inwardRef(r);
      return ref === it.poRef && (r.items || []).some(function (i) { return lcName(i.name) === lcName(it.item) && pf(i.recQty) > 0; });
    });
    if (hit) return hit.no;
  }
  return '';
}
function issueLineQty(s) {
  if (!s) return 0;
  if (s.actualQty != null && s.actualQty !== '') return pf(s.actualQty);
  return pf(s.qty);
}
function issueHitsInward(s, inwardNo, itemName) {
  if (lcName(s.item) !== lcName(itemName)) return false;
  return String(inwardNoForIssue(s) || '') === String(inwardNo || '');
}
function liveFormIssued(inwardNo, itemName, skipTr) {
  let q = 0;
  ['jw-issues', 'jw-extras'].forEach(function (tid) {
    const tb = $(tid); if (!tb) return;
    qsa('tr', tb).forEach(function (tr) {
      if (skipTr && tr === skipTr) return;
      const itEl = qs('[data-f="item"]', tr);
      const item = itEl ? (itEl.dataset.cbInput ? cboxGet(itEl.dataset.cbInput) : itEl.value) : '';
      if (lcName(item) !== lcName(itemName)) return;
      const inEl = qs('[data-f="inwardNo"]', tr);
      const no = inEl ? (inEl.dataset.cbInput ? cboxGet(inEl.dataset.cbInput) : inEl.value) : '';
      if (String(no || '') !== String(inwardNo || '')) return;
      const act = qs('[data-f="actualQty"]', tr);
      const qtyEl = qs('[data-f="qty"]', tr);
      q += pf(act ? act.value : (qtyEl && qtyEl.value));
    });
  });
  return q;
}
function stockExcept(skipTr) {
  const ex = { skipTr: skipTr || null };
  if (pageView && pageView.kind === 'form') {
    if (pageView.m === 'jobwork') ex.jwId = pageView.id || null;
    if (pageView.m === 'sample') ex.sampleId = pageView.id || null;
  }
  return ex;
}
/* Stock = Inward − Job Work − Sample − Purchase Return (this inward + item) */
function inwardItemStock(inwardNo, itemName, except) {
  const rec = inwardByNo(inwardNo);
  if (!rec) return 0;
  const nm = lcName(itemName);
  if (!nm) return 0;
  except = except || stockExcept();
  let q = 0;
  (rec.items || []).forEach(function (i) { if (lcName(i.name) === nm) q += pf(i.recQty); });
  (db.jobwork || []).forEach(function (j) {
    if (except.jwId && j.id === except.jwId) return;
    (j.issues || []).concat(j.extras || []).forEach(function (s) {
      if (issueHitsInward(s, inwardNo, itemName)) q -= issueLineQty(s);
    });
  });
  (db.sample || []).forEach(function (j) {
    if (except.sampleId && j.id === except.sampleId) return;
    (j.extras || []).concat(j.issues || []).forEach(function (s) {
      if (issueHitsInward(s, inwardNo, itemName)) q -= issueLineQty(s);
    });
  });
  const poRef = rec.poRef || '';
  (db.purchasereturn || []).forEach(function (r) {
    if (except.prId && r.id === except.prId) return;
    if (r.inwardNo) {
      if (String(r.inwardNo) !== String(inwardNo)) return;
    } else if (!poRef || r.poRef !== poRef) {
      return;
    }
    (r.items || []).forEach(function (i) { if (lcName(i.name) === nm) q -= pf(i.retQty); });
  });
  if (!except.ignoreLive) q -= liveFormIssued(inwardNo, itemName, except.skipTr);
  return Math.max(0, q);
}
function jwRefreshSharedStock() {
  ['jw-issues','jw-extras'].forEach(function(tid){
    const tb=$(tid);if(!tb)return;
    qsa('tr',tb).forEach(function(tr){
      const itEl=qs('[data-f="item"]',tr),inEl=qs('[data-f="inwardNo"]',tr),st=qs('[data-postock]',tr);
      if(!itEl||!inEl||!st)return;
      const item=itEl.dataset.cbInput?cboxGet(itEl.dataset.cbInput):itEl.value;
      const no=inEl.dataset.cbInput?cboxGet(inEl.dataset.cbInput):inEl.value;
      st.textContent=fmtNum(inwardItemStock(no,item,stockExcept(tr)));
    });
  });
}
function jwValidateCombinedStock(issues,extras,id,mod){
  const sums={},rows=(issues||[]).concat(extras||[]);
  rows.forEach(function(x){const key=lcName(x.item)+'|'+String(x.inwardNo||'');if(!x.item||!x.inwardNo)return;sums[key]=(sums[key]||0)+pf(x.actualQty!=null&&x.actualQty!==''?x.actualQty:x.qty);});
  for(const key of Object.keys(sums)){
    const parts=key.split('|'),item=parts[0],no=parts.slice(1).join('|');
    const except={ignoreLive:true};if(mod==='sample')except.sampleId=id||null;else except.jwId=id||null;
    const available=inwardItemStock(no,item,except);
    if(sums[key]>available+0.000001){
      let target=null;qsa('#jw-issues tr,#jw-extras tr').some(function(tr){const ie=qs('[data-f="item"]',tr),ne=qs('[data-f="inwardNo"]',tr);const iv=ie?(ie.dataset.cbInput?cboxGet(ie.dataset.cbInput):ie.value):'';const nv=ne?(ne.dataset.cbInput?cboxGet(ne.dataset.cbInput):ne.value):'';if(lcName(iv)===item&&String(nv)===no){target=qs('[data-f="actualQty"]',tr)||qs('[data-f="qty"]',tr);return true;}return false;});
      return reqBad(target,'Combined issue quantity for '+bareItemName(item)+' on '+no+' is '+fmtNum(sums[key])+' but available stock is '+fmtNum(available));
    }
  }
  return true;
}
function jwSyncPoRef(tr) {
  if (!tr) return;
  const inEl = qs('[data-f="inwardNo"]', tr);
  const no = inEl ? (inEl.dataset.cbInput ? cboxGet(inEl.dataset.cbInput) : inEl.value) : '';
  const hid = qs('[data-f="poRef"]', tr);
  if (hid) hid.value = poRefFromInward(no);
}
/* POs that ordered this item, newest first */
function poOptionsForItem(itemName) {
  const nm = String(itemName || '').trim().toLowerCase();
  if (!nm) return [];
  const opts = db.po
    .filter(function (p) {
      return !isInactiveDoc(p) &&
        (p.items || []).some(function (i) { return String(i.name || '').trim().toLowerCase() === nm; });
    })
    .sort(function (a, b) { return b.date < a.date ? -1 : 1; })
    .map(function (p) { return { v: p.no, l: p.no + ' · ' + (p.supplier || '') }; });
  /* opening stock behaves like a source document — issue against it too */
  db.inward
    .filter(function (r) {
      return r.entryType === 'Opening Stock' &&
        (r.items || []).some(function (i) { return String(i.name || '').trim().toLowerCase() === nm; });
    })
    .sort(function (a, b) { return b.date < a.date ? -1 : 1; })
    .forEach(function (r) { const ref = r.poRef || osRefFor(r.no); opts.push({ v: ref, l: ref + ' · Opening Stock' }); });
  /* material returned by a job worker is back in stock — re-issue it against its JWR ref */
  db.inward
    .filter(function (r) {
      return r.entryType === 'Job Work Return' &&
        (r.items || []).some(function (i) {
          return String(i.name || '').trim().toLowerCase() === nm && pf(i.recQty) > 0;
        });
    })
    .sort(function (a, b) { return b.date < a.date ? -1 : 1; })
    .forEach(function (r) {
      const ref = r.poRef || osRefFor(r.no).replace('OS-', 'JWR-');
      opts.push({ v: ref, l: ref + ' · Job Work Return' + (r.vendor ? ' · ' + r.vendor : '') });
    });
  return opts;
}
/* PO Actual Stock, per PO + item:
      Inward received − Purchase Return + Job Work Return */
function poItemStock(poNo, itemName) {
  const nm = String(itemName || '').trim().toLowerCase();
  if (!nm) return 0;
  let inward = 0, purchaseReturn = 0, jobworkReturn = 0;

  db.inward.forEach(function (r) {
    if (r.entryType === 'Job Work Return') return;   // counted separately below
    if (poNo && r.poRef !== poNo) return;
    (r.items || []).forEach(function (i) {
      if (String(i.name || '').trim().toLowerCase() === nm) inward += pf(i.recQty);
    });
  });
  db.purchasereturn.forEach(function (r) {
    if (poNo && r.poRef !== poNo) return;
    (r.items || []).forEach(function (i) {
      if (String(i.name || '').trim().toLowerCase() === nm) purchaseReturn += pf(i.retQty);
    });
  });
  /* material the job worker sent back */
  db.inward.filter(function (r) { return r.entryType === 'Job Work Return'; }).forEach(function (r) {
    const j = db.jobwork.find(function (x) { return x.no === r.jwRef; });
    const self = poNo && r.poRef === poNo;          // issuing straight off this return
    (r.items || []).forEach(function (i) {
      if (String(i.name || '').trim().toLowerCase() !== nm) return;
      if (poNo && !self) {
        const src = j ? (j.issues || []).concat(j.extras || [])
          .find(function (s) { return String(s.item || '').trim().toLowerCase() === nm; }) : null;
        if (src && src.poRef && src.poRef !== poNo) return;
      }
      jobworkReturn += pf(i.recQty);
    });
  });
  db.jobworkreturn.forEach(function (r) {
    const j = db.jobwork.find(function (x) { return x.no === r.jwRef; });
    (r.items || []).forEach(function (i) {
      if (String(i.name || '').trim().toLowerCase() !== nm) return;
      if (poNo) {
        const src = j ? (j.issues || []).concat(j.extras || [])
          .find(function (s) { return String(s.item || '').trim().toLowerCase() === nm; }) : null;
        if (src && src.poRef && src.poRef !== poNo) return;
      }
      jobworkReturn += pf(i.retQty);
    });
  });
  return Math.max(0, inward - purchaseReturn + jobworkReturn);
}
/* refresh a row's PO list + stock readout after item/PO changes */
/* write the item's UOM into the disabled cell */
function jwSetUnit(anyEl, itemName) {
  const tr = anyEl && anyEl.closest ? anyEl.closest('tr') : null; if (!tr) return;
  const u = qs('[data-f="unit"]', tr);
  if (u) u.value = itemUnit(itemName) || u.value || '';
}
/* the same Inward + item must not appear twice in one table */
function jwDupPoCheck(anyEl) {
  const tr = anyEl && anyEl.closest ? anyEl.closest('tr') : null; if (!tr) return false;
  const tb = tr.parentNode; if (!tb) return false;
  /* Sample may split the same Item + Inward across multiple issue lines/DCs.
     Shared stock validation still limits their combined quantity. */
  if (pageView && pageView.m === 'sample') {
    tr.classList.remove('row-over');
    return false;
  }
  const val = function (row, f) {
    const el = qs('[data-f="' + f + '"]', row);
    if (!el) return '';
    return String(el.dataset.cbInput ? cboxGet(el.dataset.cbInput) : el.value || '').trim().toLowerCase();
  };
  const item = val(tr, 'item'), inward = val(tr, 'inwardNo');
  if (!item || !inward) { tr.classList.remove('row-over'); return false; }
  let dup = false;
  qsa('tr', tb).forEach(function (other) {
    if (other === tr || dup) return;
    if (val(other, 'item') === item && val(other, 'inwardNo') === inward) dup = true;
  });
  tr.classList.toggle('row-over', dup);
  if (dup) {
    const nm = qs('[data-f="item"]', tr);
    const pn = qs('[data-f="inwardNo"]', tr);
    toast('Already added — ' + ((nm && nm.value) || 'item') + ' on ' + ((pn && pn.value) || 'this Inward') +
          '. Edit the existing line instead.', 'red');
    const inEl = qs('[data-f="inwardNo"]', tr);
    if (inEl && inEl.dataset.cbInput) { cboxSet(inEl.dataset.cbInput, ''); inEl.value = ''; }
    const hid = qs('[data-f="poRef"]', tr); if (hid) hid.value = '';
    const st = qs('[data-postock]', tr); if (st) st.textContent = '0';
    tr.classList.remove('row-over');
  }
  return dup;
}
function jwItemReturnBlockReason(itemName) {
  if (!pageView || pageView.kind !== 'form' || pageView.m !== 'jobwork' || !pageView.id) return '';
  const job = (db.jobwork || []).find(function (x) { return x.id === pageView.id; });
  const nm = lcName(bareItemName(itemName));
  if (!job || !nm) return '';
  const refs = [];
  (db.jobworkreturn || []).forEach(function (r) {
    if (r.jwRef !== job.no) return;
    const used = (r.items || []).some(function (i) {
      return lcName(bareItemName(i.name)) === nm && (pf(i.retQty) > 0 || pf(i.recQty) > 0);
    });
    if (used) refs.push(r.no || 'Job Work Return');
  });
  (db.inward || []).forEach(function (r) {
    if (r.entryType !== 'Job Work Return' || r.jwRef !== job.no) return;
    const used = (r.items || []).some(function (i) {
      return lcName(bareItemName(i.name)) === nm && (pf(i.recQty) > 0 || pf(i.retQty) > 0);
    });
    if (used) refs.push(r.no || 'Job Work Return Inward');
  });
  return refs.length ? 'Item is locked — used in ' + refs.join(', ') : '';
}
function jwSyncReturnItemDeleteLock(tr) {
  if (!tr) return;
  const del = qs('.jw-row-delete', tr), edit = qs('.jw-row-edit', tr);
  const reason = jwItemReturnBlockReason(rowItemName(tr));
  [del, edit].forEach(function (btn) {
    if (!btn) return;
    if (reason) {
      btn.disabled = true;
      btn.dataset.returnLocked = '1';
      btn.title = reason + ' — editing and deletion are disabled';
    } else if (btn.dataset.returnLocked === '1') {
      delete btn.dataset.returnLocked;
      btn.title = btn.classList.contains('jw-row-edit') ? 'Edit this saved row' : 'Remove this row';
      if (btn.classList.contains('jw-row-edit') || tr.dataset.jwEditing === '1' || tr.dataset.jwSaved !== '1') btn.disabled = false;
    }
  });
}
/* A returned material line stays fully locked, but its DC-level POD control
   remains usable. Since one DC can contain several lines, enable the visible
   merged POD cell when any line in that date group is return-locked. */
function jwAllowPodOnlyForReturnedGroup(tr) {
  const tb = tr && tr.closest ? tr.closest('#jw-issues, #jw-extras') : null;
  if (!tb || !pageView || pageView.m !== 'jobwork') return;
  const date = dval(qs('[data-f="date"]', tr)) || '';
  const group = qsa('tr', tb).filter(function (row) {
    if (row.classList.contains('jw-dgrp')) return false;
    return (dval(qs('[data-f="date"]', row)) || '') === date;
  });
  if (!group.some(function (row) { return !!jwItemReturnBlockReason(rowItemName(row)); })) return;
  group.forEach(function (row) {
    qsa('.pod-cell input[data-pod]', row).forEach(function (el) { el.disabled = false; el.title = 'Upload or replace POD'; });
    qsa('.pod-cell .pod-view', row).forEach(function (el) { el.disabled = false; });
    qsa('.pod-cell .pod-del', row).forEach(function (el) { el.disabled = true; el.title = 'POD removal is disabled because returned material exists'; });
  });
}
function jwRowRefresh(anyEl) {
  const tr = anyEl && anyEl.closest ? anyEl.closest('tr') : null; if (!tr) return;
  const g = function (f) { return qs('[data-f="' + f + '"]', tr); };
  const itEl = g('item');
  const item = itEl ? (itEl.dataset.cbInput ? cboxGet(itEl.dataset.cbInput) : itEl.value) : '';
  const inEl = g('inwardNo');
  if (inEl && inEl.dataset.cbInput) {
    const c = CBOX[inEl.dataset.cbInput];
    if (c) {
      c.opts = inwardOptionsForItem(item, tr).map(normalizeOpt);
      if (!c.opts.some(function (o) { return o.v === c.val; })) {
        c.val = ''; inEl.value = '';
        if (c.opts.length === 1) { c.val = c.opts[0].v; inEl.value = c.opts[0].l; }
      }
      renderCboxList(inEl.dataset.cbInput);
      jwSyncPoRef(tr);
    }
  }
  /* classification drives the row — only back-fill it when it is still empty,
     never overwrite or blank a value the user picked */
  const im = itemByName(item);
  const clsEl = g('classification');
  if (clsEl && im && im.classification) {
    if (clsEl.dataset.cbInput) {
      const cc = CBOX[clsEl.dataset.cbInput];
      if (cc && !cc.val) cboxSet(clsEl.dataset.cbInput, im.classification);
    } else if (!clsEl.value) {
      clsEl.value = im.classification;
    }
  }
  const inwardNo = inEl ? (inEl.dataset.cbInput ? cboxGet(inEl.dataset.cbInput) : inEl.value) : '';
  const stEl = qs('[data-postock]', tr);
  if (stEl) stEl.textContent = fmtNum(inwardItemStock(inwardNo, item, stockExcept(tr)));
  jwDupPoCheck(tr.querySelector('input'));
  jwSyncReturnItemDeleteLock(tr);
  jwRefreshSharedStock();
}
/* classification picked on a JW row → restrict its item list */
function jwClassChanged(clsId, itemId) {
  const cls = cboxGet(clsId);
  const c = CBOX[itemId]; if (!c) return;
  c.opts = itemOptsFor(cls).map(normalizeOpt);
  if (!c.opts.some(function (o) { return o.v === c.val; })) {
    c.val = ''; c.typed = false;
    const inp = qs('[data-cb-input="' + itemId + '"]');
    if (inp) inp.value = '';
  }
  renderCboxList(itemId);
  const anchor = qs('[data-cb-input="' + itemId + '"]');
  if (anchor) jwRowRefresh(anchor);
}
/* Actual Issue Qty can never exceed what the PO still holds */
/* Job Work: the same item may only appear once per table */
function jwDupCheck(anyEl, name) {
  const tr = anyEl && anyEl.closest ? anyEl.closest('tr') : null;
  const nm = lcName(bareItemName(name));
  if (!tr || !nm) return false;
  const tb = tr.parentNode; if (!tb) return false;
  let dup = false;
  qsa('tr', tb).forEach(function (other) {
    if (other === tr || dup) return;
    const el = qs('[data-f="item"]', other);
    const v = el ? (el.dataset.cbInput ? cboxGet(el.dataset.cbInput) : el.value) : '';
    if (lcName(bareItemName(v)) === nm) dup = true;
  });
  if (!dup) return false;
  const where = (tb.id === 'jw-extras') ? 'Extra Quantity Issued' : 'Material Issues';
  toast('Duplicate item \u2014 ' + bareItemName(name) + ' is already listed under ' + where + '. Update that line instead.', 'red');
  const el = qs('[data-f="item"]', tr);
  if (el && el.dataset.cbInput) { cboxSet(el.dataset.cbInput, ''); el.value = ''; }
  ['unit','qty','actualQty','cons'].forEach(function (f) { const x = qs('[data-f="' + f + '"]', tr); if (x) x.value = ''; });
  const st = qs('[data-postock]', tr); if (st) st.textContent = '0';
  if (tb.id === 'jw-extras') calcJWExtras(); else calcJWIssues();
  return true;
}
/* Reason is mandatory on every Extra Quantity Issued line that has an item or qty */
function jwReasonCheck(el) {
  const tr = el && el.closest ? el.closest('tr') : null; if (!tr) return;
  const item = rowItemName(tr);
  const qty = pf((qs('[data-f="qty"]', tr) || {}).value);
  const need = !!item || qty > 0;
  el.classList.toggle('inp-bad', need && !String(el.value || '').trim());
}
function jwCheckActual(el) {
  const tr = el && el.closest ? el.closest('tr') : null; if (!tr) return;
  const stEl = qs('[data-postock]', tr);
  const avail = stEl ? pf(String(stEl.textContent).replace(/,/g, '')) : 0;
  const now = pf(el.value);
  const bad = now > avail;
  el.classList.toggle('inp-bad', bad);
  if (bad) {
    if (!tr.dataset.warnedAct) {
      tr.dataset.warnedAct = '1';
      const nm = qs('[data-f="item"]', tr);
      toast('Actual issue exceeds stock — ' + ((nm && nm.value) || 'item') +
            ' has only ' + fmtNum(avail) + ' available', 'red');
    }
    el.value = avail > 0 ? avail : '';
  } else {
    delete tr.dataset.warnedAct;
  }
  if (tr.parentNode && tr.parentNode.id === 'jw-issues') calcJWIssues(); else calcJWExtras();
  jwRefreshSharedStock();
}
/* default Actual Issue Qty to the auto figure until the user overrides it */
function jwSyncActual(tr) {
  if (!tr) return;
  const autoEl = qs('[data-f="qty"]', tr);
  const actEl = qs('[data-f="actualQty"]', tr);
  if (!autoEl || !actEl) return;
  if (!actEl.dataset.touched) {
    actEl.value = autoEl.value;
    jwCheckActual(actEl);
  }
}
function jwIssueRow(it) {
  it = it || {};
  const itemId = rid('jwi'), unitId = rid('jwiu'), szId = rid('jws'), poId = rid('jwpo'), clsId = rid('jwcl');
  jwSeedGroupPod('jw-issues', it);
  const selSizes = (Array.isArray(it.sizes) && it.sizes.length) ? it.sizes.slice()
    : (it.sizes && typeof it.sizes === 'object' && Object.keys(it.sizes).length) ? Object.keys(it.sizes)
    : [];
  return '<tr data-jw-saved="' + (it._saved ? '1' : '0') + '">' +
    srTd() +
    '<td><input class="inp" type="text" data-date autocomplete="off" readonly disabled data-f="date" value="' + esc(it.date || today()) + '"></td>' +
    '<td><input class="inp" data-f="dcNo" value="' + esc(it.dcNo || '') + '" disabled title="System generated — one DC No. per date"></td>' +
    '<td>' + cbox(clsId, { opts: db.masters.classifications, value: it.classification || (itemByName(it.item) || {}).classification || '', f:'classification', placeholder:'Classification', free:true, onSet: function () { jwClassChanged(clsId, itemId); } }) + '</td>' +
    '<td>' + cbox(itemId, { opts: itemOptsFor(it.classification || (itemByName(it.item) || {}).classification || ''), value: it.item || '', f:'item', placeholder:'Fabric / Trim', free:true, onSet: function (v) { const el = qs('[data-cb-input="' + itemId + '"]'); jwSetUnit(el, v); jwRowRefresh(el); calcJWIssues(); } }) +
      '<input type="text" data-f="colour" value="' + esc(it.colour || '') + '" style="display:none" tabindex="-1" aria-hidden="true">' +
      '</td>' +
    '<td>' + cbox(poId, { opts: inwardOptionsForItem(it.item), value: inwardNoForIssue(it), f:'inwardNo', placeholder:'Select Inward', free:false, onSet: function () { jwSyncPoRef(qs('[data-cb-input="' + poId + '"]').closest('tr')); jwRowRefresh(qs('[data-cb-input="' + poId + '"]')); } }) + '</td>' +
    '<td class="po-ref-cell"><input class="inp" data-f="poRef" value="' + esc(it.poRef || poRefFromInward(inwardNoForIssue(it))) + '" placeholder="Auto" disabled></td>' +
    '<td class="num" data-postock style="color:var(--muted);font-weight:600">' + fmtNum(inwardItemStock(inwardNoForIssue(it), it.item, stockExcept())) + '</td>' +
    '<td class="sizes-cell">' +
      mcbox(szId, { opts: db.masters.sizes, selected: selSizes, placeholder: 'Select sizes…', onSet: function () { calcIssueQtyById(szId); } }) +
    '</td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="cons" value="' + (it.cons ?? '') + '" placeholder="e.g. 1.2" oninput="calcIssueQtyById(\'' + szId + '\')" title="Consumption per garment"></td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="qty" value="' + (it.qty ?? '') + '" readonly style="color:var(--muted);font-weight:700" title="Auto-calculated = Order Qty (selected sizes) × Cons./Gmt"></td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="actualQty" value="' + (it.actualQty ?? it.qty ?? '') + '" placeholder="0" oninput="this.dataset.touched=1;jwCheckActual(this)" title="Actually issued — cannot exceed stock"></td>' +
    '<td class="uom-cell"><input class="inp" data-f="unit" value="' + esc(it.unit || itemUnit(it.item) || '') + '" placeholder="Auto" disabled></td>' +
    '<td class="pod-cell" data-jw-group-pod>' + ((it.date || '') ? podCellHtml(jwPodIdFor('jw-issues', it.date || '')) : '<span class="att-none">Select date</span>') + '</td>' +
    '<td class="actions-cell">' +
      (it._saved ? '<button type="button" class="btn-icon jw-row-edit" title="Edit this saved row" onclick="jwEditIssueRow(this)">' + ic('pencil', 15) + '</button>' : '') +
      '<button type="button" class="btn-icon danger jw-row-delete" title="Remove this row" onclick="removeIssueRow(this)">' + ic('trash', 15) + '</button></td>' +
  '</tr>';
}
function jwDomSizeOrder(colour) {
  // live order qty per size from the styles register (same form); optionally filtered to a matching garment colour
  const map = {};
  qsa('#jw-garments tr').forEach(tr => {
    if (colour) {
      const colInp = qs('[data-f="colour"]', tr);
      const rc = colInp ? colInp.value.trim() : '';
      if (rc && rc.toLowerCase() !== String(colour).toLowerCase()) return;
    }
    qsa('[data-f^="sz"]', tr).forEach(inp => { const s = inp.dataset.f.slice(2); map[s] = (map[s] || 0) + pf(inp.value); });
  });
  return map;
}
function jwStyleSizesUsed(colour) {
  // sizes that actually have order qty entered in Finished Garments — Styles (optionally filtered to a colour)
  const used = jwDomSizeOrder(colour);
  return db.masters.sizes.filter(s => (used[s] || 0) > 0);
}
function refreshIssueSizeOptsForRow(tr) {
  const mcb = qs('.mcbox', tr); if (!mcb || !mcb.dataset.mcb) return;
  const c = MCBOX[mcb.dataset.mcb]; if (!c) return;
  const colInp = qs('[data-f="colour"]', tr);
  const colour = colInp ? colInp.value.trim() : '';
  let used = jwStyleSizesUsed(colour);
  if (!used.length) used = db.masters.sizes.slice();   // nothing entered in Styles yet → don't block the user
  c.opts = used.map(s => ({ v: s, l: s }));
  c.sel = c.sel.filter(s => used.indexOf(s) > -1);
  updateMcboxInput(mcb.dataset.mcb); renderMcboxList(mcb.dataset.mcb);
}
function calcIssueQtyById(szId) {
  const mcbEl = qs('[data-mcb="' + szId + '"]'); if (!mcbEl) return;
  const tr = mcbEl.closest('tr'); if (tr) { refreshIssueSizeOptsForRow(tr); calcIssueRowQty(tr); }
}
function calcIssueRowQty(tr) {
  const mcb = qs('.mcbox', tr); const c = (mcb && mcb.dataset.mcb) ? MCBOX[mcb.dataset.mcb] : null;
  const consInp = qs('[data-f="cons"]', tr);
  const cons = consInp ? pf(consInp.value) : 0;
  const colInp = qs('[data-f="colour"]', tr);
  const colour = colInp ? String(colInp.value || '').trim() : '';
  const orders = jwDomSizeOrder(colour);
  let totalOrder = 0;
  if (c) c.sel.forEach(s => { totalOrder += (orders[s] || 0); });
  const qty = (cons > 0 && totalOrder > 0) ? Math.round(totalOrder * cons * 100) / 100 : 0;
  const qtyInp = qs('[data-f="qty"]', tr);
  if (qtyInp) qtyInp.value = qty > 0 ? qty : '';
  jwSyncActual(tr);
  calcJWIssues();
}
function afterRenderIssues() {
  qsa('#jw-issues tr').forEach(function (tr) {
    if (tr.classList.contains('jw-dgrp')) return;
    const m = qs('.mcbox', tr);
    if (m && m.dataset.mcb) renderMcboxList(m.dataset.mcb);
    refreshIssueSizeOptsForRow(tr);
    calcIssueRowQty(tr);
    jwRowRefresh(qs('[data-f="item"]', tr) || tr);
  });
  calcJWIssues();
  jwGroupRowsByDate('jw-issues');
  jwLockPodRows();
  jwLockSavedRows('jw-issues');
}
/* Saved Material Issue / Extra Issue rows open read-only. The pencil button is
   the explicit opt-in that unlocks only that row for editing. */
function jwRowHasGroupPod(tr) {
  if (!tr) return false;
  const tb = tr.closest('#jw-issues, #jw-extras');
  const d = dval(qs('[data-f="date"]', tr)) || '';
  if (tb && d) {
    const pd = PODDATA[jwPodIdFor(tb.id, d)];
    if (pd && pd.pod) return true;
  }
  return typeof jwRowHasPod === 'function' ? jwRowHasPod(tr) : false;
}
function jwDisableEditIfPod(tr) {
  const edit = qs('.jw-row-edit', tr);
  if (!edit) return false;
  if (!jwRowHasGroupPod(tr)) return false;
  edit.disabled = true;
  edit.title = 'Edit locked — POD (Proof of Delivery) is uploaded for this DC';
  tr.dataset.jwEditing = '';
  return true;
}
function jwLockSavedRows(tbodyId) {
  const tb = $(tbodyId); if (!tb) return;
  qsa('tr[data-jw-saved="1"]', tb).forEach(function (tr) {
    if (tr.dataset.jwEditing === '1' && !jwRowHasGroupPod(tr)) return;
    tr.classList.add('jw-edit-locked');
    qsa('input,select,textarea,button', tr).forEach(function (el) { el.disabled = true; });
    const edit = qs('.jw-row-edit', tr);
    if (edit) { edit.disabled = false; edit.title = 'Edit this saved row'; }
    if (jwDisableEditIfPod(tr)) { /* keep pencil locked when POD exists */ }
    jwSyncReturnItemDeleteLock(tr);
    qsa('.pod-view', tr).forEach(function (el) { el.disabled = false; });
  });
  qsa('tr[data-jw-saved="1"]', tb).forEach(jwAllowPodOnlyForReturnedGroup);
}
function jwEditIssueRow(btn) {
  const tr = btn && btn.closest ? btn.closest('tr') : null; if (!tr) return;
  if (jwRowHasGroupPod(tr)) return toast('Edit locked — POD is already uploaded for this DC', 'red');
  const returnBlock = jwItemReturnBlockReason(rowItemName(tr));
  if (returnBlock) return toast(returnBlock + ' — it cannot be edited', 'red');
  const mod = (pageView && pageView.m === 'sample') ? 'sample' : 'jobwork';
  const rec = (pageView && pageView.id) ? (db[mod] || []).find(function (x) { return x.id === pageView.id; }) : null;
  if (rec && rec.status === 'Closed') return toast((rec.no || (mod === 'sample' ? 'Sample' : 'Job Work')) + ' is closed — reopen it before editing', 'red');
  tr.dataset.jwEditing = '1';
  tr.classList.remove('jw-edit-locked', 'row-locked');
  tr.removeAttribute('title');
  qsa('input,select,textarea,button', tr).forEach(function (el) {
    const f = (el.dataset && el.dataset.f) || '';
    /* System-controlled fields never become editable. */
    if (f === 'date' || f === 'dcNo' || f === 'unit' || f === 'poRef' || el.readOnly) { el.disabled = true; return; }
    el.disabled = false;
  });
  btn.disabled = true;
  jwSyncReturnItemDeleteLock(tr);
  const docName = (pageView && pageView.m === 'sample') ? 'Sample' : 'Job Work';
  btn.title = 'Editing enabled — save the ' + docName + ' to apply changes';
  const first = qs('[data-f="classification"]', tr) || qs('[data-f="item"]', tr) || qs('[data-f="reason"]', tr);
  if (first) { try { first.focus(); } catch (e) {} }
  toast('Row enabled for editing — press Save ' + docName + ' when finished', 'green');
}
function jwRowHasPod(tr) {
  if (!tr) return false;
  const inp = qs('[data-pod]', tr);
  if (!inp) return false;
  const pd = PODDATA[inp.dataset.pod];
  return !!(pd && pd.pod);
}
function jwUnlockPodRow(tr) {
  if (!tr) return;
  tr.classList.remove('row-locked');
  tr.removeAttribute('title');
  qsa('input,select,textarea,button', tr).forEach(function (el) {
    if (el.readOnly) return;
    if (el.dataset && (el.dataset.f === 'unit' || el.dataset.f === 'poRef')) return;
    if (el.getAttribute('placeholder') === 'Auto') return;
    el.disabled = false;
  });
  jwSyncReturnItemDeleteLock(tr);
}
function jwSyncPodDelete(tr) {
  if (!tr) return;
  const why = 'POD already uploaded — this line is locked';
  if (jwRowHasPod(tr)) {
    lockTableRow(tr, why);
    qsa('.pod-cell .pod-del, .pod-cell .pod-view', tr).forEach(function (el) { el.disabled = false; });
    qsa('.pod-cell input[data-pod]', tr).forEach(function (el) { el.disabled = false; });
    const del = qs('td:last-child button.btn-icon.danger', tr);
    if (del) { del.disabled = true; del.title = why; }
  } else {
    jwUnlockPodRow(tr);
  }
}
function jwLockPodRows() {
  ['jw-issues', 'jw-extras'].forEach(function (tid) {
    const tb = $(tid); if (!tb) return;
    qsa('tr', tb).forEach(function (tr) {
      if (tr.classList.contains('jw-dgrp')) return;
      const d = dval(qs('[data-f="date"]', tr)) || '';
      const pd = PODDATA[jwPodIdFor(tid, d)];
      if (pd && pd.pod) {
        lockTableRow(tr, 'POD already uploaded for DC ' + (((qs('[data-f="dcNo"]', tr) || {}).value) || '') + ' — this DC group is locked');
        jwDisableEditIfPod(tr);
        qsa('.pod-cell .pod-del, .pod-cell .pod-view', tr).forEach(function (el) { el.disabled = false; });
        qsa('.pod-cell input[data-pod]', tr).forEach(function (el) { el.disabled = false; });
      } else if (!(tr.dataset.jwSaved === '1' && tr.dataset.jwEditing !== '1')) {
        jwUnlockPodRow(tr);
      }
    });
    qsa('tr.jw-dgrp .pod-del, tr.jw-dgrp .pod-view', tb).forEach(function (el) { el.disabled = false; });
    qsa('tr', tb).forEach(function (tr) { if (!tr.classList.contains('jw-dgrp')) jwAllowPodOnlyForReturnedGroup(tr); });
  });
}
document.addEventListener('change', function (e) {
  const el = e.target;
  if (!el || !el.matches || !el.matches('input[data-date][data-f="date"]')) return;
  const tb = el.closest('#jw-issues, #jw-extras');
  if (tb && tb.id) { jwGroupRowsByDate(tb.id); jwLockPodRows(); }
});
function podIsPdf(pd) {
  if (!pd) return false;
  if (pd.kind === 'pdf') return true;
  if (/^data:application\/pdf/i.test(pd.pod || '')) return true;
  return /\.pdf$/i.test(pd.podName || '');
}
function podCellHtml(podId) {
  const pd = PODDATA[podId];
  const accept = ' accept="image/*,application/pdf,.pdf"';
  const change = ' onchange="onPODFile(this,\'' + podId + '\')"';
  if (!(pd && pd.pod)) {
    return '<label class="pod-upload-box empty" title="Upload proof of delivery">' +
      '<input type="file"' + accept + ' data-pod="' + podId + '"' + change + '>' +
      '<span class="pod-upload-plus">＋</span>' +
      '<span class="pod-upload-title">Upload POD</span>' +
      '<span class="pod-upload-help">Image or PDF · max 5 MB</span></label>';
  }
  const preview = podIsPdf(pd)
    ? '<div class="pod-preview-pdf">PDF</div>'
    : '<img class="pod-preview-img" src="' + pd.pod + '" alt="POD preview">';
  return '<div class="pod-upload-box has">' +
    '<div class="pod-upload-actions">' +
      '<button type="button" class="pod-view" title="View POD" onclick="event.stopPropagation();viewPOD(\'' + podId + '\')">' + ic('view', 12) + '</button>' +
      '<button type="button" class="pod-del" title="Remove POD" onclick="event.stopPropagation();clearPOD(\'' + podId + '\')">' + ic('trash', 11) + '</button>' +
    '</div>' + preview +
    '<div class="pod-upload-name" title="' + esc(pd.podName || 'POD') + '">' + esc(pd.podName || 'POD') + '</div>' +
    '<label class="pod-replace">Replace<input type="file"' + accept + ' data-pod="' + podId + '"' + change + '></label>' +
  '</div>';
}
/* Convert a stored data URL to a Blob URL. Browsers commonly block direct
   data: PDF navigation from a local HTML file, while Blob URLs open reliably
   in the browser's built-in PDF viewer. */
function podObjectUrl(data) {
  const src = String(data || '');
  if (!/^data:/i.test(src)) return { url: src, revoke: false };
  const comma = src.indexOf(',');
  if (comma < 0) return { url: src, revoke: false };
  const meta = src.slice(0, comma);
  const mimeHit = /^data:([^;,]+)/i.exec(meta);
  const mime = mimeHit ? mimeHit[1] : 'application/octet-stream';
  const raw = src.slice(comma + 1);
  try {
    let bytes;
    if (/;base64/i.test(meta)) {
      const bin = atob(raw);
      bytes = new Uint8Array(bin.length);
      for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
    } else {
      const text = decodeURIComponent(raw);
      bytes = new TextEncoder().encode(text);
    }
    return { url: URL.createObjectURL(new Blob([bytes], { type: mime })), revoke: true };
  } catch (e) {
    return { url: src, revoke: false };
  }
}
function viewPOD(podId) {
  const pd = PODDATA[podId]; if (!pd || !pd.pod) return;
  if (podIsPdf(pd)) {
    /* Open the tab synchronously from the click so popup blockers allow it,
       then navigate that tab to a browser-readable Blob URL. */
    const w = window.open('about:blank', '_blank');
    if (!w) return toast('Allow pop-ups to view the PDF', 'red');
    const obj = podObjectUrl(pd.pod);
    if (!obj.url) { w.close(); return toast('Could not prepare the PDF preview', 'red'); }
    try { w.location.replace(obj.url); }
    catch (e) { w.location.href = obj.url; }
    if (obj.revoke) setTimeout(function () { try { URL.revokeObjectURL(obj.url); } catch (e) {} }, 300000);
    return;
  }
  const w = window.open('', '_blank');
  if (w) w.document.write('<html><body style="margin:0;background:#1a1a1a;display:flex;align-items:center;justify-content:center;min-height:100vh"><img src="' + pd.pod + '" style="max-width:95vw;max-height:95vh;border-radius:12px"></body></html>');
}
function onPODFile(inp, podId) {
  const f = inp.files && inp.files[0]; if (!f) return;
  const isPdf = (f.type && /pdf/i.test(f.type)) || /\.pdf$/i.test(f.name);
  const isImg = (f.type && /^image\//i.test(f.type)) || /\.(png|jpe?g|gif|webp|bmp)$/i.test(f.name);
  if (!isPdf && !isImg) { toast('Upload an image or a PDF', 'red'); inp.value = ''; return; }
  if (f.size > 5 * 1024 * 1024) { toast('POD file is too large (max 5 MB)', 'red'); inp.value = ''; return; }
  const reader = new FileReader();
  reader.onload = function (e) {
    const dataUrl = e.target.result;
    const finish = function (payload) {
      PODDATA[podId] = payload;
      qsa('[data-pod="' + podId + '"]').forEach(function (podInp) {
        const cell = podInp.closest('.pod-cell');
        if (cell) cell.innerHTML = podCellHtml(podId);
      });
      jwLockPodRows();
      toast('POD attached: ' + f.name, 'green');
    };
    if (isPdf) {
      finish({ pod: dataUrl, podName: f.name, kind: 'pdf' });
      return;
    }
    const img = new Image();
    img.onload = function () {
      const max = 900;
      let w = img.width, h = img.height;
      if (w > max) { h = Math.round(h * max / w); w = max; }
      const cv = document.createElement('canvas'); cv.width = w; cv.height = h;
      cv.getContext('2d').drawImage(img, 0, 0, w, h);
      finish({ pod: cv.toDataURL('image/jpeg', 0.72), podName: f.name, kind: 'image' });
    };
    img.onerror = function () { toast('Could not read that image', 'red'); };
    img.src = dataUrl;
  };
  reader.readAsDataURL(f);
}
function clearPOD(podId) {
  delete PODDATA[podId];
  const inputs = qsa('[data-pod="' + podId + '"]'); if (!inputs.length) return;
  inputs.forEach(function (inp) {
    const cell = inp.closest('.pod-cell');
    if (cell) cell.innerHTML = podCellHtml(podId);
  });
  jwLockPodRows();
  toast('POD removed');
}
function removeIssueRow(btn) {
  const tr = btn.closest('tr');
  const returnBlock = jwItemReturnBlockReason(rowItemName(tr));
  if (returnBlock) return toast(returnBlock, 'red');
  // v5: allow delete after user has clicked Edit pencil (jwEditing=1)
  if (jwRowGroupHasPod(tr) && tr.dataset.jwEditing !== '1') return toast('Cannot delete — POD already uploaded for this date. Click the Edit pencil first to unlock the row.', 'red');
  const mcb = qs('.mcbox', tr);
  if (mcb && mcb.dataset.mcb) delete MCBOX[mcb.dataset.mcb];
  tr.remove(); calcJWIssues(); jwGroupRowsByDate('jw-issues'); jwRefreshSharedStock();
}
/* styles chosen on a job work — the header multi-select is the source of truth */
function jwStyleList(r) {
  if (r && Array.isArray(r.styles) && r.styles.length) return r.styles.slice();
  const out = [];
  ((r && r.garments) || []).forEach(function (g) {
    const s = g.style || g.desc;
    if (s && out.indexOf(s) === -1) out.push(s);
  });
  return out;
}
/* one garment row per selected style */
function jwGarmentRowsHtml(r) {
  const list = jwStyleList(r);
  if (!list.length) return '';
  return list.map(function (s) {
    const g = ((r && r.garments) || []).find(function (x) { return (x.style || x.desc) === s; }) || {};
    return jwGarmentRow(Object.assign({}, g, { style: s }));
  }).join('');
}
/* header selection changed — rebuild the row without clearing its size selection.
   Changing only the Style ID must not discard sizes/quantities already entered. */
function jwStylesChanged() {
  const tb = $('jw-garments'); if (!tb) return;
  const s = cboxGet('jw-style');
  let keep = {};
  const tr = qs('#jw-garments tr');
  if (tr) {
    const mcb = qs('.mcbox', tr);
    const mid = mcb && mcb.dataset.mcb;
    const mc = mid ? MCBOX[mid] : null;
    const store = mid ? (rowSz[mid] || { sizes: {} }) : { sizes: {} };
    const typed = Object.assign({}, store.sizes || {});

    /* Capture the current boxes before replacing the row. Selected sizes are
       retained even when their quantity is still blank or zero. */
    qsa('input[data-f^="sz"]', tr).forEach(function (inp) {
      typed[inp.dataset.f.slice(2)] = inp.value;
    });
    const selected = mc ? mc.sel.slice() : Object.keys(typed);
    const sizes = {};
    selected.forEach(function (size) {
      sizes[size] = typed[size] != null ? typed[size] : '';
    });

    const colEl = qs('[data-f="colour"]', tr);
    const rateEl = qs('[data-f="rate"]', tr);
    keep = {
      colour: colEl ? colEl.value : '',
      rate: rateEl ? rateEl.value : '',
      sizes: sizes
    };
    if (mid) {
      delete MCBOX[mid];
      delete rowSz[mid];
    }
  }
  tb.innerHTML = s ? jwGarmentRow(Object.assign({}, keep, { style: s })) : '';
  afterRenderGarments();
}
function jwGarmentRow(g) {
  g = g || {};
  const szId = rid('jgsz');
  const style = g.style || g.desc || '';
  const sizes = g.sizes || {};
  const sel = Object.keys(sizes);   // nothing pre-selected on a new row
  /* sizes saved with a quantity are locked while editing an existing Job Work */
  const lockedSizes = g._duplicate ? [] : sel.filter(function (k) { return pf(sizes[k]) > 0; });
  rowSz[szId] = { sizes: Object.assign({}, sizes) };
  return '<tr>' +
    srTd() +
    '<td class="style-cell jw-stname"><b>' + esc(styleLabel(style) || style) + '</b>' +
      '<input type="hidden" data-f="style" value="' + esc(style) + '"></td>' +
    '<input type="hidden" data-f="colour" value="' + esc(g.colour || '') + '">' +
    '<td class="sizes-cell">' +
      mcbox(szId, { opts: db.masters.sizes, selected: sel, locked: lockedSizes,
        lockMsg: 'This size already has a saved quantity on this Job Work and cannot be removed',
        placeholder: 'Select sizes…', onSet: function () { renderSzInputs(szId); } }) +
    '</td>' +
    '<td class="sizes-cell">' +
      '<div class="sz-inputs" id="szin_' + szId + '"></div>' +
    '</td>' +
    '<td class="num" data-order style="font-weight:700">0</td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="rate" value="' + (g.rate ?? '') + '" oninput="calcJW()"></td>' +
    '<td class="num" data-gamt style="font-weight:700">0</td>' +
    ((pageView && pageView.m === 'sample')
      ? '<td><button type="button" class="btn-icon danger" title="Remove this style" onclick="smpRemoveStyleRow(this)">' + ic('trash', 15) + '</button></td>'
      : '') +
  '</tr>';
}
/* dropping a row unticks that style in the header picker */
function jwRemoveStyleRow(btn) {
  const tr = btn.closest('tr'); if (!tr) return;
  cboxSet('jw-style', '');
  const inp = qs('[data-cb-input="jw-style"]');
  if (inp) inp.value = '';
  tr.remove();
  afterRenderGarments();
}
function renderSzInputs(szId) {
  const c = MCBOX[szId]; const wrap = $('szin_' + szId); if (!c || !wrap) return;
  const store = rowSz[szId] || (rowSz[szId] = { sizes: {} });
  // capture typed values before regenerating
  qsa('input[data-f^="sz"]', wrap).forEach(inp => { store.sizes[inp.dataset.f.slice(2)] = inp.value; });
  wrap.innerHTML = c.sel.map(s =>
    '<span class="szbox"><span class="szlbl">' + esc(s) + '</span><input class="inp inp-sm szin" type="number" min="0" data-f="sz' + esc(s) + '" value="' + esc(store.sizes[s] != null ? store.sizes[s] : '') + '" oninput="calcJW()"></span>'
  ).join('');
  calcJW();
}
function afterRenderGarments() {
  renumberSr();
  // called after the form HTML is injected — populates size inputs for every style row
  qsa('#jw-garments tr').forEach(tr => {
    const m = qs('.mcbox', tr);
    if (m && m.dataset.mcb) { renderMcboxList(m.dataset.mcb); renderSzInputs(m.dataset.mcb); }
  });
  calcJW();
}
function removeGarmentRow(btn) { btn.closest('tr').remove(); calcJW(); }
function gatherGarmentRows() {
  const out = [];
  qsa('#jw-garments tr').forEach(tr => {
    const styEl = qs('[data-f="style"]', tr);
    const style = styEl ? (styEl.dataset.cbInput ? cboxGet(styEl.dataset.cbInput) : (styEl.value || '')) : '';
    const colEl = qs('[data-f="colour"]', tr);
    const colour = colEl ? (colEl.dataset.cbInput ? cboxGet(colEl.dataset.cbInput) : (colEl.value || '')) : '';
    const rate = pf(qs('[data-f="rate"]', tr) ? qs('[data-f="rate"]', tr).value : 0);
    const sizes = {};
    let orderQty = 0;
    qsa('[data-f^="sz"]', tr).forEach(inp => {
      const v = pf(inp.value);
      if (v > 0) { sizes[inp.dataset.f.slice(2)] = v; orderQty += v; }
    });
    if (style || orderQty > 0) {
      const st = db.masters.styles.find(x => x.internal === style);
      out.push({ style: style, portal: st ? st.portal : '', desc: style, colour: colour, sizes: sizes, orderQty: orderQty, rate: rate });
    }
  });
  return out;
}
function calcJWIssues() {
  const tb = $('jw-issues'); if (!tb) return;
  let fab = 0, fabUnit = 'm'; const trims = {};
  qsa('tr', tb).forEach(tr => {
    const item = ((qs('[data-f="item"]', tr) || {}).value || '').trim();
    const qty = pf((qs('[data-f="qty"]', tr) || {}).value);
    if (!item || qty <= 0) return;
    if (isFabricItem(item)) { fab += qty; const u = ((qs('[data-f="unit"]', tr) || {}).value || '').trim(); if (u) fabUnit = u; }
    else trims[item] = (trims[item] || 0) + qty;
  });
  const el = $('jw-totals'); if (!el) return;
  let h = 'Fabric: <b>' + fmtNum(fab) + ' ' + fabUnit + '</b>';
  const names = Object.keys(trims);
  if (names.length) h += ' &nbsp;·&nbsp; Trims: ' + names.map(n => '<b>' + esc(n) + ': ' + fmtNum(trims[n]) + '</b>').join(' &nbsp; ');
  el.innerHTML = h;
}

function formJobWork(rec) {
  const r = rec || {};
  return '<div class="sec">Job Worker</div><div class="grid-fixed">' +
    (rec && rec.id ? field('JW No', '<input class="inp" id="f-no" value="' + esc(r.no || '') + '" disabled>') : '') +
    field('Date *', '<input class="inp" type="text" data-date autocomplete="off" readonly disabled id="f-date" value="' + esc(r.date || today()) + '">') +
    field('Job Worker / Vendor *', cbox('jw-vendor', { opts: jobWorkVendorOptions(), value: r.vendor || '', f:'vendor', placeholder:'Search Vendor parties…', free:false })) +
    field('Style (Internal ID) *', cbox('jw-style', { opts: styleOpts(),
      value: jwStyleList(r)[0] || '', display: styleLabel(jwStyleList(r)[0] || ''),
      f:'style', placeholder:'Search & select a style…', free:false,
      onSet: function () { jwStylesChanged(); } })) +
  '</div>' +
  '<div class="sec">Finished Garments — Styles <span style="text-transform:none;color:var(--muted);font-weight:600">(the style picked above · select sizes &amp; enter size-wise qty)</span></div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' + srTh() + '<th>Style</th><th>Size Selection *</th><th>Size-wise Qty</th><th>Order Qty</th><th>Job Work CM (₹/pc)</th><th>Charges</th></tr></thead>' +
  '<tbody id="jw-garments">' + jwGarmentRowsHtml(r) + '</tbody>' +
  '<tfoot><tr class="tot-row"><td></td><td>Totals</td><td></td><td></td><td class="num" id="jw-gtot-oqty">0</td><td></td><td class="num" id="jw-gtot-chg">₹0</td></tr></tfoot></table></div>' +
  '<div class="sec">Material Issues to Job Worker <span style="text-transform:none;color:var(--muted);font-weight:600">(one DC No. = one POD upload)</span></div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' + srTh() + '<th>Issue Date</th><th>DC No.</th><th>Classification *</th><th>Item Name *</th><th>Inward No. *</th><th>PO No.</th><th class="num">Stock</th><th>Size Selection *</th><th>Cons./Gmt (Mtr/Nos) *</th><th>Qty (auto)</th><th class="num">Actual Issue Qty</th><th>UOM</th><th>POD (Proof of Delivery)</th><th></th></tr></thead>' +
  '<tbody id="jw-issues">' + ((r.issues && r.issues.length) ? jwLinesByDate(r.issues).map(function (x) { return jwIssueRow(Object.assign({}, x, { _saved:!!r.id })); }).join('') : jwIssueRow({ date: today() })) + '</tbody>' +
  '</table></div>' +
  '<button class="btn btn-ghost btn-sm" style="margin-top:10px" onclick="addJwIssue()">＋ Add Issue Record</button>' +

  '<div class="sec">Extra Quantity Issued <span style="text-transform:none;color:var(--muted);font-weight:600">(one DC No. = one POD upload)</span></div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' + srTh() + '<th>Issue Date</th><th>DC No.</th><th>Classification *</th><th>Item Name *</th><th>Inward No. *</th><th>PO No.</th><th class="num">Stock</th><th class="num">Actual Issue Qty</th><th>UOM</th><th>Reason *</th><th>POD (Proof of Delivery)</th><th></th></tr></thead>' +
  '<tbody id="jw-extras">' + ((r.extras && r.extras.length) ? jwLinesByDate(r.extras).map(function (x) { return jwExtraRow(Object.assign({}, x, { _saved:!!r.id })); }).join('') : jwExtraRow({ date: today() })) + '</tbody>' +
  '</table></div>' +
  '<button class="btn btn-ghost btn-sm" style="margin-top:10px" onclick="addJwExtra()">＋ Add Extra Issue</button>' +
  '';
}
function calcJW() {
  const tb = $('jw-garments'); if (!tb) return;
  let tq = 0, tc = 0;
  qsa('tr', tb).forEach(tr => {
    let q = 0;
    qsa('[data-f^="sz"]', tr).forEach(inp => { q += pf(inp.value); });
    const rt = pf(qs('[data-f="rate"]', tr) ? qs('[data-f="rate"]', tr).value : 0);
    const a = q * rt;
    const od = qs('[data-order]', tr); if (od) od.textContent = fmtNum(q);
    const el = qs('[data-gamt]', tr); if (el) el.textContent = fmtNum(a);
    tq += q; tc += a;
  });
  $('jw-gtot-oqty').textContent = fmtNum(tq);
  $('jw-gtot-chg').textContent = fmtINR(tc);
  // refresh issue size options + Qty (auto-calculated from selected sizes' order qty × consumption)
  qsa('#jw-issues tr').forEach(tr => { refreshIssueSizeOptsForRow(tr); calcIssueRowQty(tr); });
}
/* ---------------- SAMPLE (Job Work without material issues) -------------
   Sample has its own material-issue row: no Reason column, and Issue Date /
   Classification / Item Name / Inward No. are all mandatory. */
function smpIssueRow(it) {
  it = it || {};
  const itemId = rid('smi'), poId = rid('smpo'), clsId = rid('smcl');
  const issueDate = it.date || today();
  jwSeedGroupPod('jw-extras', Object.assign({}, it, { date:issueDate }));
  const podId = jwPodIdFor('jw-extras', issueDate);
  return '<tr data-jw-saved="' + (it._saved ? '1' : '0') + '">' +
    srTd() +
    '<td><input class="inp" type="text" data-date autocomplete="off" readonly disabled data-f="date" value="' + esc(issueDate) + '"></td>' +
    '<td><input class="inp" data-f="dcNo" value="' + esc(it.dcNo || '') + '" disabled title="System generated after Save"></td>' +
    '<td>' + cbox(clsId, { opts: db.masters.classifications, value: it.classification || (itemByName(it.item) || {}).classification || '', f:'classification', placeholder:'Classification *', free:true, onSet: function () { jwClassChanged(clsId, itemId); } }) + '</td>' +
    '<td>' + cbox(itemId, { opts: itemOptsFor(it.classification || (itemByName(it.item) || {}).classification || ''), value: it.item || '', f:'item', placeholder:'Fabric / Trim *', free:true, onSet: function (v) { const el = qs('[data-cb-input="' + itemId + '"]'); jwSetUnit(el, v); jwRowRefresh(el); calcJWExtras(); } }) +
      '</td>' +
    '<td>' + cbox(poId, { opts: inwardOptionsForItem(it.item), value: inwardNoForIssue(it), f:'inwardNo', placeholder:'Select Inward *', free:false, onSet: function () { jwSyncPoRef(qs('[data-cb-input="' + poId + '"]').closest('tr')); jwRowRefresh(qs('[data-cb-input="' + poId + '"]')); } }) + '</td>' +
    '<td class="po-ref-cell"><input class="inp" data-f="poRef" value="' + esc(it.poRef || poRefFromInward(inwardNoForIssue(it))) + '" placeholder="Auto" disabled></td>' +
    '<td class="num" data-postock style="color:var(--muted);font-weight:600">' + fmtNum(inwardItemStock(inwardNoForIssue(it), it.item, stockExcept())) + '</td>' +
    '<td><input class="inp num" type="number" min="0" step="0.01" data-f="qty" value="' + (it.qty ?? '') + '" placeholder="0" oninput="jwCheckActual(this)" title="Cannot exceed stock"></td>' +
    '<td class="uom-cell"><input class="inp" data-f="unit" value="' + esc(it.unit || itemUnit(it.item) || '') + '" placeholder="Auto" disabled></td>' +
    '<td class="pod-cell" data-jw-group-pod>' + podCellHtml(podId) + '</td>' +
    '<td class="actions-cell">' +
      (it._saved ? '<button type="button" class="btn-icon jw-row-edit" title="Edit this saved row" onclick="jwEditIssueRow(this)">' + ic('pencil', 15) + '</button>' : '') +
      '<button type="button" class="btn-icon danger jw-row-delete" onclick="removeRow(this);calcJWExtras();jwGroupRowsByDate(\'jw-extras\');jwRefreshSharedStock()">' + ic('trash', 15) + '</button></td>' +
  '</tr>';
}
/* Sample allows several styles — rebuild one garment row per picked style */
function smpStylesChanged() {
  const tb = $('jw-garments'); if (!tb) return;
  const chosen = mcboxGet('smp-styles');
  const current = gatherGarmentRows();
  tb.innerHTML = chosen.map(function (s) {
    const g = current.find(function (x) { return x.style === s; }) || {};
    return jwGarmentRow(Object.assign({}, g, { style: s }));
  }).join('');
  afterRenderGarments();
}
/* dropping a garment row unticks that style in the header picker */
function smpRemoveStyleRow(btn) {
  const tr = btn.closest('tr'); if (!tr) return;
  const el = qs('[data-f="style"]', tr);
  const s = el ? el.value : '';
  const c = MCBOX['smp-styles'];
  if (c && s) {
    const i = c.sel.indexOf(s);
    if (i > -1) c.sel.splice(i, 1);
    updateMcboxInput('smp-styles'); renderMcboxList('smp-styles');
  }
  tr.remove();
  afterRenderGarments();
}
function formSample(rec) {
  const r = rec || {};
  return '<div class="sec">Sample</div><div class="grid-fixed">' +
    (rec && rec.id ? field('Sample No', '<input class="inp" id="f-no" value="' + esc(r.no || '') + '" disabled>') : '') +
    field('Date *', '<input class="inp" type="text" data-date autocomplete="off" readonly disabled id="f-date" value="' + esc(r.date || today()) + '">') +
    field('Job Worker / Vendor *', cbox('jw-vendor', { opts: jobWorkVendorOptions(), value: r.vendor || '', f:'vendor', placeholder:'Search Vendor parties…', free:false })) +
    field('Style (Internal ID) *', mcbox('smp-styles', { opts: styleOpts(),
      selected: jwStyleList(r), disabled: !!(rec && rec.id),
      placeholder: 'Search & select styles…', onSet: function () { smpStylesChanged(); } })) +
  '</div>' +
  '<div class="sec">Sample Garments — Styles <span style="text-transform:none;color:var(--muted);font-weight:600">(one row per style picked above · select sizes &amp; enter size-wise qty)</span></div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' + srTh() + '<th>Style</th><th>Size Selection *</th><th>Size-wise Qty</th><th>Sample Qty</th><th>Rate (₹/pc)</th><th>Charges</th><th></th></tr></thead>' +
  '<tbody id="jw-garments">' + jwGarmentRowsHtml(r) + '</tbody>' +
  '<tfoot><tr class="tot-row"><td></td><td>Totals</td><td></td><td></td><td class="num" id="jw-gtot-oqty">0</td><td></td><td class="num" id="jw-gtot-chg">₹0</td><td></td></tr></tfoot></table></div>' +

  '<div class="sec">Material Issues to Job Worker <span style="text-transform:none;color:var(--muted);font-weight:600">(one Sample DC No. = one POD upload · SAM number generated after Save)</span></div>' +
  '<div class="table-wrap"><table class="mini-table"><thead><tr>' + srTh() + '<th>Issue Date *</th><th>Sample DC No.</th><th>Classification *</th><th>Item Name *</th><th>Inward No. *</th><th>PO No.</th><th class="num">Inward Stock</th><th>Qty *</th><th>UOM</th><th>POD (Proof of Delivery)</th><th></th></tr></thead>' +
  '<tbody id="jw-extras">' + ((r.extras && r.extras.length) ? jwLinesByDate(r.extras).map(function(x){return smpIssueRow(Object.assign({},x,{_saved:true}));}).join('') : smpIssueRow({ date: today() })) + '</tbody>' +
  '</table></div>' +
  '<button class="btn btn-ghost btn-sm" style="margin-top:10px" onclick="addRow(\'jw-extras\',smpIssueRow);afterRenderExtras()">＋ Add Issue Record</button>' +
  '';
}
function saveSample(id) {
  clearReqMarks();
  if (!cboxGet('jw-vendor')) return reqBad('jw-vendor', 'Job worker / vendor is required');
  if (!mcboxGet('smp-styles').length) return reqBad('smp-styles', 'Style (Internal ID) is required — pick at least one style');
  if (!requireRowFields('jw-garments', [
        { f:'__sizes', label:'Size Selection' }
      ], 'style row')) return false;
  const garmentRows = gatherGarmentRows();
  if (!garmentRows.length) return reqBad('smp-styles', 'Style (Internal ID) is required — pick at least one style');
  if (!garmentRows.some(g => g.orderQty > 0)) {
    const grow = qsa('#jw-garments tr')[0];
    const szEl = grow ? (qs('.sz-inputs input', grow) || qs('.mcbox-input', grow)) : null;
    return reqBad(szEl, 'Enter size-wise qty for at least one style');
  }
  const rec = id ? db.sample.find(r => r.id === id) : null;
  const receipts = rec ? (rec.receipts || []) : [];
  const garments = garmentRows.map(g => ({
    style: g.style, portal: g.portal, desc: g.style, colour: g.colour, sizes: g.sizes,
    orderQty: g.orderQty, rate: g.rate,
    receivedQty: receipts.filter(r => r.desc === g.style && r.colour === g.colour).reduce((a, r) => a + pf(r.qty), 0)
  }));
  /* Extra Quantity Issued — same rules as Job Work */
  const extrasRaw = gatherRows('jw-extras', ['item']);
  let xi = 0;
  qsa('#jw-extras tr').forEach(tr => {
    const itemVal = ((qs('[data-f="item"]', tr) || {}).value || '').trim();
    if (!itemVal) return;
    const row = extrasRaw[xi++]; if (!row) return;
    jwApplyGroupMeta('jw-extras', tr, row);
  });
  extrasRaw.forEach(function (row) { if (row.inwardNo) row.poRef = poRefFromInward(row.inwardNo); });
  const extras = extrasRaw.filter(i => pf(i.qty) > 0);
  /* Material Issues to Job Worker is mandatory on a Sample: at least one
     complete line, and every used line must carry date, classification,
     item, PO and a quantity greater than zero. */
  const xRows = qsa('#jw-extras tr');
  let xFilled = 0;
  for (let i = 0; i < xRows.length; i++) {
    const tr = xRows[i];
    if (rowIsBlank(tr, ['classification', 'item', 'inwardNo', 'qty'])) continue;
    xFilled++;
    const need = [
      { f:'date',           label:'Issue Date' },
      { f:'classification', label:'Classification' },
      { f:'item',           label:'Item Name' },
      { f:'inwardNo',       label:'Inward No.' }
    ];
    for (let k = 0; k < need.length; k++) {
      const v = (need[k].f === 'date') ? dval(qs('[data-f="date"]', tr)) : rowVal(tr, need[k].f);
      if (!v) return reqBadIn(tr, need[k].f, need[k].label + ' is required on material issue row ' + (i + 1));
    }
    if (pf(rowVal(tr, 'qty')) <= 0) {
      return reqBadIn(tr, 'qty', 'Qty must be more than zero on material issue row ' + (i + 1) +
        (rowVal(tr, 'item') ? ' — ' + bareItemName(rowVal(tr, 'item')) : ''));
    }
  }
  if (!xFilled) {
    const first = xRows[0];
    if (first) return reqBadIn(first, 'classification',
      'Material Issues to Job Worker is required — complete at least one issue line');
    return reqBadCell('jw-extras', 0, 'classification',
      'Material Issues to Job Worker is required — complete at least one issue line');
  }
  if (!jwValidateCombinedStock([], extras, id, 'sample')) return false;
  /* Generate the sample challan only after validation and Save. */
  jwAssignDcNosOnSave(extras, 'sample');
  const data = {
    date: dval('f-date') || today(), vendor: cboxGet('jw-vendor'),
    styles: (function () {
      const out = [];
      garmentRows.forEach(function (g) { if (g.style && out.indexOf(g.style) === -1) out.push(g.style); });
      mcboxGet('smp-styles').forEach(function (s) { if (s && out.indexOf(s) === -1) out.push(s); });
      return out;
    })(),
    issues: [], garments: garments, extras: extras,
    expectedDate: rec ? (rec.expectedDate || '') : '',
    receiveDate: rec ? (rec.receiveDate || '') : '',
    status: rec ? (rec.status || 'Issued') : 'Issued',
    notes: rec ? (rec.notes || '') : ''
  };
  data.status = jwAutoStatus(data);
  if (id) Object.assign(rec, data);
  else { data.id = uid(); data.no = nextNo('sample','SMP'); data.createdAt = Date.now(); data.receipts = []; db.sample.push(data); }
  const saved = saveDB();
  const savedNo = id ? (rec ? rec.no : '') : data.no;
  pageView = null; toast('Sample saved — ' + savedNo, 'green'); render();
  if (!saved) setTimeout(function () { toast('⚠️ Storage full — POD images may not be saved. Remove some images.', 'red'); }, 400);
}
function nextSampleDcNo() {
  let max = pf(db.seq.sampledc);
  (db.sample || []).forEach(function (r) { (r.extras || []).forEach(function (x) { const m=/^SAM-(\d+)$/.exec(String(x.dcNo||'')); if(m)max=Math.max(max,parseInt(m[1],10)||0); }); });
  db.seq.sampledc = max + 1;
  return 'SAM-' + String(db.seq.sampledc).padStart(4,'0');
}
function jwAssignDcNosOnSave(lines, kind) {
  const byDate = {};
  (lines || []).forEach(function (row) { if (row.date && row.dcNo && !byDate[row.date]) byDate[row.date] = row.dcNo; });
  (lines || []).forEach(function (row) {
    if (!row.date) return;
    if (!byDate[row.date]) byDate[row.date] = kind === 'sample' ? nextSampleDcNo() : nextNo('dc','DC');
    row.dcNo = byDate[row.date];
  });
}
function saveJobWork(id) {
  clearReqMarks();
  /* validate in the same order the fields appear on screen */
  if (!cboxGet('jw-vendor')) return reqBad('jw-vendor', 'Job worker / vendor is required');
  if (!cboxGet('jw-style')) return reqBad('jw-style', 'Style (Internal ID) is required');
  const issuesRaw = gatherRows('jw-issues', ['item']);
  // attach POD uploads + size consumption to their matching issue row (by row order, skipping empty-item rows)
  let ri = 0;
  qsa('#jw-issues tr').forEach(tr => {
    const itemVal = ((qs('[data-f="item"]', tr) || {}).value || '').trim();
    if (!itemVal) return;
    const row = issuesRaw[ri++]; if (!row) return;
    jwApplyGroupMeta('jw-issues', tr, row);
    const mcb = qs('.mcbox', tr);
    if (mcb && mcb.dataset.mcb) {
      const c = MCBOX[mcb.dataset.mcb];
      if (c) row.sizes = c.sel.slice();
    }
    row.requiredFabric = row.qty || 0;      // the size-wise requirement
    if (row.actualQty === '' || row.actualQty == null) row.actualQty = row.qty || 0;
    row.qty = pf(row.actualQty) || pf(row.qty);   // what actually left the store
    if (row.inwardNo) row.poRef = poRefFromInward(row.inwardNo);
  });
  const issues = issuesRaw.filter(i => pf(i.qty) > 0 || pf(i.actualQty) > 0);
  const dupKey = {};
  for (let k = 0; k < issues.length; k++) {
    const nm = lcName(bareItemName(issues[k].item));
    const ik = nm + '|' + lcName(issues[k].inwardNo);
    if (nm && issues[k].inwardNo && dupKey[ik]) {
      return reqBadCell('jw-issues', k, 'inwardNo', 'Duplicate — ' + bareItemName(issues[k].item) + ' on ' + issues[k].inwardNo + ' appears more than once under Material Issues.');
    }
    dupKey[ik] = 1;
  }
  /* 3. Material Issues — every used row needs these */
  // v5: skip the per-row validation when there are 0 rows AND we are editing (allow deleting all)
  if (qsa('#jw-issues tr').length > 0 && !requireRowFields('jw-issues', [
        { f:'classification', label:'Classification' },
        { f:'item',           label:'Item Name' },
        { f:'inwardNo',       label:'Inward No.' },
        { f:'__sizes',        label:'Size Selection' },
        { f:'cons',           label:'Cons./Gmt' }
      ], 'material issue row')) return false;
  // v5: only require at least one issue on NEW records (allow deleting all from existing records)
  if (!id && !issues.length) return reqBadCell('jw-issues', 0, 'item', 'Add at least one material issue (fabric / trim) with qty');
  const rec = id ? db.jobwork.find(r => r.id === id) : null;
  const receipts = rec ? (rec.receipts || []) : [];
  /* 2. Style is picked in the header; each resulting row needs its sizes */
  if (!requireRowFields('jw-garments', [
        { f:'__sizes', label:'Size Selection' }
      ], 'style row')) return false;
  const garmentRows = gatherGarmentRows();
  if (!garmentRows.length) return reqBad('jw-style', 'Style (Internal ID) is required');
  if (!garmentRows.some(g => g.orderQty > 0)) {
    const grow = qsa('#jw-garments tr')[0];
    const szEl = grow ? (qs('.sz-inputs input', grow) || qs('.mcbox-input', grow)) : null;
    return reqBad(szEl, 'Enter size-wise qty for at least one style');
  }
  const garments = garmentRows.map(g => ({
    style: g.style, portal: g.portal, desc: g.style, colour: g.colour, sizes: g.sizes,
    orderQty: g.orderQty, rate: g.rate,
    receivedQty: receipts.filter(r => r.desc === g.style && r.colour === g.colour).reduce((a, r) => a + pf(r.qty), 0)
  }));
  const extrasRaw = gatherRows('jw-extras', ['item']);
  let xi = 0;
  qsa('#jw-extras tr').forEach(tr => {
    const itemVal = ((qs('[data-f="item"]', tr) || {}).value || '').trim();
    if (!itemVal) return;
    const row = extrasRaw[xi++]; if (!row) return;
    jwApplyGroupMeta('jw-extras', tr, row);
  });
  extrasRaw.forEach(function (row) { if (row.inwardNo) row.poRef = poRefFromInward(row.inwardNo); });
  const extras = extrasRaw.filter(i => pf(i.qty) > 0);
  /* 4. Extra Quantity Issued — classification / item / Inward on every used row.
     Blank spare rows are ignored, so this table stays optional as a whole. */
  const xRows = qsa('#jw-extras tr');
  for (let i = 0; i < xRows.length; i++) {
    const tr = xRows[i];
    if (rowIsBlank(tr, ['classification', 'item', 'inwardNo', 'reason', 'qty'])) continue;
    const need = [
      { f:'classification', label:'Classification' },
      { f:'item',           label:'Item Name' },
      { f:'inwardNo',       label:'Inward No.' }
    ];
    for (let k = 0; k < need.length; k++) {
      if (rowVal(tr, need[k].f) === '') {
        return reqBadIn(tr, need[k].f, need[k].label + ' is required on extra issue row ' + (i + 1));
      }
    }
  }
  /* Extra Quantity Issued intentionally allows the same item on several lines
     (separate top-ups, each with its own reason) */
  for (let k = 0; k < extras.length; k++) {
    if (!String(extras[k].reason || '').trim()) {
      return reqBadCell('jw-extras', k, 'reason', 'Reason is required for every Extra Quantity Issued line — ' + (bareItemName(extras[k].item) || 'item ' + (k + 1)));
    }
  }
  if (!jwValidateCombinedStock(issues, extras, id, 'jobwork')) return false;
  /* Consume the next DC sequence only now, after every validation has passed. */
  jwAssignDcNosOnSave(issues);
  jwAssignDcNosOnSave(extras);
  const data = {
    date: dval('f-date') || today(), vendor: cboxGet('jw-vendor'),
    /* keep `styles` in step with the garment rows actually present, so a
       legacy multi-style job work never loses a row on re-save */
    styles: (function () {
      const out = [];
      garmentRows.forEach(function (g) { if (g.style && out.indexOf(g.style) === -1) out.push(g.style); });
      const cur = cboxGet('jw-style');
      if (cur && out.indexOf(cur) === -1) out.push(cur);
      return out;
    })(),
    issues, garments, extras,
    issueDate: (issues[0] && issues[0].date) || '',
    expectedDate: rec ? (rec.expectedDate || '') : '',
    receiveDate: rec ? (rec.receiveDate || '') : '',
    status: rec ? (rec.status || 'Issued') : 'Issued',
    notes: rec ? (rec.notes || '') : ''
  };
  data.status = jwAutoStatus(data);
  if (id) Object.assign(rec, data);
  else { data.id = uid(); data.no = nextNo('jobwork','JW'); data.createdAt = Date.now(); data.receipts = []; db.jobwork.push(data); }
  const saved = saveDB();
  const savedNo = id ? (rec ? rec.no : '') : data.no;
  pageView = null; toast('Job Work saved — ' + savedNo, 'green'); render();
  if (!saved) setTimeout(function () { toast('⚠️ Storage full — POD images may not be saved. Remove some images.', 'red'); }, 400);
}

/* =====================================================================
   JOB WORK DELIVERY CHALLAN — one printable document per DC number
   ===================================================================== */
function dcGroupsForJobWork(rec, mod) {
  const map = {};
  mod = mod || 'jobwork';
  function add(line, section) {
    line = line || {};
    if (!String(line.item || '').trim()) return;
    const qty = pf(line.actualQty != null && line.actualQty !== '' ? line.actualQty : line.qty);
    if (qty <= 0) return;
    const date = String(line.date || '').trim();
    const dc = String(line.dcNo || '').trim() || ('Unnumbered · ' + (dpToDisplay(date) || date || 'No date'));
    if (!map[dc]) map[dc] = { dcNo: dc, date: date, issues: [], extras: [], pod: false, podName: '' };
    const g = map[dc];
    if (!g.date && date) g.date = date;
    g[section].push(line);
    if (line.pod) { g.pod = true; if (!g.podName) g.podName = line.podName || 'POD uploaded'; }
  }
  (rec.issues || []).forEach(function (x) { add(x, 'issues'); });
  (rec.extras || []).forEach(function (x) { add(x, mod === 'sample' ? 'issues' : 'extras'); });
  return Object.keys(map).map(function (k) { return map[k]; }).sort(function (a, b) {
    /* Latest issue date / highest DC appears first on the print-selection page. */
    if (a.date !== b.date) return a.date > b.date ? -1 : 1;
    return b.dcNo.localeCompare(a.dcNo, undefined, { numeric:true, sensitivity:'base' });
  });
}
function openDCPrint(jobId, mod) {
  mod = mod || 'jobwork';
  const rec = (db[mod] || []).find(function (x) { return x.id === jobId; });
  if (!rec) return toast((mod === 'sample' ? 'Sample' : 'Job Work') + ' not found', 'red');
  const groups = dcGroupsForJobWork(rec, mod);
  if (!groups.length) return toast('No issued material is available to print', 'red');
  pageView = { kind:'dcprint', jobId:jobId, dcMod:mod };
  render();
}
function closeDCPrintPage() {
  const id = pageView && pageView.jobId;
  const mod = (pageView && pageView.dcMod) || 'jobwork';
  pageView = { kind:'form', m:mod, id:id };
  render();
}
function dcSelection() {
  return qsa('#dc-print-list input[data-dc-check]:checked').map(function (x) { return x.value; });
}
function dcToggleAll(on) {
  qsa('#dc-print-list input[data-dc-check]').forEach(function (x) { x.checked = !!on; });
  const n = $('dc-selected-count'); if (n) n.textContent = dcSelection().length + ' selected';
}
function dcSelectionChanged() {
  const all = qsa('#dc-print-list input[data-dc-check]');
  const sel = dcSelection();
  const master = $('dc-check-all');
  if (master) { master.checked = all.length > 0 && sel.length === all.length; master.indeterminate = sel.length > 0 && sel.length < all.length; }
  const n = $('dc-selected-count'); if (n) n.textContent = sel.length + ' selected';
}
function printSelectedDCs(jobId, mod) {
  const selected = dcSelection();
  if (!selected.length) return toast('Select at least one DC to print', 'red');
  printJobWorkDCs(jobId, selected, mod || 'jobwork');
}
function printAllDCs(jobId, mod) {
  mod = mod || 'jobwork';
  const rec = (db[mod] || []).find(function(x){return x.id===jobId;});
  if (!rec) return;
  printJobWorkDCs(jobId, dcGroupsForJobWork(rec, mod).map(function(g){return g.dcNo;}), mod);
}
function printSelectedGSTDCs(jobId) {
  const selected = dcSelection();
  if (!selected.length) return toast('Select at least one DC for GST / E-Way print', 'red');
  printJobWorkGSTDCs(jobId, selected);
}
function printAllGSTDCs(jobId) {
  const rec = (db.jobwork || []).find(function(x){return x.id===jobId;});
  if (!rec) return;
  printJobWorkGSTDCs(jobId, dcGroupsForJobWork(rec, 'jobwork').map(function(g){return g.dcNo;}));
}
function printJobWorkGSTDC(jobId, dcNo) { printJobWorkGSTDCs(jobId, [dcNo]); }
function renderDCPrintPage() {
  const jobId = pageView && pageView.jobId;
  const mod = (pageView && pageView.dcMod) || 'jobwork';
  const isSample = mod === 'sample';
  const rec = (db[mod] || []).find(function (x) { return x.id === jobId; });
  if (!rec) { pageView = null; render(); return; }
  const groups = dcGroupsForJobWork(rec, mod);
  $('tb-title').textContent = isSample ? 'Print Sample Delivery Challans' : 'Print Delivery Challans';
  $('tb-sub').textContent = (rec.no || (isSample ? 'Sample' : 'Job Work')) + ' · ' + (rec.vendor || '') + ' · one DC per print page';
  $('tb-actions').innerHTML =
    '<button class="btn btn-ghost" onclick="closeDCPrintPage()">' + ic('back',16) + (isSample ? 'Back to Sample' : 'Back to Job Work') + '</button>' +
    '<button class="btn btn-ghost" onclick="printAllDCs(\'' + rec.id + '\',\'' + mod + '\')">' + ic('print',16) + 'Print all DCs</button>' +
    (isSample ? '' : '<button class="btn btn-ghost" onclick="printAllGSTDCs(\'' + rec.id + '\')">' + ic('print',16) + 'GST print all</button>') +
    (isSample ? '' : '<button class="btn btn-ghost" onclick="printSelectedGSTDCs(\'' + rec.id + '\')">' + ic('print',16) + 'GST print selected</button>') +
    '<button class="btn btn-primary" onclick="printSelectedDCs(\'' + rec.id + '\',\'' + mod + '\')">' + ic('print',16) + 'Print selected</button>';

  const totalLines = groups.reduce(function (a, g) { return a + g.issues.length + g.extras.length; }, 0);
  const rows = groups.map(function (g) {
    const pod = g.pod
      ? '<span class="dc-pod-status ok">' + ic('check',13) + 'Uploaded</span>' + (g.podName ? '<div class="sub-line">' + esc(g.podName) + '</div>' : '')
      : '<span class="dc-pod-status miss">POD pending</span>';
    return '<tr>' +
      '<td class="selcol"><label class="cbx"><input type="checkbox" data-dc-check value="' + esc(g.dcNo) + '" onchange="dcSelectionChanged()"></label></td>' +
      '<td><b>' + esc(g.dcNo) + '</b></td>' +
      '<td>' + esc(dpToDisplay(g.date) || g.date || '—') + '</td>' +
      '<td class="num">' + g.issues.length + '</td>' +
      (isSample ? '' : '<td class="num">' + g.extras.length + '</td>') +
      '<td class="num"><b>' + (g.issues.length + g.extras.length) + '</b></td>' +
      '<td>' + pod + '</td>' +
      '<td class="actions-cell">' +
        '<button class="btn btn-ghost btn-sm" data-dc="' + esc(g.dcNo) + '" onclick="printJobWorkDC(\'' + rec.id + '\',this.dataset.dc,\'' + mod + '\')">' + ic('print',14) + 'Preview / Print</button>' +
        (isSample ? '' : '<button class="btn btn-ghost btn-sm" data-dc="' + esc(g.dcNo) + '" onclick="printJobWorkGSTDC(\'' + rec.id + '\',this.dataset.dc)">' + ic('print',14) + 'GST / E-Way Print</button>') +
      '</td></tr>';
  }).join('');
  $('content').innerHTML =
    '<div class="dc-select-summary">' +
      '<div class="stock-chip">' + (isSample ? 'Sample ' : 'Job Work ') + '<b>' + esc(rec.no || '') + '</b></div>' +
      '<div class="stock-chip">DCs <b>' + groups.length + '</b></div>' +
      '<div class="stock-chip">Material lines <b>' + totalLines + '</b></div>' +
      '<div class="stock-chip" id="dc-selected-count">0 selected</div>' +
    '</div>' +
    '<div class="panel"><div class="table-wrap"><table id="dc-print-list"><thead><tr>' +
      '<th class="selcol"><label class="cbx"><input id="dc-check-all" type="checkbox" onchange="dcToggleAll(this.checked)"></label></th>' +
      '<th>DC No.</th><th>Issue Date</th><th class="num">Material Issues</th>' + (isSample ? '' : '<th class="num">Extra Issues</th>') +
      '<th class="num">Total Lines</th><th>POD Status</th><th></th></tr></thead><tbody>' + rows +
    '</tbody></table></div></div>' +
    '<div class="hint">Each DC prints on a separate page. ' + (isSample ? 'Sample material issue lines sharing the same SAM number print together.' : 'Material Issues and Extra Quantity Issued are combined only when they carry the same DC number. GST / E-Way Print creates a separate valued delivery challan with HSN, GST breakup and transport fields for e-way bill support.') + ' Use the row button for a quick print, or select several DCs for one print job.</div>';
}
function printJobWorkDC(jobId, dcNo, mod) { printJobWorkDCs(jobId, [dcNo], mod || 'jobwork'); }
function printJobWorkDCs(jobId, dcNos, mod) {
  mod = mod || 'jobwork';
  const isSample = mod === 'sample';
  const rec = (db[mod] || []).find(function (x) { return x.id === jobId; });
  if (!rec) return toast((isSample ? 'Sample' : 'Job Work') + ' not found', 'red');
  const wanted = dcNos || [];
  const groups = dcGroupsForJobWork(rec, mod).filter(function (g) { return wanted.indexOf(g.dcNo) > -1; });
  if (!groups.length) return toast('No printable DC selected', 'red');
  const party = partyByName(rec.vendor);
  const vendorName = party ? (party.legalName || party.shortName) : rec.vendor;
  const vendorAddr = party ? [party.address, party.city, party.state, party.pin].filter(Boolean).join(', ') : '';
  const styles = jwStyleList(rec).map(function (s) { return styleLabel(s) || s; }).join(', ');

  function itemRows(g) {
    const all = [];
    g.issues.forEach(function (x) { all.push({ section:'Material Issue', x:x }); });
    g.extras.forEach(function (x) { all.push({ section:'Extra Issue', x:x }); });
    return all.map(function (a, i) {
      const x = a.x;
      const q = pf(x.actualQty != null && x.actualQty !== '' ? x.actualQty : x.qty);
      return '<tr><td class="ctr">' + (i + 1) + '</td><td>' + esc(a.section) + '</td>' +
        '<td>' + esc(x.classification || '—') + '</td><td><b>' + esc(bareItemName(x.item) || x.item || '') + '</b>' +
          (x.colour ? '<div class="sub">Colour: ' + esc(x.colour) + '</div>' : '') + '</td>' +
        '<td>' + esc(x.inwardNo || '—') + '</td><td class="rt">' + fmtNum(q) + '</td><td>' + esc(x.unit || '') + '</td>' +
        '<td>' + esc(a.section === 'Extra Issue' ? (x.reason || '—') : '—') + '</td></tr>';
    }).join('');
  }
  function totals(g) {
    const map = {};
    g.issues.concat(g.extras).forEach(function (x) {
      const u = x.unit || 'Qty';
      const q = pf(x.actualQty != null && x.actualQty !== '' ? x.actualQty : x.qty);
      map[u] = (map[u] || 0) + q;
    });
    return Object.keys(map).map(function (u) { return esc(u) + ': <b>' + fmtNum(map[u]) + '</b>'; }).join(' &nbsp; | &nbsp; ');
  }
  function podAckRows(g) {
    const all = [];
    g.issues.forEach(function (x) { all.push({ section:'Material', x:x }); });
    g.extras.forEach(function (x) { all.push({ section:'Extra', x:x }); });
    return all.map(function (a, i) {
      const x = a.x;
      const q = pf(x.actualQty != null && x.actualQty !== '' ? x.actualQty : x.qty);
      return '<tr><td class="ctr">' + (i + 1) + '</td><td>' + esc(a.section) + '</td>' +
        '<td>' + esc(bareItemName(x.item) || x.item || '') + '</td><td class="rt"><b>' + fmtNum(q) + '</b></td>' +
        '<td>' + esc(x.unit || '') + '</td><td class="write"></td><td class="write"></td><td class="write wide"></td></tr>';
    }).join('');
  }
  function sheet(g) {
    return '<section class="sheet">' +
      '<div class="copytag">ORIGINAL — SEND WITH MATERIAL · RETURN STAMPED ACKNOWLEDGEMENT</div>' +
      '<div class="hd"><h1>' + esc(PRINT_CO.name) + '</h1><div>' + esc(PRINT_CO.reg) + '</div><div>' + esc(PRINT_CO.reg2) + '</div>' +
        '<div>' + esc(PRINT_CO.works) + '</div><div>' + esc(PRINT_CO.contact) + '</div></div>' +
      '<div class="title">DELIVERY CHALLAN — ' + (isSample ? 'SAMPLE MATERIAL ISSUE' : 'JOB WORK MATERIAL ISSUE') + '</div>' +
      '<div class="meta"><div><table class="kv">' +
        '<tr><td class="k">DC No.</td><td class="c">:</td><td class="v bigv">' + esc(g.dcNo) + '</td></tr>' +
        '<tr><td class="k">Issue Date</td><td class="c">:</td><td class="v">' + esc(dpToDisplay(g.date) || g.date || '') + '</td></tr>' +
        '<tr><td class="k">' + (isSample ? 'Sample No.' : 'Job Work No.') + '</td><td class="c">:</td><td class="v">' + esc(rec.no || '') + '</td></tr>' +
        '<tr><td class="k">' + (isSample ? 'Sample Date' : 'Job Work Date') + '</td><td class="c">:</td><td class="v">' + esc(dpToDisplay(rec.date) || rec.date || '') + '</td></tr>' +
      '</table></div><div><table class="kv">' +
        '<tr><td class="k">Deliver To</td><td class="c">:</td><td class="v">' + esc(vendorName || '') + '</td></tr>' +
        '<tr><td class="k">Address</td><td class="c">:</td><td>' + esc(vendorAddr || '—') + '</td></tr>' +
        '<tr><td class="k">GST No.</td><td class="c">:</td><td class="v">' + esc((party && party.gstNo) || '—') + '</td></tr>' +
        '<tr><td class="k">Style(s)</td><td class="c">:</td><td class="v">' + esc(styles || '—') + '</td></tr>' +
      '</table></div></div>' +
      '<table class="items"><thead><tr><th class="ctr">Sr.</th><th>Issue Type</th><th>Classification</th><th>Item</th>' +
        '<th>Inward No.</th><th class="rt">Actual Issue Qty</th><th>UOM</th><th>Reason</th></tr></thead><tbody>' + itemRows(g) + '</tbody></table>' +
      '<div class="total">Dispatch totals: ' + totals(g) + '</div>' +
      '<div class="note"><b>Purpose:</b> Raw material issued for job-work processing only. Ownership of the material remains with ' + esc(PRINT_CO.name) + '.' +
        (rec.notes ? '<br><b>Instructions:</b> ' + esc(rec.notes) : '') + '</div>' +
      '<div class="dc-sign"><div><b>Prepared / Checked By</b><span></span>Name &amp; Signature</div>' +
        '<div><b>For ' + esc(PRINT_CO.name) + '</b><span></span>Authorized Signatory</div></div>' +

      '<div class="cut"><span>✂</span> DETACH / RETURN THIS STAMPED SECTION AS PROOF OF DELIVERY <span>✂</span></div>' +
      '<div class="podack">' +
        '<div class="podtitle"><div><b>PROOF OF DELIVERY (POD)</b><small>JOB WORKER MATERIAL RECEIPT ACKNOWLEDGEMENT</small></div>' +
          '<div class="podref">DC No. <b>' + esc(g.dcNo) + '</b><br>Date <b>' + esc(dpToDisplay(g.date) || g.date || '') + '</b></div></div>' +
        '<div class="podmeta"><b>From:</b> ' + esc(PRINT_CO.name) + ' &nbsp; | &nbsp; <b>Received at:</b> ' + esc(vendorName || '') +
          ' &nbsp; | &nbsp; <b>' + (isSample ? 'Sample' : 'Job Work') + ':</b> ' + esc(rec.no || '') + '</div>' +
        '<div class="ackstatement">I/We acknowledge receipt of the materials listed below. Quantities were physically checked at the time of delivery.</div>' +
        '<table class="ack"><thead><tr><th>Sr.</th><th>Type</th><th>Item</th><th class="rt">DC Qty</th><th>UOM</th>' +
          '<th>Qty Received</th><th>Short / Excess</th><th>Condition / Remarks</th></tr></thead><tbody>' + podAckRows(g) + '</tbody></table>' +
        '<div class="checks">Condition: &nbsp; □ Received in full and good condition &nbsp;&nbsp; □ Short / Excess &nbsp;&nbsp; □ Damaged &nbsp;&nbsp; □ Subject to detailed verification</div>' +
        '<div class="receiver"><div><b>Receiver Name &amp; Designation</b><span></span></div><div><b>Mobile No.</b><span></span></div>' +
          '<div><b>Actual Receipt Date &amp; Time</b><span></span></div><div class="stamp"><b>Job Worker Stamp &amp; Authorized Signature</b><span></span></div></div>' +
        '<div class="podnote">Job worker: stamp and sign this acknowledgement after confirming received quantities. Return the signed copy to the sender for POD upload.</div>' +
      '</div>' +
      '<div class="foot">SUBJECT TO ' + esc(PRINT_CO.jurisdiction.toUpperCase()) + ' JURISDICTION</div>' +
    '</section>';
  }
  const doc = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' + (isSample ? 'Sample ' : '') + 'Delivery Challan ' + esc(groups.map(function(g){return g.dcNo;}).join(', ')) + '</title><style>' +
    '*{box-sizing:border-box}body{font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;margin:0;padding:14px;background:#eee}' +
    '.bar{display:flex;gap:8px;justify-content:flex-end;max-width:1000px;margin:0 auto 10px;position:sticky;top:0}' +
    '.bar button{font:inherit;padding:7px 16px;border:1px solid #999;border-radius:4px;background:#fff;cursor:pointer}.bar .p{background:#303030;color:#fff;border-color:#303030}' +
    '.sheet{max-width:1000px;margin:0 auto 16px;background:#fff;border:1px solid #000;page-break-after:always}.sheet:last-child{page-break-after:auto}.copytag{text-align:center;padding:3px 8px;background:#111;color:#fff;font-size:9px;font-weight:bold;letter-spacing:.5px}' +
    '.hd{text-align:center;padding:8px 10px;border-bottom:1px solid #000}.hd h1{font-size:15px;margin:0 0 3px}.hd div{font-size:10px;line-height:1.4}' +
    '.title{text-align:center;font-size:13px;font-weight:bold;padding:5px;border-bottom:1px solid #000;letter-spacing:.8px}.bigv{font-size:13px}' +
    '.meta{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid #000}.meta>div{padding:8px 10px}.meta>div+div{border-left:1px solid #000}' +
    'table{border-collapse:collapse;width:100%}.kv td{padding:2px 3px;vertical-align:top}.kv .k{width:100px}.kv .c{width:10px}.kv .v{font-weight:bold}' +
    '.items th,.items td{border:1px solid #000;padding:5px 6px;vertical-align:top}.items th{background:#eee;font-size:10px;text-align:left}.items .ctr,.ctr{text-align:center}.items .rt,.rt{text-align:right}.sub{font-size:9px;margin-top:2px;color:#444}' +
    '.total{padding:5px 10px;text-align:right;border-bottom:1px solid #000}.note{padding:5px 10px;border-bottom:1px solid #000;line-height:1.35;font-size:9.5px}' +
    '.dc-sign{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid #000}.dc-sign>div{padding:6px 12px;text-align:center;font-size:9.5px}.dc-sign>div+div{border-left:1px solid #000}.dc-sign span{display:block;height:25px;border-bottom:1px solid #777;margin:0 18px 3px}' +
    '.cut{position:relative;text-align:center;padding:5px 8px;border-top:1px dashed #000;border-bottom:1px dashed #000;font-size:9px;font-weight:bold;letter-spacing:.35px}.cut span{margin:0 12px}' +
    '.podack{background:#fff}.podtitle{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;border-bottom:1px solid #000}.podtitle b{font-size:13px;letter-spacing:.6px}.podtitle small{display:block;font-size:8.5px;margin-top:2px}.podref{text-align:right;line-height:1.5;font-size:10px}.podmeta,.ackstatement,.checks,.podnote{padding:4px 8px;border-bottom:1px solid #000;font-size:9px;line-height:1.35}.ackstatement{font-weight:600}.ack th,.ack td{border:1px solid #000;padding:3px 4px;font-size:8.5px;vertical-align:middle}.ack th{background:#eee}.ack .write{min-width:65px;height:22px}.ack .wide{min-width:105px}.receiver{display:grid;grid-template-columns:1.2fr .8fr 1fr 1.5fr;border-bottom:1px solid #000}.receiver>div{min-height:62px;padding:5px 7px;text-align:center;font-size:8.5px}.receiver>div+div{border-left:1px solid #000}.receiver span{display:block;height:34px;border-bottom:1px solid #777;margin:0 5px}.receiver .stamp{font-weight:bold}.podnote{text-align:center;font-weight:600;background:#f7f7f7}' +
    '.foot{text-align:center;padding:5px;font-size:10px;font-weight:bold}' +
    '@page{size:A4 portrait;margin:8mm}@media print{body{padding:0;background:#fff}.bar{display:none}.sheet{border:1px solid #000;margin:0;max-width:none;page-break-after:always}.sheet:last-child{page-break-after:auto}.podack,.receiver,.ack tr{break-inside:avoid}}' +
    '

/* --- Next Style Block --- */

'+
    '*{box-sizing:border-box}body{font-family:Arial,Helvetica,sans-serif;font-size:10px;color:#000;margin:0;padding:12px;background:#eee}.bar{display:flex;gap:8px;justify-content:flex-end;max-width:1120px;margin:0 auto 10px;position:sticky;top:0}.bar button{font:inherit;padding:7px 16px;border:1px solid #999;border-radius:4px;background:#fff;cursor:pointer}.bar .p{background:#303030;color:#fff;border-color:#303030}.sheet{max-width:1120px;margin:0 auto 16px;background:#fff;border:1px solid #000;page-break-after:always}.sheet:last-child{page-break-after:auto}.copytag{text-align:center;background:#111;color:#fff;padding:4px;font-size:9px;font-weight:bold;letter-spacing:.5px}.hd{text-align:center;padding:7px;border-bottom:1px solid #000}.hd h1{font-size:15px;margin:0 0 2px}.hd div{font-size:9px;line-height:1.3}.title{text-align:center;font-size:13px;font-weight:bold;padding:6px;border-bottom:1px solid #000;letter-spacing:.5px}.docgrid,.parties{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid #000}.docgrid>div,.parties>div{padding:7px 9px}.docgrid>div+div,.parties>div+div{border-left:1px solid #000}.parties h3,.transport h3{font-size:10px;text-transform:uppercase;margin:0 0 4px}.parties p{margin:3px 0;min-height:22px}.kv{border-collapse:collapse;width:100%}.kv td{padding:1px 3px;vertical-align:top}.kv td:first-child{width:115px}.items{border-collapse:collapse;width:100%}.items th,.items td{border:1px solid #000;padding:4px;vertical-align:top}.items th{background:#eee;font-size:8.5px}.items small{display:block;font-size:7.5px;color:#333;margin-top:2px}.ctr{text-align:center}.rt{text-align:right}.value{display:flex;justify-content:flex-end;gap:22px;padding:6px 9px;border-bottom:1px solid #000;font-size:10px}.transport{display:grid;grid-template-columns:1.3fr 1fr 1.6fr .8fr 1.2fr;gap:8px;padding:8px;border-bottom:1px solid #000}.transport h3{grid-column:1/-1}.decl{padding:6px 9px;border-bottom:1px solid #000;font-size:9px;line-height:1.35}.sign{display:grid;grid-template-columns:1fr 1fr 1fr;border-bottom:1px solid #000}.sign>div{text-align:center;padding:6px}.sign>div+div{border-left:1px solid #000}.sign span{display:block;height:34px;border-bottom:1px solid #777;margin:0 10px 3px}.foot{text-align:center;padding:5px;font-weight:bold}@page{size:A4 landscape;margin:7mm}@media print{body{padding:0;background:#fff}.bar{display:none}.sheet{margin:0;border:1px solid #000;max-width:none;page-break-after:always}.sheet:last-child{page-break-after:auto}}

/* --- Next Style Block --- */

' +
'*{box-sizing:border-box}' +
'body{font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;margin:0;padding:14px;background:#fff}' +
'.bar{display:flex;gap:8px;justify-content:flex-end;max-width:1040px;margin:0 auto 10px}' +
'.bar button{font:inherit;padding:7px 16px;border:1px solid #999;border-radius:4px;background:#f3f3f3;cursor:pointer}' +
'.bar button.p{background:#303030;color:#fff;border-color:#303030}' +
'.sheet{max-width:1040px;margin:0 auto;border:1px solid #000}' +
'.hd{text-align:center;padding:8px 10px;border-bottom:1px solid #000}' +
'.hd h1{font-size:15px;margin:0 0 3px}' +
'.hd div{font-size:10px;line-height:1.45}' +
'.title{text-align:center;font-size:13px;font-weight:bold;padding:5px;border-bottom:1px solid #000;letter-spacing:1px}' +
'.meta{display:flex;border-bottom:1px solid #000}' +
'.meta .imgcol{width:200px;padding:10px;border-right:1px solid #000;text-align:center}' +
'.simg{max-width:180px;max-height:150px;object-fit:contain;border:1px solid #ccc}' +
'.noph{color:#888;padding:28px 8px;border:1px dashed #bbb}' +
'.meta .infocol{flex:1;padding:8px 12px}' +
'.kv{border-collapse:collapse;width:100%}' +
'.kv td{padding:2px 6px;vertical-align:top}' +
'.kv .k{width:140px;font-weight:bold;color:#333}' +
'table.items{border-collapse:collapse;width:100%}' +
'.items th,.items td{border:1px solid #000;padding:4px 6px}' +
'.items th{background:#eee;font-size:10px;text-align:left}' +
'.items .rt{text-align:right}.items .ctr{text-align:center}' +
'.sec{background:#eceef1;padding:5px 10px;font-weight:bold;border-top:1px solid #000;border-bottom:1px solid #000}' +
'.strong td{font-weight:bold;background:#f4f4f4}' +
'.mpos{color:#0c5132;font-weight:bold}.mneg{color:#8e1f0b;font-weight:bold}' +
'.foot{text-align:center;padding:6px;font-size:10px;border-top:1px solid #000}' +
'@media print{.bar{display:none}body{padding:0}.sheet{border:none;max-width:none}}' +
'