import React, { useMemo, useState } from "react";
/* ------------------------------------------------------------------ */
/* DIP PROJECT — formation depth step-out calculator */
/* Projects a known formation TVD along a bearing using regional dip. */
/* Seeded with the Iron Horse / Mid Bossier example. */
/* ------------------------------------------------------------------ */
const C = {
bg: "#0E1A22",
panel: "#16242E",
panel2: "#1B2D38",
line: "#26404D",
text: "#E8EEF1",
muted: "#8AA0AD",
amber: "#F2B441",
amberDim: "#B8842B",
coral: "#E2674A",
band: "#22414F",
};
const SANS =
"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
const MONO =
"'JetBrains Mono', ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, Consolas, monospace";
const toRad = (d) => (d * Math.PI) / 180;
const num = (v, d = 0) => {
const n = parseFloat(v);
return Number.isFinite(n) ? n : d;
};
const fmt = (n, dp = 0) =>
Number.isFinite(n)
? n.toLocaleString("en-US", { minimumFractionDigits: dp, maximumFractionDigits: dp })
: "—";
function niceStep(range, target) {
if (!(range > 0)) return 1;
const raw = range / target;
const mag = Math.pow(10, Math.floor(Math.log10(raw)));
const norm = raw / mag;
let s = 10;
if (norm < 1.5) s = 1;
else if (norm < 3) s = 2;
else if (norm < 7) s = 5;
return s * mag;
}
const COMPASS = [
["N", 0], ["NE", 45], ["E", 90], ["SE", 135],
["S", 180], ["SW", 225], ["W", 270], ["NW", 315],
];
export default function DipProjector() {
const [mode, setMode] = useState("distance"); // 'distance' | 'depth'
const [refTVD, setRefTVD] = useState("13500");
const [targetTVD, setTargetTVD] = useState("19000");
const [distMi, setDistMi] = useState("15");
const [dip, setDip] = useState(300); // ft / mile (true dip)
const [stepBear, setStepBear] = useState(180);
const [dipAz, setDipAz] = useState(180);
const [ppGrad, setPpGrad] = useState("0.90");
const [geoGrad, setGeoGrad] = useState("1.5"); // °F per 100 ft
const [surfTemp, setSurfTemp] = useState("74");
const calc = useMemo(() => {
const rTVD = num(refTVD);
const tTVD = num(targetTVD);
const d = Math.max(0, num(distMi));
const off = stepBear - dipAz;
const cosOff = Math.cos(toRad(off));
const dApp = dip * cosOff; // apparent dip ft/mile along bearing
let solvedMi = NaN;
let note = null;
let markerDist = NaN;
let markerDepth = NaN;
let reachable = false;
if (mode === "distance") {
const delta = tTVD - rTVD;
if (delta <= 0) {
note = "Target is at or above the reference depth — no down-dip step-out needed.";
} else if (dApp <= 1e-6) {
note =
off === 0
? "Dip is zero — the formation is flat, target depth is never reached."
: "This bearing runs up-dip or along strike, so it gets shallower — never reaches the target.";
} else {
solvedMi = delta / dApp;
reachable = true;
markerDist = solvedMi;
markerDepth = tTVD;
}
} else {
markerDist = d;
markerDepth = rTVD + d * dApp;
reachable = true;
}
const readoutDepth =
mode === "distance" ? tTVD : Number.isFinite(markerDepth) ? markerDepth : rTVD;
const pp = num(ppGrad) * readoutDepth;
const emw = num(ppGrad) / 0.052;
const bht = num(surfTemp) + (num(geoGrad) / 100) * readoutDepth;
return {
rTVD, tTVD, d, dApp, cosOff, off, solvedMi, note, markerDist,
markerDepth, reachable, readoutDepth, pp, emw, bht,
};
}, [mode, refTVD, targetTVD, distMi, dip, stepBear, dipAz, ppGrad, geoGrad, surfTemp]);
/* ---------- cross-section geometry ---------- */
const PX_L = 64, PX_R = 700, PX_T = 26, PX_B = 402;
const chart = useMemo(() => {
const { rTVD, dApp, markerDist, markerDepth, reachable, mode: m } = calc;
let base =
m === "distance"
? reachable
? calc.solvedMi
: 10
: Math.max(calc.d, 1);
if (!Number.isFinite(base) || base <= 0) base = 10;
const Dmax = Math.min(Math.max(base * 1.3, 2), 120);
const deepAtDmax = rTVD + Dmax * dApp;
const depths = [rTVD, deepAtDmax];
if (Number.isFinite(markerDepth)) depths.push(markerDepth);
let dLo = Math.min(...depths) - 250;
let dHi = Math.max(...depths) + 350;
if (dHi - dLo < 800) dHi = dLo + 800;
const sx = (mi) => PX_L + (mi / Dmax) * (PX_R - PX_L);
const sy = (ft) => PX_T + ((ft - dLo) / (dHi - dLo)) * (PX_B - PX_T);
const dStep = niceStep(dHi - dLo, 5);
const depthTicks = [];
for (let v = Math.ceil(dLo / dStep) * dStep; v <= dHi; v += dStep) depthTicks.push(v);
const xStep = niceStep(Dmax, 5);
const xTicks = [];
for (let v = 0; v <= Dmax + 1e-6; v += xStep) xTicks.push(v);
return { Dmax, deepAtDmax, dLo, dHi, sx, sy, depthTicks, xTicks };
}, [calc]);
const inputStyle = {
width: "100%", background: C.bg, border: `1px solid ${C.line}`,
color: C.text, fontFamily: MONO, fontSize: 15, padding: "9px 11px",
borderRadius: 6, outline: "none", boxSizing: "border-box",
};
const labelStyle = {
fontFamily: SANS, fontSize: 10.5, letterSpacing: "0.13em",
textTransform: "uppercase", color: C.muted, marginBottom: 6, display: "block", fontWeight: 600,
};
const Field = ({ label, value, onChange, suffix }) => (
);
const { markerDist, markerDepth, reachable } = calc;
const showMarker = reachable && Number.isFinite(markerDist) && Number.isFinite(markerDepth);
return (
{/* header */}
DIP· PROJECT
formation depth step-out from a known TVD
seeded: Iron Horse / Mid Bossier · ref 13,500 ft → target 19,000 ft
{/* mode toggle */}
{[["distance", "Distance → target depth"], ["depth", "Depth @ distance"]].map(([k, lbl]) => (
setMode(k)}
className="chip"
style={{
fontFamily: SANS, fontSize: 12.5, fontWeight: 600, cursor: "pointer",
border: "none", borderRadius: 6, padding: "8px 14px",
background: mode === k ? C.amber : "transparent",
color: mode === k ? "#1a1206" : C.muted, transition: "all .15s",
}}
>{lbl}
))}
{/* ------------- controls ------------- */}
{mode === "distance"
?
:
}
Regional dip · {fmt(dip)} ft/mi
setDip(num(e.target.value, 300))} />
Step-out bearing
{COMPASS.map(([name, deg]) => (
setStepBear(deg)}
style={{
fontFamily: MONO, fontSize: 10.5, padding: "6px 0", cursor: "pointer",
border: `1px solid ${stepBear === deg ? C.amber : C.line}`, borderRadius: 5,
background: stepBear === deg ? `${C.amber}22` : "transparent",
color: stepBear === deg ? C.amber : C.muted, fontWeight: 600,
}}>{name}
))}
setStepBear(num(e.target.value, 0))} style={inputStyle} />
° az
setDipAz(num(v, 180))} suffix="° az" />
{/* assumptions */}
Pressure & temp assumptions
{/* ------------- results + chart ------------- */}
{/* primary result */}
{calc.note ? (
{calc.note}
) : mode === "distance" ? (
Distance to {fmt(calc.tTVD)} ft
{fmt(calc.solvedMi, 1)} mi
{fmt(calc.solvedMi * 5280)} ft along {fmt(stepBear)}° · {fmt(calc.solvedMi * 5280 / 660, 1)} legal locations
) : (
Projected depth at {fmt(calc.d, 1)} mi
{fmt(calc.markerDepth)} ft TVD
{fmt(calc.markerDepth - calc.rTVD)} ft deeper than reference
)}
apparent dip {fmt(calc.dApp, 1)} ft/mi · Δθ {fmt(Math.abs(((calc.off % 360) + 540) % 360 - 180))}° off down-dip · cos {calc.cosOff.toFixed(3)}
{/* readout strip */}
{[
["Pore pressure", `${fmt(calc.pp)} psi`, `@ ${fmt(calc.readoutDepth)} ft`],
["Mud wt equiv.", `${fmt(calc.emw, 1)} ppg`, `${num(ppGrad).toFixed(2)} psi/ft`],
["Est. BHT", `${fmt(calc.bht)} °F`, "static"],
].map(([l, v, s]) => (
))}
{/* cross-section */}
Structural cross-section · down-dip
{/* depth grid + labels */}
{chart.depthTicks.map((v, i) => (
{fmt(v)}
))}
{/* distance ticks */}
{chart.xTicks.map((v, i) => (
{fmt(v, v < 10 ? 1 : 0)}
))}
DISTANCE DOWN-DIP (MILES)
{/* formation body */}
{/* formation top line */}
Mid Bossier top (proj.)
{/* reference well */}
Iron Horse · {fmt(calc.rTVD)} ft
{/* target depth + intercept */}
{showMarker && (
<>
{fmt(markerDist, 1)} mi · {fmt(markerDepth)} ft
>
)}
Planning estimate from a single linear dip. Swap the dip value for the gradient read straight
off your base–Mid Bossier structure map for the relevant sections — and remember the Sabine-flank
growth faults can drop the section in one step rather than a smooth slope.
);
}
Zapier / Get hyper link to your leads
316 Acres / Eric needs to let you know when it will be closing
100 acre Signing Bonus, need to find out what the amount is going to be and when does he expect to mail the check
Toledo Town Surface is getting more expensive, the surface is going for over $10K and acre and the minerals are going for close to $20 thousan dollars an acre.
5N/12W/Sec5 - 309 acres - Surface and Minerals
Saratoga is alive, need to get Eric to sign off on it - get a new Operating name, Pendleton holding company, Pendleton Resources
_____________________________________________________________________
Here's your personal acreage portfolio in the Haynesville play based on what we have on record:
Property Location Acres Notes
Jesup Property : 100 Acres Sec 14/7N/10W, it is 80 acres & 20 Acres, surface is owned by Sabine Uplift
ShutEye Farm 46 Acres - Sec 24, T7N/R12W Inside Iron Horse unit area, GEPH actively drilling Iron Horse Unit Sec 13 & 24, T7N/R12W 23 NMA 22.5% cost-free royalty, IP 34,080 MCF/Day, completed 3/12/2026
Mom's Property 1Sec 1, 100 Acres - T6N/R12W - Leased by GEP
Lake Property 20 Acres - Sec 14, T6N/R13W - Surface can be used as an RV Park, it can also be sold to Ronnie Brandon, and be part of he neighborhood
Marthaville Sec 18, T8N/R10W ~55 ac Beulah Church Sec 25, T6N/R13W ~40 ac
Total: ~385 surface acres + 23 NMA royalty interest in Iron Horse
The crown jewel right now is the Iron Horse royalty — 23 NMA at 22.5% producing 34,080 MCF/Day. The ShutEye tract is next up with GEPH actively drilling in that same T7N/R12W township, projected to come online around Month 7.
Want me to pull this up on the STR research map so you can see all your sections visually?
ZJeff Rubio
David LIner
Exxon
Michael Ferguson
Rob Hargett
Zach Gray