bingo: operational-only view of the card

The card mixes facilities in service with construction projects (Sber
Balakovo at #1, MOS7, MOS11, Yandex Vladimir-2), which is fine for a
bingo but not when you want to compare what actually runs today. Add a
toggle that builds the card from operational rows only, in the same
ranking, with bingo lines recomputed for that card. The filtered view is
noindex with canonical to the plain locale URL, so it does not compete
with the main page in search.

Assisted-by: Claude:opus-5
This commit is contained in:
Sergei Poljanski 2026-10-10 06:58:30 +04:00
commit 9e885dd8c1
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
6 changed files with 74 additions and 14 deletions

View file

@ -2,11 +2,18 @@
<main>
<header>
<h1><%= t(:lead) %> <span><%= t(:accent) %></span></h1>
<nav class="langs">
<% Bingo::LOCALES.each do |l| %>
<a href="/<%= l %>" hreflang="<%= l %>" lang="<%= l %>" <%== 'aria-current="page"' if l == @locale %>><%= l %></a>
<% end %>
</nav>
<div class="switches">
<nav class="toggle" aria-label="<%= t(:view_label) %>">
<% Bingo::VIEWS.each do |v| %>
<a href="<%= card_path(@locale, v) %>" rel="nofollow" <%== 'aria-current="page"' if v == @view %>><%= t(:"view_#{v}") %></a>
<% end %>
</nav>
<nav class="langs">
<% Bingo::LOCALES.each do |l| %>
<a href="<%= card_path(l) %>" hreflang="<%= l %>" lang="<%= l %>" <%== 'aria-current="page"' if l == @locale %>><%= l %></a>
<% end %>
</nav>
</div>
</header>
<p class="lead"><%= t(:desc) %></p>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= t(:seo_title) %></title>
<meta name="description" content="<%= desc %>">
<meta name="robots" content="index, follow, max-image-preview:large">
<meta name="robots" content="<%= @board.operational_only? ? 'noindex, follow' : 'index, follow, max-image-preview:large' %>">
<meta name="theme-color" content="#121212">
<link rel="canonical" href="<%= base %>/<%= @locale %>">
<% Bingo::LOCALES.each do |l| %>
@ -58,6 +58,11 @@
header { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 16px; margin-bottom: 16px; }
h1 { margin: 0; font-size: clamp(28px, 6vw, 44px); letter-spacing: -.02em; }
h1 span { color: var(--hit); }
.switches { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.toggle { display: flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.toggle a { padding: 2px 10px; font-size: 13px; text-decoration: none; }
.toggle a + a { border-left: 1px solid var(--line); }
.toggle a[aria-current] { background: var(--hit); color: var(--bg); }
.langs { display: flex; gap: 4px; }
.langs a { padding: 2px 8px; border: 1px solid var(--line); border-radius: 6px; text-decoration: none; font-size: 13px; text-transform: uppercase; }
.langs a[aria-current] { background: var(--ink); color: var(--bg); border-color: var(--ink); }