Subversion Repositories SmartDukaan

Rev

Rev 37775 | Blame | Compare with Previous | Last modification | View Log | RSS feed

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>RBM DRR Dashboard</title>
  <style>
    :root {
      --bg: #f4f6fb;
      --card: #ffffff;
      --ink: #141a2b;
      --muted: #5d6a83;
      --soft: #97a1b5;
      --line: #e6e9f1;
      --line2: #eef1f6;
      --brand: #3b5bdb;
      --brand2: #5c7cfa;
      --teal: #0ca678;
      --cyan: #1098ad;
      --amber: #f59f00;
      --orange: #e8590c;
      --red: #e03131;
      --violet: #7048e8;
      --pink: #d6336c;
      --good: #2f9e44;
      --warn: #e8590c;
      --bad: #e03131;
      --hid: #7048e8;
      --fast: #3b5bdb;
      --run: #0ca678;
      --slow: #e8590c;
      --other: #868e96;
      --hid-bg: #f2edff;
      --fast-bg: #eaf0ff;
      --run-bg: #e5f7ee;
      --slow-bg: #ffefe0;
      --other-bg: #eef0f4;
    }

    * {
      box-sizing: border-box
    }

    html, body {
      margin: 0;
      background: var(--bg);
      color: var(--ink);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      font-size: 14px
    }

    .wrap {
      max-width: none;
      margin: 0 auto;
      padding: 22px 20px 100px
    }

    a {
      color: var(--brand);
      text-decoration: none
    }

    a:hover {
      text-decoration: underline
    }

    /* header */
    header.top {
      display: flex;
      align-items: flex-end;
      justify-content: space-between;
      gap: 18px;
      flex-wrap: wrap;
      margin-bottom: 20px
    }

    .brand .crumb {
      font-size: 12px;
      color: var(--soft);
      font-weight: 600
    }

    .brand h1 {
      margin: 4px 0 0;
      font-size: 26px;
      font-weight: 770;
      letter-spacing: -.015em;
      display: flex;
      align-items: center;
      gap: 12px
    }

    .brand h1 .live {
      font-size: 11px;
      font-weight: 700;
      color: var(--teal);
      background: #e6fcf5;
      border: 1px solid #c3fae8;
      border-radius: 999px;
      padding: 3px 10px
    }

    .brand h1 .live i {
      display: inline-block;
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--teal);
      margin-right: 5px;
      animation: pulse 1.6s infinite;
      vertical-align: middle
    }

    @keyframes pulse {
      0%, 100% {
        opacity: 1
      }
      50% {
        opacity: .25
      }
    }

    .hd-right {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap
    }

    .ctl {
      background: var(--card);
      border: 1px solid var(--line);
      border-radius: 10px;
      padding: 9px 13px;
      font-size: 13px;
      color: var(--ink);
      font-weight: 600;
      box-shadow: 0 1px 2px rgba(20, 30, 60, .04)
    }

    input.ctl {
      appearance: none;
      cursor: pointer;
      height: 38px;
      min-width: 240px
    }

    .btn-r {
      background: var(--brand);
      border: 0;
      color: #fff;
      font-weight: 670;
      padding: 9px 18px;
      border-radius: 10px;
      cursor: pointer;
      font-size: 13px;
      height: 38px;
      box-shadow: 0 1px 2px rgba(59, 91, 219, .35);
      transition: transform .1s, box-shadow .1s
    }

    .btn-r:hover {
      background: var(--brand2);
      box-shadow: 0 3px 10px rgba(59, 91, 219, .35)
    }

    .btn-r:active {
      transform: translateY(1px)
    }

    /* KPI cards */
    .kpis {
      display: grid;
      grid-template-columns:repeat(4, 1fr);
      gap: 14px;
      margin-bottom: 22px
    }

    .kpi {
      position: relative;
      background: var(--card);
      border: 1px solid var(--line);
      border-radius: 14px;
      padding: 16px 18px;
      box-shadow: 0 1px 2px rgba(20, 30, 60, .05);
      display: flex;
      flex-direction: column;
      min-height: 118px;
      overflow: hidden
    }

    .kpi::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      height: 3px;
      width: 100%;
      border-top-left-radius: 14px;
      border-top-right-radius: 14px
    }

    .kpi.a::before {
      background: linear-gradient(90deg, var(--brand), var(--brand2))
    }

    .kpi.b::before {
      background: linear-gradient(90deg, var(--teal), #20c997)
    }

    .kpi.c::before {
      background: linear-gradient(90deg, var(--amber), #ffc078)
    }

    .kpi.d::before {
      background: linear-gradient(90deg, var(--violet), #9775fa)
    }

    .kpi .top {
      display: flex;
      align-items: center;
      gap: 10px
    }

    .kpi .ic {
      width: 34px;
      height: 34px;
      border-radius: 10px;
      display: flex;
      align-items: center;
      justify-content: center;
      flex: none;
      color: #fff;
      box-shadow: 0 2px 6px rgba(0, 0, 0, .08)
    }

    .kpi.a .ic {
      background: linear-gradient(135deg, var(--brand), var(--brand2))
    }

    .kpi.b .ic {
      background: linear-gradient(135deg, var(--teal), #20c997)
    }

    .kpi.c .ic {
      background: linear-gradient(135deg, var(--amber), #ffc078);
      color: #3a2500
    }

    .kpi.d .ic {
      background: linear-gradient(135deg, var(--violet), #9775fa)
    }

    .kpi .ic svg {
      width: 18px;
      height: 18px
    }

    .kpi .label {
      font-size: 12px;
      color: var(--muted);
      font-weight: 650;
      line-height: 1.2;
      letter-spacing: .005em
    }

    .kpi .val {
      font-size: 28px;
      font-weight: 780;
      margin-top: auto;
      padding-top: 10px;
      letter-spacing: -.02em;
      line-height: 1;
      font-variant-numeric: tabular-nums
    }

    .kpi .val .u {
      font-size: 13px;
      color: var(--soft);
      font-weight: 600;
      margin-left: 3px
    }

    .kpi .val .rdrr-kpi-billed {
      font-size: 20px;
      color: var(--soft);
      font-weight: 700;
      margin-left: 4px;
      letter-spacing: -.01em
    }

    .kpi .foot {
      font-size: 11.5px;
      color: var(--soft);
      margin-top: 8px;
      font-weight: 500
    }

    .kpi .val .pct {
      margin-left: 0;
      font-size: 20px;
      padding: 4px 12px
    }

    .kpi.clickable {
      cursor: pointer;
      transition: transform .1s, box-shadow .12s, border-color .12s
    }

    .kpi.clickable:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 18px rgba(20, 30, 60, .08);
      border-color: #c8d2f4
    }

    .kpi.clickable .hint {
      position: absolute;
      bottom: 8px;
      right: 12px;
      color: var(--soft);
      font-size: 11px;
      font-weight: 600;
      opacity: .7
    }

    .kpi.clickable:hover .hint {
      opacity: 1;
      color: var(--brand)
    }

    /* Cards & section labels */
    .card {
      background: var(--card);
      border: 1px solid var(--line);
      border-radius: 15px;
      padding: 18px 20px;
      margin-bottom: 16px;
      box-shadow: 0 1px 2px rgba(20, 30, 60, .04)
    }

    .card .card-hd {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 14px;
      margin-bottom: 14px
    }

    .card h2 {
      margin: 0;
      font-size: 16px;
      font-weight: 730;
      letter-spacing: -.01em
    }

    .card .desc {
      color: var(--muted);
      font-size: 12.5px;
      margin: 2px 0 0
    }

    .card .badge-period {
      background: #eef2ff;
      border: 1px solid #d7defb;
      color: var(--brand);
      border-radius: 999px;
      padding: 3px 12px;
      font-size: 12px;
      font-weight: 650;
      letter-spacing: .01em
    }

    .sectionlabel {
      font-size: 11px;
      letter-spacing: .14em;
      text-transform: uppercase;
      color: var(--soft);
      font-weight: 700;
      margin: 20px 2px 10px
    }

    /* Legend under section label */
    .legend {
      display: flex;
      gap: 14px;
      flex-wrap: wrap;
      margin: 0 2px 10px;
      align-items: center;
      color: var(--soft);
      font-size: 11.5px;
      font-weight: 600
    }

    .legend .lg {
      display: inline-flex;
      align-items: center;
      gap: 5px
    }

    .legend .lg i {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      display: inline-block
    }

    .legend .lg.hid i {
      background: var(--hid)
    }

    .legend .lg.fast i {
      background: var(--fast)
    }

    .legend .lg.run i {
      background: var(--run)
    }

    .legend .lg.slow i {
      background: var(--slow)
    }

    .legend .lg.other i {
      background: var(--other)
    }

    .legend .lg .lgt {
      color: var(--muted)
    }

    /* Table */
    table {
      width: 100%;
      border-collapse: separate;
      border-spacing: 0;
      font-size: 13px
    }

    th, td {
      text-align: left;
      padding: 11px 10px;
      border-bottom: 1px solid var(--line2)
    }

    thead th {
      font-size: 10.5px;
      text-transform: uppercase;
      letter-spacing: .06em;
      color: var(--soft);
      font-weight: 700;
      white-space: nowrap;
      user-select: none;
      position: sticky;
      top: 0;
      background: #fff;
      z-index: 1;
      border-bottom: 1px solid var(--line)
    }

    th.num, td.num {
      text-align: right
    }

    th.g-hid {
      color: var(--hid);
      border-top: 3px solid var(--hid)
    }

    th.g-fast {
      color: var(--fast);
      border-top: 3px solid var(--fast)
    }

    th.g-run {
      color: var(--run);
      border-top: 3px solid var(--run)
    }

    th.g-slow {
      color: var(--slow);
      border-top: 3px solid var(--slow)
    }

    th.g-other {
      color: var(--other);
      border-top: 3px solid var(--other)
    }

    th.g-total {
      color: var(--brand);
      border-top: 3px solid var(--brand)
    }

    tbody tr:hover {
      background: #f6f8fd
    }

    tbody tr.clickable {
      cursor: pointer;
      transition: background .1s
    }

    tbody tr.clickable:hover {
      background: #eef2ff
    }

    tr.totalrow {
      background: #fafbfe;
      border-top: 2px solid var(--line)
    }

    tr.totalrow td {
      font-weight: 770;
      color: var(--ink)
    }

    tr.totalrow .cell-val {
      font-size: 14px
    }

    .rbm-cell {
      display: flex;
      flex-direction: column;
      gap: 2px
    }

    .rbm-cell .rn {
      font-weight: 700;
      color: var(--ink);
      font-size: 13.5px
    }

    .rbm-cell .wh {
      font-size: 11.5px;
      color: var(--muted);
      font-weight: 600
    }

    /* Top-level "All RBMs" collapsed row */
    #rdrrGrid tr.top-parent {
      cursor: pointer;
      background: linear-gradient(180deg, #eef2fb, #dfe6f5);
      font-weight: 700
    }

    #rdrrGrid tr.top-parent:hover {
      background: linear-gradient(180deg, #dfe6f5, #cfd9ee)
    }

    #rdrrGrid tr.top-parent td {
      border-top: 2px solid var(--brand);
      border-bottom: 2px solid var(--brand)
    }

    #rdrrGrid tr.top-parent .top-toggle {
      display: inline-block;
      transition: transform .15s ease;
      font-weight: 800;
      color: var(--brand);
      margin-right: 4px;
      font-size: 15px
    }

    #rdrrGrid tr.top-parent .cell-val {
      font-weight: 800;
      color: var(--brand)
    }

    /* Multi-warehouse collapse — RBM-level parent row */
    #rdrrGrid tr.grp-parent {
      cursor: pointer;
      background: #fafbfe
    }

    #rdrrGrid tr.grp-parent:hover {
      background: #eef2fb
    }

    #rdrrGrid tr.grp-parent .grp-toggle {
      display: inline-block;
      transition: transform .15s ease;
      font-weight: 700;
      color: var(--brand);
      margin-right: 2px
    }

    #rdrrGrid tr.grp-child td {
      background: #fdfdff
    }

    .cell-val {
      font-variant-numeric: tabular-nums;
      font-weight: 650;
      color: var(--ink);
      white-space: nowrap
    }

    .cell-val small {
      color: var(--soft);
      font-weight: 500;
      margin-left: 3px
    }

    .pct {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      min-width: 56px;
      padding: 3px 9px;
      border-radius: 7px;
      font-size: 11.5px;
      font-weight: 700;
      text-align: center;
      font-variant-numeric: tabular-nums;
      line-height: 1.35
    }

    .pct.good {
      background: #e5f7ee;
      color: #177a3a
    }

    .pct.warn {
      background: #fff0e0;
      color: #a04600
    }

    .pct.bad {
      background: #ffeaea;
      color: #b8272b
    }

    .pct.na {
      background: transparent;
      color: var(--soft);
      font-weight: 600;
      min-width: auto;
      padding: 0
    }

    .bar {
      height: 5px;
      border-radius: 3px;
      background: #eef1f6;
      overflow: hidden;
      margin-top: 6px
    }

    .bar > i {
      display: block;
      height: 100%;
      border-radius: 3px;
      transition: width .3s ease
    }

    .bar > i.good {
      background: linear-gradient(90deg, #51cf66, #2f9e44)
    }

    .bar > i.warn {
      background: linear-gradient(90deg, #ffa94d, #e8590c)
    }

    .bar > i.bad {
      background: linear-gradient(90deg, #ff8787, #e03131)
    }

    .chev {
      color: #c5cce0;
      font-size: 16px;
      padding-left: 6px;
      transition: transform .12s, color .12s
    }

    tr.clickable:hover .chev {
      color: var(--brand);
      transform: translateX(2px)
    }

    .loading {
      padding: 70px;
      text-align: center;
      color: var(--muted)
    }

    .spinner {
      width: 32px;
      height: 32px;
      border: 3px solid #e3e8f5;
      border-top-color: var(--brand);
      border-radius: 50%;
      animation: spin 1s linear infinite;
      margin: 0 auto 12px
    }

    @keyframes spin {
      to {
        transform: rotate(360deg)
      }
    }

    .empty {
      padding: 56px 20px;
      text-align: center;
      color: var(--soft);
      font-size: 13.5px
    }

    .empty .ei {
      font-size: 36px;
      margin-bottom: 8px;
      opacity: .5
    }

    .foot-note {
      color: var(--soft);
      font-size: 11.5px;
      margin-top: 14px;
      padding: 10px 12px;
      background: #fafbfe;
      border: 1px dashed var(--line);
      border-radius: 10px;
      line-height: 1.5
    }

    .foot-note code {
      background: #eef1f6;
      color: var(--muted);
      padding: 1px 5px;
      border-radius: 4px;
      font-size: 11px
    }

    /* drill-down panel */
    .rdrr-back {
      position: fixed;
      inset: 0;
      background: rgba(15, 22, 40, .5);
      backdrop-filter: blur(2px);
      z-index: 1200;
      display: none
    }

    .rdrr-back.show {
      display: block
    }

    .rdrr-panel {
      position: absolute;
      top: 0;
      right: 0;
      height: 100%;
      width: min(760px, 96vw);
      background: #fff;
      box-shadow: -12px 0 40px rgba(20, 30, 60, .22);
      display: flex;
      flex-direction: column;
      padding: 24px 26px 20px;
      transform: translateX(100%);
      transition: transform .22s ease;
      overflow-y: auto
    }

    .rdrr-back.show .rdrr-panel {
      transform: translateX(0)
    }

    .rdrr-panel .x {
      position: absolute;
      top: 16px;
      right: 18px;
      cursor: pointer;
      color: var(--soft);
      font-size: 24px;
      line-height: 1;
      border: 0;
      background: none;
      width: 32px;
      height: 32px;
      border-radius: 8px;
      transition: background .1s, color .1s
    }

    .rdrr-panel .x:hover {
      background: #f1f3f9;
      color: var(--ink)
    }

    .rdrr-panel h3 {
      margin: 0 0 3px;
      font-size: 19px;
      font-weight: 750;
      letter-spacing: -.01em;
      padding-right: 40px
    }

    .rdrr-panel > .desc {
      margin: 0 0 18px;
      font-size: 12.5px;
      color: var(--muted);
      font-weight: 600
    }

    .rdrr-total {
      display: flex;
      gap: 14px;
      padding: 14px 16px;
      background: linear-gradient(135deg, #f8f9fc, #eef2ff);
      border: 1px solid #dbe2f6;
      border-radius: 12px;
      margin-bottom: 18px
    }

    .rdrr-total > div {
      flex: 1;
      text-align: center;
      border-right: 1px solid #dbe2f6;
      padding: 0 4px
    }

    .rdrr-total > div:last-child {
      border-right: 0
    }

    .rdrr-total .k {
      font-size: 10.5px;
      color: var(--muted);
      text-transform: uppercase;
      letter-spacing: .06em;
      font-weight: 700
    }

    .rdrr-total .v {
      font-size: 20px;
      font-weight: 770;
      color: var(--ink);
      font-variant-numeric: tabular-nums;
      margin-top: 5px;
      letter-spacing: -.01em
    }

    .rdrr-total .v .pct {
      font-size: 16px;
      padding: 3px 10px;
      margin-top: 0
    }

    .bbars {
      display: flex;
      flex-direction: column;
      gap: 10px;
      margin-bottom: 20px
    }

    .bbar {
      display: grid;
      grid-template-columns:82px 1fr 145px 60px;
      gap: 14px;
      align-items: center;
      padding: 12px 14px;
      background: #fff;
      border: 1px solid var(--line);
      border-radius: 11px;
      transition: box-shadow .12s
    }

    .bbar:hover {
      box-shadow: 0 3px 10px rgba(20, 30, 60, .06)
    }

    .bbar .bname {
      font-size: 11.5px;
      font-weight: 750;
      color: var(--ink);
      letter-spacing: .04em;
      text-transform: uppercase;
      display: flex;
      align-items: center;
      gap: 6px
    }

    .bbar .bname i {
      display: inline-block;
      width: 8px;
      height: 8px;
      border-radius: 50%
    }

    .bbar.hid .bname {
      color: var(--hid)
    }

    .bbar.fast .bname {
      color: var(--fast)
    }

    .bbar.run .bname {
      color: var(--run)
    }

    .bbar.slow .bname {
      color: var(--slow)
    }

    .bbar.other .bname {
      color: var(--other)
    }

    .bbar.hid .bname i {
      background: var(--hid)
    }

    .bbar.fast .bname i {
      background: var(--fast)
    }

    .bbar.run .bname i {
      background: var(--run)
    }

    .bbar.slow .bname i {
      background: var(--slow)
    }

    .bbar.other .bname i {
      background: var(--other)
    }

    .bbar .btrack {
      height: 14px;
      border-radius: 8px;
      overflow: hidden;
      position: relative
    }

    .bbar.hid .btrack {
      background: var(--hid-bg)
    }

    .bbar.fast .btrack {
      background: var(--fast-bg)
    }

    .bbar.run .btrack {
      background: var(--run-bg)
    }

    .bbar.slow .btrack {
      background: var(--slow-bg)
    }

    .bbar.other .btrack {
      background: var(--other-bg)
    }

    .bbar .btrack > i {
      display: block;
      height: 100%;
      border-radius: 8px;
      transition: width .3s ease;
      position: relative
    }

    .bbar.hid .btrack > i {
      background: linear-gradient(90deg, #9775fa, var(--hid))
    }

    .bbar.fast .btrack > i {
      background: linear-gradient(90deg, var(--brand2), var(--fast))
    }

    .bbar.run .btrack > i {
      background: linear-gradient(90deg, #20c997, var(--run))
    }

    .bbar.slow .btrack > i {
      background: linear-gradient(90deg, #ffa94d, var(--slow))
    }

    .bbar.other .btrack > i {
      background: linear-gradient(90deg, #adb5bd, var(--other))
    }

    .bbar .btgt {
      font-size: 12px;
      color: var(--muted);
      font-variant-numeric: tabular-nums;
      font-weight: 600;
      text-align: right
    }

    .bbar .btgt b {
      color: var(--ink);
      font-weight: 700
    }

    .bbar .bpct {
      font-size: 13px;
      font-weight: 750;
      text-align: right;
      font-variant-numeric: tabular-nums
    }

    .bbar .bpct.good {
      color: var(--good)
    }

    .bbar .bpct.warn {
      color: var(--warn)
    }

    .bbar .bpct.bad {
      color: var(--bad)
    }

    .bbar .bpct.na {
      color: var(--soft);
      font-weight: 600
    }

    /* warehouse flow (buying vs selling) in drill-down */
    .flow-tbl {
      width: 100%;
      border-collapse: separate;
      border-spacing: 0;
      font-size: 12.5px;
      margin-bottom: 20px;
      background: #fff;
      border: 1px solid var(--line);
      border-radius: 11px;
      overflow: hidden
    }

    .flow-tbl th, .flow-tbl td {
      padding: 10px 12px;
      border-bottom: 1px solid var(--line2);
      text-align: right;
      font-variant-numeric: tabular-nums
    }

    .flow-tbl th {
      font-size: 10.5px;
      text-transform: uppercase;
      letter-spacing: .05em;
      color: var(--soft);
      font-weight: 700;
      background: #fafbfe;
      text-align: left;
      padding-left: 14px
    }

    .flow-tbl th.num, .flow-tbl td.num {
      text-align: right
    }

    .flow-tbl thead th:first-child {
      text-align: left
    }

    .flow-tbl tbody tr td:first-child {
      text-align: left;
      padding-left: 14px;
      font-weight: 700;
      letter-spacing: .03em;
      text-transform: uppercase;
      font-size: 11px
    }

    .flow-tbl tbody tr td:first-child i {
      display: inline-block;
      width: 7px;
      height: 7px;
      border-radius: 50%;
      margin-right: 6px;
      vertical-align: middle
    }

    .flow-tbl tbody tr:last-child td {
      border-bottom: 0
    }

    .flow-tbl tbody tr.hid td:first-child {
      color: var(--hid)
    }

    .flow-tbl tbody tr.hid td:first-child i {
      background: var(--hid)
    }

    .flow-tbl tbody tr.fastmoving td:first-child {
      color: var(--fast)
    }

    .flow-tbl tbody tr.fastmoving td:first-child i {
      background: var(--fast)
    }

    .flow-tbl tbody tr.running td:first-child {
      color: var(--run)
    }

    .flow-tbl tbody tr.running td:first-child i {
      background: var(--run)
    }

    .flow-tbl tbody tr.slowmoving td:first-child {
      color: var(--slow)
    }

    .flow-tbl tbody tr.slowmoving td:first-child i {
      background: var(--slow)
    }

    .flow-tbl tbody tr.other td:first-child {
      color: var(--other)
    }

    .flow-tbl tbody tr.other td:first-child i {
      background: var(--other)
    }

    .flow-tbl .cov-cell {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      justify-content: flex-end
    }

    .flow-tbl .cov-cell .cov-bar {
      width: 60px;
      height: 6px;
      border-radius: 3px;
      background: #eef1f6;
      overflow: hidden;
      flex: none
    }

    .flow-tbl .cov-cell .cov-bar > i {
      display: block;
      height: 100%;
      border-radius: 3px
    }

    .flow-tbl .cov-cell.good .cov-bar > i {
      background: var(--good)
    }

    .flow-tbl .cov-cell.warn .cov-bar > i {
      background: var(--warn)
    }

    .flow-tbl .cov-cell.bad .cov-bar > i {
      background: var(--bad)
    }

    .flow-tbl .cov-cell.good .cov-pct {
      color: var(--good);
      font-weight: 700
    }

    .flow-tbl .cov-cell.warn .cov-pct {
      color: var(--warn);
      font-weight: 700
    }

    .flow-tbl .cov-cell.bad .cov-pct {
      color: var(--bad);
      font-weight: 700
    }

    .flow-tbl .cov-cell.na {
      color: var(--soft);
      font-weight: 500
    }

    .flow-tbl tr.tot td {
      font-weight: 770;
      background: #fafbfe;
      color: var(--ink);
      border-top: 2px solid var(--line)
    }

    /* brand mix bars in drill-down */
    .brands {
      display: flex;
      flex-direction: column;
      gap: 8px;
      margin-bottom: 20px
    }

    .brand-card {
      background: #fff;
      border: 1px solid var(--line);
      border-radius: 11px;
      overflow: hidden;
      transition: box-shadow .12s
    }

    .brand-card:hover {
      box-shadow: 0 3px 10px rgba(20, 30, 60, .06)
    }

    .brand-card.open {
      box-shadow: 0 4px 14px rgba(59, 91, 219, .14);
      border-color: #c8d2f4
    }

    .brand-head {
      display: grid;
      grid-template-columns:24px 130px 1fr 96px 100px 60px;
      gap: 12px;
      align-items: center;
      padding: 10px 14px;
      cursor: pointer;
      user-select: none
    }

    .brand-head .bpc {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 4px;
      font-size: 11.5px;
      font-weight: 700;
      color: var(--brand);
      background: #eef2ff;
      border: 1px solid #d7defb;
      border-radius: 999px;
      padding: 3px 9px;
      font-variant-numeric: tabular-nums;
      white-space: nowrap;
      justify-self: center
    }

    .brand-head .bpc.zero {
      color: var(--soft);
      background: #f1f3f9;
      border-color: var(--line)
    }

    .brand-head .bpc svg {
      width: 11px;
      height: 11px
    }

    .brand-head .cx {
      color: #c5cce0;
      font-size: 14px;
      transition: transform .18s ease, color .12s
    }

    .brand-card.open .brand-head .cx {
      transform: rotate(90deg);
      color: var(--brand)
    }

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

    .brand-head .btrack {
      height: 10px;
      background: #eef1f6;
      border-radius: 6px;
      overflow: hidden
    }

    .brand-head .btrack > i {
      display: block;
      height: 100%;
      border-radius: 6px;
      background: linear-gradient(90deg, var(--brand2), var(--brand));
      transition: width .3s ease
    }

    .brand-head .bval {
      font-size: 13px;
      font-weight: 700;
      color: var(--ink);
      font-variant-numeric: tabular-nums;
      text-align: right
    }

    .brand-head .bshare {
      font-size: 12px;
      font-weight: 700;
      color: var(--muted);
      font-variant-numeric: tabular-nums;
      text-align: right
    }

    .brand-card.top .brand-head .btrack > i {
      background: linear-gradient(90deg, #38b2ac, var(--teal))
    }

    .brand-card.top .brand-head .bshare {
      color: var(--teal)
    }

    .brand-body {
      display: none;
      padding: 6px 14px 14px;
      border-top: 1px dashed var(--line);
      background: #fafbfe
    }

    .brand-card.open .brand-body {
      display: block
    }

    .brand-body .bhdr {
      font-size: 10.5px;
      letter-spacing: .06em;
      text-transform: uppercase;
      color: var(--soft);
      font-weight: 700;
      margin: 8px 0 8px
    }

    .mbars {
      display: flex;
      flex-direction: column;
      gap: 6px
    }

    .mbar {
      display: grid;
      grid-template-columns:90px 1fr 100px 60px;
      gap: 10px;
      align-items: center;
      padding: 6px 10px;
      background: #fff;
      border: 1px solid var(--line2);
      border-radius: 8px
    }

    .mbar .mname {
      font-size: 11px;
      font-weight: 700;
      letter-spacing: .04em;
      text-transform: uppercase;
      display: flex;
      align-items: center;
      gap: 5px
    }

    .mbar .mname i {
      display: inline-block;
      width: 7px;
      height: 7px;
      border-radius: 50%
    }

    .mbar.hid .mname {
      color: var(--hid)
    }

    .mbar.hid .mname i {
      background: var(--hid)
    }

    .mbar.fastmoving .mname {
      color: var(--fast)
    }

    .mbar.fastmoving .mname i {
      background: var(--fast)
    }

    .mbar.running .mname {
      color: var(--run)
    }

    .mbar.running .mname i {
      background: var(--run)
    }

    .mbar.slowmoving .mname {
      color: var(--slow)
    }

    .mbar.slowmoving .mname i {
      background: var(--slow)
    }

    .mbar.other .mname {
      color: var(--other)
    }

    .mbar.other .mname i {
      background: var(--other)
    }

    .mbar .mtrack {
      height: 10px;
      border-radius: 5px;
      overflow: hidden
    }

    .mbar.hid .mtrack {
      background: var(--hid-bg)
    }

    .mbar.fastmoving .mtrack {
      background: var(--fast-bg)
    }

    .mbar.running .mtrack {
      background: var(--run-bg)
    }

    .mbar.slowmoving .mtrack {
      background: var(--slow-bg)
    }

    .mbar.other .mtrack {
      background: var(--other-bg)
    }

    .mbar .mtrack > i {
      display: block;
      height: 100%;
      border-radius: 5px;
      transition: width .3s ease
    }

    .mbar.hid .mtrack > i {
      background: linear-gradient(90deg, #9775fa, var(--hid))
    }

    .mbar.fastmoving .mtrack > i {
      background: linear-gradient(90deg, var(--brand2), var(--fast))
    }

    .mbar.running .mtrack > i {
      background: linear-gradient(90deg, #20c997, var(--run))
    }

    .mbar.slowmoving .mtrack > i {
      background: linear-gradient(90deg, #ffa94d, var(--slow))
    }

    .mbar.other .mtrack > i {
      background: linear-gradient(90deg, #adb5bd, var(--other))
    }

    .mbar .mval {
      font-size: 12px;
      font-weight: 650;
      color: var(--ink);
      font-variant-numeric: tabular-nums;
      text-align: right
    }

    .mbar .mshare {
      font-size: 12px;
      font-weight: 700;
      font-variant-numeric: tabular-nums;
      text-align: right;
      color: var(--muted)
    }

    /* status list modal (achieved vs pending) */
    .status-back {
      position: fixed;
      inset: 0;
      background: rgba(15, 22, 40, .5);
      backdrop-filter: blur(2px);
      z-index: 1300;
      display: none;
      align-items: flex-start;
      justify-content: center;
      padding: 60px 20px;
      overflow-y: auto
    }

    .status-back.show {
      display: flex
    }

    .status-modal {
      background: #fff;
      border-radius: 16px;
      max-width: 1000px;
      width: 100%;
      box-shadow: 0 20px 60px rgba(20, 30, 60, .28);
      padding: 22px 24px 20px;
      position: relative
    }

    .status-modal .x {
      position: absolute;
      top: 14px;
      right: 16px;
      cursor: pointer;
      color: var(--soft);
      font-size: 24px;
      line-height: 1;
      border: 0;
      background: none;
      width: 32px;
      height: 32px;
      border-radius: 8px;
      transition: background .1s
    }

    .status-modal .x:hover {
      background: #f1f3f9;
      color: var(--ink)
    }

    .status-modal h3 {
      margin: 0 0 3px;
      font-size: 19px;
      font-weight: 760;
      letter-spacing: -.01em;
      padding-right: 40px
    }

    .status-modal > .desc {
      margin: 0 0 18px;
      font-size: 12.5px;
      color: var(--muted);
      font-weight: 600
    }

    .status-grid {
      display: grid;
      grid-template-columns:1fr 1fr;
      gap: 18px
    }

    .status-col {
      border: 1px solid var(--line);
      border-radius: 12px;
      padding: 14px 16px;
      background: #fbfcfe
    }

    .status-col.good {
      border-color: #c3e8d0;
      background: #f4fbf6
    }

    .status-col.bad {
      border-color: #f2c4c4;
      background: #fdf5f5
    }

    .status-col h4 {
      margin: 0 0 10px;
      font-size: 13px;
      font-weight: 730;
      display: flex;
      align-items: center;
      gap: 8px;
      letter-spacing: -.005em
    }

    .status-col h4 .cnt {
      background: #fff;
      border: 1px solid var(--line);
      color: var(--ink);
      font-size: 11px;
      font-weight: 700;
      padding: 2px 9px;
      border-radius: 999px
    }

    .status-col.good h4 {
      color: #177a3a
    }

    .status-col.good h4 .cnt {
      background: #e5f7ee;
      border-color: #c3e8d0;
      color: #177a3a
    }

    .status-col.bad h4 {
      color: #b8272b
    }

    .status-col.bad h4 .cnt {
      background: #ffeaea;
      border-color: #f2c4c4;
      color: #b8272b
    }

    .status-list {
      display: flex;
      flex-direction: column;
      gap: 6px;
      max-height: 60vh;
      overflow-y: auto;
      padding-right: 4px
    }

    .status-item {
      display: grid;
      grid-template-columns:1fr 100px 68px;
      gap: 10px;
      align-items: center;
      padding: 9px 12px;
      background: #fff;
      border: 1px solid var(--line2);
      border-radius: 9px;
      transition: box-shadow .12s
    }

    .status-item:hover {
      box-shadow: 0 2px 8px rgba(20, 30, 60, .06)
    }

    .status-item .rn {
      font-weight: 700;
      color: var(--ink);
      font-size: 12.5px;
      line-height: 1.3
    }

    .status-item .wh {
      font-size: 11px;
      color: var(--muted);
      font-weight: 600;
      display: block;
      margin-top: 2px
    }

    .status-item .vv {
      font-size: 12px;
      color: var(--muted);
      font-variant-numeric: tabular-nums;
      font-weight: 600;
      text-align: right;
      white-space: nowrap
    }

    .status-item .vv b {
      color: var(--ink);
      font-weight: 700
    }

    .status-item .pp {
      font-size: 12px;
      font-weight: 700;
      font-variant-numeric: tabular-nums;
      text-align: right
    }

    .status-item .pp.good {
      color: var(--good)
    }

    .status-item .pp.warn {
      color: var(--warn)
    }

    .status-item .pp.bad {
      color: var(--bad)
    }

    .status-empty {
      padding: 24px;
      text-align: center;
      color: var(--soft);
      font-size: 12.5px
    }

    /* extras: aging + partner-billing tables */
    .xtable {
      width: 100%;
      border-collapse: separate;
      border-spacing: 0;
      font-size: 13px
    }

    .xtable th {
      font-size: 10.5px;
      text-transform: uppercase;
      letter-spacing: .06em;
      color: var(--soft);
      font-weight: 700;
      white-space: nowrap;
      padding: 10px 10px;
      border-bottom: 1px solid var(--line);
      text-align: left;
      background: #fff
    }

    .xtable th.num {
      text-align: right
    }

    .xtable td {
      padding: 10px;
      border-bottom: 1px solid var(--line2);
      color: var(--ink);
      font-weight: 500
    }

    .xtable td.num {
      text-align: right;
      font-variant-numeric: tabular-nums;
      font-weight: 650
    }

    .xtable tbody tr:hover {
      background: #f6f8fd
    }

    .xtable .rbm-nm {
      font-weight: 700;
      color: var(--ink)
    }

    .xtable tr.tot td {
      font-weight: 770;
      background: #fafbfe;
      border-top: 2px solid var(--line);
      color: var(--ink)
    }

    .xtable tr.bal td {
      font-weight: 700;
      background: #fffbf2;
      border-top: 1px dashed #f0d68a;
      color: #7a5a00
    }

    .xtable tr.bal td.hdr {
      font-weight: 770
    }

    .xtable td.g-hid {
      border-left: 2px solid transparent
    }

    /* placeholder for future column tint */
    .xtable th.g-hid, .xtable td.g-hid {
      color: var(--hid)
    }

    .xtable th.g-fast, .xtable td.g-fast {
      color: var(--fast)
    }

    .xtable th.g-run, .xtable td.g-run {
      color: var(--run)
    }

    .xtable th.g-slow, .xtable td.g-slow {
      color: var(--slow)
    }

    .xtable th.g-other, .xtable td.g-other {
      color: var(--other)
    }

    .xtable th.g-total, .xtable td.g-total {
      color: var(--brand);
      font-weight: 700
    }

    .xtable td.g-hid, .xtable td.g-fast, .xtable td.g-run, .xtable td.g-slow, .xtable td.g-other {
      opacity: .95
    }

    .xtable td.zero {
      color: var(--soft);
      font-weight: 500
    }

    .xtable .cnt-pill {
      display: inline-block;
      min-width: 32px;
      padding: 2px 8px;
      border-radius: 6px;
      font-size: 11.5px;
      font-weight: 700;
      font-variant-numeric: tabular-nums;
      background: #eef2ff;
      color: var(--brand)
    }

    .xtable .cnt-pill.zero {
      background: #f1f3f9;
      color: var(--soft)
    }

    .xtable .cnt-pill.warn {
      background: #fff0e0;
      color: #a04600
    }

    .xtable .cnt-pill.good {
      background: #e5f7ee;
      color: #177a3a
    }

    /* call-target drawer content (Called Partners / OOS Calls) */
    #rdrrCallDrawerBody {
      overflow-x: auto
    }

    #rdrrCallDrawerBody table {
      width: 100%;
      border-collapse: separate;
      border-spacing: 0;
      font-size: 12.5px;
      background: #fff;
      margin: 0
    }

    #rdrrCallDrawerBody table th {
      font-size: 10.5px;
      text-transform: uppercase;
      letter-spacing: .04em;
      color: var(--soft);
      font-weight: 700;
      padding: 9px 10px;
      background: #fafbfe;
      border-bottom: 1px solid var(--line);
      text-align: left;
      white-space: nowrap
    }

    #rdrrCallDrawerBody table td {
      padding: 9px 10px;
      border-bottom: 1px solid var(--line2);
      color: var(--ink);
      font-weight: 500;
      vertical-align: middle
    }

    #rdrrCallDrawerBody table tr:hover {
      background: #f6f8fd
    }

    #rdrrCallDrawerBody audio {
      max-width: 170px;
      height: 26px
    }

    /* rbm performance dashboard (extracted from today_po_rbm) */
    #rdrrPerfWrap {
      overflow-x: auto
    }

    #rdrrPerfWrap table {
      width: 100%;
      border-collapse: separate;
      border-spacing: 0;
      font-size: 12.5px;
      background: #fff
    }

    #rdrrPerfWrap table th, #rdrrPerfWrap table td {
      padding: 9px 10px;
      text-align: center;
      vertical-align: middle;
      border-bottom: 1px solid var(--line2);
      color: var(--ink);
      font-weight: 500
    }

    #rdrrPerfWrap table thead th {
      font-size: 10.5px;
      text-transform: uppercase;
      letter-spacing: .05em;
      font-weight: 700;
      background: #1d2433;
      color: #fff;
      border-bottom: 0;
      padding: 10px 8px;
      line-height: 1.2
    }

    #rdrrPerfWrap table thead th small {
      font-size: 9.5px;
      color: rgba(255, 255, 255, .7);
      font-weight: 600;
      display: block;
      margin-top: 2px
    }

    /* per-column accent badges (keep original palette from today_po_rbm) */
    #rdrrPerfWrap table thead th:nth-child(4) {
      background: linear-gradient(180deg, #40c057, #2f9e44)
    }

    #rdrrPerfWrap table thead th:nth-child(5) {
      background: linear-gradient(180deg, #22b8cf, #0e959f)
    }

    #rdrrPerfWrap table thead th:nth-child(6) {
      background: linear-gradient(180deg, #ffa94d, #e8590c)
    }

    #rdrrPerfWrap table thead th:nth-child(7) {
      background: linear-gradient(180deg, #9775fa, #7048e8)
    }

    #rdrrPerfWrap table thead th:nth-child(8) {
      background: linear-gradient(180deg, #ffd43b, #e8ac00);
      color: #3a2500
    }

    #rdrrPerfWrap table thead th:nth-child(8) small {
      color: rgba(58, 37, 0, .75)
    }

    #rdrrPerfWrap table thead th:nth-child(9) {
      background: linear-gradient(180deg, #ff922b, #e8590c)
    }

    #rdrrPerfWrap table thead th:last-child {
      background: linear-gradient(180deg, #ff6b6b, #e03131)
    }

    #rdrrPerfWrap table tbody tr:hover {
      background: #f6f8fd
    }

    /* rank rows keep their original background tint from today_po_rbm inline style */
    #rdrrPerfWrap table tbody tr td:nth-child(2) {
      text-align: left;
      font-weight: 700;
      color: var(--ink)
    }

    #rdrrPerfWrap table tbody tr td:first-child img {
      width: 24px;
      height: 24px
    }

    #rdrrPerfWrap table tbody tr:first-child td {
      font-weight: 700
    }

    #rdrrPerfWrap table tbody tr td:last-child {
      font-weight: 770;
      font-size: 13.5px;
      font-variant-numeric: tabular-nums
    }

    /* purchase-milestones (brand × person) — compact heatmap grid, custom-rendered client-side */
    #rdrrPmWrap {
      overflow: hidden
    }

    #rdrrPmWrap .pm-grid-wrap {
      overflow: auto;
      max-height: 720px;
      border: 1px solid var(--line);
      border-radius: 10px;
      background: #fff
    }

    #rdrrPmWrap .pm-grid {
      border-collapse: separate;
      border-spacing: 0;
      font-size: 13px;
      background: #fff
    }

    /* --- header (brand strip) --- */
    #rdrrPmWrap .pm-grid thead th {
      position: sticky;
      top: 0;
      z-index: 3;
      padding: 10px 14px;
      color: #fff;
      font-weight: 700;
      letter-spacing: .05em;
      font-size: 11px;
      text-transform: uppercase;
      text-align: center;
      white-space: nowrap;
      background: linear-gradient(180deg, var(--brand2), var(--brand));
      border-bottom: 2px solid rgba(0, 0, 0, .08)
    }

    #rdrrPmWrap .pm-grid thead th.pm-corner {
      position: sticky;
      left: 0;
      z-index: 5;
      background: #fff;
      color: var(--ink);
      text-align: left;
      border-right: 1px solid var(--line);
      padding: 10px 14px
    }

    #rdrrPmWrap .pm-corner-lbl {
      font-size: 11.5px;
      font-weight: 700;
      letter-spacing: .05em;
      color: var(--ink)
    }

    #rdrrPmWrap .pm-corner-hint {
      font-size: 10px;
      color: var(--soft);
      font-weight: 500;
      letter-spacing: .02em;
      text-transform: none;
      margin-top: 1px
    }

    /* --- name column --- */
    #rdrrPmWrap .pm-name {
      position: sticky;
      left: 0;
      z-index: 2;
      background: #fff;
      padding: 7px 5px;
      border-right: 1px solid var(--line);
      vertical-align: middle
    }

    #rdrrPmWrap .pm-name-txt {
      font-weight: 650;
      font-size: 16px;
      color: var(--ink);
      line-height: 1.2;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis
    }

    #rdrrPmWrap .pm-name-sub {
      font-size: 9.5px;
      color: var(--soft);
      margin-top: 1px;
      letter-spacing: .04em;
      text-transform: uppercase;
      font-weight: 600
    }

    /* --- data cells --- */
    #rdrrPmWrap .pm-cell {
      padding: 5px 7px;
      text-align: left;
      vertical-align: middle;
      font-variant-numeric: tabular-nums;
      color: var(--ink)
    }

    #rdrrPmWrap .pm-cell-zero {
      color: #cbd0d9;
      font-size: 17px;
      font-weight: 500;
      text-align: center
    }

    /* Stacked cell content: partner count on top, PO in middle, Billed at bottom (primary) */
    #rdrrPmWrap .pm-partners {
      font-size: 12px;
      font-weight: 700;
      text-align: right;
      color: #000;
      line-height: 1.2;
      margin-bottom: 4px;
      white-space: nowrap
    }

    #rdrrPmWrap .pm-po {
      font-size: 14px;
      font-weight: 600;
      color: #000;
      line-height: 1.2;
      margin-bottom: 4px;
      white-space: nowrap
    }

    #rdrrPmWrap .pm-billed {
      font-weight: 800;
      font-size: 16px;
      line-height: 1.2;
      color: var(--ink);
      white-space: nowrap
    }

    #rdrrPmWrap .pm-tag {
      font-size: 12px;
      font-weight: 500;
      opacity: .6;
      margin-left: 2px;
      letter-spacing: .02em;
      color: #000;
    }

    /* --- row hover --- */
    #rdrrPmWrap .pm-grid tbody tr.pm-row + tr.pm-row td {
      border-top: 1px solid var(--line2)
    }

    #rdrrPmWrap .pm-grid tbody tr.pm-row:hover .pm-name {
      background: #eef2fb
    }

    /* --- totals row (pinned at top, distinct) --- */
    #rdrrPmWrap .pm-total-row .pm-name {
      background: linear-gradient(180deg, #f2f5fb, #eaeff9);
      border-right: 1px solid var(--line);
      border-bottom: 2px solid var(--brand)
    }

    #rdrrPmWrap .pm-total-row .pm-name-txt {
      color: var(--brand);
      font-weight: 800
    }

    #rdrrPmWrap .pm-total-row .pm-name-sub {
      color: var(--brand);
      opacity: .8
    }

    #rdrrPmWrap .pm-cell-total {
      background: linear-gradient(180deg, #f2f5fb, #eaeff9) !important;
      border-bottom: 2px solid var(--brand)
    }

    #rdrrPmWrap .pm-cell-total .pm-billed {
      color: var(--brand);
      font-weight: 800;
      font-size: 13px
    }

    /* --- brand accent header gradients (slug matches JS output) --- */
    #rdrrPmWrap .pm-grid thead th.total {
      background: linear-gradient(180deg, #3b5bdb, #28449e)
    }

    #rdrrPmWrap .pm-grid thead th.vivo {
      background: linear-gradient(180deg, #00baf2, #0a8fb5)
    }

    #rdrrPmWrap .pm-grid thead th.oppo {
      background: linear-gradient(180deg, #20a558, #177a3a)
    }

    #rdrrPmWrap .pm-grid thead th.samsung {
      background: linear-gradient(180deg, #0b3d91, #071e51)
    }

    #rdrrPmWrap .pm-grid thead th.alcatel {
      background: linear-gradient(180deg, #5c7cfa, #3b5bdb)
    }

    #rdrrPmWrap .pm-grid thead th.motorola {
      background: linear-gradient(180deg, #495057, #212529)
    }

    #rdrrPmWrap .pm-grid thead th.hmd {
      background: linear-gradient(180deg, #7048e8, #5b34c9)
    }

    #rdrrPmWrap .pm-grid thead th.xiaomi {
      background: linear-gradient(180deg, #ffa726, #e8590c)
    }

    #rdrrPmWrap .pm-grid thead th.lava {
      background: linear-gradient(180deg, #e03131, #a11515)
    }

    #rdrrPmWrap .pm-grid thead th.poco {
      background: linear-gradient(180deg, #ffd43b, #e8ac00);
      color: #3a2500
    }

    #rdrrPmWrap .pm-grid thead th.realme {
      background: linear-gradient(180deg, #f59f00, #e8590c)
    }

    #rdrrPmWrap .pm-grid thead th.apple {
      background: linear-gradient(180deg, #495057, #141a2b)
    }

    #rdrrPmWrap .pm-grid thead th.oneplus {
      background: linear-gradient(180deg, #e03131, #a11515)
    }

    #rdrrPmWrap .pm-grid thead th.tecno {
      background: linear-gradient(180deg, #0ca678, #087a56)
    }

    @media (max-width: 960px) {
      .kpis {
        grid-template-columns:repeat(2, 1fr)
      }

      .brand h1 {
        font-size: 22px
      }

      .status-grid {
        grid-template-columns:1fr
      }
    }
  </style>
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css"/>
</head>
<body>
<div class="wrap">
  <header class="top">
    <div class="brand">
      <div class="crumb"><a href="/indent/today_po_rbm">&larr; Today PO / RBM</a> &middot; Sales &middot; Indent</div>
      <h1>RBM DRR Dashboard <span class="live"><i></i>Live</span></h1>
    </div>
    <div class="hd-right">
      <select id="rdrrTypeSelect" class="ctl" style="min-width:120px;height:38px;padding:8px 12px" title="Filter type">
        <option value="RBM" selected>RBM</option>
        <option value="SALES">Sales</option>
      </select>
      <select id="rdrrLevelSelect" class="ctl" style="min-width:140px;height:38px;padding:8px 12px;display:none"
              title="Sales level">
        <option value="">Select level</option>
        <option value="ASM">ASM (L1)</option>
        <option value="CM">CM (L2)</option>
        <option value="DM">DM (L3)</option>
        <option value="BM">BM (L4)</option>
        <option value="RSM">RSM (L5)</option>
        <option value="NSM">NSM (L6)</option>
      </select>
      <select id="rdrrPersonSelect" class="ctl" style="min-width:230px;height:38px;padding:8px 12px" title="Person">
        <option value="">All RBMs</option>
      </select>
      <input class="ctl" id="rdrrPeriod" type="text" placeholder="Select date range"/>
      <button class="btn-r" id="rdrrApply">Apply</button>
    </div>
  </header>

  <div class="kpis">
    <div class="kpi a">
      <div class="top">
        <div class="ic">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"
               stroke-linejoin="round">
            <path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>
          </svg>
        </div>
        <div class="label">Total Achieved</div>
      </div>
      <div class="val" id="rdrrKpiAch">&mdash;</div>
      <div class="foot" id="rdrrKpiAchSub">PO value / Actually billed (invoiced)</div>
    </div>
    <div class="kpi b">
      <div class="top">
        <div class="ic">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"
               stroke-linejoin="round">
            <circle cx="12" cy="12" r="9"/>
            <circle cx="12" cy="12" r="5"/>
            <circle cx="12" cy="12" r="1.5" fill="currentColor"/>
          </svg>
        </div>
        <div class="label">Total Target</div>
      </div>
      <div class="val" id="rdrrKpiTgt">&mdash;</div>
      <div class="foot" id="rdrrKpiTgtSub">Sum of daily target snapshots</div>
    </div>
    <div class="kpi c">
      <div class="top">
        <div class="ic">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"
               stroke-linejoin="round">
            <path d="M3 17l6-6 4 4 8-8"/>
            <path d="M17 7h4v4"/>
          </svg>
        </div>
        <div class="label">Achievement %</div>
      </div>
      <div class="val" id="rdrrKpiPct">&mdash;</div>
      <div class="foot" id="rdrrKpiPctSub">Total ACH / Total TGT</div>
    </div>
    <div class="kpi d clickable" id="rdrrKpiTopCard" title="Click to see achieved vs pending RBMs">
      <div class="top">
        <div class="ic">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"
               stroke-linejoin="round">
            <path d="M12 2l3 6.5 7 1-5 5 1.5 7L12 18l-6.5 3.5L7 14.5 2 9.5l7-1L12 2z"/>
          </svg>
        </div>
        <div class="label">RBMs ≥ 100% Target</div>
      </div>
      <div class="val" id="rdrrKpiTop">&mdash;</div>
      <div class="foot" id="rdrrKpiTopSub">Rows meeting or beating target</div>
      <span class="hint">Click for list &rsaquo;</span>
    </div>
  </div>

  <div class="sectionlabel">RBM &times; Warehouse &times; Movement Bucket</div>
  <div class="legend">
    <span class="lg hid"><i></i><span class="lgt">HID</span></span>
    <span class="lg fast"><i></i><span class="lgt">FAST</span></span>
    <span class="lg run"><i></i><span class="lgt">RUN</span></span>
    <span class="lg slow"><i></i><span class="lgt">SLOW</span></span>
    <span class="lg other"><i></i><span class="lgt">OTHER</span></span>
  </div>
  <div class="card">
    <div class="card-hd">
      <div>
        <h2>Achievement vs Target</h2>
        <p class="desc">Click any row for a per-bucket breakdown and the catalogs sold.</p>
      </div>
      <span class="badge-period" id="rdrrPeriodLabel">&mdash;</span>
    </div>
    <div id="rdrrTableWrap">
      <div class="loading">
        <div class="spinner"></div>
        Loading&hellip;
      </div>
    </div>
    <div class="foot-note">
      <b>TGT</b> is summed from persisted per-day snapshots (<code>inventory.rbm_targets</code>) — days with no
      scheduler-written snapshot contribute 0.
      <b>ACH</b> is computed from source orders (<code>transaction.order</code>) for the range.
    </div>
  </div>

  <!-- >15 Days Aging Sold -->
  <div class="sectionlabel">&gt; 15 Days Aging Sold</div>
  <div class="card">
    <div class="card-hd">
      <div>
        <h2>Aging Inventory Cleared</h2>
        <p class="desc">Billed value of items that were &gt; 15 days old at sale, per RBM, for the picked range.</p>
      </div>
      <span class="badge-period" id="rdrrAgingPeriod">&mdash;</span>
    </div>
    <div id="rdrrAgingWrap">
      <div class="loading">
        <div class="spinner"></div>
        Loading&hellip;
      </div>
    </div>
  </div>

  <!-- Partner Billing Count — unchanged, always current month MTD -->
  <div class="sectionlabel">Partner Billing Count <span
          style="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(Targeted partners only &middot; bucketed by MTD ₹)</span>
  </div>
  <div class="card">
    <div class="card-hd">
      <div>
        <h2>Partners by MTD Bracket</h2>
        <p class="desc">Each targeted partner is counted in exactly one bracket by their current MTD billing.
          Zero-billed = partners who haven't crossed ₹1L this month.</p>
      </div>
      <span class="badge-period" id="rdrrBillingPeriod">&mdash;</span>
    </div>
    <div id="rdrrBillingWrap">
      <div class="loading">
        <div class="spinner"></div>
        Loading&hellip;
      </div>
    </div>
  </div>

  <!-- RBM Performance Dashboard — JSON-driven, supports historical months via /rbm-drr/performance?month=YYYY-MM -->
  <div class="sectionlabel">RBM Performance Dashboard</div>
  <div class="card">
    <div class="card-hd">
      <div>
        <h2>Composite MTD Ranking</h2>
        <p class="desc">Rank of RBMs by weighted score across Talk Time (10%) + MTD Achievement (55%) + Aging (2%) +
          &gt;1L Partners (29%) + Rating (2%) + Partners Rated (2%).
          <span style="color:var(--soft)"
                title="Aging denominator uses today's warehouse aging snapshot — for past months aging% is approximate.">Aging denominator is a live snapshot.</span>
        </p>
      </div>
      <div style="display:flex;align-items:center;gap:10px">
        <select id="rdrrMonthPicker" class="ctl" style="min-width:170px;height:30px;padding:4px 10px"
                title="Pick a month to view that month's ranking"></select>
        <span class="badge-period" id="rdrrPerfMonth">MTD</span>
      </div>
    </div>
    <div id="rdrrPerfWrap">
      <div class="loading">
        <div class="spinner"></div>
        Loading&hellip;
      </div>
    </div>
  </div>

  <!-- Today PO & Billed — Brand × RBM matrix (reuses /indent/today_po_table_fragment) -->
  <div class="sectionlabel">Today PO &amp; Billed — Brand &times; RBM</div>
  <div class="card">
    <div class="card-hd">
      <div>
        <h2>Purchase Milestones by Brand</h2>
        <p class="desc">Today's PO value / partner count / total billed, per RBM &times; brand. Always today
          (independent of the range picker above).</p>
      </div>
      <select id="rdrrPmCategory" class="ctl" style="min-width:170px;height:34px;padding:6px 12px">
        <option value="RBM">All RBMs</option>
        <option value="NSM">All NSMs</option>
        <option value="RSM">All RSMs</option>
        <option value="BM">All BMs</option>
        <option value="DM">All DMs</option>
        <option value="CM">All CMs</option>
        <option value="ASM">All ASMs</option>
      </select>
    </div>
    <div id="rdrrPmWrap">
      <div class="loading">
        <div class="spinner"></div>
        Loading&hellip;
      </div>
    </div>
  </div>

  <!-- RBM Call Target Summary — full interactive page injected via iframe srcdoc (no backend change) -->
  <div class="sectionlabel">RBM Call Target Summary</div>
  <div class="card">
    <div class="card-hd">
      <div>
        <h2>Today's Call Coverage per RBM</h2>
        <p class="desc">Full interactive view — all popups, drill-downs and downloads work exactly like the standalone
          page.</p>
      </div>
      <a class="badge-period" href="/indent/rbm_call_target" target="_blank" style="text-decoration:none">Open in new
        tab &rsaquo;</a>
    </div>
    <iframe id="rdrrCallFrame" title="RBM Call Target Summary"
            style="width:100%;min-height:800px;border:0;border-radius:10px;background:#fff;display:block"></iframe>
  </div>
</div>

<!-- achieved-vs-pending modal (opens from the RBMs ≥ 100% Target KPI card) -->
<div class="status-back" id="rdrrStatusBack">
  <div class="status-modal">
    <button class="x" id="rdrrStatusClose" aria-label="Close">&times;</button>
    <h3>Target Status by RBM</h3>
    <p class="desc" id="rdrrStatusDesc">&mdash;</p>
    <div class="status-grid">
      <div class="status-col good">
        <h4>✅ Achieved <span class="cnt" id="rdrrStatusAchCnt">0</span></h4>
        <div class="status-list" id="rdrrStatusAchList"></div>
      </div>
      <div class="status-col bad">
        <h4>⚠ Pending <span class="cnt" id="rdrrStatusPndCnt">0</span></h4>
        <div class="status-list" id="rdrrStatusPndList"></div>
      </div>
    </div>
  </div>
</div>

<!-- call-target drill-down drawer (Called Partners / OOS Calls) -->
<div class="rdrr-back" id="rdrrCallDrawerBack">
  <div class="rdrr-panel">
    <button class="x" id="rdrrCallDrawerClose" aria-label="Close">&times;</button>
    <h3 id="rdrrCallDrawerTitle">&mdash;</h3>
    <p class="desc" id="rdrrCallDrawerDesc">&mdash;</p>
    <div id="rdrrCallDrawerBody">
      <div class="loading">
        <div class="spinner"></div>
        Loading&hellip;
      </div>
    </div>
  </div>
</div>

<!-- drill-down panel -->
<div class="rdrr-back" id="rdrrBack">
  <div class="rdrr-panel">
    <button class="x" id="rdrrClose" aria-label="Close">&times;</button>
    <h3 id="rdrrDdTitle">&mdash;</h3>
    <p class="desc" id="rdrrDdDesc">&mdash;</p>

    <div class="rdrr-total">
      <div>
        <div class="k">Achieved</div>
        <div class="v" id="rdrrDdAch">&mdash;</div>
      </div>
      <div>
        <div class="k">Target</div>
        <div class="v" id="rdrrDdTgt">&mdash;</div>
      </div>
      <div>
        <div class="k">Achievement</div>
        <div class="v" id="rdrrDdPct">&mdash;</div>
      </div>
    </div>

    <div class="sectionlabel" style="margin-top:2px">Movement Split — Achievement vs Target</div>
    <div class="bbars" id="rdrrDdBars"></div>

    <div class="sectionlabel">Warehouse Flow — Buying vs Selling <span
            style="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(GRN into this warehouse + cross-warehouse direct billings)</span>
    </div>
    <div id="rdrrDdFlow">
      <div class="loading">
        <div class="spinner"></div>
        Loading&hellip;
      </div>
    </div>

    <div class="sectionlabel">Brand Mix — Share of Achievement <span
            style="color:var(--soft);font-weight:600;text-transform:none;letter-spacing:0;margin-left:6px">(click a brand for movement split)</span>
    </div>
    <div id="rdrrDdBrands">
      <div class="loading">
        <div class="spinner"></div>
        Loading brand mix&hellip;
      </div>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
<script>
  (function () {
    var DEFAULT_START = "$defaultStartDate";
    var DEFAULT_END = "$defaultEndDate";

    // Prefer query-string range if the caller provided one (e.g. from today_po_rbm's date picker).
    var url = new URL(window.location.href);
    var qsStart = url.searchParams.get("startDate");
    var qsEnd = url.searchParams.get("endDate");
    var startDate = qsStart || DEFAULT_START;
    var endDate = qsEnd || DEFAULT_END;

    function fmtDate(s) {
      return moment(s).format("DD MMM");
    }

    function fmtLakh(v) {
      v = Number(v) || 0;
      return (v / 100000).toFixed(1) + "L";
    }

    function pctClass(p) {
      if (p >= 95) return "good";
      if (p >= 41) return "warn";
      return "bad";
    }

    function pctLabel(ach, tgt) {
      if (!tgt || tgt <= 0) return {text: "-", cls: "na", num: null};
      var p = (ach / tgt) * 100;
      return {text: p.toFixed(1) + "%", cls: pctClass(p), num: p};
    }

    function pctBarWidth(ach, tgt) {
      if (!tgt || tgt <= 0) return 0;
      return Math.min(100, (ach / tgt) * 100);
    }

    // Initialise the range picker.
    $("#rdrrPeriod").daterangepicker({
      startDate: moment(startDate),
      endDate: moment(endDate),
      locale: {format: "YYYY-MM-DD", cancelLabel: "Clear"}
    });
    $("#rdrrPeriod").on("apply.daterangepicker", function (ev, picker) {
      startDate = picker.startDate.format("YYYY-MM-DD");
      endDate = picker.endDate.format("YYYY-MM-DD");
    });

    // Defensive: read the picker's current state at click time in case
    // the apply.daterangepicker event was swallowed for any reason.
    $("#rdrrApply").on("click", function () {
      var p = $("#rdrrPeriod").data("daterangepicker");
      if (p) {
        startDate = p.startDate.format("YYYY-MM-DD");
        endDate = p.endDate.format("YYYY-MM-DD");
      }
      load();
      // If a Sales role is currently active, re-fetch sales-data for the new range.
      if (CURRENT_SALES_ROLE) {
        $.ajax({
          url: "/rbm-drr/sales-data",
          type: "GET",
          data: {startDate: startDate, endDate: endDate},
          dataType: "json",
          success: function (payload) {
            SALES_PAYLOAD = payload;
            renderFiltered();
          },
          error: function () {
            SALES_PAYLOAD = {rows: []};
            renderFiltered();
          }
        });
      }
    });

    function load() {
      $("#rdrrTableWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
      $("#rdrrAgingWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
      $("#rdrrBillingWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
      $("#rdrrPeriodLabel").text(fmtDate(startDate) + " – " + fmtDate(endDate));
      $("#rdrrAgingPeriod").text(fmtDate(startDate) + " – " + fmtDate(endDate));
      fetch("/rbm-drr/data?startDate=" + startDate + "&endDate=" + endDate, {credentials: "same-origin"})
              .then(function (r) {
                return r.json().then(function (b) {
                  return {ok: r.ok, body: b};
                });
              })
              .then(function (res) {
                if (!res.ok) {
                  $("#rdrrTableWrap").html('<div class="empty">' + escapeHtml(res.body && res.body.error ? res.body.error : "Failed to load.") + '</div>');
                  return;
                }
                render(res.body);
              })
              .catch(function (e) {
                $("#rdrrTableWrap").html('<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>');
              });

      fetch("/rbm-drr/extras?startDate=" + startDate + "&endDate=" + endDate, {credentials: "same-origin"})
              .then(function (r) {
                return r.json().then(function (b) {
                  return {ok: r.ok, body: b};
                });
              })
              .then(function (res) {
                if (!res.ok) {
                  var msg = '<div class="empty">' + escapeHtml(res.body && res.body.error ? res.body.error : "Failed to load.") + '</div>';
                  $("#rdrrAgingWrap").html(msg);
                  $("#rdrrBillingWrap").html(msg);
                  return;
                }
                renderAging(res.body);
                renderPartnerBilling(res.body);
              })
              .catch(function (e) {
                var msg = '<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>';
                $("#rdrrAgingWrap").html(msg);
                $("#rdrrBillingWrap").html(msg);
              });
    }

    function renderAging(data) {
      var rows = data.aging || [];
      var totals = data.agingTotals || {};
      var balance = data.balance || {};
      if (rows.length === 0) {
        $("#rdrrAgingWrap").html('<div class="empty"><div class="ei">📦</div>No aging inventory sold in this range.</div>');
        return;
      }
      var html = ['<table class="xtable"><thead><tr>',
        '<th>RBM</th>',
        '<th class="num g-hid">HID</th>',
        '<th class="num g-fast">FAST</th>',
        '<th class="num g-run">RUN</th>',
        '<th class="num g-slow">SLOW</th>',
        '<th class="num g-other">OTHER</th>',
        '<th class="num g-total">TOTAL</th>',
        '</tr></thead><tbody>'];
      rows.forEach(function (r) {
        html.push('<tr>' +
                '<td class="rbm-nm">' + escapeHtml(r.rbmName || '-') + '</td>' +
                cell(r.hid, 'g-hid') + cell(r.fast, 'g-fast') + cell(r.run, 'g-run') +
                cell(r.slow, 'g-slow') + cell(r.other, 'g-other') + cell(r.total, 'g-total') +
                '</tr>');
      });
      html.push('<tr class="tot">' +
              '<td>Total</td>' +
              cell(totals.hid, 'g-hid') + cell(totals.fast, 'g-fast') + cell(totals.run, 'g-run') +
              cell(totals.slow, 'g-slow') + cell(totals.other, 'g-other') + cell(totals.total, 'g-total') +
              '</tr>');
      html.push('<tr class="bal">' +
              '<td class="hdr">15-Day Balance Value</td>' +
              cell(balance.hid) + cell(balance.fast) + cell(balance.run) +
              cell(balance.slow) + cell(balance.other) + cell(balance.total) +
              '</tr>');
      html.push('</tbody></table>');
      $("#rdrrAgingWrap").html(html.join(""));
    }

    function cell(v, cls) {
      v = Number(v) || 0;
      var zero = v === 0 ? ' zero' : '';
      var extra = cls ? ' ' + cls : '';
      return '<td class="num' + extra + zero + '">' + formatIndian(v) + '</td>';
    }

    function formatIndian(n) {
      n = Math.round(Number(n) || 0);
      if (n === 0) return '0';
      var s = n.toString();
      var lastThree = s.substring(s.length - 3);
      var otherNums = s.substring(0, s.length - 3);
      if (otherNums !== '') lastThree = ',' + lastThree;
      return otherNums.replace(/\B(?=(\d{2})+(?!\d))/g, ',') + lastThree;
    }

    function renderPartnerBilling(data) {
      var rows = data.partnerBilling || [];
      var totals = data.partnerBillingTotals || {};
      $("#rdrrBillingPeriod").text(data.partnerBillingMonth || '');
      if (rows.length === 0) {
        $("#rdrrBillingWrap").html('<div class="empty"><div class="ei">👥</div>No targeted partners this month.</div>');
        return;
      }
      var html = ['<table class="xtable"><thead><tr>',
        '<th>RBM</th>',
        '<th class="num">Targeted</th>',
        '<th class="num">₹1L – 2L</th>',
        '<th class="num">₹2L – 3L</th>',
        '<th class="num">₹3L – 4L</th>',
        '<th class="num">₹4L – 5L</th>',
        '<th class="num g-total">₹5L &amp; Above</th>',
        '<th class="num">Zero Billed<br><small style="color:var(--soft);font-weight:600">(&lt;1L)</small></th>',
        '</tr></thead><tbody>'];
      rows.forEach(function (r) {
        html.push('<tr>' +
                '<td class="rbm-nm">' + escapeHtml(r.rbmName || '-') + '</td>' +
                '<td class="num">' + pill(r.targeted, '') + '</td>' +
                '<td class="num">' + pill(r.b1_2, r.b1_2 > 0 ? '' : 'zero') + '</td>' +
                '<td class="num">' + pill(r.b2_3, r.b2_3 > 0 ? '' : 'zero') + '</td>' +
                '<td class="num">' + pill(r.b3_4, r.b3_4 > 0 ? '' : 'zero') + '</td>' +
                '<td class="num">' + pill(r.b4_5, r.b4_5 > 0 ? '' : 'zero') + '</td>' +
                '<td class="num g-total">' + pill(r.b5, r.b5 > 0 ? 'good' : 'zero') + '</td>' +
                '<td class="num">' + pill(r.zero, r.zero > 0 ? 'warn' : 'zero') + '</td>' +
                '</tr>');
      });
      html.push('<tr class="tot">' +
              '<td>Total</td>' +
              '<td class="num">' + formatIndian(totals.targeted) + '</td>' +
              '<td class="num">' + formatIndian(totals.b1_2) + '</td>' +
              '<td class="num">' + formatIndian(totals.b2_3) + '</td>' +
              '<td class="num">' + formatIndian(totals.b3_4) + '</td>' +
              '<td class="num">' + formatIndian(totals.b4_5) + '</td>' +
              '<td class="num g-total">' + formatIndian(totals.b5) + '</td>' +
              '<td class="num">' + formatIndian(totals.zero) + '</td>' +
              '</tr>');
      html.push('</tbody></table>');
      $("#rdrrBillingWrap").html(html.join(""));
    }

    function pill(v, cls) {
      v = Number(v) || 0;
      var c = cls || (v === 0 ? 'zero' : '');
      return '<span class="cnt-pill ' + c + '">' + formatIndian(v) + '</span>';
    }

    function ratio(part, total) {
      part = Number(part) || 0;
      total = Number(total) || 0;
      if (total === 0) return 'zero';
      var p = (part / total) * 100;
      if (p >= 60) return 'good';
      if (p >= 30) return '';
      if (p > 0) return 'warn';
      return 'zero';
    }

    // Cached latest payload so the "Target Status by RBM" modal can render
    // without a re-fetch when the KPI card is clicked.
    var LAST_PAYLOAD = null;
    var SALES_PAYLOAD = null;              // fetched from /rbm-drr/sales-data when a Sales role is selected
    var CURRENT_SALES_ROLE = null;         // "ASM" / "CM" / … — when set with SALES_PAYLOAD, we render Sales view
    var CURRENT_SALES_AUTH_ID = null;      // when set, further narrows Sales view to this person
    // Set of RBM authIds allowed by the current filter. null = no filter (show all).
    var CURRENT_FILTER_AUTH_IDS = null;

    function render(data) {
      LAST_PAYLOAD = data;
      renderFiltered();
    }

    // Maps a sales-data row into the same shape the RBM grid renderer expects.
    function normaliseSalesRow(r) {
      return {
        authId: r.salesAuthId, warehouseId: r.warehouseId,
        rbmName: r.salesName + (r.role ? " (" + r.role + ")" : ""),
        warehouseName: r.warehouseName,
        totalAch: r.totalAch, totalTgt: r.totalTgt,
        hidAch: r.hidAch, hidTgt: r.hidTgt,
        fastAch: r.fastAch, fastTgt: r.fastTgt,
        runAch: r.runAch, runTgt: r.runTgt,
        slowAch: r.slowAch, slowTgt: r.slowTgt,
        otherAch: r.otherAch, otherTgt: r.otherTgt
      };
    }

    // Renders KPIs + grid from the appropriate payload:
    //  - Sales role active + payload loaded → filter SALES_PAYLOAD to that role (and optionally one person)
    //  - RBM filter or no filter → LAST_PAYLOAD filtered by CURRENT_FILTER_AUTH_IDS
    function renderFiltered() {
      if (CURRENT_SALES_ROLE && SALES_PAYLOAD) {
        var salesRows = (SALES_PAYLOAD.rows || []).filter(function (r) {
          if (r.role !== CURRENT_SALES_ROLE) return false;
          if (CURRENT_SALES_AUTH_ID && String(r.salesAuthId) !== String(CURRENT_SALES_AUTH_ID)) return false;
          return true;
        }).map(normaliseSalesRow);
        renderRowsAsGrid(salesRows, true);
        return;
      }
      if (!LAST_PAYLOAD) return;
      var allRows = LAST_PAYLOAD.rows || [];
      var rows = CURRENT_FILTER_AUTH_IDS
              ? allRows.filter(function (r) {
                return CURRENT_FILTER_AUTH_IDS[String(r.authId)];
              })
              : allRows;
      renderRowsAsGrid(rows, false);
    }

    // Extracted from renderFiltered() so both RBM-source and Sales-source paths share it.
    function renderRowsAsGrid(rows, isSalesView) {

      // Recompute totals from the filtered subset. Cheaper + correct vs using server totals.
      var t = {
        totalAch: 0, totalTgt: 0, totalBilled: 0, hidAch: 0, hidTgt: 0, fastAch: 0, fastTgt: 0,
        runAch: 0, runTgt: 0, slowAch: 0, slowTgt: 0, otherAch: 0, otherTgt: 0,
        rbmsAtOrAbove100: 0, rbmRowCount: rows.length
      };
      rows.forEach(function (r) {
        t.totalAch += Number(r.totalAch) || 0;
        t.totalTgt += Number(r.totalTgt) || 0;
        t.totalBilled += Number(r.actualBilled) || 0;
        t.hidAch += Number(r.hidAch) || 0;
        t.hidTgt += Number(r.hidTgt) || 0;
        t.fastAch += Number(r.fastAch) || 0;
        t.fastTgt += Number(r.fastTgt) || 0;
        t.runAch += Number(r.runAch) || 0;
        t.runTgt += Number(r.runTgt) || 0;
        t.slowAch += Number(r.slowAch) || 0;
        t.slowTgt += Number(r.slowTgt) || 0;
        t.otherAch += Number(r.otherAch) || 0;
        t.otherTgt += Number(r.otherTgt) || 0;
        if (r.totalTgt > 0 && r.totalAch >= r.totalTgt) t.rbmsAtOrAbove100++;
      });

      $("#rdrrKpiAch").html('&#8377;&nbsp;' + fmtLakh(t.totalAch) +
              ' <span class="rdrr-kpi-billed">/ &#8377;&nbsp;' + fmtLakh(t.totalBilled) + '</span>');
      $("#rdrrKpiTgt").html('&#8377;&nbsp;' + fmtLakh(t.totalTgt));
      var pk = pctLabel(t.totalAch, t.totalTgt);
      $("#rdrrKpiPct").html(pk.cls === "na" ? '<span class="u">n/a</span>' :
              '<span class="pct ' + pk.cls + '">' + pk.text + '</span>');
      $("#rdrrKpiTop").html((t.rbmsAtOrAbove100 || 0) + ' <span class="u">/ ' + (t.rbmRowCount || 0) + '</span>');

      if (rows.length === 0) {
        var emptyMsg;
        if (isSalesView) {
          emptyMsg = 'No Sales target/achievement rows for this person in the range. Has the pushSalesTargets cron run today?';
        } else if (CURRENT_FILTER_AUTH_IDS) {
          emptyMsg = 'No RBM rows in this range for the selected filter.';
        } else {
          emptyMsg = 'No RBM rows in this range.';
        }
        $("#rdrrTableWrap").html('<div class="empty"><div class="ei">📭</div>' + emptyMsg + '</div>');
        return;
      }

      var buckets = [
        {key: "hid", label: "HID"},
        {key: "fast", label: "FAST"},
        {key: "run", label: "RUN"},
        {key: "slow", label: "SLOW"},
        {key: "other", label: "OTHER"}
      ];

      var html = ['<table id="rdrrGrid"><thead><tr>',
        '<th>RBM &middot; Warehouse</th>',
        '<th class="num g-total">Total<br><small style="color:var(--soft);font-weight:600">ACH / TGT</small></th>',
        '<th class="num g-total">%</th>'];
      buckets.forEach(function (b) {
        html.push('<th class="num g-' + b.key + '">' + b.label + '<br><small style="color:var(--soft);font-weight:600">ACH / TGT</small></th>');
        html.push('<th class="num g-' + b.key + '">%</th>');
      });
      html.push('<th></th></tr></thead><tbody>');

      // Two-level collapse:
      //   Level 1 — one "All RBMs (N)" row at the top, shows the grand-total.
      //             Click → reveal per-RBM rows below.
      //   Level 2 — per-RBM rows. Single-warehouse RBMs are directly clickable
      //             (→ drill-down). Multi-warehouse RBMs collapse into a
      //             group-parent showing summed totals; click → reveal
      //             per-warehouse child rows.
      //   Level 3 — per-warehouse rows (only under multi-warehouse RBMs).
      //             Click → drill-down for that RBM × warehouse.
      // Default state on render: only the "All RBMs" row visible.
      var topRow = ['<tr class="top-parent">'];
      topRow.push('<td><div class="rbm-cell">' +
              '<span class="rn">' +
              '<span class="top-toggle">&rsaquo;</span> All RBMs' +
              '</span>' +
              '<span class="wh">' + rows.length + ' rows (click to expand)</span>' +
              '</div></td>');
      topRow.push(cellPair(t.totalAch, t.totalTgt));
      buckets.forEach(function (b) {
        topRow.push(cellPair(t[b.key + "Ach"], t[b.key + "Tgt"]));
      });
      topRow.push('<td class="chev">&nbsp;</td>');
      topRow.push('</tr>');
      html.push(topRow.join(""));

      // Group rows per person (authId).
      var groups = new Map();
      var groupOrder = [];
      rows.forEach(function (r) {
        var key = String(r.authId);
        if (!groups.has(key)) {
          groups.set(key, {authId: r.authId, name: r.rbmName, children: []});
          groupOrder.push(key);
        }
        groups.get(key).children.push(r);
      });

      var groupCounter = 0;
      groupOrder.forEach(function (key) {
        var g = groups.get(key);
        if (g.children.length === 1) {
          // Single-warehouse person → RBM-tier row, directly clickable for drill-down.
          var r = g.children[0];
          var tr = ['<tr class="clickable tier-rbm" style="display:none" data-authid="' + r.authId + '" data-warehouseid="' + r.warehouseId + '" data-rbmname="' + escapeHtml(r.rbmName) + '" data-warehousename="' + escapeHtml(r.warehouseName) + '">'];
          tr.push('<td><div class="rbm-cell"><span class="rn">' + escapeHtml(r.rbmName) + '</span><span class="wh">' + escapeHtml(r.warehouseName) + '</span></div></td>');
          tr.push(cellPair(r.totalAch, r.totalTgt));
          buckets.forEach(function (b) {
            tr.push(cellPair(r[b.key + "Ach"], r[b.key + "Tgt"]));
          });
          tr.push('<td class="chev">&rsaquo;</td>');
          tr.push('</tr>');
          html.push(tr.join(""));
          return;
        }

        // Multi-warehouse person → RBM-tier collapsed parent + hidden per-warehouse children.
        var gid = "g" + (groupCounter++);
        var sum = {totalAch: 0, totalTgt: 0};
        buckets.forEach(function (b) {
          sum[b.key + "Ach"] = 0;
          sum[b.key + "Tgt"] = 0;
        });
        g.children.forEach(function (r) {
          sum.totalAch += Number(r.totalAch) || 0;
          sum.totalTgt += Number(r.totalTgt) || 0;
          buckets.forEach(function (b) {
            sum[b.key + "Ach"] += Number(r[b.key + "Ach"]) || 0;
            sum[b.key + "Tgt"] += Number(r[b.key + "Tgt"]) || 0;
          });
        });

        var parent = ['<tr class="grp-parent tier-rbm" style="display:none" data-gid="' + gid + '">'];
        parent.push('<td><div class="rbm-cell">' +
                '<span class="rn">' + escapeHtml(g.name) + '</span>' +
                '<span class="wh"><span class="grp-toggle">&rsaquo;</span> ' +
                g.children.length + ' warehouses (click to expand)</span>' +
                '</div></td>');
        parent.push(cellPair(sum.totalAch, sum.totalTgt));
        buckets.forEach(function (b) {
          parent.push(cellPair(sum[b.key + "Ach"], sum[b.key + "Tgt"]));
        });
        parent.push('<td class="chev">&nbsp;</td>');
        parent.push('</tr>');
        html.push(parent.join(""));

        g.children.forEach(function (r) {
          var tr = ['<tr class="clickable grp-child grp-' + gid + '" style="display:none" data-authid="' + r.authId + '" data-warehouseid="' + r.warehouseId + '" data-rbmname="' + escapeHtml(r.rbmName) + '" data-warehousename="' + escapeHtml(r.warehouseName) + '">'];
          tr.push('<td><div class="rbm-cell" style="padding-left:22px">' +
                  '<span class="rn" style="font-weight:500;color:var(--soft)">&#x21B3; ' + escapeHtml(r.warehouseName) + '</span>' +
                  '</div></td>');
          tr.push(cellPair(r.totalAch, r.totalTgt));
          buckets.forEach(function (b) {
            tr.push(cellPair(r[b.key + "Ach"], r[b.key + "Tgt"]));
          });
          tr.push('<td class="chev">&rsaquo;</td>');
          tr.push('</tr>');
          html.push(tr.join(""));
        });
      });

      // totals row
      var totTr = ['<tr class="totalrow"><td><strong>Total</strong></td>'];
      totTr.push(cellPair(t.totalAch, t.totalTgt, true));
      buckets.forEach(function (b) {
        totTr.push(cellPair(t[b.key + "Ach"], t[b.key + "Tgt"], true));
      });
      totTr.push('<td></td></tr>');
      html.push(totTr.join(""));

      html.push('</tbody></table>');
      $("#rdrrTableWrap").html(html.join(""));

      // Cache the parsed row on each <tr> so drill-down can render bars immediately.
      rows.forEach(function (r) {
        $("#rdrrGrid tbody tr").filter(function () {
          return +this.dataset.authid === r.authId && +this.dataset.warehouseid === r.warehouseId;
        }).each(function () {
          $(this).data("cached", r);
        });
      });

      $("#rdrrGrid tbody tr.clickable").on("click", function () {
        var d = this.dataset;
        var cached = $(this).data("cached");
        openDrilldown(+d.authid, +d.warehouseid, d.rbmname, d.warehousename, cached);
      });

      // Toggle collapsed groups: click parent row → show/hide the per-warehouse child rows.
      // Variable names deliberately avoid leading '$' — Velocity would try to parse them.
      $("#rdrrGrid tbody tr.grp-parent").on("click", function () {
        var gid = this.dataset.gid;
        var childRows = $("#rdrrGrid tbody tr.grp-" + gid);
        var isOpen = childRows.filter(":visible").length > 0;
        childRows.toggle(isOpen ? false : true);
        var toggleEl = $(this).find(".grp-toggle");
        toggleEl.css("transform", isOpen ? "rotate(0deg)" : "rotate(90deg)");
        toggleEl.css("display", "inline-block");
      });

      // Top-level "All RBMs" collapse: click → show/hide every RBM-tier row.
      // When collapsing, also force-hide any expanded warehouse child rows.
      $("#rdrrGrid tbody tr.top-parent").on("click", function () {
        var rbmRows = $("#rdrrGrid tbody tr.tier-rbm");
        var isOpen = rbmRows.filter(":visible").length > 0;
        rbmRows.toggle(isOpen ? false : true);
        if (isOpen) {
          // Collapsing → also hide any expanded warehouse children + reset their chevrons.
          $("#rdrrGrid tbody tr.grp-child").hide();
          $("#rdrrGrid tbody tr.grp-parent .grp-toggle").css("transform", "rotate(0deg)");
        }
        var topToggle = $(this).find(".top-toggle");
        topToggle.css("transform", isOpen ? "rotate(0deg)" : "rotate(90deg)");
        topToggle.css("display", "inline-block");
      });
    }

    function cellPair(ach, tgt, isTotal) {
      var p = pctLabel(ach, tgt);
      var vhtml = '<td class="num"><div class="cell-val">' + fmtLakh(ach) + ' <small>/ ' + fmtLakh(tgt) + '</small></div></td>';
      var barCls = p.cls === "na" ? "" : p.cls;
      var barW = pctBarWidth(ach, tgt);
      var pctHtml = '<td class="num"><span class="pct ' + p.cls + '">' + p.text + '</span>' +
              (barCls ? '<div class="bar"><i class="' + barCls + '" style="width:' + barW.toFixed(1) + '%"></i></div>' : '') +
              '</td>';
      return vhtml + pctHtml;
    }

    // --------------------------- drill-down ---------------------------

    function openDrilldown(authId, warehouseId, rbmName, warehouseName, cachedRow) {
      var back = $("#rdrrBack");
      $("#rdrrDdTitle").text(rbmName + " — " + warehouseName);
      $("#rdrrDdDesc").text(fmtDate(startDate) + " – " + fmtDate(endDate));
      $("#rdrrDdBars").empty();
      back.addClass("show");

      if (cachedRow) {
        renderBars(cachedRow);
      } else {
        $("#rdrrDdAch").text("—");
        $("#rdrrDdTgt").text("—");
        $("#rdrrDdPct").text("—");
      }
      $("#rdrrDdBrands").html('<div class="loading"><div class="spinner"></div>Loading brand mix&hellip;</div>');
      $("#rdrrDdFlow").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');

      var salesQs = CURRENT_SALES_ROLE ? ("&hierarchy=SALES&role=" + encodeURIComponent(CURRENT_SALES_ROLE)) : "";
      fetch("/rbm-drr/rbm-detail?authId=" + authId + "&warehouseId=" + warehouseId +
              "&startDate=" + startDate + "&endDate=" + endDate + salesQs, {credentials: "same-origin"})
              .then(function (r) {
                return r.json();
              })
              .then(function (data) {
                renderBrands(data);
                renderWarehouseFlow(data, cachedRow);
              })
              .catch(function (e) {
                var msg = '<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>';
                $("#rdrrDdBrands").html(msg);
                $("#rdrrDdFlow").html(msg);
              });
    }

    function renderWarehouseFlow(data, row) {
      var buying = (data && data.buying) || [];
      // Map buying values by status for O(1) lookup.
      var buyMap = {};
      buying.forEach(function (b) {
        buyMap[b.status] = Number(b.value) || 0;
      });
      // TEMP-DISABLED: opening-balance addition. Re-enable alongside the
      // controller block. Uncomment these two lines and swap the "Coverage"
      // computation below to (sell / (open + buy)).
      // var opening = (data && data.opening) || [];
      // var openMap = {}; opening.forEach(function (o) { openMap[o.status] = Number(o.value) || 0; });
      var labels = {HID: "HID", FASTMOVING: "FAST", RUNNING: "RUN", SLOWMOVING: "SLOW", OTHER: "OTHER"};
      var order = ["HID", "FASTMOVING", "RUNNING", "SLOWMOVING", "OTHER"];
      // Selling per bucket from the cached grid row (already has ACH per bucket).
      var sellMap = {
        HID: row ? Number(row.hidAch) || 0 : 0,
        FASTMOVING: row ? Number(row.fastAch) || 0 : 0,
        RUNNING: row ? Number(row.runAch) || 0 : 0,
        SLOWMOVING: row ? Number(row.slowAch) || 0 : 0,
        OTHER: row ? Number(row.otherAch) || 0 : 0
      };
      var totBuy = 0, totSell = 0;
      order.forEach(function (s) {
        totBuy += buyMap[s] || 0;
        totSell += sellMap[s] || 0;
      });

      // "Coverage" = selling / buying. High coverage = we sold most of what came in.
      // Low coverage = supply piling up (aging risk).
      function covCell(sell, buy) {
        if (!buy || buy <= 0) {
          if (sell > 0) return '<span class="cov-cell na" title="No buying recorded in this range">— <span class="cov-pct" style="color:var(--good)">sold ' + fmtLakh(sell) + '</span></span>';
          return '<span class="cov-cell na">—</span>';
        }
        var pct = (sell / buy) * 100;
        var cls = pct >= 60 ? 'good' : pct >= 25 ? 'warn' : 'bad';
        var w = Math.min(100, pct);
        return '<span class="cov-cell ' + cls + '">' +
                '<span class="cov-bar"><i style="width:' + w.toFixed(1) + '%"></i></span>' +
                '<span class="cov-pct">' + pct.toFixed(1) + '%</span></span>';
      }

      var html = '<table class="flow-tbl"><thead><tr>' +
              '<th>Movement</th><th class="num">Buying</th><th class="num">Selling</th><th class="num">Coverage (sold / bought)</th>' +
              '</tr></thead><tbody>';
      order.forEach(function (s) {
        var buy = buyMap[s] || 0, sell = sellMap[s] || 0;
        html += '<tr class="' + s.toLowerCase() + '">' +
                '<td><i></i>' + labels[s] + '</td>' +
                '<td class="num">' + fmtLakh(buy) + '</td>' +
                '<td class="num">' + fmtLakh(sell) + '</td>' +
                '<td class="num">' + covCell(sell, buy) + '</td>' +
                '</tr>';
      });
      html += '<tr class="tot">' +
              '<td>Total</td>' +
              '<td class="num">' + fmtLakh(totBuy) + '</td>' +
              '<td class="num">' + fmtLakh(totSell) + '</td>' +
              '<td class="num">' + covCell(totSell, totBuy) + '</td>' +
              '</tr>';
      html += '</tbody></table>';
      $("#rdrrDdFlow").html(html);
    }

    function renderBrands(data) {
      var brands = (data && data.brands) || [];
      if (brands.length === 0) {
        $("#rdrrDdBrands").html('<div class="empty"><div class="ei">🏷️</div>No brand sales in this range.</div>');
        return;
      }
      var maxShare = brands[0].sharePct || 0.001;
      var html = '<div class="brands">';
      brands.forEach(function (b, idx) {
        var barW = maxShare > 0 ? Math.min(100, (b.sharePct / maxShare) * 100) : 0;
        var topCls = idx === 0 ? ' top' : '';
        var pc = Number(b.partnerCount) || 0;
        var pcCls = pc === 0 ? ' zero' : '';
        var pcLabel = pc + (pc === 1 ? ' partner' : ' p');
        html += '<div class="brand-card' + topCls + '" data-idx="' + idx + '">' +
                '<div class="brand-head">' +
                '<div class="cx">&rsaquo;</div>' +
                '<div class="bn" title="' + escapeHtml(b.brand) + '">' + escapeHtml(b.brand) + '</div>' +
                '<div class="btrack"><i style="width:' + barW.toFixed(1) + '%"></i></div>' +
                '<div class="bpc' + pcCls + '" title="Distinct partners of this RBM who billed ' + escapeHtml(b.brand) + ' in the range">' +
                '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>' +
                pcLabel +
                '</div>' +
                '<div class="bval">' + fmtLakh(b.valueSold) + '</div>' +
                '<div class="bshare">' + (b.sharePct || 0).toFixed(1) + '%</div>' +
                '</div>' +
                '<div class="brand-body">' +
                '<div class="bhdr">Movement Split within ' + escapeHtml(b.brand) + '</div>' +
                renderBrandBuckets(b.buckets || []) +
                '</div>' +
                '</div>';
      });
      html += '</div>';
      $("#rdrrDdBrands").html(html);

      $("#rdrrDdBrands .brand-head").on("click", function () {
        $(this).closest(".brand-card").toggleClass("open");
      });
    }

    function renderBrandBuckets(buckets) {
      if (!buckets || buckets.length === 0) {
        return '<div style="color:var(--soft);font-size:12px;padding:8px 4px">No movement split available.</div>';
      }
      var labels = {HID: "HID", FASTMOVING: "FAST", RUNNING: "RUN", SLOWMOVING: "SLOW", OTHER: "OTHER"};
      var html = '<div class="mbars">';
      buckets.forEach(function (bk) {
        var cls = (bk.status || "OTHER").toLowerCase();
        var label = labels[bk.status] || bk.status || "OTHER";
        var sp = bk.sharePctOfBrand || 0;
        var barW = Math.min(100, sp);
        html += '<div class="mbar ' + cls + '">' +
                '<div class="mname"><i></i>' + label + '</div>' +
                '<div class="mtrack"><i style="width:' + barW.toFixed(1) + '%"></i></div>' +
                '<div class="mval">' + fmtLakh(bk.valueSold) + '</div>' +
                '<div class="mshare">' + sp.toFixed(1) + '%</div>' +
                '</div>';
      });
      html += '</div>';
      return html;
    }

    function renderBars(row) {
      var totalAch = row.totalAch, totalTgt = row.totalTgt;
      $("#rdrrDdAch").html('&#8377;&nbsp;' + fmtLakh(totalAch));
      $("#rdrrDdTgt").html('&#8377;&nbsp;' + fmtLakh(totalTgt));
      var pt = pctLabel(totalAch, totalTgt);
      $("#rdrrDdPct").html(pt.cls === "na" ? '<span style="color:var(--soft);font-size:14px">n/a</span>' :
              '<span class="pct ' + pt.cls + '">' + pt.text + '</span>');

      var buckets = [
        {key: "hid", label: "HID"},
        {key: "fast", label: "FAST"},
        {key: "run", label: "RUN"},
        {key: "slow", label: "SLOW"},
        {key: "other", label: "OTHER"}
      ];
      var html = "";
      buckets.forEach(function (b) {
        var ach = row[b.key + "Ach"], tgt = row[b.key + "Tgt"];
        var p = pctLabel(ach, tgt);
        var w = pctBarWidth(ach, tgt);
        html += '<div class="bbar ' + b.key + '">' +
                '<div class="bname"><i></i>' + b.label + '</div>' +
                '<div class="btrack"><i style="width:' + w.toFixed(1) + '%"></i></div>' +
                '<div class="btgt"><b>' + fmtLakh(ach) + '</b> / ' + fmtLakh(tgt) + '</div>' +
                '<div class="bpct ' + p.cls + '">' + p.text + '</div>' +
                '</div>';
      });
      $("#rdrrDdBars").html(html);
    }

    function escapeHtml(s) {
      if (s == null) return "";
      return String(s).replace(/[&<>"']/g, function (c) {
        return {"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;"}[c];
      });
    }

    $("#rdrrClose").on("click", function () {
      $("#rdrrBack").removeClass("show");
    });
    $("#rdrrBack").on("click", function (e) {
      if (e.target === this) $(this).removeClass("show");
    });

    // Target-status modal (achieved vs pending) — opens from the KPI card.
    $("#rdrrKpiTopCard").on("click", function () {
      openStatusModal();
    });
    $("#rdrrStatusClose").on("click", function () {
      $("#rdrrStatusBack").removeClass("show");
    });
    $("#rdrrStatusBack").on("click", function (e) {
      if (e.target === this) $(this).removeClass("show");
    });

    function openStatusModal() {
      if (!LAST_PAYLOAD) return;
      // Respect the RBM filter so the modal matches what's on screen.
      var rows = (LAST_PAYLOAD.rows || []).filter(function (r) {
        return !CURRENT_FILTER_AUTH_IDS || CURRENT_FILTER_AUTH_IDS[String(r.authId)];
      });
      var achieved = [], pending = [];
      rows.forEach(function (r) {
        if (!r.totalTgt || r.totalTgt <= 0) return;
        var pct = (r.totalAch / r.totalTgt) * 100;
        var enriched = {row: r, pct: pct};
        if (r.totalAch >= r.totalTgt) achieved.push(enriched);
        else pending.push(enriched);
      });
      achieved.sort(function (a, b) {
        return b.pct - a.pct;
      });
      pending.sort(function (a, b) {
        return a.pct - b.pct;
      });

      $("#rdrrStatusDesc").text(fmtDate(startDate) + " – " + fmtDate(endDate));
      $("#rdrrStatusAchCnt").text(achieved.length);
      $("#rdrrStatusPndCnt").text(pending.length);
      $("#rdrrStatusAchList").html(renderStatusList(achieved, "achieved"));
      $("#rdrrStatusPndList").html(renderStatusList(pending, "pending"));
      $("#rdrrStatusBack").addClass("show");

      // Bind click on each item to open the same drill-down slide-in.
      $("#rdrrStatusBack .status-item").on("click", function () {
        var d = this.dataset;
        var cachedRow = null;
        (LAST_PAYLOAD.rows || []).some(function (r) {
          if (r.authId == d.authid && r.warehouseId == d.warehouseid) {
            cachedRow = r;
            return true;
          }
          return false;
        });
        $("#rdrrStatusBack").removeClass("show");
        openDrilldown(+d.authid, +d.warehouseid, d.rbmname, d.warehousename, cachedRow);
      });
    }

    function renderStatusList(items, kind) {
      if (items.length === 0) {
        return '<div class="status-empty">' +
                (kind === "achieved" ? "No RBM has met target in this range." : "All RBMs met target — nice!") +
                '</div>';
      }
      var html = "";
      items.forEach(function (it) {
        var r = it.row;
        var p = pctClass(it.pct);
        html += '<div class="status-item" title="Click to view drill-down"' +
                ' data-authid="' + r.authId + '" data-warehouseid="' + r.warehouseId + '"' +
                ' data-rbmname="' + escapeHtml(r.rbmName) + '" data-warehousename="' + escapeHtml(r.warehouseName) + '"' +
                ' style="cursor:pointer">' +
                '<div><span class="rn">' + escapeHtml(r.rbmName) + '</span>' +
                '<span class="wh">' + escapeHtml(r.warehouseName) + '</span></div>' +
                '<div class="vv"><b>' + fmtLakh(r.totalAch) + '</b> / ' + fmtLakh(r.totalTgt) + '</div>' +
                '<div class="pp ' + p + '">' + it.pct.toFixed(1) + '%</div>' +
                '</div>';
      });
      return html;
    }

    // ------------------------- Today PO & Billed matrix -------------------------
    // Reuses the existing /indent/today_po_table_fragment endpoint (unchanged).
    // Parses the returned HTML into a data model and re-renders a compact
    // heatmap grid — one row per person, one column per brand, cell showing
    // Billed (primary) + Partners/PO (secondary), with per-brand heat shading.
    function loadPurchaseMilestones() {
      var cat = $("#rdrrPmCategory").val() || "RBM";
      $("#rdrrPmWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
      $.ajax({
        url: "/indent/today_po_table_fragment",
        type: "GET",
        data: {categoryType: cat},
        success: function (html) {
          try {
            var model = parsePurchaseMilestonesFragment(html);
            $("#rdrrPmWrap").html(renderPurchaseMilestonesGrid(model));
          } catch (e) {
            $("#rdrrPmWrap").html('<div class="empty">Failed to render purchase-milestones data.</div>');
          }
        },
        error: function () {
          $("#rdrrPmWrap").html('<div class="empty">Failed to load purchase-milestones data.</div>');
        }
      });
    }

    function numberToIndianCurrency(n) {
      if (n === 0) return "0";
      var s = n.toString();
      var lastThree = s.substring(s.length - 3);
      var other = s.substring(0, s.length - 3);
      if (other !== "") lastThree = "," + lastThree;
      return other.replace(/\B(?=(\d{2})+(?!\d))/g, ",") + lastThree;
    }

    // Compact Lakh format for the Purchase Milestones grid: 623478 → "6.2L", 500 → "0.0L", 15737358 → "157.4L".
    // Always one decimal, always suffixed "L" — matches how other DRR sections display money.
    function pmLakh(n) {
      n = Number(n) || 0;
      return (n / 100000).toFixed(1) + "L";
    }

    // Compact currency: >= 1Cr => "1.19Cr", >= 1L => "18.68L", >= 1K => "28.4K", else raw.
    // `tight=true` drops the space before the unit for narrow cells.
    function pmCompactCurrency(n, tight) {
      n = Number(n) || 0;
      var sp = tight ? "" : " ";
      if (n >= 10000000) return "₹" + (n / 10000000).toFixed(2) + sp + "Cr";
      if (n >= 100000) return "₹" + (n / 100000).toFixed(2) + sp + "L";
      if (n >= 1000) return "₹" + (n / 1000).toFixed(1) + sp + "K";
      return "₹" + numberToIndianCurrency(Math.round(n));
    }

    // Parse the injected multi-table fragment into:
    //   { brands: [{name, slug}], rows: [{name, cells: {brand: {po, count, billed}}, totals: {po, count, billed}}] }
    function parsePurchaseMilestonesFragment(html) {
      var doc = new DOMParser().parseFromString(html, "text/html");
      var tables = doc.querySelectorAll("table");
      var brandOrder = [];
      var brandSet = {};
      var rowsMap = {};   // name -> {name, order, cells}
      var rowOrder = [];

      tables.forEach(function (tbl) {
        var theadRows = tbl.querySelectorAll("thead tr");
        if (theadRows.length < 1) return;
        // First thead row: [AuthUser th] + brand ths (colspan=3)
        var brandThs = theadRows[0].querySelectorAll("th[colspan]");
        var brands = [];
        brandThs.forEach(function (th) {
          var label = (th.textContent || "").trim().toUpperCase();
          if (!label) return;
          brands.push(label);
          if (!brandSet[label]) {
            brandSet[label] = true;
            brandOrder.push({name: label, slug: label.toLowerCase().replace(/[^a-z0-9]+/g, "")});
          }
        });

        tbl.querySelectorAll("tbody tr").forEach(function (tr) {
          var tds = tr.querySelectorAll("td");
          if (tds.length < 1 + brands.length * 3) return;
          var name = (tds[0].textContent || "").trim();
          if (!name) return;
          var row = rowsMap[name];
          if (!row) {
            row = {name: name, cells: {}, isTotal: /^all\b/i.test(name)};
            rowsMap[name] = row;
            rowOrder.push(name);
          }
          for (var i = 0; i < brands.length; i++) {
            var b = brands[i];
            var po = parseFloat((tds[1 + i * 3].textContent || "0").replace(/[^\d.-]/g, "")) || 0;
            var count = parseInt((tds[1 + i * 3 + 1].textContent || "0").replace(/[^\d.-]/g, ""), 10) || 0;
            var billed = parseFloat((tds[1 + i * 3 + 2].textContent || "0").replace(/[^\d.-]/g, "")) || 0;
            row.cells[b] = {po: po, count: count, billed: billed};
          }
        });
      });

      // Pull the TOTAL brand out of brandOrder and keep it first (fixed column).
      var totalIdx = brandOrder.findIndex(function (b) {
        return b.name === "TOTAL";
      });
      var totalBrand = totalIdx >= 0 ? brandOrder.splice(totalIdx, 1)[0] : null;

      // Sort remaining brands by grand total Billed (biggest first) using the "All …" row if present.
      var totalRowName = rowOrder.find(function (n) {
        return /^all\b/i.test(n);
      });
      var totalRow = totalRowName ? rowsMap[totalRowName] : null;
      if (totalRow) {
        brandOrder.sort(function (a, b) {
          var av = (totalRow.cells[a.name] || {}).billed || 0;
          var bv = (totalRow.cells[b.name] || {}).billed || 0;
          return bv - av;
        });
      }
      if (totalBrand) brandOrder.unshift(totalBrand);

      // Order rows: total first, others by grand total Billed (from TOTAL brand or sum).
      var totalRowIdx = totalRowName ? rowOrder.indexOf(totalRowName) : -1;
      if (totalRowIdx > 0) {
        rowOrder.splice(totalRowIdx, 1);
        rowOrder.unshift(totalRowName);
      }
      var head = totalRowIdx >= 0 ? [rowOrder.shift()] : [];
      rowOrder.sort(function (a, b) {
        var av = (rowsMap[a].cells["TOTAL"] || {}).billed || 0;
        var bv = (rowsMap[b].cells["TOTAL"] || {}).billed || 0;
        return bv - av;
      });
      rowOrder = head.concat(rowOrder);

      // Per-brand max Billed for heatmap intensity (excludes the totals row).
      var brandMax = {};
      brandOrder.forEach(function (b) {
        var max = 0;
        rowOrder.forEach(function (name) {
          var row = rowsMap[name];
          if (row.isTotal) return;
          var v = (row.cells[b.name] || {}).billed || 0;
          if (v > max) max = v;
        });
        brandMax[b.name] = max;
      });

      // The source fragment doesn't set a partner count on the "All RBMs" TOTAL
      // cell (grandTotal in MonitorController.getTodayPoTableFragment only sums
      // billed/PO/POBilled, not partiesBilled). Fill it in client-side by
      // summing each RBM's own TOTAL partner count — that column is the
      // distinct-across-brands count per RBM, so this sum is the most useful
      // aggregate available without a new backend query. Small risk of double-
      // counting if the same partner is served by two RBMs (rare, region-based).
      if (totalRow && totalRow.cells["TOTAL"]) {
        var sumTotalPartners = 0;
        rowOrder.forEach(function (name) {
          var r = rowsMap[name];
          if (r.isTotal) return;
          sumTotalPartners += ((r.cells["TOTAL"] || {}).count || 0);
        });
        if (sumTotalPartners > 0) {
          totalRow.cells["TOTAL"].count = sumTotalPartners;
        }
      }

      var rows = rowOrder.map(function (n) {
        return rowsMap[n];
      });
      return {brands: brandOrder, rows: rows, brandMax: brandMax};
    }

    function renderPurchaseMilestonesGrid(model) {
      if (!model.rows.length || !model.brands.length) {
        return '<div class="empty">No data.</div>';
      }
      var html = ['<div class="pm-grid-wrap"><table class="pm-grid"><thead><tr>'];
      html.push('<th class="pm-corner">' +
              '<div class="pm-corner-lbl">Person</div>' +
              '<div class="pm-corner-hint">' + model.rows.length + ' · ' + model.brands.length + ' brands</div>' +
              '</th>');
      model.brands.forEach(function (b) {
        html.push('<th class="pm-brand ' + b.slug + '">' + b.name + '</th>');
      });
      html.push('</tr></thead><tbody>');

      model.rows.forEach(function (row) {
        html.push('<tr class="pm-row' + (row.isTotal ? ' pm-total-row' : '') + '">');
        html.push('<td class="pm-name">' +
                '<div class="pm-name-txt">' + escapeHtml(row.name) + '</div>' +
                (row.isTotal ? '<div class="pm-name-sub">Grand total</div>' : '') +
                '</td>');
        model.brands.forEach(function (b) {
          var c = row.cells[b.name] || {po: 0, count: 0, billed: 0};
          var max = model.brandMax[b.name] || 0;
          var intensity = (!row.isTotal && max > 0) ? Math.min(1, c.billed / max) : 0;
          var isZero = c.billed === 0 && c.po === 0 && c.count === 0;
          // Softer blue heatmap: capped alpha keeps dark text readable at max intensity.
          var bgAlpha = intensity > 0 ? (0.05 + intensity * 0.22).toFixed(2) : 0;
          var style = (!row.isTotal && intensity > 0)
                  ? ' style="background: rgba(59,130,246,' + bgAlpha + ')"' : '';
          if (isZero) {
            html.push('<td class="pm-cell pm-cell-zero">&mdash;</td>');
          } else {
            // Values formatted in Lakh (X.XL) — matches how the drill-down and other DRR sections display money.
            var billedTxt = pmLakh(c.billed);
            var poTxt = pmLakh(c.po);
            var partnerLine = c.count > 0
                    ? ('<div class="pm-partners">' + c.count + '<span class="pm-tag"> p' + (c.count === 1 ? '' : 's') + '</span></div>')
                    : '';
            var poLine = (c.po > 0 || c.billed > 0)
                    ? ('<div class="pm-po">' + poTxt + '<span class="pm-tag"> PO</span></div>')
                    : '';
            var billedLine = '<div class="pm-billed">' + billedTxt + '</div>';
            // Order: partner (top) → PO (middle) → billed (bottom, primary)
            html.push('<td class="pm-cell' + (row.isTotal ? ' pm-cell-total' : '') + '"' + style + '>' +
                    partnerLine + poLine + billedLine +
                    '</td>');
          }
        });
        html.push('</tr>');
      });
      html.push('</tbody></table></div>');
      return html.join("");
    }

    function escapeHtml(s) {
      return String(s == null ? "" : s)
              .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
              .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
    }

    $("#rdrrPmCategory").on("change", loadPurchaseMilestones);

    // ------------------------- Month picker + RBM Performance Dashboard -------------------------
    // The Composite MTD Ranking card now reads /rbm-drr/performance?month=YYYY-MM so historical
    // months can be viewed. Partner Billing Count re-fetches /rbm-drr/extras?month=YYYY-MM.
    // Aging/Billing extras aging bits are unaffected by the month picker — they stay driven by
    // the top-of-page date range.

    function fmtSeconds(sec) {
      sec = Math.max(0, Math.round(Number(sec) || 0));
      var h = Math.floor(sec / 3600);
      var m = Math.floor((sec % 3600) / 60);
      if (h > 0) return h + "h " + m + "m";
      return m + "m";
    }

    function pctCell(pct) {
      var p = Number(pct) || 0;
      return p.toFixed(1) + "%";
    }

    function renderPerformance(data) {
      var rows = data.rows || [];
      $("#rdrrPerfMonth").text(data.monthLabel || 'MTD');
      if (rows.length === 0) {
        $("#rdrrPerfWrap").html('<div class="empty"><div class="ei">📊</div>No performance dashboard data for this month.</div>');
        return;
      }
      var html = ['<div style="overflow-x:auto"><table class="xtable" id="rdrrPerfTable"><thead><tr>',
        '<th style="width:48px">Rank</th>',
        '<th>RBM</th>',
        '<th class="num">Partners</th>',
        '<th class="num" title="Talk time / expected (10%)">Talk Time<br><small>(10%)</small></th>',
        '<th class="num" title="MTD achieved / monthly target (55%)">MTD Achievement<br><small>(55%)</small></th>',
        '<th class="num" title="Aging sold / warehouse aging stock. Denominator is a live snapshot (2%).">15-Day Aging<br><small>(2%)</small></th>',
        '<th class="num" title="Partners who crossed ₹1L this month (29%)">&gt;1L Partners<br><small>(29%)</small></th>',
        '<th class="num" title="Average partner rating (2%)">Avg Rating<br><small>(2%)</small></th>',
        '<th class="num" title="Partners who rated / total partners (2%)">Partners Rated<br><small>(2%)</small></th>',
        '<th class="num g-total">Score</th>',
        '</tr></thead><tbody>'];
      rows.forEach(function (r) {
        var rankCell;
        if (r.rank === 1) rankCell = '<img src="/resources/images/king.png" style="width:28px" title="Rank 1">';
        else if (r.rank === 2) rankCell = '<img src="/resources/images/trophy-cup.png" style="width:24px" title="Rank 2">';
        else if (r.rank === 3) rankCell = '<img src="/resources/images/bronze-medal.png" style="width:24px" title="Rank 3">';
        else rankCell = String(r.rank);
        html.push('<tr>' +
                '<td class="num">' + rankCell + '</td>' +
                '<td class="rbm-nm">' + escapeHtml(r.rbmName || '-') + '</td>' +
                '<td class="num">' + (Number(r.partnerCount) || 0) + '</td>' +
                '<td class="num">' + fmtSeconds(r.talkSeconds) + '<br><small style="color:var(--soft)">' + pctCell(r.talkPct) + '</small></td>' +
                '<td class="num">' + pctCell(r.drrAchvPct) + '</td>' +
                '<td class="num">' + formatIndian(r.agingUnitsSold) + '<br><small style="color:var(--soft)">' + pctCell(r.agingPct) + '</small></td>' +
                '<td class="num">' + (Number(r.above1LPartnerCount) || 0) + '<br><small style="color:var(--soft)">' + pctCell(r.above1LPct) + '</small></td>' +
                '<td class="num">' + (Number(r.partnerRating) || 0).toFixed(2) + '</td>' +
                '<td class="num">' + (Number(r.ratingPartnerCount) || 0) + '</td>' +
                '<td class="num g-total">' + (Number(r.compositeScore) || 0).toFixed(1) + '</td>' +
                '</tr>');
      });
      html.push('</tbody></table></div>');
      $("#rdrrPerfWrap").html(html.join(""));
    }

    function loadPerformance(monthKey) {
      $("#rdrrPerfWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
      var qs = monthKey ? ("?month=" + encodeURIComponent(monthKey)) : "";
      fetch("/rbm-drr/performance" + qs, {credentials: "same-origin"})
              .then(function (r) {
                return r.json().then(function (b) {
                  return {ok: r.ok, body: b};
                });
              })
              .then(function (res) {
                if (!res.ok) {
                  $("#rdrrPerfWrap").html('<div class="empty">' + escapeHtml(res.body && res.body.error ? res.body.error : "Failed to load.") + '</div>');
                  return;
                }
                renderPerformance(res.body);
              })
              .catch(function (e) {
                $("#rdrrPerfWrap").html('<div class="empty">Failed to load: ' + (e && e.message ? e.message : "network error") + '</div>');
              });
    }

    // Note: variable is named `sel` without the leading dollar on purpose.
    // Velocity would parse a leading-dollar name as a template reference and
    // choke on the embedded strings in the body.
    function populateMonthPicker() {
      var sel = $("#rdrrMonthPicker");
      if (sel.length === 0 || sel.data("populated")) return;
      var now = new Date();
      var opts = [];
      for (var i = 0; i < 6; i++) {
        var d = new Date(now.getFullYear(), now.getMonth() - i, 1);
        var y = d.getFullYear();
        var m = String(d.getMonth() + 1).padStart(2, "0");
        var key = y + "-" + m;
        var label = d.toLocaleString("en-US", {month: "long", year: "numeric"}) + (i === 0 ? " (current)" : "");
        opts.push('<option value="' + key + '">' + label + '</option>');
      }
      sel.html(opts.join(""));
      sel.data("populated", true);
      sel.on("change", function () {
        loadPerformance($(this).val());
      });
    }

    // ------------------------- RBM Call Target iframe -------------------------
    // The /indent/rbm_call_target endpoint returns a Velocity FRAGMENT (no <html>,
    // no Bootstrap/jQuery). When loaded directly in an iframe, Bootstrap modals
    // collapse and their inner Loading… contents render inline. Fix: fetch the
    // fragment, wrap it in a self-contained document that loads jQuery + Popper +
    // Bootstrap, then hand it to the iframe via srcdoc. Zero backend change.
    function loadCallTargetIframe() {
      $.ajax({
        url: "/indent/rbm_call_target",
        type: "GET",
        dataType: "html",
        success: function (fragment) {
          var doc = [
            '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">',
            '<meta name="viewport" content="width=device-width,initial-scale=1">',
            '<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">',
            '<style>body{margin:0;padding:16px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;background:#fff}</style>',
            '</head><body>',
            fragment,
            '<script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></' + 'script>',
            '<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></' + 'script>',
            '<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></' + 'script>',
            '</body></html>'
          ].join("");
          var frame = document.getElementById("rdrrCallFrame");
          frame.onload = function () {
            hookCallTargetDrawer(frame);
          };
          frame.srcdoc = doc;
        },
        error: function () {
          document.getElementById("rdrrCallFrame").srcdoc =
                  '<div style="padding:24px;font-family:system-ui;color:#97a1b5">Failed to load call-target view.</div>';
        }
      });
    }

    // Redirect the iframe's Bootstrap-modal drill-downs into a slide-in parent drawer
    // that matches the dashboard style. srcdoc iframes inherit the parent origin so
    // we can freely reach into the iframe's window and override its functions.
    function hookCallTargetDrawer(frame) {
      var iw = frame.contentWindow;
      if (!iw) return;
      // Give the source's own <script> a moment to run and define the functions.
      setTimeout(function () {
        try {
          iw.showCalledPartnerDetails = function (authId, rbmName) {
            openCallTargetDrawer("called", authId, rbmName, iw.pageQueryDate);
          };
          iw.showOutOfSequenceDetails = function (authId, rbmName) {
            openCallTargetDrawer("oos", authId, rbmName, iw.pageQueryDate);
          };
        } catch (e) {
          // If cross-origin ever kicks in (shouldn't with srcdoc same-origin),
          // fall back to the iframe's native Bootstrap modal — no crash.
        }
      }, 50);
    }

    function openCallTargetDrawer(kind, authId, rbmName, dateStr) {
      var titleWho = rbmName + (dateStr ? " (" + dateStr + ")" : "");
      var title = (kind === "oos" ? "Out of Sequence Calls — " : "Called Partners — ") + titleWho;
      $("#rdrrCallDrawerTitle").text(title);
      $("#rdrrCallDrawerDesc").text(kind === "oos"
              ? "Calls placed outside the recommended sequence."
              : "All calls made by this RBM today, with recordings.");
      $("#rdrrCallDrawerBody").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
      $("#rdrrCallDrawerBack").addClass("show");

      var url;
      if (kind === "oos") {
        url = "/indent/rbm_call_target/oos_details?authId=" + encodeURIComponent(authId);
      } else {
        var dp = dateStr ? "&date=" + encodeURIComponent(dateStr) : "";
        url = "/indent/rbm_call_target/called_details?authId=" + encodeURIComponent(authId)
                + "&agentName=" + encodeURIComponent(rbmName) + dp;
      }
      $.ajax({
        url: url, type: "GET", dataType: "json",
        success: function (data) {
          if (!data || data.length === 0) {
            $("#rdrrCallDrawerBody").html('<div class="empty"><div class="ei">📭</div>No entries.</div>');
            return;
          }
          $("#rdrrCallDrawerBody").html(kind === "oos" ? renderOosTable(data) : renderCalledTable(data));
        },
        error: function () {
          $("#rdrrCallDrawerBody").html('<div class="empty">Failed to load details.</div>');
        }
      });
    }

    function renderOosTable(data) {
      var html = '<table><thead><tr><th>S.No</th><th>Party Name</th><th>Code</th><th>Time</th></tr></thead><tbody>';
      data.forEach(function (r, i) {
        html += '<tr><td>' + (i + 1) + '</td><td>' + escapeHtml(r.partyName || "-") +
                '</td><td>' + escapeHtml(r.code || "-") + '</td><td>' + escapeHtml(r.time || "-") + '</td></tr>';
      });
      return html + '</tbody></table>';
    }

    function renderCalledTable(data) {
      var html = '<table><thead><tr>' +
              '<th>S.No</th><th>Party Name</th><th>Code</th><th>Remark</th>' +
              '<th>Call Status</th><th>Duration</th><th>Date/Time</th><th>Recording</th>' +
              '</tr></thead><tbody>';
      data.forEach(function (r, i) {
        var rowStyle = "";
        var recording = "-";
        if (r.breakLog) {
          var status = (r.breakStatus || "").toLowerCase();
          if (status.indexOf("available") >= 0) rowStyle = 'style="background:#fdecec;color:#721c24"';
          else if (status.indexOf("trying") >= 0) rowStyle = 'style="background:#fff5d6;color:#856404"';
          else if (status.indexOf("logged out") >= 0 || status.indexOf("logout") >= 0) rowStyle = 'style="background:#eef0f4;color:#383d41"';
          else if (status.indexOf("break") >= 0) rowStyle = 'style="background:#ffe5cc;color:#c45200"';
        }
        if (r.recordingUrl) {
          recording = '<audio controls preload="none" src="' + escapeHtml(r.recordingUrl) + '"></audio>';
        }
        html += '<tr ' + rowStyle + '>' +
                '<td>' + (i + 1) + '</td>' +
                '<td>' + escapeHtml(r.partyName || (r.breakLog ? (r.breakStatus || "-") : "-")) + '</td>' +
                '<td>' + escapeHtml(r.code || "-") + '</td>' +
                '<td>' + escapeHtml(r.remark || "-") + '</td>' +
                '<td>' + escapeHtml(r.callStatus || "-") + '</td>' +
                '<td>' + escapeHtml(r.callDuration || "-") + '</td>' +
                '<td>' + escapeHtml(r.callDateTime || "-") + '</td>' +
                '<td>' + recording + '</td>' +
                '</tr>';
      });
      return html + '</tbody></table>';
    }

    $("#rdrrCallDrawerClose").on("click", function () {
      $("#rdrrCallDrawerBack").removeClass("show");
    });
    $("#rdrrCallDrawerBack").on("click", function (e) {
      if (e.target === this) $(this).removeClass("show");
    });

    // ------------------------- cascading filter (Type → [Level] → Person) -------------------------
    // Flow:
    //   Type = RBM   → Person dropdown = all RBMs. Pick one to filter to that RBM.
    //   Type = SALES → Level dropdown appears (ASM/CM/DM/BM/RSM/NSM). Pick a level
    //                  → Person dropdown = active people at that level. Pick a person
    //                  → fetches /rbm-drr/sales-data and shows that person's aggregated rows.
    // One backend call caches both the RBM list and the Sales list.
    var ALL_RBMS = [];         // [{authId, name}, ...]
    var ALL_SALES_BY_ROLE = {}; // {ASM: [...], CM: [...], ...}

    function loadFilterOptions() {
      $.ajax({
        url: "/rbm-drr/all-sales", type: "GET", dataType: "json",
        success: function (payload) {
          ALL_RBMS = (payload && payload.rbms) || [];
          ALL_SALES_BY_ROLE = {};
          ((payload && payload.sales) || []).forEach(function (s) {
            (ALL_SALES_BY_ROLE[s.role] = ALL_SALES_BY_ROLE[s.role] || []).push(s);
          });
          repopulatePersonForCurrentType();
        }
      });
    }

    // Refill the Person dropdown based on current Type (+ Level if Sales).
    function repopulatePersonForCurrentType() {
      var type = $("#rdrrTypeSelect").val();
      var personSel = $("#rdrrPersonSelect");
      if (type === "RBM") {
        var html = '<option value="">All RBMs (' + ALL_RBMS.length + ')</option>';
        ALL_RBMS.forEach(function (r) {
          html += '<option value="' + r.authId + '">' + escapeHtml(r.name || "Unknown") + '</option>';
        });
        personSel.html(html).prop("disabled", false);
        return;
      }
      // SALES
      var level = $("#rdrrLevelSelect").val();
      if (!level) {
        personSel.html('<option value="">Pick a level first</option>').prop("disabled", true);
        return;
      }
      var list = ALL_SALES_BY_ROLE[level] || [];
      var salesHtml = '<option value="">All ' + escapeHtml(level) + 's (' + list.length + ')</option>';
      list.forEach(function (s) {
        salesHtml += '<option value="' + s.authId + '">' + escapeHtml(s.name || "Unknown") + '</option>';
      });
      personSel.html(salesHtml).prop("disabled", false);
    }

    // Fetches (or refetches) /rbm-drr/sales-data for the current date range,
    // then renders. Called on Level change and on Apply while Sales view active.
    function fetchAndRenderSales() {
      $("#rdrrTableWrap").html('<div class="loading"><div class="spinner"></div>Loading&hellip;</div>');
      $.ajax({
        url: "/rbm-drr/sales-data", type: "GET",
        data: {startDate: startDate, endDate: endDate}, dataType: "json",
        success: function (payload) {
          SALES_PAYLOAD = payload;
          renderFiltered();
        },
        error: function () {
          SALES_PAYLOAD = {rows: []};
          renderFiltered();
        }
      });
    }

    // Type toggles: show/hide Level; reset filter state; refresh Person options.
    $("#rdrrTypeSelect").on("change", function () {
      var type = $(this).val();
      if (type === "SALES") {
        $("#rdrrLevelSelect").css("display", "").val("");
      } else {
        $("#rdrrLevelSelect").css("display", "none").val("");
      }
      // Clear all filter state whenever the type changes.
      CURRENT_FILTER_AUTH_IDS = null;
      CURRENT_SALES_ROLE = null;
      CURRENT_SALES_AUTH_ID = null;
      SALES_PAYLOAD = null;
      repopulatePersonForCurrentType();
      renderFiltered();
    });

    $("#rdrrLevelSelect").on("change", function () {
      var level = $(this).val();
      // Reset the person-specific filter; role determines the view.
      CURRENT_FILTER_AUTH_IDS = null;
      CURRENT_SALES_AUTH_ID = null;
      CURRENT_SALES_ROLE = level || null;
      repopulatePersonForCurrentType();
      if (level) {
        // Fetch Sales-data once, then all subsequent role/person filtering is client-side.
        fetchAndRenderSales();
      } else {
        SALES_PAYLOAD = null;
        renderFiltered();
      }
    });

    $("#rdrrPersonSelect").on("change", function () {
      var authId = $(this).val();
      var type = $("#rdrrTypeSelect").val();

      if (type === "RBM") {
        // RBM filter (single authId) — no server round trip needed.
        CURRENT_SALES_ROLE = null;
        CURRENT_SALES_AUTH_ID = null;
        SALES_PAYLOAD = null;
        if (!authId) {
          CURRENT_FILTER_AUTH_IDS = null;
        } else {
          var single = {};
          single[String(authId)] = true;
          CURRENT_FILTER_AUTH_IDS = single;
        }
        renderFiltered();
        return;
      }

      // SALES type — role already active from Level dropdown; SALES_PAYLOAD already fetched.
      // Person selection just further filters client-side (no re-fetch).
      CURRENT_FILTER_AUTH_IDS = null;
      CURRENT_SALES_AUTH_ID = authId || null;
      if (SALES_PAYLOAD) {
        renderFiltered();
      } else if (CURRENT_SALES_ROLE) {
        // Edge case: person changed before Level's fetch completed — trigger the fetch.
        fetchAndRenderSales();
      }
    });

    // initial load
    loadFilterOptions();
    populateMonthPicker();
    load();
    loadPurchaseMilestones();
    loadPerformance($("#rdrrMonthPicker").val() || null);
    loadCallTargetIframe();
  })();
</script>
</body>
</html>