setIsFormsHovered(true)}
onMouseLeave={() => setIsFormsHovered(false)}
diff --git a/src/components/TokenChip.tsx b/src/components/TokenChip.tsx
index a9578ef0..7337e736 100644
--- a/src/components/TokenChip.tsx
+++ b/src/components/TokenChip.tsx
@@ -421,8 +421,18 @@ export function TokenChip({
{token.surfaceText}
- {/* Read-only hides the whole morphology row for an unanalyzed token: its only content
- would be the define-breakdown affordance, which is an editing control. */}
+ {/* The define-breakdown affordance is an editing control, so read-only drops it but keeps an
+ inert slot, holding this token's gloss on its analyzed neighbors' line. */}
+ {showMorphology && readOnly && !hasMorphemes && (
+
+
+ {token.surfaceText}
+
+
+
+
+
+ )}
{showMorphology && (!readOnly || hasMorphemes) && (
// The morpheme row is the popover anchor; the panel itself is portaled to document.body
// by PopoverContent, so it escapes both the clipping of ancestor scroll viewports (e.g.
@@ -452,7 +462,7 @@ export function TokenChip({
aria-label={formatReplacementString(labels.defineMorphemes, {
token: token.surfaceText,
})}
- className={`tw:flex tw:h-auto tw:flex-row tw:items-center tw:rounded tw:px-0.5 tw:py-0 tw:font-mono tw:text-xs tw:italic tw:text-muted-foreground/50 tw:transition-colors${disabled ? '' : ' tw:cursor-pointer tw:hover:bg-accent'}`}
+ className={`tw:flex tw:h-auto tw:flex-row tw:items-center tw:rounded tw:p-0 tw:font-mono tw:text-xs tw:italic tw:text-muted-foreground/50 tw:transition-colors${disabled ? '' : ' tw:cursor-pointer tw:hover:bg-accent'}`}
tabIndex={-1}
type="button"
variant="ghost"
@@ -461,7 +471,16 @@ export function TokenChip({
if (!disabled) openMorphemeEditor();
}}
>
- {token.surfaceText}
+
+ {token.surfaceText}
+
+
+
+
)}
diff --git a/src/tailwind.css b/src/tailwind.css
index 3e4ba707..891771f7 100644
--- a/src/tailwind.css
+++ b/src/tailwind.css
@@ -185,6 +185,14 @@
@apply tw:inline-flex tw:items-start tw:gap-1;
}
+/*
+ * The single source of truth for a morphology slot's outer metrics, so an analyzed and an
+ * unanalyzed token share a height and every token gloss lands on one line across the strip.
+ */
+@utility morphology-slot {
+ @apply tw:inline-grid tw:w-fit tw:items-center tw:gap-x-0.5 tw:gap-y-0.5 tw:rounded tw:border tw:p-0.5;
+}
+
@utility section-label {
@apply tw:text-xs tw:font-medium tw:text-muted-foreground tw:uppercase tw:tracking-wide;
}