Repository navigation
Expand file tree
/
Copy pathdemo.html
More file actions
898 lines (840 loc) · 67.6 KB
/
Copy pathdemo.html
File metadata and controls
898 lines (840 loc) · 67.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#092e35">
<meta name="description" content="RebootLoop helps you choose a safer next step for an unused device, prepare your data and find an existing recycling pathway. Free public Phase 2 pilot prototype.">
<title>RebootLoop demo | A safer next step for your old device</title>
<link rel="icon" type="image/svg+xml" href="assets/brand/rebootloop-favicon.svg">
<link rel="stylesheet" href="styles.css">
<script src="assets/brand-mark-replay.js" defer></script>
</head>
<body class="demo-page">
<header class="site-header">
<div class="header-inner">
<a class="brand" href="index.html" aria-label="RebootLoop home">
<div class="brand-mark" aria-hidden="true">
<img src="assets/brand/rebootloop-mark-mint-animated.svg" alt="">
</div>
<div>
<div class="brand-name">RebootLoop</div>
<div class="brand-sub">Privacy-first circular technology</div>
</div>
</a>
<div class="header-actions">
<a class="header-link" href="index.html">About the project</a>
<div class="prototype-label">Public Phase 2 prototype<br>Independent student project</div>
<button class="reset-button" id="resetDemo" type="button">Start over</button>
</div>
</div>
</header>
<main class="shell">
<nav class="progress-wrap" aria-label="RebootLoop progress">
<div class="progress-track">
<div class="progress-step is-active" data-progress="1"><span>1</span>Start</div>
<div class="progress-step" data-progress="2"><span>2</span>Assess</div>
<div class="progress-step" data-progress="3"><span>3</span>Route</div>
<div class="progress-step" data-progress="4"><span>4</span><em id="step4Label" style="font-style:normal">Next steps</em></div>
<div class="progress-step" data-progress="5" id="step5"><span>5</span>Your impact</div>
</div>
</nav>
<section class="screen is-active" id="screen-1" data-screen="1" aria-labelledby="start-title">
<div class="surface start-grid">
<div class="start-main">
<p class="eyebrow">A new phone. An old one in the drawer.</p>
<h1 id="start-title">Unsure what to do with the old one?</h1>
<p class="lead">Keeping it “just in case” is understandable when your photos and accounts are on it. Tell us about the device, and get a practical next step for repair, reuse, donation or responsible recycling.</p>
<div class="button-row">
<button class="primary-button" type="button" data-go="2">Assess a device <span aria-hidden="true">→</span></button>
</div>
<p class="demo-label">About two minutes. No account, file upload, serial number or IMEI. The assessment works on this public page without a booking or partner account.</p>
</div>
<aside class="start-aside" aria-label="RebootLoop safety promise">
<p class="eyebrow" style="color: var(--lime)">Safety promise</p>
<h2 class="aside-title">A useful answer, with clear limits.</h2>
<div class="promise-list">
<div class="promise"><div class="promise-icon">1</div><strong>Safety and ownership first</strong></div>
<div class="promise"><div class="promise-icon">2</div><strong>Official preparation guidance</strong></div>
<div class="promise"><div class="promise-icon">3</div><strong>Your next action</strong></div>
</div>
<details class="more more-dark">
<summary>What these mean</summary>
<ul class="promise-detail">
<li>Hazards and managed devices stop the normal flow.</li>
<li>Platform links are shown without asking for credentials.</li>
<li>Get relevant public services when eligible, and see the difference finishing the job makes.</li>
</ul>
</details>
<div class="notice"><strong>Important:</strong> RebootLoop does not erase data, certify destruction, collect devices or guarantee that information is unrecoverable. We have no confirmed delivery partnership.</div>
</aside>
</div>
</section>
<section class="screen" id="screen-2" data-screen="2" aria-labelledby="assess-title">
<div class="surface">
<header class="content-head">
<div><p class="eyebrow">Step 2 - assess · about 60-90 seconds</p><h2 id="assess-title">Tell us about the device.</h2></div>
<div class="head-side">
<div class="presentation-shortcuts" aria-label="Presentation shortcuts">
<strong>Demo:</strong>
<button class="shortcut" type="button" data-demo="a">A - working phone for donation</button>
<button class="shortcut" type="button" data-demo="b">B - non-functioning sensitive laptop</button>
</div>
</div>
</header>
<form class="assessment-form" id="assessmentForm" novalidate>
<div class="tiles">
<fieldset class="tile" data-icon="🔑"><legend>Authority</legend><p class="helper">Is this device yours to transfer?</p><div class="choices">
<input id="own-owned" name="ownership" type="radio" value="owned"><label for="own-owned">Yes, it is mine</label>
<input id="own-managed" name="ownership" type="radio" value="managed"><label for="own-managed">University / employer managed</label>
<input id="own-other" name="ownership" type="radio" value="other"><label for="own-other">No / borrowed / found</label>
</div></fieldset>
<fieldset class="tile device-q" data-icon="📱"><legend>Device</legend><p class="helper">Choose the closest category.</p><div class="choices">
<input id="dev-iphone" name="device" type="radio" value="iphone"><label for="dev-iphone">iPhone</label>
<input id="dev-ipad" name="device" type="radio" value="ipad"><label for="dev-ipad">iPad</label>
<input id="dev-android" name="device" type="radio" value="android"><label for="dev-android">Android</label>
<input id="dev-windows" name="device" type="radio" value="windows"><label for="dev-windows">Windows PC (laptop / desktop)</label>
<input id="dev-mac" name="device" type="radio" value="mac"><label for="dev-mac">Mac (laptop / desktop)</label>
<input id="dev-chromeos" name="device" type="radio" value="chromeos"><label for="dev-chromeos">Chromebook / ChromeOS</label>
<input id="dev-linux" name="device" type="radio" value="linux"><label for="dev-linux">Linux computer</label>
<input id="dev-console" name="device" type="radio" value="console"><label for="dev-console">Game console (PlayStation / Xbox)</label>
<input id="dev-storage" name="device" type="radio" value="storage"><label for="dev-storage">HDD / SSD / USB / SD</label>
<input id="dev-other" name="device" type="radio" value="other"><label for="dev-other">Other technology</label>
</div></fieldset>
<fieldset class="tile" data-icon="🔋"><legend>Power state</legend><p class="helper">Can the device turn on and operate? This means functionality, not battery percentage.</p><div class="choices">
<input id="power-works" name="power" type="radio" value="works"><label for="power-works">Works normally</label>
<input id="power-intermittent" name="power" type="radio" value="intermittent"><label for="power-intermittent">Intermittent</label>
<input id="power-off" name="power" type="radio" value="off"><label for="power-off">Does not power on</label>
</div></fieldset>
<fieldset class="tile" data-icon="⚠️"><legend>Physical safety</legend><p class="helper">Stop if you notice a battery or heat hazard.</p><div class="choices">
<input id="safe-normal" name="safety" type="radio" value="normal"><label for="safe-normal">Normal</label>
<input id="safe-swollen" name="safety" type="radio" value="swollen"><label for="safe-swollen">Swollen / leaking battery</label>
<input id="safe-heat" name="safety" type="radio" value="heat"><label for="safe-heat">Heat / odour</label>
<input id="safe-damaged" name="safety" type="radio" value="damaged"><label for="safe-damaged">Seriously damaged</label>
</div></fieldset>
<fieldset class="tile" data-icon="✨"><legend>Condition</legend><p class="helper">Think about usefulness, not only appearance.</p><div class="choices">
<input id="cond-good" name="condition" type="radio" value="good"><label for="cond-good">Good</label>
<input id="cond-repair" name="condition" type="radio" value="repairable"><label for="cond-repair">Repairable</label>
<input id="cond-eol" name="condition" type="radio" value="eol"><label for="cond-eol">End-of-life</label>
<input id="cond-unsure" name="condition" type="radio" value="unsure"><label for="cond-unsure">Unsure</label>
</div></fieldset>
<fieldset class="tile" data-icon="🎯"><legend>Preferred outcome</legend><p class="helper">What would you like to do?</p><div class="choices">
<input id="goal-repair" name="goal" type="radio" value="repair"><label for="goal-repair">Keep / repair</label>
<input id="goal-sell" name="goal" type="radio" value="sell"><label for="goal-sell">Sell</label>
<input id="goal-donate" name="goal" type="radio" value="donate"><label for="goal-donate">Donate</label>
<input id="goal-family" name="goal" type="radio" value="family"><label for="goal-family">Give to family</label>
<input id="goal-recycle" name="goal" type="radio" value="recycle"><label for="goal-recycle">Recycle</label>
<input id="goal-unsure" name="goal" type="radio" value="unsure"><label for="goal-unsure">Unsure</label>
</div></fieldset>
<fieldset class="tile" data-icon="🔒"><legend>Data sensitivity</legend><p class="helper">Choose the highest sensitivity that ever applied to information on the device.</p><div class="choices">
<input id="sens-everyday" name="sensitivity" type="radio" value="everyday"><label for="sens-everyday">Everyday - photos, messages, ordinary accounts</label>
<input id="sens-sensitive" name="sensitivity" type="radio" value="sensitive"><label for="sens-sensitive">Sensitive - health, financial or identity records</label>
<input id="sens-high" name="sensitivity" type="radio" value="high"><label for="sens-high">Highly sensitive - managed, research or regulated data</label>
</div></fieldset>
<div class="tile-progress" id="tileProgress" aria-live="polite"></div>
</div>
<div class="form-footer">
<div class="form-status"><p class="form-note">These answers drive a transparent decision. They stay in this tab and are not sent to RebootLoop.</p><p class="error-message" id="formError" role="alert" aria-live="polite"></p></div>
<div class="button-row"><button class="quiet-button" type="button" data-go="1">Back</button><button class="primary-button" type="submit">See my recommended route <span aria-hidden="true">→</span></button></div>
</div>
</form>
</div>
</section>
<section class="screen" id="screen-3" data-screen="3" aria-labelledby="route-title">
<div class="surface focus-surface route-layout">
<div class="main-panel">
<div class="route-kicker"><span class="route-badge" id="routeBadge">Recommended route</span><p class="eyebrow">Step 3 - route</p></div>
<h2 id="route-title">Prepare, then donate.</h2>
<p class="route-reason" id="routeReason"></p>
<div class="safety-callout" id="routeWarning"></div>
<div class="button-row"><button class="quiet-button" type="button" data-go="2">Change answers</button><button class="primary-button" type="button" id="routeNext">View preparation path <span aria-hidden="true">→</span></button></div>
<div class="more-list">
<details class="more">
<summary>Why this route?</summary>
<ul class="reason-list" id="routeReasons"></ul>
<p class="more-label">Your answers, not a hidden score</p>
<ul class="summary-list" id="answerSummary"></ul>
</details>
<details class="more">
<summary id="destinationSummary">What happens next</summary>
<div class="route-destination" id="routeDestination" aria-live="polite"></div>
</details>
</div>
</div>
</div>
</section>
<section class="screen" id="screen-4" data-screen="4" aria-labelledby="prepare-title">
<div class="surface focus-surface prepare-layout">
<div class="main-panel">
<p class="eyebrow">Step 4 - next steps</p>
<h2 id="prepare-title">Secure preparation checklist</h2>
<p class="route-reason" id="prepareIntro"></p>
<div class="safety-callout"><strong>Residual-risk warning:</strong> These steps reduce risk but do not guarantee that data is unrecoverable. If you cannot complete a step, stop and seek appropriate professional help.</div>
<div class="checklist" id="checklist"></div>
<div id="specialistActions"></div>
<a class="official-link" id="officialLink" href="https://www.cyber.gov.au/protect-yourself/securing-your-devices/how-secure-your-device/how-dispose-your-device-securely" target="_blank" rel="noreferrer">Open official guidance <span aria-hidden="true">↗</span></a>
<div class="button-row"><button class="quiet-button" type="button" data-go="3">Back</button><button class="primary-button" type="button" id="finishStop">Finish and assess another device</button><a class="quiet-button feedback-link" href="contact.html#feedback" target="_blank" rel="noopener">Give feedback <span aria-hidden="true">↗</span></a></div>
<div class="more-list">
<details class="more">
<summary>What a tick does and doesn't prove</summary>
<div class="assurance-card">
<h3>Status must match evidence.</h3>
<p>A tick is a declaration, not a security certificate.</p>
<div class="assurance-levels">
<div class="assurance-level is-current" id="assuranceGuided"><strong>1. Guided</strong><br><small>A route and guidance were generated.</small></div>
<div class="assurance-level" id="assuranceDeclared"><strong>2. Self-declared</strong><br><small>The user says every applicable step is complete.</small></div>
<div class="assurance-level"><strong>3. Partner-verified</strong><br><small>Future pilot only - local service matching and verification require approved partners and evidence.</small></div>
</div>
</div>
</details>
</div>
</div>
</div>
</section>
<section class="screen" id="screen-5" data-screen="5" aria-labelledby="steps-title">
<div class="surface">
<div class="main-panel">
<div class="steps-head">
<div><p class="eyebrow">Step 4 - next steps</p><h2 id="steps-title" style="margin:0">Finish the job.</h2></div>
<div class="steps-progress"><span id="stepsCount">0 of 2 done</span><div class="steps-bar"><i id="stepsBar"></i></div></div>
</div>
<div class="safety-callout"><strong>Residual-risk warning:</strong> These steps reduce risk but do not guarantee that data is unrecoverable. If you cannot complete a step, stop and seek appropriate professional help.</div>
<div class="action-cards" id="actionCards"></div>
<div class="steps-foot">
<details class="more">
<summary>Stuck on a step?</summary>
<p class="micro-note">You don't have to finish now. RebootLoop can't check the device or confirm erasure; if any step fails, stop and get help. These are official guidance and public services for your route; RebootLoop has no partnership with them.</p>
<div class="help-links" id="helpLinks"></div>
</details>
<div class="button-row"><button class="quiet-button" type="button" data-go="3">Back</button><button class="primary-button" type="button" id="showImpact">See your impact <span aria-hidden="true">→</span></button></div>
</div>
</div>
</div>
</section>
<section class="screen" id="screen-6" data-screen="6" aria-labelledby="impact-title">
<div class="surface finale">
<div class="impact-hero finale-hero">
<div class="finale-top">
<div>
<p class="eyebrow">Step 5 - your impact <span aria-hidden="true">🎉</span></p>
<h2 id="impact-title"></h2>
<p class="impact-lede" id="impactLede"></p>
</div>
<div class="finale-stat" id="impactStats"></div>
</div>
</div>
<div class="finale-next">
<h3 id="nextTitle">Where to go next</h3>
<div class="next-links" id="nextLinks"></div>
<div class="finale-foot">
<div class="save-inline">
<span>Save your results</span>
<a id="saveEmail" href="#">✉️ Email</a>
<a id="saveText" href="#">💬 Text</a>
<button type="button" id="saveShare" hidden>📤 Share</button>
<button type="button" id="saveCopy">📋 Copy</button>
<span class="save-confirm" id="saveConfirm" aria-live="polite"></span>
</div>
<div class="button-row finale-buttons"><button class="quiet-button" type="button" data-go="5">Back to my steps</button><button class="secondary-button" type="button" id="assessAnother">Assess another device</button><button class="quiet-button" type="button" id="restartDemo">Start over</button></div>
</div>
<div class="finale-extras">
<details class="more">
<summary>This session's totals</summary>
<p class="finale-session">Circular routes <strong id="metricCircular">0</strong> · safety stops <strong id="metricStops">0</strong> · CO₂e avoided (est.) <strong id="metricCarbon">0 kg</strong> · e-waste diverted (est.) <strong id="metricMass">0 kg</strong></p>
</details>
<p class="feedback-inline"><strong>How did we do?</strong> <a class="feedback-link" href="contact.html#feedback" target="_blank" rel="noopener">Give feedback <span aria-hidden="true">↗</span></a></p>
</div>
<p class="finale-note">Impact is an estimate for a typical device, based on manufacturers' published footprints, and applies once the job is done. RebootLoop has no partnership with the services linked here. <a href="why.html#carbon" target="_blank" rel="noopener">How we estimate ↗</a></p>
</div>
</div>
</section>
</main>
<footer class="site-footer"><p class="footer-feedback">Tried the demo? <a class="feedback-link" href="contact.html#feedback" target="_blank" rel="noopener">Tell us what you think ↗</a></p><strong>RebootLoop Team:</strong> <a class="team-link" href="https://www.linkedin.com/in/shah-noor-mostafa-a42b8134a" target="_blank" rel="noopener noreferrer" aria-label="Shah Noor Mostafa Bhuiyan on LinkedIn">Shah Noor Mostafa Bhuiyan</a> · <a class="team-link" href="https://au.linkedin.com/in/nafe-ibne-mamun-7329b1255" target="_blank" rel="noopener noreferrer" aria-label="Nafe Ibne Mamun on LinkedIn">Nafe Ibne Mamun</a> · <a class="team-link" href="https://au.linkedin.com/in/kaicryan" target="_blank" rel="noopener noreferrer" aria-label="Alexander Kai Cryan on LinkedIn">Alexander Kai Cryan</a><br>Independent student project · Phase 2 public prototype · Your in-tab assessment resets on reload</footer>
<script>
(() => {
const deviceLabels = { iphone: "iPhone", ipad: "iPad", android: "Android device", windows: "Windows PC", mac: "Mac", chromeos: "Chromebook / ChromeOS device", linux: "Linux computer", console: "Game console", storage: "HDD / SSD / USB / SD", other: "Other technology" };
const optionLabels = {
ownership: { owned: "Owned by user", managed: "University / employer managed", other: "Not owned / borrowed / found" },
power: { works: "Works normally", intermittent: "Intermittent", off: "Does not power on" },
safety: { normal: "No physical hazard reported", swollen: "Swollen / leaking battery", heat: "Heat / odour", damaged: "Seriously damaged" },
condition: { good: "Good", repairable: "Repairable", eol: "End-of-life", unsure: "Unsure" },
goal: { repair: "Keep / repair", sell: "Sell", donate: "Donate", family: "Give to family", recycle: "Recycle", unsure: "Unsure" },
sensitivity: { everyday: "Everyday", sensitive: "Sensitive", high: "Highly sensitive" }
};
const sources = {
iphone: { url: "https://support.apple.com/en-au/109511", label: "Open Apple's iPhone transfer and erase guidance" },
ipad: { url: "https://support.apple.com/en-au/109511", label: "Open Apple's iPad transfer and erase guidance" },
android: { url: "https://support.google.com/accounts/answer/17299872?hl=en", label: "Open Google's old-Android-device preparation guidance" },
windows: { url: "https://support.microsoft.com/en-US/Windows/Experience/backup-recovery/reset-your-pc", label: "Open Microsoft's Reset your PC guidance" },
mac: { url: "https://support.apple.com/en-au/102773", label: "Open Apple's current Mac transfer and erase guidance" },
chromeos: { url: "https://support.google.com/chromebook/answer/183084?hl=en", label: "Open Google's current Chromebook reset guidance" },
linux: { url: "https://www.cyber.gov.au/protect-yourself/securing-your-devices/how-secure-your-device/how-dispose-your-device-securely", label: "Open ASD ACSC secure-disposal guidance" },
console: { url: "https://www.cyber.gov.au/protect-yourself/securing-your-devices/how-secure-your-device/how-dispose-your-device-securely", label: "Open ASD ACSC gaming-device disposal guidance" },
storage: { url: "https://www.cyber.gov.au/protect-yourself/securing-your-devices/how-secure-your-device/how-dispose-your-device-securely", label: "Open ASD ACSC secure-disposal guidance" },
other: { url: "https://www.cyber.gov.au/protect-yourself/securing-your-devices/how-secure-your-device/how-dispose-your-device-securely", label: "Open ASD ACSC secure-disposal guidance" }
};
const transferChecklist = [
["Back up what you need", "Copy important files to another secure device before any erase or reset."],
["Transfer access", "Move authenticator apps, passkeys and recovery methods needed for your accounts."],
["Remove connections", "Sign out or unlink accounts, payment methods and trusted-device relationships where applicable."],
["Remove physical media", "Take out SIM cards, SD cards, USB devices and any other removable media."],
["Follow the official erase guide", "Use the current manufacturer guidance linked below for this device and software version."],
["Check the handover state", "Where the device supports it, check for the initial setup screen and remove activation locks before transfer."],
];
const recycleChecklist = [
["Back up what you need", "Save important files before they are lost."],
["Transfer account access", "Move authenticator apps and recovery methods that you still need."],
["Remove removable media", "Take out SIM cards, SD cards and other media and handle them separately."],
["Use the official erase guide", "Follow the manufacturer's current steps if the device can be reset. Stop and seek advice if it fails."],
["Check accepted items", "Confirm the collection point accepts this device and follow its current safety instructions."],
];
const repairChecklist = [
["Back up important files", "Keep a current backup before a repair attempt."],
["Choose a suitable repairer", "Ask about repair scope, cost and device-data practices before handover."],
["Limit access", "Remove unnecessary sensitive information and avoid sharing passwords where possible; ask what access is genuinely needed."],
["Agree on the return", "Confirm how and when you will receive the device and check it after service."],
];
const platformExtras = {
iphone: ["For Apple devices", "Unpair an Apple Watch if relevant, choose how to handle the eSIM, and confirm the device reaches the Hello screen."],
ipad: ["For iPad", "Follow Apple's model-specific erase and account guidance, then check the device is ready for a new user."],
android: ["For Android devices", "Remove the Google Account before reset where required so device protection does not block the next owner."],
windows: ["For Windows PCs", "Use Reset this PC, Remove everything and the clean-data option when transferring the device. This is consumer preparation, not certified sanitisation."],
mac: ["For Mac computers", "Use Erase All Content and Settings when supported; otherwise follow Apple's model-specific process and remove Find My / Activation Lock."],
chromeos: ["For Chromebooks", "Remove accounts and follow Google's current Powerwash guidance. Confirm the device returns to its initial setup screen before transfer."],
linux: ["For Linux computers", "Back up required files, remove accounts and encryption recovery material, then follow current device, distribution and storage-media guidance. Seek specialist help when the correct erase process is uncertain."],
console: ["For game consoles", "Back up saved games, deactivate or unlink the console, remove discs and external storage, then follow the manufacturer's current factory-reset instructions."],
storage: ["For storage media", "Identify whether it is an HDD, SSD, USB or SD card. A quick format alone is not proof of secure erasure."],
other: ["For this device", "Use the manufacturer's current preparation instructions and seek professional help if the correct process is unclear."]
};
let currentData = null;
let currentRoute = null;
let assessmentCount = 0;
let assessmentCounted = false;
let planCount = 0;
// Session impact, one entry per assessment so changed answers replace rather than add.
const sessionImpact = new Map();
let assessmentId = 0;
// Typical device weights in kg, for the e-waste estimate. VERIFY before showcase.
const typicalMass = { iphone: 0.2, android: 0.2, ipad: 0.5, windows: 1.8, mac: 1.4, chromeos: 1.3, linux: 1.8, console: 4, storage: 0.1, other: 0.5 };
function goTo(number) {
document.querySelectorAll(".screen").forEach(el => el.classList.toggle("is-active", Number(el.dataset.screen) === number));
// Screen 4 (stop / specialist actions) and screen 5 (impact) are alternative step 4s.
const progress = { 5: 4, 6: 5 }[number] || number;
const stopPath = number === 4 || (currentRoute && currentRoute.type !== "normal");
document.getElementById("step4Label").textContent = stopPath ? "Safe next steps" : "Next steps";
document.getElementById("step5").hidden = stopPath;
document.querySelectorAll(".progress-step").forEach(el => {
const step = Number(el.dataset.progress);
el.classList.toggle("is-active", step === progress);
el.classList.toggle("is-complete", step < progress);
});
window.scrollTo({ top: 0, behavior: "smooth" });
const heading = document.querySelector(`#screen-${number} h1, #screen-${number} h2`);
if (heading) { heading.setAttribute("tabindex", "-1"); heading.focus({ preventScroll: true }); }
}
function setRadio(name, value) {
const input = document.querySelector(`input[name="${name}"][value="${value}"]`);
if (input) input.checked = true;
}
function loadDemo(kind) {
document.getElementById("assessmentForm").reset();
const demo = kind === "a"
? { ownership: "owned", device: "iphone", power: "works", safety: "normal", condition: "good", goal: "donate", sensitivity: "everyday" }
: { ownership: "owned", device: "windows", power: "off", safety: "normal", condition: "unsure", goal: "recycle", sensitivity: "high" };
Object.entries(demo).forEach(([key, value]) => setRadio(key, value));
document.getElementById("formError").textContent = "";
syncPickers();
}
// Each question shows a select button; its radio answers open in a floating list.
const pickers = [...document.querySelectorAll("#assessmentForm fieldset")];
function syncPickers() {
pickers.forEach(fieldset => {
const checked = fieldset.querySelector("input:checked");
const trigger = fieldset.querySelector(".picker-trigger");
trigger.querySelector(".picker-value").textContent = checked ? fieldset.querySelector(`label[for="${checked.id}"]`).textContent : "Choose…";
trigger.classList.toggle("has-value", Boolean(checked));
if (checked) fieldset.classList.remove("is-missing");
});
renderTiles();
}
// The device tile shows the chosen device's icon, and the last tile rings up progress.
const deviceIcon = { iphone: "📱", android: "📱", ipad: "📱", windows: "💻", mac: "💻", chromeos: "💻", linux: "💻", console: "🎮", storage: "💾", other: "🔌" };
function progressRing(done, total) {
const r = 26, c = 2 * Math.PI * r;
return `<svg class="ring" viewBox="0 0 64 64" aria-hidden="true"><circle cx="32" cy="32" r="${r}" class="ring-track"/><circle cx="32" cy="32" r="${r}" class="ring-fill" stroke-dasharray="${c}" stroke-dashoffset="${c * (1 - done / total)}"/></svg><span class="ring-count">${done}<small>/${total}</small></span>`;
}
function renderTiles() {
const device = document.querySelector('input[name="device"]:checked')?.value;
document.querySelector("#assessmentForm .device-q").dataset.icon = deviceIcon[device] || "📱";
const done = pickers.filter(fieldset => fieldset.querySelector("input:checked")).length;
const progress = document.getElementById("tileProgress");
progress.classList.toggle("is-done", done === pickers.length);
progress.innerHTML = `<div class="ring-wrap">${progressRing(done, pickers.length)}</div><span>${done === pickers.length ? "All set. See your route below." : done === 0 ? "Tap a tile to start" : `${pickers.length - done} to go`}</span>`;
}
function setPickerOpen(fieldset, open) {
pickers.forEach(other => {
const isOpen = open && other === fieldset;
other.classList.toggle("is-open", isOpen);
other.querySelector(".picker-trigger").setAttribute("aria-expanded", String(isOpen));
});
if (open) (fieldset.querySelector("input:checked") || fieldset.querySelector("input")).focus();
}
function closePicker(fieldset) {
setPickerOpen(fieldset, false);
fieldset.querySelector(".picker-trigger").focus();
}
// After an answer, open the next unanswered question, or move on to the submit button.
function advancePicker(fieldset) {
if (!fieldset.querySelector("input:checked")) return;
const index = pickers.indexOf(fieldset);
const next = [...pickers.slice(index + 1), ...pickers.slice(0, index)].find(other => !other.querySelector("input:checked"));
if (next) {
setPickerOpen(next, true);
next.scrollIntoView({ behavior: "smooth", block: "nearest" });
} else {
setPickerOpen(fieldset, false);
document.querySelector("#assessmentForm [type=submit]").focus();
}
}
pickers.forEach((fieldset, index) => {
const list = fieldset.querySelector(".choices");
list.id = `choices-${index}`;
const trigger = document.createElement("button");
trigger.type = "button";
trigger.className = "picker-trigger";
trigger.setAttribute("aria-expanded", "false");
trigger.setAttribute("aria-controls", list.id);
const helper = fieldset.querySelector(".helper");
helper.id = `helper-${index}`;
trigger.setAttribute("aria-describedby", helper.id);
trigger.innerHTML = '<span class="picker-value">Choose…</span><span class="picker-caret" aria-hidden="true">▾</span>';
list.before(trigger);
trigger.addEventListener("click", () => setPickerOpen(fieldset, !fieldset.classList.contains("is-open")));
list.addEventListener("change", syncPickers);
// Mouse picks confirm straight away; arrow keys only move the selection, and Enter or Space confirms it.
// Deferred so the label has checked its radio before the next list takes focus.
list.addEventListener("click", event => { if (event.detail > 0) setTimeout(() => advancePicker(fieldset)); });
list.addEventListener("keydown", event => {
if (event.key === "Enter") {
event.preventDefault();
if (!event.target.checked) event.target.click();
advancePicker(fieldset);
}
if (event.key === "Escape") closePicker(fieldset);
});
list.addEventListener("keyup", event => { if (event.key === " ") setTimeout(() => advancePicker(fieldset)); });
});
syncPickers();
document.addEventListener("pointerdown", event => {
const open = pickers.find(fieldset => fieldset.classList.contains("is-open"));
if (open && !open.contains(event.target)) setPickerOpen(open, false);
});
function collectForm() {
const form = new FormData(document.getElementById("assessmentForm"));
const required = ["ownership", "device", "power", "safety", "condition", "goal", "sensitivity"];
const missing = required.filter(name => !form.get(name));
if (missing.length) {
const first = document.querySelector(`input[name="${missing[0]}"]`);
document.getElementById("formError").textContent = `Please answer ${missing.length === 1 ? "the remaining question" : `all ${missing.length} remaining questions`}.`;
pickers.forEach(fieldset => fieldset.classList.toggle("is-missing", missing.includes(fieldset.querySelector("input").name)));
first?.closest("fieldset")?.scrollIntoView({ behavior: "smooth", block: "center" });
first?.closest("fieldset")?.querySelector(".picker-trigger").focus();
return null;
}
document.getElementById("formError").textContent = "";
return Object.fromEntries(required.map(name => [name, form.get(name)]));
}
function evaluate(d) {
const transferGoal = ["sell", "donate", "family"].includes(d.goal);
if (d.safety !== "normal") return {
id: "hazard", type: "stop", title: "Use a qualified battery or e-waste service.",
short: "Hazard specialist",
reason: "A swollen, leaking, hot or seriously damaged device needs specialist handling before any data or circularity decision.",
reasons: ["Physical safety is RebootLoop's highest-priority rule.", "Normal charging, posting and self-service preparation are suppressed.", "The correct local pathway depends on the battery and device type."],
warning: `Do not charge, puncture, crush or post the device. Seek current advice from a qualified service or local council.${d.ownership !== "owned" ? " Also contact the owner or authorised IT team." : ""}`,
actions: ["Do not continue ordinary handling.", "Move away from heat or flammable materials only if it is safe to do so.", "Contact a qualified battery / e-waste service or local council for instructions."],
next: "View hazard actions"
};
if (d.ownership !== "owned") return {
id: "return", type: "stop", title: "Return it to the owner or authorised IT.",
short: "Return to owner / IT",
reason: "RebootLoop cannot authorise the transfer, preparation or destruction of a device you do not own.",
reasons: ["Managed devices may contain organisational data and must follow the owner's process.", "No erasure or transfer checklist is offered for this stop route."],
warning: "Do not reset, sell, donate or recycle this device. Contact the owner or the relevant university / employer IT team.",
actions: ["Stop the disposal flow.", "Keep the device secure.", "Contact the owner or authorised IT service."],
next: "View safe return actions"
};
if (d.sensitivity === "high") return {
id: "high", type: "specialist", title: "Get professional data-handling advice first.",
short: "Professional data review",
reason: "Highly sensitive, research or regulated data needs a process appropriate to its obligations before repair, transfer or recycling.",
reasons: ["A general-purpose checklist cannot verify the necessary sanitisation standard.", "The right outcome depends on the information and storage medium.", "This app cannot create a verified destruction record."],
warning: "Keep the device secure. Ask the responsible data owner or a qualified professional about sanitisation or destruction before any handover.",
actions: ["Do not self-certify erasure in this app.", "Consult the responsible data owner or an appropriate IT / data-destruction professional.", "Agree on the next physical route only after the data process is defined."],
next: "View specialist actions"
};
if (d.power === "off") return {
id: "off", type: "specialist", title: "Resolve data handling before handover.",
short: "Data review before handover",
reason: "The device does not power on, so you cannot complete or check a normal on-device reset here.",
reasons: ["An unavailable reset must not be marked complete.", "A provider may need a medium-specific process before repair or recycling.", "The physical route can be chosen after data handling is resolved."],
warning: "Keep the device secure and consult a suitable professional if data may remain. Do not claim the device was erased.",
actions: ["Keep the device secure.", "Ask a qualified provider about access, sanitisation or destruction appropriate to the device and data.", "Check accepted items and handling terms before any handover."],
next: "View specialist actions"
};
if (d.device === "storage") return {
id: "storage", type: "specialist", title: "Identify the storage medium and erase method.",
short: "Media-specific data review",
reason: "HDDs, SSDs, USB drives and SD cards need different processes; a generic checkbox cannot establish that data is gone.",
reasons: ["The exact medium and any encryption affect the correct process.", "A quick format is not evidence of secure erasure.", "Reuse or recycling follows a suitable data process."],
warning: "Keep the media secure until you have checked current manufacturer / ACSC guidance or consulted a qualified provider.",
actions: ["Identify the precise media type and the information it held.", "Follow current manufacturer / ACSC advice or seek professional sanitisation.", "Check the receiving service accepts the item before drop-off."],
next: "View specialist actions"
};
if (d.goal === "repair" && ["good", "repairable"].includes(d.condition)) return {
id: "repair", type: "normal", title: "Repair and extend its useful life.",
short: "Repair / extend life", circular: true,
reason: "The device is still useful or repairable, and keeping it in service avoids a premature end-of-life decision.",
reasons: ["Reuse and repair sit ahead of recycling in this decision flow.", "Back up first and give a repairer only the access they need.", "A repair outcome can be recorded without storing your identity."],
warning: "Ask the repairer how they handle access to devices and data. Back up important files before handover.",
next: "Show my next steps"
};
if (d.power === "works" && d.condition === "good" && transferGoal) {
const action = d.goal === "donate" ? "donate" : d.goal === "sell" ? "sell" : "give to family";
return {
id: "transfer", type: "normal", title: `Prepare, then ${action}.`,
short: `Prepare -> ${action}`, circular: true,
reason: "The device works, appears reusable and is intended for another person. Secure preparation must happen before it changes hands.",
reasons: ["Reuse keeps a functioning device in circulation.", "Official platform guidance reduces avoidable account and data risks.", "Official manufacturer steps come first; RebootLoop never certifies erasure."],
warning: "Follow the current manufacturer instructions for the exact device and software version. If any step fails, stop and seek professional help.",
next: "Show my next steps"
};
}
if (d.condition === "repairable") return {
id: "repair", type: "normal", title: "Seek a repair assessment first.",
short: "Repair assessment", circular: true,
reason: "The device may have useful life remaining, so a repair decision should come before recycling.",
reasons: ["A repair assessment avoids discarding recoverable value.", "Data precautions still apply before a third party receives the device.", "If repair is uneconomic, the flow can continue to responsible recycling."],
warning: "Back up what you can and ask the repair provider how it protects device data.",
next: "Show my next steps"
};
return {
id: "recycle", type: "normal", title: "Prepare, then recycle responsibly.",
short: "Prepare -> responsible recycling", circular: true,
reason: "The device is not currently a confident reuse candidate, so preparation comes before an accepted-item recycling pathway.",
reasons: ["The flow does not send electronics to general waste.", "Data preparation is addressed before handover.", "Existing accredited or otherwise verified services remain the recycling endpoint."],
warning: "Check the service's accepted items and current data-handling information before drop-off.",
next: "Show my next steps"
};
}
function showRoute() {
currentRoute = evaluate(currentData);
if (!assessmentCounted) { assessmentCount += 1; assessmentCounted = true; assessmentId += 1; }
const range = impactRanges[impactCategory[currentData.device]];
sessionImpact.set(assessmentId, {
circular: Boolean(currentRoute.circular),
carbon: currentRoute.circular && currentRoute.id !== "recycle" && range ? range : [0, 0],
mass: currentRoute.circular ? typicalMass[currentData.device] || 0 : 0
});
updateMetrics();
const badge = document.getElementById("routeBadge");
badge.textContent = currentRoute.type === "stop" ? "Safety / authority stop" : currentRoute.type === "specialist" ? "Specialist route" : "Recommended circular route";
badge.className = `route-badge${currentRoute.type === "stop" ? " is-stop" : currentRoute.type === "specialist" ? " is-specialist" : ""}`;
document.getElementById("route-title").textContent = currentRoute.title;
document.getElementById("routeReason").textContent = currentRoute.reason;
document.getElementById("routeReasons").innerHTML = currentRoute.reasons.map(r => `<li>${r}</li>`).join("");
document.getElementById("routeWarning").innerHTML = `<strong>Safety boundary:</strong> ${currentRoute.warning}`;
document.getElementById("routeNext").innerHTML = `${currentRoute.next} <span aria-hidden="true">→</span>`;
const summary = [
["Authority", optionLabels.ownership[currentData.ownership]],
["Device", deviceLabels[currentData.device]],
["Power", optionLabels.power[currentData.power]],
["Physical safety", optionLabels.safety[currentData.safety]],
["Condition", optionLabels.condition[currentData.condition]],
["Goal", optionLabels.goal[currentData.goal]],
["Sensitivity", optionLabels.sensitivity[currentData.sensitivity]],
];
document.getElementById("answerSummary").innerHTML = summary.map(([k,v]) => `<li><span>${k}</span><strong>${v}</strong></li>`).join("");
showDestination();
goTo(3);
}
function showDestination() {
const box = document.getElementById("routeDestination");
const safeLink = (url, label) => `<a href="${url}" target="_blank" rel="noopener noreferrer">${label} ↗</a>`;
const government = "https://www.dcceew.gov.au/environment/protection/waste/consumers/recycling-drop-off";
if (currentRoute.type === "stop") {
box.innerHTML = `<h3>Next action: stop and ask</h3><p>No self-service drop-off or collection is suggested for this case. Follow the safe actions on the next screen.</p><small>No booking or handover is made in RebootLoop.</small>`;
} else if (currentRoute.type === "specialist") {
box.innerHTML = `<h3>Next action: resolve data handling</h3><p>Seek suitable advice before any handover. The Australian Cyber Security Centre explains when professional help may be needed.</p>${safeLink(sources.storage.url, "Read official secure-disposal guidance")}<small>This is public guidance, not a booked or verified provider.</small>`;
} else if (currentRoute.id === "repair") {
box.innerHTML = `<h3>Next action: ask a repairer</h3><p>Get a diagnosis, cost and data-handling terms before you leave the device. We have not vetted or booked a repair provider.</p><small>A recommendation does not confirm a repair happened.</small>`;
} else if (currentRoute.id === "transfer") {
box.innerHTML = `<h3>Next action: prepare for your recipient</h3><p>Confirm the person or organisation wants this working device, then complete the current manufacturer steps before transfer. We have not verified a donation recipient.</p><small>A self-declaration does not confirm a donation or displaced purchase.</small>`;
} else if (["iphone", "android"].includes(currentData.device)) {
box.innerHTML = `<h3>Existing public phone pathway</h3><p>MobileMuster lists phone drop-off points and free post-back options. Check the site's accepted items and battery instructions before going.</p>${safeLink("https://www.mobilemuster.com.au/recycle-a-mobile/", "Find MobileMuster public options")}<small>MobileMuster has been contacted for feedback; no partnership or referral agreement is confirmed. Do not post a hazardous device.</small>`;
} else if (["windows", "mac", "chromeos", "linux"].includes(currentData.device)) {
box.innerHTML = `<h3>Existing public computer pathway</h3><p>Australia's National Television and Computer Recycling Scheme gives households and small businesses access to designated collection sites. Check the exact accepted items and opening hours.</p>${safeLink("https://www.dcceew.gov.au/environment/protection/waste/product-stewardship/television-computer-recycling-scheme", "Read NTCRS collection guidance")}<small>Public information only; no site, pickup or service agreement is confirmed here.</small>`;
} else {
box.innerHTML = `<h3>Check local accepted items</h3><p>For an iPad, console or other device, use the Australian Government's drop-off guidance and confirm a local service accepts this exact item.</p>${safeLink(government, "Read government e-waste drop-off guidance")}<small>No particular collection point is booked or endorsed.</small>`;
}
document.getElementById("destinationSummary").textContent = box.querySelector("h3").textContent;
}
function setFeedbackLinks() {
const tried = `${currentRoute.title} · ${planNoun[currentData.device]}`;
document.querySelectorAll(".feedback-link").forEach(link => { link.href = `contact.html?tried=${encodeURIComponent(tried)}#feedback`; });
}
function buildPrepare() {
const official = document.getElementById("officialLink");
official.href = sources.storage.url;
official.innerHTML = `${sources.storage.label} <span aria-hidden="true">↗</span>`;
document.getElementById("prepare-title").textContent = "Safe next actions";
document.getElementById("prepareIntro").textContent = "This route deliberately withholds a self-erasure checklist. Follow only the safe escalation actions below.";
document.getElementById("checklist").innerHTML = "";
document.getElementById("specialistActions").innerHTML = `<ul class="action-list">${currentRoute.actions.map(a => `<li>${a}</li>`).join("")}</ul>`;
setFeedbackLinks();
goTo(4);
}
const planNoun = { iphone: "iPhone", ipad: "iPad", android: "Android phone", windows: "Windows PC", mac: "Mac", chromeos: "Chromebook", linux: "Linux computer", console: "game console", storage: "storage device", other: "device" };
// Estimated manufacturing (production-phase) emissions of a typical new device, kg CO2e.
// Phones: Apple iPhone 16 (56 kg x 80% = 44.8) to iPhone 16 Pro Max (74 kg, similar share ~ 59).
// Tablets and computers are placeholder ranges - VERIFY against current manufacturer
// product environmental reports before the showcase and record them in docs/evidence-sources.md.
const impactRanges = { phone: [45, 60], tablet: [50, 90], computer: [100, 250] };
const impactCategory = { iphone: "phone", android: "phone", ipad: "tablet", windows: "computer", mac: "computer", chromeos: "computer", linux: "computer" };
const KG_CO2E_PER_CAR_KM = 0.17; // rough average passenger car; VERIFY before showcase
// What a correctly reset device shows first, and the lock that means it isn't ready.
const handoverCheck = {
iphone: ["the Hello screen", "an Apple Account or passcode from the previous owner (Activation Lock)"],
ipad: ["the Hello screen", "an Apple Account or passcode from the previous owner (Activation Lock)"],
android: ["the first-time setup screen", "the previous owner's Google Account (Factory Reset Protection)"],
windows: ["Windows setup, asking for your country or region", "the previous owner's account or password"],
mac: ["Setup Assistant, asking for your country or region", "the previous owner's Apple Account (Activation Lock)"],
chromeos: ["the Chromebook welcome screen", "a previous owner's account or a school / work enrolment"],
linux: ["an installer or first-time setup with no previous user accounts", "a previous user's password or disk-encryption passphrase"],
console: ["the console's first-time setup screen", "the previous owner's account, still signed in"],
other: ["a first-time setup or factory screen", "the previous owner's account, PIN or passcode"]
};
function recycleDestination() {
if (["iphone", "android"].includes(currentData.device)) return { url: "https://www.mobilemuster.com.au/recycle-a-mobile/", label: "Find a MobileMuster drop-off point", note: "Free drop-off points and post-back satchels. Check the battery instructions first." };
if (["windows", "mac", "chromeos", "linux"].includes(currentData.device)) return { url: "https://www.dcceew.gov.au/environment/protection/waste/product-stewardship/television-computer-recycling-scheme", label: "Find an NTCRS collection site", note: "Free for households. Check the site's accepted items and opening hours." };
return { url: "https://www.dcceew.gov.au/environment/protection/waste/consumers/recycling-drop-off", label: "Find an e-waste drop-off", note: "Confirm the site accepts this exact item before you go." };
}
function impactContent(noun) {
const range = impactRanges[impactCategory[currentData.device]];
const km = range && range.map(kg => Math.round(kg / KG_CO2E_PER_CAR_KM / 10) * 10);
const isPhone = impactCategory[currentData.device] === "phone";
const carbonStat = range && {
big: `${range[0]}–${range[1]} kg`,
label: currentRoute.id === "repair"
? `of CO₂e from making a new ${noun}, avoided by keeping this one going`
: `of CO₂e from making a new ${noun}, avoided when yours is used instead`,
small: `About ${km[0]}–${km[1]} km of driving in an average car. <a href="why.html#carbon" target="_blank" rel="noopener">How we estimate ↗</a>`
};
if (currentRoute.id === "transfer") {
const who = { donate: "someone who needs it", sell: "its next owner", family: "someone in your family" }[currentData.goal] || "its next owner";
return {
title: `Nice work. Your ${noun} is getting a second life.`,
lede: `Most old tech sits forgotten in a drawer. You chose to give yours to ${who} instead. Thank you.`,
stat: carbonStat || { big: "1 less", label: `new ${noun} that needs to be made when yours is used instead.`, small: "" }
};
}
if (currentRoute.id === "repair") return {
title: `Nice work. You're keeping your ${noun} going.`,
lede: "Repair is the greenest choice there is: nothing new made, nothing thrown away. Thank you.",
stat: carbonStat || { big: "0", label: "new devices made when you repair instead of replacing.", small: "" }
};
return {
title: `Nice work. Your ${noun} is becoming something new.`,
lede: "Instead of a drawer or landfill, its metals and battery go back into new products. Thank you.",
stat: isPhone
? { big: "96%", label: "of materials from phones MobileMuster collects are kept out of landfill.", small: '<a href="https://www.mobilemuster.com.au/about-us/" target="_blank" rel="noopener noreferrer">MobileMuster</a>' }
: { big: "511,000 t", label: "of e-waste in Australia in 2019. Recycling yours helps bring it down.", small: '<a href="https://www.dcceew.gov.au/environment/protection/waste/e-waste" target="_blank" rel="noopener noreferrer">DCCEEW</a>' }
};
}
function buildImpact() {
const noun = planNoun[currentData.device];
const content = impactContent(noun);
document.getElementById("impact-title").textContent = content.title;
document.getElementById("impactLede").textContent = content.lede;
const { stat } = content;
document.getElementById("impactStats").innerHTML = `<strong>${stat.big}</strong><span>${stat.label}</span>${stat.small ? `<small>${stat.small}</small>` : ""}`;
renderNextLinks(noun);
// Next steps
const source = sources[currentData.device] || sources.other;
const items = currentRoute.id === "repair" ? repairChecklist : [platformExtras[currentData.device] || platformExtras.other, ...(currentRoute.id === "transfer" ? transferChecklist : recycleChecklist)];
const checklistHtml = items.map(([title, desc], i) => `<div class="check-item"><input type="checkbox" id="check-${i}"><label for="check-${i}"><span><strong>${title}</strong><small>${desc}</small></span></label></div>`).join("");
const card = (n, title, time, desc, body) => `<div class="action-card" data-step="${n}"><div class="action-top"><div class="action-num">${n}</div><div><button class="done-toggle" type="button" aria-pressed="false">Mark done</button><span class="action-time">${time}</span><h3>${title}</h3><p>${desc}</p></div></div>${body}</div>`;
const cards = [];
if (currentRoute.id === "repair") {
cards.push(card(1, "Back it up", "About 15 minutes", "Save what matters before anyone else touches your device.",
`<details><summary>Show the prep checklist</summary><div class="checklist">${checklistHtml}</div></details>`));
cards.push(card(2, "Get a repair quote", "About 10 minutes", "Ask a repairer these questions before you leave the device with them.",
`<ul class="plain"><li>What's wrong, and what will it cost?</li><li>Do you need my passcode, or can you work without it?</li><li>Who can access my data while it's here?</li><li>When will it be ready, and is the repair guaranteed?</li></ul>`));
} else {
cards.push(card(1, "Wipe it", "About 20 minutes", `Follow the official steps so none of your data goes with your ${noun}.`,
`<div class="action-links"><a class="primary-button" href="${source.url}" target="_blank" rel="noopener noreferrer">${source.label} ↗</a></div><details><summary>Show the step-by-step checklist</summary><div class="checklist">${checklistHtml}</div></details>`));
if (currentRoute.id === "transfer") {
const [screen, lock] = handoverCheck[currentData.device] || handoverCheck.other;
cards.push(card(2, "Hand it over", "5 minutes", "Give it to its next owner with this note, so they can check it's properly reset.",
`<div class="action-links"><button class="secondary-button" type="button" id="printSlip">Print the note for the next owner</button></div>
<details class="slip-details"><summary>Preview the note</summary><div class="handover-slip" id="handoverSlip"><span class="slip-cut">✂ Note for the next owner</span>
<h4>Before you set up this ${noun}</h4>
<ol>
<li>Turn it on. It should open at <strong>${screen}</strong>, not someone else's home screen or files.</li>
<li>If it asks for <strong>${lock}</strong>, stop. Ask the person who gave it to you to remove it. Don't try to get around it.</li>
<li>Only then add your own accounts.</li>
</ol>
<p class="micro-note">RebootLoop didn't check this device. These steps are how you check it yourself.</p>
<div class="slip-contact">If something's wrong, contact:</div>
</div></details>`));
} else {
const dest = recycleDestination();
cards.push(card(2, "Drop it off", "On your next trip out", dest.note,
`<div class="action-links"><a class="primary-button" href="${dest.url}" target="_blank" rel="noopener noreferrer">${dest.label} ↗</a></div><ul class="plain"><li>Tape over the battery terminals if it's loose or removable.</li><li>Don't put it in household recycling or general waste.</li></ul>`));
}
}
const holder = document.getElementById("actionCards");
holder.innerHTML = cards.join("");
holder.querySelectorAll(".done-toggle").forEach(btn => btn.addEventListener("click", () => setStepDone(btn.closest(".action-card"), btn.getAttribute("aria-pressed") !== "true")));
holder.querySelectorAll(".checklist input").forEach(input => input.addEventListener("change", () => {
const all = [...holder.querySelectorAll(".checklist input")];
setStepDone(holder.querySelector('[data-step="1"]'), all.every(i => i.checked));
}));
document.getElementById("printSlip")?.addEventListener("click", () => {
document.getElementById("handoverSlip").closest("details").open = true;
window.print();
});
document.getElementById("steps-title").textContent = { transfer: `Get your ${noun} ready for its next owner.`, recycle: `Get your ${noun} recycled.`, repair: `Get your ${noun} fixed.` }[currentRoute.id];
renderSaveAndHelp();
updateSteps();
planCount += 1;
updateMetrics();
setFeedbackLinks();
goTo(5);
}
function setStepDone(card, done) {
if (!card || card.classList.contains("is-done") === done) return;
card.classList.toggle("is-done", done);
const btn = card.querySelector(".done-toggle");
btn.setAttribute("aria-pressed", String(done));
btn.textContent = done ? "✓ Done" : "Mark done";
updateSteps();
}
function updateSteps() {
const cards = [...document.querySelectorAll("#actionCards .action-card")];
const done = cards.filter(c => c.classList.contains("is-done")).length;
document.getElementById("stepsCount").textContent = `${done} of ${cards.length} done`;
document.getElementById("stepsBar").style.width = `${cards.length ? done / cards.length * 100 : 0}%`;
const allDone = cards.length > 0 && done === cards.length;
document.getElementById("showImpact").innerHTML = allDone
? 'All done! See your impact <span aria-hidden="true">🎉</span>'
: 'See your impact <span aria-hidden="true">→</span>';
}
function resultsText() {
const noun = planNoun[currentData.device];
const range = impactRanges[impactCategory[currentData.device]];
const source = sources[currentData.device] || sources.other;
const lines = [`My RebootLoop result: ${currentRoute.title}`, ""];
if (range && currentRoute.id !== "recycle") lines.push(`Impact: finishing this helps avoid roughly ${range[0]}-${range[1]} kg CO2e from making a new ${noun}.`, "");
lines.push("Next steps:");
if (currentRoute.id === "repair") lines.push("1. Back up my files", "2. Get a repair quote and ask how they handle my data");
else {
lines.push(`1. Wipe it: ${source.url}`);
lines.push(currentRoute.id === "transfer" ? "2. Hand it over and give the next owner the setup-screen check" : `2. Drop it off: ${recycleDestination().url}`);
}
lines.push("", "Assess another device: https://rebootloopmq.github.io/RebootLoop/demo.html");
return lines.join("\n");
}
function renderNextLinks(noun) {
const phone = ["iphone", "android"].includes(currentData.device);
const computer = ["windows", "mac", "chromeos", "linux"].includes(currentData.device);
const links = phone ? [
["📍", "Find your nearest MobileMuster drop-off", "Free drop-off points and post-back across Australia", "https://www.mobilemuster.com.au/recycle-a-mobile/#find-drop-off"],
["📞", "Contact MobileMuster", "Ask what they accept or how collection works", "https://www.mobilemuster.com.au/contact/"]
] : computer ? [
["📍", "Find your nearest NTCRS collection site", "Free computer recycling for households", "https://www.dcceew.gov.au/environment/protection/waste/product-stewardship/television-computer-recycling-scheme"],
["🔎", "Search Recycling Near You", "Local drop-off options by postcode", "https://recyclingnearyou.com.au/"]
] : [
["🔎", "Search Recycling Near You", "Local drop-off options by postcode", "https://recyclingnearyou.com.au/"],
["📍", "Government e-waste drop-off guidance", "Check an accepted-item list before you go", "https://www.dcceew.gov.au/environment/protection/waste/consumers/recycling-drop-off"]
];
document.getElementById("nextTitle").textContent = currentRoute.id === "recycle"
? `Drop off your ${noun}`
: currentRoute.id === "repair" ? `When it's finally done, recycle it right` : `If it can't be reused, recycle it right`;
document.getElementById("nextLinks").innerHTML = links.map(([icon, title, desc, url]) =>
`<a class="next-link" href="${url}" target="_blank" rel="noopener noreferrer"><span class="next-icon" aria-hidden="true">${icon}</span><span><strong>${title}</strong><small>${desc}</small></span><span class="next-arrow" aria-hidden="true">↗</span></a>`).join("");
}
function renderSaveAndHelp() {
const text = resultsText();
const subject = `My plan for my old ${planNoun[currentData.device]}`;
document.getElementById("saveEmail").href = `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(text)}`;
document.getElementById("saveText").href = `sms:?&body=${encodeURIComponent(text)}`;
document.getElementById("saveShare").hidden = !navigator.share;
document.getElementById("saveConfirm").textContent = "";
const links = [];
if (currentRoute.id === "recycle") {
if (["iphone", "android"].includes(currentData.device)) {
links.push(["Find your nearest MobileMuster drop-off", "https://www.mobilemuster.com.au/recycle-a-mobile/#find-drop-off"]);
links.push(["Contact MobileMuster with a question", "https://www.mobilemuster.com.au/contact/"]);
} else links.push([recycleDestination().label, recycleDestination().url]);
links.push(["Search Recycling Near You", "https://recyclingnearyou.com.au/"]);
} else if (currentRoute.id === "transfer") {
links.push(["Official help for this device", (sources[currentData.device] || sources.other).url]);
links.push(["Want to recycle instead? Search Recycling Near You", "https://recyclingnearyou.com.au/"]);
}
if (!links.length) links.push(["ACSC advice on protecting your device data", sources.storage.url]);
document.getElementById("helpLinks").innerHTML = links.map(([label, url]) => `<a href="${url}" target="_blank" rel="noopener noreferrer">${label} ↗</a>`).join("");
}
function updateMetrics() {
const entries = [...sessionImpact.values()];
const circular = entries.filter(e => e.circular).length;
const low = entries.reduce((sum, e) => sum + e.carbon[0], 0);
const high = entries.reduce((sum, e) => sum + e.carbon[1], 0);
const mass = entries.reduce((sum, e) => sum + e.mass, 0);
document.getElementById("metricCircular").textContent = circular;
document.getElementById("metricStops").textContent = entries.length - circular;
document.getElementById("metricCarbon").textContent = high ? `${low}–${high} kg` : "0 kg";
document.getElementById("metricMass").textContent = `${Math.round(mass * 10) / 10} kg`;
}
function finishStop() {
resetAll(false);
}
function resetAll(startScreen = true) {
document.getElementById("assessmentForm").reset();
document.getElementById("formError").textContent = "";
pickers.forEach(fieldset => fieldset.classList.remove("is-missing"));
syncPickers();
currentData = null;
currentRoute = null;
assessmentCounted = false;
if (startScreen) {
assessmentCount = 0; planCount = 0; sessionImpact.clear();
updateMetrics();
goTo(1);
} else goTo(2);
}
document.querySelectorAll("[data-go]").forEach(button => button.addEventListener("click", () => goTo(Number(button.dataset.go))));
document.querySelectorAll("[data-demo]").forEach(button => button.addEventListener("click", () => loadDemo(button.dataset.demo)));
document.getElementById("assessmentForm").addEventListener("submit", event => {
event.preventDefault();
currentData = collectForm();
if (currentData) showRoute();
});
document.getElementById("routeNext").addEventListener("click", () => currentRoute.type === "normal" ? buildImpact() : buildPrepare());
document.getElementById("finishStop").addEventListener("click", finishStop);
document.getElementById("showImpact").addEventListener("click", () => goTo(6));
document.getElementById("saveShare").addEventListener("click", () => {
navigator.share({ title: "My RebootLoop result", text: resultsText() }).catch(() => {});
});
document.getElementById("saveCopy").addEventListener("click", async () => {
const confirm = document.getElementById("saveConfirm");
try { await navigator.clipboard.writeText(resultsText()); confirm.textContent = "Copied. Paste it into your notes or messages."; }
catch { confirm.textContent = "Couldn't copy here. Try Email or Text instead."; }
});
document.getElementById("assessAnother").addEventListener("click", () => resetAll(false));
document.getElementById("restartDemo").addEventListener("click", () => resetAll(true));
document.getElementById("resetDemo").addEventListener("click", () => resetAll(true));
})();
</script>
</body>
</html>