carmen-mattos-kitchen/masa-calculator.html
2026-07-18 16:53:12 +00:00

434 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>&copy; 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>