<div class="njvs-wrap" id="njvs-widget">
<div class="njvs-card">
<div class="njvs-head">
<h2 class="njvs-title">NJ Dispensary Vetting Scorecard</h2>
<p class="njvs-subtitle">Answer a few checklist items, then generate a one-page Vetting Report you can copy, print to PDF, or share via link.</p>
</div>
<div class="njvs-info" role="note" aria-label="Important info">
<div class="njvs-info-title">How this works</div>
<div class="njvs-info-text">This scorecard is a practical risk screen—not legal advice. “Green” doesn’t guarantee payment or compliance; it means fewer unanswered questions. Always verify NJ licensure and receiving requirements directly with the dispensary.</div>
</div>
<form class="njvs-form" id="njvs-form" aria-label="NJ dispensary vetting form">
<div class="njvs-group">
<label class="njvs-label" for="njvs-disp-name">Dispensary / Retailer name</label>
<input class="njvs-input" id="njvs-disp-name" aria-label="Dispensary name" type="text" placeholder="Example: Garden State Wellness" />
</div>
<div class="njvs-group">
<label class="njvs-label" for="njvs-disp-location">City (optional)</label>
<input class="njvs-input" id="njvs-disp-location" aria-label="Dispensary city" type="text" placeholder="Example: Newark" />
</div>
<div class="njvs-group">
<label class="njvs-label" for="njvs-ship-type">What are you shipping?</label>
<select class="njvs-select" id="njvs-ship-type" aria-label="Shipment type">
<option value="flower">Flower</option>
<option value="vape">Vape</option>
<option value="edibles">Edibles</option>
<option value="accessories">Accessories / Non-cannabis</option>
</select>
</div>
<div class="njvs-group">
<label class="njvs-label" for="njvs-your-company">Your company (optional)</label>
<input class="njvs-input" id="njvs-your-company" aria-label="Your company" type="text" placeholder="Example: ABC Brands LLC" />
</div>
<hr class="njvs-divider" />
<div class="njvs-section-title">Vetting checklist (10–15 prompts)</div>
<div class="njvs-q" data-field="licenseVerified">
<label class="njvs-label" for="njvs-license-verified">1) NJ license verified on an official source</label>
<select class="njvs-select" id="njvs-license-verified" aria-label="NJ license verified">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
<div class="njvs-help">Tip: verify the license status and confirm it’s the same entity you’re dealing with.</div>
</div>
<div class="njvs-q" data-field="licenseDetailsMatch">
<label class="njvs-label" for="njvs-license-details">2) License details match who you’re speaking with (name/DBA/contact)</label>
<select class="njvs-select" id="njvs-license-details" aria-label="License details match">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="nameAddressMatch">
<label class="njvs-label" for="njvs-name-address">3) Business name + receiving address match (no mismatched suites/LLCs)</label>
<select class="njvs-select" id="njvs-name-address" aria-label="Business name and address match">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="acceptsDeliveries">
<label class="njvs-label" for="njvs-accepts-deliveries">4) They confirm they accept vendor deliveries/shipments</label>
<select class="njvs-select" id="njvs-accepts-deliveries" aria-label="Accepts vendor deliveries">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="appointmentConfirmed">
<label class="njvs-label" for="njvs-appointment">5) Receiving process confirmed (appointment needed / check-in rules)</label>
<select class="njvs-select" id="njvs-appointment" aria-label="Receiving process confirmed">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="coiRequirement">
<label class="njvs-label" for="njvs-coi">6) COI requirement (certificate of insurance)</label>
<select class="njvs-select" id="njvs-coi" aria-label="COI requirement">
<option value="required">Required (confirmed)</option>
<option value="not_required">Not required (confirmed)</option>
<option value="unsure" selected>Unsure</option>
</select>
</div>
<div class="njvs-q" data-field="insuranceConfirmed">
<label class="njvs-label" for="njvs-insurance">7) Insurance requirements are confirmed and you can comply</label>
<select class="njvs-select" id="njvs-insurance" aria-label="Insurance requirements confirmed">
<option value="ok">Confirmed & acceptable</option>
<option value="hard">Confirmed but difficult / may need changes</option>
<option value="unknown" selected>Not confirmed</option>
</select>
</div>
<div class="njvs-q" data-field="paymentTerms">
<label class="njvs-label" for="njvs-payment">8) Payment terms</label>
<select class="njvs-select" id="njvs-payment" aria-label="Payment terms">
<option value="prepay">Paid up front</option>
<option value="delivery">Pay on delivery (Net 0)</option>
<option value="net30">Net 15–30</option>
<option value="net45">Net 45+ (higher risk)</option>
<option value="unsure" selected>Unsure</option>
</select>
</div>
<div class="njvs-q" data-field="intakeHours">
<label class="njvs-label" for="njvs-intake">9) Intake hours confirmed (days/times they actually receive)</label>
<select class="njvs-select" id="njvs-intake" aria-label="Intake hours confirmed">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="packagingRules">
<label class="njvs-label" for="njvs-packaging">10) Packaging/labeling rules received in writing (or portal requirements)</label>
<select class="njvs-select" id="njvs-packaging" aria-label="Packaging rules received">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="returnPolicy">
<label class="njvs-label" for="njvs-returns">11) Return / rejection policy is clear (damaged, late, noncompliant)</label>
<select class="njvs-select" id="njvs-returns" aria-label="Return policy clear">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="poInvoicing">
<label class="njvs-label" for="njvs-po">12) PO/invoicing process confirmed (who signs, what must be on invoice)</label>
<select class="njvs-select" id="njvs-po" aria-label="PO and invoicing process confirmed">
<option value="yes">Yes</option>
<option value="unsure" selected>Unsure</option>
<option value="no">No</option>
</select>
</div>
<div class="njvs-q" data-field="responsiveness">
<label class="njvs-label" for="njvs-response">13) Primary contact responsiveness</label>
<select class="njvs-select" id="njvs-response" aria-label="Contact responsiveness">
<option value="fast">Responds within 24 hours</option>
<option value="medium">1–3 days</option>
<option value="slow">Slow / unclear</option>
<option value="unsure" selected>Unsure</option>
</select>
</div>
<div class="njvs-group">
<label class="njvs-label" for="njvs-notes">Notes (optional)</label>
<textarea class="njvs-textarea" id="njvs-notes" aria-label="Notes" rows="4" placeholder="Add any context: buyer name, agreed delivery window, special instructions, etc."></textarea>
</div>
<button class="njvs-btn" type="button" id="njvs-generate" aria-label="Generate vetting report">Generate Vetting Report</button>
<button class="njvs-btn njvs-btn-secondary" type="button" id="njvs-reset" aria-label="Reset form">Reset</button>
<div class="njvs-leadGate" id="njvs-leadGate" hidden aria-label="Lead details">
<div class="njvs-leadBox">
<div class="njvs-leadHeading">Your vetting report is ready</div>
<div class="njvs-leadDesc">Enter your business details to view the full score, risk rating, and follow-up checklist.</div>
<div class="njvs-group">
<label class="njvs-label" for="njvs-lead-company">Company name</label>
<input class="njvs-input" id="njvs-lead-company" type="text" autocomplete="organization" placeholder="e.g., Garden State Cannabis LLC" />
</div>
<div class="njvs-group">
<label class="njvs-label" for="njvs-lead-email">Business email</label>
<input class="njvs-input" id="njvs-lead-email" type="email" inputmode="email" autocomplete="email" placeholder="you@company.com" />
<div class="njvs-leadEmailErr" id="njvs-leadEmailErr" role="alert" hidden>Please enter a valid business email.</div>
</div>
<div class="njvs-group">
<label class="njvs-label" for="njvs-lead-role">Which best describes you?</label>
<select class="njvs-select" id="njvs-lead-role">
<option value="">— Select —</option>
<option value="dispensary_retailer">Dispensary / Retailer</option>
<option value="cultivator_processor">Cultivator / Processor</option>
<option value="distributor">Distributor</option>
<option value="brand_manufacturer">Brand / Manufacturer</option>
<option value="consultant_advisor">Consultant / Advisor</option>
<option value="other">Other</option>
</select>
</div>
<button class="njvs-btn" type="button" id="njvs-show-results">Show My Results</button>
<div class="njvs-leadError" id="njvs-leadError" role="alert" hidden></div>
<div class="njvs-leadTrust">CannaBiz Credit Association · Cannabis credit reports & risk monitoring · 30+ state markets</div>
</div>
</div>
<div class="njvs-results" id="njvs-results" hidden aria-label="Vetting report results">
<div class="njvs-results-head">
<div>
<div class="njvs-results-title">Vetting Report</div>
<div class="njvs-results-meta" id="njvs-results-meta"></div>
</div>
<div class="njvs-badges">
<span class="njvs-badge" id="njvs-risk-badge">Risk</span>
<span class="njvs-score" id="njvs-score">Score: —</span>
</div>
</div>
<div class="njvs-callouts" id="njvs-callouts"></div>
<div class="njvs-block">
<div class="njvs-block-title">Follow-ups / missing items</div>
<ul class="njvs-list" id="njvs-missing-list"></ul>
</div>
<div class="njvs-block">
<div class="njvs-block-title">One-page report (copy/share/print)</div>
<textarea class="njvs-report" id="njvs-report" aria-label="Generated vetting report text" rows="14" readonly></textarea>
<div class="njvs-actions">
<button class="njvs-btn" type="button" id="njvs-copy" aria-label="Copy report to clipboard">Copy Report</button>
<button class="njvs-btn njvs-btn-secondary" type="button" id="njvs-copy-link" aria-label="Copy shareable link">Copy Shareable Link</button>
<button class="njvs-btn njvs-btn-secondary" type="button" id="njvs-print" aria-label="Print or save as PDF">Print / Save PDF</button>
</div>
<div class="njvs-small" id="njvs-share-hint">Sharing creates a link that stores your answers in the URL hash. Don’t include sensitive info.</div>
</div>
</div>
</form>
</div>
</div>
<style>
/* Scoped styles: njvs- */
.njvs-wrap{max-width:860px;margin:24px auto;padding:12px;box-sizing:border-box;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,"Apple Color Emoji","Segoe UI Emoji";color:#111827;}
.njvs-card{background:#fff;border-radius:12px;padding:40px;box-shadow:0 10px 30px rgba(17,24,39,.08);border:1px solid rgba(17,24,39,.06);}
.njvs-head{text-align:center;margin-bottom:18px;}
.njvs-title{font-size:32px;line-height:1.15;margin:0 0 10px;font-weight:800;letter-spacing:-0.02em;}
.njvs-subtitle{margin:0 auto;color:#6b7280;font-size:16px;max-width:62ch;}
.njvs-info{margin:18px 0 22px;border-left:4px solid #2563eb;background:#f8fafc;padding:14px 14px 14px 14px;border-radius:10px;}
.njvs-info-title{font-weight:700;margin-bottom:4px;}
.njvs-info-text{color:#374151;font-size:14px;line-height:1.45;}
.njvs-form{margin-top:10px;}
.njvs-group,.njvs-q{margin:14px 0;}
.njvs-label{display:block;font-weight:700;margin-bottom:8px;font-size:14px;}
.njvs-help{color:#6b7280;font-size:12px;margin-top:6px;line-height:1.35;}
.njvs-input,.njvs-select,.njvs-textarea{width:100%;box-sizing:border-box;border:1px solid rgba(17,24,39,.18);border-radius:10px;padding:12px 12px;font-size:14px;outline:none;background:#fff;}
.njvs-textarea{resize:vertical;min-height:96px;}
.njvs-input:focus,.njvs-select:focus,.njvs-textarea:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.12);}
.njvs-divider{border:none;border-top:1px solid rgba(17,24,39,.10);margin:20px 0;}
.njvs-section-title{font-weight:800;margin:2px 0 8px;font-size:15px;}
.njvs-btn{width:100%;border:none;border-radius:12px;padding:13px 14px;font-weight:800;font-size:15px;cursor:pointer;background:#2563eb;color:#fff;margin-top:12px;}
.njvs-btn:hover{background:#1d4ed8;}
.njvs-btn:active{transform:translateY(1px);}
.njvs-btn-secondary{background:#111827;color:#fff;}
.njvs-btn-secondary:hover{background:#0b1220;}
.njvs-results{margin-top:22px;border-top:1px solid rgba(17,24,39,.10);padding-top:18px;}
.njvs-results-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;flex-wrap:wrap;}
.njvs-results-title{font-size:20px;font-weight:900;margin:0;}
.njvs-results-meta{color:#6b7280;font-size:13px;margin-top:6px;}
.njvs-badges{display:flex;gap:10px;align-items:center;flex-wrap:wrap;}
.njvs-badge{display:inline-block;padding:8px 10px;border-radius:999px;font-weight:900;font-size:12px;border:1px solid rgba(17,24,39,.12);background:#f3f4f6;}
.njvs-badge.njvs-green{background:#ecfdf5;border-color:#34d399;color:#065f46;}
.njvs-badge.njvs-yellow{background:#fffbeb;border-color:#fbbf24;color:#92400e;}
.njvs-badge.njvs-red{background:#fef2f2;border-color:#fca5a5;color:#991b1b;}
.njvs-score{font-weight:900;color:#111827;font-size:13px;border:1px solid rgba(17,24,39,.12);background:#fff;border-radius:999px;padding:8px 10px;}
.njvs-callouts{margin:12px 0 6px;}
.njvs-callout{border:1px solid rgba(17,24,39,.10);background:#f8fafc;border-radius:12px;padding:12px 12px;margin:10px 0;color:#111827;}
.njvs-callout strong{display:block;margin-bottom:4px;}
.njvs-callout.njvs-callout-warn{border-left:4px solid #f59e0b;}
.njvs-callout.njvs-callout-bad{border-left:4px solid #ef4444;}
.njvs-callout.njvs-callout-good{border-left:4px solid #10b981;}
.njvs-block{margin:14px 0;}
.njvs-block-title{font-weight:900;margin-bottom:8px;}
.njvs-list{margin:0;padding-left:18px;color:#111827;}
.njvs-list li{margin:6px 0;}
.njvs-report{width:100%;box-sizing:border-box;border:1px solid rgba(17,24,39,.18);border-radius:12px;padding:12px;font-size:13px;line-height:1.35;background:#fff;}
.njvs-actions{display:flex;flex-direction:column;gap:10px;margin-top:12px;}
.njvs-small{color:#6b7280;font-size:12px;margin-top:10px;line-height:1.35;}
.njvs-leadGate{margin-top:22px;}
.njvs-leadBox{border:2px solid rgba(37,99,235,.22);border-radius:14px;background:linear-gradient(135deg,rgba(37,99,235,.06),#fff);padding:24px;}
.njvs-leadHeading{font-size:18px;font-weight:800;color:#2563eb;margin-bottom:6px;}
.njvs-leadDesc{font-size:14px;line-height:1.5;color:#6b7280;margin-bottom:16px;}
.njvs-leadEmailErr,.njvs-leadError{margin-top:8px;color:#991b1b;font-size:13px;}
.njvs-leadError{padding:10px 12px;border:1px solid rgba(176,0,32,.22);background:rgba(176,0,32,.05);border-radius:10px;}
.njvs-leadTrust{font-size:11px;color:#9ca3af;text-align:center;margin-top:14px;}
.njvs-missing{border:1px solid rgba(239,68,68,.35);background:#fff7f7;border-radius:12px;padding:12px;}
@media (max-width:640px){
.njvs-card{padding:22px;}
.njvs-title{font-size:26px;}
}
/* Print: focus on the report portion */
@media print{
.njvs-wrap{max-width:none;margin:0;padding:0;}
.njvs-card{box-shadow:none;border:none;padding:0;}
#njvs-form > :not(#njvs-results){display:none !important;}
#njvs-results{border-top:none;padding-top:0;}
.njvs-actions,.njvs-small{display:none !important;}
.njvs-report{border:1px solid #ccc;}
}
</style>
<script>
(function() {
var $ = function(sel, root) { return (root || document).querySelector(sel); };
var $$ = function(sel, root) { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); };
var LEAD_CONFIG = {
API_URL: 'https://cannabiz-leads.vercel.app/api/lead',
SHARED_SECRET: 'cannabiz_lead_2025_Xk9mP7nQ2vR8wL4jT6hY3sF9dA1bC5eG0iN',
DOMAIN_LABEL: 'cannabizcredit.com',
PAGE_ID: 'nj-dispensary-vetting-scorecard'
};
var els = {
widget: $('#njvs-widget'),
form: $('#njvs-form'),
generate: $('#njvs-generate'),
reset: $('#njvs-reset'),
results: $('#njvs-results'),
meta: $('#njvs-results-meta'),
riskBadge: $('#njvs-risk-badge'),
score: $('#njvs-score'),
missingList: $('#njvs-missing-list'),
report: $('#njvs-report'),
copy: $('#njvs-copy'),
copyLink: $('#njvs-copy-link'),
printBtn: $('#njvs-print'),
callouts: $('#njvs-callouts'),
leadGate: $('#njvs-leadGate'),
leadCompany: $('#njvs-lead-company'),
leadEmail: $('#njvs-lead-email'),
leadRole: $('#njvs-lead-role'),
leadEmailErr: $('#njvs-leadEmailErr'),
leadError: $('#njvs-leadError'),
showResults: $('#njvs-show-results')
};
var pendingLeadContext = null;
var leadSubmitted = false;
var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
var fields = {
dispName: $('#njvs-disp-name'),
dispLocation: $('#njvs-disp-location'),
shipType: $('#njvs-ship-type'),
yourCompany: $('#njvs-your-company'),
notes: $('#njvs-notes'),
licenseVerified: $('#njvs-license-verified'),
licenseDetailsMatch: $('#njvs-license-details'),
nameAddressMatch: $('#njvs-name-address'),
acceptsDeliveries: $('#njvs-accepts-deliveries'),
appointmentConfirmed: $('#njvs-appointment'),
coiRequirement: $('#njvs-coi'),
insuranceConfirmed: $('#njvs-insurance'),
paymentTerms: $('#njvs-payment'),
intakeHours: $('#njvs-intake'),
packagingRules: $('#njvs-packaging'),
returnPolicy: $('#njvs-returns'),
poInvoicing: $('#njvs-po'),
responsiveness: $('#njvs-response')
};
var questionConfig = [
{
key: 'licenseVerified',
label: 'NJ license verified on an official source',
weight: 14,
type: 'yn',
criticalYes: true
},
{
key: 'licenseDetailsMatch',
label: 'License details match who you’re speaking with (name/DBA/contact)',
weight: 6,
type: 'yn'
},
{
key: 'nameAddressMatch',
label: 'Business name + receiving address match (no mismatched suites/LLCs)',
weight: 10,
type: 'yn',
criticalYes: true
},
{
key: 'acceptsDeliveries',
label: 'They confirm they accept vendor deliveries/shipments',
weight: 10,
type: 'yn',
criticalYes: true
},
{
key: 'appointmentConfirmed',
label: 'Receiving process confirmed (appointment needed / check-in rules)',
weight: 4,
type: 'yn'
},
{
key: 'coiRequirement',
label: 'COI requirement (certificate of insurance)',
weight: 6,
type: 'map',
map: { required: 6, not_required: 6, unsure: 3 }
},
{
key: 'insuranceConfirmed',
label: 'Insurance requirements are confirmed and you can comply',
weight: 6,
type: 'map',
map: { ok: 6, hard: 3, unknown: 1 }
},
{
key: 'paymentTerms',
label: 'Payment terms',
weight: 10,
type: 'map',
map: { prepay: 10, delivery: 8, net30: 6, net45: 3, unsure: 2 }
},
{
key: 'intakeHours',
label: 'Intake hours confirmed (days/times they actually receive)',
weight: 6,
type: 'yn'
},
{
key: 'packagingRules',
label: 'Packaging/labeling rules received in writing (or portal requirements)',
weight: 8,
type: 'yn'
},
{
key: 'returnPolicy',
label: 'Return / rejection policy is clear (damaged, late, noncompliant)',
weight: 6,
type: 'yn'
},
{
key: 'poInvoicing',
label: 'PO/invoicing process confirmed (who signs, what must be on invoice)',
weight: 6,
type: 'yn'
},
{
key: 'responsiveness',
label: 'Primary contact responsiveness',
weight: 6,
type: 'map',
map: { fast: 6, medium: 4, slow: 2, unsure: 3 }
}
];
function safeEncode(obj) {
var json = JSON.stringify(obj);
// btoa for UTF-8
return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g, function(_, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function safeDecode(str) {
var bin = atob(str);
var esc = Array.prototype.map.call(bin, function(ch) {
var code = ch.charCodeAt(0).toString(16).toUpperCase();
return '%' + (code.length < 2 ? '0' + code : code);
}).join('');
return JSON.parse(decodeURIComponent(esc));
}
function nowStamp() {
try {
return new Date().toLocaleString();
} catch(e) {
return new Date().toString();
}
}
function getTextForSelect(selectEl) {
if (!selectEl) return '';
var opt = selectEl.options[selectEl.selectedIndex];
return (opt && opt.textContent) ? opt.textContent.trim() : '';
}
function ynPoints(val, weight) {
if (val === 'yes') return weight;
if (val === 'unsure') return Math.round(weight * 0.5);
return 0;
}
function classifyRisk(scorePct, criticalFail) {
if (criticalFail) return 'red';
if (scorePct >= 80) return 'green';
if (scorePct >= 60) return 'yellow';
return 'red';
}
function typeSpecificNotes(shipType) {
if (shipType === 'flower') {
return [
'Confirm accepted package sizes/units and any store-specific intake labeling.',
'Confirm whether they require COAs/COIs uploaded to a portal before delivery.'
];
}
if (shipType === 'vape') {
return [
'Confirm any device/battery restrictions and packaging requirements for cartridges/pods.',
'Confirm testing/COA upload requirements before intake.'
];
}
if (shipType === 'edibles') {
return [
'Confirm ingredient/allergen documentation requirements (if applicable).',
'Confirm temperature/handling expectations at receiving.'
];
}
return [
'Confirm whether accessories must be on a separate PO/invoice from regulated products (if applicable).',
'Confirm labeling/UPC requirements and return policy for unsold/damaged units.'
];
}
function collectData() {
var data = {
dispName: (fields.dispName.value || '').trim(),
dispLocation: (fields.dispLocation.value || '').trim(),
shipType: fields.shipType.value,
yourCompany: (fields.yourCompany.value || '').trim(),
notes: (fields.notes.value || '').trim(),
answers: {}
};
questionConfig.forEach(function(q) {
data.answers[q.key] = (fields[q.key] && fields[q.key].value) ? fields[q.key].value : '';
});
return data;
}
function computeScore(data) {
var totalMax = 0;
var totalPoints = 0;
var missing = [];
var criticalFail = false;
questionConfig.forEach(function(q) {
totalMax += q.weight;
var val = data.answers[q.key];
var earned = 0;
if (q.type === 'yn') {
earned = ynPoints(val, q.weight);
if (val !== 'yes') missing.push(q);
if (q.criticalYes && val !== 'yes') criticalFail = true;
} else if (q.type === 'map') {
var mapVal = (q.map && Object.prototype.hasOwnProperty.call(q.map, val)) ? q.map[val] : 0;
earned = mapVal;
// mark missing if unsure/unknown/slow-ish choices
if (val === 'unsure' || val === 'unknown' || val === 'slow' || val === '') missing.push(q);
}
totalPoints += earned;
});
var scorePct = totalMax ? Math.round((totalPoints / totalMax) * 100) : 0;
var risk = classifyRisk(scorePct, criticalFail);
return {
totalMax: totalMax,
totalPoints: totalPoints,
scorePct: scorePct,
risk: risk,
missing: missing,
criticalFail: criticalFail
};
}
function clearMissingHighlights() {
$$('.njvs-q').forEach(function(el) { el.classList.remove('njvs-missing'); });
}
function applyMissingHighlights(missingQs) {
var missingKeys = missingQs.map(function(q) { return q.key; });
$$('.njvs-q').forEach(function(el) {
var key = el.getAttribute('data-field');
if (missingKeys.indexOf(key) !== -1) el.classList.add('njvs-missing');
});
}
function buildReportText(data, result) {
var dispLine = data.dispName || '(Dispensary not entered)';
if (data.dispLocation) dispLine += ' — ' + data.dispLocation;
var shipTypeText = getTextForSelect(fields.shipType);
var lines = [];
lines.push('NJ DISPENSARY VETTING REPORT');
lines.push('Generated: ' + nowStamp());
lines.push('');
lines.push('Dispensary: ' + dispLine);
lines.push('Shipment type: ' + shipTypeText);
if (data.yourCompany) lines.push('Prepared by: ' + data.yourCompany);
lines.push('');
lines.push('Score: ' + result.scorePct + '/100');
lines.push('Risk rating: ' + result.risk.toUpperCase());
if (result.criticalFail) lines.push('Critical flag: One or more must-have items are not confirmed.');
lines.push('');
lines.push('CHECKLIST RESPONSES');
questionConfig.forEach(function(q, idx) {
var val = data.answers[q.key];
var human = '';
if (q.type === 'yn') {
human = (val === 'yes') ? 'Yes' : (val === 'no') ? 'No' : 'Unsure';
} else {
human = getTextForSelect(fields[q.key]);
}
lines.push((idx + 1) + ') ' + q.label + ': ' + human);
});
lines.push('');
lines.push('FOLLOW-UPS (MISSING / NOT CONFIRMED)');
if (!result.missing.length) {
lines.push('- None flagged. Still keep documentation in writing.');
} else {
result.missing.forEach(function(q) {
lines.push('- ' + q.label);
});
}
lines.push('');
lines.push('SHIPMENT-TYPE NOTES');
typeSpecificNotes(data.shipType).forEach(function(n) { lines.push('- ' + n); });
if (data.notes) {
lines.push('');
lines.push('NOTES');
lines.push(data.notes);
}
lines.push('');
lines.push('Disclaimer: This checklist is informational and does not replace legal/compliance guidance or official verification.');
return lines.join('\n');
}
function setRiskBadge(risk) {
els.riskBadge.classList.remove('njvs-green','njvs-yellow','njvs-red');
if (risk === 'green') {
els.riskBadge.textContent = 'GREEN (Lower Risk)';
els.riskBadge.classList.add('njvs-green');
} else if (risk === 'yellow') {
els.riskBadge.textContent = 'YELLOW (Medium Risk)';
els.riskBadge.classList.add('njvs-yellow');
} else {
els.riskBadge.textContent = 'RED (Higher Risk)';
els.riskBadge.classList.add('njvs-red');
}
}
function renderCallouts(data, result) {
els.callouts.innerHTML = '';
var callout = document.createElement('div');
callout.className = 'njvs-callout ' + (result.risk === 'green' ? 'njvs-callout-good' : result.risk === 'yellow' ? 'njvs-callout-warn' : 'njvs-callout-bad');
var headline = document.createElement('strong');
headline.textContent = (result.risk === 'green')
? 'Proceed with normal caution.'
: (result.risk === 'yellow')
? 'Proceed only after closing gaps.'
: 'Do not ship until critical items are confirmed.';
var detail = document.createElement('div');
var detailText = '';
if (result.criticalFail) {
detailText = 'At least one must-have item (license verification, name/address match, or delivery acceptance) is not confirmed.';
} else if (result.missing.length >= 5) {
detailText = 'Several items are not confirmed. Get written confirmation before shipping.';
} else {
detailText = 'Review the follow-ups list and document confirmations in writing.';
}
detail.textContent = detailText;
callout.appendChild(headline);
callout.appendChild(detail);
els.callouts.appendChild(callout);
// Add a small reminder for regulated shipments
if (data.shipType !== 'accessories') {
var reg = document.createElement('div');
reg.className = 'njvs-callout';
var regStrong = document.createElement('strong');
regStrong.textContent = 'Documentation reminder';
var regText = document.createElement('div');
regText.textContent = 'Keep license verification, receiving rules, and COI/insurance requirements in writing (email/portal screenshots).';
reg.appendChild(regStrong);
reg.appendChild(regText);
els.callouts.appendChild(reg);
}
}
function renderMissingList(missingQs) {
els.missingList.innerHTML = '';
if (!missingQs.length) {
var li = document.createElement('li');
li.textContent = 'No missing items flagged by the scorecard.';
els.missingList.appendChild(li);
return;
}
missingQs.forEach(function(q) {
var li = document.createElement('li');
li.textContent = q.label;
els.missingList.appendChild(li);
});
}
function shareUrlForData(data) {
var payload = safeEncode(data);
var base = window.location.origin + window.location.pathname + window.location.search;
return base + '#njvs=' + encodeURIComponent(payload);
}
function copyToClipboard(text, fallbackEl) {
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
// Fallback
if (fallbackEl) {
fallbackEl.removeAttribute('readonly');
fallbackEl.value = text;
fallbackEl.focus();
fallbackEl.select();
try { document.execCommand('copy'); } catch(e) {}
fallbackEl.setAttribute('readonly', 'readonly');
return Promise.resolve();
}
return Promise.resolve();
}
function validateLead() {
var company = String(els.leadCompany.value || '').trim();
var email = String(els.leadEmail.value || '').trim();
var role = els.leadRole.value || '';
var okEmail = emailRe.test(email);
els.leadEmailErr.hidden = okEmail || !email;
if (!company || !okEmail || !role) {
els.leadError.textContent = 'Please fill in all fields with a valid business email.';
els.leadError.hidden = false;
return false;
}
els.leadError.hidden = true;
return true;
}
function buildLeadContext(data, result) {
return {
tool: LEAD_CONFIG.PAGE_ID,
dispensary_name: data.dispName,
dispensary_city: data.dispLocation,
shipment_type: data.shipType,
prepared_by_company: data.yourCompany,
checklist_answers: data.answers,
score: result.scorePct,
risk_rating: result.risk,
critical_fail: result.criticalFail,
missing_items: result.missing.map(function(q) { return q.label; })
};
}
function submitLead(context) {
if (leadSubmitted) return;
leadSubmitted = true;
var payload = {
company: String(els.leadCompany.value || '').trim(),
email: String(els.leadEmail.value || '').trim(),
role: els.leadRole.value || '',
domain: LEAD_CONFIG.DOMAIN_LABEL,
page: LEAD_CONFIG.PAGE_ID,
context: context || {},
utm: {}
};
try {
var sp = new URLSearchParams(window.location.search || '');
['utm_source','utm_medium','utm_campaign','utm_term','utm_content'].forEach(function(key) {
if (sp.get(key)) payload.utm[key] = sp.get(key);
});
} catch(e) {}
(async function() {
try {
var signal = (typeof AbortSignal !== 'undefined' && AbortSignal.timeout) ? AbortSignal.timeout(2000) : undefined;
var ipResponse = await fetch('https://api.ipify.org?format=json', { signal: signal });
var ipData = await ipResponse.json();
payload.ip = ipData.ip;
} catch(e) {}
try {
await fetch(LEAD_CONFIG.API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-shared-secret': LEAD_CONFIG.SHARED_SECRET
},
body: JSON.stringify(payload)
});
} catch(e) {}
})();
}
function revealPendingResult() {
if (!pendingLeadContext) return;
els.leadGate.hidden = true;
els.results.hidden = false;
try { els.results.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch(e) { els.results.scrollIntoView(true); }
}
function generate() {
clearMissingHighlights();
var data = collectData();
if (!data.dispName) {
fields.dispName.focus();
fields.dispName.style.borderColor = 'rgba(239,68,68,.75)';
fields.dispName.style.boxShadow = '0 0 0 3px rgba(239,68,68,.10)';
setTimeout(function(){
fields.dispName.style.borderColor = '';
fields.dispName.style.boxShadow = '';
}, 1200);
return;
}
var result = computeScore(data);
applyMissingHighlights(result.missing);
var shipTypeText = getTextForSelect(fields.shipType);
var meta = [];
meta.push((data.dispName || '').trim());
if (data.dispLocation) meta.push(data.dispLocation);
meta.push('• ' + shipTypeText);
meta.push('• ' + nowStamp());
els.meta.textContent = meta.join(' ');
els.score.textContent = 'Score: ' + result.scorePct + '/100';
setRiskBadge(result.risk);
renderCallouts(data, result);
renderMissingList(result.missing);
var reportText = buildReportText(data, result);
els.report.value = reportText;
pendingLeadContext = buildLeadContext(data, result);
els.results.hidden = true;
els.leadGate.hidden = true;
// Update hash for convenience (but only after user clicks Generate)
try {
var url = shareUrlForData(data);
window.history.replaceState(null, '', url);
} catch(e) {}
if (leadSubmitted) {
revealPendingResult();
} else {
els.leadGate.hidden = false;
try { els.leadGate.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch(e) { els.leadGate.scrollIntoView(true); }
}
}
function resetAll() {
els.form.reset();
clearMissingHighlights();
els.results.hidden = true;
els.leadGate.hidden = true;
els.leadEmailErr.hidden = true;
els.leadError.hidden = true;
pendingLeadContext = null;
els.callouts.innerHTML = '';
els.missingList.innerHTML = '';
els.report.value = '';
try {
window.history.replaceState(null, '', window.location.origin + window.location.pathname + window.location.search);
} catch(e) {
window.location.hash = '';
}
}
function loadFromHashIfPresent() {
var hash = window.location.hash || '';
var m = hash.match(/#njvs=([^&]+)/);
if (!m || !m[1]) return;
try {
var data = safeDecode(decodeURIComponent(m[1]));
if (!data || typeof data !== 'object') return;
fields.dispName.value = data.dispName || '';
fields.dispLocation.value = data.dispLocation || '';
fields.shipType.value = data.shipType || 'flower';
fields.yourCompany.value = data.yourCompany || '';
fields.notes.value = data.notes || '';
if (data.answers && typeof data.answers === 'object') {
questionConfig.forEach(function(q) {
if (fields[q.key] && data.answers[q.key]) fields[q.key].value = data.answers[q.key];
});
}
// Auto-generate so the shared link is immediately useful
generate();
} catch(e) {
// If decode fails, do nothing
}
}
// Events
els.generate.addEventListener('click', generate);
els.reset.addEventListener('click', resetAll);
els.leadEmail.addEventListener('input', function() {
var email = String(els.leadEmail.value || '').trim();
els.leadEmailErr.hidden = emailRe.test(email) || !email;
});
els.showResults.addEventListener('click', function() {
if (!validateLead()) return;
submitLead(pendingLeadContext);
revealPendingResult();
});
els.copy.addEventListener('click', function() {
copyToClipboard(els.report.value, els.report);
});
els.copyLink.addEventListener('click', function() {
var data = collectData();
// allow copying link even if name missing, but link will be less useful
var url = shareUrlForData(data);
copyToClipboard(url, els.report);
});
els.printBtn.addEventListener('click', function() {
// Ensure results visible before printing
if (els.results.hidden) generate();
window.print();
});
// Init
loadFromHashIfPresent();
})();
</script>
Ordering cannabis for delivery is convenient—but only if it’s legal, compliant, and handled safely. If you want to Vet a New Jersey Dispensary Before You Ship, you need more than a nice website and fast delivery claims. New Jersey has clear rules for licensed retailers and delivery, and the state provides practical ways to verify who’s legitimate. Below is a step-by-step checklist you can use before you place (and pay for) a delivery order.
1) Verify the Dispensary Is State-Licensed (Use the NJ-CRC Map First)
The most reliable starting point is the state’s own directory. The New Jersey Cannabis Regulatory Commission (NJ-CRC) instructs consumers to confirm a dispensary is legal by checking whether it’s listed on the NJ-CRC dispensary map. According to the NJ-CRC consumer guidance, all legal dispensaries are added to the map once they’ve informed the agency they are operational. You can search by zip code, city, dispensary type, or even the business name if you’re already on-site.
Action step: before ordering, open the NJ-CRC dispensary lookup at the NJ-CRC “spot a legal dispensary” guidance and follow the link to njcrcgov.info/dispensary (as the state recommends). If the retailer isn’t there, treat it as a major red flag.
What to do if a site claims “licensed” but isn’t listed
The NJ-CRC’s guidance is straightforward: the map is the consumer-facing confirmation tool. If you don’t see the business listed, don’t “take their word for it.” Pause your order and re-check the spelling, city, and dispensary type filters. If it still doesn’t appear, choose a different retailer that is listed.
2) Confirm They Enforce NJ’s ID and Sales Rules (These Are Non-Negotiable)
Legitimate dispensaries in New Jersey are required to run age checks. The NJ-CRC specifically warns that licensed dispensaries must verify every customer is at least 21, and that not asking for ID is a major red flag. That’s not a “store policy”—it’s part of operating legally.
New Jersey’s rules also outline what dispensaries can and can’t sell. Under N.J.A.C. 17:30-9.5 (summarized by Cova), dispensaries may not sell cannabis to anyone under 21, and they may not sell seedlings, immature plants, or live plant material. The same summary notes paraphernalia can’t be sold unless it qualifies as a cannabis product accessory.
A quick “red flag” checklist before you pay
- No ID verification: NJ-CRC says everyone must be carded; if they don’t check, walk away.
- Offers of live plants/seedlings: Cova’s summary of N.J.A.C. 17:30-9.5 says those are not permitted retail items.
- Vague product info: NJ-CRC cautions that unregulated products can skip New Jersey’s required safety testing, labeling, and warnings, which can put consumers at risk.
If you’re trying to Vet a New Jersey Dispensary Before You Ship, these are the easiest compliance signals to check because they show up immediately during ordering and delivery handoff.
3) Validate Delivery Legality and Tracking (Metrc + Licensed Delivery Operators)
In New Jersey, cannabis delivery is allowed statewide—but it must be done the right way. Cova’s regulatory overview notes that delivery is permitted under N.J. Admin. Code 17:30-14.8, and that licensed retailers may deliver through their own employees or through a licensed Class 6 Delivery operator.
Beyond “is delivery allowed,” your next question is: is this delivery operation traceable and compliant? New Jersey uses Metrc as the official seed-to-sale tracking system. Meadow’s New Jersey delivery guide explains that the CRC confirmed Metrc as the state’s tracking system when it selected the platform in January 2022, and that it has been used for enforcement—including placing products on Administrative Hold in Metrc when license violations are found.
What to ask a dispensary (or delivery partner) before you order
- “Are you delivering through your own employees or a licensed Class 6 delivery operator?” (Cova’s summary of N.J. Admin. Code 17:30-14.8)
- “How do you ensure orders are tracked in Metrc?” (Meadow notes Metrc is the official tracking system and used for enforcement.)
- “What privacy measures do you use for vehicles and packaging?” Meadow notes delivery vehicles must remain discreet and unmarked (no cannabis-related branding).
Discreet delivery isn’t just preference—it’s part of compliant operations
Meadow’s guide highlights a key delivery requirement: no cannabis-related branding on vehicles; they must be discreet and unmarked. When a service markets loud vehicle wraps or branded “weed delivery” cars, that conflicts with the discreet-vehicle approach described for compliant New Jersey delivery operations.
Meadow also notes that New Jersey operators must pass a final NJ-CRC inspection before launching delivery, and that the CRC’s compliance focus has intensified—making it smart for consumers to choose delivery programs that clearly take compliance seriously.
4) Check the Business Licenses and Staff Credentials Behind the Counter
Licensing in New Jersey is structured by class, and knowing the basics helps you shop smarter. Flowhub’s overview explains that to open a dispensary in New Jersey, you need a Class 5 Cannabis Retail License. When you see a retailer describing itself as a “dispensary,” it should align with that retail licensing framework.
Staffing compliance matters, too. Cova’s summary of New Jersey rules states that every person involved in a New Jersey cannabis business—including owners, principals, managers, and employees—must obtain a Cannabis Business Identification Card (CBIC) before beginning work (citing N.J.A.C. 17:30-8.1).
Practical ways to vet licensing without being an expert
- Ask what license class they operate under. A legitimate retailer should be able to speak to being a Class 5 retailer (Flowhub).
- Ask about delivery licensing. If they use a third party, that partner should be a licensed Class 6 delivery operator (Cova).
- Look for compliance-forward language. Businesses that emphasize regulated sourcing, testing/labeling, and standard ID verification are aligning with what NJ-CRC warns consumers to look for (NJ-CRC consumer guidance).
Local rules can shape what you see (and where dispensaries operate)
Not every compliance detail is statewide. Cova’s New Jersey overview explains the state does not impose a statewide minimum setback; instead, location requirements are set at the municipal level. Municipalities may enact ordinances requiring buffers from schools, playgrounds, parks, or child daycare facilities—and Cova notes a 600-foot buffer is a common local choice. While this is more relevant for operators than consumers, it’s a helpful clue that legitimate businesses pay attention to municipal zoning and local ordinances.
5) Evaluate the Delivery Experience: Timing, Verification, and Privacy
After you’ve confirmed licensing and compliance basics, assess the “last mile” details—because that’s where many consumer risks show up. A strong delivery program should be clear about delivery windows, order verification, and privacy practices.
For example, Blue Oak Dispensary’s delivery page describes a structured process with same-day delivery timing and customer verification steps. Blue Oak notes orders can arrive in as little as one hour after placement, and provides specific cutoff times for delivery windows (order by 11:45 a.m. for a 12–2 p.m. window, or by 4:45 p.m. for a 5–7 p.m. window). It also states customers receive email and text updates with estimated delivery time, and that a staff member will call to verify order details.
What “good” looks like when you vet the delivery flow
- Clear delivery windows and cutoffs: Blue Oak publishes order-by times and delivery windows rather than vague promises.
- Proactive communication: Blue Oak describes sending ETA updates via email and text.
- Verification step: Blue Oak states a staff member calls to confirm order details, which supports accuracy and reduces misdelivery risk.
- Discreet packaging: Blue Oak emphasizes secure, unmarked packaging—consistent with the discreet approach Meadow describes for delivery operations.
Confirm the delivery partner is licensed (not just “a courier”)
Blue Oak also gives a useful example of how a dispensary may work with a delivery partner: it states it partners with Weedies, described as a woman and veteran-owned cannabis delivery service, and notes the partner is fully licensed by the state of New Jersey. Whether you’re ordering from Blue Oak or another retailer, the takeaway is the same: when a dispensary uses a third party, it should be a properly licensed delivery operator, aligning with the statewide delivery permission framework described by Cova (N.J. Admin. Code 17:30-14.8).
Putting it all together: if you’re trying to Vet a New Jersey Dispensary Before You Ship, you’re looking for a retailer that can be verified on the NJ-CRC map, enforces ID checks, follows delivery licensing rules, and communicates clearly with discreet handling.
Frequently Asked Questions
How can I quickly confirm a dispensary is legal in New Jersey?
Use the NJ-CRC dispensary map referenced in the NJ-CRC consumer guidance on spotting legal dispensaries. The state advises consumers to check njcrcgov.info/dispensary and search by zip code, city, dispensary type, or business name. The NJ-CRC notes legal dispensaries are added once they inform the agency they’re operational.
What’s the biggest red flag when ordering cannabis delivery in NJ?
If they don’t verify your age. NJ-CRC guidance states licensed dispensaries are required by law to check that every customer is at least 21, and warns that a business not asking for ID is a major red flag.
Is cannabis delivery allowed statewide in New Jersey?
Yes. Cova’s overview states delivery is permitted statewide under N.J. Admin. Code 17:30-14.8. It also notes retailers may deliver via their own employees or through a licensed Class 6 Delivery operator.
What is Metrc, and why does it matter when I’m vetting a dispensary?
Metrc is New Jersey’s official seed-to-sale tracking system. Meadow explains the CRC confirmed Metrc as the state’s tracking platform when it selected it in January 2022, and that Metrc has been used for enforcement actions, including placing products on Administrative Hold when violations are found. Asking how a business tracks inventory and deliveries helps you choose a compliance-forward operation.
What license should a New Jersey dispensary have?
Flowhub’s New Jersey overview states that opening a dispensary requires a Class 5 Cannabis Retail License. If a business markets itself as a dispensary, it should align with the Class 5 retail framework, and if it uses a delivery partner, that delivery should align with the licensed delivery structure described by Cova.
If you follow the steps above and use state-provided tools like the NJ-CRC dispensary map, you’ll be in a strong position to Vet a New Jersey Dispensary Before You Ship—and to choose delivery that matches New Jersey’s licensing, tracking, and safety expectations.



















