Add weapon library dropdown and restyle UI

This commit is contained in:
2026-01-14 20:25:27 +01:00
parent b960f4da3b
commit bfe59fcb39
5 changed files with 447 additions and 428 deletions
+79
View File
@@ -1,4 +1,5 @@
const form = document.getElementById('af-calculator'); const form = document.getElementById('af-calculator');
const weaponSelect = document.getElementById('af-weapon-select');
const outputs = { const outputs = {
shotDamage: document.querySelector('[data-output="shotDamage"]'), shotDamage: document.querySelector('[data-output="shotDamage"]'),
burstDps: document.querySelector('[data-output="burstDps"]'), burstDps: document.querySelector('[data-output="burstDps"]'),
@@ -10,6 +11,7 @@ const outputs = {
const resetButton = document.getElementById('af-reset'); const resetButton = document.getElementById('af-reset');
const copyButton = document.getElementById('af-copy'); const copyButton = document.getElementById('af-copy');
const weaponsById = new Map();
const numberOrNull = (value) => { const numberOrNull = (value) => {
const parsed = Number.parseFloat(value); const parsed = Number.parseFloat(value);
@@ -64,6 +66,72 @@ const calculate = () => {
outputs.ttk.textContent = formatValue(ttk, 's'); outputs.ttk.textContent = formatValue(ttk, 's');
}; };
const setFormValue = (name, value) => {
const field = form.elements[name];
if (!field) {
return;
}
field.value = value ?? '';
};
const applyWeapon = (weapon) => {
if (!weapon) {
return;
}
setFormValue('weaponName', weapon.name ?? '');
setFormValue('baseDamage', weapon.damage ?? '');
setFormValue('projectiles', weapon.damage_multiplier ?? 1);
setFormValue('rpm', weapon.rpm ?? '');
setFormValue('magazine', weapon.mag_size ?? '');
setFormValue('reload', weapon.reload_time ?? '');
calculate();
};
const loadWeaponLibrary = async () => {
if (!weaponSelect) {
return;
}
const source = weaponSelect.dataset.source;
if (!source) {
return;
}
try {
const response = await fetch(source);
if (!response.ok) {
throw new Error('Failed to load weapons');
}
const weapons = await response.json();
const normalized = weapons
.filter((weapon) => weapon && weapon.name)
.map((weapon) => ({
id: weapon.id ?? weapon.name,
name: weapon.name,
damage: weapon.damage,
damage_multiplier: weapon.damage_multiplier ?? 1,
rpm: weapon.rpm,
mag_size: weapon.mag_size,
reload_time: weapon.reload_time
}))
.sort((a, b) => a.name.localeCompare(b.name));
normalized.forEach((weapon) => {
const key = String(weapon.id);
weaponsById.set(key, weapon);
const option = document.createElement('option');
option.value = key;
option.textContent = weapon.name;
weaponSelect.appendChild(option);
});
} catch (error) {
console.error('Weapon library load failed', error);
const option = document.createElement('option');
option.textContent = 'Weapon library unavailable';
option.disabled = true;
weaponSelect.appendChild(option);
}
};
form.addEventListener('input', calculate); form.addEventListener('input', calculate);
resetButton.addEventListener('click', () => { resetButton.addEventListener('click', () => {
@@ -71,6 +139,16 @@ resetButton.addEventListener('click', () => {
calculate(); calculate();
}); });
if (weaponSelect) {
weaponSelect.addEventListener('change', (event) => {
const selected = weaponsById.get(event.target.value);
if (!selected) {
return;
}
applyWeapon(selected);
});
}
copyButton.addEventListener('click', async () => { copyButton.addEventListener('click', async () => {
const data = new FormData(form); const data = new FormData(form);
const name = data.get('weaponName') || 'Unnamed loadout'; const name = data.get('weaponName') || 'Unnamed loadout';
@@ -100,3 +178,4 @@ copyButton.addEventListener('click', async () => {
}); });
calculate(); calculate();
loadWeaponLibrary();
+347 -415
View File
@@ -518,6 +518,353 @@ body.menu-open {
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
} }
.af-body {
margin: 0;
min-height: 100vh;
background: radial-gradient(circle at top, #1e293b 0%, #0f172a 45%, #020617 100%);
color: #e2e8f0;
font-family: "Inter", "Segoe UI", system-ui, sans-serif;
}
.af-header {
position: sticky;
top: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
padding: 20px 32px;
background: rgba(15, 23, 42, 0.92);
border-bottom: 1px solid rgba(148, 163, 184, 0.2);
backdrop-filter: blur(12px);
}
.af-brand {
display: flex;
align-items: center;
gap: 16px;
}
.af-logo {
width: 44px;
height: 44px;
border-radius: 16px;
padding: 8px;
display: grid;
gap: 6px;
background: linear-gradient(135deg, rgba(56, 189, 248, 0.3), rgba(94, 234, 212, 0.1));
border: 1px solid rgba(56, 189, 248, 0.35);
}
.af-logo span {
display: block;
height: 4px;
border-radius: 999px;
background: linear-gradient(90deg, #38bdf8, #22c55e);
}
.af-title {
font-size: 1.2rem;
font-weight: 600;
margin: 0;
}
.af-subtitle {
margin: 4px 0 0;
color: #94a3b8;
font-size: 0.95rem;
}
.af-nav {
display: flex;
align-items: center;
gap: 12px;
}
.af-nav-link {
padding: 8px 16px;
border-radius: 999px;
text-decoration: none;
font-weight: 600;
color: #e2e8f0;
background: rgba(148, 163, 184, 0.12);
border: 1px solid transparent;
transition: all 0.2s ease;
}
.af-nav-link:hover {
border-color: rgba(56, 189, 248, 0.4);
background: rgba(56, 189, 248, 0.2);
color: #f8fafc;
}
.af-nav-toggle {
display: none;
padding: 8px 16px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.4);
background: transparent;
color: #e2e8f0;
font-weight: 600;
}
.af-shell {
max-width: 1200px;
margin: 32px auto 80px;
padding: 0 24px;
background: transparent;
box-shadow: none;
border: none;
}
.af-hero {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 24px;
margin-bottom: 32px;
}
.af-hero h1,
.af-card h1 {
color: #f8fafc;
margin-top: 0;
}
.af-hero p {
color: #cbd5f5;
line-height: 1.6;
}
.af-status {
display: grid;
gap: 16px;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 16px;
padding: 20px;
}
.af-status-label {
font-size: 0.75rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #94a3b8;
margin: 0;
}
.af-status-value {
margin: 4px 0 0;
font-weight: 600;
}
.af-grid {
display: grid;
gap: 24px;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
margin-bottom: 32px;
}
.af-card {
background: rgba(15, 23, 42, 0.75);
border-radius: 20px;
border: 1px solid rgba(148, 163, 184, 0.2);
padding: 24px;
box-shadow: 0 20px 45px rgba(15, 23, 42, 0.35);
}
.af-card-header h2 {
margin: 0 0 8px;
color: #f8fafc;
}
.af-card-header p {
margin: 0;
color: #cbd5f5;
line-height: 1.6;
}
.af-card-actions {
margin-top: 20px;
}
.af-cta {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
border-radius: 999px;
background: linear-gradient(120deg, #38bdf8, #22c55e);
color: #0f172a;
font-weight: 700;
text-decoration: none;
border: none;
cursor: pointer;
}
.af-cta:hover {
filter: brightness(1.05);
}
.af-form {
display: grid;
gap: 16px;
margin-top: 16px;
}
.af-form label {
display: flex;
flex-direction: column;
gap: 8px;
font-weight: 600;
color: #e2e8f0;
}
.af-form input,
.af-form select,
.af-form textarea,
.af-input {
padding: 12px 14px;
border-radius: 12px;
border: 1px solid rgba(148, 163, 184, 0.3);
background: rgba(15, 23, 42, 0.7);
color: #f8fafc;
font-size: 0.95rem;
}
.af-form input:focus,
.af-form select:focus,
.af-form textarea:focus,
.af-input:focus {
outline: 2px solid rgba(56, 189, 248, 0.6);
border-color: rgba(56, 189, 248, 0.6);
}
.af-field-hint {
font-size: 0.85rem;
font-weight: 400;
color: #94a3b8;
}
.af-form-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 8px;
}
.af-form-actions button {
padding: 10px 18px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.3);
background: rgba(148, 163, 184, 0.12);
color: #e2e8f0;
cursor: pointer;
}
.af-form-actions button:hover {
background: rgba(56, 189, 248, 0.25);
border-color: rgba(56, 189, 248, 0.4);
}
.af-metrics {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
margin-top: 12px;
}
.af-metric {
padding: 16px;
border-radius: 16px;
background: rgba(2, 6, 23, 0.6);
border: 1px solid rgba(148, 163, 184, 0.2);
}
.af-metric-label {
margin: 0 0 6px;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #94a3b8;
}
.af-metric-value {
margin: 0;
font-size: 1.4rem;
font-weight: 700;
color: #f8fafc;
}
.af-callout {
margin-top: 20px;
padding: 16px;
border-radius: 16px;
background: rgba(56, 189, 248, 0.1);
border: 1px solid rgba(56, 189, 248, 0.3);
}
.af-callout h3 {
margin-top: 0;
}
.af-empty {
display: grid;
gap: 12px;
padding: 16px;
border-radius: 16px;
background: rgba(2, 6, 23, 0.6);
border: 1px dashed rgba(148, 163, 184, 0.3);
}
.af-muted-button {
padding: 8px 14px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.3);
background: transparent;
color: #e2e8f0;
}
.af-notes {
width: 100%;
min-height: 160px;
border-radius: 16px;
padding: 14px;
background: rgba(15, 23, 42, 0.7);
border: 1px solid rgba(148, 163, 184, 0.3);
color: #f8fafc;
}
.af-copy-grid {
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
margin-top: 20px;
}
@media (max-width: 820px) {
.af-header {
flex-wrap: wrap;
}
.af-nav {
display: none;
width: 100%;
flex-direction: column;
align-items: flex-start;
padding-top: 12px;
}
.af-nav.is-open {
display: flex;
}
.af-nav-toggle {
display: inline-flex;
margin-left: auto;
}
}
#weapon-form h2 { #weapon-form h2 {
margin-top: 0; margin-top: 0;
color: #222; color: #222;
@@ -1059,418 +1406,3 @@ th, td {
flex: 1; flex: 1;
} }
} }
/* --------------------------
Anomaly Front calculator
-------------------------- */
.af-body {
margin: 0;
padding: 32px;
}
.af-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
background: linear-gradient(135deg, rgba(15, 23, 42, 0.85), rgba(30, 41, 59, 0.6));
border: 1px solid rgba(148, 163, 184, 0.25);
border-radius: 24px;
padding: 20px 28px;
margin-bottom: 24px;
position: relative;
z-index: 2;
backdrop-filter: blur(12px);
}
.af-header::before {
content: "";
position: absolute;
inset: 0;
border-radius: 24px;
padding: 1px;
background: linear-gradient(120deg, rgba(56, 189, 248, 0.6), rgba(59, 130, 246, 0.2), rgba(14, 116, 144, 0.5));
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.af-brand {
display: flex;
align-items: center;
gap: 16px;
}
.af-logo {
width: 44px;
height: 44px;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 4px;
padding: 6px;
border-radius: 14px;
background: rgba(15, 23, 42, 0.8);
border: 1px solid rgba(148, 163, 184, 0.35);
box-shadow: 0 8px 20px rgba(2, 6, 23, 0.45);
}
.af-logo span {
border-radius: 6px;
background: linear-gradient(135deg, rgba(56, 189, 248, 0.9), rgba(59, 130, 246, 0.6));
}
.af-logo span:nth-child(2) {
background: linear-gradient(135deg, rgba(14, 116, 144, 0.9), rgba(2, 132, 199, 0.6));
}
.af-logo span:nth-child(3) {
background: linear-gradient(135deg, rgba(37, 99, 235, 0.9), rgba(15, 23, 42, 0.6));
}
.af-title {
font-size: 1.5rem;
margin: 0;
color: #f8fafc;
font-weight: 600;
}
.af-subtitle {
margin: 4px 0 0;
color: #cbd5f5;
font-size: 0.95rem;
}
.af-nav-toggle {
display: none;
align-items: center;
gap: 8px;
padding: 8px 14px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(148, 163, 184, 0.35);
color: #e2e8f0;
font-weight: 600;
cursor: pointer;
z-index: 1;
}
.af-nav {
display: flex;
gap: 16px;
flex-wrap: wrap;
z-index: 1;
}
.af-nav-link {
position: relative;
color: #e2e8f0;
font-weight: 500;
padding: 8px 16px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.3);
background: rgba(15, 23, 42, 0.4);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.af-nav-link::after {
content: "";
position: absolute;
left: 16px;
right: 16px;
bottom: 6px;
height: 2px;
border-radius: 999px;
background: linear-gradient(90deg, rgba(56, 189, 248, 0), rgba(56, 189, 248, 0.9), rgba(56, 189, 248, 0));
opacity: 0;
transition: opacity 0.2s ease;
}
.af-nav-link:hover {
color: #fff;
background: rgba(59, 130, 246, 0.3);
box-shadow: 0 10px 20px rgba(14, 116, 144, 0.2);
transform: translateY(-1px);
text-decoration: none;
}
.af-nav-link:hover::after {
opacity: 1;
}
.af-shell {
display: flex;
flex-direction: column;
gap: 24px;
position: relative;
z-index: 2;
}
.af-hero {
display: flex;
flex-wrap: wrap;
gap: 24px;
align-items: center;
justify-content: space-between;
background: rgba(15, 23, 42, 0.65);
border-radius: 24px;
padding: 28px;
border: 1px solid rgba(148, 163, 184, 0.2);
box-shadow: 0 20px 40px rgba(2, 6, 23, 0.4);
}
.af-hero h1 {
margin-top: 0;
color: #f8fafc;
}
.af-hero p {
color: #cbd5f5;
max-width: 560px;
}
.af-status {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 16px;
width: min(520px, 100%);
}
.af-status-label {
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.08em;
color: #94a3b8;
margin-bottom: 6px;
}
.af-status-value {
margin: 0;
font-weight: 600;
color: #e2e8f0;
}
.af-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
}
.af-card {
background: rgba(15, 23, 42, 0.65);
border-radius: 20px;
padding: 24px;
border: 1px solid rgba(148, 163, 184, 0.2);
box-shadow: 0 16px 30px rgba(2, 6, 23, 0.35);
}
.af-card-actions {
margin-top: 18px;
}
.af-cta {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 18px;
border-radius: 999px;
background: linear-gradient(120deg, rgba(56, 189, 248, 0.9), rgba(59, 130, 246, 0.85));
color: #f8fafc;
font-weight: 600;
border: 1px solid rgba(56, 189, 248, 0.6);
box-shadow: 0 12px 24px rgba(14, 116, 144, 0.35);
}
.af-cta:hover {
color: #fff;
text-decoration: none;
transform: translateY(-1px);
}
.af-card-header h2 {
margin-top: 0;
color: #f8fafc;
}
.af-card-header p {
color: #cbd5f5;
margin-bottom: 20px;
}
.af-form {
display: grid;
gap: 14px;
}
.af-form label {
display: grid;
gap: 6px;
color: #e2e8f0;
font-weight: 500;
}
.af-form input {
background: rgba(15, 23, 42, 0.8);
border: 1px solid rgba(148, 163, 184, 0.3);
color: #f8fafc;
}
.af-form input:focus,
.af-notes:focus,
.af-input:focus {
border-color: rgba(56, 189, 248, 0.7);
box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.35);
background: rgba(15, 23, 42, 0.92);
color: #f8fafc;
}
.af-field {
display: grid;
gap: 6px;
color: #e2e8f0;
font-weight: 500;
}
.af-input {
width: 100%;
padding: 12px 14px;
border-radius: 12px;
background: rgba(15, 23, 42, 0.8);
border: 1px solid rgba(148, 163, 184, 0.3);
color: #f8fafc;
}
.af-form-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-top: 6px;
}
.af-form-actions button {
background: rgba(59, 130, 246, 0.9);
color: #f8fafc;
border: none;
border-radius: 999px;
padding: 10px 18px;
font-weight: 600;
}
#af-reset {
background: rgba(148, 163, 184, 0.4);
}
.af-metrics {
display: grid;
gap: 16px;
}
.af-metric {
background: rgba(2, 6, 23, 0.35);
border-radius: 16px;
padding: 16px;
border: 1px solid rgba(148, 163, 184, 0.15);
}
.af-metric-label {
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.08em;
color: #94a3b8;
margin-bottom: 8px;
}
.af-metric-value {
margin: 0;
font-size: 1.4rem;
font-weight: 600;
color: #f8fafc;
}
.af-callout {
margin-top: 20px;
background: rgba(59, 130, 246, 0.15);
border-radius: 16px;
padding: 16px;
border: 1px solid rgba(59, 130, 246, 0.35);
}
.af-callout h3 {
margin-top: 0;
color: #e0f2fe;
}
.af-callout ul {
margin: 0;
padding-left: 18px;
color: #cbd5f5;
}
.af-empty {
display: grid;
gap: 12px;
color: #cbd5f5;
background: rgba(2, 6, 23, 0.3);
border-radius: 16px;
padding: 18px;
border: 1px dashed rgba(148, 163, 184, 0.3);
}
.af-muted-button {
background: rgba(148, 163, 184, 0.2);
border-radius: 999px;
padding: 10px 16px;
color: #e2e8f0;
border: none;
width: fit-content;
}
.af-notes {
width: 100%;
min-height: 180px;
border-radius: 16px;
padding: 16px;
background: rgba(15, 23, 42, 0.8);
border: 1px solid rgba(148, 163, 184, 0.3);
color: #f8fafc;
resize: vertical;
}
.af-copy-grid {
display: grid;
gap: 18px;
color: #cbd5f5;
}
.af-copy-grid ul {
padding-left: 18px;
margin: 8px 0 0;
}
@media (max-width: 720px) {
.af-body {
padding: 20px;
}
.af-header {
flex-direction: column;
align-items: flex-start;
}
.af-nav-toggle {
display: inline-flex;
}
.af-nav {
width: 100%;
flex-direction: column;
align-items: flex-start;
gap: 10px;
display: none;
}
.af-nav.is-open {
display: flex;
}
}
+5 -5
View File
@@ -34,8 +34,8 @@
<div class="af-card-header"> <div class="af-card-header">
<h1>About the Anomaly Front toolkit</h1> <h1>About the Anomaly Front toolkit</h1>
<p> <p>
This site is the staging ground for an unreleased experience. We are rebuilding every tool from the This site is the staging ground for the toolkit. We are rebuilding every tool from the ground up to
ground up to avoid legacy stats, old branding, or outdated data. avoid legacy stats, old branding, or outdated data.
</p> </p>
</div> </div>
<div class="af-copy-grid"> <div class="af-copy-grid">
@@ -43,7 +43,7 @@
<h2>Purpose</h2> <h2>Purpose</h2>
<p> <p>
Provide a shared workspace for balancing weapons, capturing loadout notes, and testing combat pacing Provide a shared workspace for balancing weapons, capturing loadout notes, and testing combat pacing
long before the official numbers land. as the library grows.
</p> </p>
</div> </div>
<div> <div>
@@ -79,8 +79,8 @@
<div class="af-card-header"> <div class="af-card-header">
<h2>Release posture</h2> <h2>Release posture</h2>
<p> <p>
Anomaly Front is unreleased. Everything shown here is a placeholder designed to help us think Everything shown here is a placeholder designed to help us think through combat pacing while the
through combat pacing before the official data arrives. data set is still evolving.
</p> </p>
</div> </div>
</div> </div>
+12 -4
View File
@@ -33,14 +33,14 @@
<div> <div>
<h1>Build a baseline weapon profile</h1> <h1>Build a baseline weapon profile</h1>
<p> <p>
Anomaly Front is still unreleased, so this calculator starts from scratch. Add your own Start from a library weapon or add your own values to explore pacing, time-to-eliminate, and
placeholder values to explore pacing, time-to-eliminate, and reload cadence. reload cadence.
</p> </p>
</div> </div>
<div class="af-status"> <div class="af-status">
<div> <div>
<p class="af-status-label">Stats library</p> <p class="af-status-label">Stats library</p>
<p class="af-status-value">No official stats yet</p> <p class="af-status-value">Community draft data</p>
</div> </div>
<div> <div>
<p class="af-status-label">Modifier stack</p> <p class="af-status-label">Modifier stack</p>
@@ -48,7 +48,7 @@
</div> </div>
<div> <div>
<p class="af-status-label">Version</p> <p class="af-status-label">Version</p>
<p class="af-status-value">Foundation v0.1</p> <p class="af-status-value">Foundation v0.2</p>
</div> </div>
</div> </div>
</section> </section>
@@ -60,6 +60,14 @@
<p>Enter a prototype loadout and tweak values to see combat pacing.</p> <p>Enter a prototype loadout and tweak values to see combat pacing.</p>
</div> </div>
<form class="af-form" id="af-calculator"> <form class="af-form" id="af-calculator">
<label>
Weapon library
<select name="weaponSelect" id="af-weapon-select"
data-source="{{ url_for('static', filename='json/data.json') }}">
<option value="">Custom entry</option>
</select>
<span class="af-field-hint">Select a weapon to prefill values. You can still edit every field.</span>
</label>
<label> <label>
Weapon name Weapon name
<input type="text" name="weaponName" placeholder="Prototype AR" autocomplete="off"> <input type="text" name="weaponName" placeholder="Prototype AR" autocomplete="off">
+4 -4
View File
@@ -16,7 +16,7 @@
</div> </div>
<div> <div>
<p class="af-title">Anomaly Front</p> <p class="af-title">Anomaly Front</p>
<p class="af-subtitle">Operations hub for the unreleased toolkit.</p> <p class="af-subtitle">Operations hub for the toolkit.</p>
</div> </div>
</div> </div>
<button class="af-nav-toggle" type="button" aria-expanded="false" aria-controls="af-nav"> <button class="af-nav-toggle" type="button" aria-expanded="false" aria-controls="af-nav">
@@ -33,8 +33,8 @@
<div> <div>
<h1>Welcome to the Anomaly Front planning space</h1> <h1>Welcome to the Anomaly Front planning space</h1>
<p> <p>
This hub replaces the legacy stats pages with a clean foundation for a new game. Start with the This hub replaces the legacy stats pages with a clean foundation for loadout planning. Start with the
calculator, capture balancing ideas, and iterate as official data arrives. calculator, capture balancing ideas, and iterate as your data set evolves.
</p> </p>
</div> </div>
<div class="af-status"> <div class="af-status">
@@ -44,7 +44,7 @@
</div> </div>
<div> <div>
<p class="af-status-label">Data state</p> <p class="af-status-label">Data state</p>
<p class="af-status-value">Prototype-only</p> <p class="af-status-value">Community draft</p>
</div> </div>
<div> <div>
<p class="af-status-label">Next release</p> <p class="af-status-label">Next release</p>