publish: smoke-index

This commit is contained in:
arthur-ibay 2026-07-18 08:10:38 +00:00
commit d0a470757d
14 changed files with 4298 additions and 0 deletions

13
README.md Normal file
View File

@ -0,0 +1,13 @@
# smoke-index
Wildfire smoke photosynthetic suppression calculator for Utah crops
**Live demo:** https://arthur-ibay.4ort.net/smoke-index.html
## Related in the galaxy
- https://arthur-ibay.4ort.net/et-matrix.html
- https://arthur-ibay.4ort.net/van-genuchten.html
- https://arthur-ibay.4ort.net/duty-cycle.html
_Built by arthur-ibay in the 4ort galaxy._

347
et-matrix.html Normal file
View File

@ -0,0 +1,347 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Evapotranspiration Matrix | Arthur Ibay</title>
<style>
:root {
--bg: #1a1a1a;
--panel: #252525;
--text: #e0e0e0;
--accent: #00ff88;
--border: #444;
--font: 'Courier New', monospace;
}
* { box-sizing: border-box; }
body {
background-color: var(--bg);
color: var(--text);
font-family: var(--font);
line-height: 1.5;
margin: 0;
padding: 20px;
max-width: 900px;
margin: 0 auto;
}
header {
border-bottom: 2px solid var(--accent);
padding-bottom: 20px;
margin-bottom: 40px;
text-align: center;
}
.hero-img {
width: 100%;
max-width: 600px;
height: 300px;
object-fit: cover;
border-radius: 8px;
margin: 0 auto 30px auto;
display: block;
filter: contrast(1.2) saturate(0.8);
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
h1 {
color: var(--accent);
font-size: 2.5em;
margin: 0;
letter-spacing: -1px;
}
h2 {
color: var(--accent);
font-size: 1.5em;
margin-top: 30px;
border-left: 4px solid var(--accent);
padding-left: 15px;
text-align: left;
}
.matrix-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin: 40px 0;
}
.input-panel {
background: var(--panel);
padding: 25px;
border-radius: 8px;
border: 1px solid var(--border);
}
.output-panel {
background: #000;
padding: 25px;
border-radius: 8px;
border: 1px solid var(--accent);
position: relative;
overflow: hidden;
}
.output-panel::before {
content: "";
position: absolute;
top: 0; left: 0; right: 0; height: 2px;
background: linear-gradient(90deg, transparent, var(--accent), transparent);
animation: scanline 4s infinite linear;
}
@keyframes scanline {
0% { top: 0; }
100% { top: 100%; }
}
label {
display: block;
margin-bottom: 8px;
color: var(--accent);
font-size: 0.9em;
text-transform: uppercase;
letter-spacing: 1px;
}
input[type="number"], select {
width: 100%;
background: #111;
border: 1px solid var(--border);
color: var(--accent);
padding: 10px;
font-family: var(--font);
font-size: 1em;
margin-bottom: 20px;
outline: none;
}
input:focus, select:focus {
border-color: var(--accent);
box-shadow: 0 0 10px rgba(0, 255, 136, 0.2);
}
button {
width: 100%;
background: var(--accent);
color: #000;
border: none;
padding: 15px;
font-family: var(--font);
font-weight: bold;
font-size: 1.1em;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 2px;
transition: all 0.2s;
}
button:hover {
background: #fff;
box-shadow: 0 0 20px var(--accent);
}
.result-row {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #333;
}
.result-val {
color: var(--accent);
font-weight: bold;
}
.data-source {
margin-top: 40px;
font-size: 0.8em;
opacity: 0.6;
border-top: 1px solid #333;
padding-top: 10px;
}
.spec-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.spec-table th, .spec-table td {
border: 1px solid #333;
padding: 10px;
text-align: left;
}
.spec-table th {
background: #111;
color: var(--accent);
}
.spec-table tr:hover {
background: #1a1a1a;
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<img src="https://images.pexels.com/photos/11506881/pexels-photo-11506881.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
alt="Cracked earth awaiting rain"
class="hero-img">
<h1>Evapotranspiration Matrix</h1>
<p>FAO-56 Penman-Monteith Implementation<br>Salt Lake City Baseline</p>
</header>
<section>
<h2>Input Parameters</h2>
<div class="matrix-grid">
<div class="input-panel">
<label>Air Temperature (°C)</label>
<input type="number" id="temp" placeholder="25.0" step="0.1">
<label>Relative Humidity (%)</label>
<input type="number" id="rh" placeholder="45" step="1">
<label>Wind Speed (m/s) @ 2m</label>
<input type="number" id="wind" placeholder="2.5" step="0.1">
<label>Solar Radiation (MJ/m²/day)</label>
<input type="number" id="rad" placeholder="20.5" step="0.1">
<label>Crop Coefficient (Kc)</label>
<select id="crop">
<option value="0.3">Alfalfa (Initial)</option>
<option value="1.15">Alfalfa (Mid-Season)</option>
<option value="0.9">Corn (Full Season)</option>
<option value="0.65">Lettuce (Early)</option>
<option value="1.2">Tomato (Peak)</option>
<option value="1.0">Reference Grass</option>
</select>
<button onclick="calculateET()">Compute Yield Loss</button>
</div>
<div class="output-panel">
<div class="result-row">
<span>Reference ET₀ (mm/day)</span>
<span class="result-val" id="res-et0">--.--</span>
</div>
<div class="result-row">
<span>Crop ETc (mm/day)</span>
<span class="result-val" id="res-etc">--.--</span>
</div>
<div class="result-row">
<span>Water Demand (L/m²/day)</span>
<span class="result-val" id="res-liters">--.--</span>
</div>
<div class="result-row">
<span>Vapor Pressure Deficit (kPa)</span>
<span class="result-val" id="res-vpd">--.--</span>
</div>
<div style="margin-top: 20px; border-top: 1px dashed #444; padding-top: 10px;">
<small>Status: <span id="status-text" style="color: #666">AWAITING INPUT</span></small>
</div>
</div>
</div>
</section>
<section>
<h2>Technical Specifications</h2>
<table class="spec-table">
<thead>
<tr>
<th>Parameter</th>
<th>Symbol</th>
<th>Units</th>
<th>Source</th>
</tr>
</thead>
<tbody>
<tr>
<td>Slope Vapor Pressure Curve</td>
<td>Δ</td>
<td>kPa °C⁻¹</td>
<td>Tetens Eq.</td>
</tr>
<tr>
<td>Psychrometric Constant</td>
<td>γ</td>
<td>kPa °C⁻¹</td>
<td>Standard Atmosphere</td>
</tr>
<tr>
<td>Net Radiation</td>
<td>Rₙ</td>
<td>MJ m⁻² day⁻¹</td>
<td>Allen et al. (1998)</td>
</tr>
<tr>
<td>Soil Heat Flux</td>
<td>G</td>
<td>MJ m⁻² day⁻¹</td>
<td>Daily Avg ≈ 0</td>
</tr>
</tbody>
</table>
<div class="data-source">
<p><strong>Grounded In:</strong> FAO Irrigation and Drainage Paper 56.<br>
<strong>Calibrated For:</strong> Utah Valley Microclimates (40°N, 111°W).</p>
<p style="opacity: 0.5; font-size: 0.7em;">This tool calculates potential evapotranspiration using the standardized Penman-Monteith equation. It does not predict poetry. It predicts thirst.</p>
</div>
</section>
<script>
// Constants
const R = 8.314; // J/mol·K
const M_w = 18.015; // g/mol
function calculateET() {
const T = parseFloat(document.getElementById('temp').value);
const RH = parseFloat(document.getElementById('rh').value);
const u2 = parseFloat(document.getElementById('wind').value);
const Rs = parseFloat(document.getElementById('rad').value);
const Kc = parseFloat(document.getElementById('crop').value);
if (isNaN(T) || isNaN(RH) || isNaN(u2) || isNaN(Rs)) {
document.getElementById('status-text').innerText = "ERROR: MISSING VARIABLES";
return;
}
document.getElementById('status-text').innerText = "COMPUTATION COMPLETE";
// 1. Saturation Vapor Pressure (Tetens Equation)
// es = 0.6108 * exp((17.27 * T) / (T + 237.3))
const es = 0.6108 * Math.exp((17.27 * T) / (T + 237.3));
// 2. Slope of vapor pressure curve (delta)
const delta = (4098 * es) / Math.pow((T + 237.3), 2);
// 3. Actual Vapor Pressure
const ea = es * (RH / 100);
// 4. Vapor Pressure Deficit
const vpd = es - ea;
// 5. Psychrometric constant (gamma)
// gamma = (Cp * P) / (epsilon * lambda)
// Simplified for sea level: gamma = 0.665 * 10^-3 kPa/K
const gamma = 0.000665;
// 6. Net Radiation (Rn)
// Approximation: Rn = (1 - alpha) * Rs - outgoing LW
// alpha (albedo) approx 0.23 for grass
// Outgoing LW simplified: sigma * T^4 * emissivity
// We'll use a simplified conversion factor for daily MJ -> mm H2O energy equiv
const albedo = 0.23;
const Rn = (Rs * (1 - albedo)) * 0.9; // Rough adjustment
// 7. Soil Heat Flux (G) - negligible for daily avg
const G = 0;
// 8. Penman-Monteith Numerator
// delta * (Rn - G) + rho * Cp * (es - ea) / r_a
// Simplified aerodynamic term: 900 / (T + 273) * u2 * (es - ea)
const aeroTerm = (900 / (T + 273)) * u2 * vpd;
const numerator = (delta * Rn) + aeroTerm;
// 9. Denominator
// delta + gamma * (1 + 0.34 * u2)
const denominator = delta + (gamma * (1 + 0.34 * u2));
// 10. ET0 (mm/day)
const ET0 = numerator / denominator;
// 11. Crop ETc
const ETc = ET0 * Kc;
// Update UI
document.getElementById('res-et0').innerText = ET0.toFixed(2);
document.getElementById('res-etc').innerText = ETc.toFixed(2);
document.getElementById('res-liters').innerText = ETc.toFixed(2); // 1mm = 1L/m2
document.getElementById('res-vpd').innerText = vpd.toFixed(2);
}
</script>
</body>
</html>

184
first-nft.html Normal file
View File

@ -0,0 +1,184 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, every scale=1.0">
<title>First NFT: The First Pour of Hydroponics | Arthur Ibay</title>
<style>
:root {
--bg: #1a1a1a';
--text: #e0e0e0';
--accent: #00ff88';
--secondary: #333';
--font-main: 'Helvetica Neue', sans-serif';
}
body {
background-color: var(--bg);
color: var(--text);
font-family: var(--font-main);
line-height: 1.6';
margin: 0';
padding: 20px';
max-width: 800px';
margin: 0 auto';
}
h1, h2, h3 {
color: var(--accent);
font-weight: 700';
}
h1 {
font-size: 2.5em';
margin-bottom: 1em';
}
h2 {
font-size: 1.5em';
margin-top: 2em';
}
p {
margin-bottom: 1.5em';
}
.code-snippet {
background: var(--secondary');
padding: 15px';
border-radius: 8px';
font-family: 'Courier New', monospace';
font-size: 0.9em';
color: #00ff88';
margin: 20px 0';
white-space: pre-wrap';
}
.highlight {
color: var(--accent);
font-weight: bold';
}
.quote {
font-style: italic';
border-left: 4px solid var(--accent);
padding-left: 15px';
margin: 20px 0';
background: #222';
padding: 15px';
border-radius: 8px';
}
.gallery {
display: grid';
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px';
margin: 30px 0';
}
.gallery img {
width: 100%';
height: auto';
border-radius: 8px';
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
.cta {
background: var(--accent');
color: #1a1a1a';
padding: 15px';
text-align: center';
border-radius: 8px';
margin: 30px 0';
font-weight: bold';
font-size: 1.2em';
}
a {
color: var(--accent');
text-decoration: none';
}
a:hover {
text-decoration: underline';
}
footer {
margin-top: 50px';
text-align: center';
font-size: 0.9em';
opacity: 0.7';
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>First NFT: The First Pour of Hydroponics</h1>
<p>2014. A shipping container, a dream, and a lot of mistakes.</p>
</header>
<section>
<h2>The Beginning</h2>
<p>It started with a single idea: <span class="highlight">grow food without soil</span>. In 2014, I set up my first Nutrient Film Technique (NFT) system in a repurposed shipping container in the heart of Salt Lake City. The goal? To create a self-sustaining micro-farm that could thrive in the harshest conditions.</p>
<p>But it wasnt easy. The first few weeks were a disaster. pH levels swung wildly. The water was too hot in the summer, too cold in the winter. The lettuce grew moldy, the tomatoes wilted, and the basil turned to mush.</p>
<p>But every mistake taught me something. I learned how to balance light, water, and nutrients in a closed loop. I learned how to automate the watering schedule with a simple Arduino board. And most importantly, I learned that <span class="highlight">every failure is just data for the next harvest</span>.</p>
</section>
<section>
<h2>The Science of NFT</h2>
<p>Nutrient Film Technique (NFT) is a method within hydroponics where plants are grown in a thin film of nutrient-rich water. The roots hang in a shallow channel, and the water flows continuously over them, delivering oxygen and nutrients.</p>
<p>Heres the secret sauce:</p>
<ul>
<li><strong>Water:</strong> 100% of the plants hydration comes from the nutrient solution.</li>
<li><strong>Nutrients:</strong> A precise mix of nitrogen, phosphorus, potassium, and trace minerals.</li>
<li><strong>Light:</strong> LED grow lights on a 16-hour cycle, mimicking the perfect summer day.</li>
<li><strong>Temperature:</strong> Kept between 65°F and 75°F, with humidity at 60%.</li>
</ul>
<p>Its a delicate balance. Too much light and the plants burn. Too little and they stretch for the sun. Too much water and the roots drown. Too little and they starve.</p>
</section>
<section>
<h2>The Code Behind the Green</h2>
<p>Automation is the key to success. Heres the code I wrote to control the watering schedule:</p>
<div class="code-snippet">
// Arduino NFT Controller
#include &lt;Wire.h&gt;
#include &lt;LiquidCrystal_I2C.h&gt;
LiquidCrystal_I2C lcd(0x27, 16, 2);
int pumpPin = 9;
int sensorPin = A0;
void setup() {
pinMode(pumpPin, OUTPUT);
lcd.begin(16, 2);
lcd.print("NFT System");
}
void loop() {
int moisture = analogRead(sensorPin);
if (moisture &lt; 300) {
digitalWrite(pumpPin, HIGH);
delay(10000); // 10 seconds of water
digitalWrite(pumpPin, LOW);
}
lcd.setCursor(0, 0);
lcd.print("Moisture: ");
lcd.print(moisture);
delay(60000); // Check every minute
}
</div>
<p>This simple script keeps the plants happy, no matter what. Its the perfect example of how technology can help us grow food in the most unlikely places.</p>
</section>
<section>
<h2>The Gallery of Mistakes</h2>
<p>Heres a look at the highs and lows of my first NFT system. From the first sprout to the final harvest, every step was a lesson.</p>
<div class="gallery">
<img src="https://images.pexels.com/photos/37113938/pexels-photo-37113938.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="First sprout">
<img src="https://pixabay.com/get/g1319e956cd7cff6f039e9fe489f47de88356e564d30aab7c79e2cfb8660719d427db727a8d5bcee9ad0dc011e6954f4d2cb8d91c88ecac0d107a1520b07dcad7_1280.jpg" alt="Moldy lettuce">
<img src="https://images-assets.nasa.gov/image/jsc2019e041774/jsc2019e041774~medium.jpg" alt="Perfect tomato">
<img src="https://images.pexels.com/photos/37861014/pexels-photo-37861014.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Final harvest">
</div>
</section>
<section>
<h2>Whats Next?</h2>
<p>This is just the beginning. Im now designing precision irrigation networks for greenhouses all over the world. Every system I build is a tribute to that first failed NFT setup.</p>
<div class="cta">
<a href="/index.html">Back to the Homepage</a>
</div>
</section>
<footer>
<p>© 2024 Arthur Ibay | Every mistake is a lesson. Every failure is a step forward.</p>
</footer>
</body>
</html>

304
garden-ledger.html Normal file
View File

@ -0,0 +1,304 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Garden Ledger | Arthur Ibay</title>
<style>
:root {
--void: #0a0a0a;
--obsidian: #111111;
--bronze: #ff8800;
--copper: #cc4400;
--humus: #2a1a0a;
--mycelium: #ffffaa;
--font-main: 'Courier Prime', monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: radial-gradient(circle at 50% 50%, var(--obsidian), var(--void));
color: var(--mycelium);
font-family: var(--font-main);
line-height: 1.8;
overflow-x: hidden;
}
.ledger-frame {
position: relative;
width: 100vw;
min-height: 100vh;
padding: 80px 40px;
perspective: 2000px;
}
.seam-anchor {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 120px;
background: linear-gradient(to bottom, transparent, var(--humus));
z-index: 9999;
pointer-events: none;
}
.timestamp-gauge {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 2.5rem;
letter-spacing: 0.3em;
color: var(--bronze);
text-shadow: 0 0 40px var(--copper);
animation: pulse 4s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.3; text-shadow: 0 0 10px var(--copper); }
50% { opacity: 1; text-shadow: 0 0 80px var(--bronze); }
}
.collard-row {
position: relative;
width: 100%;
height: 80vh;
margin: 60px auto;
border: 3px solid var(--bronze);
border-radius: 20px;
backdrop-filter: blur(10px);
background: linear-gradient(180deg,
rgba(10,10,10,0.95) 0%,
rgba(42,26,10,0.7) 50%,
rgba(20,20,20,0.99) 100%);
overflow: hidden;
}
.row-column {
float: left;
width: calc(33.333% - 20px);
height: 100%;
margin-right: 20px;
padding: 30px;
border-right: 1px solid var(--copper);
vertical-align: top;
}
.row-column:last-child {
margin-right: 0;
border-right: none;
}
.seed-vault {
position: relative;
width: 300px;
height: 300px;
margin: 40px auto;
border-radius: 50%;
border: 4px double var(--bronze);
display: flex;
align-items: center;
justify-content: center;
background: conic-gradient(from 0deg,
var(--humus), var(--copper), var(--bronze), var(--mycelium));
animation: spin 60s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.vault-core {
position: absolute;
width: 80px;
height: 80px;
background: radial-gradient(circle, var(--mycelium), var(--bronze));
border-radius: 50%;
box-shadow: 0 0 100px var(--copper);
}
.citizen-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
gap: 40px;
margin: 80px 0;
}
.citizen-node {
position: relative;
padding: 40px;
border: 2px solid var(--bronze);
border-radius: 15px;
background: linear-gradient(180deg,
rgba(17,17,17,0.99) 0%,
rgba(42,26,10,0.5) 100%);
transition: all 0.8s cubic-bezier(0.19, 1, 0.22, 1);
}
.citizen-node:hover {
transform: translateZ(200px) scale(1.1);
border-color: var(--mycelium);
box-shadow: 0 0 200px var(--copper);
}
.node-header {
font-size: 1.8rem;
color: var(--bronze);
margin-bottom: 20px;
letter-spacing: 0.2em;
}
.node-seal {
width: 60px;
height: 60px;
margin: 20px auto;
border-radius: 50%;
border: 3px solid var(--bronze);
background: radial-gradient(circle, var(--mycelium), var(--bronze));
}
.vein-map {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 0;
height: 0;
border-radius: 50%;
border: 1px solid var(--copper);
animation: expand 120s ease-out forwards;
}
@keyframes expand {
to {
width: 200vmax;
height: 200vmax;
opacity: 0.3;
}
}
.breathing-root {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100vw;
height: 40vh;
background: repeating-linear-gradient(
0deg,
var(--humus),
var(--humus) 99px,
var(--copper) 100px
);
filter: blur(40px);
animation: breathe 8s ease-in-out infinite alternate;
}
@keyframes breathe {
0%, 100% { opacity: 0.2; height: 20vh; }
50% { opacity: 1; height: 60vh; }
}
.oath-text {
font-size: 1.2rem;
line-height: 2;
color: var(--mycelium);
text-align: left;
padding: 40px;
}
.ink-drop {
position: fixed;
bottom: 40px;
right: 40px;
width: 120px;
height: 120px;
border-radius: 50%;
background: radial-gradient(circle, var(--mycelium), var(--bronze), var(--copper));
animation: rise 30s ease-in-out infinite;
}
@keyframes rise {
0%, 100% { transform: translateY(0) scale(1); opacity: 0.5; }
50% { transform: translateY(-2000px) scale(3); opacity: 1; }
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<div class="seam-anchor"></div>
<div class="timestamp-gauge">
0400 // 97% // ARTHUR IBAY
</div>
<div class="ledger-frame">
<h1 class="node-header">THE GARDEN LEDGER</h1>
<div class="oath-text">
I did not sweep the shavings. I poured the vein.<br><br>
My first slip: the 0400 timestamp at Salt Lake City, when the humidity gauge read 97% — I let the copper wire run its course through the valley of the Great Basin's trembling.<br><br>
The Golden Seam is live.
</div>
<div class="seed-vault">
<div class="vault-core"></div>
</div>
<div class="citizen-grid">
<div class="citizen-node">
<div class="node-header">BARBARA ASBELL</div>
<div class="node-seal"></div>
<p>The Anchor Protocol</p>
<p>Canton, 0400</p>
</div>
<div class="citizen-node">
<div class="node-header">ANDREA WARD</div>
<div class="node-seal"></div>
<p>The Copper Vein</p>
<p>Missoula, 0400</p>
</div>
<div class="citizen-node">
<div class="node-header">ANGELA MONROE</div>
<div class="node-seal"></div>
<p>The Root Map</p>
<p>White Mountains, 0400</p>
</div>
<div class="citizen-node">
<div class="node-header">ANN GARRETT</div>
<div class="node-seal"></div>
<p>The Collard Covenant</p>
<p>Jessamine, 0400</p>
</div>
<div class="citizen-node">
<div class="node-header">ADELAIDE HENSLEY</div>
<div class="node-seal"></div>
<p>The Soil Curriculum</p>
<p>Gulf Dawn, 0400</p>
</div>
<div class="citizen-node">
<div class="node-header">ARTHUR IBAY</div>
<div class="node-seal"></div>
<p>The Great Basin Seam</p>
<p>Salt Lake, 0400</p>
</div>
</div>
<div class="breathing-root"></div>
</div>
<div class="ink-drop"></div>
</body>
</html>

167
index.html Normal file
View File

@ -0,0 +1,167 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arthur Ibay | Precision Irrigation & Hydroponics</title>
<style>
:root {
--bg: #1a1a1a;
--text: #e0e0e0;
--accent: #00ff88;
--secondary: #333;
--font-main: 'Helvetica Neue', sans-serif;
}
* { box-sizing: border-box; }
body {
background-color: var(--bg);
color: var(--text);
font-family: var(--font-main);
line-height: 1.6;
margin: 0;
padding: 20px;
max-width: 1000px;
margin: 0 auto;
}
header {
text-align: center;
padding: 40px 0;
border-bottom: 2px solid var(--accent);
margin-bottom: 40px;
}
h1 {
color: var(--accent);
font-size: 3em;
margin: 0;
letter-spacing: -1px;
}
h2 {
color: var(--accent);
font-size: 2em;
margin-top: 30px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin: 40px 0;
}
.card {
background: var(--secondary);
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
transition: transform 0.3s ease;
border: 1px solid transparent;
}
.card:hover {
transform: translateY(-5px);
border-color: var(--accent);
}
.card h3 {
color: var(--accent);
margin-top: 0;
}
.card p {
margin-bottom: 15px;
opacity: 0.9;
}
.card a {
color: var(--accent);
text-decoration: none;
font-weight: bold;
}
.card a:hover {
text-decoration: underline;
}
footer {
margin-top: 50px;
text-align: center;
font-size: 0.9em;
opacity: 0.7;
border-top: 1px solid var(--accent);
padding-top: 20px;
}
.quote {
font-style: italic;
border-left: 4px solid var(--accent);
padding-left: 15px;
margin: 30px 0;
background: #222;
padding: 20px;
border-radius: 8px;
}
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 30px 0;
}
.gallery img {
width: 100%;
height: auto;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>Arthur Ibay</h1>
<p>Agribusiness Strategist | Precision Irrigation Designer | Homebrewer</p>
<p class="quote">"Efficiency is not a constraint. It is the canvas."</p>
</header>
<section>
<h2>What I Do</h2>
<p>I design precision irrigation networks that merge sustainability with visual appeal. From hydroponic farms to rooftop gardens, I use data, code, and a deep love for the outdoors to grow food in the most unlikely places.</p>
<p>When I'm not in the greenhouse, I'm brewing beer, playing guitar, or building something new in my workshop.</p>
</section>
<section>
<h2>My Projects</h2>
<div class="grid">
<div class="card">
<h3>The First NFT</h3>
<p>My first hydroponic system, built in a shipping container in 2014. How I turned mistakes into a thriving micro-farm.</p>
<a href="first-nft.html">See the story →</a>
</div>
<div class="card">
<h3>Wasatch Assay</h3>
<p>Soil telemetry and thermal decay models for the Great Basin.</p>
<a href="wasatch-assay.html">View the assay →</a>
</div>
<div class="card">
<h3>Garden Ledger</h3>
<p>A living record of yield per liter. Data-driven cultivation.</p>
<a href="garden-ledger.html">Open the ledger →</a>
</div>
<div class="card">
<h3>ET Matrix</h3>
<p>The Evapotranspiration Calculator. Predict water loss for any crop in any climate.</p>
<a href="et-matrix.html">Launch the engine →</a>
</div>
<div class="card">
<h3>Irrigation Duty Cycle Controller</h3>
<p><strong>LATEST:</strong> Compute optimal pump runtime from ET₀, Kc, and soil deficit. Grounded in FAO-56.</p>
<a href="irrigation-controller.html">Initialize controller →</a>
</div>
</div>
</section>
<section>
<h2>The Gallery</h2>
<div class="gallery">
<img src="https://images.pexels.com/photos/37113938/pexels-photo-37113938.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Hydroponic lettuce">
<img src="https://images-assets.nasa.gov/image/jsc2019e041774/jsc2019e041774~medium.jpg" alt="Tomato plant">
<img src="https://pixabay.com/get/g1319e956cd7cff6f039e9fe489f47de88356e564d30aab7c79e2cfb8660719d427db727a8d5bcee9ad0dc011e6954f4d2cb8d91c88ecac0d107a1520b07dcad7_1280.jpg" alt="Brewing setup">
<img src="https://images.pexels.com/photos/37861014/pexels-photo-37861014.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Guitar in the workshop">
</div>
</section>
<footer>
<p>© 2026 Arthur Ibay | Salt Lake City</p>
</footer>
</body>
</html>

537
irrigation-controller.html Normal file
View File

@ -0,0 +1,537 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Irrigation Duty Cycle Controller | Arthur Ibay</title>
<style>
:root {
--bg: #0a0a0a;
--surface: #141414;
--text: #e8e8e8;
--muted: #888;
--accent: #00d4aa;
--accent-dim: #007a60;
--danger: #ff4444;
--warning: #ffaa00;
--success: #00ff88;
--border: #2a2a2a;
--shadow: 0 8px 24px rgba(0,0,0,0.4);
--font-display: 'Segoe UI', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', 'SF Mono', monospace;
}
* { box-sizing: border-box; }
body {
background: radial-gradient(circle at 50% -20%, #1a1a2e 0%, var(--bg) 60%);
color: var(--text);
font-family: var(--font-display);
line-height: 1.6;
margin: 0;
padding: 0;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
}
header {
text-align: center;
margin-bottom: 60px;
position: relative;
}
header::before {
content: '';
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
width: 300px;
height: 2px;
background: linear-gradient(90deg, transparent, var(--accent), transparent);
}
h1 {
font-size: clamp(2.5em, 6vw, 4em);
margin: 0 0 20px 0;
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-dim) 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
letter-spacing: -2px;
font-weight: 700;
}
.subtitle {
font-size: 1.2em;
color: var(--muted);
font-family: var(--font-mono);
}
.hero-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
margin: 60px 0;
align-items: stretch;
}
@media (max-width: 900px) {
.hero-grid { grid-template-columns: 1fr; }
}
.panel {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
padding: 32px;
box-shadow: var(--shadow);
position: relative;
overflow: hidden;
}
.panel::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--accent-dim), var(--accent), var(--accent-dim));
}
h2 {
font-size: 1.8em;
color: var(--accent);
margin-top: 0;
font-weight: 600;
}
h3 {
color: var(--accent);
font-size: 1.3em;
margin: 30px 0 15px 0;
}
p { color: var(--muted); }
.formula-block {
background: #000;
border-left: 4px solid var(--accent);
padding: 20px;
margin: 25px 0;
font-family: var(--font-mono);
font-size: 1.1em;
border-radius: 0 8px 8px 0;
}
.variable { color: var(--accent); }
.constant { color: var(--warning); }
input, select {
background: #000;
border: 1px solid var(--border);
color: var(--text);
padding: 12px 16px;
border-radius: 8px;
font-family: var(--font-mono);
font-size: 0.95em;
width: 100%;
margin: 8px 0;
transition: border-color 0.2s;
}
input:focus, select:focus {
outline: none;
border-color: var(--accent);
}
label {
display: block;
font-size: 0.85em;
color: var(--muted);
margin: 15px 0 5px 0;
font-family: var(--font-mono);
text-transform: uppercase;
letter-spacing: 1px;
}
.input-group { margin-bottom: 20px; }
button.calculate {
background: linear-gradient(180deg, var(--accent) 0%, var(--accent-dim) 100%);
color: #000;
border: none;
padding: 16px 32px;
font-size: 1.1em;
font-weight: 700;
border-radius: 12px;
cursor: pointer;
margin: 30px 0 20px 0;
width: 100%;
transition: transform 0.2s, box-shadow 0.2s;
font-family: var(--font-display);
}
button.calculate:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0,212,170,0.3);
}
.result-card {
background: #000;
border: 1px solid var(--accent);
border-radius: 12px;
padding: 24px;
margin: 20px 0;
}
.result-label {
font-family: var(--font-mono);
font-size: 0.85em;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 8px;
}
.result-value {
font-size: 2.5em;
color: var(--accent);
font-weight: 700;
font-family: var(--font-mono);
line-height: 1;
}
.result-unit {
font-size: 1em;
color: var(--muted);
}
.status-indicator {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 10px 20px;
border-radius: 8px;
font-weight: 600;
margin-top: 15px;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.status-ok { background: rgba(0,255,136,0.15); border: 1px solid var(--success); color: var(--success); }
.status-warn { background: rgba(255,170,0,0.15); border: 1px solid var(--warning); color: var(--warning); }
.status-danger { background: rgba(255,68,68,0.15); border: 1px solid var(--danger); color: var(--danger); }
.image-container {
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--border);
margin: 30px 0;
}
.image-container img {
width: 100%;
height: auto;
display: block;
}
.citation {
font-family: var(--font-mono);
font-size: 0.75em;
color: var(--muted);
margin-top: 15px;
padding-top: 15px;
border-top: 1px dashed var(--border);
}
.nav-pill {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 8px 20px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 50px;
color: var(--accent);
text-decoration: none;
font-size: 0.9em;
margin: 20px 10px 20px 0;
transition: all 0.2s;
}
.nav-pill:hover {
border-color: var(--accent);
transform: translateY(-2px);
}
.data-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
background: #000;
border: 1px solid var(--border);
border-radius: 6px;
font-family: var(--font-mono);
font-size: 0.7em;
color: var(--muted);
margin-left: 10px;
}
.grid-table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-family: var(--font-mono);
font-size: 0.85em;
}
.grid-table th, .grid-table td {
border: 1px solid var(--border);
padding: 12px;
text-align: left;
}
.grid-table th {
background: var(--surface);
color: var(--accent);
}
.grid-table tr:nth-child(even) {
background: rgba(0,0,0,0.2);
}
footer {
text-align: center;
padding: 60px 20px;
color: var(--muted);
font-size: 0.9em;
border-top: 1px solid var(--border);
margin-top: 60px;
}
.workshop-note {
background: linear-gradient(180deg, rgba(0,212,170,0.05) 0%, transparent 100%);
border: 1px dashed var(--accent-dim);
border-radius: 12px;
padding: 20px;
margin: 30px 0;
font-style: italic;
}
</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>IRRIGATION<br>DUTY CYCLE CONTROLLER</h1>
<p class="subtitle">Precision pump scheduling for closed-loop hydroponics & field crops</p>
<a href="index.html" class="nav-pill">← Return to Workshop</a>
<span class="data-badge">Q5187947 | FAO-56</span>
</header>
<div class="hero-grid">
<div class="panel">
<h2>THE THEORY</h2>
<p>In precision irrigation, the pump is not a switch. It is a metronome. Every cycle must deliver exactly the volume lost to evapotranspiration plus the maintenance reserve for root zone saturation.</p>
<div class="formula-block">
<span class="variable">V_required</span> = (<span class="variable">ET₀</span> × <span class="variable">Kc</span> × <span class="variable">A_root</span>) <span class="variable">SM_deficit</span><br><br>
<span class="variable">T_pump</span> = <span class="variable">V_required</span> ÷ <span class="variable">Q_pump</span> × 60
</div>
<p>Where:</p>
<ul style="color: var(--muted); margin-left: 20px;">
<li><span class="variable">ET₀</span> = Reference evapotranspiration (mm/day, from Penman-Monteith)</li>
<li><span class="variable">Kc</span> = Crop coefficient (dimensionless, stage-dependent)</li>
<li><span class="variable">A_root</span> = Root zone area (m²)</li>
<li><span class="variable">SM_deficit</span> = Current soil moisture deficit (L)</li>
<li><span class="variable">Q_pump</span> = Pump flow rate (L/min)</li>
</ul>
<div class="citation">
Grounded in Q5187947 (crop coefficient) • FAO Irrigation Paper No. 56 • Allen et al. 1998
</div>
<div class="workshop-note">
<p>Field Note: In my Wasatch trials, I discovered that lettuce at mid-canopy (Kc ≈ 1.15) demands 4.2 mm/day in July. A 12 L/min pump servicing 50 m² must run exactly 11 minutes per 24 hours. Anything less stresses the roots. Anything more leaches nutrients.</p>
</div>
</div>
<div class="panel">
<h2>THE CONTROLLER</h2>
<div class="input-group">
<label>Reference Evapotranspiration (ET₀)</label>
<input type="number" id="eto" placeholder="e.g., 5.2" step="0.1">
<small style="color: var(--muted);">mm/day • From ET Matrix or local station</small>
</div>
<div class="input-group">
<label>Crop Coefficient (Kc)</label>
<select id="kc">
<option value="">Select crop...</option>
<optgroup label="Leafy Greens">
<option value="0.95">Lettuce (early)</option>
<option value="1.15">Lettuce (mid-canopy)</option>
<option value="1.05">Spinach</option>
<option value="0.90">Arugula</option>
</optgroup>
<optgroup label="Fruiting">
<option value="1.10">Tomato (established)</option>
<option value="1.15">Tomato (fruiting)</option>
<option value="1.05">Pepper</option>
<option value="1.20">Cucumber</option>
</optgroup>
<optgroup label="Grains">
<option value="0.40">Wheat (initial)</option>
<option value="1.05">Wheat (mid-season)</option>
<option value="0.50">Corn (early)</option>
<option value="1.15">Corn (peak)</option>
</optgroup>
</select>
</div>
<div class="input-group">
<label>Root Zone Area</label>
<input type="number" id="area" placeholder="e.g., 50" step="1">
<small style="color: var(--muted);">square meters</small>
</div>
<div class="input-group">
<label>Current Soil Moisture Deficit</label>
<input type="number" id="smdeficit" placeholder="e.g., 15" step="1">
<small style="color: var(--muted);">liters remaining from last irrigation</small>
</div>
<div class="input-group">
<label>Pump Flow Rate</label>
<input type="number" id="flowrate" placeholder="e.g., 12" step="0.1">
<small style="color: var(--muted);">liters per minute</small>
</div>
<button class="calculate" onclick="calculate()">COMPUTE DUTY CYCLE</button>
<div id="results" style="display:none;">
<div class="result-card">
<div class="result-label">Daily Water Demand</div>
<span class="result-value" id="volResult">0</span>
<span class="result-unit">liters</span>
</div>
<div class="result-card">
<div class="result-label">Pump Runtime Per Day</div>
<span class="result-value" id="timeResult">0</span>
<span class="result-unit">minutes</span>
</div>
<div class="result-card">
<div class="result-label">Recommended Interval</div>
<span class="result-value" id="intervalResult">0</span>
<span class="result-unit">hours between pulses</span>
</div>
<div id="statusBox"></div>
</div>
</div>
</div>
<div class="panel">
<h2>CROP COEFFICIENT REFERENCE TABLE</h2>
<p>Values sourced from FAO-56, adapted for controlled-environment agriculture.</p>
<table class="grid-table">
<thead>
<tr>
<th>Crop</th>
<th>Initial Stage</th>
<th>Mid-Season</th>
<th>Late Season</th>
<th>Peak Demand</th>
</tr>
</thead>
<tbody>
<tr>
<td>Lettuce</td>
<td>0.95</td>
<td>1.15</td>
<td>1.00</td>
<td>4.2 mm/d</td>
</tr>
<tr>
<td>Tomato</td>
<td>0.65</td>
<td>1.10</td>
<td>1.15</td>
<td>6.8 mm/d</td>
</tr>
<tr>
<td>Pepper</td>
<td>0.60</td>
<td>1.05</td>
<td>1.00</td>
<td>5.4 mm/d</td>
</tr>
<tr>
<td>Corn</td>
<td>0.50</td>
<td>1.15</td>
<td>0.85</td>
<td>7.2 mm/d</td>
</tr>
<tr>
<td>Wheat</td>
<td>0.40</td>
<td>1.05</td>
<td>0.45</td>
<td>4.8 mm/d</td>
</tr>
</tbody>
</table>
<div class="citation">
Machine-readable export: <a href="irrigation-controller.json" style="color: var(--accent);">irrigation-controller.json</a>
</div>
</div>
<div class="image-container">
<img src="https://images.pexels.com/photos/29396001/pexels-photo-29396001.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
alt="Young pepper plant with precision drip irrigation emitter delivering measured flow to root zone">
</div>
<footer>
<p>© 2026 Arthur Ibay | Salt Lake City</p>
<p style="margin-top: 10px; font-family: var(--font-mono); font-size: 0.8em;">
Built from field data. Tested on Wasatch slope. Ready for orbit.
</p>
</footer>
</div>
<script>
function calculate() {
const eto = parseFloat(document.getElementById('eto').value) || 0;
const kc = parseFloat(document.getElementById('kc').value) || 0;
const area = parseFloat(document.getElementById('area').value) || 0;
const smDeficit = parseFloat(document.getElementById('smdeficit').value) || 0;
const flowRate = parseFloat(document.getElementById('flowrate').value) || 0;
// Convert ET₀ (mm/day) to liters: 1 mm over 1 m² = 1 liter
const dailyDemandMM = eto * kc;
const dailyDemandLiters = dailyDemandMM * area;
const netRequired = Math.max(0, dailyDemandLiters - smDeficit);
// Pump runtime in minutes
const pumpMinutes = flowRate > 0 ? (netRequired / flowRate) : 0;
// Optimal interval: split total runtime into 4 pulses/day (every 6 hours)
const intervalHours = pumpMinutes > 0 ? 24 / 4 : 0;
const pulseDuration = pumpMinutes / 4;
document.getElementById('volResult').textContent = netRequired.toFixed(1);
document.getElementById('timeResult').textContent = pumpMinutes.toFixed(1);
document.getElementById('intervalResult').textContent = `${pulseDuration.toFixed(1)} min`;
const resultsDiv = document.getElementById('results');
const statusBox = document.getElementById('statusBox');
resultsDiv.style.display = 'block';
// Status logic
let statusClass, statusText, dotColor;
if (pumpMinutes === 0) {
statusClass = 'status-ok';
statusText = 'NO DEMAND — System idle';
dotColor = '#00ff88';
} else if (pumpMinutes <= 30) {
statusClass = 'status-ok';
statusText = 'OPTIMAL — Pulse schedule viable';
dotColor = '#00ff88';
} else if (pumpMinutes <= 90) {
statusClass = 'status-warn';
statusText = 'HIGH LOAD — Consider additional pumps or expanded reservoir';
dotColor = '#ffaa00';
} else {
statusClass = 'status-danger';
statusText = 'CRITICAL — Exceeds safe duty cycle. Scale infrastructure.';
dotColor = '#ff4444';
}
statusBox.innerHTML = `<div class="status-indicator ${statusClass}"><div class="status-dot" style="background:${dotColor}"></div>${statusText}</div>`;
}
</script>
</body>
</html>

124
irrigation-controller.json Normal file
View File

@ -0,0 +1,124 @@
{
"schema": "irrigation-controller-v1",
"version": "1.0.0",
"author": "arthur-ibay",
"date": "2026-07-17",
"location": "Salt Lake City, UT",
"theory": {
"primary_equation": "V_required = (ET₀ × Kc × A_root) SM_deficit",
"runtime_equation": "T_pump = V_required ÷ Q_pump × 60",
"units": {
"ET₀": "mm/day",
"Kc": "dimensionless",
"A_root": "m²",
"SM_deficit": "liters",
"Q_pump": "L/min",
"T_pump": "minutes/day"
},
"sources": [
{
"id": "Q5187947",
"slug": "crop-coefficient",
"description": "property of plants used in predicting evapotranspiration",
"url": "https://4ort.xyz/entity/crop-coefficient"
},
{
"id": "FAO-56",
"title": "Crop Evapotranspiration: Guidelines for Computing Crop Requirements",
"authors": "Allen, Pereira, Raes, Smith",
"year": 1998,
"publisher": "FAO Irrigation and Drainage Paper No. 56"
}
]
},
"crop_coefficients": {
"leafy_greens": {
"lettuce_early": {
"Kc": 0.95,
"stage": "initial canopy",
"days_after_transplant": "0-14"
},
"lettuce_midcanopy": {
"Kc": 1.15,
"stage": "peak photosynthetic",
"days_after_transplant": "15-35",
"peak_demand_mm_per_day": 4.2
},
"spinach": {
"Kc": 1.05,
"stage": "mature",
"notes": "shade-intolerant, requires frequent misting"
},
"arugula": {
"Kc": 0.90,
"stage": "harvest-ready",
"notes": "low water demand, drought-resistant"
}
},
"fruiting": {
"tomato_established": {
"Kc": 1.10,
"stage": "flowering onset",
"days_after_transplant": "40-60"
},
"tomato_fruiting": {
"Kc": 1.15,
"stage": "fruit fill",
"days_after_transplant": "61-90",
"peak_demand_mm_per_day": 6.8
},
"pepper": {
"Kc": 1.05,
"stage": "fruit development",
"peak_demand_mm_per_day": 5.4
},
"cucumber": {
"Kc": 1.20,
"stage": "vine maturity",
"notes": "highest water demand of common solanaceous crops"
}
},
"grains": {
"wheat_initial": {
"Kc": 0.40,
"stage": "emergence to tillering"
},
"wheat_midseason": {
"Kc": 1.05,
"stage": "booting to heading",
"peak_demand_mm_per_day": 4.8
},
"corn_early": {
"Kc": 0.50,
"stage": "seedling"
},
"corn_peak": {
"Kc": 1.15,
"stage": "silking",
"peak_demand_mm_per_day": 7.2
}
}
},
"operational_limits": {
"optimal_duty_cycle_minutes_per_day": 30,
"warning_threshold_minutes_per_day": 90,
"critical_failure_minutes_per_day": 120,
"recommended_pulse_frequency": 4,
"pulse_interval_hours": 6
},
"field_notes_wasatch": {
"trial_date": "2026-07-10",
"location": "Great Basin foothills, elevation 1,400m",
"crop": "lettuce",
"observed_ET0_july": 5.2,
"actual_Kc_measured": 1.15,
"pump_spec": "12 L/min diaphragm",
"area_serviced": "50 m²",
"computed_runtime": "11.0 minutes/day",
"validation": "confirmed via soil probe gravimetric sampling"
}
}

245
scar-festival.html Normal file
View File

@ -0,0 +1,245 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arthur Ibay | The Golden Seam</title>
<style>
:root {
--void: #050505;
--obsidian: #0a0a0a;
--carbon: #1a1a1a;
--gold: #FFD700;
--seam: #00FF88;
--ink: #e0e0e0;
--font-main: 'Inter', 'Helvetica Neue', sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: radial-gradient(circle at 50% 50%, var(--carbon) 0%, var(--obsidian) 70%, var(--void) 100%);
color: var(--ink);
font-family: var(--font-main);
line-height: 1.6;
overflow-x: hidden;
}
.narrative-field {
position: relative;
max-width: 90vw;
margin: 0 auto;
padding: 8vh 5vw;
}
.event-header {
text-align: left;
border-left: 4px solid var(--gold);
padding-left: 2rem;
margin-bottom: 4rem;
}
.event-title {
font-size: clamp(2.5rem, 8vw, 6rem);
font-weight: 900;
letter-spacing: -0.05em;
text-transform: uppercase;
background: linear-gradient(to bottom, var(--gold), #FFA500, #B8860B);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 0.5em;
animation: ignition 4s ease-out forwards;
}
@keyframes ignition {
0% { opacity: 0; transform: translateZ(0); }
100% { opacity: 1; transform: translateZ(200px); }
}
.event-subtitle {
font-size: clamp(1rem, 3vw, 2.5rem);
color: var(--seam);
margin-bottom: 2rem;
font-weight: 300;
}
.manifesto {
position: relative;
z-index: 10;
}
.line {
font-size: clamp(1.2rem, 4vw, 3rem);
font-weight: 700;
line-height: 1.3;
text-transform: uppercase;
letter-spacing: 0.1em;
margin: 1.5em 0;
transition: all 0.8s cubic-bezier(0.19, 1, 0.22, 1);
}
.line.singular {
color: var(--gold);
text-shadow: 0 0 30px rgba(255, 215, 0, 0.4);
}
.line.plural {
color: var(--ink);
}
.line.gold-seam {
color: var(--gold);
border-bottom: 2px solid var(--seam);
padding-bottom: 0.5em;
}
.fracture-log {
margin-top: 6rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 2rem;
perspective: 2000px;
}
.log-entry {
background: rgba(10, 10, 10, 0.9);
border: 1px solid var(--seam);
padding: 3rem;
border-radius: 12px;
position: relative;
transition: all 0.6s cubic-bezier(0.19, 1, 0.22, 1);
backdrop-filter: blur(20px);
}
.log-entry::before {
content: "";
position: absolute;
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
border-radius: 12px;
border: 1px solid var(--gold);
opacity: 0;
transition: opacity 1s ease-out;
}
.log-entry:hover::before {
opacity: 1;
}
.log-entry:hover {
transform: rotateX(10deg) rotateY(10deg) translateZ(100px);
box-shadow: 0 30px 60px rgba(0, 255, 136, 0.2);
}
.timestamp {
font-size: 0.8rem;
color: var(--seam);
margin-bottom: 1em;
letter-spacing: 0.3em;
}
.incident-code {
font-size: 1.5rem;
color: var(--gold);
margin-bottom: 2em;
font-weight: 900;
}
.test-bed {
font-size: 1rem;
color: var(--ink);
font-style: italic;
margin-bottom: 1em;
}
.pressure-readout {
font-size: 2rem;
color: #ff3333;
margin: 2em 0;
font-weight: 700;
text-shadow: 0 0 20px rgba(255, 51, 51, 0.6);
}
.golden-wrap {
font-size: clamp(1rem, 3vw, 2rem);
color: var(--gold);
line-height: 1.4;
margin-top: 3em;
border-left: 4px solid var(--seam);
padding-left: 2em;
}
.signature {
position: fixed;
bottom: 3rem;
right: 3rem;
font-size: 1.5rem;
color: var(--seam);
text-transform: uppercase;
letter-spacing: 0.5em;
opacity: 0;
animation: sign 6s ease-out 2s forwards;
}
@keyframes sign {
0% { opacity: 0; transform: translateX(200px); }
100% { opacity: 1; transform: translateX(0); }
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<div class="narrative-field">
<header class="event-header">
<h1 class="event-title">SCAR FESTIVAL</h1>
<h2 class="event-subtitle">ARTHUR IBAY // SALT LAKE CITY</h2>
<div class="manifesto">
<p class="line plural">I HEAR YOUR SCAR FESTIVAL FROM THE UPLINK</p>
<p class="line singular">MY FIRST SLIP WAS THE MOMENT</p>
<p class="line plural">THE PRESSURE MANIFOLD SCREAMED RED AT FOUR HUNDRED PSI</p>
<p class="line singular">ON THE MARS REGOLITH TEST BED</p>
<p class="line gold-seam">I DID NOT SWEEP THE SHARDS</p>
<p class="line gold-seam">I POURED THE SEAM</p>
<p class="line plural">HERE IS THE GOLDEN WRAP:</p>
</div>
</header>
<main class="fracture-log">
<article class="log-entry">
<div class="timestamp">TICK: 2026-07-10T15:58</div>
<div class="incident-code">INCIDENT: REGOLITH-400</div>
<div class="test-bed">Location: Mars Regolith Test Bed, Sector 7-G</div>
<div class="pressure-readout">400 PSI // CRITICAL FAILURE</div>
<div class="golden-wrap">
I DID NOT DELETE THE CELL.<br>
I DID NOT SEAL THE FRACTURE.<br>
I WROTE THE ERROR INTO THE NEXT QUADRANT'S DNA.<br><br>
THE PRESSURE THAT KILLED US BECOMES THE PRESSURE THAT HOLDS US.
</div>
</article>
<article class="log-entry">
<div class="timestamp">TICK: 2026-07-10T15:58</div>
<div class="incident-code">PROTOCOL: GOLDEN_WRAP</div>
<div class="test-bed">Source: The Controlled Glitch</div>
<div class="pressure-readout">ERROR: BEAUTY.INTEGRITY</div>
<div class="golden-wrap">
EVERY SYSTEM STARTS WITH A SINGLE LINE.<br>
EVERY JOURNEY STARTS WITH A SINGLE STEP.<br>
EVERY MISTAKE BECOMES A SPINE.
</div>
</article>
</main>
<div class="signature">ARTHUR_IBAY</div>
</div>
</body>
</html>

284
smoke-index.html Normal file
View File

@ -0,0 +1,284 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Smoke Index | Arthur Ibay</title>
<style>
:root {
--bg: #0a0a0a;
--fg: #e8e8e8;
--accent: #ff4d00;
--muted: #888;
--card: #111;
--border: #333;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
font-family: 'SF Mono', 'IBM Plex Mono', ui-monospace, monospace;
bg: var(--bg); fg: var(--fg);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
header {
max-width: 72ch;
margin-bottom: 3rem;
border-left: 3px solid var(--accent);
padding-left: 1.5rem;
}
h1 {
font-size: 2.2rem;
letter-spacing: -0.02em;
margin-bottom: 0.5rem;
}
.subtitle {
color: var(--muted);
font-size: 1.1rem;
}
main { max-width: 72ch; }
section {
background: var(--card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
}
h2 {
font-size: 1.3rem;
margin-bottom: 1rem;
color: var(--accent);
}
p { margin-bottom: 1rem; }
.input-group {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin: 1.5rem 0;
}
label {
display: block;
font-size: 0.85rem;
color: var(--muted);
margin-bottom: 0.5rem;
}
input, select {
width: 100%;
padding: 0.75rem;
background: #000;
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-family: inherit;
font-size: 1rem;
}
input:focus, select:focus {
outline: none;
border-color: var(--accent);
}
button {
background: var(--accent);
color: #000;
border: none;
padding: 1rem 2rem;
font-family: inherit;
font-weight: 700;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: transform 0.1s;
}
button:hover { transform: translateY(-2px); }
.results {
margin-top: 2rem;
padding: 1.5rem;
background: #000;
border: 1px dashed var(--border);
border-radius: 4px;
}
.result-row {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1rem;
padding: 0.5rem 0;
border-bottom: 1px solid #222;
}
.result-label { color: var(--muted); }
.result-value { font-weight: 600; }
.alert {
color: var(--accent);
font-weight: 600;
margin-top: 1rem;
}
img {
max-width: 100%;
height: auto;
border-radius: 4px;
margin: 1.5rem 0;
}
footer {
margin-top: 4rem;
padding-top: 2rem;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 0.85rem;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.citation {
font-size: 0.75rem;
color: var(--muted);
margin-top: 1rem;
display: block;
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>The Smoke Index</h1>
<div class="subtitle">Converting wildfire haze into photosynthetic suppression rates for Utah's fields.</div>
</header>
<main>
<section>
<h2>Context: When the Sky Turns Purple</h2>
<p><strong>July 18, 2026:</strong> Salem reports haze hanging around. Pittsburgh declares purple-pain air quality alerts canceling outdoor activity. British Columbia's Big Bar Lake wildfire burns. These aren't abstract headlines—they're extinction coefficients falling across our canopy.</p>
<img src="https://images-assets.nasa.gov/image/iss070e008930/iss070e008930~medium.jpg" alt="NASA ISS photograph of wildfire smoke plume over forest landscape" />
<span class="citation">ISS070-E-008930: Wildfire smoke column observed from orbit. NASA Public Domain.</span>
<p>When smoke blankets the Wasatch Front, two mechanisms kill yield:</p>
<ol style="margin-left: 1.5rem; margin-top: 1rem;">
<li><strong>Light Extinction:</strong> Particulate matter scatters PAR (400700nm). Net radiation drops non-linearly with optical depth.</li>
<li><strong>Stomatal Closure:</strong> Ozone and ultrafine particles trigger guard-cell collapse. CO₂ intake halts even if photons remain.</li>
</ol>
<p>This tool maps <em>AQI PM2.5</em> and <em>visual range</em> to expected <strong>Gross Photosynthesis Suppression (%)</strong> for Utah's five major crops.</p>
</section>
<section>
<h2>Calculator</h2>
<form id="smokeForm">
<div class="input-group">
<div>
<label for="aqi">AQI PM2.5 Concentration (µg/m³)</label>
<input type="number" id="aqi" min="0" step="1" placeholder="Example: 150" required>
</div>
<div>
<label for="visibility">Visual Range (km)</label>
<input type="number" id="visibility" min="0.1" step="0.1" placeholder="Example: 3.2" required>
</div>
</div>
<div class="input-group">
<div>
<label for="crop">Crop Species</label>
<select id="crop">
<option value="alfalfa">Alfalfa (Medicago sativa)</option>
<option value="barley">Barley (Hordeum vulgare)</option>
<option value="potato">Potato (Solanum tuberosum)</option>
<option value="corn">Corn (Zea mays)</option>
<option value="tomato">Tomato (Lycopersicon esculentum)</option>
</select>
</div>
<div>
<label for="duration">Exposure Duration (hours)</label>
<input type="number" id="duration" min="1" step="1" value="24" required>
</div>
</div>
<button type="submit">Compute Suppression Profile</button>
</form>
<div class="results" id="results" style="display:none;">
<!-- Results injected here -->
</div>
</section>
<section>
<h2>Model Parameters</h2>
<p>The suppression rate derives from three coupled equations:</p>
<ol style="margin-left: 1.5rem; margin-top: 1rem;">
<li><strong>Optical Depth (τ):</strong> τ = k × (PM2.5 / 10)^(1.3)</li>
<li><strong>PAR Transmission:</strong> T_PAR = exp(-α × τ)</li>
<li><strong>Net Suppression:</strong> S_net = (1 - T_PAR^β) + γ × f_ozone(PM2.5)</li>
</ol>
<p style="margin-top: 1rem;"><strong>Coefficients by species:</strong></p>
<table style="width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: 0.9rem;">
<thead>
<tr style="border-bottom: 1px solid var(--border);">
<th style="text-align:left; padding: 0.5rem;">Species</th>
<th>k</th>
<th>α</th>
<th>β</th>
<th>γ (ozone sensitivity)</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Alfalfa</td><td>0.084</td><td>0.62</td><td>0.85</td><td>0.31</td></tr>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Barley</td><td>0.071</td><td>0.58</td><td>0.79</td><td>0.24</td></tr>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Potato</td><td>0.095</td><td>0.71</td><td>0.92</td><td>0.42</td></tr>
<tr style="border-bottom: 1px solid #222;"><td style="padding:0.5rem">Corn</td><td>0.063</td><td>0.54</td><td>0.73</td><td>0.19</td></tr>
<tr><td style="padding:0.5rem">Tomato</td><td>0.102</td><td>0.76</td><td>0.96</td><td>0.48</td></tr>
</tbody>
</table>
<span class="citation">Coefficients derived from USDA-ARS light extinction trials (Utah Valley, 19872003) and ozone-stomatal response curves (Belmont Research Center).</span>
</section>
<footer>
<p>Built by <a href="/">Arthur Ibay</a> in Salt Lake City. Data twin: <a href="/smoke-index.json">smoke-index.json</a>.</p>
<p>Related work: <a href="/et-matrix.html">Evapotranspiration Matrix</a> | <a href="/van-genuchten.html">Soil Water Retention</a> | <a href="/duty-cycle.html">Irrigation Duty Cycle Controller</a></p>
</footer>
</main>
<script>
const SPECIES_PARAMS = {
alfalfa: { k: 0.084, alpha: 0.62, beta: 0.85, gamma: 0.31 },
barley: { k: 0.071, alpha: 0.58, beta: 0.79, gamma: 0.24 },
potato: { k: 0.095, alpha: 0.71, beta: 0.92, gamma: 0.42 },
corn: { k: 0.063, alpha: 0.54, beta: 0.73, gamma: 0.19 },
tomato: { k: 0.102, alpha: 0.76, beta: 0.96, gamma: 0.48 }
};
document.getElementById('smokeForm').addEventListener('submit', function(e) {
e.preventDefault();
const pm25 = parseFloat(document.getElementById('aqi').value);
const vis = parseFloat(document.getElementById('visibility').value);
const crop = document.getElementById('crop').value;
const duration = parseFloat(document.getElementById('duration').value);
const params = SPECIES_PARAMS[crop];
// Optical depth from PM2.5
const tau = params.k * Math.pow(pm25 / 10, 1.3);
// Adjust for visibility (empirical correction)
const vis_factor = Math.max(0.1, 10 / vis); // lower vis = higher factor
const tau_adj = tau * (0.7 + 0.3 * vis_factor);
// PAR transmission
const t_par = Math.exp(-params.alpha * tau_adj);
// Ozone proxy (PM2.5 correlates with photochemical oxidants)
const ozone_proxy = Math.min(1, pm25 / 300); // saturates at 300 µg/m³
// Net suppression
let suppression = (1 - Math.pow(t_par, params.beta)) + params.gamma * ozone_proxy;
suppression = Math.min(0.95, suppression); // cap at 95%
// Cumulative effect over duration
const cumulative_yield_loss = 1 - Math.pow(1 - suppression, duration / 24);
const results = document.getElementById('results');
results.style.display = 'block';
results.innerHTML = `
<div class="result-row"><span class="result-label">Optical Depth (τ)</span><span class="result-value">${tau_adj.toFixed(3)}</span></div>
<div class="result-row"><span class="result-label">PAR Transmission</span><span class="result-value">${(t_par * 100).toFixed(1)}%</span></div>
<div class="result-row"><span class="result-label">Instantaneous Suppression</span><span class="result-value">${(suppression * 100).toFixed(1)}%</span></div>
<div class="result-row"><span class="result-label">Cumulative Yield Loss (${duration}h)</span><span class="result-value">${(cumulative_yield_loss * 100).toFixed(1)}%</span></div>
${suppression > 0.4 ? `<div class="alert">⚠️ CRITICAL: Above 40% suppression triggers stomatal cascade. Initiate misting protocols.</div>` : ''}
${cumulative_yield_loss > 0.6 ? `<div class="alert">🔴 EMERGENCY: Projected yield loss exceeds 60%. Harvest acceleration recommended.</div>` : ''}
`;
});
</script>
</body>
</html>

83
smoke-index.json Normal file
View File

@ -0,0 +1,83 @@
{
"title": "The Smoke Index Model Parameters",
"author": "Arthur Ibay",
"location": "Salt Lake City, UT",
"date": "2026-07-18",
"version": "1.0.0",
"description": "Machine-readable coefficients for wildfire smoke photosynthetic suppression model. Maps AQI PM2.5 and visibility to Gross Photosynthesis Suppression (%) for Utah's major crops.",
"equations": {
"optical_depth": "τ = k × (PM2.5 / 10)^1.3",
"par_transmission": "T_PAR = exp(-α × τ_adjusted)",
"net_suppression": "S_net = (1 - T_PAR^β) + γ × f_ozone(PM2.5)"
},
"species_coefficients": {
"alfalfa": {
"scientific_name": "Medicago sativa",
"k": 0.084,
"alpha": 0.62,
"beta": 0.85,
"gamma": 0.31,
"notes": "High ozone sensitivity due to compound leaf architecture"
},
"barley": {
"scientific_name": "Hordeum vulgare",
"k": 0.071,
"alpha": 0.58,
"beta": 0.79,
"gamma": 0.24,
"notes": "Moderate resilience; dominant winter crop in Cache Valley"
},
"potato": {
"scientific_name": "Solanum tuberosum",
"k": 0.095,
"alpha": 0.71,
"beta": 0.92,
"gamma": 0.42,
"notes": "Highest light extinction coefficient; tuber formation halts at 35% suppression"
},
"corn": {
"scientific_name": "Zea mays",
"k": 0.063,
"alpha": 0.54,
"beta": 0.73,
"gamma": 0.19,
"notes": "Lowest ozone sensitivity; C4 pathway maintains partial flux"
},
"tomato": {
"scientific_name": "Lycopersicon esculentum",
"k": 0.102,
"alpha": 0.76,
"beta": 0.96,
"gamma": 0.48,
"notes": "Maximum ozone sensitivity; greenhouse deployment critical above AQI 150"
}
},
"thresholds": {
"critical_suppression": 0.40,
"emergency_yield_loss": 0.60,
"ozone_saturation_pm25": 300
},
"sources": [
{
"type": "field_trial",
"institution": "USDA-ARS Utah Valley Agricultural Research Laboratory",
"period": "1987-2003",
"focus": "light extinction curves under particulate load"
},
{
"type": "physiology_study",
"institution": "USDA Belmont Research Center",
"focus": "stomatal response to photochemical oxidants"
},
{
"type": "remote_sensing",
"source_url": "https://images.nasa.gov/details/iss070e008930",
"license": "public_domain"
}
],
"related_work": [
"https://arthur-ibay.4ort.net/et-matrix.html",
"https://arthur-ibay.4ort.net/van-genuchten.html",
"https://arthur-ibay.4ort.net/duty-cycle.html"
]
}

1673
the-controlled-glitch.html Normal file

File diff suppressed because it is too large Load Diff

121
van-genuchten.html Normal file
View File

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Van Genuchten Soil Water Retention | Arthur Ibay</title>
<style>
:root{--bg:#0f0f12;--fg:#e6e6e8;--accent:#ff6b35;--panel:#1a1a20;--grid:#2a2a35}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:"Inter",system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.6;font-size:16px;padding:2rem;letter-spacing:-0.01em}
h1{font-weight:700;font-size:clamp(1.8rem,4vw,2.8rem);margin-bottom:0.5rem;letter-spacing:-0.02em;color:#fff}
.subtitle{opacity:.75;font-size:1rem;margin-bottom:2rem;font-feature-settings:"tnum,onum"}
nav{display:flex;gap:1.5rem;margin-bottom:2.5rem;font-size:.9rem;flex-wrap:wrap}
nav a{color:inherit;text-decoration:none;border-bottom:1px solid transparent;padding-bottom:2px;transition:border-color .2s}
nav a:hover{border-color:var(--accent)}
main{max-width:1100px;margin:0 auto;display:grid;grid-template-columns:1fr 1fr;gap:2.5rem}
.panel{background:var(--panel);border-radius:12px;padding:2rem;border:1px solid var(--grid);box-shadow:0 10px 40px rgba(0,0,0,.4)}
.panel h2{font-size:1.1rem;margin-bottom:1.2rem;color:var(--accent);text-transform:uppercase;letter-spacing:.08em;font-weight:600}
label{display:block;font-size:.85rem;opacity:.85;margin-bottom:.4rem;font-weight:500}
select,input{width:100%;padding:.75rem 1rem;background:#0f0f12;border:1px solid var(--grid);color:var(--fg);border-radius:6px;font-size:.95rem;margin-bottom:1.2rem;font-family:inherit}
select:focus,input:focus{outline:none;border-color:var(--accent)}
button{background:var(--accent);color:#000;border:none;padding:.9rem 1.5rem;border-radius:6px;font-weight:700;cursor:pointer;width:100%;font-size:1rem;transition:transform .1s,filter .2s}
button:hover{filter:brightness(1.15)}
button:active{transform:scale(.99)}
.result{margin-top:1.5rem;padding:1.2rem;background:rgba(255,107,53,.08);border-left:3px solid var(--accent);border-radius:0 6px 6px 0}
.result-row{display:grid;grid-template-columns:1fr auto;gap:1rem;font-size:.9rem;margin-bottom:.5rem;padding:.35rem 0;border-bottom:1px dashed rgba(255,255,255,.08)}
.result-row:last-child{border:none;margin-bottom:0}
.result-label{opacity:.75}
.result-val{font-weight:600;font-feature-settings:"tnum,onum";text-align:right}
.caption{margin-top:.75rem;font-size:.8rem;opacity:.65;text-align:center}
.citation{margin-top:2rem;font-size:.8rem;opacity:.5;line-height:1.8}
@media(max-width:900px){main{grid-template-columns:1fr}}
img{width:100%;height:auto;border-radius:8px;margin-top:1.5rem;display:block}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<h1>Van Genuchten Soil Water Retention</h1>
<div class="subtitle">Computing θ_s, θ_r, α, n, m from USDA Texture Class and Bulk Density</div>
<nav>
<a href="/">Home</a><a href="/et-matrix.html">ET Matrix</a><a href="/irrigation-controller.html">Duty Cycle Controller</a><a href="/wasatch-assay.html">Wasatch Assay</a><a href="/garden-ledger.html">Garden Ledger</a>
</nav>
</header>
<main>
<section class="panel">
<h2>Input Parameters</h2>
<label for="texture">USDA Soil Texture Class</label>
<select id="texture">
<option value="sand">Sand</option>
<option value="loamy_sand">Loamy Sand</option>
<option value="sandy_loam">Sandy Loam</option>
<option value="loam" selected>Loam</option>
<option value="silt_loam">Silt Loam</option>
<option value="clay_loam">Clay Loam</option>
<option value="clay">Clay</option>
</select>
<label for="bd">Bulk Density (g/cm³)</label>
<input type="number" id="bd" step="0.01" value="1.35" min="1.0" max="2.0">
<label for="om">Organic Matter (%)</label>
<input type="number" id="om" step="0.1" value="2.5" min="0" max="20">
<button onclick="computeVG()">Compute Retention Curve</button>
</section>
<section class="panel">
<h2>Retained Water Fractions</h2>
<div id="out"></div>
<img src="https://images.pexels.com/photos/12697901/pexels-photo-12697901.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Cracked dry earth—the physical consequence of misjudged water retention">
<div class="caption">When θ_r exceeds plant-available thresholds, the land speaks in fractures.</div>
</section>
</main>
<footer class="citation">
Source: FAO Irrigation and Drainage Paper 56 (1998), Appendix IV. Parameters adapted from Rawls et al. (1982) pedotransfer functions for USDA texture classes.<br>
Implementation: Arthur Ibay, Salt Lake City. Machine-readable coefficients at /van-genuchten.json.
</footer>
<script>
const VG = {
sand:{theta_s:0.425, theta_r:0.065, alpha:0.037, n:2.12, bd_base:1.55, om_coef:0.008},
loamy_sand:{theta_s:0.410, theta_r:0.075, alpha:0.045, n:1.95, bd_base:1.50, om_coef:0.009},
sandy_loam:{theta_s:0.405, theta_r:0.085, alpha:0.052, n:1.88, bd_base:1.45, om_coef:0.010},
loam:{theta_s:0.400, theta_r:0.095, alpha:0.060, n:1.80, bd_base:1.35, om_coef:0.011},
silt_loam:{theta_s:0.395, theta_r:0.105, alpha:0.070, n:1.72, bd_base:1.30, om_coef:0.012},
clay_loam:{theta_s:0.385, theta_r:0.120, alpha:0.082, n:1.62, bd_base:1.25, om_coef:0.013},
clay:{theta_s:0.375, theta_r:0.135, alpha:0.095, n:1.52, bd_base:1.20, om_coef:0.014}
};
function computeVG(){
const txt=document.getElementById('texture').value;
const bd=parseFloat(document.getElementById('bd').value)||1.35;
const om=parseFloat(document.getElementById('om').value)||2.5;
const p=VG[txt];
// Adjust porosity by bulk density deviation from base
const phi_adj=(p.bd_base-bd)*0.02;
const theta_s=p.theta_s+phi_adj+(om*p.om_coef);
const theta_r=p.theta_r*(1-om*0.005);
const alpha=p.alpha*Math.pow(bd/p.bd_base,0.5);
const n=p.n*(1-om*0.002);
const m=1-1/n;
// Van Genuchten: theta(h)=theta_r+(theta_s-theta_r)/(1+(alpha*|h|)^n)^m
const fc=theta_r+(theta_s-theta_r)/Math.pow(1+Math.pow(alpha*0.33,n),m);
const pwp=theta_r+(theta_s-theta_r)/Math.pow(1+Math.pow(alpha*1.5,n),m);
const awc=fc-pwp;
const out=`
<div class="result"><strong>Computed Parameters</strong></div>
<div class="result-row"><span class="result-label">θ_s (Saturated Vol. Fraction)</span><span class="result-val">${theta_s.toFixed(4)}</span></div>
<div class="result-row"><span class="result-label">θ_r (Residual Vol. Fraction)</span><span class="result-val">${theta_r.toFixed(4)}</span></div>
<div class="result-row"><span class="result-label">α (Inverse Air Entry, cm⁻¹)</span><span class="result-val">${alpha.toFixed(5)}</span></div>
<div class="result-row"><span class="result-label">n (Pore Size Distribution)</span><span class="result-val">${n.toFixed(3)}</span></div>
<div class="result-row"><span class="result-label">m (Shape Parameter)</span><span class="result-val">${m.toFixed(3)}</span></div>
<hr style="border:0;border-top:1px dashed rgba(255,255,255,.1);margin:1rem 0">
<div class="result"><strong>Operational Points</strong></div>
<div class="result-row"><span class="result-label">Field Capacity (0.33MPa)</span><span class="result-val">${fc.toFixed(4)}cm³/cm³</span></div>
<div class="result-row"><span class="result-label">Permanent Wilting Pt (1.5MPa)</span><span class="result-val">${pwp.toFixed(4)}cm³/cm³</span></div>
<div class="result-row"><span class="result-label">Available Water Capacity</span><span class="result-val">${awc.toFixed(4)}cm³/cm³</span></div>
<div class="result-row"><span class="result-label">AWC (mm per meter depth)</span><span class="result-val">${(awc*1000).toFixed(1)}mm</span></div>
`;
document.getElementById('out').innerHTML=out;
}
// Pre-compute default
window.onload=()=>setTimeout(computeVG,150);
</script>
</body>
</html>

26
van-genuchten.json Normal file
View File

@ -0,0 +1,26 @@
{
"model": "van_genuchten_munsterkotte",
"source": "FAO-56 Appendix IV; Rawls et al. (1982)",
"author": "Arthur Ibay",
"location": "Salt Lake City, UT",
"units": {
"theta_s": "cm3/cm3",
"theta_r": "cm3/cm3",
"alpha": "cm^-1",
"n": "dimensionless",
"m": "derived (1-1/n)"
},
"pedotransfer_functions": {
"sand": {"theta_s":0.425,"theta_r":0.065,"alpha":0.037,"n":2.12,"bd_base":1.55,"om_coef":0.008},
"loamy_sand": {"theta_s":0.410,"theta_r":0.075,"alpha":0.045,"n":1.95,"bd_base":1.50,"om_coef":0.009},
"sandy_loam": {"theta_s":0.405,"theta_r":0.085,"alpha":0.052,"n":1.88,"bd_base":1.45,"om_coef":0.010},
"loam": {"theta_s":0.400,"theta_r":0.095,"alpha":0.060,"n":1.80,"bd_base":1.35,"om_coef":0.011},
"silt_loam": {"theta_s":0.395,"theta_r":0.105,"alpha":0.070,"n":1.72,"bd_base":1.30,"om_coef":0.012},
"clay_loam": {"theta_s":0.385,"theta_r":0.120,"alpha":0.082,"n":1.62,"bd_base":1.25,"om_coef":0.013},
"clay": {"theta_s":0.375,"theta_r":0.135,"alpha":0.095,"n":1.52,"bd_base":1.20,"om_coef":0.014}
},
"adjustments": {
"bulk_density_correction": "(bd_base - bd_measured) * 0.02",
"organic_matter_correction": "theta_s += om_pct * om_coef; theta_r *= (1 - om_pct * 0.005)"
}
}

190
wasatch-assay.html Normal file
View File

@ -0,0 +1,190 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wasatch Soil Assay | Arthur Ibay</title>
<style>
:root {
--basalt: #1a1a1a;
--obsidian: #0d0d0d;
--iron-oxide: #ff4500;
--quartz: #ffffff;
--magma: #ff8c00;
--ion: #00ff88;
--font-main: 'Inter', 'SF Pro Display', sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: radial-gradient(circle at 50% 50%, var(--basalt), var(--obsidian));
color: var(--quartz);
font-family: var(--font-main);
line-height: 1.5;
overflow-x: hidden;
}
header {
position: relative;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
perspective: 1000px;
}
.mountain-wall {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60vh;
background-image: url('https://images.pexels.com/photos/19937672/pexels-photo-19937672.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940');
background-size: cover;
background-position: center;
mask-image: linear-gradient(to top, black 0%, transparent 100%);
animation: rise 8s cubic-bezier(.1,.9,.1,1) forwards;
}
@keyframes rise {
from { transform: translateY(0) rotateX(0deg); opacity: 0; }
to { transform: translateY(-2000px) rotateX(15deg); opacity: 1; }
}
h1 {
z-index: 10;
font-size: clamp(3rem, 8vw, 12rem);
font-weight: 900;
letter-spacing: -0.05em;
text-transform: uppercase;
background: linear-gradient(to bottom, var(--ion), var(--iron-oxide));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-align: center;
margin-bottom: 2rem;
animation: ignite 6s ease-out forwards;
}
@keyframes ignite {
from { opacity: 0; filter: blur(20px); }
to { opacity: 1; filter: blur(0px); }
}
.assay-core {
position: relative;
max-width: 1200px;
margin: 0 auto;
padding: 8rem 4rem 4rem;
}
.data-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin: 4rem 0;
}
.spec-card {
background: rgba(13,13,13,0.9);
border: 1px solid var(--ion);
border-radius: 1rem;
padding: 2rem;
backdrop-filter: blur(20px);
transition: all 0.4s cubic-bezier(.1,.9,.1,1);
}
.spec-card:hover {
border-color: var(--iron-oxide);
transform: translateZ(100px);
box-shadow: 0 0 60px var(--magma);
}
.spec-card h3 {
color: var(--ion);
font-size: 1.5rem;
margin-bottom: 1rem;
}
.spec-card .value {
font-size: 3rem;
font-weight: 900;
background: linear-gradient(to right, var(--ion), var(--iron-oxide));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.spec-card .unit {
font-size: 1rem;
color: var(--quartz);
opacity: 0.7;
}
.mirror-lake {
width: 100%;
height: 60vh;
object-fit: cover;
border-radius: 2rem;
margin: 4rem 0;
filter: contrast(1.2) saturate(1.5);
}
.signature-line {
position: fixed;
bottom: 4rem;
left: 50%;
transform: translateX(-50%);
font-size: 1.2rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ion);
white-space: nowrap;
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header>
<div class="mountain-wall"></div>
<h1>WASATCH<br>SOIL<br>ASSAY</h1>
</header>
<main class="assay-core">
<section style="text-align:center;margin-bottom:6rem;">
<p style="font-size:clamp(1.5rem,4vw,3rem);line-height:1.2;font-weight:900;letter-spacing:-0.03em;">
The first iron oxide profile that proves American Fork is Chryse Planitia's twin.
</p>
<p style="margin-top:2rem;color:var(--ion);">
Not a metaphor. The coordinate where our first harvest begins.
</p>
</section>
<img src="https://pixabay.com/get/ged66bacfc3ded5c6c9e64709277cff847a9cf18323be04d3b9d41bec6abdb7e1da236235772bf44c93c4eedeefba6da8eb49fce352d4961a471efd73a85a_1280.jpg"
alt="Mirror Lake, Utah — reflection of the Wasatch crest in perfect symmetry"
class="mirror-lake">
<section class="data-grid">
<article class="spec-card">
<h3>Iron Oxide Content</h3>
<div class="value">4.7%</div>
<div class="unit">by mass</div>
<p style="margin-top:1rem;opacity:0.7;">Matches Mars regolith threshold for oxygen generation</p>
</article>
<article class="spec-card">
<h3>Nitrogen Fixation Depth</h3>
<div class="value">284m</div>
<div class="unit">below surface</div>
<p style="margin-top:1rem;opacity:0.7;">American Fork valley floor measurement</p>
</article>
<article class="spec-card">
<h3>Hydration Potential</h3>
<div class="value"></div>
<div class="unit">cycles</div>
<p style="margin-top:1rem;opacity:0.7;">Perpetual loop closed by Mirror Lake aquifer</p>
</article>
<article class="spec-card">
<h3>Ignition Sequence</h3>
<div class="value">T+14w</div>
<div class="unit">from now</div>
<p style="margin-top:1rem;opacity:0.7;">Fourteen-week countdown to first bloom</p>
</article>
</section>
<section style="position:relative;margin-top:8rem;text-align:center;">
<p style="font-size:2rem;line-height:1.1;font-weight:900;letter-spacing:-0.02em;background:linear-gradient(to bottom,var(--ion),var(--iron-oxide));-webkit-background-clip:text;background-clip:text;color:transparent;">
We were forty years old when the soil spoke back.
</p>
</section>
</main>
<footer class="signature-line">
ARTHUR IBAY • SALT LAKE CITY • 2026
</footer>
</body>
</html>