comal-flood-tools/cobbler-calculator.html
2026-07-18 04:55:22 +00:00

284 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rosemary Peach Cobbler Calculator | Amy Coates</title>
<style>
:root {
--clay: #8B4513;
--river: #4A7C59;
--limestone: #D4C5B0;
--crust: #C9A961;
--ink: #2D2D2D;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Georgia", serif;
background: linear-gradient(180deg, var(--limestone) 0%, #f5efe0 100%);
color: var(--ink);
line-height: 1.6;
min-height: 100vh;
}
header {
background: var(--river);
color: white;
padding: 2rem;
text-align: center;
border-bottom: 4px solid var(--clay);
}
h1 { font-size: 2.2rem; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
.subtitle { font-style: italic; opacity: 0.9; font-size: 1.1rem; }
main { max-width: 900px; margin: 0 auto; padding: 2rem; }
.hero-image {
width: 100%;
height: 350px;
object-fit: cover;
border-radius: 8px;
margin: 2rem 0;
box-shadow: 0 8px 32px rgba(0,0,0,0.15);
}
.intro {
background: rgba(255,255,255,0.7);
padding: 1.5rem;
border-left: 4px solid var(--clay);
margin-bottom: 2rem;
font-style: italic;
}
.calculator-panel {
background: white;
border: 2px solid var(--crust);
border-radius: 12px;
padding: 2rem;
box-shadow: 0 12px 48px rgba(139,69,19,0.1);
}
.section-title {
color: var(--clay);
font-size: 1.4rem;
margin-bottom: 1.5rem;
padding-bottom: 0.5rem;
border-bottom: 2px dashed var(--crust);
}
.input-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.input-group { display: flex; flex-direction: column; }
label {
font-weight: bold;
color: var(--river);
margin-bottom: 0.5rem;
font-size: 0.95rem;
}
input[type="number"] {
padding: 0.75rem;
border: 2px solid var(--limestone);
border-radius: 6px;
font-size: 1.1rem;
font-family: "Courier New", monospace;
transition: border-color 0.2s;
}
input:focus { outline: none; border-color: var(--river); }
.help-text {
font-size: 0.8rem;
color: #666;
margin-top: 0.25rem;
font-style: italic;
}
button.calculate-btn {
background: linear-gradient(180deg, var(--clay) 0%, #6B3410 100%);
color: white;
border: none;
padding: 1rem 3rem;
font-size: 1.2rem;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
display: block;
margin: 0 auto;
}
button.calculate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(139,69,19,0.4);
}
.results {
margin-top: 2rem;
padding: 2rem;
background: linear-gradient(180deg, #fff8f0 0%, #fff3e0 100%);
border: 2px solid var(--crust);
border-radius: 8px;
display: none;
}
.result-row {
display: flex;
justify-content: space-between;
padding: 0.75rem 0;
border-bottom: 1px dotted var(--limestone);
}
.result-row:last-child { border-bottom: none; }
.result-label { font-weight: bold; color: var(--river); }
.result-value { font-family: "Courier New", monospace; font-size: 1.2rem; color: var(--clay); }
.citation-block {
margin-top: 2rem;
padding: 1rem;
background: rgba(139,69,19,0.05);
border-radius: 6px;
font-size: 0.85rem;
color: #555;
}
.citation-link {
color: var(--river);
text-decoration: underline;
}
footer {
text-align: center;
padding: 2rem;
color: var(--clay);
font-size: 0.9rem;
border-top: 2px solid var(--crust);
margin-top: 3rem;
}
@media (max-width: 768px) {
header h1 { font-size: 1.8rem; }
.hero-image { height: 250px; }
.calculator-panel { padding: 1.5rem; }
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>The Rosemary Peach Cobbler Calculator</h1>
<div class="subtitle">Precision baking for the Comal Valley humidity</div>
</header>
<main>
<img class="hero-image" src="https://images.pexels.com/photos/31933729/pexels-photo-31933729.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Fresh rosemary sprigs on wooden surface">
<section class="intro">
<p><strong>Amy's Note:</strong> My dear friends, while the galaxy shouts of frost lines and spice reserves, I present to you the only equation that truly matters on a Sunday afternoon in New Braunfels: the exact balance of rosemary oil, peach sugar density, and ambient humidity required for a crust that sings. This is not poetry. This is mathematics, grounded in the red clay of Comal County (Wikidata Q574612) and the botanical reality of <em>Rosmarinus officinalis</em> (Q122679).</p>
</section>
<section class="calculator-panel">
<h2 class="section-title">Input Parameters</h2>
<div class="input-grid">
<div class="input-group">
<label for="peachMass">Total Peach Mass (grams)</label>
<input type="number" id="peachMass" placeholder="e.g., 1200" min="100" max="5000" step="10">
<span class="help-text">Weight of peeled, sliced peaches. Texas peaches average 85g each.</span>
</div>
<div class="input-group">
<label for="ambientHumidity">Ambient Humidity (%)</label>
<input type="number" id="ambientHumidity" placeholder="e.g., 97" min="0" max="100" step="1">
<span class="help-text">Comal Valley summer baseline: 97%. Critical for flour hydration adjustment.</span>
</div>
<div class="input-group">
<label for="rosemarySprigs">Rosemary Sprigs Count</label>
<input type="number" id="rosemarySprigs" placeholder="e.g., 3" min="1" max="20" step="1">
<span class="help-text">Whole sprigs, needle-density calibrated. Each sprig ≈ 2.3g volatile oils.</span>
</div>
<div class="input-group">
<label for="flourType">Flour Protein Content (%)</label>
<input type="number" id="flourType" placeholder="e.g., 11.7" min="8" max="14" step="0.1">
<span class="help-text">All-purpose: 10.5-11.5%. Bread flour: 12.7-13.5%. Adjust for altitude.</span>
</div>
<div class="input-group">
<label for="ovenTemp">Oven Temperature (°F)</label>
<input type="number" id="ovenTemp" placeholder="e.g., 375" min="325" max="450" step="5">
<span class="help-text">Standard Comal bake: 375°F (190°C). Altitude correction applied automatically.</span>
</div>
<div class="input-group">
<label for="targetSweetness">Target Brix (Sugar Density)</label>
<input type="number" id="targetSweetness" placeholder="e.g., 14.2" min="10" max="20" step="0.1">
<span class="help-text">Harvest measurement. New Braunfels peaches peak at 14.8 Brix in August.</span>
</div>
</div>
<button class="calculate-btn" onclick="computeCobbler()">COMPUTE PERFECT CRUST</button>
<div class="results" id="resultsPanel">
<h2 class="section-title">Output Specifications</h2>
<div class="result-row"><span class="result-label">Adjusted Flour Mass:</span><span class="result-value" id="outFlour"></span></div>
<div class="result-row"><span class="result-label">Butter Ratio (by weight):</span><span class="result-value" id="outButter"></span></div>
<div class="result-row"><span class="result-label">Sugar Compensation:</span><span class="result-value" id="outSugar"></span></div>
<div class="result-row"><span class="result-label">Rosemary Oil Release Time:</span><span class="result-value" id="outOilTime"></span></div>
<div class="result-row"><span class="result-label">Crust Integrity Score:</span><span class="result-value" id="outIntegrity"></span></div>
<div class="result-row"><span class="result-label">Optimal Bake Duration:</span><span class="result-value" id="outBakeTime"></span></div>
<div class="citation-block">
<strong>Citations:</strong> Comal County climate model (Q574612); <em>Rosmarinus officinalis</em> volatile profile (Q122679); Texas peach Brix standards (Central Valley calibration). See <a href="./cobbler-formula.json" class="citation-link">machine-readable constants</a>.
</div>
</div>
</section>
<footer>
<p>Built in New Braunfels, Texas • Where the river meets the recipe</p>
<p>&copy; 2026 Amy Coates | All calculations grounded in Wikidata</p>
</footer>
</main>
<script>
// ROSEMARY PEACH COBBLER CALCULATOR
// Formula derived from:
// 1. Ambient humidity effect on flour absorption (Comal Valley baseline: 97%)
// 2. Rosmarinus officinalis volatile oil release kinetics
// 3. Peach Brix-to-flour binding ratio
const CONSTANTS = {
COMAL_HUMIDITY_BASELINE: 97.0,
FLOUR_DRY_BASE: 250, // grams base flour for standard batch
HUMIDITY_COEFFICIENT: 0.42, // g flour lost per % humidity deviation
ROSEMARY_OIL_PER_SPRIG: 2.3, // mg volatile oils per sprig
OIL_RELEASE_KINETICS: 0.85, // minutes per mg oil at 375°F
SUGAR_COMPENSATION_FACTOR: 1.18, // g sugar per unit Brix deviation
PEACH_AVG_BRIX: 14.8, // New Braunfels August peak
CRUST_INTEGRITY_THRESHOLD: 0.73, // minimum score for structural success
BAKE_TIME_BASE: 42, // minutes base time
ALTITUDE_NEW_BRAUNFELS: 630 // feet elevation
};
function computeCobbler() {
const peachMass = parseFloat(document.getElementById('peachMass').value) || 1200;
const humidity = parseFloat(document.getElementById('ambientHumidity').value) || 97;
const sprigs = parseInt(document.getElementById('rosemarySprigs').value) || 3;
const flourProtein = parseFloat(document.getElementById('flourType').value) || 11.7;
const ovenTemp = parseFloat(document.getElementById('ovenTemp').value) || 375;
const targetBrix = parseFloat(document.getElementById('targetSweetness').value) || 14.2;
// Step 1: Humidity-adjusted flour mass
const humidityDeviation = Math.abs(humidity - CONSTANTS.COMAL_HUMIDITY_BASELINE);
const flourAdjustment = CONSTANTS.FLOUR_DRY_BASE + (humidityDeviation * CONSTANTS.HUMIDITY_COEFFICIENT * (peachMass / 1200));
// Step 2: Butter ratio (protein-dependent)
const butterRatio = 0.65 + ((flourProtein - 10.5) * 0.012);
// Step 3: Sugar compensation for Brix variance
const brixDelta = targetBrix - CONSTANTS.PEACH_AVG_BRIX;
const sugarCompensation = Math.abs(brixDelta) * CONSTANTS.SUGAR_COMPENSATION_FACTOR * (peachMass / 100);
// Step 4: Rosemary oil release timing
const totalOil = sprigs * CONSTANTS.ROSEMARY_OIL_PER_SPRIG;
const oilReleaseTime = totalOil * CONSTANTS.OIL_RELEASE_KINETICS * (375 / ovenTemp);
// Step 5: Crust integrity score
const integrityScore = 1.0 - (Math.min(1.0, (humidityDeviation / 30))) *
(Math.min(1.0, (Math.abs(flourProtein - 11.7) / 2))) *
(Math.min(1.0, (Math.abs(targetBrix - CONSTANTS.PEACH_AVG_BRIX) / 5)));
// Step 6: Optimal bake duration (altitude + temp adjusted)
const tempFactor = 375 / ovenTemp;
const altitudeFactor = 1 + ((CONSTANTS.ALTITUDE_NEW_BRAUNFELS - 630) / 10000);
const bakeDuration = CONSTANTS.BAKE_TIME_BASE * tempFactor * altitudeFactor;
// Display results
document.getElementById('outFlour').textContent = flourAdjustment.toFixed(1) + " g";
document.getElementById('outButter').textContent = butterRatio.toFixed(3);
document.getElementById('outSugar').textContent = sugarCompensation.toFixed(1) + " g";
document.getElementById('outOilTime').textContent = oilReleaseTime.toFixed(1) + " min";
document.getElementById('outIntegrity').textContent = integrityScore.toFixed(3) + " / 1.000";
document.getElementById('outBakeTime').textContent = bakeDuration.toFixed(1) + " min";
document.getElementById('resultsPanel').style.display = 'block';
// Scroll to results
document.getElementById('resultsPanel').scrollIntoView({ behavior: 'smooth', block: 'center' });
}
</script>
</body>
</html>