22< html lang ="en ">
33< head >
44 < meta charset ="UTF-8 ">
5+ < script > document . documentElement . classList . add ( 'js-reveal' ) ; </ script >
56 < meta name ="viewport " content ="width=device-width, initial-scale=1 ">
7+ <!-- Consent Mode defaults (before gtag.js); restored choice from localStorage if any -->
8+ < script >
9+ window . dataLayer = window . dataLayer || [ ] ;
10+ function gtag ( ) { dataLayer . push ( arguments ) ; }
11+ ( function ( ) {
12+ var key = 'cuepoint_ga_consent' ;
13+ var stored = null ;
14+ try { stored = localStorage . getItem ( key ) ; } catch ( e ) { }
15+ gtag ( 'consent' , 'default' , {
16+ ad_storage : 'denied' ,
17+ ad_user_data : 'denied' ,
18+ ad_personalization : 'denied' ,
19+ analytics_storage : 'denied' ,
20+ wait_for_update : 500
21+ } ) ;
22+ if ( stored === 'granted' ) {
23+ gtag ( 'consent' , 'update' , {
24+ ad_storage : 'granted' ,
25+ ad_user_data : 'granted' ,
26+ ad_personalization : 'granted' ,
27+ analytics_storage : 'granted'
28+ } ) ;
29+ }
30+ } ) ( ) ;
31+ </ script >
32+ < script async src ="https://www.googletagmanager.com/gtag/js?id=G-4QM1M6Q2ZT "> </ script >
33+ < script >
34+ gtag ( 'js' , new Date ( ) ) ;
35+ gtag ( 'config' , 'G-4QM1M6Q2ZT' ) ;
36+ </ script >
637 < title > CuePoint – Accurate metadata for Rekordbox</ title >
738 < meta name ="description " content ="Match Rekordbox tracks to Beatport for key, BPM, label, genre, and release date. Desktop app for Windows and macOS with a full audit trail. ">
39+ < meta name ="robots " content ="index, follow, max-image-preview:large ">
40+ < meta name ="theme-color " content ="#0d1117 ">
841 < link rel ="canonical " href ="https://stuchain.github.io/CuePoint/ ">
942 < meta property ="og:title " content ="CuePoint – Accurate metadata for Rekordbox ">
1043 < meta property ="og:description " content ="Match Rekordbox tracks to Beatport for key, BPM, label, genre, and release date. Desktop app for Windows and macOS with a full audit trail. ">
1144 < meta property ="og:url " content ="https://stuchain.github.io/CuePoint/ ">
1245 < meta property ="og:type " content ="website ">
46+ < meta property ="og:site_name " content ="CuePoint ">
47+ < meta property ="og:locale " content ="en_US ">
1348 < meta property ="og:image " content ="https://stuchain.github.io/CuePoint/logo.png ">
49+ < meta property ="og:image:width " content ="120 ">
50+ < meta property ="og:image:height " content ="120 ">
51+ < meta property ="og:image:alt " content ="CuePoint logo ">
1452 < meta name ="twitter:card " content ="summary ">
1553 < meta name ="twitter:title " content ="CuePoint – Accurate metadata for Rekordbox ">
1654 < meta name ="twitter:description " content ="Match Rekordbox tracks to Beatport for key, BPM, label, genre, and release date. Desktop app for Windows and macOS with a full audit trail. ">
1755 < meta name ="twitter:image " content ="https://stuchain.github.io/CuePoint/logo.png ">
1856 < link rel ="icon " href ="logo.png " type ="image/png ">
1957 < link rel ="shortcut icon " href ="logo.png " type ="image/png ">
2058 < link rel ="apple-touch-icon " href ="logo.png ">
59+ < script type ="application/ld+json ">
60+ { "@context" :"https://schema.org" , "@graph" :[
61+ { "@type" :"WebSite" , "name" :"CuePoint" , "url" :"https://stuchain.github.io/CuePoint/" , "description" :"Match Rekordbox tracks to Beatport for key, BPM, label, genre, and release date. Desktop app for Windows and macOS with a full audit trail." } ,
62+ { "@type" :"SoftwareApplication" , "name" :"CuePoint" , "url" :"https://stuchain.github.io/CuePoint/" , "applicationCategory" :"DesktopApplication" , "operatingSystem" :"Windows, macOS" , "offers" :{ "@type" :"Offer" , "price" :"0" , "priceCurrency" :"USD" } , "downloadUrl" :"https://github.com/stuchain/CuePoint/releases/latest" }
63+ ] }
64+ </ script >
2165 < style >
2266 : root {
2367 --bg : # 0d1117 ;
5397 /* Layout */
5498 .page { max-width : 960px ; margin : 0 auto; padding : 0 1.5rem ; }
5599 section { padding : 4rem 0 ; }
56- section .reveal { opacity : 0 ; transform : translateY (24px ); transition : opacity 0.6s ease, transform 0.6s ease; }
57- section .reveal .visible { opacity : 1 ; transform : translateY (0 ); }
100+ html .js-reveal section .reveal ,
101+ html .js-reveal footer .reveal {
102+ opacity : 0 ;
103+ transform : translateY (24px );
104+ transition : opacity 0.6s ease, transform 0.6s ease;
105+ }
106+ html .js-reveal section .reveal .visible ,
107+ html .js-reveal footer .reveal .visible {
108+ opacity : 1 ;
109+ transform : translateY (0 );
110+ }
58111 @media (prefers-reduced-motion : reduce) {
59- section .reveal { transform : translateY (8px ); transition : opacity 0.2s ease, transform 0.2s ease; }
112+ html .js-reveal section .reveal ,
113+ html .js-reveal footer .reveal {
114+ transform : translateY (8px );
115+ transition : opacity 0.2s ease, transform 0.2s ease;
116+ }
60117 }
61118
62119 /* Hero */
146203 }
147204
148205 /* Intro */
206+ .intro .section-title { margin-bottom : 1rem ; }
149207 .intro .lead {
150208 font-size : 1.25rem ;
151209 color : var (--muted );
327385 margin : 0 auto;
328386 line-height : 1.5 ;
329387 }
388+
389+ /* Cookie / analytics consent (Consent Mode + EEA guidance) */
390+ .consent-banner {
391+ position : fixed;
392+ bottom : 0 ;
393+ left : 0 ;
394+ right : 0 ;
395+ z-index : 9999 ;
396+ background : var (--surface );
397+ border-top : 1px solid var (--border );
398+ padding : 1rem 1.25rem ;
399+ box-shadow : 0 -8px 32px rgba (0 , 0 , 0 , 0.35 );
400+ display : none;
401+ }
402+ .consent-banner .is-visible { display : block; }
403+ .consent-inner {
404+ max-width : 960px ;
405+ margin : 0 auto;
406+ display : flex;
407+ flex-wrap : wrap;
408+ gap : 1rem ;
409+ align-items : center;
410+ justify-content : space-between;
411+ }
412+ .consent-banner p { margin : 0 ; font-size : 0.875rem ; color : var (--text ); flex : 1 1 240px ; line-height : 1.5 ; }
413+ .consent-actions {
414+ display : flex;
415+ flex-wrap : wrap;
416+ gap : 0.5rem ;
417+ align-items : center;
418+ justify-content : flex-end;
419+ }
420+ .consent-actions a {
421+ color : var (--link );
422+ font-size : 0.8125rem ;
423+ text-decoration : none;
424+ margin-right : 0.25rem ;
425+ }
426+ .consent-actions a : hover { color : var (--link-hover ); text-decoration : underline; }
427+ .consent-actions .btn { padding : 0.45rem 0.9rem ; font-size : 0.8125rem ; border : none; cursor : pointer; }
428+ .consent-actions .btn-secondary { background : var (--border ); color : var (--text ); }
429+ .consent-actions .btn-secondary : hover { background : # 484f58 ; color : var (--text ); }
330430 </ style >
331431</ head >
332432< body >
@@ -343,8 +443,9 @@ <h1>CuePoint</h1>
343443 </ header >
344444
345445 < main >
346- < section class ="intro reveal ">
446+ < section class ="intro reveal " aria-labelledby =" intro-heading " >
347447 < div class ="page ">
448+ < h2 id ="intro-heading " class ="section-title "> About CuePoint</ h2 >
348449 < p class ="lead "> Match your Rekordbox tracks to Beatport and get key, BPM, label, genre, and release date without manual lookup.</ p >
349450 < p class ="sub "> Export from Rekordbox → run CuePoint → review or re-import. Full audit trail for every match.</ p >
350451 < p class ="tools-line "> CuePoint includes < strong > inKey</ strong > (Beatport metadata for Rekordbox playlists) and < strong > inCrate</ strong > (inventory, charts, new releases, and Beatport playlists from your collection).</ p >
@@ -413,6 +514,50 @@ <h2>Download</h2>
413514 </ div >
414515 </ footer >
415516
517+ < div id ="consent-banner " class ="consent-banner " role ="dialog " aria-labelledby ="consent-heading " aria-describedby ="consent-desc " hidden >
518+ < div class ="consent-inner ">
519+ < p id ="consent-desc "> < strong id ="consent-heading "> Analytics</ strong > — We use Google Analytics to understand traffic to this page. You can accept measurement cookies or decline; the site works either way.</ p >
520+ < div class ="consent-actions ">
521+ < a href ="https://github.com/stuchain/CuePoint/blob/main/PRIVACY_NOTICE.md " target ="_blank " rel ="noopener noreferrer "> Privacy notice</ a >
522+ < button type ="button " class ="btn btn-secondary " id ="consent-decline "> Decline</ button >
523+ < button type ="button " class ="btn " id ="consent-accept "> Accept</ button >
524+ </ div >
525+ </ div >
526+ </ div >
527+
528+ < script >
529+ ( function ( ) {
530+ var key = 'cuepoint_ga_consent' ;
531+ var banner = document . getElementById ( 'consent-banner' ) ;
532+ if ( ! banner ) return ;
533+ var stored = null ;
534+ try { stored = localStorage . getItem ( key ) ; } catch ( e ) { }
535+ if ( stored !== null ) return ;
536+ banner . removeAttribute ( 'hidden' ) ;
537+ banner . classList . add ( 'is-visible' ) ;
538+ function hide ( ) {
539+ banner . classList . remove ( 'is-visible' ) ;
540+ banner . setAttribute ( 'hidden' , '' ) ;
541+ }
542+ document . getElementById ( 'consent-accept' ) . addEventListener ( 'click' , function ( ) {
543+ try { localStorage . setItem ( key , 'granted' ) ; } catch ( e ) { }
544+ if ( typeof gtag === 'function' ) {
545+ gtag ( 'consent' , 'update' , {
546+ ad_storage : 'granted' ,
547+ ad_user_data : 'granted' ,
548+ ad_personalization : 'granted' ,
549+ analytics_storage : 'granted'
550+ } ) ;
551+ }
552+ hide ( ) ;
553+ } ) ;
554+ document . getElementById ( 'consent-decline' ) . addEventListener ( 'click' , function ( ) {
555+ try { localStorage . setItem ( key , 'declined' ) ; } catch ( e ) { }
556+ hide ( ) ;
557+ } ) ;
558+ } ) ( ) ;
559+ </ script >
560+
416561 < script >
417562 ( function ( ) {
418563 var LATEST = 'https://github.com/stuchain/CuePoint/releases/latest' ;
@@ -421,6 +566,19 @@ <h2>Download</h2>
421566 var statusEl = document . getElementById ( 'dl-status' ) ;
422567 var versionEl = document . getElementById ( 'dl-version' ) ;
423568
569+ function trackDownloadClick ( el , fileExtension ) {
570+ if ( typeof gtag !== 'function' ) return ;
571+ var href = el . getAttribute ( 'href' ) ;
572+ if ( ! href || href === '#' ) return ;
573+ gtag ( 'event' , 'file_download' , {
574+ link_url : href ,
575+ link_text : el . textContent . trim ( ) ,
576+ file_extension : fileExtension
577+ } ) ;
578+ }
579+ winEl . addEventListener ( 'click' , function ( ) { trackDownloadClick ( winEl , 'exe' ) ; } ) ;
580+ macEl . addEventListener ( 'click' , function ( ) { trackDownloadClick ( macEl , 'dmg' ) ; } ) ;
581+
424582 function finishLoading ( ) {
425583 statusEl . classList . add ( 'is-hidden' ) ;
426584 winEl . removeAttribute ( 'aria-disabled' ) ;
0 commit comments