// Halifax Now v3 — Home (v4 broadsheet layout + strips) + Tonight page + Submit flow // ─── stub events (v4-style, enough for all home sections) ─────────────────── const V4_EVENTS = [ { id:'e1', title:'Halifax Jazz Festival — Opening Night', venue:'The Carleton', hood:'Downtown', category:'music', date:'2026-04-24', time:'20:00', price:'free', priceLabel:'Free', hue:220, seed:'e1jazz', critic:true, blurb:'The Carleton kicks off ten days of jazz with the HFX Quartet in the room, a fire in the hearth, and the kind of crowd that actually listens. Arrive by 7:30.', short:'The Carleton kicks off ten days of jazz.' }, { id:'e2', title:'AGNS First Fridays — Opening Reception', venue:'Art Gallery of Nova Scotia', hood:'Downtown', category:'arts', date:'2026-04-24', time:'18:00', price:'free', priceLabel:'Free', hue:280, seed:'e2agns', critic:true, blurb:"The gallery stays open late, there's free coffee, and nobody checks if you're talking about the art.", short:'Gallery late, free coffee, nobody checks.' }, { id:'e3', title:'Seahorse — Hip Hop Night', venue:'The Seahorse Tavern', hood:'Downtown', category:'nightlife', date:'2026-04-24', time:'22:00', price:'paid', priceLabel:'$10', hue:300, seed:'e3seahorse', blurb:'Local DJs, no dress code, sweat on the ceiling by midnight.', short:'Local DJs. Sweat on the ceiling.' }, { id:'e4', title:"Bearly's Blues Jam", venue:"Bearly's House of Blues", hood:'Downtown', category:'music', date:'2026-04-24', time:'21:00', price:'free', priceLabel:'Free', hue:210, seed:'e4bearlys', blurb:"The jam that's been running since before half the musicians in it were born. Bring your harp.", short:'Running forever. Bring your harp.' }, { id:'e5', title:"Alderney Landing Farmer's Market", venue:'Alderney Landing', hood:'Dartmouth', category:'market', date:'2026-04-25', time:'08:00', price:'free', priceLabel:'Free', hue:60, seed:'e5market', blurb:'The ferry ride over is the first part of the morning. The oyster guy is the second.', short:'Ferry + oysters + bread.' }, { id:'e6', title:'Halifax Mooseheads vs. Charlottetown', venue:'Scotiabank Centre', hood:'Downtown', category:'sports', date:'2026-04-25', time:'19:00', price:'paid', priceLabel:'$22+', hue:0, seed:'e6hockey', blurb:'Final home stretch. Wear red, scream when appropriate.', short:'Wear red. Scream when appropriate.' }, { id:'e7', title:"Seaport Market — Spring Makers' Fair", venue:'Halifax Seaport Market', hood:'Downtown', category:'market', date:'2026-04-26', time:'10:00', price:'free', priceLabel:'Free', hue:90, seed:'e7makers', blurb:'60+ makers, a hot-chocolate stand run by a twelve-year-old that you should support.', short:'60+ makers. Support the kid with cocoa.' }, { id:'e8', title:'Neptune Theatre — Spring Production Opening', venue:'Neptune Theatre', hood:'Downtown', category:'theatre', date:'2026-04-27', time:'19:30', price:'paid', priceLabel:'$25–$65', hue:340, seed:'e8neptune', critic:true, blurb:'A new piece from a local playwright, directed by someone who knows what she\'s doing. Tickets moving fast.', short:'New local play. Tickets moving.' }, { id:'e9', title:'Good Robot — Trivia Night', venue:'Good Robot Brewing', hood:'North End', category:'food', date:'2026-04-29', time:'19:00', price:'free', priceLabel:'Free', hue:170, seed:'e9trivia', blurb:'Four rounds, one round of pizza, team of five max. The host is mean in a fun way.', short:'Four rounds. Host is mean.' }, { id:'e10', title:'Carbon Arc: Paris, Texas', venue:'Carbon Arc Cinema', hood:'Downtown', category:'film', date:'2026-04-30', time:'20:00', price:'paid', priceLabel:'$12', hue:20, seed:'e10film', critic:true, blurb:'Wim Wenders, 35mm, and the Harry Dean Stanton of it all.', short:'Wenders. 35mm. Harry Dean Stanton.' }, ]; const TONIGHT = V4_EVENTS.filter(e => e.date === '2026-04-24'); const WEEKEND = V4_EVENTS.filter(e => ['2026-04-25','2026-04-26'].includes(e.date)); const PICKS = V4_EVENTS.filter(e => e.critic); const WEEK = V4_EVENTS.slice(0, 8); const MOODS = [ { id:'chill', label:'Chill', emoji:'🌙' }, { id:'rowdy', label:'Rowdy', emoji:'🔥' }, { id:'date', label:'Date Night', emoji:'💋' }, { id:'kids', label:'Kid-friendly', emoji:'🧃' }, { id:'solo', label:'Solo OK', emoji:'👤' }, { id:'free', label:'Broke-friendly', emoji:'🪙' }, ]; const HOODS = ['North End','Downtown','South End','West End','Dartmouth','Spring Garden']; // ─── v4-style CSS (scoped to .v4h wrapper) ────────────────────────────────── const V4H_CSS = ` .v4h { --paper:#f4efe6;--ink:#0f0f0f;--red:#c23a1e;--acid:#e8ff00;--soft:#e8d8cc;--muted:#6b6459; background:var(--paper); color:var(--ink); font-family:'Source Serif 4',Georgia,serif; } .v4h *{box-sizing:border-box;} .v4h::before{content:'';position:fixed;inset:0;pointer-events:none;z-index:1;background:radial-gradient(circle at 1px 1px,rgba(0,0,0,0.045) 1px,transparent 1.2px) 0 0/5px 5px;} .v4h-hero{padding:40px 32px 36px;border-bottom:3px double var(--ink);display:grid;grid-template-columns:1.4fr 1fr;gap:48px;align-items:stretch;position:relative;z-index:2;} .v4h-stamp{display:inline-block;background:var(--acid);color:var(--ink);padding:4px 12px;font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:700;letter-spacing:0.16em;text-transform:uppercase;transform:rotate(-1.5deg);margin-bottom:16px;box-shadow:3px 3px 0 var(--ink);} .v4h-h1{font-family:'Anton',sans-serif;font-weight:400;font-size:clamp(80px,11vw,160px);line-height:0.84;letter-spacing:-0.01em;text-transform:uppercase;margin:0 0 18px;} .v4h-h1 em{font-style:normal;color:var(--red);} .v4h-h1 .lean{display:inline-block;transform:rotate(-3deg);} .v4h-h1 .knock{color:transparent;-webkit-text-stroke:3px var(--ink);} .v4h-lede{font-family:'Source Serif 4',serif;font-size:18px;line-height:1.5;max-width:520px;margin-bottom:22px;} .v4h-lede::first-letter{font-family:'Playfair Display',serif;font-size:52px;font-weight:900;font-style:italic;color:var(--red);float:left;line-height:0.85;margin:4px 10px 0 0;} .v4h-chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px;} .v4h-chip{padding:8px 13px;border:2.5px solid var(--ink);background:var(--paper);font-family:'Space Grotesk',sans-serif;font-size:12px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;cursor:pointer;box-shadow:3px 3px 0 var(--ink);transition:transform 0.08s,box-shadow 0.08s;color:var(--ink);} .v4h-chip:hover{transform:translate(-1px,-1px);box-shadow:5px 5px 0 var(--ink);} .v4h-chip.on{background:var(--red);color:var(--paper);transform:translate(3px,3px);box-shadow:0 0 0 var(--ink);} .v4h-chip.surprise{background:var(--acid);} .v4h-search{display:flex;border:2.5px solid var(--ink);background:#fff;max-width:500px;box-shadow:3px 3px 0 var(--ink);} .v4h-search input{flex:1;padding:11px 16px;border:0;outline:0;font-family:'Source Serif 4',serif;font-size:16px;font-style:italic;background:transparent;} .v4h-search button{padding:11px 18px;background:var(--ink);color:var(--paper);border:0;font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;cursor:pointer;} .v4h-picks{border:2.5px solid var(--ink);background:var(--paper);box-shadow:6px 6px 0 var(--ink);display:flex;flex-direction:column;} .v4h-picks-hd{background:var(--ink);color:var(--paper);padding:10px 16px;display:flex;justify-content:space-between;align-items:baseline;} .v4h-picks-hd .t{font-family:'Playfair Display',serif;font-weight:900;font-style:italic;font-size:22px;} .v4h-picks-hd .t em{color:var(--acid);} .v4h-picks-hd .s{font-family:'Space Grotesk',sans-serif;font-size:10px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--acid);} .v4h-pick{padding:12px 16px;border-bottom:1px solid var(--ink);cursor:pointer;display:grid;grid-template-columns:28px 1fr auto;gap:12px;align-items:center;background:var(--paper);transition:background 0.1s;} .v4h-pick:last-child{border-bottom:0;} .v4h-pick:hover{background:var(--acid);} .v4h-pick .star{font-size:20px;color:var(--red);font-weight:900;line-height:1;} .v4h-pick .ca{font-family:'Space Grotesk',sans-serif;font-size:9px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--red);margin-bottom:2px;} .v4h-pick .ti{font-family:'Playfair Display',serif;font-weight:700;font-size:17px;line-height:1.1;margin-bottom:2px;} .v4h-pick .me{font-family:'Space Grotesk',sans-serif;font-size:11px;color:var(--muted);} .v4h-pick .pr{font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:700;color:var(--red);} .v4h-mood{display:flex;justify-content:center;align-items:center;gap:10px;flex-wrap:wrap;padding:14px 32px;border-bottom:2px solid var(--ink);background:var(--ink);color:var(--paper);position:relative;z-index:2;} .v4h-mood-l{font-family:'Playfair Display',serif;font-weight:900;font-style:italic;font-size:22px;color:var(--acid);flex-shrink:0;} .v4h-mpill{background:transparent;color:var(--paper);border:1.5px solid rgba(255,255,255,0.4);padding:5px 11px;font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;cursor:pointer;display:flex;align-items:center;gap:5px;border-radius:99px;} .v4h-mpill:hover{background:var(--acid);color:var(--ink);border-color:var(--acid);} .v4h-mpill.on{background:var(--red);color:var(--paper);border-color:var(--red);} .v4h-sec{padding:36px 32px;border-bottom:1px solid var(--ink);position:relative;z-index:2;} .v4h-sec-hd{display:flex;align-items:baseline;justify-content:space-between;border-bottom:2px solid var(--ink);padding-bottom:10px;margin-bottom:24px;position:relative;} .v4h-sec-hd .h{font-family:'Playfair Display',serif;font-weight:900;font-style:italic;font-size:48px;line-height:0.95;letter-spacing:-0.02em;} .v4h-sec-hd .h em{color:var(--red);font-style:italic;} .v4h-sec-hd .cnt{position:absolute;top:-8px;left:-6px;background:var(--acid);color:var(--ink);font-family:'Space Grotesk',sans-serif;font-size:10px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;padding:3px 9px;transform:rotate(-4deg);box-shadow:2px 2px 0 var(--ink);} .v4h-sec-hd .lk{font-family:'Space Grotesk',sans-serif;font-size:12px;letter-spacing:0.14em;text-transform:uppercase;font-weight:700;color:var(--ink);cursor:pointer;padding:6px 12px;border:2px solid var(--ink);box-shadow:3px 3px 0 var(--ink);background:var(--paper);} .v4h-sec-hd .lk:hover{background:var(--red);color:var(--paper);} .v4h-page{display:grid;grid-template-columns:1fr 320px;gap:48px;} .v4h-feat{display:grid;grid-template-columns:1.1fr 1fr;gap:24px;margin-bottom:28px;padding-bottom:28px;border-bottom:1px solid var(--ink);} .v4h-feat-img{aspect-ratio:4/3;border:2.5px solid var(--ink);box-shadow:8px 8px 0 var(--ink);} .v4h-feat-cat{display:inline-block;background:var(--red);color:var(--paper);padding:4px 10px;font-family:'Space Grotesk',sans-serif;font-size:10px;font-weight:700;letter-spacing:0.16em;text-transform:uppercase;margin-bottom:12px;} .v4h-feat-cat.pick{background:var(--acid);color:var(--ink);} .v4h-feat-title{font-family:'Playfair Display',serif;font-weight:900;font-size:40px;line-height:0.95;letter-spacing:-0.02em;margin:0 0 14px;cursor:pointer;} .v4h-feat-blurb{font-size:16px;line-height:1.5;margin-bottom:16px;} .v4h-feat-meta{font-family:'Space Grotesk',sans-serif;font-size:12px;padding-top:12px;border-top:1px solid var(--ink);display:flex;justify-content:space-between;align-items:center;} .v4h-feat-meta .pr{background:var(--ink);color:var(--paper);padding:4px 10px;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;} .v4h-feat-meta .pr.free{background:var(--red);} .v4h-list{display:grid;grid-template-columns:1fr 1fr;gap:0 36px;} .v4h-item{padding:15px 0;border-bottom:1px solid var(--ink);display:grid;grid-template-columns:54px 1fr auto;gap:12px;align-items:start;cursor:pointer;} .v4h-item:hover .v4h-item-t{color:var(--red);} .v4h-idate{background:var(--ink);color:var(--paper);text-align:center;padding:6px 4px;border:2px solid var(--ink);box-shadow:2px 2px 0 var(--ink);} .v4h-idate .n{font-family:'Playfair Display',serif;font-weight:900;font-size:24px;} .v4h-idate .m{font-family:'Space Grotesk',sans-serif;font-size:9px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;margin-top:3px;color:var(--acid);} .v4h-item-cat{font-family:'Space Grotesk',sans-serif;font-size:10px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--red);margin-bottom:3px;} .v4h-item-cat .pick{color:var(--ink);background:var(--acid);padding:1px 5px;margin-right:4px;font-size:9px;} .v4h-item-t{font-family:'Playfair Display',serif;font-weight:700;font-size:19px;line-height:1.1;margin-bottom:3px;transition:color 0.12s;} .v4h-item-b{font-size:13px;line-height:1.35;margin-bottom:4px;} .v4h-item-m{font-family:'Space Grotesk',sans-serif;font-size:11px;color:var(--muted);} .v4h-item-pr{font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:700;color:var(--red);padding:3px 7px;border:1.5px solid var(--red);white-space:nowrap;} .v4h-wall{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;} .v4h-card{background:var(--paper);border:2.5px solid var(--ink);box-shadow:5px 5px 0 var(--ink);cursor:pointer;display:flex;flex-direction:column;transition:transform 0.1s,box-shadow 0.1s;overflow:hidden;} .v4h-card:hover{transform:translate(-2px,-2px);box-shadow:7px 7px 0 var(--ink);} .v4h-card.wide{grid-column:span 2;} .v4h-card.red-bg{background:var(--red);color:var(--paper);} .v4h-card.acid-bg{background:var(--acid);} .v4h-card.ink-bg{background:var(--ink);color:var(--paper);} .v4h-card-img{aspect-ratio:4/3;border-bottom:2.5px solid var(--ink);} .v4h-card-body{padding:12px 14px 14px;flex:1;display:flex;flex-direction:column;} .v4h-card-when{font-family:'Space Grotesk',sans-serif;font-size:10px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;margin-bottom:7px;color:var(--red);} .v4h-card.red-bg .v4h-card-when,.v4h-card.ink-bg .v4h-card-when{color:var(--acid);} .v4h-card.acid-bg .v4h-card-when{color:var(--ink);} .v4h-card-title{font-family:'Playfair Display',serif;font-weight:900;font-size:20px;line-height:1.02;letter-spacing:-0.01em;margin-bottom:5px;} .v4h-card.wide .v4h-card-title{font-size:30px;} .v4h-card-blurb{font-size:13px;line-height:1.4;margin-bottom:8px;font-family:'Source Serif 4',serif;} .v4h-card-meta{margin-top:auto;padding-top:9px;border-top:1.5px dashed currentColor;font-family:'Space Grotesk',sans-serif;font-size:11px;display:flex;justify-content:space-between;font-weight:500;} .v4h-card-stamp{position:absolute;top:10px;right:-34px;background:var(--acid);color:var(--ink);padding:3px 36px;transform:rotate(35deg);font-family:'Space Grotesk',sans-serif;font-size:10px;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;box-shadow:0 2px 0 var(--ink);z-index:2;} .v4h-side-box{border:2.5px solid var(--ink);padding:16px;margin-bottom:20px;background:var(--paper);box-shadow:5px 5px 0 var(--ink);} .v4h-side-box.dark{background:var(--ink);color:var(--paper);} .v4h-side-box.red-b{background:var(--red);color:var(--paper);} .v4h-side-box.acid-b{background:var(--acid);} .v4h-side-hd{font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:700;letter-spacing:0.18em;text-transform:uppercase;margin-bottom:10px;padding-bottom:8px;border-bottom:2px solid currentColor;} .v4h-big-n{font-family:'Playfair Display',serif;font-weight:900;font-style:italic;font-size:52px;line-height:0.9;margin-bottom:4px;color:var(--acid);} .v4h-surprise{width:100%;padding:14px;background:var(--acid);color:var(--ink);border:2.5px solid var(--ink);font-family:'Playfair Display',serif;font-weight:900;font-style:italic;font-size:22px;cursor:pointer;box-shadow:5px 5px 0 var(--ink);margin-bottom:20px;} .v4h-surprise:hover{background:var(--red);color:var(--paper);} .v4h-hoods{display:flex;flex-wrap:wrap;gap:5px;} .v4h-hood{font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;padding:4px 9px;border:1.5px solid var(--ink);background:var(--paper);color:var(--ink);cursor:pointer;} .v4h-side-box.dark .v4h-hood{background:transparent;color:var(--paper);border-color:rgba(255,255,255,0.3);} .v4h-hood:hover,.v4h-hood.on{background:var(--acid);color:var(--ink);border-color:var(--acid);} `; // ─── shared tiny helpers ───────────────────────────────────────────────────── function fmtCat(c) { return ({music:'Live Music',comedy:'Comedy',arts:'Arts & Culture',food:'Food & Drink',community:'Community',outdoors:'Outdoors',film:'Film',theatre:'Theatre',sports:'Sports',family:'Family',nightlife:'Nightlife',market:'Markets'})[c] || c; } function fmtWhen(date) { const d = new Date(date + 'T12:00:00'); const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; if (date === '2026-04-24') return 'Tonight'; if (date === '2026-04-25') return 'Tomorrow'; return `${days[d.getDay()]} ${months[d.getMonth()]} ${d.getDate()}`; } // Reuse V3Img from v3-shared for images function WallCard({ ev, wide, colorClass }) { return (
{ev.critic &&
★ Pick
}
{fmtWhen(ev.date)} · {D.fmtTime(ev.time)}
{ev.title}
{ev.short &&
{ev.short}
}
{ev.venue}{ev.priceLabel}
); } function ListItem({ ev }) { const d = new Date(ev.date + 'T12:00:00'); const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; return (
{d.getDate()}
{months[d.getMonth()]}
{ev.critic && ★ Pick}{fmtCat(ev.category)}
{ev.title}
{ev.short &&
{ev.short}
}
{D.fmtTime(ev.time)} · {ev.venue} · {ev.hood}
{ev.priceLabel}
); } // Section strip (for new content types on home) function HomeStrip({ icon, title, italic, badge, badgeColor, items, renderCard, setPage, target }) { return (
{icon}

{title}{italic && {italic}}

{items.slice(0,4).map(item =>
{renderCard(item)}
)}
); } // Small cards for strips function StripRunCard({ club }) { return (
{club.day.slice(0,3).toUpperCase()}
{D.fmtTime(club.time)}
{club.distance} · {club.hood}
{club.name}
☕ {club.coffee}
); } function StripHHCard({ hh, active }) { return (
{active &&
● open · closes {D.fmtTime(hh.ends)}
}
{hh.venue}
{hh.deal}
{hh.hood} · {D.fmtTime(hh.starts)}–{D.fmtTime(hh.ends)}
); } function StripPatioCard({ patio }) { return (
{patio.hood}
{patio.venue}
{patio.vibe}{patio.view?' · 🌅':''}
); } // ─── HOME V3: v4 broadsheet layout + strips ────────────────────────────────── function HomeV3({ setPage, flagOn, searchQuery, setSearchQuery }) { const [quick, setQuick] = useState(null); const [moods, setMoods] = useState([]); const [hoods, setHoods] = useState([]); const toggleMood = m => setMoods(ms => ms.includes(m) ? ms.filter(x=>x!==m) : [...ms,m]); const toggleHood = h => setHoods(hs => hs.includes(h) ? hs.filter(x=>x!==h) : [...hs,h]); const activeHH = D.HAPPY_HOURS.filter(h => { const s = D.toMins(h.starts), e = D.toMins(h.ends); return h.days.includes(D.TODAY_DOW) && s <= D.NOW_MINS && e > D.NOW_MINS; }); return (
{/* HERO */}
★ Issue 117 · The Week of April 24

Do stuff.
Have fun.

The best of Halifax this week, in plain English. Forty-two things worth doing across the peninsula and Dartmouth — opinionated where it counts, exhaustive where it doesn't.

{[['★ Tonight','tonight'],['Tomorrow','tomorrow'],['The Weekend','weekend'],['Free / $0','free']].map(([l,k]) => ( ))}
{ e.preventDefault(); setPage('browse'); }}> setSearchQuery(e.target.value)} placeholder="Search venues, artists, things to do…" />
Critics' Picks ★ This Week
{PICKS.map(e => (
{fmtCat(e.category)}
{e.title}
{fmtWhen(e.date)} · {D.fmtTime(e.time)} · {e.venue}
{e.priceLabel}
))}
{/* MOOD BAND */}
Mood: {MOODS.map(m => ( ))}
{/* TONIGHT SECTION */}
Tonight in Halifax{TONIGHT.length} things
{TONIGHT[0] && (
{TONIGHT[0].critic?'★ Critic\'s Pick · ':''}{fmtCat(TONIGHT[0].category)}

{TONIGHT[0].title}

{TONIGHT[0].blurb}

{D.fmtTime(TONIGHT[0].time)} · {TONIGHT[0].venue} · {TONIGHT[0].hood} {TONIGHT[0].priceLabel}
)}
{TONIGHT.slice(1).map(e => )}
{/* THE WALL */}
The Wall+ {WEEK.length} THIS WEEK
{WEEK.map((e, i) => )}
{/* WEEKEND */}
The Weekend{WEEKEND.length} events
{WEEKEND.map(e => )}
{/* NEW SECTION STRIPS — flag-gated */} {flagOn && (
New on Halifax Now Run Clubs · Happy Hours · Patios
} setPage={setPage} target="runclubs"/> !activeHH.includes(h))]} renderCard={h => } setPage={setPage} target="happyhours"/> } setPage={setPage} target="patios"/>
)}
); } // ─── TONIGHT PAGE ───────────────────────────────────────────────────────────── // v4 editorial layout + right-now sidebar fusing HH, runs, patios function TonightPage({ setPage }) { const activeHH = D.HAPPY_HOURS.filter(h => { const s = D.toMins(h.starts), e = D.toMins(h.ends); return h.days.includes(D.TODAY_DOW) && s <= D.NOW_MINS && e > D.NOW_MINS; }); const closingSoon = activeHH.filter(h => D.toMins(h.ends) - D.NOW_MINS < 45); const tonightRuns = D.RUN_CLUBS.filter(r => r.day === 'Thursday'); const patioSuggestions = D.PATIOS.filter(p => p.vibe === 'Date night' || p.view).slice(0,3); return (
{/* dark header */}
Thu Apr 24, 2026 · 4:45pm

Tonight

{[ [activeHH.length, 'HH active', T.acid], [TONIGHT.length, 'Events', T.paper], ['14°', 'Patio weather', 'oklch(72% 0.22 90)'], ].map(([val, label, col]) => (
{val}
{label}
))}
{/* main layout: events left, right-now sidebar right */}
{/* LEFT: tonight's events */}
Tonight in Halifax{TONIGHT.length} things
{/* featured */} {TONIGHT[0] && (
{TONIGHT[0].critic?'★ Critic\'s Pick · ':''}{fmtCat(TONIGHT[0].category)}

{TONIGHT[0].title}

{TONIGHT[0].blurb}

{D.fmtTime(TONIGHT[0].time)} · {TONIGHT[0].venue} {TONIGHT[0].priceLabel}
)}
{TONIGHT.slice(1).map(e => )}
{/* RIGHT: fused right-now sidebar */}
); } // ─── SUBMIT FLOW ────────────────────────────────────────────────────────────── function SubmitFlow() { const [type, setType] = useState(null); const [sent, setSent] = useState(false); const TYPES = [ { id:'event', label:'Event', icon:'📅', desc:'One-off or recurring — concert, show, market, talk' }, { id:'runclub', label:'Run Club', icon:'🏃', desc:'Weekly group run looking for new members' }, { id:'happyhour', label:'Happy Hour', icon:'🍺', desc:'Bar deal — price, hours, what\'s included' }, { id:'patio', label:'Patio', icon:'☀️', desc:'Outdoor seating that deserves to be in the guide' }, ]; const FIELDS = { event: [['Venue name','text'],['Event title','text'],['Date','date'],['Time','time'],['Price','text'],['Neighbourhood','text'],['Short description','textarea']], runclub: [['Club name','text'],['Meeting day','text'],['Meeting time','time'],['Distance','text'],['Pace','text'],['Meet-up location','text'],['Coffee spot after','text'],['Short description','textarea']], happyhour: [['Venue name','text'],['Address','text'],['The deal','text'],['Starts','time'],['Ends','time'],['Days (e.g. Mon–Fri)','text'],['Notes','textarea']], patio: [['Venue name','text'],['Address','text'],['Neighbourhood','text'],['Patio size','text'],['Dog-friendly? (y/n)','text'],['View? (y/n)','text'],['Covered / heated?','text'],['Notes','textarea']], }; if (sent) return (
Got
it.

Your submission is in. We review everything before it goes live — usually within 48 hours.

); return (

Know about a great run club, a generous happy hour, or a patio that deserves to be in the guide? Add it here. Everything is reviewed before going live.

What are you submitting?
{TYPES.map(t => ( ))}
{type && (
{TYPES.find(t=>t.id===type)?.icon} {TYPES.find(t=>t.id===type)?.label} details
{FIELDS[type].map(([label,kind]) => (
{kind==='textarea' ?