Cannabis Freedom Index

Changelog 5c99bac

add can i

39 history entries added2 country profiles edited5 files of code, design or docs +718 −9 lines

History changes.yaml

Added 39

Countries and states 2 files

Botswana countries/bw.yaml

medical

nonelimited

summary

Under 60 g carries a fine of up to 20,000 pula or up to three years; most small cases end in fines. A 2025 Act allows licensed medical, research and industrial cannabis.

context › production › text

NoneThe Cannabis Act 2025 allows cultivation, manufacture, transport, import and export under licence, with licensing and security requirements set by the Cannabis Regulations 2026.

context › production › sources

tripwiki

context › medical › text

No medical cannabisCannabis is legal for medicinal, scientific, research and industrial purposes within the licensed framework of the Cannabis Act 2025. Recreational use remains illegal.

context › medical › sources

tripwiki

sources

Wikipedia — Cannabis in Botswana 3 unchanged

Germany countries/de.yaml

context › medical › text

Cannabis is no longer a narcotic in law: any doctor can prescribe it on an ordinary prescription, dispensed by pharmacies, and statutory insurers cover it for serious illness after approval. Since 30 July 2026 that cover no longer extends to flower; extracts and other cannabis-based medicines are still reimbursed. A government bill to require an in-person consultation before prescribing, aimed at teleme … put to a final vote before the summer recess and had not been passed as of September 2026.

context › medical › sources

juravendis, medcang, bmg, boc, cansativa

sources

Business of Cannabis — Is cannabis legal in Germany in 2026? 5 unchanged

Code, design and documents 5 files

src/components/CountryArticle.astro +12 −0
@@ -6,4 +6,5 @@ import { tierOf, tierColor } from "../lib/tiers.js";
 import CountryChanges from "./CountryChanges.astro";
 import CountryChart from "./CountryChart.astro";
+import { answersFor } from "../lib/answers.js";
 
 // `sub` is optional: one state, province or territory of `c`. Without it the national view is shown.
@@ -36,4 +37,5 @@ const travel = sub ? null : [
     : `A prescription or medical card from another country is generally not recognised. Travellers who need a cannabis-based medicine should ask ${c.name}'s embassy before travelling.`,
 ].filter(Boolean);
+const answers = answersFor(v.s);
 const r = 42;
 const circ = 2 * Math.PI * r;
@@ -72,4 +74,14 @@ const circ = 2 * Math.PI * r;
       <p class="c-disputed"><strong>Disputed territory.</strong> {c.disputed.note} See also <a href={`${base}country/${c.disputed.country.slug}/`} data-country={c.disputed.country.id}>{c.disputed.country.flag} {c.disputed.country.name}</a>.</p>
     )}
+    <h2 class="d-h">In short <span><a href={`${base}can-i/`}>all countries →</a></span></h2>
+    <dl class="answers">
+      {answers.map((x) => (
+        <div class={`answer a-${x.answer}`}>
+          <dt>{x.q}</dt>
+          <dd><b>{x.label}</b><span>{x.text}</span></dd>
+        </div>
+      ))}
+    </dl>
+    <p class="d-hint">A rough guide read from the scores below, not legal advice. The details and the exceptions are in each segment.</p>
     {use && (
       <dl class="c-use">

src/layouts/Base.astro +1 −0
@@ -41,4 +41,5 @@ const fullTitle = title ? `${title} — Cannabis Freedom Index` : "Cannabis Free
       <a href={`${base}#rankings`}>Rankings</a>
       <a href={`${base}#methodology`}>Methodology</a>
+      <a href={`${base}can-i/`}>Can I?</a>
       <a href={`${base}history/`}>History</a>
       <a href={`${base}compare/`}>Compare</a>

src/lib/answers.js +49 −0
@@ -0,0 +1,49 @@
+// Five plain-language answers per country, read straight off the segment scores.
+// Scores are in the order of segments.yaml: possession, cultivation, enforcement,
+// sharing, consumption, access. See segments.yaml for what each level means.
+
+export const ANSWER_LABEL = { yes: "Yes", limited: "Limited", no: "No" };
+
+const pick = (score, steps) => steps.find(([min]) => score >= min).slice(1);
+
+export const QUESTIONS = [
+  { id: "carry", q: "Can I carry it?", seg: 0, steps: [
+    [5, "yes", "Legal up to a limit"],
+    [3, "limited", "Not a crime, but you can be fined"],
+    [0, "no", "It is a crime"],
+  ] },
+  { id: "grow", q: "Can I grow it?", seg: 1, steps: [
+    [4, "yes", "A few plants at home are legal"],
+    [2, "limited", "A few plants are tolerated"],
+    [0, "no", "It is a crime"],
+  ] },
+  { id: "buy", q: "Can I buy it?", seg: 5, steps: [
+    [6, "yes", "In licensed shops"],
+    [4, "yes", "Through clubs or state outlets"],
+    [3, "limited", "Tolerated shops or clubs, or on prescription"],
+    [1, "limited", "On prescription only"],
+    [0, "no", "There is no legal source"],
+  ] },
+  { id: "public", q: "Can I use it in public?", seg: 4, steps: [
+    [7, "yes", "Wherever smoking is allowed"],
+    [5, "limited", "In some places, with many restrictions"],
+    [3, "no", "In private only"],
+    [0, "no", "Use itself is an offence"],
+  ] },
+  // Not a yes/no question, so each step carries its own short label.
+  { id: "caught", q: "What if I'm caught?", seg: 2, steps: [
+    [6, "yes", "If you stay within the limits", "Nothing"],
+    [4, "limited", "Or a referral to treatment", "A fine"],
+    [2, "limited", "Usually a fine; prison is possible", "Fine or prison"],
+    [1, "no", "Users are routinely jailed", "Prison"],
+    [0, "no", "Long sentences or worse", "Severe punishment"],
+  ] },
+];
+
+// `s` is the array of six segment scores of a country, state or province.
+export const answersFor = (s) =>
+  QUESTIONS.map(({ id, q, seg, steps }) => {
+    const [answer, text, label = ANSWER_LABEL[answer]] = pick(s[seg], steps);
+    return { id, q, answer, text, label };
+  });
+

src/pages/can-i.astro +86 −0
@@ -0,0 +1,86 @@
+---
+// Five plain-language answers for every country, side by side.
+import Base from "../layouts/Base.astro";
+import { places, regions } from "../lib/countries.js";
+import { answersFor, QUESTIONS, ANSWER_LABEL } from "../lib/answers.js";
+
+const base = import.meta.env.BASE_URL;
+const rows = [...places]
+  .sort((a, b) => a.name.localeCompare(b.name))
+  .map((c) => ({ c, answers: answersFor(c.s) }));
+---
+<Base title="Can I?" description="Can I carry, grow, buy or use cannabis in public, and what happens if I'm caught? Five plain answers for every country.">
+  <main class="cani-page">
+    <section class="hero hero-sm">
+      <p class="eyebrow">Can I?</p>
+      <h1>Five plain answers for every country</h1>
+      <p class="lede">Can you carry it, grow it, buy it or use it in public, and what happens if you are caught? The answers are read from each country's scores, so they are a rough guide, not legal advice. Open a country for the details.</p>
+    </section>
+
+    <div class="filters cani-filters">
+      <input id="caniSearch" type="search" placeholder="Search country…" aria-label="Search country">
+      <select id="caniRegion" aria-label="Region">
+        <option value="">All regions</option>
+        {regions.map((r) => <option value={r}>{r}</option>)}
+      </select>
+      <div class="legend cani-legend">
+        {Object.entries(ANSWER_LABEL).map(([k, label]) => <span><i class={`sw a-${k}`}></i>{label}</span>)}
+      </div>
+    </div>
+
+    <div class="card table-card">
+      <div class="table-scroll">
+        <table class="cani" id="caniTable">
+          <thead>
+            <tr>
+              <th scope="col">Country</th>
+              {QUESTIONS.map((x) => <th scope="col">{x.q}</th>)}
+            </tr>
+          </thead>
+          <tbody>
+            {rows.map(({ c, answers }) => (
+              <tr data-name={c.name.toLowerCase()} data-region={c.region}>
+                <th scope="row"><a href={`${base}country/${c.slug}/`}><span class="flag">{c.flag}</span> {c.name}</a></th>
+                {answers.map((x) => (
+                  <td class={`a-${x.answer}`} title={x.text}>
+                    <b>{x.label}</b>
+                    <small>{x.text}</small>
+                  </td>
+                ))}
+              </tr>
+            ))}
+          </tbody>
+        </table>
+      </div>
+    </div>
+    <p class="table-updated" id="caniCount">{rows.length} countries and territories</p>
+  </main>
+
+  <script>
+    const search = document.getElementById("caniSearch");
+    const region = document.getElementById("caniRegion");
+    const rows = [...document.querySelectorAll("#caniTable tbody tr")];
+    const count = document.getElementById("caniCount");
+    const apply = () => {
+      const q = search.value.trim().toLowerCase();
+      let shown = 0;
+      for (const tr of rows) {
+        const ok = tr.dataset.name.includes(q) && (!region.value || tr.dataset.region === region.value);
+        tr.hidden = !ok;
+        if (ok) shown++;
+      }
+      count.textContent = `${shown} of ${rows.length} countries and territories`;
+    };
+    // The filter bar and the table's header row stay in view while scrolling;
+    // they need to know how tall the bars above them are.
+    const measure = () => {
+      const root = document.documentElement.style;
+      root.setProperty("--hdr", `${document.querySelector(".topbar").offsetHeight}px`);
+      root.setProperty("--bar", `${document.querySelector(".cani-filters").offsetHeight}px`);
+    };
+    measure();
+    addEventListener("resize", measure);
+    search.addEventListener("input", apply);
+    region.addEventListener("change", apply);
+  </script>
+</Base>

src/styles/global.css +37 −0
@@ -639,2 +639,39 @@ th.num-col { text-align: right; }
 .change-srcs { margin-top: 4px; font-size: 13.5px; }
 .c-changes-note { margin: 8px 0 0; color: var(--muted); font-size: 12px; }
+
+/* Five plain answers: the block on a country page and the Can I? table */
+.a-yes { --a: var(--t5); }
+.a-limited { --a: var(--t3); }
+.a-no { --a: var(--t1); }
+.answers { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; margin: 0 0 8px; }
+.answer { margin: 0; padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--a) 30%, var(--border)); border-left: 3px solid var(--a); border-radius: 10px; background: color-mix(in srgb, var(--a) 12%, var(--surface)); }
+.answer dt { color: var(--text-2); font-size: 12.5px; margin-bottom: 4px; }
+.answer dd { margin: 0; display: flex; flex-direction: column; gap: 2px; }
+.answer dd b { font-size: 15px; }
+.answer dd span { color: var(--muted); font-size: 12.5px; line-height: 1.35; }
+.d-h span a { color: var(--text-2); }
+.cani-page { max-width: 1160px; margin: 0 auto; padding: 0 20px 64px; }
+.cani-filters {
+  align-items: center; margin: 0 -20px; padding: 10px 20px;
+  position: sticky; top: var(--hdr, 60px); z-index: 6;
+  background: color-mix(in srgb, var(--bg) 92%, transparent); backdrop-filter: blur(10px);
+}
+.cani-page .table-card { overflow: clip; }
+.cani-legend { border: 0; padding: 0; margin-left: auto; }
+.cani-legend .sw { background: var(--a); }
+/* The header row can only stick to the page while the table is not inside a scroll box,
+   so sideways scrolling is kept for narrow screens only. */
+@media (max-width: 899px) { .table-scroll { overflow-x: auto; } }
+@media (min-width: 900px) {
+  table.cani thead th { position: sticky; top: calc(var(--hdr, 60px) + var(--bar, 58px)); z-index: 5; background: var(--surface); box-shadow: 0 1px 0 var(--border); }
+}
+table.cani { min-width: 860px; }
+table.cani th { cursor: default; }
+table.cani th[scope="row"] { text-align: left; font-weight: 500; white-space: nowrap; padding: 10px 14px; border-bottom: 1px solid var(--border); color: var(--text); text-transform: none; letter-spacing: 0; font-size: 14px; }
+table.cani th[scope="row"] a { color: inherit; text-decoration: none; }
+table.cani th[scope="row"] a:hover { text-decoration: underline; }
+table.cani td { border-left: 3px solid var(--a); vertical-align: top; background: color-mix(in srgb, var(--a) 14%, transparent); }
+table.cani tbody tr:hover td { background: color-mix(in srgb, var(--a) 22%, transparent); }
+table.cani td b { display: block; font-size: 13.5px; }
+table.cani td small { display: block; color: var(--text-2); font-size: 12px; line-height: 1.35; }
+table.cani tr[hidden] { display: none; }

    ↑↓ to move, ↵ to open.