Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -392,22 +392,35 @@ body::after{content:'';position:fixed;inset:0;pointer-events:none;z-index:9999;o
[data-lowpower] [data-lp-skip]{display:none}

/* ---------- home gallery tunnel (HomeView.tsx) ---------- */
/* The visible height, so the counter and "View full gallery" aren't tucked behind a phone's browser toolbar. */
.t-stage{height:100vh;height:100dvh}
.t-scrim{display:none}
/* Phones: photos are capped by height too (--ar is the photo's width/height; --tx/--ty its offset from the centre), so a tall one can't outgrow the room
between the heading and the counter; the tunnel's centre and vanishing point drop into that room; and dark fades sit
behind the heading and the counter, so a photo passing beneath never covers the text. */
/* Narrow phones: a smaller counter and button keep the bottom bar on one line. */
@media (max-width:400px){
.t-bar{gap:10px !important}
.t-bar [data-t-count]{font-size:32px !important}
.t-bar a{padding:12px 16px !important;font-size:12px !important}
}
/* Phones: photos are capped by height too (--ar is the photo's width/height; --tx/--ty its offset from the centre), so a tall
one can't outgrow the room between the nav and the counter. The heading fades out as the tunnel starts, so the photos
fly through the middle of the screen; dark fades sit behind the heading and the counter, so a photo passing beneath
never covers the text. */
@media (max-width:719px){
.t-space{perspective-origin:50% 56% !important}
.t-frame{width:min(54vw,calc(28vh * var(--ar))) !important;top:calc(56% + var(--ty) * .5) !important}
.t-space{perspective-origin:50% 50% !important}
.t-frame{width:min(70vw,calc(36vh * var(--ar))) !important;top:calc(50% + var(--ty) * .5) !important}
.t-scrim{display:block;position:absolute;left:0;right:0;pointer-events:none}
.t-scrim-top{top:0;height:calc(4vh + 290px);background:linear-gradient(180deg,#141312 0,rgba(20,19,18,.94) 62%,rgba(20,19,18,0))}
.t-scrim-bot{bottom:0;height:200px;background:linear-gradient(0deg,#141312 0,rgba(20,19,18,.9) 45%,rgba(20,19,18,0))}
}
/* Portrait tablets: the desktop width (30vw) leaves the photos small in a tall screen, so size them like phones do. */
@media (min-width:720px) and (max-width:1199px) and (orientation:portrait){
.t-frame{width:min(56vw,calc(34vh * var(--ar))) !important}
}
/* Landscape phones: too short to stack the photos between the heading and the counter, so they fly through the open
right-hand side instead, sized off the height. */
right-hand side instead, sized off the height so the closest one still fits between the nav and "View full gallery". */
@media (max-height:499px) and (orientation:landscape){
.t-space{perspective-origin:76% 42% !important}
.t-frame{width:calc(38vh * var(--ar)) !important;left:calc(76% + var(--tx) * .3) !important;top:calc(42% + var(--ty) * .3) !important}
.t-space{perspective-origin:76% 40% !important}
.t-frame{width:calc(30vh * var(--ar)) !important;left:calc(76% + var(--tx) * .3) !important;top:calc(40% + var(--ty) * .3) !important}
}

/* ---------- gallery (GalleryView.tsx) ---------- */
Expand Down
23 changes: 18 additions & 5 deletions src/components/innovision/DetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,15 @@ export default function DetailView({ v }: { v: V }) {
</p>
<h2 style={{ margin: "0", fontFamily: "var(--font-display)", fontWeight: "400", fontSize: "clamp(36px,5vw,80px)", lineHeight: "1" }}>Mission manifest</h2>
</div>
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
<strong style={{ fontFamily: "var(--font-display)", fontWeight: "400", fontSize: "clamp(56px,6vw,104px)", lineHeight: ".8", color: "transparent", WebkitTextStroke: `1.5px ${cw.accentL}` }}>{cw.missionCount}</strong>
<span style={{ display: "flex", flexDirection: "column", gap: "6px", fontSize: "12px", letterSpacing: ".3em", color: "rgba(236,232,223,.65)" }}><span>EVENTS</span><span>ON BOARD</span></span>
</div>
{/* Until a world has published events, its count reads as "coming soon" rather than "00". */}
{cw.missions.length ? (
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
<strong style={{ fontFamily: "var(--font-display)", fontWeight: "400", fontSize: "clamp(56px,6vw,104px)", lineHeight: ".8", color: "transparent", WebkitTextStroke: `1.5px ${cw.accentL}` }}>{cw.missionCount}</strong>
<span style={{ display: "flex", flexDirection: "column", gap: "6px", fontSize: "12px", letterSpacing: ".3em", color: "rgba(236,232,223,.65)" }}><span>EVENTS</span><span>ON BOARD</span></span>
</div>
) : (
<span style={{ display: "flex", flexDirection: "column", gap: "6px", fontSize: "12px", letterSpacing: ".3em", color: "rgba(236,232,223,.65)" }}><span>LINE-UP</span><span style={{ color: cw.accentL }}>COMING SOON</span></span>
)}
</header>
{/* One block of tickets per group: a world that combines categories (DTS and Fun) titles each one. */}
{cw.groups.map((g, gI) => (
Expand All @@ -459,10 +464,18 @@ export default function DetailView({ v }: { v: V }) {
<span style={{ width: "10px", height: "10px", flex: "none", transform: "rotate(45deg)", background: cw.accentL }}></span>
<h3 style={{ margin: "0", fontFamily: "var(--font-display)", fontWeight: "400", fontSize: "clamp(24px,2.6vw,40px)", lineHeight: "1", letterSpacing: ".04em" }}>{g.titleU}</h3>
<span aria-hidden="true" style={{ flex: "1", height: "1.5px", background: "repeating-linear-gradient(90deg,rgba(236,232,223,.3) 0 6px,transparent 6px 12px)" }}></span>
<span style={{ flex: "none", fontSize: "12px", fontWeight: "700", letterSpacing: ".3em", color: cw.accentL }}>{g.count} {g.missions.length === 1 ? "EVENT" : "EVENTS"}</span>
<span style={{ flex: "none", fontSize: "12px", fontWeight: "700", letterSpacing: ".3em", color: cw.accentL }}>{g.soon ? "COMING SOON" : `${g.count} ${g.missions.length === 1 ? "EVENT" : "EVENTS"}`}</span>
</div>
)}
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(min(100%,500px),1fr))", gap: "22px" }}>
{/* A group whose events aren't published yet holds one "Coming soon" ticket stub in their place. */}
{g.soon && (
<div data-d-card="" style={{ position: "relative", display: "grid", alignContent: "center", justifyItems: "start", gap: "14px", minHeight: "220px", padding: "32px clamp(22px,2.4vw,36px)", border: "1.5px dashed rgba(236,232,223,.3)", borderRadius: "18px", background: "radial-gradient(rgba(236,232,223,.06) 1px,transparent 1.3px) 0 0/14px 14px,linear-gradient(160deg,#23211e,#191816 70%)" }}>
<span style={{ fontSize: "11px", fontWeight: "700", letterSpacing: ".28em", color: cw.accentL }}>{g.titleU} · {cw.serial}</span>
<strong style={{ fontFamily: "var(--font-cinzel),serif", fontWeight: "900", fontSize: "clamp(28px,3vw,44px)", lineHeight: "1.05", color: "#ECE8DF" }}>Coming soon</strong>
<p style={{ margin: "0", maxWidth: "46ch", fontSize: "15px", lineHeight: "1.6", color: "rgba(236,232,223,.75)", textWrap: "pretty" }}>The {g.title} line-up is still being charted. Check back soon for the full manifest.</p>
</div>
)}
{g.missions.map((m) => (

<article key={m.no} data-d-card="" onMouseEnter={v.beep} style={{ display: "flex" }}>
Expand Down
18 changes: 11 additions & 7 deletions src/components/innovision/HomeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export default function HomeView({ v }: { v: V }) {
</div>
</section>
<section data-sec="gallery" data-tunnel="" aria-label="Gallery" style={{ position: "relative", height: "600vh", background: "#141312", color: "#ECE8DF" }}>
<div style={{ position: "sticky", top: "0", height: "100vh", overflow: "hidden" }}>
<div className="t-stage" style={{ position: "sticky", top: "0", overflow: "hidden" }}>
<img decoding="async" src="/assets/starfield.svg" alt="" style={{ position: "absolute", inset: "0", width: "100%", height: "100%", objectFit: "cover", opacity: ".75", pointerEvents: "none" }} />
<div className="t-space" style={{ position: "absolute", inset: "0", perspective: "900px", perspectiveOrigin: "50% 50%" }}>
<div style={{ position: "absolute", inset: "0", transformStyle: "preserve-3d" }}>
Expand All @@ -199,21 +199,25 @@ export default function HomeView({ v }: { v: V }) {
</div>
</div>
{/* Phones only (globals.css): dark fades behind the heading and the counter, so a photo flying past never covers the text. */}
<div aria-hidden="true" className="t-scrim t-scrim-top"></div>
{/* The heading and its scrim fade out as the visitor scrolls into the tunnel (Innovision#homeScroll). */}
<div aria-hidden="true" data-t-head="" className="t-scrim t-scrim-top"></div>
<div aria-hidden="true" className="t-scrim t-scrim-bot"></div>
<header style={{ position: "absolute", left: "clamp(20px,4vw,64px)", top: "calc(72px + 4vh)", maxWidth: "440px", pointerEvents: "none" }}>
<header data-t-head="" className="t-head" style={{ position: "absolute", left: "clamp(20px,4vw,64px)", top: "calc(72px + 4vh)", maxWidth: "440px", pointerEvents: "none", willChange: "opacity,transform" }}>
<p style={{ display: "flex", alignItems: "center", gap: "10px", margin: "0 0 16px", fontSize: "13px", fontWeight: "700", letterSpacing: ".3em", color: "oklch(0.8 0.12 85)" }}>GALLERY</p>
<h2 style={{ margin: "0 0 14px", fontFamily: "var(--font-display)", fontWeight: "400", fontSize: "clamp(34px,4.4vw,68px)", lineHeight: "1" }}>Into the archive</h2>
<p style={{ margin: "0", fontSize: "16px", lineHeight: "1.6", color: "rgba(236,232,223,.82)", textWrap: "pretty" }}>Keep scrolling to fly through moments from Innovision past.</p>
</header>
<div style={{ position: "absolute", left: "clamp(20px,4vw,64px)", right: "clamp(20px,4vw,64px)", bottom: "calc(clamp(16px,2.6vw,44px) + 64px)", display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: "16px", pointerEvents: "none" }}>
<div className="t-bar" style={{ position: "absolute", left: "clamp(20px,4vw,64px)", right: "clamp(20px,4vw,64px)", bottom: "calc(clamp(16px,2.6vw,44px) + 64px)", display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: "16px", pointerEvents: "none" }}>
<div style={{ display: "flex", alignItems: "baseline", gap: "10px", fontFamily: "var(--font-display)", fontWeight: "400" }}>
<span data-t-count="" style={{ fontSize: "clamp(36px,4vw,60px)", lineHeight: "1" }}>01</span>
<span style={{ fontSize: "18px", color: "rgba(236,232,223,.7)" }}>/ {v.tunnelTotal}</span>
</div>
<a href="#/gallery" onMouseEnter={v.hover} style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: "0", padding: "15px 26px", textDecoration: "none", fontWeight: "700", fontSize: "14px", letterSpacing: ".06em", whiteSpace: "nowrap", clipPath: "polygon(12px 0,100% 0,100% calc(100% - 12px),calc(100% - 12px) 100%,0 100%,0 12px)", transition: "background-color .4s cubic-bezier(.25,1,.1,1)", color: "#141312", background: "#ECE8DF", pointerEvents: "auto" }} className="hv-gold-fill">
<span data-scr="">VIEW FULL GALLERY</span>
</a>
{/* Shown only at the end of the tunnel (Innovision#homeScroll); hidden until then, so it isn't tabbable either. */}
<div data-t-cta="" style={{ flexShrink: "0", opacity: "0", visibility: "hidden", pointerEvents: "none", willChange: "opacity,transform" }}>
<a href="#/gallery" onMouseEnter={v.hover} style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", padding: "15px 26px", textDecoration: "none", fontWeight: "700", fontSize: "14px", letterSpacing: ".06em", whiteSpace: "nowrap", clipPath: "polygon(12px 0,100% 0,100% calc(100% - 12px),calc(100% - 12px) 100%,0 100%,0 12px)", transition: "background-color .4s cubic-bezier(.25,1,.1,1)", color: "#141312", background: "#ECE8DF" }} className="hv-gold-fill">
<span data-scr="">VIEW FULL GALLERY</span>
</a>
</div>
</div>
</div>
</section>
Expand Down
22 changes: 20 additions & 2 deletions src/components/innovision/Innovision.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1002,9 +1002,24 @@ export class Innovision extends Component<Props, State> {
const tun = this.$('[data-tunnel]');
if (tun) {
const fr = this.$$('[data-t-frame]'), N = fr.length, TGAP = 1000, cnt = this.$('[data-t-count]');
const head = this.$$('[data-t-head]'), cta = this.$('[data-t-cta]');
fr.forEach((f, i) => { f.style.zIndex = String(N - i); });
// The heading (and the phone scrim behind it) fades out over the first steps into the tunnel; "View full gallery"
// fades in once the last photo is on its way. Both follow the scroll, so scrolling back up reverses them.
const fade = (el: HTMLElement, o: number, dy: number) => {
const os = String(+o.toFixed(3)), vis = o > 0 ? 'visible' : 'hidden';
if (el.style.opacity !== os) { el.style.opacity = os; el.style.transform = 'translateY(' + ((1 - o) * dy).toFixed(1) + 'px)'; }
if (el.style.visibility !== vis) el.style.visibility = vis;
};
// Only write what changed: replacing the counter's text every scroll update invalidates layout.
const upd = (p: number) => {
const ho = Math.max(0, 1 - p / .05);
head.forEach((el) => fade(el, ho, -24));
if (cta) {
const co = Math.min(1, Math.max(0, (p - .86) / .08));
fade(cta, co, 18);
cta.style.pointerEvents = co > .6 ? 'auto' : 'none';
}
const travel = p * (N * TGAP - 200);
// Phones fade a photo out sooner as it passes the camera, before it grows past the room between the heading and the counter.
const near = innerWidth < 720 || (innerHeight < 500 && innerWidth > innerHeight) ? 120 : 250;
Expand Down Expand Up @@ -2670,11 +2685,14 @@ export class Innovision extends Component<Props, State> {
{ key: 'other', title: '', items: list.filter((m) => !dw.groups!.some(([cat]) => cat === m.cat)) }]
: [{ key: 'all', title: '', items: list }];
// Numbered in display order; idx is the ticket's place in missions, which the event popup reads.
// A titled group with no events yet stays, shown as "Coming soon".
let n = 0;
const groups = sections.filter((g) => g.items.length).map((g) => ({
const groups = sections.filter((g) => g.items.length || g.title).map((g) => ({
key: g.key,
title: g.title,
titleU: g.title.toUpperCase(),
count: String(g.items.length).padStart(2, '0'),
soon: !g.items.length,
missions: g.items.map((m) => {
const k = n++;
return {
Expand All @@ -2697,7 +2715,7 @@ export class Innovision extends Component<Props, State> {
serial: 'IV26-0' + (s.dIndex + 1),
stampText: 'BOARDING SOON · ' + dw.statL.toUpperCase() + ' · ',
frame: 'color-mix(in oklab, ' + dw.ink + ' 50%, transparent)',
ticker: [0, 1, 2, 3].flatMap(() => ['Now boarding · ' + dw.name, ...missions.map((m) => m.name)]).map((t) => ({ t })),
ticker: [0, 1, 2, 3].flatMap(() => ['Now boarding · ' + dw.name, ...groups.flatMap((g) => g.soon ? [g.titleU + ' · Coming soon'] : g.missions.map((m) => m.name))]).map((t) => ({ t })),
chars: [...dw.name.toUpperCase()].map((ch) => ({ ch: ch === ' ' ? ' ' : ch })),
words: dw.tagline.split(' ').map((t) => ({ t })),
specs: dw.specs.map(([k, v], j) => ({ k, v, i: String(j + 1).padStart(2, '0') })),
Expand Down
3 changes: 2 additions & 1 deletion src/components/innovision/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ export const WORLDS: World[] = [
specs: [['Category', 'Fun & games'], ['Format', 'Open to all'], ['Vibe', 'Euphoric'], ['Fuel', 'Team spirit'], ['Status', 'Boarding soon']],
categories: ['dts events', 'fun events'],
groups: [['dts events', 'DTS Events'], ['fun events', 'Fun Events']],
missions: [['Quiz Night', 'Tech, pop culture and everything in between.', 'Team · 2–3', '3 rounds', 'dts events'], ['Project Expo', 'Walk the hall of student builds and vote for your favourite.', 'Open to all', 'All fest', 'dts events'], ['Gaming Arena', 'Squad up for LAN battles and console showdowns.', 'Squad', 'All night', 'fun events'], ['Treasure Hunt', 'Clues scattered across campus. Only the sharpest crews finish.', 'Team · 3–4', 'Campus-wide', 'fun events']] },
// No placeholder line-up: each group shows a "Coming soon" card until its events are published.
missions: [] },
];

/**
Expand Down
Loading