{"id":190,"date":"2026-06-16T22:09:40","date_gmt":"2026-06-16T22:09:40","guid":{"rendered":"https:\/\/anythingdigital.co.uk\/llynlive\/?page_id=190"},"modified":"2026-06-16T22:09:40","modified_gmt":"2026-06-16T22:09:40","slug":"news","status":"publish","type":"page","link":"https:\/\/anythingdigital.co.uk\/llynlive\/news\/","title":{"rendered":"News"},"content":{"rendered":"    <div class=\"llynews-wrap\" id=\"llynews-6a337a74d3d6c\">\n\n        <div class=\"llynews-header\">\n            <h2 class=\"llynews-title\">Local News &amp; Updates<\/h2>\n            <span class=\"llynews-updating\" id=\"llynews-status\">Loading...<\/span>\n        <\/div>\n\n        <!-- Category tabs -->\n        <div class=\"llynews-tabs\" role=\"tablist\">\n            <button class=\"llynews-tab on\" data-cat=\"all\" onclick=\"llynewsFilter('all',this)\">All news<\/button>\n            <button class=\"llynews-tab\" data-cat=\"local\" onclick=\"llynewsFilter('local',this)\">Local<\/button>\n            <button class=\"llynews-tab\" data-cat=\"planning\" onclick=\"llynewsFilter('planning',this)\">Planning<\/button>\n            <button class=\"llynews-tab\" data-cat=\"welsh\" onclick=\"llynewsFilter('welsh',this)\">Welsh language<\/button>\n            <button class=\"llynews-tab\" data-cat=\"tourism\" onclick=\"llynewsFilter('tourism',this)\">Tourism<\/button>\n        <\/div>\n\n        <!-- News grid -->\n        <div class=\"llynews-grid\" id=\"llynews-grid\">\n                        <div class=\"llynews-skeleton\"><\/div>\n                        <div class=\"llynews-skeleton\"><\/div>\n                        <div class=\"llynews-skeleton\"><\/div>\n                        <div class=\"llynews-skeleton\"><\/div>\n                        <div class=\"llynews-skeleton\"><\/div>\n                        <div class=\"llynews-skeleton\"><\/div>\n                        <div class=\"llynews-skeleton\"><\/div>\n                        <div class=\"llynews-skeleton\"><\/div>\n                    <\/div>\n\n        <div class=\"llynews-footer\">\n            <span class=\"llynews-attribution\">News sourced via Google News \u2014 stories link to original publishers<\/span>\n            <button class=\"llynews-refresh\" onclick=\"llynewsLoad(llynewsCurrentCat)\">\u21bb Refresh<\/button>\n        <\/div>\n\n    <\/div>\n\n    <style>\n    .llynews-wrap{max-width:100%;font-family:-apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;color:#2c2c2a;margin:0 0 2rem}\n    .llynews-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;flex-wrap:wrap;gap:8px}\n    .llynews-title{font-size:1.3rem;font-weight:700;margin:0;background:linear-gradient(135deg,#E8762B,#D4AF37);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}\n    .llynews-updating{font-size:12px;color:#b4b2a9}\n    .llynews-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:1.25rem;border-bottom:1px solid #e5e4e0;padding-bottom:0}\n    .llynews-tab{padding:7px 16px;border:none;border-bottom:2px solid transparent;margin-bottom:-1px;background:none;font-size:13px;color:#888780;cursor:pointer;font-family:inherit;transition:all 0.15s;border-radius:0}\n    .llynews-tab.on{color:#1a8b7f;border-bottom-color:#1a8b7f;font-weight:600}\n    .llynews-tab:hover:not(.on){color:#2c2c2a;background:#f8f7f4}\n    .llynews-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;min-height:200px}\n    .llynews-card{background:#fff;border:1px solid #e5e4e0;border-radius:12px;padding:1.1rem 1.25rem;display:flex;flex-direction:column;gap:8px;transition:box-shadow 0.15s,transform 0.15s;text-decoration:none}\n    .llynews-card:hover{box-shadow:0 4px 16px rgba(0,0,0,0.07);transform:translateY(-2px);border-color:#d3d1c7}\n    .llynews-card-source{display:flex;align-items:center;justify-content:space-between;gap:6px}\n    .llynews-source-name{font-size:11px;font-weight:600;color:#1a8b7f;text-transform:uppercase;letter-spacing:0.04em}\n    .llynews-date{font-size:11px;color:#b4b2a9;white-space:nowrap}\n    .llynews-card-title{font-size:14px;font-weight:600;color:#2c2c2a;line-height:1.45;flex:1}\n    .llynews-card-desc{font-size:12px;color:#888780;line-height:1.6;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n    .llynews-cat-badge{display:inline-block;font-size:10px;font-weight:600;padding:2px 8px;border-radius:10px;align-self:flex-start}\n    .llynews-cat-local{background:#e8f5f1;color:#085041}\n    .llynews-cat-planning{background:#fff3e0;color:#412402}\n    .llynews-cat-welsh{background:#e8f0fb;color:#26215C}\n    .llynews-cat-tourism{background:#fff8e1;color:#5d4037}\n    .llynews-cat-general{background:#f0f0f0;color:#555}\n    .llynews-skeleton{background:#f8f7f4;border-radius:12px;height:140px;animation:llynpulse 1.4s ease-in-out infinite}\n    @keyframes llynpulse{0%,100%{opacity:1}50%{opacity:0.5}}\n    .llynews-error{grid-column:1\/-1;text-align:center;padding:2rem;color:#888;font-size:14px}\n    .llynews-error a{color:#1a8b7f}\n    .llynews-empty{grid-column:1\/-1;text-align:center;padding:2rem;color:#888;font-size:14px}\n    .llynews-footer{display:flex;align-items:center;justify-content:space-between;margin-top:1rem;flex-wrap:wrap;gap:8px}\n    .llynews-attribution{font-size:11px;color:#b4b2a9}\n    .llynews-refresh{background:none;border:1px solid #e5e4e0;border-radius:6px;padding:4px 12px;font-size:12px;color:#888;cursor:pointer;font-family:inherit;transition:all 0.15s}\n    .llynews-refresh:hover{border-color:#1a8b7f;color:#1a8b7f}\n    @media(max-width:600px){.llynews-grid{grid-template-columns:1fr}.llynews-tabs{gap:4px}.llynews-tab{padding:6px 10px;font-size:12px}}\n    <\/style>\n\n    <script>\n    var llynewsCurrentCat = 'all';\n    var llynewsCache = {};\n    var llynewsMaxItems = 8;\n\n    var llynewsFeeds = {\n        all: [\n            'https:\/\/news.google.com\/rss\/search?q=Ll%C5%B3n+Peninsula&hl=en-GB&gl=GB&ceid=GB:en',\n            'https:\/\/news.google.com\/rss\/search?q=Pwllheli+OR+Abersoch+OR+Nefyn+OR+Aberdaron&hl=en-GB&gl=GB&ceid=GB:en',\n            'https:\/\/news.google.com\/rss\/search?q=Gwynedd+Wales+news&hl=en-GB&gl=GB&ceid=GB:en'\n        ],\n        local: [\n            'https:\/\/news.google.com\/rss\/search?q=Pwllheli+OR+Abersoch+OR+Nefyn+Ll\u0177n&hl=en-GB&gl=GB&ceid=GB:en',\n            'https:\/\/news.google.com\/rss\/search?q=Criccieth+OR+Llanbedrog+OR+Aberdaron+Wales&hl=en-GB&gl=GB&ceid=GB:en'\n        ],\n        planning: [\n            'https:\/\/news.google.com\/rss\/search?q=Gwynedd+planning+application&hl=en-GB&gl=GB&ceid=GB:en',\n            'https:\/\/news.google.com\/rss\/search?q=Ll\u0177n+Peninsula+development+planning&hl=en-GB&gl=GB&ceid=GB:en',\n            'https:\/\/news.google.com\/rss\/search?q=Gwynedd+Council+housing&hl=en-GB&gl=GB&ceid=GB:en'\n        ],\n        welsh: [\n            'https:\/\/news.google.com\/rss\/search?q=Welsh+language+Cymraeg+Gwynedd&hl=en-GB&gl=GB&ceid=GB:en',\n            'https:\/\/news.google.com\/rss\/search?q=Ll\u0177n+Peninsula+Welsh+community&hl=en-GB&gl=GB&ceid=GB:en'\n        ],\n        tourism: [\n            'https:\/\/news.google.com\/rss\/search?q=Ll\u0177n+Peninsula+tourism+visitor&hl=en-GB&gl=GB&ceid=GB:en',\n            'https:\/\/news.google.com\/rss\/search?q=Abersoch+Pwllheli+holiday+beach&hl=en-GB&gl=GB&ceid=GB:en'\n        ]\n    };\n\n    var llynewsCatLabels = {\n        local: { label:'Local', cls:'llynews-cat-local' },\n        planning: { label:'Planning', cls:'llynews-cat-planning' },\n        welsh: { label:'Welsh language', cls:'llynews-cat-welsh' },\n        tourism: { label:'Tourism', cls:'llynews-cat-tourism' },\n        general: { label:'General', cls:'llynews-cat-general' }\n    };\n\n    function llynewsTimeAgo(dateStr) {\n        var d = new Date(dateStr);\n        if (isNaN(d)) return '';\n        var diff = Math.floor((Date.now() - d) \/ 1000);\n        if (diff < 60) return 'Just now';\n        if (diff < 3600) return Math.floor(diff\/60) + 'm ago';\n        if (diff < 86400) return Math.floor(diff\/3600) + 'h ago';\n        if (diff < 604800) return Math.floor(diff\/86400) + 'd ago';\n        return d.toLocaleDateString('en-GB', {day:'numeric',month:'short'});\n    }\n\n    function llynewsGuessCategory(title, desc) {\n        var text = (title + ' ' + desc).toLowerCase();\n        if (\/planning|application|development|build|housing|council|permission\/.test(text)) return 'planning';\n        if (\/welsh|cymraeg|cymru|language|bilingual\/.test(text)) return 'welsh';\n        if (\/tourism|tourist|visitor|holiday|beach|coast|walking|surfing|sailing\/.test(text)) return 'tourism';\n        if (\/pwllheli|abersoch|nefyn|aberdaron|criccieth|llanbedrog|morfa|bardsey|porthdinllaen\/.test(text)) return 'local';\n        return 'general';\n    }\n\n    function llynewsCleanTitle(title) {\n        \/\/ Google News appends \" - Source Name\" \u2014 strip it for cleaner display\n        return title.replace(\/\\s[-\u2013]\\s[^-\u2013]+$\/, '').trim();\n    }\n\n    function llynewsExtractSource(title) {\n        var match = title.match(\/\\s[-\u2013]\\s([^-\u2013]+)$\/);\n        return match ? match[1].trim() : 'News';\n    }\n\n    function llynewsStripHtml(html) {\n        var d = document.createElement('div');\n        d.innerHTML = html;\n        return d.textContent || d.innerText || '';\n    }\n\n    async function llynewsFetchFeed(url) {\n        var proxy = 'https:\/\/api.rss2json.com\/v1\/api.json?rss_url=' + encodeURIComponent(url) + '&api_key=&count=10';\n        try {\n            var r = await fetch(proxy, { cache: 'default' });\n            if (!r.ok) throw new Error('HTTP ' + r.status);\n            var d = await r.json();\n            if (d.status !== 'ok') throw new Error('Feed error');\n            return d.items || [];\n        } catch(e) {\n            \/\/ Fallback: try alternate proxy\n            try {\n                var proxy2 = 'https:\/\/api.allorigins.win\/get?url=' + encodeURIComponent(url);\n                var r2 = await fetch(proxy2);\n                var d2 = await r2.json();\n                var parser = new DOMParser();\n                var xml = parser.parseFromString(d2.contents, 'text\/xml');\n                var items = Array.from(xml.querySelectorAll('item')).slice(0, 10);\n                return items.map(function(item) {\n                    return {\n                        title: item.querySelector('title')?.textContent || '',\n                        link: item.querySelector('link')?.textContent || '',\n                        pubDate: item.querySelector('pubDate')?.textContent || '',\n                        description: item.querySelector('description')?.textContent || ''\n                    };\n                });\n            } catch(e2) {\n                return [];\n            }\n        }\n    }\n\n    async function llynewsLoad(cat) {\n        if (!cat) cat = 'all';\n        llynewsCurrentCat = cat;\n\n        var grid = document.getElementById('llynews-grid');\n        var status = document.getElementById('llynews-status');\n        if (!grid) return;\n\n        \/\/ Show skeletons\n        var skels = '';\n        for (var i=0; i<llynewsMaxItems; i++) skels += '<div class=\"llynews-skeleton\"><\/div>';\n        grid.innerHTML = skels;\n        if (status) status.textContent = 'Loading...';\n\n        \/\/ Use cache if available (5 min TTL)\n        var cacheKey = cat;\n        var now = Date.now();\n        if (llynewsCache[cacheKey] && (now - llynewsCache[cacheKey].ts) < 300000) {\n            llynewsRender(llynewsCache[cacheKey].items, grid, status);\n            return;\n        }\n\n        var feeds = llynewsFeeds[cat] || llynewsFeeds['all'];\n        var allItems = [];\n\n        \/\/ Fetch all feeds in parallel\n        var results = await Promise.allSettled(feeds.map(llynewsFetchFeed));\n        results.forEach(function(r) {\n            if (r.status === 'fulfilled') allItems = allItems.concat(r.value);\n        });\n\n        \/\/ Deduplicate by title similarity\n        var seen = [];\n        allItems = allItems.filter(function(item) {\n            var clean = (item.title||'').toLowerCase().replace(\/[^a-z0-9]\/g,'').substring(0,40);\n            if (seen.indexOf(clean) !== -1) return false;\n            seen.push(clean);\n            return true;\n        });\n\n        \/\/ Sort by date desc\n        allItems.sort(function(a,b) {\n            return new Date(b.pubDate||0) - new Date(a.pubDate||0);\n        });\n\n        \/\/ Limit\n        allItems = allItems.slice(0, llynewsMaxItems);\n\n        \/\/ Cache\n        llynewsCache[cacheKey] = { items: allItems, ts: now };\n\n        llynewsRender(allItems, grid, status);\n    }\n\n    function llynewsRender(items, grid, status) {\n        if (!items || items.length === 0) {\n            grid.innerHTML = '<div class=\"llynews-empty\"><p>No news found for this category right now.<\/p><p>Try <a href=\"#\" onclick=\"llynewsLoad(\\'all\\');return false;\">all news<\/a> or check back later.<\/p><\/div>';\n            if (status) status.textContent = 'No results';\n            return;\n        }\n\n        var html = '';\n        items.forEach(function(item) {\n            var rawTitle = item.title || 'Untitled';\n            var title = llynewsCleanTitle(rawTitle);\n            var source = llynewsExtractSource(rawTitle);\n            var link = item.link || '#';\n            var date = llynewsTimeAgo(item.pubDate);\n            var desc = llynewsStripHtml(item.description || item.content || '').substring(0, 120);\n            if (desc && !desc.endsWith('.')) desc = desc.replace(\/[,\\s]+$\/, '') + '...';\n            var cat = llynewsGuessCategory(title, desc);\n            var badge = llynewsCatLabels[cat] || llynewsCatLabels.general;\n\n            html += '<a class=\"llynews-card\" href=\"' + link + '\" target=\"_blank\" rel=\"noopener noreferrer\">' +\n                '<div class=\"llynews-card-source\">' +\n                    '<span class=\"llynews-source-name\">' + llynewsEsc(source) + '<\/span>' +\n                    '<span class=\"llynews-date\">' + date + '<\/span>' +\n                '<\/div>' +\n                '<div class=\"llynews-card-title\">' + llynewsEsc(title) + '<\/div>' +\n                (desc ? '<div class=\"llynews-card-desc\">' + llynewsEsc(desc) + '<\/div>' : '') +\n                '<span class=\"llynews-cat-badge ' + badge.cls + '\">' + badge.label + '<\/span>' +\n            '<\/a>';\n        });\n\n        grid.innerHTML = html;\n        if (status) {\n            var now = new Date();\n            status.textContent = 'Updated ' + now.getHours().toString().padStart(2,'0') + ':' + now.getMinutes().toString().padStart(2,'0');\n        }\n    }\n\n    function llynewsEsc(str) {\n        return String(str).replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;');\n    }\n\n    function llynewsFilter(cat, btn) {\n        document.querySelectorAll('.llynews-tab').forEach(function(b) { b.classList.remove('on'); });\n        btn.classList.add('on');\n        llynewsLoad(cat);\n    }\n\n    \/\/ Auto-load on DOMContentLoaded\n    document.addEventListener('DOMContentLoaded', function() {\n        llynewsLoad('all');\n    });\n    \/\/ Also load immediately in case DOM is already ready\n    if (document.readyState !== 'loading') {\n        llynewsLoad('all');\n    }\n    <\/script>\n    \n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"page-with-title","meta":{"footnotes":""},"class_list":["post-190","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/pages\/190","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/comments?post=190"}],"version-history":[{"count":1,"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/pages\/190\/revisions"}],"predecessor-version":[{"id":191,"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/pages\/190\/revisions\/191"}],"wp:attachment":[{"href":"https:\/\/anythingdigital.co.uk\/llynlive\/wp-json\/wp\/v2\/media?parent=190"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}