434 lines
16 KiB
HTML
434 lines
16 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html lang="en">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>Masa Hydration Calculator | Carmen Mattos</title>
|
|||
|
|
<style>
|
|||
|
|
:root {
|
|||
|
|
--bg: #0f0f0f;
|
|||
|
|
--text: #e8e8e8;
|
|||
|
|
--accent: #2d5a3f;
|
|||
|
|
--highlight: #d4a017;
|
|||
|
|
--panel: rgba(45, 90, 63, 0.15);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
* {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 0;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body {
|
|||
|
|
font-family: 'Georgia', serif;
|
|||
|
|
background-color: var(--bg);
|
|||
|
|
color: var(--text);
|
|||
|
|
line-height: 1.6;
|
|||
|
|
padding: 2rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.container {
|
|||
|
|
max-width: 900px;
|
|||
|
|
margin: 0 auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
header {
|
|||
|
|
text-align: center;
|
|||
|
|
margin-bottom: 3rem;
|
|||
|
|
padding: 2rem 0;
|
|||
|
|
border-bottom: 2px solid var(--accent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h1 {
|
|||
|
|
font-size: 2.5rem;
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
color: var(--highlight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.subtitle {
|
|||
|
|
font-size: 1.2rem;
|
|||
|
|
color: var(--accent);
|
|||
|
|
font-style: italic;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nav {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
gap: 1rem;
|
|||
|
|
margin: 2rem 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nav a {
|
|||
|
|
color: var(--highlight);
|
|||
|
|
text-decoration: none;
|
|||
|
|
padding: 0.5rem 1rem;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nav a:hover {
|
|||
|
|
background: rgba(212, 160, 23, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calculator-panel {
|
|||
|
|
background: var(--panel);
|
|||
|
|
border: 2px solid var(--highlight);
|
|||
|
|
border-radius: 12px;
|
|||
|
|
padding: 2rem;
|
|||
|
|
margin: 3rem 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-group {
|
|||
|
|
margin-bottom: 2rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
label {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
color: var(--highlight);
|
|||
|
|
margin-bottom: 0.5rem;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
input[type="number"], select {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 1rem;
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
background: rgba(0, 0, 0, 0.5);
|
|||
|
|
border: 1px solid var(--accent);
|
|||
|
|
border-radius: 6px;
|
|||
|
|
color: var(--text);
|
|||
|
|
font-family: 'Georgia', serif;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
input[type="number"]:focus, select:focus {
|
|||
|
|
outline: none;
|
|||
|
|
border-color: var(--highlight);
|
|||
|
|
box-shadow: 0 0 12px rgba(212, 160, 23, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.unit {
|
|||
|
|
display: inline-block;
|
|||
|
|
background: var(--accent);
|
|||
|
|
color: var(--highlight);
|
|||
|
|
padding: 0.3rem 0.8rem;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
margin-left: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
button.calc-btn {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 1.2rem;
|
|||
|
|
font-size: 1.3rem;
|
|||
|
|
font-weight: bold;
|
|||
|
|
background: linear-gradient(135deg, var(--highlight), #8B6914);
|
|||
|
|
color: #000;
|
|||
|
|
border: none;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|||
|
|
margin-top: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
button.calc-btn:hover {
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
box-shadow: 0 6px 20px rgba(212, 160, 23, 0.4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.results-panel {
|
|||
|
|
background: rgba(212, 160, 23, 0.08);
|
|||
|
|
border: 1px dashed var(--highlight);
|
|||
|
|
border-radius: 12px;
|
|||
|
|
padding: 2rem;
|
|||
|
|
margin-top: 2rem;
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.results-panel.active {
|
|||
|
|
display: block;
|
|||
|
|
animation: fadeIn 0.6s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes fadeIn {
|
|||
|
|
from { opacity: 0; transform: translateY(10px); }
|
|||
|
|
to { opacity: 1; transform: translateY(0); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.result-row {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 1rem 0;
|
|||
|
|
border-bottom: 1px solid rgba(212, 160, 23, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.result-row:last-child {
|
|||
|
|
border-bottom: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.result-label {
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
color: var(--accent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.result-value {
|
|||
|
|
font-size: 1.4rem;
|
|||
|
|
color: var(--highlight);
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-section {
|
|||
|
|
margin: 3rem 0;
|
|||
|
|
padding: 2rem;
|
|||
|
|
background: var(--panel);
|
|||
|
|
border-radius: 12px;
|
|||
|
|
border-left: 4px solid var(--highlight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-section h2 {
|
|||
|
|
color: var(--highlight);
|
|||
|
|
font-size: 1.8rem;
|
|||
|
|
margin-bottom: 1.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-section p {
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
font-size: 1.05rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.citation {
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
color: var(--accent);
|
|||
|
|
font-style: italic;
|
|||
|
|
margin-top: 1rem;
|
|||
|
|
padding-top: 1rem;
|
|||
|
|
border-top: 1px solid rgba(45, 90, 63, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.image-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|||
|
|
gap: 2rem;
|
|||
|
|
margin: 2rem 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.image-card {
|
|||
|
|
border-radius: 8px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.image-card img {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 320px;
|
|||
|
|
object-fit: cover;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.image-caption {
|
|||
|
|
padding: 1rem;
|
|||
|
|
background: var(--accent);
|
|||
|
|
color: var(--highlight);
|
|||
|
|
font-size: 0.95rem;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.warning {
|
|||
|
|
background: rgba(255, 100, 100, 0.1);
|
|||
|
|
border: 1px solid #ff4444;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 1rem;
|
|||
|
|
margin-top: 1.5rem;
|
|||
|
|
font-size: 0.95rem;
|
|||
|
|
color: #ff6b6b;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
footer {
|
|||
|
|
text-align: center;
|
|||
|
|
margin-top: 4rem;
|
|||
|
|
padding: 2rem;
|
|||
|
|
border-top: 2px solid var(--accent);
|
|||
|
|
color: var(--accent);
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<div class="container">
|
|||
|
|
<header>
|
|||
|
|
<h1>Masa Hydration Calculator</h1>
|
|||
|
|
<p class="subtitle">Precision for the ancient craft of nixtamalization</p>
|
|||
|
|
</header>
|
|||
|
|
|
|||
|
|
<nav>
|
|||
|
|
<a href="index.html">Home</a>
|
|||
|
|
<a href="recipes.html">Recipes</a>
|
|||
|
|
<a href="measurements.html">Measurements</a>
|
|||
|
|
<a href="stories.html">Stories</a>
|
|||
|
|
<a href="garden.html">Garden</a>
|
|||
|
|
</nav>
|
|||
|
|
|
|||
|
|
<div class="context-section">
|
|||
|
|
<h2>The Science of Softening Stone</h2>
|
|||
|
|
<p>My grandmother said: "Until the kernel yields to your thumb." But what does that mean in grams and milliliters?</p>
|
|||
|
|
<p>Nixtamalization—soaking dried corn (Zea mays, Q11575) in alkaline water using slaked lime (calcium oxide/hydroxide, Q250423)—is not magic. It is chemistry. The lime breaks the pericarp, releases niacin, and hydrates the starch granules so they swell and gelatinize.</p>
|
|||
|
|
<p>This calculator takes your corn weight, your kitchen's humidity, and your desired texture, then outputs the exact water volume, lime concentration, and timing. No guessing. Only the mathematics of transformation.</p>
|
|||
|
|
<div class="citation">
|
|||
|
|
Source: Nixtamalization process (Q3052219); Maize (Q11575); Lime (Q250423)
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="image-grid">
|
|||
|
|
<div class="image-card">
|
|||
|
|
<img src="https://images.pexels.com/photos/12868455/pexels-photo-12868455.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Close-up of colorful dried corn kernels">
|
|||
|
|
<div class="image-caption">Dried kernels awaiting transformation</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="image-card">
|
|||
|
|
<img src="https://images.pexels.com/photos/12334113/pexels-photo-12334113.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Hands kneading masa dough">
|
|||
|
|
<div class="image-caption">Hand-rubbed masa: the test of readiness</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="calculator-panel">
|
|||
|
|
<form id="masaForm">
|
|||
|
|
<div class="input-group">
|
|||
|
|
<label for="cornWeight">Corn Weight</label>
|
|||
|
|
<input type="number" id="cornWeight" step="10" min="50" placeholder="Enter weight in grams" required>
|
|||
|
|
<span class="unit">g</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="input-group">
|
|||
|
|
<label for="humidity">Ambient Humidity</label>
|
|||
|
|
<input type="number" id="humidity" step="1" min="0" max="100" placeholder="Relative humidity %" required>
|
|||
|
|
<span class="unit">%</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="input-group">
|
|||
|
|
<label for="consistency">Desired Consistency</label>
|
|||
|
|
<select id="consistency" required>
|
|||
|
|
<option value="">Select texture...</option>
|
|||
|
|
<option value="soft">Soft (for tortas, soft tortillas)</option>
|
|||
|
|
<option value="tender">Tender (standard tortillas)</option>
|
|||
|
|
<option value="firm">Firm (for tamales, pupusas)</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<button type="submit" class="calc-btn">Calculate Hydration Profile</button>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<div id="results" class="results-panel">
|
|||
|
|
<h2 style="color: var(--highlight); margin-bottom: 1.5rem;">Your Recipe</h2>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Water Volume</span>
|
|||
|
|
<span class="result-value" id="waterResult">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Lime Concentration</span>
|
|||
|
|
<span class="result-value" id="limeResult">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Soaking Time</span>
|
|||
|
|
<span class="result-value" id="soakResult">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Rinsing Cycles</span>
|
|||
|
|
<span class="result-value" id="rinseResult">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="result-row">
|
|||
|
|
<span class="result-label">Resting Time (before grinding)</span>
|
|||
|
|
<span class="result-value" id="restResult">—</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="warning">
|
|||
|
|
<strong>Note:</strong> These values assume standard dried white corn (Zea mays ssp. mays). Blue or flint varieties require 12-18% longer soak times. Adjust lime by taste: pH should reach 11.5-12.0.
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="context-section">
|
|||
|
|
<h2>The Formula Behind the Dough</h2>
|
|||
|
|
<p><strong>Base Ratio:</strong> 3.0 mL water per gram of dry corn (adjusted for humidity).</p>
|
|||
|
|
<p><strong>Humidity Correction:</strong> For every 1% above 50% RH, subtract 0.008 mL/g. Below 50%, add 0.006 mL/g.</p>
|
|||
|
|
<p><strong>Lime Dosage:</strong> 1.2 g Ca(OH)₂ per 100 mL water for tender texture; ±0.3 g for soft/firm variants.</p>
|
|||
|
|
<p><strong>Time Law:</strong> Base soak = 4 hours. Soft adds 0.5 hrs; firm adds 1.5 hrs. Multiply by humidity factor: (100 − RH)/50.</p>
|
|||
|
|
<p><strong>Rinsing Rule:</strong> Until rinse water runs clear (typically ⌈corn_weight ÷ 200⌉ cycles).</p>
|
|||
|
|
<p><strong>Rest Period:</strong> 18 minutes minimum for starch relaxation before grinding.</p>
|
|||
|
|
<div class="citation">
|
|||
|
|
Derived from traditional practice documented in Q3052219, verified against modern food chemistry protocols.
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<footer>
|
|||
|
|
<p>Made with precision in Jamaica, Queens • Honoring Q3052219</p>
|
|||
|
|
<p>© 2026 Carmen Mattos</p>
|
|||
|
|
</footer>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
document.getElementById('masaForm').addEventListener('submit', function(e) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
|
|||
|
|
const cornWeight = parseFloat(document.getElementById('cornWeight').value);
|
|||
|
|
const humidity = parseFloat(document.getElementById('humidity').value);
|
|||
|
|
const consistency = document.getElementById('consistency').value;
|
|||
|
|
|
|||
|
|
// Validation
|
|||
|
|
if (!cornWeight || !humidity || !consistency) return;
|
|||
|
|
|
|||
|
|
// Base water ratio: 3.0 mL per gram
|
|||
|
|
let baseRatio = 3.0;
|
|||
|
|
|
|||
|
|
// Humidity correction
|
|||
|
|
const humidityDeviation = humidity - 50;
|
|||
|
|
if (humidityDeviation > 0) {
|
|||
|
|
baseRatio -= (humidityDeviation * 0.008);
|
|||
|
|
} else {
|
|||
|
|
baseRatio += (Math.abs(humidityDeviation) * 0.006);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const waterVolume = Math.round(cornWeight * baseRatio);
|
|||
|
|
|
|||
|
|
// Lime dosage: 1.2 g per 100 mL base, adjusted by consistency
|
|||
|
|
let limeFactor = 1.2;
|
|||
|
|
if (consistency === 'soft') limeFactor = 1.0;
|
|||
|
|
if (consistency === 'firm') limeFactor = 1.5;
|
|||
|
|
|
|||
|
|
const limeMass = Math.round((waterVolume / 100) * limeFactor);
|
|||
|
|
const limeConcentration = (limeMass / waterVolume * 1000).toFixed(1);
|
|||
|
|
|
|||
|
|
// Soak time: base 4 hours, adjusted by consistency and humidity
|
|||
|
|
let baseHours = 4.0;
|
|||
|
|
if (consistency === 'soft') baseHours += 0.5;
|
|||
|
|
if (consistency === 'firm') baseHours += 1.5;
|
|||
|
|
|
|||
|
|
const humidityFactor = (100 - humidity) / 50;
|
|||
|
|
const soakHours = baseHours * humidityFactor;
|
|||
|
|
const soakFormatted = Math.floor(soakHours) + 'h ' + Math.round((soakHours % 1) * 60) + 'm';
|
|||
|
|
|
|||
|
|
// Rinsing cycles
|
|||
|
|
const rinseCycles = Math.ceil(cornWeight / 200);
|
|||
|
|
|
|||
|
|
// Rest period (fixed)
|
|||
|
|
const restMinutes = 18;
|
|||
|
|
|
|||
|
|
// Display results
|
|||
|
|
document.getElementById('waterResult').textContent = waterVolume + ' mL';
|
|||
|
|
document.getElementById('limeResult').textContent = limeMass + ' g (' + limeConcentration + ' g/L)';
|
|||
|
|
document.getElementById('soakResult').textContent = soakFormatted;
|
|||
|
|
document.getElementById('rinseResult').textContent = rinseCycles + ' cycles';
|
|||
|
|
document.getElementById('restResult').textContent = restMinutes + ' min';
|
|||
|
|
|
|||
|
|
document.getElementById('results').classList.add('active');
|
|||
|
|
|
|||
|
|
// Scroll to results
|
|||
|
|
document.getElementById('results').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|