// ════════════════════════════════════════════════════════════ // Caulis — Plant detail + Add/Edit (overlays) // ════════════════════════════════════════════════════════════ // ── info tile ───────────────────────────────────────────── function InfoTile({ icon, label, children, accent = C.forest }) { return (
{m[4]});
last = m.index + m[0].length;
}
if (last < str.length) out.push(str.slice(last));
return out;
}
// block-level: paragraphs, bullets, numbered lists, light headings
function MarkdownText({ text }) {
const lines = (text || '').split('\n');
const blocks = []; let list = null;
const flush = () => { if (list) { blocks.push(list); list = null; } };
for (const ln of lines) {
const bullet = /^\s*[-*•]\s+(.*)/.exec(ln);
const num = /^\s*\d+[.)]\s+(.*)/.exec(ln);
const head = /^\s*#{1,3}\s+(.*)/.exec(ln);
if (bullet) { if (!list || list.type !== 'ul') { flush(); list = { type:'ul', items:[] }; } list.items.push(bullet[1]); }
else if (num) { if (!list || list.type !== 'ol') { flush(); list = { type:'ol', items:[] }; } list.items.push(num[1]); }
else if (head) { flush(); blocks.push({ type:'h', text:head[1] }); }
else if (ln.trim() === '') { flush(); }
else { flush(); blocks.push({ type:'p', text:ln }); }
}
flush();
return (