1<!doctype html> 2<html lang="en"> 3<head> 4 <meta charset="utf-8" /> 5 <title>New Stations â Quick Add | Non Stop Radio</title> 6 <link rel="canonical" href="https://nonstopradio.app/stations/" /> 7 <meta name="robots" content="noindex, follow" /> 8 <meta name="viewport" content="width=device-width, initial-scale=1" /> 9 <meta name="description" 10 content="Preview new stations that will be added to Non Stop Radio. Open this page on your Android device and add them straight to your favourites with one tap."> 11 12 <style> 13 :root { 14 color-scheme: dark; 15 --bg: #050c14; 16 --card-bg: #0f2230; 17 --accent: #ffd700; 18 --accent-soft: #ffe680; 19 --text-main: #f5f7fa; 20 --text-muted: #9fb3c8; 21 --border-subtle: #1e3647; 22 } 23 24 * { box-sizing: border-box; } 25 26 body { 27 margin: 0; 28 min-height: 100vh; 29 display: flex; 30 justify-content: center; 31 align-items: center; 32 background: radial-gradient(circle at top, #132a3b 0, #050c14 55%); 33 color: var(--text-main); 34 font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; 35 } 36 37 .page { 38 width: 100%; 39 max-width: 980px; 40 padding: 2.5rem 1.5rem 2.75rem; 41 } 42 43 .card { 44 background: rgba(15, 34, 48, 0.96); 45 border-radius: 24px; 46 padding: 2.1rem 1.7rem 1.9rem; 47 border: 1px solid var(--border-subtle); 48 box-shadow: 0 18px 60px rgba(0, 0, 0, 0.45); 49 } 50 51 @media (max-width: 640px) { 52 .card { 53 padding: 1.7rem 1.3rem 1.7rem; 54 } 55 } 56 57 .header { 58 margin-bottom: 1.4rem; 59 } 60 61 .eyebrow { 62 font-size: .78rem; 63 text-transform: uppercase; 64 letter-spacing: .09em; 65 color: var(--accent-soft); 66 opacity: .9; 67 } 68 69 .title { 70 margin: .25rem 0 .45rem; 71 font-size: clamp(1.6rem, 3vw, 1.9rem); 72 } 73 74 .subtitle { 75 margin: 0; 76 font-size: .95rem; 77 color: var(--text-muted); 78 max-width: 48rem; 79 } 80 81 .stations-grid { 82 margin-top: 1.2rem; 83 display: grid; 84 grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); 85 gap: 0.9rem 1rem; 86 } 87 88 .station-card { 89 display: flex; 90 align-items: flex-start; 91 gap: .75rem; 92 padding: .85rem .85rem 1rem; 93 border-radius: 16px; 94 background: rgba(4, 14, 24, 0.9); 95 border: 1px solid rgba(30, 54, 71, 0.9); 96 } 97 98 .station-icon { 99 width: 48px; 100 height: 48px; 101 border-radius: 14px; 102 background: #122536; 103 flex-shrink: 0; 104 object-fit: cover; 105 display: block; 106 } 107 108 .station-icon.placeholder { 109 display: flex; 110 align-items: center; 111 justify-content: center; 112 font-weight: 700; 113 font-size: 1.1rem; 114 color: var(--accent-soft); 115 text-transform: uppercase; 116 } 117 118 .station-body { 119 min-width: 0; 120 flex: 1; 121 } 122 123 .station-name { 124 font-size: .98rem; 125 font-weight: 700; 126 margin: 0 0 .1rem; 127 } 128 129 .station-meta { 130 font-size: .78rem; 131 color: var(--text-muted); 132 margin: 0 0 .35rem; 133 } 134 135 .station-meta span + span::before { 136 content: " · "; 137 opacity: .8; 138 } 139 140 .station-homepage { 141 font-size: .8rem; 142 color: var(--accent-soft); 143 text-decoration: none; 144 word-break: break-all; 145 } 146 147 .station-homepage:hover { 148 text-decoration: underline; 149 } 150 151 .station-footer { 152 margin-top: .5rem; 153 display: flex; 154 justify-content: flex-start; 155 align-items: center; 156 gap: .4rem; 157 font-size: .76rem; 158 color: var(--text-muted); 159 } 160 161 .btn { 162 display: inline-flex; 163 align-items: center; 164 justify-content: center; 165 padding: .5rem 1.1rem; 166 border-radius: 999px; 167 border: 1px solid transparent; 168 font-size: .8rem; 169 font-weight: 600; 170 cursor: pointer; 171 background: var(--accent); 172 color: #222; 173 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6); 174 white-space: nowrap; 175 flex-shrink: 0; 176 } 177 178 .btn:hover { 179 background: #ffe36a; 180 } 181 182 /* ---- Support block (no icon) ---- */ 183 .support-card { 184 margin-top: 1rem; 185 padding: .95rem 1rem; 186 border-radius: 18px;
187 background: rgba(4, 14, 24, 0.9); 188 border: 1px solid rgba(30, 54, 71, 0.9); 189 display: flex; 190 gap: .9rem; 191 align-items: center; 192 justify-content: space-between; 193 } 194 195 .support-text { 196 min-width: 0; 197 } 198 199 .support-title { 200 margin: 0 0 .25rem; 201 font-size: .95rem; 202 font-weight: 750; 203 } 204 205 .support-desc { 206 margin: 0; 207 font-size: .85rem; 208 color: var(--text-muted); 209 line-height: 1.35; 210 } 211 212 .support-actions { 213 display: flex; 214 align-items: center; 215 gap: .6rem; 216 flex-shrink: 0; 217 } 218 219 .btn-support { 220 background: transparent; 221 color: var(--accent-soft); 222 border: 1px solid rgba(255, 215, 0, 0.5); 223 box-shadow: none; 224 padding: .55rem 1.05rem; 225 } 226 227 .btn-support:hover { 228 background: rgba(6, 18, 30, 0.9); 229 border-color: var(--accent); 230 } 231 /* ---- End support block ---- */ 232 233 .tip { 234 margin-top: 1.2rem; 235 font-size: .78rem; 236 color: var(--text-muted); 237 border-top: 1px dashed var(--border-subtle); 238 padding-top: .7rem; 239 } 240 </style> 241</head> 242<body> 243 <main class="page"> 244 <section class="card"> 245 <header class="header"> 246 <div class="eyebrow">Upcoming stations</div> 247 <h1 class="title">New Stations & Quick Add</h1> 248 <p class="subtitle"> 249 These stations will be included in the next Non Stop Radio update. 250 If you want to add any of them right now, open this page on your Android phone 251 where the Non Stop Radio app is installed and tap âAdd to favouritesâ. 252 </p> 253 </header> 254 255 <section id="stations-grid" class="stations-grid" aria-live="polite"></section> 256 257 <!-- Support block (no icon) --> 258 <section class="support-card" aria-label="Optional support"> 259 <div class="support-text"> 260 <h2 class="support-title">Support NSR (optional)</h2> 261 <p class="support-desc"> 262 NSR is free and ad-free. If this page helps you add stations faster, optional support helps cover hosting and development time. 263 No features are unlocked. 264 </p> 265 </div> 266 267 <div class="support-actions"> 268 <a class="btn btn-support" href="/support/" aria-label="Support Non Stop Radio"> 269 Support NSR 270 </a> 271 </div> 272 </section> 273 274 <p class="tip"> 275 Tip: If tapping the button doesnât open the app, it will try to fall back to Google Play. 276 </p> 277 </section> 278 </main> 279 280
280<script> 281 (async function () { 282 const grid = document.getElementById('stations-grid'); 283 let stations = []; 284 285 try { 286 const res = await fetch('./stations.json', { cache: 'no-store' }); 287 stations = await res.json(); 288 } catch (e) { 289 console.error('Failed to load stations.json', e); 290 grid.innerHTML = '<p>Could not load stations. Please try again later.</p>'; 291 return; 292 } 293 294 function openDeepLink(url) { 295 // Î ÏοÏÏαθεί να ανοίξει Ïην εÏαÏμογή· αν δεν Ï ÏάÏÏει, Ïάει Google Play 296 window.location.href = url; 297 setTimeout(() => { 298 window.location.href = 299 'https://play.google.com/store/apps/details?id=com.assistant.greekradio'; 300 }, 1200); 301 } 302 303 stations.forEach(st => { 304 const card = document.createElement('article'); 305 card.className = 'station-card'; 306 307 const name = st.name || 'Unknown station'; 308 const country = st.country || ''; 309 const tagsText = Array.isArray(st.tags) ? st.tags.join(' · ') : (st.tags || ''); 310 const homepage = st.homepage || ''; 311 const deeplink = st.deeplink; 312 313 const iconHtml = (st.favicon && String(st.favicon).trim()) 314 ? `<img class="station-icon" src="${st.favicon}" alt="${name} logo" loading="lazy">` 315 : `<div class="station-icon placeholder">${name.charAt(0)}</div>`; 316 317 card.innerHTML = ` 318 ${iconHtml} 319 <div class="station-body"> 320 <h2 class="station-name">${name}</h2> 321 <p class="station-meta"> 322 ${country ? `<span>${country}</span>` : ''} 323 ${tagsText ? `<span>${tagsText}</span>` : ''} 324 </p> 325 ${homepage ? `<a class="station-homepage" href="${homepage}" target="_blank" rel="noopener">Homepage</a>` : ''} 326 <div class="station-footer"> 327 <button class="btn" type="button">Add to favourites</button> 328 </div> 329 </div> 330 `; 331 332 const btn = card.querySelector('.btn'); 333 if (deeplink && btn) { 334 btn.addEventListener('click', () => openDeepLink(deeplink)); 335 } else if (btn) { 336 btn.disabled = true; 337 btn.textContent = 'No deeplink'; 338 } 339 340 const img = card.querySelector('img.station-icon'); 341 if (img) { 342 img.addEventListener('error', () => { 343 img.replaceWith(createPlaceholderIcon(name)); 344 }); 345 } 346 347 grid.appendChild(card); 348 }); 349 350 function createPlaceholderIcon(name) { 351 const div = document.createElement('div'); 352 div.className = 'station-icon placeholder'; 353 div.textContent = (name || '?').charAt(0); 354 return div; 355 } 356 })(); 357 </script>
357 358
358<script src="/assets/i18n-fr.js"></script>
358 359
359<script src="/assets/i18n-es.js"></script>
359 360
360<script src="/assets/i18n-it.js"></script>
360 361
361<script src="/assets/i18n.js"></script>
361 362</body> 363</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.