Andorra countries/ad.yaml
4522
4855
5637
4885
5337
6018
9532
19999
35802
52616
65703
80727
77397
82904
14.6
2008
8700
Changelog efa0774
196 country profiles edited13 files of code, design or docs +4,322 −138 lines
4522
4855
5637
4885
5337
6018
9532
19999
35802
52616
65703
80727
77397
82904
14.6
2008
8700
48933
52215
56883
61903
67365
74532
131348
286553
1015711
1898220
3493583
6938372
9448529
11513149
5.4
2006
510000
illegaldeath
4707744
5504191
10188845
6626142
6468510
7776180
9035048
11290134
13169313
12045664
20130334
28284088
39068977
43844111
3.81
2015
920000
Trafficking can bring the death penalty.
cann
34575
31659
29765
31089
40909
45462
55621
64549
64531
63087
74929
85337
91864
94209
10.6
2005
7000
805974
881057
941221
995454
1100833
1247854
1649393
2181226
2735424
3277965
3166147
2928731
2871950
2349580
5.6
2014
87000
863227
946846
1038218
1137524
1246328
1382896
1863710
2475628
3107418
3552130
3125630
2931083
2890893
3086700
3.5
2003
72000
3101503
2906100
2838595
3255737
3752594
4551869
5231664
5852790
8133867
11626361
16194873
23294822
33451139
39040039
2.1
1999
430000
4685089
6748544
8818354
11726853
14078649
17018032
20386055
23878330
28011597
32755907
37213986
41288694
45191960
45851378
8.13
2017
2500000
5979176
6571587
6486069
6679644
6725734
6936455
7047274
7466320
7548407
7679627
8012929
8365087
8921402
9208163
6.3
2020
380000
3693331
4329018
5274343
6332771
6967916
8176751
10280815
12599414
14721345
17126304
19132472
22141583
25743787
27614411
11.6
2019
2100000
1938032
2147611
2371595
2598437
2846977
3131979
3982711
5183827
6284707
7231084
8174784
9146852
10181736
10246996
3.5
2004
250000
1764005
1974663
2134369
2300619
2479819
2690407
3281387
3773248
4148427
4448658
4159785
3811480
3299352
3140095
1.6
2011
32000
193451
172186
158593
173809
193407
209928
230901
249398
251533
258583
262071
275251
281706
282623
8.3
2006
15000
illegaldeath
26849595
27913022
28623503
30077163
36071677
41206857
51828663
69058898
88016435
111633722
134544303
152201978
166298024
175686899
3.3
2004
3800000
Courts have discretion to impose the death sentence for more than two kilograms of cannabis, and trafficking carries long terms, life imprisonment or death.
netra, wiki, dt
6705987
7427418
7551028
8029570
8315772
8617442
9102172
9630508
9833093
9960968
10251719
10936619
11540102
11941781
7
2020
530000
2299307
2573061
2816241
3193275
3608646
4213152
4778626
5610307
6947900
9159358
11925548
16176503
21478691
24074580
2.9
2006
390000
3956269
4467209
5025707
5925817
6575077
7248509
7862935
8490822
8859539
8822366
8000519
7437594
6933656
6433302
2.4
2020
98000
illegaldeath
77042
83407
117143
119805
119490
117275
165490
224817
358990
514799
669589
1213248
1483081
1600366
1.9
true
24000
Trafficking, organised smuggling or supplying minors can bring life imprisonment or the death penalty.
cr
1627830
1488732
1429644
1639735
1889974
2254942
2764265
3530431
4383713
5587051
6470196
9376448
12617041
14390003
3.1
true
240000
1187522
1306722
1429764
1650885
1902827
2250476
2517289
3065061
3925258
5281482
7221618
9797488
13070169
14814460
3.1
true
260000
illegaldeath
16978
23145
26970
31492
42042
48316
88359
135157
186121
255307
326429
392340
447411
466330
0.02
1996
67
Trafficking carries a mandatory death sentence on the statute book. Death sentences are imposed but not carried out: Brunei has not executed anyone in decades.
hoc, wiki, trip
1813429
2017450
2354214
2647402
2955228
3089975
3702323
4590008
5758905
7130143
8606330
10182345
11816301
12581843
2.19
2018
180000
18058538
22361184
27593186
33813213
41568218
53408551
72388124
95375656
121207451
149143223
174018278
193701931
208660845
212812405
2.81
2012
4100000
58828
62021
60371
67687
77209
83505
116328
184995
232540
275954
323846
367482
395874
403033
3.1
2017
8800
269139
257966
258029
258029
251726
169991
224098
308536
433412
589295
596871
701638
770013
796682
4.2
2009
24000
165725
194195
227317
278852
340490
413708
516174
590338
937493
1305699
1677384
2033110
2365895
2562122
3.1
true
51000
4986216
5483965
6024473
6600712
7232068
7127595
8208003
9014901
9603173
10186258
9991310
9490752
9350943
9085991
1.1
2007
66000
37771
41386
46640
52902
60990
69085
91330
120746
145072
183090
240809
320256
390819
422924
12.7
2019
37000
5414425
7002961
8571309
10191122
11453308
13741740
17898793
21440022
24533779
27789437
30891800
34196900
38171903
41651653
27
2022
7300000
9451620
8599666
8182274
9220147
10442344
12294293
15264853
20137442
26711098
36684065
50507445
68563038
95990000
112832473
3.1
true
1800000
1043751
997088
966611
1087592
1229063
1438062
1702346
2097513
2450537
2871915
3833421
4493003
5026629
5513282
3.1
true
84000
607115
554548
529118
606145
698649
835269
1056738
1397640
1828991
2380562
3151349
4462304
5752792
6484437
3.1
true
110000
3297641
3708897
3870407
4042131
4233223
4692642
5327777
6180922
6319848
6713168
7184003
7825750
8640582
9092436
9.09
2016
530000
1505099
1588890
1707305
1969853
2270472
2735187
3673672
5411573
8220717
12189780
17699007
22488068
28915452
32711547
3.24
2017
600000
3144546
3569943
4044201
4638243
5492530
6628577
8153356
9845347
11508553
13437181
15503638
17181463
19370621
19859921
12.05
2020
1600000
3216679
2952166
2839117
3205602
3653142
4330088
5159059
6449795
8512217
11331827
14912632
19668063
26210566
29879337
3.1
true
520000
illegaldeath
395929430
419058430
463330522
482831090
511303712
544044355
654802068
823308964
983163540
1153582725
1269581174
1351561505
1426106093
1406585000
0.04
2015
390000
Trafficking in large amounts can bring life imprisonment or death.
lc, wiki
4034733
4944811
6275946
7946343
9311615
11766991
15606209
20811790
26104503
32440064
39089939
44777325
50629997
53425635
2.68
2019
1000000
317246
391337
459026
546929
681697
926761
1327501
1838946
2390641
3121707
3937737
4555965
5034323
5152950
5.18
2015
180000
1719649
2275154
3069258
3923683
4707248
5924519
7238083
8824389
9804798
10631796
11109112
11297414
11176349
10937203
6.1
true
450000
151362
170646
158084
179789
217206
184986
210034
287895
323310
375437
453335
509793
514683
527326
2.4
2012
8600
236693
272882
309680
349917
444169
493883
586493
647443
683082
788355
947667
1127826
1302250
1370754
4
2020
38000
9334000
10036000
9978000
10648000
11160000
8916715
9541605
9801253
10275404
10301989
10236809
10464035
10550129
10886878
11.1
2020
770000
55775391
64056716
63273356
67246048
71740083
69847778
72811635
78205103
78315373
79707963
81797255
80830916
83628711
83491249
7.1
2020
3700000
42320
43547
44643
49022
53375
62349
86040
145188
325822
579544
747326
930252
1105193
1184076
3.1
true
24000
2565333
2886076
3241362
3545296
3844754
4268408
4579347
4928674
5123079
5141024
5339659
5547865
5831529
6009169
6.3
2020
240000
28518
32963
36060
41643
46479
50219
59386
68927
73102
69644
68527
68872
67588
65871
10.8
2006
4900
521386
694364
896843
1267768
1702095
2379896
3268341
4399294
5729043
7151165
8584196
9820180
11008298
11520487
0.68
2010
51000
4721802
5497750
5846192
6530132
7739370
9018423
11424930
13783722
18607175
25375815
30903894
36188237
44042094
47435312
0.52
2010
160000
1451333
1679429
1870163
2052817
2592696
3516791
4590893
6155392
8165882
10473674
12689215
15076697
17546058
18289896
0.67
2013
83000
873409
984932
1105673
1121590
1118777
1102046
1220464
1361872
1476465
1570309
1397001
1331450
1329675
1366475
6.6
2020
57000
illegaldeath
9940289
11361079
12731391
14249967
16423191
21148168
26896480
34623990
43950413
58396625
73083287
89196073
109315118
118365995
5.31
2016
4000000
Cultivation is punished with life imprisonment, or death where an intent to traffic is established. Importing for supply carries life imprisonment or the death penalty.
cr, law, es
571858
578010
591172
650705
708488
814880
972558
1243457
1605800
2032749
2247036
2945184
3291277
3607003
3.1
true
65000
18592411
19859082
21275357
23440849
25747164
28077048
30404938
33812720
37537560
39084975
41019772
46840469
47679482
49355143
10.5
2020
3400000
17719308
17576370
17476313
17476313
17476313
17670418
21376688
27829135
34428515
47609760
67411496
90538518
118917670
135472051
2.6
1999
2000000
2641827
2919587
3134300
3439039
3695558
4008410
4429631
4606308
4779358
4986576
5176197
5363347
5529616
5646436
8.2
2020
280000
126498
147045
167453
210278
272546
309525
404901
531198
641985
773325
840913
910427
914971
933154
5.1
2004
32000
24543
25839
27177
28519
29927
32128
43081
58943
77421
100904
112067
107559
110930
113683
11.6
true
8200
40019136
40344819
38529587
40618294
40337546
41883122
46428136
50813071
53941061
56990239
59483716
63417366
65905273
68720337
11
2020
4600000
369735
336988
320664
360582
407486
472843
512324
595856
748395
983740
1275578
1719883
2322538
2593130
3.1
true
48000
41089602
44723743
46564792
45980369
48108804
50121020
52538548
55663801
56282102
57367867
59057333
63006482
67351860
69487000
7.1
2020
3100000
62362
66402
66457
78401
87959
77280
94074
98431
94554
99973
107466
111600
116353
117303
10.8
2005
8700
2219948
2430789
2662145
2916778
3195768
3497447
4053800
4804060
5165546
5448865
4328361
3895932
3795680
3935766
3.4
2015
85000
2717458
2945929
3173635
3664587
4223839
5080664
6961216
8925813
11941451
15394650
19637085
25475000
31887802
35064272
21.5
1998
4600000
167051
180090
193150
222853
256862
306745
398071
524115
713921
1054703
1455095
1926634
2515744
2822093
3.1
true
50000
1533203
1673930
1845110
2168599
2502427
3013177
3556756
4277540
5047423
6434383
8428837
10396090
13371185
15099727
3.1
true
260000
135206
123323
117411
132027
151826
226570
269813
320815
288652
474289
703287
1189097
1716479
1938431
3.1
true
36000
4990133
5368127
5771532
6426998
7310983
7651080
8384484
8747778
9653419
10258494
10782638
11124666
10699370
10413962
2.8
2020
180000
1352626
1494871
1663732
1857837
2304571
3097199
4063659
5396972
6900215
9024775
11697592
14500118
17357331
18687881
3.48
2014
420000
295214
319975
344458
397429
458081
543632
577634
584409
829314
973678
1234747
1566352
2013259
2249515
3.1
true
41000
297513
296177
295538
303521
339001
418045
569156
702714
774372
749891
764836
749259
807486
835986
4.6
2016
25000
530342
662011
785083
1022849
1246523
1558941
2062067
2750721
3727179
4980493
6577781
8365881
10119638
11005850
0.8
2005
57000
2493105
2725259
2977792
3250582
3548361
3864992
4200272
4462673
4643900
4835067
4326105
4301150
3953956
3876200
10.2
2020
250000
1616961
1883502
2215834
2530442
2873280
3265716
3858447
4635126
5614494
6854444
8303151
9803299
11243846
11906095
2.5
2018
190000
7106097
7604701
7942153
8610564
9216204
9337883
9983383
10315142
10695372
10381280
10190598
9979128
9749459
9514251
1.3
2020
80000
illegaldeath
38179623
42820498
47304303
53422386
61843190
68799031
88296074
115657496
148950537
183501097
216077789
246305327
274814861
285721236
1.44
2019
2800000
Large-scale trafficking can bring life imprisonment or death, and Indonesia retains and uses the death penalty for drug trafficking.
sun, gp
2912656
2912656
2912656
2912656
2912656
2912656
2827691
2961402
3415240
3514015
3802589
4559084
4982602
5484367
7.1
2020
260000
359278
410239
455007
587972
878256
1289244
1891941
2864773
3721666
4544995
6111194
7336735
8800380
10122800
14
2020
850000
280714154
295496971
300639878
330163509
373766276
346278818
435990342
545864271
687354029
864972221
1057922731
1243481565
1402617694
1463865525
3.2
2020
32000000
illegaldeath
2399016
2850254
3459608
3933814
4140032
5653357
7022057
9735611
13591996
17581208
24424060
31045367
42116602
47020774
1.9
true
540000
Importing, exporting or producing with intent to trade, and cultivating narcotic plants for trade, carry death or life imprisonment. In one case three men were sentenced to death over 18 kg of hashish.
hoc, um
illegaldeath
10122370
10967798
9455525
12691310
15178703
16510553
21470433
29145099
39842323
58380098
66418661
77420851
87723448
92417681
0.58
2015
370000
The death penalty applies to large quantities. A 2017 amendment raised the capital threshold for hashish and opium from 5 kg to 50 kg and limited it mainly to armed or organised trafficking, though drug executions have since risen again.
ihr, lc
77854
87565
97534
108477
120892
142684
175572
204489
228147
254842
281293
318163
366626
392404
5.8
2017
15000
33332371
35939804
36967588
40159184
43602046
46391674
49514555
53390231
56412086
57031457
57272200
60164212
59912763
58915656
10.2
2020
3800000
727676
815621
873146
1024958
1226097
1407214
1650009
1878191
2145661
2379880
2607827
2748573
2830745
2837077
18
2016
370000
illegaldeath
254431
273656
196295
233585
246075
438808
853471
1565172
2234192
3621915
5386945
7297046
10865232
11520684
2.1
2001
160000
Trafficking carries long sentences with hard labour, up to life or death in aggravated cases.
wiki, cr
44416502
49880362
56328237
64665549
73591941
86443278
96399755
106712370
118358761
123399767
127027794
128185273
126304533
123366734
0.1
2019
72000
3950614
3633121
3566429
4167035
4808498
5769190
7695310
11368806
16018465
22892655
30642893
41598570
52217327
57532493
1.2
2016
420000
1100576
1205653
1320825
1447162
1585583
1751141
2215517
3025143
3729372
4459545
5025150
5491763
6664143
7343064
6.4
2001
290000
2839483
3012110
2895102
2788985
3061261
4375252
5449964
6639187
5199874
7374754
12462343
14500730
16725484
17847982
3.5
2003
400000
28664
30579
32455
34058
35740
37871
47177
57530
61170
74872
88632
108717
126110
136488
11.6
true
9700
85742
105346
129010
139454
149571
161951
197710
244196
337728
444601
536084
654846
802172
882847
2.9
2002
15000
48203
45331
39746
38573
40766
47254
55834
44632
42866
40456
45272
46871
46896
46922
11.7
2006
3800
illegaldeath
5432032
5724136
6800254
8019424
9457170
11116734
11722950
15172777
18236783
21112134
23665909
24987257
26136312
26571036
1.9
true
350000
Drug offences can bring labour-camp sentences, and executions for drug trafficking have been reported.
lc
9687522
9911592
11464533
13456220
15330291
19757319
26115388
32542203
38141109
44081425
46766660
48769329
51858481
51684564
0.3
2004
110000
illegaldeath
98794
105366
114746
124871
135890
153973
311066
809118
1504789
1684833
1954713
2943374
4400146
4865298
3.1
2005
120000
Trafficking, smuggling or manufacturing can carry the death penalty or life imprisonment, and so can cultivation for trafficking.
trip, wiki, kt
4161983
4543093
4964613
5439477
5959762
6787149
9429982
12419056
15152953
17154060
15501108
16836808
19482116
20843754
2.4
2012
310000
1378713
1272193
1046704
876948
957965
1684556
2121517
2669703
3292575
4311960
5431213
6334198
7346534
7873046
0.9
2008
46000
501417
637099
769131
926073
1115039
1349351
1794411
2386119
2944765
3595175
4329345
5041290
5702400
5849421
1.9
2009
71000
53029
53545
57376
65923
76707
93009
92605
103184
119302
137874
158624
170921
178261
180149
8.87
2010
12000
10331
11121
12070
12176
12869
13786
16512
21170
25114
28912
33137
36021
38793
41024
8.6
2005
2300
4063376
4860106
5500957
5987713
6492745
7811051
9661384
12287113
14847972
16352460
19293056
20879092
22561808
21756000
1.9
2018
270000
490243
533090
575156
663604
764876
918912
1139519
1466677
1929260
2223667
2928117
4058897
5149470
5731206
7.52
2017
250000
249213
281353
320591
393273
480202
579554
736507
1019273
1406530
1809698
2003919
1996256
2235727
2363325
3.1
true
45000
2523151
2850870
2283303
2417070
2453223
2433000
2769099
3135874
3428417
3695016
3500410
3097721
2795766
2888774
2.7
2020
51000
235574
258721
269864
297712
300207
296580
313888
339337
363711
381707
436142
507418
630597
686970
5.4
2020
25000
2148646
2450389
1860798
1875135
1921871
1918725
2131169
2357559
2511756
2660131
2368320
2097834
1901119
1847785
3.9
2020
45000
illegaldeath
514376
598690
679435
760476
876455
1131471
1492890
2082597
3424375
4445243
5305020
6497837
7045403
7458555
0.05
1998
2500
Trafficking can bring life imprisonment or death in aggravated cases.
cr
4681030
5437479
5772593
6447923
7641938
8935456
11624932
15129453
19459942
24375558
28423439
32467009
36584208
38430770
10.47
2017
2700000
21331
24028
23596
26270
25118
19689
21829
24252
27146
30382
32477
33188
38074
38341
10.47
2017
2000
1484973
1628124
1784707
1955414
2142449
2528365
3076172
3643522
4048713
4451150
4234969
3631990
3069135
2360527
0.7
2010
11000
442653
510756
484521
453378
424237
402519
471205
529679
584831
620507
634198
632088
607934
623129
4.6
2017
18000
2678373
2543231
2495724
2865906
3303271
3956077
5111033
6672004
9006039
12055649
16516336
22183343
28953560
32740678
14.2
2019
2700000
39597
50566
64282
75486
31529
13019
14728
22552
30793
44648
50721
52064
42728
36282
5.5
2007
1200
802202
878324
960798
1048814
1144894
1248682
1435060
1664214
1933043
2063690
2057254
2051510
1872021
1820909
6.6
2017
78000
2331296
2707812
3040300
3479504
3971134
4696914
5402550
6276476
7530522
9178826
11559291
15945201
21713837
25198821
7.8
1995
1000000
illegaldeath
9411380
10934246
11923214
13231889
15124546
17737123
21730255
27193015
33227204
39817253
45290850
49024376
53016525
54850648
0.9
2005
340000
Production, sale, import or export carries fifteen years to an unlimited term, or death.
tni
696737
719186
732268
745432
758832
781691
977602
1295275
1701341
2164578
2455182
2701739
3290786
3568978
1.9
true
43000
illegaldeath
359492
388783
413307
476866
549640
657722
824307
1099815
1474046
1951881
2613450
3390968
4600136
5315065
3.1
true
90000
Trafficking is a capital offence on the statute book, and growers and producers can also be sentenced to death, which is unique in the Maghreb. Mauritania has not carried out executions for decades.
gdpo, trip
206234
215840
212759
227340
254503
294685
330832
326017
342882
367913
399905
422654
518212
579704
0.9
2020
3500
392834
395786
406448
424691
454310
525560
680332
827700
954221
1089033
1216632
1282710
1283231
1243741
1.08
2009
9600
illegaldeath
64150
66898
65426
73027
78873
74461
91203
123240
164844
224772
282025
360802
502127
529676
2.55
2012
10000
Recent amendments added the death penalty for bringing more than 350 g of cannabis into the country.
mi, at
2067047
1922933
1868175
2142711
2469709
2950299
3628136
4633949
6265529
9545860
11315947
14826466
19533885
22216120
3.1
true
390000
13261087
14503032
14579605
16689077
19982850
27587941
36724614
50814958
67300338
82820170
98625553
113623898
126799053
131946900
2.1
2016
1900000
illegaldeath
2094024
2684056
3274070
4069257
5001715
6098285
7956201
10178802
12983365
17833179
22967817
28655772
33889554
35977838
1.6
2003
410000
The death penalty remains available for trafficking, which is presumed from 200 g of cannabis. Since July 2023 it is discretionary, not mandatory, and the reform let over a thousand death- row prisoners apply for resentencing.
hrw, mh
4251279
3902611
3756127
4308103
4965561
5910234
6713698
8250504
11336220
13094535
18129652
22999234
30783685
35631653
3.1
true
590000
203718
240833
283647
347953
424866
514598
591473
757876
981440
1369309
1818650
2109815
2728760
3092816
3.9
2000
72000
1320431
1494234
1649641
1887949
2154703
2597818
3505056
4650272
6135968
8286066
11509634
16548833
23717615
27917831
3.1
true
440000
18664170
20655981
22927580
26735105
31085546
37283019
45053787
55893836
73764641
97120926
126382490
166642888
213996185
237527782
19.4
2019
26000000
553531
656534
769730
828973
1008702
1331445
1782371
2423065
3252404
4162566
5022636
5737451
6565267
7007502
1.1
2006
51000
5140189
5914229
6834137
7859443
8868956
10113759
11486909
13052082
14162686
15030886
16058757
16771232
17636732
18087633
10.1
2020
1200000
2232623
2399597
2642453
2820108
2994493
3265199
3581119
3875351
4085603
4241440
4490864
4889160
5379275
5610870
7
2024
260000
5116267
5384368
5360778
5343320
5500086
8396610
10123664
12436191
15553106
19524579
24546509
27341955
28966580
29618118
0.71
2019
140000
1921
1928
1986
2839
2931
3424
4625
6733
7670
9645
10187
10067
11665
12025
11.6
true
830
809099
1037814
1239349
1481347
1638415
1911265
2379482
2822184
3146325
3396981
3857576
4347766
5069895
5324700
15
2020
520000
illegaldeath
379583
412514
421003
431196
441636
456997
530489
662691
1001248
1763957
2282139
2757450
4522502
5494691
0.1
1999
4000
The death penalty applies to smuggling for trafficking in aggravated cases such as repeat offences, abuse of office or involvement with international gangs.
hoc, gn
257249
319341
460090
504023
670153
860855
1126004
1513071
1957543
2455588
3010376
3628537
4293258
4571189
0.77
2015
23000
3657023
4137540
4699402
5483224
6444987
7677414
10174128
13501237
17360955
22014772
26598328
29086021
32838577
34576665
1.62
2017
370000
841827
833850
554530
592614
953522
1543441
1995115
2505632
3131516
3896482
5537068
7633524
9815743
10762817
29.5
1995
2000000
6644021
7835476
9347641
11504117
14454975
18849532
27891900
37622203
48043744
62854668
79625401
96337127
112081270
116786962
1.64
2016
1300000
illegaldeath
18263133
19230643
21780196
26064359
31284924
35849256
45709314
60166721
82291008
116155584
154879124
199239048
235001745
255219554
3.6
2012
5500000
Trafficking carries sentences up to life or death for large quantities.
shc
24312686
25809505
23791651
27385667
28883551
24780169
29575967
32542639
35576518
38055448
38258077
38095356
38171005
36435861
3.8
2020
900000
291751
337256
353234
463247
719218
944632
1073080
1116285
1469058
2139065
3151800
4016918
5069692
5413596
1.9
true
60000
5402551
5854984
6054309
6777114
7651628
8418877
8873223
8669523
9763622
9989526
10283196
10578659
10370519
10804871
5.1
2020
340000
5745
6038
6344
6657
6986
7494
9349
11393
12296
15282
19206
18420
17817
17663
24.2
2007
3000
463602
564592
713685
898760
1138105
1504351
1883517
2388030
3053239
4035752
5100875
5737973
6603741
7013078
1.6
2005
73000
illegaldeath
16061
16905
18241
19851
21603
24304
36025
116370
275923
440184
644993
1709238
2803375
2972215
0.1
1996
2500
Repeat traffickers face life imprisonment or death.
cann
11005350
11832057
12403390
14116480
15807371
16397275
18313498
20011626
22154776
22939530
22046264
20432575
19392467
19020271
3.5
2020
430000
2313842
2665974
3237697
3989085
4914850
6009201
6702964
7324426
7796286
7874595
7694608
7395872
6907814
6549143
2
2018
82000
64843834
70982800
77706431
85188053
93390010
103392364
119934143
130367188
138572628
148979537
146717784
143934468
146371295
143513328
3.5
2007
3300000
1515744
1401547
1357035
1556456
1793986
2157101
3022300
3980673
5353263
7375313
8213936
10317560
13065838
14569341
3.1
true
270000
illegaldeath
2123533
2188136
2331840
2555457
2800517
1703280
2435410
3562475
6047997
10640697
16177733
25157134
30991206
36973555
0.3
2006
81000
Article 37 permits death sentences for importing, exporting, manufacturing or receiving drugs for distribution, although a court may reduce the sentence to at least 15 years in prison. Executions resumed in 2022, and people were executed for hashish smuggling as recently as July 2026.
law, un, amnesty, hrw
133028
146847
144333
95207
93992
108650
139698
176590
238149
331448
440481
532976
744505
838645
11.6
true
58000
21066
25794
28579
31053
36286
38306
43249
54276
66281
74000
83339
94993
120299
122730
3.1
true
2700
illegaldeath
5164788
5255604
5388664
5633637
5889749
6189915
8364491
11695768
17058908
22021484
27816748
35414403
46789237
51662147
3.1
true
900000
In aggravated cases trafficking carries the death penalty, under sharia-influenced provisions.
cr, wiki
5123503
5463482
5876734
6141408
6393377
7016565
7484597
8042783
8310470
8558571
8872099
9378234
10353682
10596620
3.8
2020
250000
illegaldeath
212200
311561
386317
574238
735023
1013318
1619328
2091568
2437496
3039593
4035251
5077020
5620151
6111175
0.004
2004
180
Trafficking, importing or exporting more than 500 g of cannabis is a capital offence, with narrow exceptions for qualifying couriers. Cannabis executions have been carried out as recently as April 2026.
cnb, cnb_execution, mh
937205
1020920
1112809
1214751
1326032
1446362
1605080
1739869
1903075
1998010
1986817
2044611
2102421
2130986
5.9
2020
80000
2865937
2971421
3081588
3197780
3318354
3465750
4124444
4525876
4976659
5266077
5380001
5391857
5455028
5413813
3.8
2020
130000
1095929
1179670
1263887
1458247
1680790
1999628
2268361
2712287
3272097
4196834
4423615
6222715
7912557
8819794
6.48
2017
340000
10133
10995
12786
13026
14632
12970
15449
18194
21609
23501
26823
30648
34796
34109
5.7
true
1300
1377037
1470404
1566552
1807456
2083292
2555376
3340900
4466330
5845640
7720524
9968262
12635418
16789217
18931966
2.8
1999
310000
1520159
1559015
1594481
1750902
1906384
2213139
2876333
3738451
5936155
7082816
8838714
12261761
16651200
19654739
2.5
2002
250000
88412
90910
124719
142870
170501
195575
263935
379451
375393
412500
476647
549931
612320
639850
3.4
2013
14000
illegaldeath
1863101
2022548
2168997
2267602
2370690
2493286
2931567
3409620
4339041
4737637
6032274
9745955
10698471
12188788
3.1
true
220000
Unlawful dealing under section 384 can bring life imprisonment or death.
eye, law
41279
58302
57306
60764
61831
60895
68028
77639
98563
122993
144029
181810
217450
240254
3.1
true
4400
839397
1045631
1307019
1600789
1826506
2183513
2791448
3668799
4540377
5399728
5943363
6068253
6234679
6365503
2.03
2014
87000
illegaldeath
1768504
2006651
2309054
2656835
3056998
3612085
4673502
6380612
8957664
12579212
16637884
22482417
21049431
25620427
2
2002
340000
Trafficking in large quantities can bring life imprisonment or death, and a 1993 law explicitly extended the death penalty to growers.
cr, wiki
109096
127720
149409
183282
223795
271670
345339
441223
596657
872436
1041428
1111651
1192734
1256174
3.1
true
24000
1797125
1730245
1684657
1896655
2143365
2525457
3049843
3727834
4515461
5982838
8512092
12312580
17224686
21003705
0.9
1995
98000
746223
806672
881367
1018096
1173468
1399596
1673110
2149480
2791409
3714023
4783721
6325791
7788058
8591626
1
2009
48000
7411725
8430298
9983580
12563733
15767871
20428401
26851751
35954411
45650031
54738329
63007815
68579443
71641484
71619863
1.33
2019
660000
957687
1046782
1144984
1254501
1374494
1528311
2112136
2936784
3995258
5398773
6284737
7652144
9749312
10786734
3.4
1998
220000
329377
351497
372111
432625
419382
414767
468130
553079
649877
760348
745095
1084688
1326051
1418517
1.9
true
17000
760911
833309
912721
1000023
1095675
1269184
1643462
2195617
2832201
3760561
4582675
5564356
6949915
7618847
1.9
true
93000
1878648
2193797
2337611
2611086
3094602
3608997
4267299
5131394
6606194
8324509
9764940
10765984
11974058
12348573
5.35
2017
440000
21444
24690
27259
31521
37250
49679
66187
84416
97785
99717
102695
107415
105721
103742
11.6
true
7000
13972684
14683987
13800950
14806857
17561075
21021142
28329895
36638458
45408818
56015627
65425957
73346766
86091691
85878556
1.1
2020
650000
273257
337613
396002
418330
491882
648778
844952
991480
1121750
1252215
1319781
1386657
1481026
1367764
4.79
2017
46000
3696
3883
4078
4279
4490
4784
5619
5684
7387
8821
9566
10635
10423
9492
11.6
true
660
2809533
3228010
3639403
4490164
5826611
7596535
11046263
14815755
18086521
20594907
22332873
23224049
23663461
23300000
0.3
2005
45000
4975342
4773506
4800744
5519397
6361710
7625152
9901422
13492674
19189769
26110210
34260142
44758491
60972803
70545865
0.2
1999
77000
23480335
25767239
28263326
30966706
33928663
37310888
42824099
47261309
49955147
52054096
49556654
46455997
44680008
38980376
2.13
2015
560000
3774052
3562507
3555241
4153962
4793413
5750917
7640975
10321608
13275751
17569895
24000158
32390801
44457148
51384894
3.1
true
870000
74829905
90151889
103898175
119629770
129417367
154202683
180274556
207796338
229858656
253373387
281484126
311062785
339436156
341784857
21.9
2020
48000000
919712
1101649
1392464
1732125
1993585
2234414
2529964
2790811
2951956
3104404
3266212
3318581
3398966
3384688
14.6
2018
330000
3925731
4288052
4688099
5136514
5627821
6239906
8209125
11762643
15619935
20464963
24787052
28385509
33586373
37053428
4.2
2003
970000
44311
39368
40525
44563
56344
61686
84409
95977
104229
111825
113561
109974
103540
99924
7.1
2006
4700
2699561
3014767
3255672
3592246
4146367
5488858
8160087
11387795
15258225
19827005
24526707
28813101
28444080
28516896
1.56
2011
290000
illegaldeath
11800814
18608564
18390657
17715169
19114980
25100732
32531929
41475227
52490098
65504551
77154016
87455148
98079196
101598527
0.3
2002
210000
Trafficking can carry the death penalty, which applies from 5 kg of resin or 75 kg of plant material.
cr, ln1
59840
64963
59170
58144
49640
51557
64446
86283
115683
148073
186290
238256
298867
335169
0.1
1997
190
35087
39803
39583
58916
72525
88299
112508
140420
164335
169061
181695
193035
211957
219306
11.6
true
14000
626679
657039
687398
717758
748118
778478
984854
1249555
1604825
1973789
1802488
1788756
1751413
1576876
1.3
2019
14000
illegaldeath
2967405
3225110
3533546
3880617
4261779
4616782
5532299
7065279
9543358
13887863
19624139
26754383
36134867
41773878
1.9
true
450000
Trafficking carries ten to twenty-five years, or death for repeat or aggravated offences.
cr, trip
5163858
6093688
7168076
8793161
10736810
13038501
16440175
22279990
29518857
40746274
47159715
52344047
60562374
64747319
3.65
2011
1600000
1612918
1514671
1462906
1677885
1933946
2321310
3153730
4321726
5802841
7786163
10017633
13965592
19059394
21913874
14.8
2019
1800000
1111376
1304325
1528456
1874974
2289421
2790908
3809392
5215919
7041304
10137287
11892055
13356551
15526887
16950795
6.9
2000
660000
src/components/AverageChart.astro +49 @@ -1,7 +1,8 @@ --- // World average score over time: a step line that moves at each recorded change. -// Two views are rendered, the whole history and the last fifteen years; a switch shows one of them. +// Four views are rendered: the whole history or the last fifteen years, each averaged per country +// or weighted by population. Two switches pick which one is shown. // The plot is a stretched SVG; labels and markers are HTML so they stay readable at any width. -import { averageHistory, formatDate, dateTime, lastUpdated } from "../lib/countries.js"; +import { averageHistory, averageHistoryByPopulation, formatDate, dateTime, lastUpdated } from "../lib/countries.js"; // `note` adds the caveat under the chart. @@ -11,12 +12,24 @@ const fmt = (v) => v.toFixed(1); const time = dateTime; -const now = averageHistory.at(-1).after; const endYear = +lastUpdated.slice(0, 4); const t1 = time(lastUpdated); -function view(id, label, startYear) { +// one chart for each combination of period and weighting +const RANGES = [ + { id: "all", label: "All", startYear: 1900 }, // a few bans are older; they are already counted in the starting value + { id: "15y", label: "Last 15 years", startYear: endYear - 15 }, +]; +const WEIGHTS = [ + { id: "country", label: "By country", title: "Every country counts the same", history: averageHistory }, + { id: "people", label: "By population", title: "Each country counts in proportion to its population today", history: averageHistoryByPopulation }, +]; + +function view(range, weighting) { + const { startYear } = range; + const history = weighting.history; + const now = history.at(-1).after; const t0 = Date.UTC(startYear, 0, 1); const x = (t) => ((t - t0) / (t1 - t0)) * 100; - const inRange = averageHistory.filter((p) => time(p.date) >= t0); + const inRange = history.filter((p) => time(p.date) >= t0); const start = inRange[0].before; // the average when the view begins @@ -40,30 +53,33 @@ function view(id, label, startYear) { const line = `M0,${y(start)}` + steps.map((p) => `H${p.x}V${p.y}`).join("") + "H100"; const points = steps.filter((p) => p.change); // estimated changes move the line but get no dot - return { id, label, startYear, whole: tick >= 1, delta: now - start, ticks: ticks.map((t) => ({ t, y: y(t) })), years, points, line }; + return { range: range.id, weight: weighting.id, startYear, now, whole: tick >= 1, delta: now - start, ticks: ticks.map((t) => ({ t, y: y(t) })), years, points, line }; } -const views = [ - view("all", "All", 1900), // a few bans are older; they are already counted in the starting value - view("15y", "Last 15 years", endYear - 15), -]; +const views = WEIGHTS.flatMap((w) => RANGES.map((r) => view(r, w))); +const shown = (v) => v.range === RANGES[0].id && v.weight === WEIGHTS[0].id; // the view visible before any click --- <section class="card avg" id="avgChart" aria-labelledby="avgTitle"> <div class="avg-head"> - <div> - <h2 id="avgTitle">World average score</h2> - <div class="avg-switch" role="group" aria-label="Period shown"> - {views.map((v, i) => <button type="button" data-view={v.id} aria-pressed={i === 0 ? "true" : "false"}>{v.label}</button>)} - </div> - </div> + <h2 id="avgTitle">World average score</h2> <p class="avg-now"> - <b>{fmt(now)}</b> - {views.map((v, i) => ( - <span class={`delta ${v.delta >= 0 ? "up" : "down"}`} data-view={v.id} hidden={i > 0}>{v.delta >= 0 ? "+" : "−"}{fmt(Math.abs(v.delta))} since {v.startYear}</span> + {views.map((v) => ( + <span class="avg-figure" data-range={v.range} data-weight={v.weight} hidden={!shown(v)}> + <b>{fmt(v.now)}</b> + <span class={`delta ${v.delta >= 0 ? "up" : "down"}`}>{v.delta >= 0 ? "+" : "−"}{fmt(Math.abs(v.delta))} since {v.startYear}</span> + </span> ))} </p> </div> + <div class="avg-controls"> + <div class="avg-switch" role="group" aria-label="Period shown" data-switch="range"> + {RANGES.map((r, i) => <button type="button" data-value={r.id} aria-pressed={i === 0 ? "true" : "false"}>{r.label}</button>)} + </div> + <div class="avg-switch" role="group" aria-label="How countries are weighted" data-switch="weight"> + {WEIGHTS.map((w, i) => <button type="button" data-value={w.id} title={w.title} aria-pressed={i === 0 ? "true" : "false"}>{w.label}</button>)} + </div> + </div> - {views.map((v, i) => ( - <div class="avg-chart" data-view={v.id} hidden={i > 0}> + {views.map((v) => ( + <div class="avg-chart" data-range={v.range} data-weight={v.weight} hidden={!shown(v)}> <div class="avg-y" aria-hidden="true"> {v.ticks.map(({ t, y }) => <span style={`top:${y}%`}>{v.whole ? t : fmt(t)}</span>)} @@ -87,5 +103,5 @@ const views = [ </div> ))} - {note && <p class="avg-note">Rebuilt from the recorded changes, so it is only as complete as that list: a country with no recorded change is held at today's score for the whole period. The scale does not start at zero.</p>} + {note && <p class="avg-note">Rebuilt from the recorded changes, so it is only as complete as that list. The population-weighted line uses today's populations for every year. The scale does not start at zero.</p>} </section> @@ -93,9 +109,15 @@ const views = [ const chart = document.querySelector("#avgChart"); - // Period switch: show one view and its "since" figure. - chart?.querySelectorAll(".avg-switch button").forEach((btn) => { - btn.addEventListener("click", () => { - chart.querySelectorAll(".avg-switch button").forEach((b) => b.setAttribute("aria-pressed", String(b === btn))); - chart.querySelectorAll(".avg-chart, .avg-now .delta").forEach((el) => { el.hidden = el.dataset.view !== btn.dataset.view; }); + // Two switches, period and weighting: show the one chart and figure that match both. + const state = { range: "all", weight: "country" }; + chart?.querySelectorAll(".avg-switch").forEach((group) => { + group.querySelectorAll("button").forEach((btn) => { + btn.addEventListener("click", () => { + state[group.dataset.switch] = btn.dataset.value; + group.querySelectorAll("button").forEach((b) => b.setAttribute("aria-pressed", String(b === btn))); + chart.querySelectorAll(".avg-chart, .avg-figure").forEach((el) => { + el.hidden = el.dataset.range !== state.range || el.dataset.weight !== state.weight; + }); + }); }); });
src/components/CountryArticle.astro +22 @@ -2,5 +2,5 @@ // Full write-up for one country. Rendered on its own page, and fetched into the // drawer on the home page — so there is a single template for both views. -import { SEGMENTS, REC_LABEL, MED_LABEL, countries, changes, segAvg, segmentPoints, segmentMax, subunitsOf, formatDate } from "../lib/countries.js"; +import { SEGMENTS, REC_LABEL, REC_HELP, MED_LABEL, countries, changes, segAvg, segmentPoints, segmentMax, subunitsOf, formatDate } from "../lib/countries.js"; import { tierOf, tierColor } from "../lib/tiers.js"; import CountryChanges from "./CountryChanges.astro"; @@ -22,4 +22,7 @@ const byName = group ? [...group.units].sort((a, b) => a.name.localeCompare(b.na const history = sub ? [] : changes.filter((ch) => ch.country === c); const scored = history.filter((ch) => ch.score); // the ones that moved the score +// 48,000,000 → "48 million", 770,000 → "770,000" +const people = (n) => (n >= 1e6 ? `${+(n / 1e6).toPrecision(2)} million` : n.toLocaleString("en-GB")); +const use = sub ? null : c.use; const r = 42; const circ = 2 * Math.PI * r; @@ -49,8 +52,23 @@ const circ = 2 * Math.PI * r; </div> <div class="d-chips"> - <span class={`chip s-${v.rec}`}>Recreational: {REC_LABEL[v.rec]}</span> + <span class={`chip s-${v.rec}`} title={REC_HELP[v.rec]}>Recreational: {REC_LABEL[v.rec]}</span> <span class={`chip s-${v.med}`}>Medical: {MED_LABEL[v.med]}</span> </div> <p class="d-note">{v.note}</p> + {use && ( + <dl class="c-use"> + <div> + <dt>Cannabis users</dt> + <dd>{use.estimate ? "about " : ""}{people(use.users)}</dd> + </div> + <div> + <dt>Share aged 15–64</dt> + <dd>{use.estimate ? "about " : ""}{use.prevalence}%</dd> + </div> + <p>{use.estimate + ? `An estimate: there is no national survey, so the median rate of surveyed countries in ${c.region} is assumed.` + : `People who used cannabis in the past year, from a ${use.year} survey applied to today's population.`}</p> + </dl> + )} {scored.length > 0 && <CountryChart history={scored} />} {history.length > 0 && <CountryChanges history={history} place="side" />} @@ -152,7 +170,7 @@ const circ = 2 * Math.PI * r; <> <h2 class="d-h">More context <span>not part of the score</span></h2> - {d.context.map((x) => ( + {d.context.filter(Boolean).map((x) => ( <div class="segrow"> - <div class="segrow-top"><span>{x.name}</span></div> + <div class="segrow-top"><span>{x.name === "Death penalty" ? "☠️ " : ""}{x.name}</span></div> <p class="ctx">{x.t}</p> <ul class="srcs">
src/components/HistoryList.astro +54 @@ -0,0 +1,54 @@ +--- +// One page of the History list: a period of entries, newest first, grouped by year, with links +// to the other periods above and below. +import ScoreChange from "./ScoreChange.astro"; +import { historyPeriods, formatDate } from "../lib/countries.js"; +import { tierColor } from "../lib/tiers.js"; + +const { period } = Astro.props; +const base = import.meta.env.BASE_URL; +// the newest period lives at /history/ itself +const href = (p) => (p === historyPeriods[0] ? `${base}history/` : `${base}history/period/${p.slug}/`); +const index = historyPeriods.indexOf(period); +const newer = historyPeriods[index - 1]; +const older = historyPeriods[index + 1]; +const years = [...new Set(period.items.map((ch) => ch.date.slice(0, 4)))] + .map((year) => ({ year, items: period.items.filter((ch) => ch.date.startsWith(year)) })); +--- +<nav class="periods" aria-label="Periods"> + {historyPeriods.map((p) => ( + <a href={href(p)} aria-current={p === period ? "page" : undefined}>{p.label} <small>{p.items.length}</small></a> + ))} +</nav> + +<div class="chlog"> + {years.map(({ year, items }) => ( + <section aria-labelledby={`y${year}`}> + <h2 class="chl-year" id={`y${year}`}>{year}</h2> + <ol class="chl-list"> + {items.map((ch) => ( + <li class="chl-item" style={`--c:${ch.score ? tierColor(ch.score.after) : "var(--muted)"}`}> + <a class="card chl-card" href={`${base}history/${ch.id}/`}> + <p class="change-meta"> + <time datetime={ch.date}>{formatDate(ch.date)}</time> + <span>{ch.place.flag} {ch.place.name}</span> + </p> + <h3>{ch.title}</h3> + <p class="chl-text">{ch.description}</p> + {ch.score && <ScoreChange score={ch.score} />} + </a> + </li> + ))} + </ol> + </section> + ))} +</div> + +<nav class="pager chl-pager" aria-label="Other periods"> + {newer + ? <a href={href(newer)}><small>Newer</small><span>← {newer.label}</span></a> + : <span></span>} + {older + ? <a class="next" href={href(older)}><small>Older</small><span>{older.label} →</span></a> + : <span></span>} +</nav>
src/content.config.ts +20 @@ -12,5 +12,6 @@ const source = z.object({ title: z.string().min(1), url: z.string().url() }).str const cited = z.object({ text: z.string().min(1), sources: z.array(z.string()) }).strict(); const status = { - recreational: z.enum(["legal", "partial", "decriminalised", "illegal"]), + // "death": illegal, and the law allows the death penalty for cannabis offences, in practice trafficking. + recreational: z.enum(["legal", "partial", "decriminalised", "illegal", "death"]), medical: z.enum(["legal", "limited", "none"]), }; @@ -28,4 +29,17 @@ const countries = defineCollection({ region: z.enum(["Africa", "Asia", "Europe", "North America", "South America", "Oceania"]), iso_numeric: z.number().int().optional(), // matches the country to its shape on the map + // Population by year: a snapshot every ten years from 1900 (Our World in Data) and the + // latest figure (World Bank). It weights the population-weighted world average. + population: z.record(z.string().regex(/^\d{4}$/), z.number().int().positive()), + // People who used cannabis in the past year. `prevalence` is the share of 15–64-year-olds, + // from the survey of `year` (UNODC World Drug Report figures); `users` is that share of the + // country's 15–64 population today. Where there is no survey, `estimate: true` and the + // prevalence is the median of the surveyed countries in the same region. + cannabis_use: z.object({ + prevalence: z.number().min(0).max(100), + year: z.number().int().optional(), + estimate: z.boolean().optional(), + users: z.number().int().nonnegative(), + }).strict(), ...status, updated: z.coerce.date(), @@ -33,5 +47,6 @@ const countries = defineCollection({ scores: perSegment(score), segments: perSegment(cited), - context: z.object(Object.fromEntries(CONTEXT_IDS.map((id) => [id, cited]))).strict(), + // the four standard sections, plus `death_penalty` where the law provides for one + context: z.object({ death_penalty: cited.optional(), ...Object.fromEntries(CONTEXT_IDS.map((id) => [id, cited])) }).strict(), sources: z.record(z.string(), source), subunits: z @@ -48,4 +63,7 @@ const countries = defineCollection({ .superRefine((data, ctx) => { // Every source key cited by a segment must be defined under `sources`. + if ((data.recreational === "death") !== Boolean(data.context.death_penalty)) { + ctx.addIssue({ code: "custom", message: 'a country marked "recreational: death" needs a context.death_penalty section, and only such a country may have one' }); + } const blocks = { ...data.segments, ...data.context } as Record<string, { sources: string[] }>; for (const [name, block] of Object.entries(blocks)) {
src/lib/countries.js +111 @@ -5,12 +5,22 @@ import { SEGMENT_IDS } from "./segment-ids.js"; export const DATA_SNAPSHOT = "2026"; -export const REC_LABEL = { legal: "Legal", partial: "Partly legal / tolerated", decrim: "Decriminalised", illegal: "Illegal" }; +export const REC_LABEL = { legal: "Legal", partial: "Partly legal / tolerated", decrim: "Decriminalized", illegal: "Illegal", death: "Death penalty" }; +// from most to least free; also what each status means, shown as a tooltip +export const REC_ORDER = ["legal", "partial", "decrim", "illegal", "death"]; +export const REC_HELP = { + legal: "Adults may legally possess cannabis, within limits", + partial: "Legal in part of the country or in some settings, or openly tolerated", + decrim: "Still prohibited, but small amounts bring at most a fine", + illegal: "A crime, punished with anything from a fine to prison", + death: "A crime, and the law allows the death penalty for cannabis offences, in practice trafficking", +}; export const MED_LABEL = { yes: "Legal", limited: "Limited", no: "None" }; // Explanations that exist for each country but are not part of the score. -const CONTEXT = [["production", "Commercial production"], ["products", "Products"], ["medical", "Medical use"], ["consequences", "Other consequences"]]; +// "Death penalty" comes first and exists only for countries whose law provides for it +const CONTEXT = [["death_penalty", "Death penalty"], ["production", "Commercial production"], ["products", "Products"], ["medical", "Medical use"], ["consequences", "Other consequences"]]; export const CONTEXT_NAMES = CONTEXT.map(([, name]) => name); // File values → short codes used in class names and filters. -const REC_CODE = { legal: "legal", partial: "partial", decriminalised: "decrim", illegal: "illegal" }; +const REC_CODE = { legal: "legal", partial: "partial", decriminalised: "decrim", illegal: "illegal", death: "death" }; const MED_CODE = { legal: "yes", limited: "limited", none: "no" }; const isoDate = (d) => d.toISOString().slice(0, 10); @@ -45,4 +55,7 @@ export const countries = (await getCollection("countries")) flag: flagOf(id), num: data.iso_numeric ?? null, + populationByYear: Object.entries(data.population).map(([y, v]) => [+y, v]).sort((a, b) => a[0] - b[0]), + population: data.population[Object.keys(data.population).sort().at(-1)], // the latest figure + use: data.cannabis_use, // { prevalence, year?, estimate?, users } name: data.name, region: data.region, @@ -53,5 +66,5 @@ export const countries = (await getCollection("countries")) reviewed: isoDate(data.updated), segments: SEGMENTS.map((seg) => cited(data.segments[seg.id])), // in SEGMENTS order - context: CONTEXT.map(([key, name]) => ({ name, ...cited(data.context[key]) })), + context: CONTEXT.map(([key, name]) => (data.context[key] ? { name, ...cited(data.context[key]) } : null)), // null: no such section sources: Object.values(data.sources).map(pair), }, @@ -140,23 +153,99 @@ export const changes = changeEntries export const ESTIMATE_WINDOW = [1925, 1961]; -// World average over time, oldest first, rebuilt by undoing the recorded changes one by one. -// A change with a known date is one step. An estimated one is spread evenly over the window, -// a year at a time, so that a guess never shows up as a sudden drop on one day. -export const averageHistory = (() => { - const n = countries.length; - const [from, to] = ESTIMATE_WINDOW; - const scored = changes.filter((ch) => ch.score); - const events = scored.filter((ch) => !ch.estimate).map((ch) => ({ change: ch, date: ch.date, delta: ch.raw.after - ch.raw.before })); - // all the estimates together, as one small step per year of the window - const estimated = scored.filter((ch) => ch.estimate).reduce((a, ch) => a + ch.raw.after - ch.raw.before, 0); - for (let year = from + 1; year <= to; year++) events.push({ change: null, date: `${year}-01-01`, delta: estimated / (to - from) }); - events.sort((a, b) => dateTime(b.date) - dateTime(a.date)); // newest first - let sum = countries.reduce((a, c) => a + c.raw, 0); - const points = events.map((e) => { - const after = sum / n; - sum -= e.delta; - return { change: e.change, date: e.date, before: sum / n, after }; +// A country's population at a moment in time, on a straight line between its snapshots. +const MS_YEAR = 365.25 * 24 * 3600 * 1000; +function populationAt(c, t) { + const year = 1970 + t / MS_YEAR; + const snaps = c.populationByYear; + if (year <= snaps[0][0]) return snaps[0][1]; + if (year >= snaps.at(-1)[0]) return snaps.at(-1)[1]; + const i = snaps.findIndex(([y]) => y > year); + const [y0, v0] = snaps[i - 1], [y1, v1] = snaps[i]; + return v0 + ((v1 - v0) * (year - y0)) / (y1 - y0); +} + +// A country's score at a moment in time, from its recorded changes. A change with a known date is +// a step. An estimated one rises or falls in a straight line across the estimate window, so that +// a guess never shows up as a sudden drop on one day. +const [estFrom, estTo] = ESTIMATE_WINDOW.map((y) => Date.UTC(y, 0, 1)); +const scoredByCountry = new Map(countries.map((c) => [c, changes.filter((ch) => ch.country === c && ch.score).reverse()])); // oldest first +function scoreAt(c, t) { + let value = c.raw; + const list = scoredByCountry.get(c); + if (list.length) value = list[0].raw.before; + for (const ch of list) { + if (ch.estimate) { + if (t <= estFrom) break; + value = t >= estTo ? ch.raw.after : ch.raw.before + ((ch.raw.after - ch.raw.before) * (t - estFrom)) / (estTo - estFrom); + } else if (t >= dateTime(ch.date)) value = ch.raw.after; + else break; + } + return value; +} + +// World average over time, oldest first. `weight(country, time)` gives each country's share: +// the same for all in the plain average, or its population at that time. +// There is a point at every dated change, and one each New Year so that slow shifts (estimated +// changes, and populations growing at different rates) show up as well. +const CHART_FROM = 1900; +function averageOverTime(weight) { + const at = (t) => { + let sum = 0, total = 0; + for (const c of countries) { + const w = weight(c, t); + sum += scoreAt(c, t) * w; + total += w; + } + return sum / total; + }; + const events = changes.filter((ch) => ch.score && !ch.estimate).map((ch) => ({ change: ch, date: ch.date, t: dateTime(ch.date) })); + for (let year = CHART_FROM; year <= +DATA_SNAPSHOT; year++) events.push({ change: null, date: `${year}-01-01`, t: Date.UTC(year, 0, 1) }); + events.sort((a, b) => a.t - b.t); + // changes on the same day are applied one after another, in a fixed order + return events.map((e) => { + if (!e.change) return { change: null, date: e.date, before: at(e.t - 1), after: at(e.t) }; + const c = e.change.country; + const w = weight(c, e.t); + let total = 0; + for (const x of countries) total += weight(x, e.t); + const after = at(e.t); + return { change: e.change, date: e.date, before: after - ((e.change.raw.after - e.change.raw.before) * w) / total, after }; + }); +} +export const averageHistory = averageOverTime(() => 1); +export const averageHistoryByPopulation = averageOverTime(populationAt); + +// ── History pages ── +// The History list is split into periods so that no page is too long or too short. It starts from +// decades: a crowded decade is cut into two halves, and quiet decades are merged with their +// neighbours until a page has enough on it. Estimated entries have no real date and are left out. +const PAGE_MIN = 24, PAGE_MAX = 60; +export const historyPeriods = (() => { + const dated = changes.filter((ch) => !ch.estimate); // newest first + const year = (ch) => +ch.date.slice(0, 4); + const thisYear = +DATA_SNAPSHOT; + const count = (from, to) => dated.filter((ch) => year(ch) >= from && year(ch) <= to).length; + // decades from the current one back to the oldest entry, crowded ones in two halves + const spans = []; + for (let d = Math.floor(thisYear / 10) * 10; d >= Math.floor(year(dated.at(-1)) / 10) * 10; d -= 10) { + if (count(d, d + 9) > PAGE_MAX) spans.push([d + 5, d + 9], [d, d + 4]); + else spans.push([d, d + 9]); + } + // merge quiet spans into the one before them in time order (walking from newest to oldest) + const merged = []; + for (const [from, to] of spans) { + const last = merged.at(-1); + if (last && count(last.from, last.to) < PAGE_MIN) last.from = from; + else merged.push({ from, to }); + } + // a quiet tail (the oldest entries) joins the period after it + if (merged.length > 1 && count(merged.at(-1).from, merged.at(-1).to) < PAGE_MIN) merged.at(-2).from = merged.pop().from; + return merged.map(({ from, to }, i) => { + const items = dated.filter((ch) => year(ch) >= from && year(ch) <= to); + const first = i === merged.length - 1 ? year(items.at(-1)) : from; // the oldest page starts at its first entry + const end = Math.min(to, thisYear); + const label = first % 10 === 0 && end === first + 9 ? `${first}s` : `${first}–${end}`; + return { slug: `${first}-${end}`, label, from: first, to: end, items }; }); - return points.reverse(); })();
src/lib/facts.js +1 @@ -16,5 +16,5 @@ function computed() { const noShop = countries.filter((c) => c.s[P] >= 5 && c.s[A] <= 1); const lowest = segAvg.indexOf(Math.min(...segAvg)); - const medOnly = countries.filter((c) => c.rec === "illegal" && c.med !== "no"); + const medOnly = countries.filter((c) => ["illegal", "death"].includes(c.rec) && c.med !== "no"); const regionAvg = regions .map((r) => {
src/pages/api/[id].json.js +1 @@ -15,5 +15,5 @@ export function GET({ props }) { reviewed: c.details.reviewed, segments: c.details.segments.map(entry), - context: c.details.context.map(entry), + context: c.details.context.map((x) => x && entry(x)), }; return new Response(JSON.stringify(body), { headers: { "Content-Type": "application/json" } });
src/pages/compare.astro +3 @@ -154,5 +154,7 @@ const presets = [ rows.push(`<tr class="cmp-divider"><th colspan="${cs.length + 1}">More context — not part of the score</th></tr>`); META.context.forEach((name, i) => { - rows.push(row(name, cs.map((c) => prose(details.get(c.id)?.context[i])))); + const cells = cs.map((c) => prose(details.get(c.id)?.context[i])); + // a section that none of the chosen countries has (the death penalty, usually) is left out + if (cells.some(Boolean)) rows.push(row(name, cells)); }); }
src/pages/history/[id].astro +1 @@ -25,4 +25,5 @@ const diff = (seg) => seg.after - seg.before; <h1>{ch.title}</h1> <p class="lede">{ch.description}</p> + <p class="change-source">Source: <a href={ch.source[1]} target="_blank" rel="noopener noreferrer">{ch.source[0]}</a></p> {ch.score && ( @@ -55,9 +56,4 @@ const diff = (seg) => seg.after - seg.before; {ch.estimate && <p class="change-note">This entry is an estimate. No first law is recorded for {c.name}, so its first restrictions are assumed to have come at some point between the 1925 Geneva convention and the 1961 Single Convention. On the charts the change is spread evenly across those years.</p>} - - <h2 class="d-h">Source</h2> - <ul class="srcs"> - <li><a href={ch.source[1]} target="_blank" rel="noopener noreferrer">{ch.source[0]}</a></li> - </ul> </article>
src/pages/history/index.astro +4 @@ -1,14 +1,7 @@ --- +// The newest period of the History list. Older periods are at /history/period/<years>/. import Base from "../../layouts/Base.astro"; -import ScoreChange from "../../components/ScoreChange.astro"; -import { changes, formatDate } from "../../lib/countries.js"; -import { tierColor } from "../../lib/tiers.js"; - -const base = import.meta.env.BASE_URL; -// Newest first, grouped by year. -// Estimated first restrictions have no real date, so they are left to the country pages. -const dated = changes.filter((ch) => !ch.estimate); -const years = [...new Set(dated.map((ch) => ch.date.slice(0, 4)))] - .map((year) => ({ year, items: dated.filter((ch) => ch.date.startsWith(year)) })); +import HistoryList from "../../components/HistoryList.astro"; +import { historyPeriods } from "../../lib/countries.js"; --- <Base title="History" description="Laws, court rulings and treaties that shaped cannabis freedom, from the first bans to today, and how each one moved a country's score on the Cannabis Freedom Index."> @@ -19,27 +12,5 @@ const years = [...new Set(dated.map((ch) => ch.date.slice(0, 4)))] <p class="lede">The laws, court rulings and other developments that changed a country's score, newest first, along with the international treaties that set the rules countries work within. Open one to see which segments moved and the source.</p> </section> - - <div class="chlog"> - {years.map(({ year, items }) => ( - <section aria-labelledby={`y${year}`}> - <h2 class="chl-year" id={`y${year}`}>{year}</h2> - <ol class="chl-list"> - {items.map((ch) => ( - <li class="chl-item" style={`--c:${ch.score ? tierColor(ch.score.after) : "var(--muted)"}`}> - <a class="card chl-card" href={`${base}history/${ch.id}/`}> - <p class="change-meta"> - <time datetime={ch.date}>{formatDate(ch.date)}</time> - <span>{ch.place.flag} {ch.place.name}</span> - </p> - <h3>{ch.title}</h3> - <p class="chl-text">{ch.description}</p> - {ch.score && <ScoreChange score={ch.score} />} - </a> - </li> - ))} - </ol> - </section> - ))} - </div> + <HistoryList period={historyPeriods[0]} /> </main> </Base>
src/pages/history/period/[period].astro +21 @@ -0,0 +1,21 @@ +--- +// An older period of the History list. +import Base from "../../../layouts/Base.astro"; +import HistoryList from "../../../components/HistoryList.astro"; +import { historyPeriods } from "../../../lib/countries.js"; + +export function getStaticPaths() { + return historyPeriods.slice(1).map((period) => ({ params: { period: period.slug }, props: { period } })); +} + +const { period } = Astro.props; +--- +<Base title={`History, ${period.label}`} description={`Cannabis laws, court rulings and treaties from ${period.from} to ${period.to}, and how each one moved a country's score on the Cannabis Freedom Index.`}> + <main class="changes-page"> + <section class="hero hero-sm"> + <p class="eyebrow">History</p> + <h1>{period.label}</h1> + </section> + <HistoryList period={period} /> + </main> +</Base>
src/pages/index.astro +57 @@ -3,23 +3,32 @@ import Base from "../layouts/Base.astro"; import DidYouKnow from "../components/DidYouKnow.astro"; import Changes from "../components/Changes.astro"; -import { SEGMENTS, DATA_SNAPSHOT, REC_LABEL, MED_LABEL, countries, regions, worldAvg, lastUpdated, formatDate } from "../lib/countries.js"; +import { SEGMENTS, DATA_SNAPSHOT, REC_LABEL, REC_ORDER, REC_HELP, MED_LABEL, countries, regions, worldAvg, lastUpdated, formatDate } from "../lib/countries.js"; import { TIERS, tierColor } from "../lib/tiers.js"; const base = import.meta.env.BASE_URL; const count = (fn) => countries.filter(fn).length; +// 48,000,000 → "48M", 770,000 → "770k" +const compact = (n) => (n >= 1e6 ? `${+(n / 1e6).toPrecision(2)}M` : n >= 1e3 ? `${+(n / 1e3).toPrecision(2)}k` : String(n)); +const useNote = (c) => c.use.estimate + ? `Estimate: no national survey, so the regional median of ${c.use.prevalence}% of people aged 15–64 is assumed` + : `${c.use.prevalence}% of people aged 15–64 used cannabis in the past year (${c.use.year} survey)`; const stats = [ - [countries.length, "countries scored"], [worldAvg.toFixed(1), "world average, out of 100"], [count((c) => c.raw >= 60), "reach the “Free” tier (60+)"], [count((c) => c.raw >= 40), "score 40+: legal to possess and grow, with limits"], [count((c) => c.raw < 15), "score under 15: prison is a real risk"], + [count((c) => c.rec === "death"), "keep the death penalty for cannabis offences", "danger"], ]; const legend = [...TIERS].reverse().map((t, i, arr) => ({ name: t.name, min: t.min, max: i < arr.length - 1 ? arr[i + 1].min - 1 : 100, i, })); -const REC_ORDER = ["legal", "partial", "decrim", "illegal"]; const MED_ORDER = ["yes", "limited", "no"]; // What the browser needs for the map: scores only, no prose. -const mapData = countries.map(({ id, num, name, flag, raw, score, rank, s }) => ({ id, num, name, flag, raw, score, rank, s })); +const mapData = countries.map(({ id, num, name, flag, raw, score, rank, s, use }) => ({ id, num, name, flag, raw, score, rank, s, use })); +// Cannabis use on the map: one hue from light to dark, in five steps. Each step starts at `min`. +const USE_SCALES = { + users: { label: "Cannabis users", steps: [[0, "Under 10k"], [1e4, "10k–100k"], [1e5, "100k–1M"], [1e6, "1M–10M"], [1e7, "10M or more"]] }, + prevalence: { label: "Share who use cannabis", steps: [[0, "Under 1%"], [1, "1–3%"], [3, "3–7%"], [7, "7–15%"], [15, "15% or more"]] }, +}; --- <Base description="A 0–100 score of how free cannabis is in every country on Earth, built from six weighted segments of law and practice."> @@ -30,5 +39,5 @@ const mapData = countries.map(({ id, num, name, flag, raw, score, rank, s }) => <p class="lede">One number from 0 to 100 for every country on Earth, measuring personal cannabis freedom: can you carry it, grow it, share it and use it — and what happens to you if you do. The bar is high: 100 means cannabis is treated like tomatoes. Nobody is close.</p> <div class="stats"> - {stats.map(([n, label]) => <div class="stat"><div class="big">{n}</div><div class="label">{label}</div></div>)} + {stats.map(([n, label, tone]) => <div class={`stat ${tone ?? ""}`}><div class="big">{n}</div><div class="label">{label}</div></div>)} </div> <Changes /> @@ -39,5 +48,5 @@ const mapData = countries.map(({ id, num, name, flag, raw, score, rank, s }) => <div> <h2>World map</h2> - <p class="sub">Red means prison risk, green means free. Select a country for its full breakdown.</p> + <p class="sub" id="mapSub" data-score="Red means prison risk, green means free. Select a country for its full breakdown." data-use="The stronger the blue, the more people use cannabis. Select a country for its full breakdown.">Red means prison risk, green means free. Select a country for its full breakdown.</p> </div> <label class="field"> @@ -46,4 +55,6 @@ const mapData = countries.map(({ id, num, name, flag, raw, score, rank, s }) => <option value="index">Overall index</option> {SEGMENTS.map((s, i) => <option value={i}>{s.name}</option>)} + <option value="users">Cannabis users (number)</option> + <option value="prevalence">Cannabis users (share of adults)</option> </select> </label> … 98 more lines
src/styles/global.css +41 Not shown: a picture, a generated file, or past the length shown per change.