__( 'Lisbon Travel Guide (demo site)', 'clusterlane' ), // Site Intelligence Analysis — same shape as the live response (0.98.110+): // "Label: text" strengths, and a tool + clusters per opportunity/action // (shown as disabled "go" buttons; the demo clusters don't exist here). 'site_analysis' => [ 'overview' => __( 'The site is in fair shape: its 11 clusters score between 38% and 84%, and 9 of 142 articles have no links pointing to them. The neighbourhood guides are the strongest part — they link to each other a lot. Day Trips and Food & Drink are the weakest: few links between their articles, and restaurant pages that mention neighbourhoods without linking to them.', 'clusterlane' ), 'strengths' => [ __( 'Neighbourhood guides reinforce each other: Alfama, Baixa and Belém link to each other in both directions and no article is left out, so the whole topic is easy to find and follow.', 'clusterlane' ), __( 'Museums has one clear main article: every museum page links to the Lisbon museums guide, which gives the topic one strong page to rank.', 'clusterlane' ), __( 'Almost no unlinked articles: 9 of 11 clusters have every article linked from at least one other article.', 'clusterlane' ), ], 'weak_clusters' => [ [ 'label' => __( 'Day Trips', 'clusterlane' ), 'health' => 38 ], [ 'label' => __( 'Food & Drink', 'clusterlane' ), 'health' => 47 ], ], 'opportunities' => [ [ 'title' => __( 'Day Trips: link the articles to each other', 'clusterlane' ), 'description' => __( 'The 8 day-trip articles link almost only to “Sintra in a Day”. Links between Cascais, Évora and Óbidos would help readers compare and keep them on the site.', 'clusterlane' ), 'tool' => 'improve', 'targets' => [ [ 'key' => 'demo_day_trips', 'label' => __( 'Day Trips', 'clusterlane' ) ] ] ], [ 'title' => __( 'Food & Drink mentions neighbourhoods without linking', 'clusterlane' ), 'description' => __( 'Six restaurant articles name Alfama or Bairro Alto without a link to those guides — each is a one-click fix.', 'clusterlane' ), 'tool' => 'text_match', 'targets' => [] ], [ 'title' => __( 'Food & Drink has no clear main article', 'clusterlane' ), 'description' => __( 'Links are spread evenly across 21 articles. Choosing “Where to eat in Lisbon” as the main article and linking every food page to it would give the topic one page to rank.', 'clusterlane' ), 'tool' => 'pillar', 'targets' => [ [ 'key' => 'demo_food', 'label' => __( 'Food & Drink', 'clusterlane' ) ] ] ], [ 'title' => __( 'Day Trips is missing the inland towns', 'clusterlane' ), 'description' => __( 'Évora and Tomar are common searches from Lisbon but have no articles yet.', 'clusterlane' ), 'tool' => 'gap', 'targets' => [ [ 'key' => 'demo_day_trips', 'label' => __( 'Day Trips', 'clusterlane' ) ] ] ], ], 'priority_actions' => [ [ 'action' => __( 'Link the 9 articles that nothing links to', 'clusterlane' ), 'rationale' => __( 'The fastest gain: every article becomes reachable, and the two weakest clusters move toward the site average.', 'clusterlane' ), 'tool' => 'improve', 'targets' => [ [ 'key' => 'demo_day_trips', 'label' => __( 'Day Trips', 'clusterlane' ) ], [ 'key' => 'demo_food', 'label' => __( 'Food & Drink', 'clusterlane' ) ] ] ], [ 'action' => __( 'Make “Where to eat in Lisbon” the main article of Food & Drink', 'clusterlane' ), 'rationale' => __( 'One clear page for the topic, fed by the 20 restaurant and market articles.', 'clusterlane' ), 'tool' => 'pillar', 'targets' => [ [ 'key' => 'demo_food', 'label' => __( 'Food & Drink', 'clusterlane' ) ] ] ], [ 'action' => __( 'Write “Évora Day Trip from Lisbon”', 'clusterlane' ), 'rationale' => __( 'Fills the biggest gap in Day Trips and gives Sintra and Cascais a natural article to link to.', 'clusterlane' ), 'tool' => 'gap', 'targets' => [ [ 'key' => 'demo_day_trips', 'label' => __( 'Day Trips', 'clusterlane' ) ] ] ], ], ], 'seo_meta' => [ 'suggestions' => [ [ 'title' => __( 'Sintra in a Day: Palaces, Gardens & Trains from Lisbon', 'clusterlane' ), 'description' => __( 'Plan a day trip to Sintra from Lisbon: which palaces to pick, how to beat the queues, and the train times that make it work without a car.', 'clusterlane' ) ], [ 'title' => __( 'Sintra Day Trip: Pena Palace, Quinta da Regaleira & More', 'clusterlane' ), 'description' => __( 'Fairy-tale palaces, mossy gardens and Atlantic views: see the best of Sintra in one day, with a route that starts and ends at Rossio station.', 'clusterlane' ) ], [ 'title' => __( 'How to See Sintra in One Day (Without a Car)', 'clusterlane' ), 'description' => __( 'Train from Lisbon, bus 434 up the hill and a walking route between Pena, the Moorish Castle and Regaleira — with tickets and timings for one day.', 'clusterlane' ) ], ], 'cached' => true, 'can_write' => true, 'source' => __( 'Custom fields', 'clusterlane' ), 'source_id' => 'custom', 'write_note' => '', ], // SEO Titles (0.98.154): two rows of the demo site with the AI's suggestions under each field. 'seo_titles' => [ 'ctx' => [ 'source' => __( 'Custom fields', 'clusterlane' ), 'source_id' => 'custom', 'title_max' => 60, 'desc_min' => 70, 'desc_max' => 160, 'suffix' => [ 'mode' => 'none', 'text' => '' ] ], 'items' => [ [ 'id' => -1, 'title' => __( 'Sintra Day Trip from Lisbon: The Complete Guide to Palaces, Gardens and Getting There', 'clusterlane' ), 'cluster_label' => __( 'Day Trips', 'clusterlane' ), 'role' => 'pillar', 'url' => 'https://lisbon.example/day-trips/sintra/', 'seo' => [ 'title' => __( 'Sintra Day Trip from Lisbon: The Complete Guide to Palaces, Gardens and Getting There', 'clusterlane' ), 'title_from' => 'post_title', 'description' => '', 'description_from' => '', 'title_status' => 'long', 'description_status' => 'missing' ], 'check' => [ 't' => 2, 'd' => 1, 'ti' => __( 'No SEO title — the long post title is cut off in results.', 'clusterlane' ), 'di' => __( 'No meta description.', 'clusterlane' ), 'stale' => false ], 'ai' => [ [ 'title' => __( 'Sintra in a Day: Palaces, Gardens & Trains from Lisbon', 'clusterlane' ), 'description' => __( 'Plan a day trip to Sintra from Lisbon: which palaces to pick, how to beat the queues, and the train times that make it work without a car.', 'clusterlane' ) ], [ 'title' => __( 'How to See Sintra in One Day (Without a Car)', 'clusterlane' ), 'description' => __( 'Train from Lisbon, bus 434 up the hill and a walking route between Pena, the Moorish Castle and Regaleira — with tickets and timings for one day.', 'clusterlane' ) ], ] ], [ 'id' => -2, 'title' => __( 'Where to Eat in Alfama', 'clusterlane' ), 'cluster_label' => __( 'Food & Drink', 'clusterlane' ), 'role' => 'support', 'url' => 'https://lisbon.example/food/alfama/', 'seo' => [ 'title' => __( 'Where to Eat in Alfama: 12 Tascas Locals Love', 'clusterlane' ), 'title_from' => 'field', 'description' => __( 'Good food in Alfama.', 'clusterlane' ), 'description_from' => 'field', 'title_status' => 'ok', 'description_status' => 'short' ], 'check' => [ 't' => 5, 'd' => 2, 'ti' => '', 'di' => __( 'Too short and generic — names no tasca, dish or price.', 'clusterlane' ), 'stale' => false ], 'ai' => [ [ 'title' => __( 'Alfama Restaurants: 12 Tascas and Fado Houses', 'clusterlane' ), 'description' => __( 'Twelve small tascas in Alfama where locals eat — grilled sardines, bacalhau and fado nights — with prices, opening hours and which ones to book.', 'clusterlane' ) ], ] ], ], ], 'gap' => [ 'suggestions' => [ [ 'title' => __( 'Évora Day Trip: Roman Temple, Bone Chapel and Wine', 'clusterlane' ), 'rationale' => __( 'The cluster covers Sintra and Cascais but not the most-searched inland day trip.', 'clusterlane' ) ], [ 'title' => __( 'Getting Around: Trains from Lisbon to Every Day-Trip Town', 'clusterlane' ), 'rationale' => __( 'A practical hub every day-trip article can link to — fixes the single-hub problem.', 'clusterlane' ) ], [ 'title' => __( 'Day Trips from Lisbon Without a Car', 'clusterlane' ), 'rationale' => __( 'Matches a common search intent the current articles only touch on.', 'clusterlane' ) ], ], ], 'cluster_analysis' => [ 'model' => 'gpt-4o-mini', 'summary' => __( 'Day Trips has 7 articles and a health of 38%. Almost all internal links point to "Sintra in a Day", two articles are orphans, and the cluster has no practical transport hub.', 'clusterlane' ), 'strengths' => [ __( '"Sintra in a Day" is a strong pillar (p100, 6 inbound links).', 'clusterlane' ), __( 'Articles are long and well illustrated (avg. 1,450 words).', 'clusterlane' ), ], 'content_gaps' => [ __( 'Évora and the Alentejo', 'clusterlane' ), __( 'Train and bus connections', 'clusterlane' ), ], 'improvements' => [ [ 'title' => __( 'Link the two orphans', 'clusterlane' ), 'description' => __( '"Óbidos Walled Town" and "Arrábida Beaches" have no inbound links — add them from the Sintra and Cascais articles.', 'clusterlane' ) ], [ 'title' => __( 'Spread links beyond the pillar', 'clusterlane' ), 'description' => __( 'Gini is 78. Let supporting articles link to each other, not only to Sintra.', 'clusterlane' ) ], ], ], 'article_analysis' => [ 'status_summary' => __( '"Cascais Beaches Guide" is a solid support article (score 74) with 2 inbound and 1 outbound link. It is well structured but under-connected to the rest of Day Trips.', 'clusterlane' ), 'content_improvements' => [ [ 'area' => __( 'Meta description', 'clusterlane' ), 'suggestion' => __( 'Missing — add a 140–160 character summary mentioning the train from Cais do Sodré.', 'clusterlane' ) ], [ 'area' => __( 'Structure', 'clusterlane' ), 'suggestion' => __( 'Split the 600-word "Getting there" section into train, car and bike with H2s.', 'clusterlane' ) ], ], 'sister_articles' => [ [ 'title' => __( 'Boca do Inferno Cliffs at Sunset', 'clusterlane' ), 'rationale' => __( 'Readers of beach guides often look for the nearby viewpoint; no article covers it yet.', 'clusterlane' ) ], ], 'cluster_link_actions' => [ [ 'action' => __( 'Link to "Sintra in a Day"', 'clusterlane' ), 'rationale' => __( 'Many visitors combine Cascais and Sintra; the article mentions Sintra twice without a link.', 'clusterlane' ) ], ], ], // Review cluster structure — the overview in Cluster Admin (0.98.141). 'structure_review' => [ 'min' => 6, 'clusters' => [ [ 'key' => 'demo_hotels', 'label' => __( 'Hotels', 'clusterlane' ), 'type' => 'category', 'members' => 21, 'health' => 61, 'eligible' => true, 'status' => 'fresh', 'generated_at' => '', 'pillar' => [ 'id' => 1, 'title' => __( 'Where to Stay in Lisbon: The Complete Hotel Guide', 'clusterlane' ), 'chosen' => true ], 'summary' => [ 'by' => __( 'area', 'clusterlane' ), 'alt_by' => __( 'price', 'clusterlane' ), 'groups' => [ [ 'name' => __( 'Alfama & Castelo hotels', 'clusterlane' ), 'n' => 5, 'idea' => '' ], [ 'name' => __( 'Riverside hotels', 'clusterlane' ), 'n' => 4, 'idea' => '' ], [ 'name' => __( 'Avenida & Bairro Alto hotels', 'clusterlane' ), 'n' => 5, 'idea' => __( 'Where to Stay Around Avenida da Liberdade', 'clusterlane' ) ] ], 'note' => '', 'pillar_idea' => '', 'better_pillar' => null ] ], [ 'key' => 'demo_food', 'label' => __( 'Food & Drink', 'clusterlane' ), 'type' => 'category', 'members' => 21, 'health' => 47, 'eligible' => true, 'status' => 'fresh', 'generated_at' => '', 'pillar' => [ 'id' => 2, 'title' => __( 'Time Out Market: What to Eat', 'clusterlane' ), 'chosen' => false ], 'summary' => [ 'groups' => [ [ 'name' => __( 'Restaurants', 'clusterlane' ), 'n' => 9, 'idea' => '' ], [ 'name' => __( 'Pastries & cafés', 'clusterlane' ), 'n' => 7, 'idea' => '' ] ], 'note' => '', 'pillar_idea' => __( 'Where to Eat in Lisbon: A Neighbourhood Guide', 'clusterlane' ), 'better_pillar' => null ] ], [ 'key' => 'demo_day_trips', 'label' => __( 'Day Trips', 'clusterlane' ), 'type' => 'category', 'members' => 8, 'health' => 38, 'eligible' => true, 'status' => 'fresh', 'generated_at' => '', 'pillar' => [ 'id' => 3, 'title' => __( 'Cascais by Train', 'clusterlane' ), 'chosen' => false ], 'summary' => [ 'groups' => [], 'note' => __( 'One topic: day trips from Lisbon.', 'clusterlane' ), 'pillar_idea' => '', 'better_pillar' => [ 'id' => 4, 'title' => __( 'Sintra in a Day', 'clusterlane' ) ] ] ], [ 'key' => 'demo_alfama', 'label' => __( 'Alfama', 'clusterlane' ), 'type' => 'category', 'members' => 12, 'health' => 84, 'eligible' => true, 'status' => 'fresh', 'generated_at' => '', 'pillar' => [ 'id' => 5, 'title' => __( 'Alfama: A Walking Guide', 'clusterlane' ), 'chosen' => true ], 'summary' => [ 'groups' => [], 'note' => __( 'One coherent neighbourhood guide.', 'clusterlane' ), 'pillar_idea' => '', 'better_pillar' => null ] ], [ 'key' => 'demo_museums', 'label' => __( 'Museums', 'clusterlane' ), 'type' => 'category', 'members' => 14, 'health' => 72, 'eligible' => true, 'status' => 'none', 'pillar' => [ 'id' => 6, 'title' => __( 'Lisbon Museums Guide', 'clusterlane' ), 'chosen' => false ] ], [ 'key' => 'demo_viewpoints', 'label' => __( 'Viewpoints', 'clusterlane' ), 'type' => 'post_tag', 'members' => 4, 'health' => 55, 'eligible' => false, 'status' => 'none', 'pillar' => [ 'id' => 0, 'title' => '', 'chosen' => false ] ], ], ], // Split this cluster — Hotels in the demo site (0.98.138). 'cluster_split' => [ 'cluster' => [ 'key' => 'demo_hotels', 'label' => __( 'Hotels', 'clusterlane' ) ], 'members' => [ [ 'id' => 1, 'title' => __( 'Where to Stay in Lisbon: The Complete Hotel Guide', 'clusterlane' ), 'role' => 'pillar', 'thumb' => '' ], [ 'id' => 2, 'title' => __( 'Memmo Alfama: Rooftop Pool Over the Old Town', 'clusterlane' ), 'role' => 'support', 'thumb' => '' ], [ 'id' => 3, 'title' => __( 'Solar do Castelo: Sleeping Inside the Castle Walls', 'clusterlane' ), 'role' => 'support', 'thumb' => '' ], [ 'id' => 4, 'title' => __( 'Palácio Belmonte: Lisbon’s Oldest Palace Hotel', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 5, 'title' => __( 'Santiago de Alfama: Boutique Hotel on the Tram 28 Line', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 6, 'title' => __( 'Hotel Convento do Salvador', 'clusterlane' ), 'role' => 'orphan', 'thumb' => '' ], [ 'id' => 7, 'title' => __( 'Altis Belém: Design Hotel on the River', 'clusterlane' ), 'role' => 'support', 'thumb' => '' ], [ 'id' => 8, 'title' => __( 'Pestana Palace: Gardens Above the Tagus', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 9, 'title' => __( 'The Lumiares: Bairro Alto Apartments with a View', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 10, 'title' => __( 'Corinthia Lisbon: Business Hotel Near the Park', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 11, 'title' => __( 'Hotel Cais do Sodré: Waterfront on a Budget', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 12, 'title' => __( 'Lx Boutique Hotel by the River', 'clusterlane' ), 'role' => 'support', 'thumb' => '' ], [ 'id' => 13, 'title' => __( 'Sheraton Lisboa Hotel & Spa', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 14, 'title' => __( 'Home Lisbon Hostel: Family-Run in Baixa', 'clusterlane' ), 'role' => 'support', 'thumb' => '' ], [ 'id' => 15, 'title' => __( 'Lisb’on Hostel: Garden Hostel in Chiado', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 16, 'title' => __( 'Yes! Lisbon Hostel', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 17, 'title' => __( 'Goodmorning Solo Traveller Hostel', 'clusterlane' ), 'role' => 'orphan', 'thumb' => '' ], [ 'id' => 18, 'title' => __( 'Selina Secret Garden: Hostel with a Pool', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 19, 'title' => __( 'Four Seasons Ritz Lisbon', 'clusterlane' ), 'role' => 'support', 'thumb' => '' ], [ 'id' => 20, 'title' => __( 'Tivoli Avenida Liberdade', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], [ 'id' => 21, 'title' => __( 'Bairro Alto Hotel: Rooftop Bar and Views', 'clusterlane' ), 'role' => 'light', 'thumb' => '' ], ], 'links' => [ [ 1, 2 ], [ 1, 3 ], [ 1, 7 ], [ 1, 14 ], [ 1, 19 ], [ 1, 20 ], [ 2, 3 ], [ 3, 4 ], [ 4, 2 ], [ 5, 3 ], [ 5, 2 ], [ 6, 2 ], [ 7, 8 ], [ 7, 12 ], [ 12, 11 ], [ 8, 7 ], [ 11, 12 ], [ 14, 15 ], [ 16, 14 ], [ 18, 15 ], [ 14, 1 ], [ 19, 20 ], [ 20, 10 ], [ 21, 9 ], [ 13, 10 ], [ 9, 1 ], [ 11, 14 ] ], 'groups' => [ [ 'name' => __( 'Alfama & Castelo hotels', 'clusterlane' ), 'why' => __( 'All in the old town around the castle; readers compare them when choosing where to stay there.', 'clusterlane' ), 'ids' => [ 2, 3, 4, 5, 6 ], 'pillar' => 3 ], [ 'name' => __( 'Riverside hotels', 'clusterlane' ), 'why' => __( 'On the Tagus, from Belém to Cais do Sodré — chosen for the river and the views.', 'clusterlane' ), 'ids' => [ 7, 8, 11, 12 ], 'pillar' => 7 ], [ 'name' => __( 'Avenida & Bairro Alto hotels', 'clusterlane' ), 'why' => __( 'Along Avenida da Liberdade and up in Bairro Alto — the smart, central part of town.', 'clusterlane' ), 'ids' => [ 9, 10, 19, 20, 21 ], 'pillar' => 20, 'pillar_idea' => __( 'Where to Stay Around Avenida da Liberdade', 'clusterlane' ) ], ], 'by' => __( 'area', 'clusterlane' ), 'alternative' => [ 'by' => __( 'price', 'clusterlane' ), 'groups' => [ [ 'name' => __( 'Luxury hotels', 'clusterlane' ), 'why' => __( 'Palaces and five-star hotels, compared by readers with a bigger budget.', 'clusterlane' ), 'ids' => [ 2, 4, 8, 19, 20 ], 'pillar' => 19 ], [ 'name' => __( 'Budget hotels & hostels', 'clusterlane' ), 'why' => __( 'Hostels and simple hotels that are searched for together.', 'clusterlane' ), 'ids' => [ 11, 14, 15, 16, 17, 18 ], 'pillar' => 14, 'pillar_idea' => __( 'Cheap Places to Stay in Lisbon: Hostels and Budget Hotels by Area', 'clusterlane' ) ], ], ], 'note' => __( 'Hotels covers three parts of town that readers choose between. Price would also work as a split — both are offered, never mixed. The hostels fit no area and stay in Hotels.', 'clusterlane' ), 'cluster_pillar' => 1, 'current_pillar' => [ 'id' => 1, 'title' => __( 'Where to Stay in Lisbon: The Complete Hotel Guide', 'clusterlane' ), 'chosen' => true ], 'cached' => true, ], // Report summary — two paragraphs in plain words (0.98.125). 'report_summary' => [ 'paragraphs' => [ __( 'The site\'s structure improved over the last twelve weeks. Average cluster health rose from 60% to 68%, mostly because 41 internal links were added and Hotels and Beaches got a clear main article. Articles with no links pointing to them fell from 21 to 9.', 'clusterlane' ), __( 'The next step is Day Trips, the weakest cluster at 38%: its articles link almost only to one page. Links between the day trips, and a few from Food & Drink (47%) to the neighbourhood guides, would bring both above 50%.', 'clusterlane' ), ], ], 'link_suggest' => [ 'source_title' => __( 'Cascais Beaches Guide', 'clusterlane' ), 'target_title' => __( 'Sintra in a Day', 'clusterlane' ), 'suggestions' => [ [ 'anchor_text' => __( 'a day in Sintra', 'clusterlane' ), 'context' => __( 'If you have more time, combine the coast with [LINK] — the palaces are 30 minutes away by bus 403.', 'clusterlane' ), 'position' => 'end' ], [ 'anchor_text' => __( 'Sintra’s palaces', 'clusterlane' ), 'context' => __( 'From Cabo da Roca the road continues inland to [LINK], an easy add-on to a beach day.', 'clusterlane' ), 'position' => 'middle' ], [ 'anchor_text' => __( 'our Sintra day-trip guide', 'clusterlane' ), 'context' => __( 'Planning the whole coast? Start with [LINK] and end the day on the beach.', 'clusterlane' ), 'position' => 'beginning' ], ], ], ]; } /* ------------------------------------------------------------------ PHP-rendered pieces (pages/sections rendered server-side) ------------------------------------------------------------------ */ /** * The Report page for the demo site (Free): same shape as Report::build(), * scaled to the chosen period. 142 articles in 11 clusters, like the * Site Analysis example; the 12-week figures match the summary example. * @return array */ public static function report( int $days ): array { $tz = wp_timezone(); $today = new \DateTimeImmutable( 'today', $tz ); $start = $today->modify( '-' . $days . ' days' ); // How far back the change goes: 1 = the 12-week change, more for 6 months. $f = static fn( int $ago ): float => $ago <= 84 ? $ago / 84 : 1 + ( $ago - 84 ) / 168; $fp = $f( $days ); // label n now then archetype pillar support light orphan $rows = [ [ __( 'Day Trips', 'clusterlane' ), 8, 38, 29, 'Weak Structure', 1, 1, 4, 2 ], [ __( 'Food & Drink', 'clusterlane' ), 14, 47, 36, 'Fragmented', 1, 3, 8, 2 ], [ __( 'Nightlife', 'clusterlane' ), 7, 55, 44, 'Emerging Cluster', 1, 2, 3, 1 ], [ __( 'Markets', 'clusterlane' ), 9, 61, 52, 'Mixed', 1, 3, 5, 0 ], [ __( 'Hotels', 'clusterlane' ), 21, 64, 55, 'Balanced Mesh', 2, 7, 11, 1 ], [ __( 'Beaches', 'clusterlane' ), 11, 68, 57, 'Balanced Mesh', 1, 4, 5, 1 ], [ __( 'Viewpoints', 'clusterlane' ), 10, 72, 64, 'Balanced Mesh', 1, 4, 5, 0 ], [ __( 'Belém', 'clusterlane' ), 12, 76, 68, 'Developed Cluster', 1, 5, 6, 0 ], [ __( 'Baixa', 'clusterlane' ), 15, 79, 70, 'Balanced Mesh', 1, 6, 7, 1 ], [ __( 'Alfama', 'clusterlane' ), 16, 81, 74, 'Balanced Mesh', 1, 7, 8, 0 ], [ __( 'Museums', 'clusterlane' ), 19, 84, 78, 'Authority Pyramid', 1, 8, 10, 0 ], ]; $now = []; $then = []; $arch = []; foreach ( $rows as $i => [ $l, $n, $h, $h0, $a, $p, $su, $li, $o ] ) { $k = 'demo_' . $i; $now[] = [ 'k' => $k, 'l' => $l, 't' => 'category', 'n' => $n, 'h' => $h, 'a' => $a, 'r' => [ 'pillar' => $p, 'support' => $su, 'light' => $li, 'orphan' => $o ] ]; $then[] = [ 'k' => $k, 'h' => (int) round( $h - ( $h - $h0 ) * $fp ) ]; $arch[ $a ] = ( $arch[ $a ] ?? 0 ) + 1; } $health = static fn( int $ago ): int => (int) round( 68 - 8 * $f( $ago ) + sin( $ago / 4 ) * 0.8 ); $orph = static fn( int $ago ): int => max( 0, (int) round( 9 + 12 * $f( $ago ) + cos( $ago / 5 ) * 0.6 ) ); $series = []; for ( $ago = $days; $ago >= 0; $ago-- ) { $series[] = [ 'snap_date' => $today->modify( '-' . $ago . ' days' )->format( 'Y-m-d' ), 'metrics_version' => 1, 'health' => $ago === 0 ? 68 : $health( $ago ), 'orphans' => $ago === 0 ? 9 : $orph( $ago ) ]; } $g = $days / 84; $links = (int) round( 41 * $g ); $tm = (int) round( $links * 0.6 ); $bins = (int) ceil( $days / 7 ); $shape = [ 2, 3, 4, 2, 5, 3, 4, 3, 5, 4, 3, 3 ]; $w = []; for ( $i = 0; $i < $bins; $i++ ) $w[] = $shape[ $i % count( $shape ) ]; $week = array_map( static fn( $x ) => (int) floor( $x * $links / array_sum( $w ) ), $w ); for ( $i = 0; array_sum( $week ) < $links; $i++ ) $week[ $i % $bins ]++; $s = \Clusterlane\Core\Settings::get_instance(); $th = $s ? (array) $s->get( 'thresholds', [] ) : []; $b_health = $health( $days ); $b_orph = $orph( $days ); return [ 'site_name' => __( 'Lisbon Travel Guide (demo site)', 'clusterlane' ), 'host' => 'lisbon-guide.example', 'days' => $days, 'today' => $today->format( 'Y-m-d' ), 'start' => $start->format( 'Y-m-d' ), 'now' => [ 'site' => [ 'articles' => 142, 'scanned' => 142, 'clusters' => 11, 'health' => 68, 'orphans' => 9, 'avg_inbound' => 6.3, 'roles' => [ 'pillar' => 12, 'support' => 50, 'light' => 71, 'orphan' => 9 ], 'meta' => [ 'good' => 101, 'weak' => 23, 'missing' => 18 ], 'archetypes' => $arch, ], 'clusters' => $now, ], 'base' => [ 'date' => $start->format( 'Y-m-d' ), 'site' => [ 'articles' => 142 - (int) round( 9 * $g ), 'clusters' => $days > 84 ? 10 : 11, 'health' => $b_health, 'orphans' => $b_orph, 'avg_inbound' => round( 6.3 - 1.2 * $fp, 1 ) ], 'clusters' => $then, ], 'partial' => false, 'series' => $series, 'first' => $today->modify( '-200 days' )->format( 'Y-m-d' ), 'activity' => [ 'total' => [ 'link_added' => $links, 'meta_written' => (int) round( 12 * $g ), 'pillar_chosen' => max( 1, (int) round( 3 * min( 1, $g ) ) ), 'draft_created' => (int) round( 2 * $g ) ], 'by_tool' => [ 'link_added' => [ 'text_match' => $tm, 'link_suggest' => $links - $tm ] ], 'undone' => (int) round( 3 * $g ), ], 'links_week' => $week, 'orphan_list' => array_map( static fn( $t ) => [ 'id' => 0, 'title' => $t ], [ __( 'Óbidos in a Day', 'clusterlane' ), __( 'Tasca do Chico', 'clusterlane' ), __( 'LX Factory Brunch', 'clusterlane' ), __( 'Praia da Ursa', 'clusterlane' ), __( 'Miradouro da Graça at Dawn', 'clusterlane' ), ] ), 'computed' => $today->format( 'Y-m-d' ) . ' 03:17:00', 'thresholds' => [ 'green' => (int) ( $th['health_green'] ?? 70 ), 'orange' => (int) ( $th['health_orange'] ?? 50 ) ], 'log_since' => $start->format( 'Y-m-d' ), ]; } public static function labels(): array { return [ 'pro' => __( 'PRO', 'clusterlane' ), 'example' => __( 'Example', 'clusterlane' ), /* translators: 1: feature name, 2: demo site name */ 'bannerText' => __( '%1$s for a demo site (%2$s). Clusterlane Pro does the same for your own clusters.', 'clusterlane' ), 'seePro' => __( 'See Pro →', 'clusterlane' ), 'exampleOnly' => __( 'Example only', 'clusterlane' ), ]; } /** Same banner as clusterlaneProBanner() in JS. */ public static function banner_html( string $feature_label ): string { $l = self::labels(); $url = \Clusterlane\Core\Edition::upgrade_url(); return '
' . '' . esc_html( $l['pro'] ) . '' . '' . esc_html( $l['example'] ) . ' — ' . esc_html( sprintf( $l['bannerText'], $feature_label, self::data()['site'] ) ) . '' . ' ' . esc_html( $l['seePro'] ) . '' . '
'; } /** Compact "part of Pro" card for settings sections that don't exist in Free. */ public static function note_html( string $feature, string $title, string $text ): string { return '
' . '' . esc_html( self::labels()['pro'] ) . '' . '
' . esc_html( $title ) . '' . '

' . esc_html( $text ) . ' ' . esc_html( \Clusterlane\Core\Edition::missing_message( $feature ) ) . '

' . '' . esc_html( self::labels()['seePro'] ) . '' . '
'; } /** AI Link Suggestions page body in Free: what the flow produces, as an example. */ public static function render_link_suggest(): void { $ex = self::data()['link_suggest']; $place = [ 'beginning' => __( 'Near the beginning', 'clusterlane' ), 'middle' => __( 'In the middle', 'clusterlane' ), 'end' => __( 'Near the end', 'clusterlane' ) ]; ?>

' . esc_html( $v['anchor_text'] ) . '', esc_html( $v['context'] ) ); ?>

[ 'class' => [] ] ] ); ?>