diff --git a/memory-bank/implementation-plan.md b/memory-bank/implementation-plan.md
index b704720..53a2f1f 100644
--- a/memory-bank/implementation-plan.md
+++ b/memory-bank/implementation-plan.md
@@ -1,6 +1,3 @@
-
-
-
# MyKindlebox — Implementation Plan
**Version:** 1.1
diff --git a/mykindlebox/client/src/components/PedigreeTree.jsx b/mykindlebox/client/src/components/PedigreeTree.jsx
index 06cff5e..a0fe974 100644
--- a/mykindlebox/client/src/components/PedigreeTree.jsx
+++ b/mykindlebox/client/src/components/PedigreeTree.jsx
@@ -14,7 +14,12 @@ import {
formatManualAncestorDisplay,
} from '../lib/pedigree'
-const EMPTY_MANUAL_FORM = { name: '', farm: '', ear_number: '', weight_lbs: '0', dob: '', dobUnknown: true }
+const EMPTY_MANUAL_FORM = { name: '', farm: '', ear_number: '', weight_lbs: '0', dob: '', dobUnknown: false }
+
+// Upper bound on the popup's height (its tallest state is the manual-entry
+// form). Used to decide drop direction synchronously on open, before the popup
+// mounts, so the very first click is positioned correctly.
+const POPUP_MAX_HEIGHT = 360
// `dob` on the manual-ancestor form is a real date input value; `dobUnknown`
// is the separate "Unknown" checkbox. They collapse into one stored string
@@ -40,6 +45,20 @@ function AncestorSlot({ path, slot, label, state, setSlotState, cascadeCache, fe
const containerRef = useRef(null)
const inputRef = useRef(null)
const triggerRef = useRef(null)
+ // Open the popup above the trigger when there isn't room below it (e.g. nodes
+ // near the bottom of the window) so the tall manual form doesn't get cut off.
+ const [dropUp, setDropUp] = useState(false)
+
+ // Decide drop direction from the trigger's position in the viewport. Called
+ // synchronously on open (the trigger already exists in the DOM) so the first
+ // click lands in the right place rather than flipping on the next open.
+ function updateDropUp() {
+ if (!triggerRef.current) return
+ const rect = triggerRef.current.getBoundingClientRect()
+ const spaceBelow = window.innerHeight - rect.bottom
+ const spaceAbove = rect.top
+ setDropUp(spaceBelow < POPUP_MAX_HEIGHT + 8 && spaceAbove > spaceBelow)
+ }
function close() {
setOpen(false)
@@ -125,6 +144,7 @@ function AncestorSlot({ path, slot, label, state, setSlotState, cascadeCache, fe
ref={triggerRef}
type="button"
onClick={() => {
+ if (!open) updateDropUp()
setOpen((v) => !v)
if (node.mode === 'manual') {
setMode('manual')
@@ -145,7 +165,11 @@ function AncestorSlot({ path, slot, label, state, setSlotState, cascadeCache, fe
{open && (
-