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 }) => (
onChange(e.target.value)} style={inputStyle} /> {suffix && ( {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]) => ( ))}
{/* ------------- controls ------------- */}
{mode === "distance" ? : }
setDip(num(e.target.value, 300))} />
{COMPASS.map(([name, deg]) => ( ))}
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]) => (
{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.
); }
0
Skip to Content
mymineraloptions.com 800-256-7501
New Page
Map w STR
Custom La Map
Leasing/Selling
Mineral Owner Form
Natchitoches Parish Mineral Rights-What Owners Need to Know
DeSoto Parish Mineral Rights — What Owners Need to Know in 2026
Sabine Parish Mineral Rights — What Owners Need to Know in 2026
Home
mymineraloptions.com 800-256-7501
New Page
Map w STR
Custom La Map
Leasing/Selling
Mineral Owner Form
Natchitoches Parish Mineral Rights-What Owners Need to Know
DeSoto Parish Mineral Rights — What Owners Need to Know in 2026
Sabine Parish Mineral Rights — What Owners Need to Know in 2026
Home
New Page
Map w STR
Custom La Map
Leasing/Selling
Mineral Owner Form
Natchitoches Parish Mineral Rights-What Owners Need to Know
DeSoto Parish Mineral Rights — What Owners Need to Know in 2026
Sabine Parish Mineral Rights — What Owners Need to Know in 2026
Home
  • MyMineralOptions.com is not a law firm and does not provide legal, tax, or accounting advice. Everything we share is for educational purposes only. Every mineral situation is different, so we always encourage owners to speak with qualified professionals before making decisions.

    There is never any obligation to sell minerals. No guarantees are made or implied. Our goal is simple: help you understand your options so you can decide what’s right for youoes here

  • We respect your privacy. Any information you share with us — online or by phone — is used only to respond to your inquiry. We do not sell or trade personal information. text goes here

  • If a form does not redirect or confirm properly after submission, please click “Home” and try again, or call us directly. We’ll make sure your information is received

Contact

📞 800-256-7501
☎️ All calls returned personally
🌎 Serving mineral owners nationwide

Legal and Professional Services

www.Legoilconsulting.com

 Seterson LLC Company

© 2026 MyMineralOptions.com. All rights reserved.