PageSourceSearch

https://www.hetphoenixproject.nl/kermis/

html hetphoenixproject.nl collected 2026-10-02 16:07:03 UTC 28,349 bytes, 745 lines download raw bytes

1P<!DOCTYPE html>
2<html lang="nl">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Octopus Kermisattractie Simulator - Operator View</title>
7    
7<script type="importmap">
8    {
9        "imports": {
10            "three": "https://unpkg.com/[email protected]/build/three.module.js",
11            "three/examples/jsm/": "https://unpkg.com/[email protected]/examples/jsm/"
12        }
13    }
14    </script>
14
15    <style>
16        * {
17            margin: 0;
18            padding: 0;
19            box-sizing: border-box;
20        }
21        
22        body {
23            font-family: 'Arial', sans-serif;
24            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
25            overflow: hidden;
26            color: white;
27        }
28        
29        #loading-screen {
30            position: fixed;
31            top: 0;
32            left: 0;
33            width: 100vw;
34            height: 100vh;
35            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
36            display: flex;
37            flex-direction: column;
38            justify-content: center;
39            align-items: center;
40            z-index: 9999;
41            transition: opacity 0.5s ease-out;
42        }
43        
44        #loading-screen.hidden {
45            opacity: 0;
46            pointer-events: none;
47        }
48        
49        .loading-spinner {
50            width: 60px;
51            height: 60px;
52            border: 4px solid rgba(255, 107, 53, 0.2);
53            border-top: 4px solid #ff6b35;
54            border-radius: 50%;
55            animation: spin 1s linear infinite;
56            margin-bottom: 20px;
57        }
58        
59        @keyframes spin {
60            0% { transform: rotate(0deg); }
61            100% { transform: rotate(360deg); }
62        }
63        
64        .loading-text {
65            color: #ff6b35;
66            font-size: 20px;
67            font-weight: bold;
68            margin-bottom: 10px;
69        }
70        
71        .loading-details {
72            color: rgba(255, 255, 255, 0.6);
73            font-size: 14px;
74        }
75        
76        #canvas-container {
77            width: 100vw;
78            height: 100vh;
79            position: relative;
80        }
81        
82        #control-panel {
83            position: absolute;
84            top: 10px;
85            left: 10px;
86            background: rgba(0, 0, 0, 0.8);
87            border: 2px solid #ff6b35;
88            border-radius: 10px;
89            padding: 12px;
90            min-width: 320px;
91            max-width: 400px;
92            backdrop-filter: blur(10px);
93            box-shadow: 0 8px 32px rgba(255, 107, 53, 0.3);
94        }
95        
96        .control-group {
97            margin-bottom: 12px;
98        }
99        
100        .control-group h2 {
101            color: #ff6b35;
102            font-size: 14px;
103            margin-bottom: 6px;
104            border-bottom: 1px solid #ff6b35;
105            padding-bottom: 3px;
106        }
107        
108        .button-group {
109            display: flex;
110            gap: 10px;
111            flex-wrap: wrap;
112        }
113
114        .button-group.visitor-buttons button {
115            flex: 1 1 calc(33.33% - 10px);
116            min-width: 70px;
117            padding: 6px 8px;
118            font-size: 11px;
119        }
120        
121        button {
122            background: linear-gradient(135deg, #ff6b35 0%, #f7931e 100%);
123            color: white;
124            border: none;
125            padding: 8px 12px;
126            border-radius: 6px;
127            cursor: pointer;
128            font-size: 12px;
129            font-weight: bold;
130            transition: all 0.3s;
131            box-shadow: 0 4px 15px rgba(255, 107, 53, 0.4);
132        }
133        
134        button:hover {
135            transform: translateY(-2px);
136            box-shadow: 0 6px 20px rgba(255, 107, 53, 0.6);
137        }
138        
139        button:active {
140            transform: translateY(0);
141        }
142        
143        button.active {
144            background: linear-gradient(135deg, #00d4aa 0%, #00b894 100%);
145            box-shadow: 0 4px 15px rgba(0, 212, 170, 0.4);
146        }
147        
148        button.stop {
149            background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
150        }
151        
152        /* Toggle Switch Style */
153        .toggle-row {
154            display: flex;
155            gap: 10px;
156            flex-wrap: nowrap;
157            justify-content: space-between;
158        }
159
160        .toggle-switch {
161            position: relative;
162            display: inline-flex;
163            flex-direction: column;
164            align-items: center;
165            width: 118px;
166            gap: 6px;
167            padding: 8px 6px;
168            background: transparent;
169            border: none;
170            cursor: pointer;
171        }
172        .toggle-switch .switch-label {
173            color: #ddd;
174            font-size: 11px;
175            text-align: center;
176            letter-spacing: 0.3px;
177        }
178        .toggle-switch .switch-knob {
179            position: relative;
180            width: 64px;
181            height: 64px;
182            border-radius: 50%;
183            background: radial-gradient(circle at 30% 30%, #666, #222 65%, #0d0d0d 100%);
184            border: 2px solid #444;
185            box-shadow: inset 0 1px 2px rgba(255,255,255,0.08), 0 4px 12px rgba(0,0,0,0.45);
186            transition: transform 0.3s, box-shadow 0.3s;
187        }
188        .toggle-switch .switch-pointer {
189            position: absolute;
190            width: 4px;
191            height: 20px;
192            background: #ff6b35;
193            top: 8px;
194            left: 50%;
195            transform-origin: center 26px;
196            transform: translateX(-50%) rotate(-110deg);
197            border-radius: 2px;
198            box-shadow: 0 0 10px rgba(255,107,53,0.6);
199        }
200        .toggle-switch .switch-ticks {
201            position: absolute;
202            inset: 4px;
203            pointer-events: none;
204        }
205        .toggle-switch .switch-ticks span {
206            position: absolute;
207            width: 6px;
208            height: 2px;
209            background: #777;
210            top: 6px;
211            left: 50%;
212            transform-origin: center 24px;
213            border-radius: 2px;
214        }
215        .switch-state {
216            position: absolute;
217            font-size: 10px;
218            color: #ddd;
219            letter-spacing: 0.4px;
220            text-shadow: 0 1px 2px rgba(0,0,0,0.6);
221            pointer-events: none;
222        }
223        .switch-state.left {
224            left: -6px;
225            top: 65%;
226            transform: translateY(-50%);
227        }
228        .switch-state.right {
229            right: -6px;
230            top: 35%;
231            transform: translateY(-50%);
232        }
233        .switch-state.mid {
234            left: 50%;
235            top: 20%;
236            transform: translate(-50%, -50%);
237        }
238        .toggle-switch.active .switch-knob {
239            box-shadow: inset 0 1px 2px rgba(255,255,255,0.12), 0 4px 14px rgba(255,107,53,0.55);
240        }
241        .toggle-switch.active .switch-pointer {
242            transform: translateX(-50%) rotate(70deg);
243        }
244        
245        .slider-control {
246            margin: 8px 0;
247        }
248        .slider-control label {
249            display: block;
250            margin-bottom: 6px;
251            font-size: 11px;
252            color: #ddd;
253        }
254
255        .value-display { display: none; }
256
257        /* Rotary knob style */
258        .rotary-wrap {
259            display: flex;
260            align-items: center;
261            gap: 10px;
262        }
263        .rotary {
264            width: 72px;
265            height: 72px;
266            border-radius: 50%;
267            background: radial-gradient(circle at 30% 30%, #555, #1a1a1a 60%, #0d0d0d 100%);
268            border: 2px solid #444;
269            position: relative;
270            box-shadow: inset 0 1px 2px rgba(255,255,255,0.08), 0 4px 12px rgba(0,0,0,0.45);
271            cursor: pointer;
272        }
273        .rotary::after {
274            content: '';
275            position: absolute;
276            inset: 12px;
277            border-radius: 50%;
278            background: radial-gradient(circle at 30% 30%, #777, #222 70%, #111 100%);
279        }
280        .rotary-pointer {
281            position: absolute;
282            width: 4px;
283            height: 24px;
284            background: #ff6b35;
285            top: 6px;
286            left: 50%;
287            transform-origin: center 30px;
288            transform: translateX(-50%) rotate(-135deg);
289            border-radius: 2px;
290            box-shadow: 0 0 10px rgba(255,107,53,0.6);
291            z-index: 2;
292        }
293
294        .knob-grid {
295            display: flex;
296            gap: 10px;
297            flex-wrap: nowrap;
298            justify-content: space-between;
299        }
300        .rotary-block {
301            display: flex;
302            flex-direction: column;
303            align-items: center;
304            width: 118px;
305            gap: 6px;
306        }
307        .step-row {
308            display: flex;
309            gap: 6px;
310            margin-top: 4px;
311            justify-content: center;
312        }
313        .step-dot {
314            width: 14px;
315            height: 14px;
316            border-radius: 50%;
317            border: 1px solid #555;
318            background: linear-gradient(135deg, #2a2a2a 0%, #111 100%);
319            box-shadow: inset 0 1px 2px rgba(255,255,255,0.08), 0 2px 6px rgba(0,0,0,0.4);
320            cursor: pointer;
321            padding: 0;
322        }
323        .step-dot.active {
324            background: linear-gradient(135deg, #ff6b35 0%, #f7931e 100%);
325            border-color: #ff8c4a;
326            box-shadow: 0 2px 8px rgba(255,107,53,0.45);
327        }
328        .rotary-label {
329            font-size: 11px;
330            color: #ddd;
331            letter-spacing: 0.5px;
332        }
333        .rotary-ticks {
334            position: absolute;
335            inset: 4px;
336            pointer-events: none;
337        }
338        .rotary-ticks span {
339            position: absolute;
340            width: 6px;
341            height: 2px;
342            background: #777;
343            top: 6px;
344            left: 50%;
345            transform-origin: center 30px;
346            border-radius: 2px;
347        }
348        
349        .value-display {
350            display: inline-block;
351            background: #333;
352            padding: 2px 8px;
353            border-radius: 4px;
354            margin-left: 6px;
355            font-weight: bold;
356            font-size: 11px;
357            color: #ff6b35;
358        }
359        
360        #status-panel {
361            position: absolute;
362            top: 10px;
363            right: 10px;
364            background: rgba(0, 0, 0, 0.8);
365            border: 2px solid #00d4aa;
366            border-radius: 10px;
367            padding: 12px;
368            min-width: 200px;
369            backdrop-filter: blur(10px);
370            box-shadow: 0 8px 32px rgba(0, 212, 170, 0.3);
371        }
372        
373        .status-item {
374            display: flex;
375            justify-content: space-between;
376            margin: 6px 0;
377            font-size: 11px;
378        }
379        
380        .status-label {
381            color: #aaa;
382        }
383        
384        .status-value {
385            color: #00d4aa;
386            font-weight: bold;
387        }
388        
389        .visitor-queue {
390            margin-top: 10px;
391            padding-top: 10px;
392            border-top: 1px solid #444;
393        }
394
395        .mic-button {
396            width: 100%;
397            padding: 12px;
398            font-size: 14px;
399            background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);
400            box-shadow: 0 4px 15px rgba(155, 89, 182, 0.4);
401            display: flex;
402            align-items: center;
403            justify-content: center;
404            gap: 8px;
405            margin-bottom: 10px;
406        }
407        
408        .mic-button:hover {
409            box-shadow: 0 6px 20px rgba(155, 89, 182, 0.6);
410        }
411        
412        .mic-button.active {
413            background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
414            box-shadow: 0 4px 15px rgba(231, 76, 60, 0.6);
415            animation: pulse-mic 1s infinite;
416        }
417        
418        @keyframes pulse-mic {
419            0%, 100% { transform: scale(1); }
420            50% { transform: scale(1.02); }
421        }
422
423        .sound-buttons {
424            display: grid;
425            grid-template-columns: repeat(4, minmax(70px, 1fr));
426            gap: 8px;
427        }
428        .sound-buttons .small-button {
429            padding: 6px 10px;
430            font-size: 11px;
431            min-width: 70px;
432        }
433        
434        .visitor-icon {
435            display: inline-block;
436            width: 20px;
437            height: 20px;
438            margin: 2px;
439            background: #ff6b35;
440            border-radius: 50%;
441            position: relative;
442            animation: pulse 2s infinite;
443        }
444        
445        @keyframes pulse {
446            0%, 100% { opacity: 1; }
447            50% { opacity: 0.6; }
448        }
449        
450        .emergency-stop {
451            background: #e74c3c !important;
452            font-size: 13px;
453            padding: 10px;
454            margin-top: 10px;
455            animation: emergency-blink 1s infinite;
456        }
457        
458        @keyframes emergency-blink {
459            0%, 100% { opacity: 1; }
460            50% { opacity: 0.7; }
461        }
462        
463        #info-display {
464            position: absolute;
465            bottom: 10px;
466            left: 10px;
467            background: rgba(0, 0, 0, 0.7);
468            padding: 6px 10px;
469            border-radius: 6px;
470            font-size: 10px;
471            color: #aaa;
472        }
473    </style>
474</head>
475<body>
476    <div id="loading-screen">
477        <div class="loading-spinner"></div>
478        <div class="loading-text">Octopus wordt geladen...</div>
479        <div class="loading-details" id="loading-details">Initialiseren...</div>
480    </div>
481    <div id="canvas-container"></div>
482    
483    <div id="control-panel">
484        <div class="control-group">
485            <h2>⚡ HOOFDSCHAKELAAR</h2>
486            <div class="toggle-row" style="margin-bottom: 10px;">
487                <div id="powerBtn" class="toggle-switch stop">
488                    <div class="switch-state left">UIT</div>
489                    <div class="switch-state right">AAN</div>
490                    <div class="switch-knob">
491                        <div class="switch-pointer"></div>
492                        <div class="switch-ticks">
493                            <span style="transform: translateX(-50%) rotate(-110deg);"></span>
494                            <span style="transform: translateX(-50%) rotate(70deg);"></span>
495                        </div>
496                    </div>
497                    <span class="switch-label">Stroom</span>
498                </div>
499                <div id="startBtn" class="toggle-switch">
500                    <div class="switch-state left">STOP</div>
501                    <div class="switch-state right">START</div>
502                    <div class="switch-knob">
503                        <div class="switch-pointer"></div>
504                        <div class="switch-ticks">
505                            <span style="transform: translateX(-50%) rotate(-110deg);"></span>
506                            <span style="transform: translateX(-50%) rotate(70deg);"></span>
507                        </div>
508                    </div>
509                    <span class="switch-label">Start</span>
510                </div>
511            </div>
512        </div>
513
514        <div class="control-group">
515            <h2>👥 BEZOEKERS</h2>
516            <div class="button-group visitor-buttons">
517                <button id="addVisitorBtn">+ Bezoeker</button>
518                <button id="boardBtn">Instappen</button>
519                <button id="exitBtn">Uitstappen</button>
520            </div>
521        </div>
522        
523        <div class="control-group">
524            <h2>🔒 BEDIENING</h2>
525            <div class="toggle-row">
526                <div id="restraintBtn" class="toggle-switch">
527                    <div class="switch-state left">LOS</div>
528                    <div class="switch-state right">VAST</div>
529                    <div class="switch-knob">
530                        <div class="switch-pointer"></div>
531                        <div class="switch-ticks">
532                            <span style="transform: translateX(-50%) rotate(-110deg);"></span>
533                            <span style="transform: translateX(-50%) rotate(70deg);"></span>
534                        </div>
535                    </div>
536                    <span class="switch-label">Beugels</span>
537                </div>
538                <div id="liftBtn" class="toggle-switch">
539                    <div class="switch-state left">OML</div>
540                    <div class="switch-state right">OMH</div>
541                    <div class="switch-knob">
542                        <div class="switch-pointer"></div>
543                        <div class="switch-ticks">
544                            <span style="transform: translateX(-50%) rotate(-110deg);"></span>
545                            <span style="transform: translateX(-50%) rotate(70deg);"></span>
546                        </div>
547                    </div>
548                    <span class="switch-label">Armen</span>
549                </div>
550                <div id="gondolaLockBtn" class="toggle-switch active">
551                    <div class="switch-state left">LOS</div>
552                    <div class="switch-state right">VAST</div>
553                    <div class="switch-knob">
554                        <div class="switch-pointer"></div>
555                        <div class="switch-ticks">
556                            <span style="transform: translateX(-50%) rotate(-110deg);"></span>
557                            <span style="transform: translateX(-50%) rotate(70deg);"></span>
558                        </div>
559                    </div>
560                    <span class="switch-label">Gondels</span>
561                </div>
562            </div>
563        </div>
564        
565        <div class="control-group">
566            <h2>⚙️ SNELHEID</h2>
567            <div class="knob-grid">
568                <div class="rotary-block">
569                    <div class="rotary-label">Molen</div>
570                    <div class="rotary" id="mainSpeedKnob" data-type="main">
571                        <div class="rotary-pointer"></div>
572                        <div class="rotary-ticks">
573                            <span style="transform: translateX(-50%) rotate(-120deg);"></span>
574                            <span style="transform: translateX(-50%) rotate(-60deg);"></span>
575                            <span style="transform: translateX(-50%) rotate(0deg);"></span>
576                            <span style="transform: translateX(-50%) rotate(60deg);"></span>
577                            <span style="transform: translateX(-50%) rotate(120deg);"></span>
578                        </div>
579                    </div>
580                    <div class="step-row" aria-label="Molen snelheidsstappen">
581                        <button class="step-dot" data-type="main" data-value="0" aria-label="0%"></button>
582                        <button class="step-dot" data-type="main" data-value="25" aria-label="25%"></button>
583                        <button class="step-dot" data-type="main" data-value="50" aria-label="50%"></button>
584                        <button class="step-dot" data-type="main" data-value="75" aria-label="75%"></button>
585                        <button class="step-dot" data-type="main" data-value="100" aria-label="100%"></button>
586                    </div>
587                </div>
588                <div class="rotary-block">
589                    <div class="rotary-label">Kruis</div>
590                    <div class="rotary" id="crossSpeedKnob" data-type="cross">
591                        <div class="rotary-pointer"></div>
592                        <div class="rotary-ticks">
593                            <span style="transform: translateX(-50%) rotate(-120deg);"></span>
594                            <span style="transform: translateX(-50%) rotate(-60deg);"></span>
595                            <span style="transform: translateX(-50%) rotate(0deg);"></span>
596                            <span style="transform: translateX(-50%) rotate(60deg);"></span>
597                            <span style="transform: translateX(-50%) rotate(120deg);"></span>
598                        </div>
599                    </div>
600                    <div class="step-row" aria-label="Kruis snelheidsstappen">
601                        <button class="step-dot" data-type="cross" data-value="0" aria-label="0%"></button>
602                        <button class="step-dot" data-type="cross" data-value="25" aria-label="25%"></button>
603                        <button class="step-dot" data-type="cross" data-value="50" aria-label="50%"></button>
604                        <button class="step-dot" data-type="cross" data-value="75" aria-label="75%"></button>
605                        <button class="step-dot" data-type="cross" data-value="100" aria-label="100%"></button>
606                    </div>
607                </div>
608                <div class="rotary-block">
609                    <div class="rotary-label">Golf</div>
610                    <div class="rotary" id="waveSpeedKnob" data-type="wave">
611                        <div class="rotary-pointer"></div>
612                        <div class="rotary-ticks">
613                            <span style="transform: translateX(-50%) rotate(-120deg);"></span>
614                            <span style="transform: translateX(-50%) rotate(0deg);"></span>
615                            <span style="transform: translateX(-50%) rotate(120deg);"></span>
616                        </div>
617                    </div>
618                    <div class="step-row" aria-label="Golf snelheidsstappen">
619                        <button class="step-dot" data-type="wave" data-value="0" aria-label="0%"></button>
620                        <button class="step-dot" data-type="wave" data-value="25" aria-label="25%"></button>
621                        <button class="step-dot" data-type="wave" data-value="40" aria-label="40%"></button>
622                    </div>
623                </div>
624            </div>
625        </div>
626        
627        <div class="control-group">
628            <h2>💡 VERLICHTING</h2>
629            <div class="toggle-row">
630                <div id="lightsSwitch" class="toggle-switch">
631                    <div class="switch-state left">UIT</div>
632                    <div class="switch-state right">AAN</div>
633                    <div class="switch-knob">
634                        <div class="switch-pointer"></div>
635                        <div class="switch-ticks">
636                            <span style="transform: translateX(-50%) rotate(-110deg);"></span>
637                            <span style="transform: translateX(-50%) rotate(70deg);"></span>
638                        </div>
639                    </div>
640                    <span class="switch-label">Verlichting</span>
641                </div>
642                <div id="lightModeSwitch" class="toggle-switch">
643                    <div class="switch-state left">P</div>
644                    <div class="switch-state mid">R</div>
645                    <div class="switch-state right">C</div>
646                    <div class="switch-knob">
647                        <div class="switch-pointer"></div>
648                        <div class="switch-ticks">
649                            <span style="transform: translateX(-50%) rotate(-90deg);"></span>
650                            <span style="transform: translateX(-50%) rotate(0deg);"></span>
651                            <span style="transform: translateX(-50%) rotate(90deg);"></span>
652                        </div>
653                    </div>
654                    <span class="switch-label">Modus</span>
655                </div>
656            </div>
657        </div>
658        
659        <div class="control-group">
660            <h2>📷 CAMERA VIEW</h2>
661            <div class="button-group" style="gap:10px; flex-wrap:wrap;">
662                <button id="cameraViewBtn">🎥 STANDAARD</button>
663                <button id="dayNightBtn">🌙 NACHT</button>
664            </div>
665        </div>
666        
667        <div class="control-group">
668            <h2>⚡ KWALITEIT</h2>
669            <div class="button-group" style="gap:10px; flex-wrap:wrap;">
670                <button id="qualityHighBtn" class="active">🎨 HOOG</button>
671                <button id="qualityMedBtn">⚖️ MIDDEN</button>
672                <button id="qualityLowBtn">⚡ LAAG</button>
673                <button id="qualityUltraLowBtn">🚀 ULTRA</button>
674            </div>
675        </div>
676
677        <div class="control-group">
678            <h2>🔊 SOUND</h2>
679            <button id="micBtn" class="mic-button">🎤 OMROEP (ingedrukt houden)</button>
680            <div class="sound-buttons">
681                <button class="small-button" id="sound1Btn">Sound 1</button>
682                <button class="small-button" id="sound2Btn">Sound 2</button>
683                <button class="small-button" id="sound3Btn">Sound 3</button>
684                <button class="small-button" id="sound4Btn">Sound 4</button>
685                <button class="small-button" id="sound5Btn">Sound 5</button>
686                <button class="small-button" id="sound6Btn">Sound 6</button>
687                <button class="small-button" id="sound7Btn">Sound 7</button>
688                <button class="small-button" id="sound8Btn">Sound 8</button>
689            </div>
690        </div>
691    </div>
692    
693    <div id="status-panel">
694        <h2 style="color: #00d4aa; margin-bottom: 15px;">📊 STATUS</h2>
695        <div class="status-item">
696            <span class="status-label">Status:</span>
697            <span class="status-value" id="rideStatus">GESTOPT</span>
698        </div>
699        <div class="status-item">
700            <span class="status-label">RPM:</span>
701            <span class="status-value" id="rpmDisplay">0</span>
702        </div>
703        <div class="status-item">
704            <span class="status-label">G-kracht (onride):</span>
705            <span class="status-value" id="gForceValue">1.0</span>
706        </div>
707        <div class="status-item">
708            <span class="status-label">Bezoekers aan boord:</span>
709            <span class="status-value" id="ridersCount">0</span>
710        </div>
711        <div class="status-item">
712            <span class="status-label">Ritten vandaag:</span>
713            <span class="status-value" id="ridesCount">0</span>
714        </div>
715        <div class="status-item">
716            <span class="status-label">⏱️ Ritduur:</span>
717            <span class="status-value" id="rideTimer" style="color: #ffcc00;">0:00</span>
718        </div>
719        
720        <div class="visitor-queue">
721            <div style="color: #aaa; margin-bottom: 10px;">Wachtrij:</div>
722            <div id="queueDisplay"></div>
723        </div>
724
725        <div style="margin-top: 12px;">
726            <button id="helpBtn" style="width: 100%; font-size: 11px; padding: 6px 8px;">❓ Hulp & Instructies</button>
727        </div>
728    </div>
729    
730    <div id="info-display">
731        🎮 Camera: Muisbeweging | Zoom: Scrollwiel | Pan: Rechtermuisknop
732    </div>
733    <div id="error-display" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: red; background: rgba(0,0,0,0.8); padding: 20px; display: none; z-index: 9999;"></div>
734    
735    
735<script>
736        window.onerror = function(message, source, lineno, colno, error) {
737            const errorDisplay = document.getElementById('error-display');
738            errorDisplay.style.display = 'block';
739            errorDisplay.innerHTML = `<h3>Error:</h3><p>${message}</p><p>Line: ${lineno}</p>`;
740            console.error(error);
741        };
742    </script>
742
743    
743<script type="module" src="octopus.js"></script>
743
744</body>
745</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.