Sessions 5-7a: 955 tests, deployment ready
@@ -0,0 +1,4 @@
|
||||
Contact: mailto:security@vyndr.xyz
|
||||
Expires: 2027-05-28T00:00:00.000Z
|
||||
Preferred-Languages: en
|
||||
Canonical: https://vyndr.xyz/.well-known/security.txt
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 722 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 161 B |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="VYNDR">
|
||||
<defs>
|
||||
<linearGradient id="vy" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#00FFB8"/>
|
||||
<stop offset="100%" stop-color="#00D4A0"/>
|
||||
</linearGradient>
|
||||
<filter id="glow" x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur stdDeviation="1.6"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="64" height="64" rx="12" fill="#06060B"/>
|
||||
<g stroke="url(#vy)" stroke-linecap="round" fill="none">
|
||||
<g filter="url(#glow)" opacity="0.55">
|
||||
<line x1="14" y1="14" x2="32" y2="34" stroke-width="6"/>
|
||||
<line x1="32" y1="34" x2="32" y2="52" stroke-width="6"/>
|
||||
<line x1="6" y1="58" x2="58" y2="6" stroke-width="6"/>
|
||||
</g>
|
||||
<line x1="14" y1="14" x2="32" y2="34" stroke-width="6"/>
|
||||
<line x1="32" y1="34" x2="32" y2="52" stroke-width="6"/>
|
||||
<line x1="6" y1="58" x2="58" y2="6" stroke-width="5.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 965 B |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 59 KiB |
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "VYNDR",
|
||||
"short_name": "VYNDR",
|
||||
"description": "Grade your props with intelligence the books don't want you to have.",
|
||||
"start_url": "/dashboard",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#06060B",
|
||||
"theme_color": "#06060B",
|
||||
"orientation": "portrait-primary",
|
||||
"icons": [
|
||||
{ "src": "/favicon.svg", "sizes": "any", "type": "image/svg+xml" },
|
||||
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
||||
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
|
||||
{ "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0E0E16"/>
|
||||
<stop offset="100%" stop-color="#06060B"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="glow" cx="50%" cy="40%" r="60%">
|
||||
<stop offset="0%" stop-color="rgba(0,212,160,0.16)"/>
|
||||
<stop offset="100%" stop-color="rgba(0,212,160,0)"/>
|
||||
</radialGradient>
|
||||
<pattern id="scan" width="4" height="4" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="1200" y2="0" stroke="rgba(255,255,255,0.04)" stroke-width="1"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="1200" height="630" fill="url(#bg)"/>
|
||||
<rect width="1200" height="630" fill="url(#glow)"/>
|
||||
<rect width="1200" height="630" fill="url(#scan)"/>
|
||||
|
||||
<!-- Wordmark -->
|
||||
<g font-family="'IBM Plex Mono','JetBrains Mono',monospace" font-weight="800" letter-spacing="8">
|
||||
<text x="600" y="280" text-anchor="middle" font-size="140" fill="#E8E8F0">VYND</text>
|
||||
<text x="780" y="280" text-anchor="middle" font-size="140" fill="#00D4A0" style="filter:drop-shadow(0 0 24px rgba(0,212,160,0.6))">R</text>
|
||||
</g>
|
||||
|
||||
<!-- Tagline -->
|
||||
<text x="600" y="360" text-anchor="middle" font-family="'Instrument Sans',system-ui,sans-serif" font-size="28" font-weight="600" fill="#E8E8F0">
|
||||
The books have every advantage.
|
||||
</text>
|
||||
<text x="600" y="400" text-anchor="middle" font-family="'Instrument Sans',system-ui,sans-serif" font-size="28" font-weight="600" fill="#00D4A0">
|
||||
We built this to give it back.
|
||||
</text>
|
||||
|
||||
<!-- Example A- grade card -->
|
||||
<g transform="translate(490 460)">
|
||||
<rect width="220" height="110" rx="12" fill="#0E0E16" stroke="#1E1E2E"/>
|
||||
<text x="20" y="35" font-family="'IBM Plex Mono',monospace" font-size="11" letter-spacing="2" fill="#7A7A8E">NBA · JOKIC · PTS</text>
|
||||
<text x="200" y="90" text-anchor="end" font-family="'IBM Plex Mono',monospace" font-weight="800" font-size="64" fill="#00D4A0" style="filter:drop-shadow(0 0 16px rgba(0,212,160,0.7))">A-</text>
|
||||
</g>
|
||||
|
||||
<!-- Built in Detroit stamp -->
|
||||
<text x="600" y="600" text-anchor="middle" font-family="'IBM Plex Mono',monospace" font-size="14" letter-spacing="6" fill="#4A4A5E">
|
||||
BUILT IN DETROIT · VYNDR.XYZ
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,158 @@
|
||||
/* VYNDR embeddable widget — single self-contained file, ~3.5 KB minified.
|
||||
*
|
||||
* <script src="https://vyndr.app/widget.js" data-sport="nba" data-theme="dark"></script>
|
||||
*
|
||||
* Mounts a 300px card at the script tag's location, refreshes every 30
|
||||
* minutes, links each grade to https://vyndr.app (new tab). Two themes:
|
||||
* dark (default), light.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var SCRIPT = document.currentScript;
|
||||
if (!SCRIPT) {
|
||||
var all = document.getElementsByTagName('script');
|
||||
for (var i = all.length - 1; i >= 0; i--) {
|
||||
if (/widget\.js(\?|$)/.test(all[i].src)) { SCRIPT = all[i]; break; }
|
||||
}
|
||||
}
|
||||
if (!SCRIPT) return;
|
||||
|
||||
var SPORT = (SCRIPT.getAttribute('data-sport') || 'nba').toLowerCase();
|
||||
var THEME = (SCRIPT.getAttribute('data-theme') || 'dark').toLowerCase();
|
||||
if (['nba', 'wnba', 'mlb'].indexOf(SPORT) < 0) SPORT = 'nba';
|
||||
if (['dark', 'light'].indexOf(THEME) < 0) THEME = 'dark';
|
||||
|
||||
var API = (SCRIPT.getAttribute('data-api') || 'https://api.vyndr.app') + '/api/widget';
|
||||
var REFRESH_MS = 30 * 60 * 1000;
|
||||
|
||||
// ── styles ─────────────────────────────────────────────────────────────
|
||||
var PALETTE = THEME === 'light' ? {
|
||||
bg: '#FFFFFF', border: '#E5E5EC', text: '#0E0E16',
|
||||
textDim: '#4A4A5E', accent: '#0F3D2E',
|
||||
} : {
|
||||
bg: '#06060B', border: '#1E1E2E', text: '#E8E8F0',
|
||||
textDim: '#7A7A8E', accent: '#00D4A0',
|
||||
};
|
||||
var GRADE_COLORS = {
|
||||
'A+': '#00FFB8', 'A': '#00D4A0', 'A-': '#00D4A0',
|
||||
'B+': '#4A9EFF', 'B': '#4A9EFF', 'B-': '#4A9EFF',
|
||||
'C+': '#FFB347', 'C': '#FFB347', 'C-': '#FFB347',
|
||||
'D': '#FF5252', 'F': '#FF5252',
|
||||
};
|
||||
|
||||
function el(tag, css, text) {
|
||||
var n = document.createElement(tag);
|
||||
if (css) n.style.cssText = css;
|
||||
if (text != null) n.textContent = text;
|
||||
return n;
|
||||
}
|
||||
|
||||
function gradeColor(g) { return GRADE_COLORS[g] || PALETTE.textDim; }
|
||||
|
||||
// ── mount ─────────────────────────────────────────────────────────────
|
||||
var host = el('div', [
|
||||
'box-sizing:border-box',
|
||||
'width:300px',
|
||||
'background:' + PALETTE.bg,
|
||||
'color:' + PALETTE.text,
|
||||
'border:1px solid ' + PALETTE.border,
|
||||
'border-radius:14px',
|
||||
'padding:16px',
|
||||
'font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",sans-serif',
|
||||
'font-size:14px',
|
||||
'line-height:1.4',
|
||||
].join(';') + ';');
|
||||
host.setAttribute('data-vyndr-widget', SPORT);
|
||||
|
||||
var header = el('div', 'display:flex;justify-content:space-between;align-items:baseline;margin-bottom:10px;');
|
||||
var brand = el('span', 'font-family:"IBM Plex Mono","JetBrains Mono","SF Mono",ui-monospace,monospace;font-weight:800;letter-spacing:2px;color:' + PALETTE.text + ';');
|
||||
brand.appendChild(document.createTextNode('VYND'));
|
||||
var r = el('span', 'color:' + PALETTE.accent + ';text-shadow:0 0 6px rgba(0,212,160,.45);', 'R');
|
||||
brand.appendChild(r);
|
||||
header.appendChild(brand);
|
||||
header.appendChild(el('span', 'font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:2px;color:' + PALETTE.textDim + ';', 'TOP ' + SPORT.toUpperCase()));
|
||||
host.appendChild(header);
|
||||
|
||||
var list = el('div', '');
|
||||
host.appendChild(list);
|
||||
|
||||
var footer = el('div', 'margin-top:12px;font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:2px;color:' + PALETTE.textDim + ';text-align:center;');
|
||||
var foot = el('a', 'color:inherit;text-decoration:none;');
|
||||
foot.href = 'https://vyndr.app';
|
||||
foot.target = '_blank';
|
||||
foot.rel = 'noopener noreferrer';
|
||||
foot.textContent = 'Powered by VYNDR · vyndr.app';
|
||||
footer.appendChild(foot);
|
||||
host.appendChild(footer);
|
||||
|
||||
if (SCRIPT.parentNode) SCRIPT.parentNode.insertBefore(host, SCRIPT);
|
||||
|
||||
// ── render ────────────────────────────────────────────────────────────
|
||||
function row(g) {
|
||||
var a = el('a', [
|
||||
'display:flex',
|
||||
'justify-content:space-between',
|
||||
'align-items:center',
|
||||
'padding:10px 12px',
|
||||
'margin-bottom:6px',
|
||||
'border-radius:8px',
|
||||
'text-decoration:none',
|
||||
'color:' + PALETTE.text,
|
||||
'border:1px solid ' + PALETTE.border,
|
||||
'background:' + (THEME === 'light' ? '#F8F8FC' : '#0E0E16'),
|
||||
].join(';') + ';');
|
||||
a.href = 'https://vyndr.app';
|
||||
a.target = '_blank';
|
||||
a.rel = 'noopener noreferrer';
|
||||
|
||||
var left = el('div', 'min-width:0;');
|
||||
var name = el('div', 'font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;', String(g.player || ''));
|
||||
var sub = el('div', 'font-family:"IBM Plex Mono",monospace;font-size:11px;color:' + PALETTE.textDim + ';',
|
||||
[g.stat || '', String(g.direction || '').toUpperCase(), g.line ?? ''].join(' '));
|
||||
left.appendChild(name);
|
||||
left.appendChild(sub);
|
||||
|
||||
var grade = el('div', [
|
||||
'font-family:"IBM Plex Mono",monospace',
|
||||
'font-weight:800',
|
||||
'font-size:22px',
|
||||
'margin-left:8px',
|
||||
'color:' + gradeColor(g.grade),
|
||||
'text-shadow:0 0 6px ' + gradeColor(g.grade) + '55',
|
||||
].join(';') + ';', String(g.grade || ''));
|
||||
|
||||
a.appendChild(left);
|
||||
a.appendChild(grade);
|
||||
return a;
|
||||
}
|
||||
|
||||
function setError(msg) {
|
||||
list.innerHTML = '';
|
||||
var p = el('div', 'padding:10px 0;color:' + PALETTE.textDim + ';font-size:12px;', msg);
|
||||
list.appendChild(p);
|
||||
}
|
||||
|
||||
function fetchAndRender() {
|
||||
var url = API + '?sport=' + encodeURIComponent(SPORT) + '&_=' + Date.now();
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.timeout = 8000;
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState !== 4) return;
|
||||
if (xhr.status < 200 || xhr.status >= 300) return setError('Signal unavailable. Try later.');
|
||||
try {
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
list.innerHTML = '';
|
||||
var props = (data && data.props) || [];
|
||||
if (!props.length) return setError('No grades on the slate yet.');
|
||||
for (var i = 0; i < props.length; i++) list.appendChild(row(props[i]));
|
||||
} catch (e) { setError('Could not parse response.'); }
|
||||
};
|
||||
xhr.ontimeout = function () { setError('Signal timed out.'); };
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
fetchAndRender();
|
||||
setInterval(fetchAndRender, REFRESH_MS);
|
||||
})();
|
||||