Cannabis Freedom Index

Changelog 242f874tree, with Claude Opus 5.5

Profiles: context headings explain on hover, dataset names link to their pages

A context section's name shows what the section covers on hover or focus and leads to the topic's page on a click, without the separate info button; the status chips beside it are labels rather than links. A dataset card's name leads to /datasets/<slug>/, replacing its Map, Chart and About links.

3 files of code, design or docs +19 −26 lines

Code, design and documents 3 files

src/components/CountryArticle.astro +8 −12
@@ -18,5 +18,4 @@ import { references, citeParts, citedKeys } from "../lib/cite.js";
 import { PLANTING_STATUS, STATUS_SECTIONS, PLANTING_TRADE } from "../lib/segment-ids.js";
 import { TREATMENT, punishmentText, punishmentExtra } from "../lib/penalties.js";
-import { viewByMetric } from "../lib/map-views.js";
 import { Icon } from "astro-icon/components";
 import PenaltyTable from "./PenaltyTable.astro";
@@ -69,7 +68,4 @@ const ctxParts = ctxList ? Object.fromEntries(ctxList.filter(Boolean).map((x) =>
 // a maximum fine in the profile's currency: "€5,500", "$300"
 const money = (n, currency) => new Intl.NumberFormat("en-GB", { style: "currency", currency, maximumFractionDigits: 0 }).format(n);
-// a chip inside a context section leads to the world map coloured by the same status
-const mapOf = (metric) => (viewByMetric[metric] ? `${base}map/${viewByMetric[metric].slug}/` : undefined);
-const onMap = (help) => `${help} · See the world map`;
 // the recreational and medical status chips lead to their own context sections, where those exist
 const ctxHas = (key) => Boolean(ctxList?.some((x) => x?.key === key));
@@ -373,23 +369,23 @@ const uncited = srcList.filter((x) => !citedSet.has(refKey(x)));
           {list.map((x) => (
             <div class="segrow ctx-tile" data-seg={`ctx-${x.key}`} id={`seg-ctx-${x.key}`}>
-              <div class="segrow-top"><span>{CONTEXT_ICONS[x.key] && <Icon name={CONTEXT_ICONS[x.key]} class="ctx-icon" aria-hidden="true" />}{x.key === "products" ? x.name : <a class="ctx-topic" href={`${base}context/${x.key.replace(/_/g, "-")}/`} title={`${x.name} in every country`}>{x.name}</a>}<SourceTip label="What this covers" inline>{x.help}</SourceTip></span></div>
+              <div class="segrow-top"><span>{CONTEXT_ICONS[x.key] && <Icon name={CONTEXT_ICONS[x.key]} class="ctx-icon" aria-hidden="true" />}<span class="src-tip src-tip-inline ctx-topic-tip">{x.key === "products" ? x.name : <a class="ctx-topic" href={`${base}context/${x.key.replace(/_/g, "-")}/`}>{x.name}</a>}<span class="src-tip-pop" role="tooltip"><b>What this covers</b> {x.help}{x.key !== "products" && <i>Select for every country</i>}</span></span></span></div>
               {x.national && <p class="ctx ctx-general">For all of {c.name}; nothing written for {sub.name} yet.</p>}
               {x.key === "recreational" && (
-                <div class="d-chips"><a class={`chip s-${v.rec}`} href={mapOf("status:rec")} title={onMap(REC_HELP[v.rec])}>Recreational: {REC_LABEL[v.rec]}</a></div>
+                <div class="d-chips"><span class={`chip s-${v.rec}`} title={REC_HELP[v.rec]}>Recreational: {REC_LABEL[v.rec]}</span></div>
               )}
               {x.key === "medical" && (
-                <div class="d-chips"><a class={`chip s-${v.med}`} href={mapOf("status:med")} title={onMap(MED_HELP[v.med])}>Medical: {MED_LABEL[v.med]}</a></div>
+                <div class="d-chips"><span class={`chip s-${v.med}`} title={MED_HELP[v.med]}>Medical: {MED_LABEL[v.med]}</span></div>
               )}
               {x.status && (
                 <div class="d-chips">
-                  <a class={`chip s-${STATUS_SECTIONS[x.kind].statuses[x.status].tone}`} href={mapOf(`status:${x.kind}`)} title={onMap(STATUS_SECTIONS[x.kind].statuses[x.status].help)}>{STATUS_SECTIONS[x.kind].chip}: {STATUS_SECTIONS[x.kind].statuses[x.status].label}</a>
+                  <span class={`chip s-${STATUS_SECTIONS[x.kind].statuses[x.status].tone}`} title={STATUS_SECTIONS[x.kind].statuses[x.status].help}>{STATUS_SECTIONS[x.kind].chip}: {STATUS_SECTIONS[x.kind].statuses[x.status].label}</span>
                 </div>
               )}
               {x.seeds && (
                 <div class="d-chips">
-                  <a class={`chip s-${PLANTING_STATUS[x.seeds].tone}`} href={mapOf("plant:seeds")} title={onMap(PLANTING_STATUS[x.seeds].help[0])}>Seeds: {PLANTING_STATUS[x.seeds].label}</a>
-                  <a class={`chip s-${PLANTING_TRADE[x.seeds_trade].tone}`} href={mapOf("plant:seeds_trade")} title={onMap(PLANTING_TRADE[x.seeds_trade].help[0])}>Seed sales: {PLANTING_TRADE[x.seeds_trade].label}</a>
-                  <a class={`chip s-${PLANTING_STATUS[x.clones].tone}`} href={mapOf("plant:clones")} title={onMap(PLANTING_STATUS[x.clones].help[1])}>Clones: {PLANTING_STATUS[x.clones].label}</a>
-                  <a class={`chip s-${PLANTING_TRADE[x.clones_trade].tone}`} href={mapOf("plant:clones_trade")} title={onMap(PLANTING_TRADE[x.clones_trade].help[1])}>Clone sales: {PLANTING_TRADE[x.clones_trade].label}</a>
+                  <span class={`chip s-${PLANTING_STATUS[x.seeds].tone}`} title={PLANTING_STATUS[x.seeds].help[0]}>Seeds: {PLANTING_STATUS[x.seeds].label}</span>
+                  <span class={`chip s-${PLANTING_TRADE[x.seeds_trade].tone}`} title={PLANTING_TRADE[x.seeds_trade].help[0]}>Seed sales: {PLANTING_TRADE[x.seeds_trade].label}</span>
+                  <span class={`chip s-${PLANTING_STATUS[x.clones].tone}`} title={PLANTING_STATUS[x.clones].help[1]}>Clones: {PLANTING_STATUS[x.clones].label}</span>
+                  <span class={`chip s-${PLANTING_TRADE[x.clones_trade].tone}`} title={PLANTING_TRADE[x.clones_trade].help[1]}>Clone sales: {PLANTING_TRADE[x.clones_trade].label}</span>
                 </div>
               )}

src/components/CountryDatasetTab.astro +4 −11
@@ -4,10 +4,9 @@
 // name, one headline figure and a line of further figures, written as set under `profile` in
 // datasets.yaml; the figure over the years; where the country stands among every country the
-// dataset covers; and links to its map and its entry on the sources page.
+// dataset covers. Its name leads to the dataset's page across every country (/datasets/<slug>/).
 import SourceTip from "./SourceTip.astro";
 import { USE_COMPARISON_NOTE } from "../lib/use-labels.js";
 import { DATASETS, datasetsOf } from "../lib/datasets.js";
-import { viewByMetric } from "../lib/map-views.js";
-import { figure as figureOf, fill as fillOf, headline as headlineOf, headField, TONES, POLARITY, toneAt, CHART_OF, GROUPS, groupOf, standing as standingOf } from "../lib/dataset-text.js";
+import { figure as figureOf, fill as fillOf, headline as headlineOf, headField, TONES, POLARITY, toneAt, GROUPS, groupOf, standing as standingOf } from "../lib/dataset-text.js";
 
 const { c } = Astro.props;
@@ -18,5 +17,4 @@ const figure = (d, row, name) => figureOf(c, d, row, name);
 const fill = (d, row, template) => fillOf(c, d, row, template);
 const headline = (d, row) => headlineOf(c, d, row);
-const mapHref = (d) => (d.map ? `${base}map/${viewByMetric[`data:${d.id}`].slug}/` : (d.pages ?? []).filter((p) => p.path.startsWith("map/")).map((p) => `${base}${p.path}`)[0]);
 
 // The headline's figure over the years, for a small line chart: [{ year, v, t }] with at least two
@@ -58,5 +56,5 @@ const groups = GROUPS.map((name) => ({ name, rows: rows.filter((r) => groupOf(r.
   <section class="c-ds">
     <h2 class="d-h">Datasets <span>not part of the score</span></h2>
-    <p class="d-hint">What other organisations publish about {c.name}, and where it stands among the countries each one covers. Every file is published in full on the <a href={`${base}sources/#datasets`}>sources page</a>.</p>
+    <p class="d-hint">What other organisations publish about {c.name}, and where it stands among the countries each one covers. Each dataset has a <a href={`${base}datasets/`}>page of its own</a>, with a world map and every country's figure.</p>
     {groups.length > 1 && (
       <nav class="ds-nav" aria-label="Dataset groups">
@@ -72,5 +70,5 @@ const groups = GROUPS.map((name) => ({ name, rows: rows.filter((r) => groupOf(r.
               style={stand ? (POLARITY[d.id] ? `--ds:${toneAt(stand.at, POLARITY[d.id])};--lo:var(--t${POLARITY[d.id] > 0 ? 1 : 5});--hi:var(--t${POLARITY[d.id] > 0 ? 5 : 1})` : `--ds:var(--u${stand.step})`) : d.id === "cannabis_use" ? "position:relative" : undefined}>
               <p class="ds-kicker"><span>{d.credit ?? d.publisher}{year != null && ` · ${year}`}</span></p>
-              <h4>{d.name}</h4>
+              <h4><a href={`${base}datasets/${d.id.replace(/_/g, "-")}/`}>{d.name}</a></h4>
               <p class:list={["ds-figure", { words: d.fields[headField(d)]?.type === "text" }]} title={stand?.label ? `On the map: ${stand.label}` : undefined}><b>{head[0]}</b>{head[1] && <span>{head[1]}</span>}</p>
               {chart && (
@@ -97,9 +95,4 @@ const groups = GROUPS.map((name) => ({ name, rows: rows.filter((r) => groupOf(r.
               {details && <p class="ds-details">{details}</p>}
               {note && (d.id === "cannabis_use" ? <SourceTip>{note}</SourceTip> : <p class="ds-details ds-note">{note}</p>)}
-              <p class="ds-links">
-                {mapHref(d) && <a href={mapHref(d)}>Map</a>}
-                {CHART_OF[d.id] && <a href={`${base}charts/#${CHART_OF[d.id]}`}>Chart</a>}
-                <a href={`${base}sources/#dataset-${d.id}`}>About the data</a>
-              </p>
             </li>
           ))}

src/styles/global.css +7 −3
@@ -1251,7 +1251,7 @@ details.segrow .segrow-top > span, .segrow:not(details) .segrow-top > span { fon
 .ds-details { margin: 2px 0 0; color: var(--muted); font-size: 12.5px; line-height: 1.5; }
 .ds-note { color: var(--muted); }
-.ds-links { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 0; padding-top: 4px; font: 400 10.5px var(--mono); text-transform: uppercase; letter-spacing: .06em; }
-.ds-links a { color: var(--muted); }
-.ds-links a:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
+/* the dataset's name leads to its page across every country */
+.ds-card h4 a { color: inherit; }
+.ds-card h4 a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
 @media (max-width: 640px) { .ds-figure b { font-size: 24px; } }
 .cd-stack { display: flex; height: 10px; overflow: hidden; border-radius: var(--r); background: var(--border); }
@@ -3504,4 +3504,8 @@ a.ch-db-track:hover { background: color-mix(in srgb, var(--text) 6%, transparent
 a.ctx-topic { color: inherit; }
 a.ctx-topic:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
+/* a context section's name: what the section covers on hover or focus, the topic's page on a click */
+.src-tip.ctx-topic-tip { margin-left: 0; vertical-align: baseline; }
+.ctx-topic-tip:has(a:focus-visible) .src-tip-pop { display: block; }
+.ctx-topic-tip .src-tip-pop i { display: block; margin-top: 6px; font: 400 10.5px var(--mono); font-style: normal; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
 /* the row of topics at the top of the context pages */
 .ctxp-menu { margin: 18px 0 0; border-bottom: 1px solid var(--border); }

    ↑↓ to move, ↵ to open.