1104 lines
52 KiB
JavaScript
1104 lines
52 KiB
JavaScript
/* global React */
|
|
const { useState, useEffect, useRef, Fragment } = React;
|
|
|
|
// ─── Icons ───────────────────────────────────────────────────────────────────
|
|
const Icon = ({ size = 16, sw = 1.75, className = "", children }) => (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24"
|
|
fill="none" stroke="currentColor" strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" className={className}>
|
|
{children}
|
|
</svg>
|
|
);
|
|
const ChevronDown = (p) => <Icon size={16} {...p}><polyline points="6 9 12 15 18 9"/></Icon>;
|
|
const Menu = (p) => <Icon size={24} {...p}><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="18" y2="18"/></Icon>;
|
|
const X = (p) => <Icon size={24} {...p}><path d="M18 6 6 18"/><path d="m6 6 12 12"/></Icon>;
|
|
const ArrowRight = (p) => <Icon size={16} {...p}><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></Icon>;
|
|
const ArrowLeft = (p) => <Icon size={16} {...p}><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></Icon>;
|
|
const ArrowUpRight = (p) => <Icon size={14} {...p}><path d="M7 17 17 7"/><path d="M7 7h10v10"/></Icon>;
|
|
const Mail = (p) => <Icon size={16} {...p}><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></Icon>;
|
|
const MapPin = (p) => <Icon size={16} {...p}><path d="M20 10c0 7-8 13-8 13s-8-6-8-13a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></Icon>;
|
|
const Linkedin = (p) => <Icon size={16} {...p}><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect width="4" height="12" x="2" y="9"/><circle cx="4" cy="4" r="2"/></Icon>;
|
|
const ExternalLink = (p) => <Icon size={13} {...p}><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></Icon>;
|
|
const Play = (p) => <Icon size={16} {...p}><polygon points="6 3 20 12 6 21 6 3"/></Icon>;
|
|
const Check = (p) => <Icon size={16} {...p}><polyline points="20 6 9 17 4 12"/></Icon>;
|
|
const Server = (p) => <Icon size={16} {...p}><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></Icon>;
|
|
const Radio = (p) => <Icon size={16} {...p}><circle cx="12" cy="12" r="2"/><path d="M4.93 19.07a10 10 0 0 1 0-14.14"/><path d="M7.76 16.24a6 6 0 0 1 0-8.48"/><path d="M16.24 7.76a6 6 0 0 1 0 8.48"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></Icon>;
|
|
const Cable = (p) => <Icon size={16} {...p}><path d="M17 21v-2a1 1 0 0 1-1-1v-1a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1v2"/><path d="M19 15V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V9"/><path d="M21 21v-2h-4v2"/><path d="M3 5h4V3H3z"/><path d="M7 5v1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V5"/></Icon>;
|
|
const Cloud = (p) => <Icon size={16} {...p}><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"/></Icon>;
|
|
const Tv = (p) => <Icon size={16} {...p}><rect width="20" height="15" x="2" y="7" rx="2" ry="2"/><polyline points="17 2 12 7 7 2"/></Icon>;
|
|
const Cpu = (p) => <Icon size={16} {...p}><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 1v3"/><path d="M15 1v3"/><path d="M9 20v3"/><path d="M15 20v3"/><path d="M20 9h3"/><path d="M20 14h3"/><path d="M1 9h3"/><path d="M1 14h3"/></Icon>;
|
|
const ShieldCheck = (p) => <Icon size={16} {...p}><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></Icon>;
|
|
const Ruler = (p) => <Icon size={16} {...p}><path d="M21.3 15.3a2.4 2.4 0 0 1 0 3.4l-2.6 2.6a2.4 2.4 0 0 1-3.4 0L2.7 8.7a2.4 2.4 0 0 1 0-3.4l2.6-2.6a2.4 2.4 0 0 1 3.4 0Z"/><path d="m14.5 12.5 2-2"/><path d="m11.5 9.5 2-2"/><path d="m8.5 6.5 2-2"/><path d="m17.5 15.5 2-2"/></Icon>;
|
|
const Wrench = (p) => <Icon size={16} {...p}><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></Icon>;
|
|
const MonitorCheck = (p) => <Icon size={16} {...p}><path d="m9 10 2 2 4-4"/><rect width="20" height="14" x="2" y="3" rx="2"/><path d="M12 17v4"/><path d="M8 21h8"/></Icon>;
|
|
const Headset = (p) => <Icon size={16} {...p}><path d="M3 11h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-5a9 9 0 0 1 18 0v5a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3"/><path d="M21 16v2a4 4 0 0 1-4 4h-5"/></Icon>;
|
|
const Star = (p) => <Icon size={14} {...p}><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></Icon>;
|
|
const Sparkles = (p) => <Icon size={16} {...p}><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/></Icon>;
|
|
|
|
// ─── Reveal helper ────────────────────────────────────────────────────────────
|
|
function Reveal({ children, className = "", delay = 0 }) {
|
|
const ref = useRef(null);
|
|
const [vis, setVis] = useState(false);
|
|
useEffect(() => {
|
|
const el = ref.current;
|
|
if (!el) return;
|
|
const obs = new IntersectionObserver(([e]) => {
|
|
if (e.isIntersecting) { setVis(true); obs.unobserve(el); }
|
|
}, { threshold: 0.1, rootMargin: "0px 0px -40px 0px" });
|
|
obs.observe(el);
|
|
return () => obs.disconnect();
|
|
}, []);
|
|
return (
|
|
<div ref={ref} className={`reveal ${vis ? "visible" : ""} ${className}`} style={{ transitionDelay: `${delay}ms` }}>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ─── Cursor follower ─────────────────────────────────────────────────────────
|
|
function CursorDot() {
|
|
const ref = useRef(null);
|
|
useEffect(() => {
|
|
const onMove = (e) => {
|
|
if (ref.current) {
|
|
ref.current.style.transform = `translate(${e.clientX}px, ${e.clientY}px) translate(-50%, -50%)`;
|
|
}
|
|
document.body.dataset.cursor = "on";
|
|
};
|
|
window.addEventListener("mousemove", onMove);
|
|
return () => window.removeEventListener("mousemove", onMove);
|
|
}, []);
|
|
return <div ref={ref} className="cursor-dot"></div>;
|
|
}
|
|
|
|
// ─── Page-load curtain ───────────────────────────────────────────────────────
|
|
function Curtain() {
|
|
const [gone, setGone] = useState(false);
|
|
useEffect(() => { const t = setTimeout(() => setGone(true), 1300); return () => clearTimeout(t); }, []);
|
|
if (gone) return null;
|
|
return <div className="curtain"></div>;
|
|
}
|
|
|
|
// ─── NAVIGATION ──────────────────────────────────────────────────────────────
|
|
function Navigation() {
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const [open, setOpen] = useState(false);
|
|
useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > 50);
|
|
window.addEventListener("scroll", onScroll);
|
|
return () => window.removeEventListener("scroll", onScroll);
|
|
}, []);
|
|
const links = [
|
|
{ href: "#services", label: "Capabilities" },
|
|
{ href: "#corridors", label: "Regional" },
|
|
{ href: "#projects", label: "Projects" },
|
|
{ href: "#about", label: "About" },
|
|
{ href: "#contact", label: "Contact" }
|
|
];
|
|
return (
|
|
<nav className={`nav ${scrolled ? "scrolled" : ""}`}>
|
|
<div className="nav-inner">
|
|
<a href="#" className="nav-logo">
|
|
<img src={__R("images/dragon-mark.png")} alt="Wild Dragon" width="32" height="32" />
|
|
<span>Wild Dragon</span>
|
|
</a>
|
|
<div className="nav-links">
|
|
{links.map(l => <a key={l.href} href={l.href}>{l.label}</a>)}
|
|
<a href="#contact" className="nav-cta-btn">Request Scope</a>
|
|
</div>
|
|
<button className="nav-toggle" onClick={() => setOpen(!open)} aria-label="Toggle menu">
|
|
{open ? <X/> : <Menu/>}
|
|
</button>
|
|
</div>
|
|
<div className={`nav-mobile ${open ? "open" : ""}`}>
|
|
{links.map(l => <a key={l.href} href={l.href} onClick={()=>setOpen(false)}>{l.label}</a>)}
|
|
<a href="#contact" className="nav-mobile-cta" onClick={()=>setOpen(false)}>Request Scope</a>
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|
|
|
|
// ─── HERO ────────────────────────────────────────────────────────────────────
|
|
const HERO_ROLES = [
|
|
"Broadcast Systems Integration",
|
|
"Commercial AV Installation",
|
|
"SMPTE ST 2110 IP Facilities",
|
|
"Production Control Rooms",
|
|
"Direct-View LED Video Walls",
|
|
"Extended Reality (XR) Stages"
|
|
];
|
|
function Hero({ ctaLabel }) {
|
|
const [loaded, setLoaded] = useState(false);
|
|
const [idx, setIdx] = useState(0);
|
|
const [text, setText] = useState("");
|
|
const [typing, setTyping] = useState(true);
|
|
useEffect(() => { const t = setTimeout(() => setLoaded(true), 100); return () => clearTimeout(t); }, []);
|
|
useEffect(() => {
|
|
const role = HERO_ROLES[idx];
|
|
if (typing) {
|
|
if (text.length < role.length) {
|
|
const t = setTimeout(() => setText(role.slice(0, text.length + 1)), 45);
|
|
return () => clearTimeout(t);
|
|
} else {
|
|
const t = setTimeout(() => setTyping(false), 2400);
|
|
return () => clearTimeout(t);
|
|
}
|
|
} else {
|
|
if (text.length > 0) {
|
|
const t = setTimeout(() => setText(text.slice(0, -1)), 25);
|
|
return () => clearTimeout(t);
|
|
} else {
|
|
setIdx((i) => (i + 1) % HERO_ROLES.length);
|
|
setTyping(true);
|
|
}
|
|
}
|
|
}, [text, typing, idx]);
|
|
|
|
const aniBase = (d) => ({
|
|
transition: `opacity .9s ${d}s, transform .9s ${d}s`,
|
|
opacity: loaded ? 1 : 0,
|
|
transform: loaded ? "translateY(0)" : "translateY(24px)"
|
|
});
|
|
|
|
return (
|
|
<section className="hero grain">
|
|
<div className="hero-bg">
|
|
<img src={__R("images/photos/production-switcher.jpg")} alt="Broadcast control room switcher console" fetchpriority="high" />
|
|
</div>
|
|
<div className="hero-grid"></div>
|
|
<div className="hero-fade-v"></div>
|
|
<div className="hero-fade-h"></div>
|
|
<div className="hero-corner tl">// 38.9°N · 77.0°W</div>
|
|
<div className="hero-corner tr">EST. 2024 · DC / DMV</div>
|
|
<div className="hero-corner bl">SMPTE ST 2110 · NDI 6</div>
|
|
<div className="hero-corner br">● SYSTEM ONLINE</div>
|
|
|
|
<div className="hero-content">
|
|
{/* Availability & Google Business Trust Badges */}
|
|
<div className="hero-badges-row" style={aniBase(0)}>
|
|
<div className="hero-status-chip">
|
|
<span className="live-dot"></span>
|
|
<span>Available for DMV Integration & Commissioning</span>
|
|
</div>
|
|
<a href="#contact" className="hero-google-badge">
|
|
<span className="g-star">★ 5.0</span>
|
|
<span>Google Verified Integrator · DC · MD · VA</span>
|
|
</a>
|
|
</div>
|
|
|
|
<div className="hero-tag" style={aniBase(.12)}>
|
|
<span className="hero-tag-prefix">//</span> {text}<span className="caret">|</span>
|
|
</div>
|
|
|
|
<h1 style={aniBase(.24)} className="hero-h1">
|
|
<span className="hero-h1-brand">Wild Dragon</span>
|
|
<span className="hero-h1-sub">Broadcast Systems Integration & Commercial AV</span>
|
|
</h1>
|
|
|
|
<div className="hero-rule" style={{
|
|
transition: "opacity 1s .35s, width 1s .35s",
|
|
opacity: loaded ? 1 : 0,
|
|
width: loaded ? "7rem" : 0
|
|
}}></div>
|
|
|
|
<p className="hero-desc" style={aniBase(.4)}>
|
|
Principal Design Engineer <strong>Zachary Gaetano</strong> leads end-to-end broadcast facility design, SMPTE ST 2110 IP video infrastructure, and high-performance commercial AV installations across Washington, DC, Maryland, and Northern Virginia.
|
|
</p>
|
|
|
|
<div className="hero-actions" style={aniBase(.5)}>
|
|
<a href="#contact" className="btn btn-primary hero-cta-main">
|
|
Request Scope & Bid <ArrowRight size={16}/>
|
|
</a>
|
|
<a href="#projects" className="btn btn-ghost-dark hero-cta-sec">
|
|
{ctaLabel || "Explore Case Studies"}
|
|
</a>
|
|
</div>
|
|
|
|
{/* Quick Tech Hardware Ecosystem Pills */}
|
|
<div className="hero-tech-pills" style={aniBase(.6)}>
|
|
<span>SMPTE ST 2110</span>
|
|
<span>Ross Ultrix & Carbonite</span>
|
|
<span>Q-SYS Certified</span>
|
|
<span>Brompton Tessera SX40</span>
|
|
<span>12G-SDI & NDI 6</span>
|
|
<span>Dante Audio</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── CLIENTS ─────────────────────────────────────────────────────────────────
|
|
const CLIENTS = [
|
|
{ name:"Washington Commanders", logo:__R("images/clients/commanders.png") },
|
|
{ name:"BetMGM", logo:__R("images/clients/betmgm.png") },
|
|
{ name:"CVS Health / Aetna", logo:__R("images/clients/cvs.png") },
|
|
{ name:"UBS", logo:__R("images/clients/ubs.png") },
|
|
{ name:"Monumental Sports", logo:__R("images/clients/monumental.png") },
|
|
{ name:"NBC Sports", logo:__R("images/clients/nbc-sports.png") },
|
|
{ name:"Cosm", logo:__R("images/clients/cosm.svg") },
|
|
{ name:"Intuit", logo:__R("images/clients/intuit.png") },
|
|
{ name:"Red Sands", logo:__R("images/clients/redsands.svg") },
|
|
{ name:"Blue Origin", logo:__R("images/clients/blue-origin.png") },
|
|
{ name:"Broadcast Management Group", logo:__R("images/clients/bmg.png") },
|
|
{ name:"AVI-SPL", logo:__R("images/clients/avispl.png") },
|
|
];
|
|
function Clients({ marquee }) {
|
|
return (
|
|
<section className="clients">
|
|
<div className="container clients-heading">
|
|
<p className="eyebrow">// Trusted by Enterprise, Sports & Broadcast Leaders</p>
|
|
</div>
|
|
{marquee ? (
|
|
<div className="marquee">
|
|
<div className="marquee-track">
|
|
{[...CLIENTS, ...CLIENTS].map((c, i) => (
|
|
<div className="marquee-item" key={i} title={c.name}>
|
|
<img src={c.logo} alt={c.name}/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="clients-static">
|
|
{CLIENTS.map((c) => (
|
|
<div className="marquee-item" key={c.name} title={c.name}>
|
|
<img src={c.logo} alt={c.name}/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── PARTNERS ────────────────────────────────────────────────────────────────
|
|
const PARTNERS = [
|
|
{ name:"THOR Broadcast", logo:__R("images/clients/thor-logo.png") },
|
|
{ name:"Ross Video", logo:__R("images/clients/ross-video.png") },
|
|
{ name:"Filmtools", logo:__R("images/clients/filmtools.png") },
|
|
{ name:"Forecast Consoles", logo:__R("images/clients/forecast.png") },
|
|
{ name:"vMix by Studio Coast", logo:__R("images/clients/vmix.png") },
|
|
{ name:"RED Digital Cinema", logo:__R("images/partners/red-digital.png") },
|
|
];
|
|
function Partners() {
|
|
return (
|
|
<section className="partners">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="partners-wrap">
|
|
<p className="eyebrow">// Technology & Manufacturing Partners</p>
|
|
<div className="partners-grid">
|
|
{PARTNERS.map(p => (
|
|
<div className="partner-logo" title={p.name} key={p.name}>
|
|
<img src={p.logo} alt={p.name}/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── ABOUT ───────────────────────────────────────────────────────────────────
|
|
const CAPS = [
|
|
{ icon: Radio, title:"Signal-Flow Engineering", desc:"End-to-end facility architecture: cable schedules, signal flow schematics, rack elevations, and single-line drawings before cable is pulled." },
|
|
{ icon: Cable, title:"Turnkey Integration", desc:"Precision rack fabrication, custom patching, structured cabling, label standard compliance, and hardware commissioning." },
|
|
{ icon: Cloud, title:"IP Video & ST 2110", desc:"Uncompressed SMPTE ST 2110-20/30/40, PTP IEEE 1588 timing, NMOS IS-04/05 routing, NDI 6, SRT, and hybrid cloud production." },
|
|
{ icon: Tv, title:"Production Engineering", desc:"Live control room switchers, replay servers, intercom systems, multiviewers, and real-time tally & router integration." },
|
|
];
|
|
function About() {
|
|
return (
|
|
<section id="about" className="about">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="about-intro">
|
|
<div className="section-counter"><span className="num">01</span><span className="bar"></span><span>About</span></div>
|
|
<h2 className="section-title">From live production to<br/>systems architecture.</h2>
|
|
<div className="body" style={{marginTop:"2rem"}}>
|
|
<p>Wild Dragon is a specialized broadcast systems integrator and commercial AV contractor based in the Washington, DC area. Founded by <strong>Zachary Gaetano</strong>, the company bridges production operations and mission-critical engineering.</p>
|
|
<p>Because the same engineer designs, installs, and commissions the facility, there is zero translation loss between engineering drawings and real-world rack builds. Every system is built to broadcast-grade reliability standards, documented thoroughly, and handed off with complete operational training.</p>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
<div className="caps-grid">
|
|
{CAPS.map((c, i) => (
|
|
<Reveal key={c.title} delay={i * 80}>
|
|
<div className="cap">
|
|
<div className="cap-icon"><c.icon size={20} sw={1.5}/></div>
|
|
<h3>{c.title}</h3>
|
|
<p>{c.desc}</p>
|
|
</div>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── STATS — dark band ───────────────────────────────────────────────────────
|
|
const STATS = [
|
|
{ v:10, suf:"+", label:"Years in Broadcast", desc:"Production & engineering" },
|
|
{ v:8, suf:"+", label:"Major Facilities", desc:"Designed & integrated" },
|
|
{ v:5900, suf:"+", label:"End Users Served", desc:"Global content delivery" },
|
|
{ v:5, suf:"", label:"Industry Verticals", desc:"Enterprise, sports, financial, aerospace, defense" },
|
|
];
|
|
function AnimatedNumber({ value, suffix }) {
|
|
const [n, setN] = useState(0);
|
|
const ref = useRef(null);
|
|
const done = useRef(false);
|
|
useEffect(() => {
|
|
const obs = new IntersectionObserver(([e]) => {
|
|
if (e.isIntersecting && !done.current) {
|
|
done.current = true;
|
|
const dur = 2000, steps = 60, inc = value/steps;
|
|
let cur = 0;
|
|
const t = setInterval(() => {
|
|
cur += inc;
|
|
if (cur >= value) { setN(value); clearInterval(t); }
|
|
else setN(Math.floor(cur));
|
|
}, dur/steps);
|
|
}
|
|
}, { threshold: .3 });
|
|
if (ref.current) obs.observe(ref.current);
|
|
return () => obs.disconnect();
|
|
}, [value]);
|
|
const display = n >= 1000 && value >= 1000 ? `${(n/1000).toFixed(1).replace(/\.0$/,"")}k` : n;
|
|
return <span ref={ref} className="tabular-nums">{display}<span className="suf">{suffix}</span></span>;
|
|
}
|
|
function Stats() {
|
|
return (
|
|
<section className="stats grain dark-section">
|
|
<div className="container">
|
|
<div className="stats-grid">
|
|
{STATS.map(s => (
|
|
<div key={s.label}>
|
|
<div className="stat-value"><AnimatedNumber value={s.v} suffix={s.suf}/></div>
|
|
<div className="stat-label">{s.label}</div>
|
|
<div className="stat-desc">{s.desc}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── SERVICES & CAPABILITIES BENTO (SEO HUB) ──────────────────────────────────
|
|
const CORE_SERVICES = [
|
|
{
|
|
icon: Tv,
|
|
slug: "commercial-av-installation-washington-dc",
|
|
title: "Commercial AV Installation",
|
|
tagline: "Corporate Boardrooms, Town Halls & Briefing Centers",
|
|
desc: "Design-led commercial AV installation for organizations across Washington DC, Northern Virginia, and Maryland. Video conferencing, high-resolution display walls, automated room control, and precision audio DSP.",
|
|
tags: ["Q-SYS Control", "Shure Microflex", "Crestron", "Teams/Zoom Rooms"],
|
|
url: "/services/commercial-av-installation-washington-dc/"
|
|
},
|
|
{
|
|
icon: Radio,
|
|
slug: "broadcast-systems-integration",
|
|
title: "Broadcast Systems Integration",
|
|
tagline: "Live Production Studios & Master Control Rooms",
|
|
desc: "Turnkey broadcast facility engineering from signal-flow drafting to final commissioning. Production switchers, routing switchers, multiviewers, replay servers, tally systems, and studio camera chains.",
|
|
tags: ["Ross Video", "vMix 4K", "Grass Valley", "Riedel Intercom"],
|
|
url: "/services/broadcast-systems-integration/"
|
|
},
|
|
{
|
|
icon: Server,
|
|
slug: "smpte-st-2110-infrastructure",
|
|
title: "IP Video & SMPTE ST 2110 Infrastructure",
|
|
tagline: "PTP Timing, NMOS Routing & Cloud Production",
|
|
desc: "Uncompressed IP broadcast infrastructure and cloud-hybrid production. SMPTE ST 2110-20/30/40 standards, PTP IEEE 1588 grandmaster timing, NMOS IS-04/05 discovery/routing, NDI 6, and SRT contribution.",
|
|
tags: ["ST 2110", "PTP IEEE 1588", "NMOS Routing", "SRT / NDI 6"],
|
|
url: "/services/broadcast-systems-integration/"
|
|
},
|
|
{
|
|
icon: Cpu,
|
|
slug: "led-video-wall-installation",
|
|
title: "LED Video Walls & XR Stages",
|
|
tagline: "Direct-View LED, Tessera SX40 & Virtual Sets",
|
|
desc: "Direct-view fine-pitch LED display wall installation, processor engineering, and XR virtual production stages. Brompton Tessera processing, sub-frame latency, camera tracking, and genlock synchronization.",
|
|
tags: ["Brompton SX40", "NovaStar", "Megapixel VR", "Unreal Engine XR"],
|
|
url: "/services/led-video-wall-installation/"
|
|
}
|
|
];
|
|
function Services() {
|
|
return (
|
|
<section id="services" className="services">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="services-head">
|
|
<div className="section-counter"><span className="num">02</span><span className="bar"></span><span>Capabilities</span></div>
|
|
<h2 className="section-title">Core solutions & engineering.</h2>
|
|
<p className="section-lede">Specialized engineering disciplines designed to eliminate complexity, ensure signal integrity, and deliver broadcast reliability for enterprise, sports, and federal clients.</p>
|
|
</div>
|
|
</Reveal>
|
|
|
|
<div className="services-bento-grid">
|
|
{CORE_SERVICES.map((s, i) => (
|
|
<Reveal key={s.slug} delay={i * 70}>
|
|
<div className="svc-bento-card">
|
|
<div className="svc-bento-top">
|
|
<div className="svc-icon-box"><s.icon size={22} sw={1.5}/></div>
|
|
<span className="svc-bento-badge">{`0${i + 1}`}</span>
|
|
</div>
|
|
<h3 className="svc-bento-title">{s.title}</h3>
|
|
<p className="svc-bento-tagline">{s.tagline}</p>
|
|
<p className="svc-bento-desc">{s.desc}</p>
|
|
<div className="svc-tags">
|
|
{s.tags.map(t => <span key={t} className="svc-tag-pill">{t}</span>)}
|
|
</div>
|
|
<a href={s.url} className="svc-bento-link">
|
|
<span>Explore service specification</span>
|
|
<ArrowRight size={14}/>
|
|
</a>
|
|
</div>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── REGIONAL CORRIDORS (LOCAL SEO & GBP HUB) ─────────────────────────────────
|
|
const CORRIDORS = [
|
|
{
|
|
title: "Washington, DC",
|
|
badge: "District Core",
|
|
focus: "Capitol Hill, K Street, Federal Newsrooms & Executive Briefing Centers",
|
|
desc: "Rapid on-site integration, secure boardroom AV, live broadcast studios, and press briefing room engineering.",
|
|
url: "/washington-dc/"
|
|
},
|
|
{
|
|
title: "Northern Virginia",
|
|
badge: "Tech & Defense",
|
|
focus: "Tysons, Reston, Herndon, Arlington & Alexandria Enterprise Corridor",
|
|
desc: "High-density enterprise commercial AV, defense contractor briefing facilities, and SMPTE ST 2110 studio hubs.",
|
|
url: "/northern-virginia/"
|
|
},
|
|
{
|
|
title: "Maryland",
|
|
badge: "Biotech & Federal",
|
|
focus: "Bethesda, Rockville, Silver Spring & Baltimore Corporate Centers",
|
|
desc: "Biotech auditorium AV, executive boardrooms, healthcare streaming suites, and regional broadcast facilities.",
|
|
url: "/maryland/"
|
|
},
|
|
{
|
|
title: "Broadcast Integration DMV",
|
|
badge: "Regional Systems",
|
|
focus: "Regional Multi-Site Facilities, Master Control & Production Trucks",
|
|
desc: "End-to-end broadcast facility architecture, routing matrix upgrades, and turn-key control room builds.",
|
|
url: "/broadcast-integration-dmv/"
|
|
},
|
|
{
|
|
title: "Commercial AV DMV",
|
|
badge: "Enterprise AV",
|
|
focus: "Turnkey Corporate AV, All-Hands Spaces & Direct-View LED Walls",
|
|
desc: "Single-engineer design, installation, DSP programming, and written acceptance testing for enterprise rooms.",
|
|
url: "/av-installation-dmv/"
|
|
}
|
|
];
|
|
function RegionalCorridors() {
|
|
return (
|
|
<section id="corridors" className="corridors-section">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="corridors-head">
|
|
<div className="section-counter"><span className="num">03</span><span className="bar"></span><span>Regional Corridors</span></div>
|
|
<h2 className="section-title">DMV Service Area & Local Reach.</h2>
|
|
<p className="section-lede">Direct engineering presence with immediate on-site response across Washington, DC, Maryland, and Northern Virginia. Google Verified commercial integrator.</p>
|
|
</div>
|
|
</Reveal>
|
|
|
|
<div className="corridors-grid">
|
|
{CORRIDORS.map((c, i) => (
|
|
<Reveal key={c.title} delay={i * 60}>
|
|
<a href={c.url} className="corridor-card">
|
|
<div className="corridor-top">
|
|
<div className="corridor-pin"><MapPin size={16} sw={1.75}/></div>
|
|
<span className="corridor-badge">{c.badge}</span>
|
|
</div>
|
|
<h3 className="corridor-title">{c.title}</h3>
|
|
<p className="corridor-focus">{c.focus}</p>
|
|
<p className="corridor-desc">{c.desc}</p>
|
|
<div className="corridor-action">
|
|
<span>View regional coverage</span>
|
|
<ArrowUpRight size={14}/>
|
|
</div>
|
|
</a>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
|
|
<Reveal delay={200}>
|
|
<div className="corridor-sublinks">
|
|
<span className="sublinks-label">Local Metro Coverage:</span>
|
|
<div className="sublinks-pills">
|
|
<a href="/washington-dc/">Downtown DC</a>
|
|
<a href="/northern-virginia/">Tysons & Reston</a>
|
|
<a href="/bethesda-rockville-av/">Bethesda & Rockville</a>
|
|
<a href="/maryland/">Silver Spring</a>
|
|
<a href="/northern-virginia/">Arlington & Alexandria</a>
|
|
<a href="/broadcast-integration-dmv/">Baltimore Metro</a>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── PROJECTS ─────────────────────────────────────────────────────────────────
|
|
function Projects({ onOpen }) {
|
|
const list = window.PROJECTS || [];
|
|
const featured = list.find(p => p.featured) || list[0];
|
|
const rest = list.filter(p => p !== featured);
|
|
|
|
const handleProjectClick = (e, slug) => {
|
|
// If not middle click or modifier key, open in-page modal
|
|
if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
|
e.preventDefault();
|
|
onOpen(slug);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section id="projects" className="projects">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="projects-head">
|
|
<div>
|
|
<div className="section-counter"><span className="num">04</span><span className="bar"></span><span>Projects</span></div>
|
|
<h2 className="section-title">Selected case studies.</h2>
|
|
</div>
|
|
<p className="lede">Broadcast facility design, cloud production architectures, and commercial AV integration for enterprise, sports, aerospace, and financial services clients.</p>
|
|
</div>
|
|
</Reveal>
|
|
|
|
{featured && (
|
|
<Reveal>
|
|
<a className="proj-featured"
|
|
href={`/projects/${featured.slug}/`}
|
|
onClick={(e) => handleProjectClick(e, featured.slug)}>
|
|
<img src={featured.thumbnail} alt={`${featured.client} — ${featured.category}`} loading="lazy"/>
|
|
<div className="proj-featured-body">
|
|
<div className="proj-featured-meta">
|
|
<span className="badge">Featured Project</span>
|
|
<span className="yr">{featured.category} · {featured.year}</span>
|
|
</div>
|
|
<h3>{featured.client}</h3>
|
|
<p>{featured.summary}</p>
|
|
<span className="cta">Read case study <ArrowRight/></span>
|
|
</div>
|
|
</a>
|
|
</Reveal>
|
|
)}
|
|
|
|
<div className="proj-grid">
|
|
{rest.map((p, i) => (
|
|
<Reveal key={p.slug} delay={i * 60}>
|
|
<a className="proj-card"
|
|
href={`/projects/${p.slug}/`}
|
|
onClick={(e) => handleProjectClick(e, p.slug)}>
|
|
<img src={p.thumbnail} alt={`${p.client} — ${p.category}`} loading="lazy"/>
|
|
<div className="proj-card-overlay"></div>
|
|
<div className="proj-arrow"><ArrowUpRight/></div>
|
|
<div className="proj-card-content">
|
|
<div className="proj-card-tags">
|
|
<span className="proj-tag">{p.category}</span>
|
|
<span className="proj-yr">{p.year}</span>
|
|
</div>
|
|
<h3>{p.client}</h3>
|
|
<p>{p.summary}</p>
|
|
</div>
|
|
</a>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
|
|
<Reveal delay={150}>
|
|
<div className="projects-bottom-cta">
|
|
<a href="/projects/" className="btn btn-secondary">
|
|
View All Case Studies & Technical Summaries <ArrowRight size={14}/>
|
|
</a>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── TECH STACK ──────────────────────────────────────────────────────────────
|
|
const STACK = [
|
|
{ cat:"Routing & Infrastructure", desc:"12G-SDI, SMPTE ST 2110, Ross Ultrix, Blackmagic Videohub, THOR fiber transmitters, patch bays" },
|
|
{ cat:"Production Switchers", desc:"Ross Carbonite Ultra, vMix Pro 4K, Grass Valley, Blackmagic ATEM Constellation 8K" },
|
|
{ cat:"Audio & Intercom", desc:"Q-SYS Core DSP, Dante audio networking, Shure Microflex, Riedel Bolero, RTS Adam, Clear-Com" },
|
|
{ cat:"LED & Display Processing", desc:"Brompton Tessera SX40 / S8, NovaStar MCTRL4K, Megapixel VR HELIOS, fine-pitch dvLED" },
|
|
{ cat:"IP Video & Cloud", desc:"SMPTE ST 2110-20/30/40, PTP IEEE 1588 grandmaster timing, NMOS IS-04/05, NDI 6, SRT, LiveU" },
|
|
{ cat:"Control & Automation", desc:"Ross DashBoard custom panels, Q-SYS UCI automation, Crestron / Extron, Bitfocus Companion" },
|
|
];
|
|
const GEAR = [
|
|
{ icon:Tv, cat:"Switchers & Vision", items:["Ross Carbonite Ultra 24-input 4K","vMix Pro 4K 16-input production rig","Blackmagic ATEM 4 M/E Constellation","Stream Deck XL & Companion integration"] },
|
|
{ icon:Server, cat:"Infrastructure & Routing", items:["Ross Ultrix 64x64 12G router matrix","SMPTE ST 2110 IP gateways & PTP timing","THOR Broadcast fiber TX/RX optical links","AJA & Decimator cross-converters"] },
|
|
{ icon:Cpu, cat:"Audio & Processing", items:["Q-SYS Core 110f & 8-Flex DSPs","Dante Virtual Soundcard & Yamaha consoles","Shure MXA920 ceiling array microphones","Riedel Bolero wireless intercom"] },
|
|
{ icon:Cable, cat:"Cameras & Acquisition", items:["RED Komodo-X 6K & Arri Trinity","Sony FX9 / FX6 4K production chains","Panasonic AW-UE150 4K NDI PTZ cameras","LiveU LU600 / LU800 bonded cellular units"] },
|
|
];
|
|
function TechStack() {
|
|
return (
|
|
<section className="tech">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="tech-head">
|
|
<div className="section-rule"></div>
|
|
<p className="eyebrow">// Hardware & Ecosystem</p>
|
|
<h2 className="section-title">Broadcast-grade hardware.<br/>Enterprise reliability.</h2>
|
|
<p className="lede">Systems specified with tier-one broadcast hardware and IT-standard networking designed for 24/7 continuous operation.</p>
|
|
</div>
|
|
</Reveal>
|
|
<div className="stack-grid">
|
|
{STACK.map((s, i) => (
|
|
<Reveal key={s.cat} delay={i * 60}>
|
|
<div className="stack-card">
|
|
<span className="stack-num">{`0${i + 1}`}</span>
|
|
<h3>{s.cat}</h3>
|
|
<p>{s.desc}</p>
|
|
</div>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
<div style={{marginTop:"5rem"}}>
|
|
<Reveal><p className="eyebrow">// Core Engineering Kit & Platforms</p></Reveal>
|
|
<div className="gear-grid">
|
|
{GEAR.map((g, i) => (
|
|
<Reveal key={g.cat} delay={i * 80}>
|
|
<div className="gear-card">
|
|
<div className="cap-icon" style={{marginBottom:"1rem"}}><g.icon size={20} sw={1.5}/></div>
|
|
<h3>{g.cat}</h3>
|
|
<ul>{g.items.map(it => <li key={it}>{it}</li>)}</ul>
|
|
</div>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── ON SET ──────────────────────────────────────────────────────────────────
|
|
const ON_SET_IMAGES = [
|
|
{ src:__R("images/photos/dit-setup-wide.jpg"), title:"DIT Cart Configuration", subtitle:"Digital Imaging Technician — On-set color grading & live data management" },
|
|
{ src:__R("images/photos/trinity-operator.jpg"), title:"Arri Trinity Operation", subtitle:"Certified Trinity Operator — High-dynamic camera stabilization" },
|
|
{ src:__R("images/photos/control-room-monitors.jpg"), title:"Multi-View Monitoring", subtitle:"Broadcast Master Control Room — 16-channel multiviewer layout" },
|
|
{ src:__R("images/photos/audio-mixing-console.jpg"), title:"Broadcast Audio Console", subtitle:"Multi-channel audio mixing & Dante network routing" },
|
|
{ src:__R("images/photos/rack-cable-management.jpg"), title:"Structured Cabling & Rack Build", subtitle:"Precision wiring, color-coded termination, and signal-path labeling" },
|
|
{ src:__R("images/photos/stage-production-lighting.jpg"), title:"Studio Production Stage", subtitle:"Live multi-camera broadcast production & XR virtual set" },
|
|
];
|
|
function OnSet() {
|
|
const [active, setActive] = useState(null);
|
|
return (
|
|
<section id="on-set" className="on-set">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="on-set-head">
|
|
<div className="section-rule"></div>
|
|
<p className="eyebrow">// Field & Production Work</p>
|
|
<h2 className="section-title">In the field. On the air.</h2>
|
|
<p className="lede">Real-world experience operating the systems we design — ensuring builds reflect the exact demands of live production crews.</p>
|
|
</div>
|
|
</Reveal>
|
|
<div className="on-set-grid">
|
|
{ON_SET_IMAGES.map((img, i) => (
|
|
<Reveal key={img.title} delay={i * 60}>
|
|
<div className="on-set-card" onClick={() => setActive(img)}>
|
|
<img src={img.src} alt={img.title} loading="lazy" />
|
|
<div className="on-set-card-overlay">
|
|
<div className="on-set-card-text">
|
|
<span className="cat">// Field Work</span>
|
|
<h4>{img.title}</h4>
|
|
<p>{img.subtitle}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
</div>
|
|
{active && <Lightbox image={active} onClose={() => setActive(null)} />}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── CONTACT & SCOPE SELECTOR ────────────────────────────────────────────────
|
|
const SCOPES = [
|
|
"Broadcast Facility / Control Room",
|
|
"Commercial Boardroom / Enterprise AV",
|
|
"SMPTE ST 2110 IP Migration",
|
|
"Direct-View LED Video Wall / XR Stage",
|
|
"System Commissioning & Engineering Audit"
|
|
];
|
|
function Contact() {
|
|
const [selectedScope, setSelectedScope] = useState(SCOPES[0]);
|
|
const mailtoHref = `mailto:zgaetano@wilddragon.net?subject=${encodeURIComponent(`Project Scope Inquiry: ${selectedScope}`)}&body=${encodeURIComponent(`Hi Zachary,\n\nI would like to discuss a project regarding ${selectedScope}.\n\nLocation (DC/MD/VA/Other):\nTimeline / Target Date:\nProject Overview:\n\nBest regards,`)}`;
|
|
|
|
return (
|
|
<section id="contact" className="contact">
|
|
<div className="container">
|
|
<Reveal>
|
|
<div className="contact-head">
|
|
<div className="section-rule center"></div>
|
|
<p className="eyebrow center">// Project Scoping & Inquiries</p>
|
|
<h2 className="section-title">Let's engineer your next facility.</h2>
|
|
<p className="lede" style={{marginTop:"1.25rem"}}>
|
|
Whether you are planning a new broadcast studio, upgrading enterprise conference rooms, or commissioning an uncompressed IP video network, connect directly with our principal design engineer.
|
|
</p>
|
|
</div>
|
|
</Reveal>
|
|
|
|
{/* Interactive Scope Selection */}
|
|
<Reveal delay={60}>
|
|
<div className="scope-selector-card">
|
|
<div className="scope-header">
|
|
<span className="scope-prompt">// Select your project discipline:</span>
|
|
</div>
|
|
<div className="scope-pills">
|
|
{SCOPES.map(scope => (
|
|
<button
|
|
key={scope}
|
|
type="button"
|
|
className={`scope-pill ${selectedScope === scope ? "active" : ""}`}
|
|
onClick={() => setSelectedScope(scope)}>
|
|
{selectedScope === scope && <Check size={14} className="scope-check"/>}
|
|
{scope}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="scope-action-row">
|
|
<a href={mailtoHref} className="btn btn-primary scope-submit-btn">
|
|
Launch Inquiry for {selectedScope} <ArrowRight size={16}/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
|
|
<div className="contact-grid" style={{marginTop:"3rem"}}>
|
|
<Reveal delay={0}>
|
|
<a className="contact-card" href="mailto:zgaetano@wilddragon.net">
|
|
<div className="icon"><Mail size={16} sw={1.5}/></div>
|
|
<span className="label">Direct Engineering Email</span>
|
|
<span className="val">zgaetano@wilddragon.net</span>
|
|
</a>
|
|
</Reveal>
|
|
|
|
<Reveal delay={80}>
|
|
<a className="contact-card" href="https://www.linkedin.com/in/zachary-gaetano-05962386/" target="_blank" rel="noopener noreferrer">
|
|
<div className="icon"><Linkedin size={16} sw={1.5}/></div>
|
|
<span className="label">LinkedIn Profile</span>
|
|
<span className="val">Zachary Gaetano</span>
|
|
</a>
|
|
</Reveal>
|
|
|
|
<Reveal delay={160}>
|
|
<div className="contact-card">
|
|
<div className="icon"><MapPin size={16} sw={1.5}/></div>
|
|
<span className="label">Primary Service Region</span>
|
|
<span className="val">Washington DC · MD · VA (DMV)</span>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
|
|
{/* Google Business Profile Verified Trust Box */}
|
|
<Reveal delay={200}>
|
|
<div className="google-trust-box">
|
|
<div className="google-trust-content">
|
|
<div className="google-trust-rating">
|
|
<span className="star-rating">★★★★★</span>
|
|
<span className="score">5.0 Star Rating</span>
|
|
</div>
|
|
<h4>Google Verified Business Profile</h4>
|
|
<p>Wild Dragon is a verified broadcast systems integrator & commercial AV contractor serving the entire DMV corridor.</p>
|
|
</div>
|
|
<div className="google-trust-action">
|
|
<a href="mailto:zgaetano@wilddragon.net?subject=Wild%20Dragon%20Review%20or%20Inquiry" className="btn btn-ghost-dark">
|
|
Inquire or Leave a Review <ArrowUpRight size={14}/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
|
|
<Reveal delay={240}>
|
|
<div className="cta-banner grain">
|
|
<div className="cta-banner-grid"></div>
|
|
<div className="inner">
|
|
<h3>Ready to scope your facility build?</h3>
|
|
<p>Every facility receives full signal-flow drawings, documented wire runs, and end-to-end commissioning against a written acceptance standard.</p>
|
|
<a href={mailtoHref} className="btn btn-primary">Start a Consultation <ArrowRight/></a>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// ─── FOOTER ──────────────────────────────────────────────────────────────────
|
|
function Footer() {
|
|
const serviceLinks = [
|
|
{ l: "Commercial AV Installation", h: "/services/commercial-av-installation-washington-dc/" },
|
|
{ l: "Broadcast Systems Integration", h: "/services/broadcast-systems-integration/" },
|
|
{ l: "LED Video Wall & XR Stages", h: "/services/led-video-wall-installation/" },
|
|
{ l: "Conference Room AV Systems", h: "/services/conference-room-av/" },
|
|
];
|
|
const corridorLinks = [
|
|
{ l: "Washington, DC", h: "/washington-dc/" },
|
|
{ l: "Northern Virginia (Tysons/Reston)", h: "/northern-virginia/" },
|
|
{ l: "Maryland (Bethesda/Rockville)", h: "/maryland/" },
|
|
{ l: "Broadcast Integration DMV", h: "/broadcast-integration-dmv/" },
|
|
{ l: "Commercial AV DMV", h: "/av-installation-dmv/" },
|
|
];
|
|
const projectLinks = [
|
|
{ l: "Washington Commanders Stadium", h: "/projects/washington-commanders/" },
|
|
{ l: "BetMGM Cloud Production", h: "/projects/betmgm-cloud-production/" },
|
|
{ l: "CVS / Aetna Broadcast Facility", h: "/projects/cvs-aetna-broadcast-facility/" },
|
|
{ l: "UBS Broadcast Center", h: "/projects/ubs-broadcast-center/" },
|
|
{ l: "All Case Studies", h: "/projects/" },
|
|
];
|
|
|
|
return (
|
|
<footer className="footer">
|
|
<div className="container">
|
|
<div className="footer-top">
|
|
<div className="footer-brand-col">
|
|
<div className="footer-brand">
|
|
<img src={__R("images/dragon-mark.png")} alt="Wild Dragon" width="30" height="30" />
|
|
<span>Wild Dragon</span>
|
|
</div>
|
|
<p className="about-line">
|
|
Broadcast systems design, commercial AV installation, and SMPTE ST 2110 commissioning across Washington, DC, Maryland, and Northern Virginia.
|
|
</p>
|
|
<div className="footer-gbp-tag">
|
|
<span className="g-dot">●</span>
|
|
<span>Google Verified Business · DMV Region</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="footer-col">
|
|
<p className="footer-title">Capabilities</p>
|
|
<ul>
|
|
{serviceLinks.map(s => <li key={s.h}><a href={s.h}>{s.l}</a></li>)}
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="footer-col">
|
|
<p className="footer-title">Regional Corridors</p>
|
|
<ul>
|
|
{corridorLinks.map(s => <li key={s.h}><a href={s.h}>{s.l}</a></li>)}
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="footer-col">
|
|
<p className="footer-title">Case Studies</p>
|
|
<ul>
|
|
{projectLinks.map(s => <li key={s.h}><a href={s.h}>{s.l}</a></li>)}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="footer-bottom">
|
|
<p>© {new Date().getFullYear()} Wild Dragon LLC · Zachary Gaetano. All rights reserved.</p>
|
|
<div className="footer-legal-links">
|
|
<a href="/llms.txt">LLMs / AI Spec</a>
|
|
<a href="/sitemap.xml">Sitemap</a>
|
|
<a href="mailto:zgaetano@wilddragon.net">zgaetano@wilddragon.net</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
);
|
|
}
|
|
|
|
// ─── PROJECT OVERLAY (CASE STUDY) ────────────────────────────────────────────
|
|
function ProjectOverlay({ slug, onClose, onOpen }) {
|
|
const list = window.PROJECTS || [];
|
|
const project = list.find(p => p.slug === slug);
|
|
const nextProject = list.find((p, i) => list[(i - 1 + list.length) % list.length].slug === slug);
|
|
|
|
useEffect(() => {
|
|
const onKey = (e) => { if (e.key === "Escape") onClose(); };
|
|
window.addEventListener("keydown", onKey);
|
|
document.body.style.overflow = "hidden";
|
|
return () => {
|
|
window.removeEventListener("keydown", onKey);
|
|
document.body.style.overflow = "";
|
|
};
|
|
}, [onClose]);
|
|
|
|
if (!project) return null;
|
|
|
|
const lede = project.description && project.description[0];
|
|
const restBody = (project.description || []).slice(1);
|
|
|
|
return (
|
|
<div className="po-mask" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
|
|
<button className="po-close" onClick={onClose} aria-label="Close case study"><X size={20}/></button>
|
|
<article className="po">
|
|
<div className="po-nav">
|
|
<div className="po-nav-inner">
|
|
<a href="#projects" className="po-back" onClick={(e) => { e.preventDefault(); onClose(); }}>
|
|
<ArrowLeft size={14}/> Back to projects
|
|
</a>
|
|
<span className="po-nav-title">{project.client}</span>
|
|
<div className="po-nav-cat">{project.category}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<header className="po-hero grain">
|
|
<div className="container">
|
|
<div className="po-hero-meta">
|
|
<span className="badge">{project.category}</span>
|
|
<span className="yr">{project.year}</span>
|
|
</div>
|
|
<h1 className="po-hero-title">{project.title || project.client}</h1>
|
|
<p className="po-hero-sub">{project.summary}</p>
|
|
<div className="po-hero-divider"></div>
|
|
</div>
|
|
</header>
|
|
|
|
<section className="po-facts">
|
|
<div className="container">
|
|
<div className="po-facts-grid">
|
|
<div><span className="lbl">Client</span><span className="val">{project.client}</span></div>
|
|
<div><span className="lbl">Category</span><span className="val">{project.category}</span></div>
|
|
<div><span className="lbl">Timeline</span><span className="val">{project.year}</span></div>
|
|
<div><span className="lbl">Scope</span><span className="val">{(project.scope && project.scope[0]) || "Systems Design & Commissioning"}</span></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{lede && (
|
|
<section className="po-section po-opening">
|
|
<div className="container po-opening-grid">
|
|
<div className="po-opening-label">
|
|
<span className="num">01</span>
|
|
<span className="bar"></span>
|
|
<span>Overview</span>
|
|
</div>
|
|
<p className="po-opening-text">{lede}</p>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
<div className="po-feature">
|
|
<div className="po-feature-inner">
|
|
<img src={project.thumbnail} alt={`${project.client} — ${project.category}`}/>
|
|
<div className="po-feature-cap">
|
|
<span>{project.client} · {project.category}</span>
|
|
<span>{project.year}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<section className="po-section po-body">
|
|
<div className="container po-body-grid">
|
|
<div className="po-body-main">
|
|
<div className="po-body-label">
|
|
<span className="num">02</span>
|
|
<span className="bar"></span>
|
|
<span>Architecture</span>
|
|
</div>
|
|
{restBody.map((p, i) => <p key={i}>{p}</p>)}
|
|
</div>
|
|
<aside className="po-sidecar">
|
|
{project.scope && (
|
|
<div className="po-card">
|
|
<h4>Engineering Scope</h4>
|
|
<ul>{project.scope.map(s => <li key={s}><Check size={14}/><span>{s}</span></li>)}</ul>
|
|
</div>
|
|
)}
|
|
{project.highlights && (
|
|
<div className="po-card">
|
|
<h4>Key Deliverables</h4>
|
|
<ul>{project.highlights.map(h => <li key={h}><Check size={14}/><span>{h}</span></li>)}</ul>
|
|
</div>
|
|
)}
|
|
</aside>
|
|
</div>
|
|
</section>
|
|
|
|
{project.quote && (
|
|
<section className="po-quote grain">
|
|
<div className="container">
|
|
<blockquote>“{project.quote.text}”</blockquote>
|
|
<cite>— {project.quote.author}, {project.quote.role}</cite>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
<footer className="po-foot">
|
|
<div className="container po-foot-inner">
|
|
<a href="#projects" className="po-back-btn" onClick={(e) => { e.preventDefault(); onClose(); }}>
|
|
<ArrowLeft size={16}/> Back to All Projects
|
|
</a>
|
|
{nextProject && (
|
|
<button type="button" className="po-next-btn" onClick={() => onOpen(nextProject.slug)}>
|
|
Next Case Study: {nextProject.client} <ArrowRight size={16}/>
|
|
</button>
|
|
)}
|
|
</div>
|
|
</footer>
|
|
</article>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ─── LIGHTBOX ─────────────────────────────────────────────────────────────────
|
|
function Lightbox({ image, onClose }) {
|
|
useEffect(() => {
|
|
const onKey = (e) => { if (e.key === "Escape") onClose(); };
|
|
window.addEventListener("keydown", onKey);
|
|
return () => window.removeEventListener("keydown", onKey);
|
|
}, [onClose]);
|
|
return (
|
|
<div className="lightbox-mask" onClick={onClose}>
|
|
<button className="lightbox-close" onClick={onClose} aria-label="Close lightbox"><X size={24}/></button>
|
|
<div className="lightbox-content" onClick={(e) => e.stopPropagation()}>
|
|
<img src={image.src} alt={image.title}/>
|
|
<div className="lightbox-cap">
|
|
<h4>{image.title}</h4>
|
|
<p>{image.subtitle}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ─── VIDEO MODAL ─────────────────────────────────────────────────────────────
|
|
function VideoModal({ videoId, onClose }) {
|
|
useEffect(() => {
|
|
const onKey = (e) => { if (e.key === "Escape") onClose(); };
|
|
window.addEventListener("keydown", onKey);
|
|
return () => window.removeEventListener("keydown", onKey);
|
|
}, [onClose]);
|
|
return (
|
|
<div className="lightbox-mask" onClick={onClose}>
|
|
<button className="lightbox-close" onClick={onClose} aria-label="Close video"><X size={24}/></button>
|
|
<div className="video-modal-content" onClick={(e) => e.stopPropagation()}>
|
|
<iframe
|
|
src={`https://player.vimeo.com/video/${videoId}?autoplay=1&title=0&byline=0&portrait=0`}
|
|
frameBorder="0"
|
|
allow="autoplay; fullscreen; picture-in-picture"
|
|
allowFullScreen
|
|
title="Video Player"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|