{
  "sha": "8fb37161b09fbe42fa5b76e725593b68173cb344",
  "message": "LIVE ticker: each flash links to its story (no underline)\n\nTicker items stay structured through to base.html: every marquee entry\nis an <a> to its article (external agg items open in a new tab), with\nthe clickable affordance carried by an accent ↗ mark and hover color -\nnever an underline (owner request). The track pauses on hover so the\nmoving items can actually be clicked; placeholder items without a URL\nrender as plain text.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
  "author": {
    "login": "avwire",
    "name": "avwire",
    "email": ""
  },
  "committer": {
    "login": "avwire",
    "name": "avwire",
    "email": ""
  },
  "created_at": "2026-07-26T23:47:40Z",
  "files": [
    {
      "filename": "pipeline/build.py",
      "previous_filename": "",
      "status": "modified",
      "additions": 7,
      "deletions": 3,
      "changes": 10
    },
    {
      "filename": "static/site.css",
      "previous_filename": "",
      "status": "modified",
      "additions": 15,
      "deletions": 0,
      "changes": 15
    },
    {
      "filename": "templates/base.html",
      "previous_filename": "",
      "status": "modified",
      "additions": 13,
      "deletions": 3,
      "changes": 16
    }
  ],
  "diff": "@@ -575,7 +575,7 @@ def _pre(x):\\n     },\\n }\\n \\n-TICKER_SEP = \"　▪　\"  # 「　▪　」 per the design\\n+# (the design's 「　▪　」 ticker separator now lives in base.html/.ticker-sep)\\n \\n # Footer source-tag order, exactly as in the design footer.\\n FOOTER_ORDER = [\"faa\", \"icao\", \"iata\", \"ntsb\", \"easa\", \"eurocontrol\",\\n@@ -1341,7 +1341,7 @@ def base_ctx(lang, page, sub, *, title, description, ticker, build, hreflang=Tru\\n         \"home_url\": page_url(lang, \"\"),\\n         \"nav\": [{\"label\": t[\"nav\"][i], \"url\": page_url(lang, s), \"active\": page == p}\\n                 for i, (p, s) in enumerate(nav_defs)],\\n-        \"ticker_line\": ticker, \"build\": build,\\n+        \"ticker_items\": ticker or [], \"build\": build,\\n         \"footer_sources\": FOOTER_SOURCES,\\n     }\\n \\n@@ -1422,7 +1422,11 @@ def main() -> int:\\n             fl = flash_view(flashes, lang)\\n             hero = None\\n             feed = []\\n-        ticker = TICKER_SEP.join(f\"{f['time']}  {f['text']}\" for f in fl)\\n+        # ticker items stay structured so the marquee can link each flash\\n+        # to its story (owner request: clickable, no underline)\\n+        ticker = [{\"time\": f.get(\"time\"), \"text\": f.get(\"text\"),\\n+                   \"url\": f.get(\"url\"), \"hot\": bool(f.get(\"hot\")),\\n+                   \"external\": bool(f.get(\"external\"))} for f in fl]\\n         sv = stats_views(stats_raw, lang)\\n \\n         bviews = [brief_view(b, lang, t, published_ids) for b in briefings]\\n@@ -30,6 +30,21 @@ a:hover { color: var(--color-accent-600); }\\n }\\n \\n @keyframes avwire-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }\\n+\\n+/* — LIVE ticker: each flash links to its story. Affordance is the accent\\n+     ↗ mark + hover color (owner: clickable-looking but NO underline);\\n+     the track pauses on hover so items can actually be clicked. — */\\n+.marquee-track:hover { animation-play-state: paused; }\\n+.ticker-item { color: inherit; text-decoration: none; }\\n+a.ticker-item:hover { color: var(--color-accent); }\\n+a.ticker-item:hover .ticker-time { color: var(--color-accent); }\\n+.ticker-time { font-weight: 800; }\\n+.ticker-time.hot { color: var(--color-accent); }\\n+.ticker-go {\\n+  font-size: 10px; font-weight: 800; color: var(--color-accent);\\n+  margin-left: 4px; vertical-align: 2px;\\n+}\\n+.ticker-sep { padding: 0 18px; color: color-mix(in srgb, var(--color-text) 35%, transparent); }\\n @keyframes avwire-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }\\n \\n /* — chrome — */\\n@@ -57,13 +57,23 @@\\n       </nav>\\n     </div>\\n   </div>\\n-  {% if ticker_line %}\\n+  {% if ticker_items %}\\n   <div style=\"border-bottom:2px solid var(--color-divider);display:flex;align-items:stretch;\">\\n     <div style=\"background:var(--color-accent);color:var(--color-bg);font-weight:800;font-size:12px;letter-spacing:.14em;display:flex;align-items:center;padding:7px 16px;flex-shrink:0;text-transform:uppercase;\">{{ t.live }}</div>\\n     <div style=\"overflow:hidden;flex:1;display:flex;align-items:center;\">\\n       <div class=\"marquee-track\">\\n-        <span style=\"padding-right:70px;font-size:13px;\">{{ ticker_line }}</span>\\n-        <span style=\"padding-right:70px;font-size:13px;\">{{ ticker_line }}</span>\\n+        {% for pass in range(2) %}\\n+        <span style=\"padding-right:70px;font-size:13px;white-space:nowrap;\">\\n+          {%- for it in ticker_items -%}\\n+          {%- if it.url -%}\\n+          <a class=\"ticker-item\" href=\"{{ it.url }}\"{% if it.external %} target=\"_blank\" rel=\"noopener\"{% endif %}><b class=\"ticker-time{% if it.hot %} hot{% endif %}\">{{ it.time }}</b>&#8195;{{ it.text }}<span class=\"ticker-go\">↗</span></a>\\n+          {%- else -%}\\n+          <span class=\"ticker-item\"><b class=\"ticker-time{% if it.hot %} hot{% endif %}\">{{ it.time }}</b>&#8195;{{ it.text }}</span>\\n+          {%- endif -%}\\n+          {%- if not loop.last %}<span class=\"ticker-sep\">▪</span>{% endif -%}\\n+          {%- endfor -%}\\n+        </span>\\n+        {% endfor %}\\n       </div>\\n     </div>\\n   </div>"
}
