Skip to content
LAM
Read Home Blog
Make Projects HTML Tools Games
Touch grass Notes Resume Links
Home Blog HTML Projects
Tools Games Notes Resume Links
Back METRC Workflow: Manufacturing + Distribution Programming
Download Open
Show description 2,287 chars · Programming

METRC Workflow: Manufacturing + Distribution

METRC Workflow: Manufacturing + Distribution

METRC OS Map

Licenses
Flow
API
Legal
DB

One brand · two licenses · one audit trail
METRC workflow for manufacturing + distribution

A black-box-to-glass-box map for moving cannabis product legally from manufacturing into distribution, through testing, QA, transfer manifests, and retailer delivery — while keeping the software aligned with METRC as the legal source of truth.

Manufacturing
Distribution
Track & Trace
Audit Logs

The rule

Same brand does not mean same inventory bucket. Each license is its own legal facility context with its own packages, users, transfers, and compliance duties.

Compliance heartbeat

Tagged inventory

24-hour event entry

Manifest before movement

30-day reconciliation

01

Two licenses, two legal worlds

The app should treat manufacturing and distribution as separate legal contexts even when the same brand owns both.

Manufacturing license

Makes product

Accepts incoming source material or bulk cannabis products.

Creates manufacturing batches, production records, yields, waste, and new packages.

Handles extraction, infusion, blending, packaging, labeling, rework, and internal quality checks.

Transfers finished goods to the distribution license with proper package UIDs and manifests.

Distribution license

Moves product

Receives finished goods from manufacturing or other licensed businesses.

Coordinates lab testing, COAs, QA review, inventory release, holds, and rejections.

Creates wholesale transfers and transport manifests to retailers or other licensees.

Controls sellable inventory, retailer delivery, returns, destruction, and reconciliation.

02

Product flow

Click the filters to isolate manufacturing, distribution, QA, or legal gate steps.

All steps
Manufacturing
Distribution
QA / Testing
Legal gates

1. Incoming material

Manufacturer receives source material or bulk product from a licensed source.

MFG + METRC

2. Accept transfer

Incoming transfer is accepted in METRC under the manufacturing facility/license.

UID control

3. Production batch

Extraction, infusion, blending, packaging, labeling, yield, and waste are recorded.

Batch record

4. New packages

Finished goods are created as new package UIDs with item/category details.

Package lineage

5.…

METRC Workflow: Manufacturing + Distribution

40,850 bytes · HTML source
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>METRC Workflow: Manufacturing + Distribution</title>
  <style>
    :root {
      --bg: #050505;
      --paper: #f5f2e8;
      --ink: #050505;
      --white: #ffffff;
      --muted: #cfcfcf;
      --line: #ffffff;
      --red: #ff3b30;
      --yellow: #ffd60a;
      --green: #34c759;
      --blue: #0a84ff;
      --purple: #bf5af2;
      --orange: #ff9f0a;
      --cyan: #64d2ff;
      --shadow: 8px 8px 0 #ffffff;
      --shadow-dark: 8px 8px 0 #000000;
      --radius: 18px;
    }

    * {
      box-sizing: border-box;
    }

    html {
      scroll-behavior: smooth;
    }

    body {
      margin: 0;
      background:
        radial-gradient(circle at top left, rgba(255, 59, 48, 0.22), transparent 28rem),
        radial-gradient(circle at 80% 20%, rgba(10, 132, 255, 0.2), transparent 26rem),
        radial-gradient(circle at 50% 100%, rgba(52, 199, 89, 0.15), transparent 28rem),
        var(--bg);
      color: var(--white);
      font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      line-height: 1.45;
      overflow-x: hidden;
    }

    a {
      color: inherit;
      text-decoration: none;
    }

    .noise {
      position: fixed;
      inset: 0;
      pointer-events: none;
      opacity: 0.08;
      background-image:
        linear-gradient(transparent 50%, rgba(255,255,255,.08) 50%),
        linear-gradient(90deg, transparent 50%, rgba(255,255,255,.05) 50%);
      background-size: 8px 8px;
      z-index: 0;
    }

    .page {
      width: min(1220px, calc(100% - 28px));
      margin: 0 auto;
      position: relative;
      z-index: 1;
      padding: 24px 0 80px;
    }

    .topbar {
      position: sticky;
      top: 10px;
      z-index: 50;
      display: flex;
      gap: 10px;
      align-items: center;
      justify-content: space-between;
      padding: 10px;
      border: 4px solid var(--white);
      border-radius: 999px;
      background: rgba(5,5,5,0.84);
      backdrop-filter: blur(14px);
      box-shadow: 0 0 0 2px #000;
    }

    .brand-pill {
      display: inline-flex;
      align-items: center;
      gap: 10px;
      padding: 8px 14px;
      background: var(--white);
      color: var(--ink);
      border-radius: 999px;
      border: 3px solid var(--ink);
      font-weight: 1000;
      letter-spacing: -0.04em;
      text-transform: uppercase;
      white-space: nowrap;
    }

    .brand-mark {
      width: 18px;
      height: 18px;
      border: 3px solid var(--ink);
      border-radius: 50%;
      background: conic-gradient(var(--red), var(--yellow), var(--green), var(--blue), var(--purple), var(--red));
    }

    .nav {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      justify-content: flex-end;
    }

    .nav a {
      border: 2px solid var(--white);
      border-radius: 999px;
      padding: 8px 12px;
      font-weight: 800;
      font-size: 12px;
      text-transform: uppercase;
      background: #111;
    }

    .nav a:hover {
      background: var(--yellow);
      color: var(--ink);
      border-color: var(--ink);
    }

    .hero {
      margin-top: 34px;
      display: grid;
      grid-template-columns: 1.15fr 0.85fr;
      gap: 22px;
      align-items: stretch;
    }

    .hero-card {
      background: var(--paper);
      color: var(--ink);
      border: 6px solid var(--ink);
      border-radius: var(--radius);
      box-shadow: var(--shadow);
      padding: clamp(24px, 4vw, 54px);
      position: relative;
      overflow: hidden;
      min-height: 460px;
    }

    .hero-card::before {
      content: "";
      position: absolute;
      inset: 16px;
      border: 3px dashed var(--ink);
      border-radius: 12px;
      pointer-events: none;
    }

    .kicker {
      display: inline-flex;
      align-items: center;
      gap: 10px;
      border: 3px solid var(--ink);
      background: var(--yellow);
      border-radius: 999px;
      padding: 8px 14px;
      font-size: 13px;
      font-weight: 1000;
      text-transform: uppercase;
      transform: rotate(-1deg);
      box-shadow: 4px 4px 0 var(--ink);
    }

    h1 {
      margin: 24px 0 16px;
      font-size: clamp(48px, 8vw, 104px);
      line-height: 0.88;
      letter-spacing: -0.085em;
      text-transform: uppercase;
      max-width: 960px;
    }

    .hero-card p {
      max-width: 760px;
      font-size: clamp(17px, 2vw, 22px);
      font-weight: 750;
      margin: 0;
    }

    .hero-badges {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-top: 28px;
    }

    .badge {
      border: 3px solid var(--ink);
      border-radius: 999px;
      padding: 9px 13px;
      background: var(--white);
      color: var(--ink);
      font-size: 12px;
      font-weight: 1000;
      text-transform: uppercase;
      box-shadow: 4px 4px 0 var(--ink);
    }

    .badge.red { background: var(--red); color: white; }
    .badge.blue { background: var(--blue); color: white; }
    .badge.green { background: var(--green); }
    .badge.purple { background: var(--purple); color: white; }

    .side-stack {
      display: grid;
      gap: 18px;
    }

    .status-card {
      border: 5px solid var(--white);
      border-radius: var(--radius);
      background: #101010;
      box-shadow: 7px 7px 0 rgba(255,255,255,.9);
      padding: 22px;
      position: relative;
      overflow: hidden;
    }

    .status-card.light {
      background: var(--white);
      color: var(--ink);
      border-color: var(--ink);
      box-shadow: 7px 7px 0 var(--red);
    }

    .status-card h2,
    .status-card h3 {
      margin: 0 0 12px;
      line-height: 0.95;
      letter-spacing: -0.05em;
      text-transform: uppercase;
    }

    .status-card h2 { font-size: clamp(30px, 4vw, 48px); }
    .status-card h3 { font-size: 28px; }

    .mini-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 10px;
      margin-top: 12px;
    }

    .mini {
      border: 3px solid currentColor;
      border-radius: 12px;
      padding: 12px;
      font-weight: 950;
      text-transform: uppercase;
      font-size: 12px;
      min-height: 78px;
      display: flex;
      align-items: flex-end;
    }

    .mini:nth-child(1) { background: var(--red); color: white; }
    .mini:nth-child(2) { background: var(--yellow); color: black; }
    .mini:nth-child(3) { background: var(--blue); color: white; }
    .mini:nth-child(4) { background: var(--green); color: black; }

    .section {
      margin-top: 58px;
    }

    .section-header {
      display: grid;
      grid-template-columns: auto 1fr;
      gap: 14px;
      align-items: end;
      margin-bottom: 18px;
    }

    .section-number {
      width: 76px;
      height: 76px;
      display: grid;
      place-items: center;
      border: 5px solid var(--white);
      border-radius: 18px;
      font-size: 34px;
      font-weight: 1000;
      background: var(--red);
      box-shadow: 6px 6px 0 var(--white);
    }

    .section-title {
      border-bottom: 5px solid var(--white);
      padding-bottom: 10px;
    }

    .section-title h2 {
      margin: 0;
      font-size: clamp(34px, 5vw, 70px);
      line-height: 0.92;
      letter-spacing: -0.06em;
      text-transform: uppercase;
    }

    .section-title p {
      margin: 8px 0 0;
      color: var(--muted);
      font-size: 15px;
      font-weight: 700;
      max-width: 780px;
    }

    .license-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 22px;
    }

    .license-card {
      border: 5px solid var(--white);
      border-radius: var(--radius);
      background: #0f0f0f;
      box-shadow: 8px 8px 0 var(--white);
      padding: 24px;
    }

    .license-card.manufacturing { border-color: var(--cyan); box-shadow: 8px 8px 0 var(--cyan); }
    .license-card.distribution { border-color: var(--orange); box-shadow: 8px 8px 0 var(--orange); }

    .label-row {
      display: flex;
      justify-content: space-between;
      gap: 12px;
      align-items: flex-start;
      margin-bottom: 16px;
    }

    .label-row h3 {
      margin: 0;
      font-size: clamp(32px, 4vw, 54px);
      letter-spacing: -0.065em;
      line-height: 0.9;
      text-transform: uppercase;
    }

    .tag {
      border: 3px solid var(--white);
      border-radius: 999px;
      padding: 7px 11px;
      font-size: 11px;
      font-weight: 1000;
      white-space: nowrap;
      text-transform: uppercase;
    }

    .manufacturing .tag { background: var(--cyan); color: var(--ink); border-color: var(--ink); }
    .distribution .tag { background: var(--orange); color: var(--ink); border-color: var(--ink); }

    .bullet-list {
      list-style: none;
      margin: 0;
      padding: 0;
      display: grid;
      gap: 10px;
    }

    .bullet-list li {
      border: 3px solid var(--white);
      border-radius: 14px;
      padding: 12px 12px 12px 42px;
      position: relative;
      font-weight: 760;
      background: rgba(255,255,255,0.055);
    }

    .bullet-list li::before {
      content: "✓";
      position: absolute;
      left: 12px;
      top: 10px;
      width: 20px;
      height: 20px;
      display: grid;
      place-items: center;
      border-radius: 50%;
      background: var(--green);
      color: var(--ink);
      border: 2px solid var(--white);
      font-size: 12px;
      font-weight: 1000;
    }

    .flow-board {
      border: 5px solid var(--white);
      border-radius: var(--radius);
      background: var(--white);
      color: var(--ink);
      box-shadow: var(--shadow);
      padding: 20px;
    }

    .flow-controls {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-bottom: 18px;
    }

    button,
    .button {
      appearance: none;
      cursor: pointer;
      border: 3px solid var(--ink);
      border-radius: 999px;
      background: var(--white);
      color: var(--ink);
      padding: 10px 16px;
      font-weight: 1000;
      text-transform: uppercase;
      box-shadow: 4px 4px 0 var(--ink);
      transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
    }

    button:hover,
    .button:hover {
      transform: translate(2px, 2px);
      box-shadow: 2px 2px 0 var(--ink);
    }

    button.active {
      background: var(--yellow);
    }

    .flow {
      display: grid;
      grid-template-columns: repeat(5, 1fr);
      gap: 14px;
    }

    .flow-step {
      border: 4px solid var(--ink);
      border-radius: 18px;
      padding: 16px;
      min-height: 172px;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      background: var(--paper);
      position: relative;
    }

    .flow-step::after {
      content: "→";
      position: absolute;
      right: -22px;
      top: 50%;
      transform: translateY(-50%);
      width: 30px;
      height: 30px;
      display: grid;
      place-items: center;
      border: 3px solid var(--ink);
      background: var(--yellow);
      border-radius: 50%;
      z-index: 2;
      font-weight: 1000;
    }

    .flow-step:nth-child(5)::after,
    .flow-step:nth-child(10)::after {
      display: none;
    }

    .flow-step h4 {
      margin: 0 0 8px;
      font-size: 20px;
      line-height: 0.95;
      letter-spacing: -0.05em;
      text-transform: uppercase;
    }

    .flow-step p {
      margin: 0;
      font-size: 13px;
      font-weight: 750;
    }

    .step-type {
      align-self: flex-start;
      margin-top: 14px;
      border: 2px solid var(--ink);
      border-radius: 999px;
      padding: 5px 8px;
      font-size: 10px;
      font-weight: 1000;
      text-transform: uppercase;
    }

    .flow-step.mfg { background: #dff7ff; }
    .flow-step.dist { background: #fff0d6; }
    .flow-step.qa { background: #e7ffe8; }
    .flow-step.legal { background: #ffe6e3; }

    .flow-step.dimmed {
      opacity: 0.24;
      filter: grayscale(1);
    }

    .split-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 22px;
    }

    .panel {
      border: 5px solid var(--white);
      border-radius: var(--radius);
      background: #0d0d0d;
      box-shadow: 8px 8px 0 var(--white);
      padding: 22px;
    }

    .panel.white {
      background: var(--white);
      color: var(--ink);
      border-color: var(--ink);
      box-shadow: 8px 8px 0 var(--purple);
    }

    .panel h3 {
      margin: 0 0 14px;
      font-size: clamp(28px, 3vw, 44px);
      line-height: 0.95;
      letter-spacing: -0.055em;
      text-transform: uppercase;
    }

    .diagram {
      display: grid;
      gap: 12px;
    }

    .diagram-node {
      border: 4px solid currentColor;
      border-radius: 16px;
      padding: 16px;
      font-weight: 900;
      display: grid;
      grid-template-columns: auto 1fr;
      gap: 12px;
      align-items: center;
      background: rgba(255,255,255,.07);
    }

    .panel.white .diagram-node {
      background: var(--paper);
    }

    .node-dot {
      width: 24px;
      height: 24px;
      border-radius: 50%;
      border: 3px solid currentColor;
      background: var(--yellow);
    }

    .api-section {
      border: 5px solid var(--white);
      border-radius: var(--radius);
      background: var(--paper);
      color: var(--ink);
      box-shadow: var(--shadow);
      padding: 22px;
    }

    .api-toolbar {
      display: grid;
      grid-template-columns: 1fr auto;
      gap: 12px;
      margin-bottom: 16px;
    }

    .search {
      width: 100%;
      border: 4px solid var(--ink);
      border-radius: 999px;
      padding: 12px 16px;
      font-size: 15px;
      font-weight: 800;
      background: var(--white);
      color: var(--ink);
      outline: none;
      box-shadow: 4px 4px 0 var(--ink);
    }

    .endpoint-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 14px;
    }

    .endpoint-card {
      border: 4px solid var(--ink);
      border-radius: 18px;
      background: var(--white);
      padding: 16px;
    }

    .endpoint-card h4 {
      margin: 0 0 10px;
      font-size: 23px;
      letter-spacing: -0.05em;
      line-height: 0.95;
      text-transform: uppercase;
    }

    code {
      display: block;
      white-space: pre-wrap;
      border: 3px solid var(--ink);
      border-radius: 12px;
      background: #0a0a0a;
      color: #ffffff;
      padding: 12px;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
      font-size: 12px;
      line-height: 1.5;
      overflow-x: auto;
    }

    .check-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 16px;
    }

    .check-card {
      border: 5px solid var(--white);
      border-radius: var(--radius);
      background: #111;
      box-shadow: 7px 7px 0 var(--white);
      padding: 18px;
      min-height: 230px;
    }

    .check-card:nth-child(1) { border-color: var(--green); box-shadow: 7px 7px 0 var(--green); }
    .check-card:nth-child(2) { border-color: var(--yellow); box-shadow: 7px 7px 0 var(--yellow); }
    .check-card:nth-child(3) { border-color: var(--red); box-shadow: 7px 7px 0 var(--red); }
    .check-card:nth-child(4) { border-color: var(--blue); box-shadow: 7px 7px 0 var(--blue); }
    .check-card:nth-child(5) { border-color: var(--purple); box-shadow: 7px 7px 0 var(--purple); }
    .check-card:nth-child(6) { border-color: var(--orange); box-shadow: 7px 7px 0 var(--orange); }

    .check-card h4 {
      margin: 0 0 10px;
      font-size: 25px;
      line-height: 0.95;
      text-transform: uppercase;
      letter-spacing: -0.055em;
    }

    .tiny {
      font-size: 12px;
      text-transform: uppercase;
      font-weight: 1000;
      color: var(--muted);
      letter-spacing: 0.06em;
    }

    .risk-board {
      display: grid;
      grid-template-columns: 0.95fr 1.05fr;
      gap: 22px;
    }

    .big-warning {
      border: 6px solid var(--ink);
      background: var(--red);
      color: var(--white);
      border-radius: var(--radius);
      padding: 26px;
      box-shadow: var(--shadow);
    }

    .big-warning h3 {
      font-size: clamp(42px, 5vw, 78px);
      margin: 0 0 14px;
      line-height: 0.84;
      letter-spacing: -0.075em;
      text-transform: uppercase;
    }

    .big-warning p {
      font-size: 18px;
      font-weight: 850;
      margin: 0;
    }

    .contract-list {
      border: 5px solid var(--white);
      border-radius: var(--radius);
      padding: 22px;
      background: #0e0e0e;
      box-shadow: 8px 8px 0 var(--white);
    }

    .contract-list details {
      border: 3px solid var(--white);
      border-radius: 15px;
      margin-bottom: 10px;
      background: rgba(255,255,255,.05);
      overflow: hidden;
    }

    .contract-list summary {
      cursor: pointer;
      padding: 14px 16px;
      font-size: 16px;
      font-weight: 1000;
      text-transform: uppercase;
    }

    .contract-list .detail-body {
      border-top: 3px solid var(--white);
      padding: 14px 16px;
      color: var(--muted);
      font-weight: 700;
    }

    .db-map {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      border: 5px solid var(--white);
      border-radius: var(--radius);
      background: #0d0d0d;
      padding: 20px;
      box-shadow: 8px 8px 0 var(--white);
    }

    .table-pill {
      border: 3px solid var(--white);
      border-radius: 999px;
      padding: 9px 12px;
      font-size: 12px;
      font-weight: 1000;
      text-transform: uppercase;
      background: rgba(255,255,255,.08);
    }

    .table-pill:nth-child(6n+1) { background: var(--red); }
    .table-pill:nth-child(6n+2) { background: var(--yellow); color: var(--ink); }
    .table-pill:nth-child(6n+3) { background: var(--green); color: var(--ink); }
    .table-pill:nth-child(6n+4) { background: var(--blue); }
    .table-pill:nth-child(6n+5) { background: var(--purple); }
    .table-pill:nth-child(6n+6) { background: var(--orange); color: var(--ink); }

    .final-strip {
      margin-top: 58px;
      border: 6px solid var(--ink);
      border-radius: var(--radius);
      background: var(--yellow);
      color: var(--ink);
      box-shadow: var(--shadow);
      padding: 28px;
      display: grid;
      grid-template-columns: 1fr auto;
      gap: 18px;
      align-items: center;
    }

    .final-strip h2 {
      margin: 0;
      font-size: clamp(32px, 5vw, 62px);
      line-height: 0.9;
      letter-spacing: -0.06em;
      text-transform: uppercase;
    }

    .final-strip p {
      margin: 8px 0 0;
      font-weight: 850;
      max-width: 780px;
    }

    .print-button {
      background: var(--ink);
      color: var(--white);
      border-color: var(--ink);
      box-shadow: 4px 4px 0 var(--white);
    }

    @media (max-width: 960px) {
      .hero,
      .split-grid,
      .license-grid,
      .risk-board,
      .final-strip {
        grid-template-columns: 1fr;
      }

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

      .flow-step::after {
        display: none;
      }

      .endpoint-grid,
      .check-grid {
        grid-template-columns: 1fr;
      }

      .topbar {
        align-items: flex-start;
        border-radius: 24px;
        flex-direction: column;
      }

      .nav {
        justify-content: flex-start;
      }
    }

    @media (max-width: 560px) {
      .page {
        width: min(100% - 18px, 1220px);
      }

      .flow,
      .mini-grid,
      .api-toolbar {
        grid-template-columns: 1fr;
      }

      .hero-card,
      .status-card,
      .panel,
      .api-section,
      .license-card,
      .check-card,
      .contract-list,
      .db-map,
      .final-strip {
        box-shadow: 5px 5px 0 var(--white);
      }
    }

    @media print {
      body {
        background: white;
        color: black;
      }

      .noise,
      .topbar,
      .flow-controls,
      .api-toolbar,
      .print-button {
        display: none !important;
      }

      .page {
        width: 100%;
        padding: 0;
      }

      .section,
      .hero {
        break-inside: avoid;
      }

      .hero-card,
      .status-card,
      .license-card,
      .panel,
      .api-section,
      .check-card,
      .contract-list,
      .db-map,
      .final-strip,
      .flow-board {
        box-shadow: none !important;
      }
    }
  </style>
</head>
<body>
  <div class="noise" aria-hidden="true"></div>
  <main class="page">
    <nav class="topbar" aria-label="Page navigation">
      <a href="#top" class="brand-pill"><span class="brand-mark"></span> METRC OS Map</a>
      <div class="nav">
        <a href="#licenses">Licenses</a>
        <a href="#flow">Flow</a>
        <a href="#api">API</a>
        <a href="#legal">Legal</a>
        <a href="#database">DB</a>
      </div>
    </nav>

    <section id="top" class="hero">
      <div class="hero-card">
        <span class="kicker">One brand · two licenses · one audit trail</span>
        <h1>METRC workflow for manufacturing + distribution</h1>
        <p>
          A black-box-to-glass-box map for moving cannabis product legally from manufacturing into distribution, through testing, QA, transfer manifests, and retailer delivery — while keeping the software aligned with METRC as the legal source of truth.
        </p>
        <div class="hero-badges">
          <span class="badge red">Manufacturing</span>
          <span class="badge blue">Distribution</span>
          <span class="badge green">Track & Trace</span>
          <span class="badge purple">Audit Logs</span>
        </div>
      </div>

      <aside class="side-stack">
        <div class="status-card light">
          <h2>The rule</h2>
          <p><strong>Same brand does not mean same inventory bucket.</strong> Each license is its own legal facility context with its own packages, users, transfers, and compliance duties.</p>
        </div>
        <div class="status-card">
          <h3>Compliance heartbeat</h3>
          <div class="mini-grid">
            <div class="mini">Tagged inventory</div>
            <div class="mini">24-hour event entry</div>
            <div class="mini">Manifest before movement</div>
            <div class="mini">30-day reconciliation</div>
          </div>
        </div>
      </aside>
    </section>

    <section id="licenses" class="section">
      <header class="section-header">
        <div class="section-number">01</div>
        <div class="section-title">
          <h2>Two licenses, two legal worlds</h2>
          <p>The app should treat manufacturing and distribution as separate legal contexts even when the same brand owns both.</p>
        </div>
      </header>

      <div class="license-grid">
        <article class="license-card manufacturing">
          <div class="label-row">
            <h3>Manufacturing license</h3>
            <span class="tag">Makes product</span>
          </div>
          <ul class="bullet-list">
            <li>Accepts incoming source material or bulk cannabis products.</li>
            <li>Creates manufacturing batches, production records, yields, waste, and new packages.</li>
            <li>Handles extraction, infusion, blending, packaging, labeling, rework, and internal quality checks.</li>
            <li>Transfers finished goods to the distribution license with proper package UIDs and manifests.</li>
          </ul>
        </article>

        <article class="license-card distribution">
          <div class="label-row">
            <h3>Distribution license</h3>
            <span class="tag">Moves product</span>
          </div>
          <ul class="bullet-list">
            <li>Receives finished goods from manufacturing or other licensed businesses.</li>
            <li>Coordinates lab testing, COAs, QA review, inventory release, holds, and rejections.</li>
            <li>Creates wholesale transfers and transport manifests to retailers or other licensees.</li>
            <li>Controls sellable inventory, retailer delivery, returns, destruction, and reconciliation.</li>
          </ul>
        </article>
      </div>
    </section>

    <section id="flow" class="section">
      <header class="section-header">
        <div class="section-number">02</div>
        <div class="section-title">
          <h2>Product flow</h2>
          <p>Click the filters to isolate manufacturing, distribution, QA, or legal gate steps.</p>
        </div>
      </header>

      <div class="flow-board">
        <div class="flow-controls" role="group" aria-label="Flow filters">
          <button class="active" data-filter="all">All steps</button>
          <button data-filter="mfg">Manufacturing</button>
          <button data-filter="dist">Distribution</button>
          <button data-filter="qa">QA / Testing</button>
          <button data-filter="legal">Legal gates</button>
        </div>

        <div class="flow" id="flowGrid">
          <article class="flow-step mfg" data-type="mfg legal">
            <h4>1. Incoming material</h4>
            <p>Manufacturer receives source material or bulk product from a licensed source.</p>
            <span class="step-type">MFG + METRC</span>
          </article>
          <article class="flow-step mfg" data-type="mfg legal">
            <h4>2. Accept transfer</h4>
            <p>Incoming transfer is accepted in METRC under the manufacturing facility/license.</p>
            <span class="step-type">UID control</span>
          </article>
          <article class="flow-step mfg" data-type="mfg">
            <h4>3. Production batch</h4>
            <p>Extraction, infusion, blending, packaging, labeling, yield, and waste are recorded.</p>
            <span class="step-type">Batch record</span>
          </article>
          <article class="flow-step mfg legal" data-type="mfg legal">
            <h4>4. New packages</h4>
            <p>Finished goods are created as new package UIDs with item/category details.</p>
            <span class="step-type">Package lineage</span>
          </article>
          <article class="flow-step dist legal" data-type="dist legal">
            <h4>5. Transfer to distro</h4>
            <p>Manufacturing creates a transfer/manifest to move goods to the distribution license.</p>
            <span class="step-type">Manifest</span>
          </article>
          <article class="flow-step dist" data-type="dist legal">
            <h4>6. Distro receives</h4>
            <p>Distributor accepts the packages, checks counts, labels, and package status.</p>
            <span class="step-type">Receive</span>
          </article>
          <article class="flow-step qa" data-type="qa dist legal">
            <h4>7. Lab sample / COA</h4>
            <p>Testing workflow links batch, lab sample, COA, release status, and documents.</p>
            <span class="step-type">Testing</span>
          </article>
          <article class="flow-step qa" data-type="qa dist legal">
            <h4>8. QA review</h4>
            <p>Distributor verifies COA, packaging, label data, weight/count, and event history.</p>
            <span class="step-type">Release gate</span>
          </article>
          <article class="flow-step dist" data-type="dist legal">
            <h4>9. Sellable inventory</h4>
            <p>Inventory becomes available only when local records and METRC package state agree.</p>
            <span class="step-type">Reconciled</span>
          </article>
          <article class="flow-step dist legal" data-type="dist legal">
            <h4>10. Retail transfer</h4>
            <p>Distributor creates outgoing transfer/manifest, dispatches, and records delivery.</p>
            <span class="step-type">Wholesale</span>
          </article>
        </div>
      </div>
    </section>

    <section class="section">
      <header class="section-header">
        <div class="section-number">03</div>
        <div class="section-title">
          <h2>Data flow</h2>
          <p>Your software should be the workflow brain, not a shadow version of METRC.</p>
        </div>
      </header>

      <div class="split-grid">
        <article class="panel white">
          <h3>Correct architecture</h3>
          <div class="diagram">
            <div class="diagram-node"><span class="node-dot"></span><span>Local app UI: dashboards, workflows, approvals, alerts.</span></div>
            <div class="diagram-node"><span class="node-dot"></span><span>Local compliance DB: snapshots, audit logs, sync runs, exceptions.</span></div>
            <div class="diagram-node"><span class="node-dot"></span><span>METRC API: legal source of truth for packages, transfers, tests, and events.</span></div>
          </div>
        </article>

        <article class="panel">
          <h3>Reconciliation mindset</h3>
          <ul class="bullet-list">
            <li>Show local count, METRC count, sync timestamp, and status.</li>
            <li>Block transfers when package status, testing status, hold status, or quantity does not match.</li>
            <li>Use overlap windows when syncing LastModified data to avoid clock drift misses.</li>
            <li>Keep every risky action in draft → review → approved → submitted → accepted state.</li>
          </ul>
        </article>
      </div>
    </section>

    <section id="api" class="section">
      <header class="section-header">
        <div class="section-number">04</div>
        <div class="section-title">
          <h2>METRC API surface</h2>
          <p>Core endpoint groups your custom app would likely touch. Search by keyword like packages, transfers, items, lab, or transporters.</p>
        </div>
      </header>

      <div class="api-section">
        <div class="api-toolbar">
          <input id="endpointSearch" class="search" type="search" placeholder="Search endpoints..." />
          <button id="clearSearch" type="button">Clear</button>
        </div>

        <div class="endpoint-grid" id="endpointGrid">
          <article class="endpoint-card" data-keywords="facilities employees permissions setup identity users">
            <h4>Setup / identity</h4>
            <code>GET /facilities/v2/
GET /employees/v2/
GET /employees/v2/permissions</code>
          </article>
          <article class="endpoint-card" data-keywords="items products brands categories catalog sku">
            <h4>Items / products</h4>
            <code>GET  /items/v2/active
GET  /items/v2/inactive
GET  /items/v2/categories
GET  /items/v2/brands
POST /items/v2/
PUT  /items/v2/</code>
          </article>
          <article class="endpoint-card" data-keywords="locations rooms inventory cage storage">
            <h4>Locations</h4>
            <code>GET    /locations/v2/active
POST   /locations/v2/
PUT    /locations/v2/
DELETE /locations/v2/{id}</code>
          </article>
          <article class="endpoint-card" data-keywords="tags package tags plant tags uid labels">
            <h4>Tags</h4>
            <code>GET /tags/v2/package/available
GET /tags/v2/plant/available
GET /tags/v2/staged</code>
          </article>
          <article class="endpoint-card" data-keywords="packages inventory active inactive onhold intransit adjust finish lab samples">
            <h4>Packages / inventory</h4>
            <code>GET  /packages/v2/active
GET  /packages/v2/inactive
GET  /packages/v2/onhold
GET  /packages/v2/intransit
GET  /packages/v2/{label}
POST /packages/v2/
POST /packages/v2/testing
POST /packages/v2/adjust
PUT  /packages/v2/location
PUT  /packages/v2/finish</code>
          </article>
          <article class="endpoint-card" data-keywords="lab tests coa testing results documents release batches">
            <h4>Lab tests / COAs</h4>
            <code>GET  /labtests/v2/states
GET  /labtests/v2/batches
GET  /labtests/v2/types
GET  /labtests/v2/results
GET  /labtests/v2/labtestdocument/{id}
POST /labtests/v2/record
PUT  /labtests/v2/results/release</code>
          </article>
          <article class="endpoint-card" data-keywords="transfers manifests incoming outgoing rejected wholesale delivery transport movement">
            <h4>Transfers / manifests</h4>
            <code>GET  /transfers/v2/incoming
GET  /transfers/v2/outgoing
GET  /transfers/v2/rejected
GET  /transfers/v2/{id}/deliveries
GET  /transfers/v2/deliveries/{id}/packages
GET  /transfers/v2/manifest/{id}/pdf
POST /transfers/v2/templates/outgoing
PUT  /transfers/v2/templates/outgoing</code>
          </article>
          <article class="endpoint-card" data-keywords="transporters drivers vehicles delivery driver vehicle">
            <h4>Transporters</h4>
            <code>GET    /transporters/v2/drivers
POST   /transporters/v2/drivers
PUT    /transporters/v2/drivers
DELETE /transporters/v2/drivers/{id}
GET    /transporters/v2/vehicles
POST   /transporters/v2/vehicles
PUT    /transporters/v2/vehicles</code>
          </article>
        </div>
      </div>
    </section>

    <section id="legal" class="section">
      <header class="section-header">
        <div class="section-number">05</div>
        <div class="section-title">
          <h2>Compliance gates</h2>
          <p>These are the places your app should slow the user down before something dangerous happens.</p>
        </div>
      </header>

      <div class="check-grid">
        <article class="check-card">
          <span class="tiny">Gate 01</span>
          <h4>Receiving</h4>
          <p>Block acceptance until origin, destination, package UID, counts, product type, and license context are confirmed.</p>
        </article>
        <article class="check-card">
          <span class="tiny">Gate 02</span>
          <h4>Manufacturing</h4>
          <p>Require batch record, source package lineage, yield, waste, ingredients/components, and new package creation trail.</p>
        </article>
        <article class="check-card">
          <span class="tiny">Gate 03</span>
          <h4>Adjustments</h4>
          <p>Require reason, before/after quantity, user, approval, METRC submission status, and locked audit event.</p>
        </article>
        <article class="check-card">
          <span class="tiny">Gate 04</span>
          <h4>Testing</h4>
          <p>Do not release inventory until lab sample, COA, result status, and batch/package relationships are clean.</p>
        </article>
        <article class="check-card">
          <span class="tiny">Gate 05</span>
          <h4>QA release</h4>
          <p>Verify COA, labels, package count, weight, item data, expiration, and prior METRC events before retail movement.</p>
        </article>
        <article class="check-card">
          <span class="tiny">Gate 06</span>
          <h4>Transfers</h4>
          <p>Require destination license, driver, vehicle, package list, departure/arrival windows, and generated manifest.</p>
        </article>
      </div>
    </section>

    <section class="section">
      <header class="section-header">
        <div class="section-number">06</div>
        <div class="section-title">
          <h2>Integrator / LLC protections</h2>
          <p>Build like a software vendor, not like the licensee’s compliance department.</p>
        </div>
      </header>

      <div class="risk-board">
        <article class="big-warning">
          <h3>Do not become the legal operator.</h3>
          <p>Your app can assist, validate, queue, and submit. The licensee should remain responsible for compliance decisions, METRC users, approvals, and regulated operations.</p>
        </article>

        <article class="contract-list">
          <details open>
            <summary>Business setup</summary>
            <div class="detail-body">LLC, EIN, business bank account, professional domain email, signed MSA/SOW, NDA, privacy policy, terms, and cannabis attorney review.</div>
          </details>
          <details>
            <summary>Insurance</summary>
            <div class="detail-body">Cyber liability, technology E&O / professional liability, and general liability. Consider higher limits if handling production credentials or compliance records.</div>
          </details>
          <details>
            <summary>API access</summary>
            <div class="detail-body">Use your integrator/vendor access where required. Customer provides user API keys. Never ask for shared passwords. Never hardcode production credentials.</div>
          </details>
          <details>
            <summary>Contract disclaimers</summary>
            <div class="detail-body">State that the customer remains responsible for legal compliance, regulatory submissions, user permissions, review/approval, and maintaining accurate records.</div>
          </details>
          <details>
            <summary>Security</summary>
            <div class="detail-body">Encrypt keys at rest, restrict admin access, use MFA, separate dev/staging/prod, avoid logging sensitive data, and keep incident/backup/restore procedures.</div>
          </details>
        </article>
      </div>
    </section>

    <section id="database" class="section">
      <header class="section-header">
        <div class="section-number">07</div>
        <div class="section-title">
          <h2>Suggested database map</h2>
          <p>Tables that help the app explain what happened, who did it, why it happened, and what METRC returned.</p>
        </div>
      </header>

      <div class="db-map" aria-label="Suggested database tables">
        <span class="table-pill">companies</span>
        <span class="table-pill">licenses</span>
        <span class="table-pill">facilities</span>
        <span class="table-pill">users</span>
        <span class="table-pill">roles</span>
        <span class="table-pill">metrc_credentials_encrypted</span>
        <span class="table-pill">items</span>
        <span class="table-pill">locations</span>
        <span class="table-pill">package_snapshots</span>
        <span class="table-pill">package_lineage</span>
        <span class="table-pill">manufacturing_batches</span>
        <span class="table-pill">batch_production_records</span>
        <span class="table-pill">ingredients</span>
        <span class="table-pill">waste_events</span>
        <span class="table-pill">lab_samples</span>
        <span class="table-pill">lab_results</span>
        <span class="table-pill">coa_documents</span>
        <span class="table-pill">qa_reviews</span>
        <span class="table-pill">transfers</span>
        <span class="table-pill">transfer_packages</span>
        <span class="table-pill">drivers</span>
        <span class="table-pill">vehicles</span>
        <span class="table-pill">sync_runs</span>
        <span class="table-pill">metrc_api_requests</span>
        <span class="table-pill">metrc_api_response_hashes</span>
        <span class="table-pill">compliance_events</span>
        <span class="table-pill">reconciliation_reports</span>
        <span class="table-pill">exceptions</span>
      </div>
    </section>

    <section class="final-strip">
      <div>
        <h2>Build the UI friendly. Build the backend paranoid.</h2>
        <p>Every regulated action should have a human-readable reason, a user, a license context, a METRC endpoint, a response, and an audit event that cannot be quietly rewritten.</p>
      </div>
      <button class="print-button" onclick="window.print()">Print / PDF</button>
    </section>
  </main>

  <script>
    const filterButtons = document.querySelectorAll('[data-filter]');
    const flowSteps = document.querySelectorAll('.flow-step');

    filterButtons.forEach((button) => {
      button.addEventListener('click', () => {
        const filter = button.dataset.filter;
        filterButtons.forEach((btn) => btn.classList.remove('active'));
        button.classList.add('active');

        flowSteps.forEach((step) => {
          const types = step.dataset.type.split(' ');
          const shouldShow = filter === 'all' || types.includes(filter);
          step.classList.toggle('dimmed', !shouldShow);
        });
      });
    });

    const endpointSearch = document.getElementById('endpointSearch');
    const clearSearch = document.getElementById('clearSearch');
    const endpointCards = document.querySelectorAll('.endpoint-card');

    function filterEndpoints() {
      const query = endpointSearch.value.trim().toLowerCase();
      endpointCards.forEach((card) => {
        const text = `${card.textContent} ${card.dataset.keywords}`.toLowerCase();
        card.style.display = !query || text.includes(query) ? '' : 'none';
      });
    }

    endpointSearch.addEventListener('input', filterEndpoints);
    clearSearch.addEventListener('click', () => {
      endpointSearch.value = '';
      filterEndpoints();
      endpointSearch.focus();
    });
  </script>
</body>
</html>