import { css, html, nothing } from 'lit'; import { customElement } from 'lit/decorators.js'; import type { CalculatePentaResponse } from '../types/index.js'; import { RoxyDataElement } from '../utils/base-element.js'; import { baseStyles } from '../utils/base-styles.js'; /** * Human Design penta. Renders /human-design/penta: the group field (3 to 5 people) as the penta channels they form, split into the upper (direction) and lower (execution) triangles, with each channel marked defined or open and core or not, and which members hold each gate. The team/business chart. */ @customElement('roxy-hd-penta') export class RoxyHdPenta extends RoxyDataElement { static styles = [ baseStyles, css` .wrap { background: var(--roxy-surface, #fff); color: var(--roxy-fg, #0a0a0a); border: 1px solid var(--roxy-border, #e4e4e7); border-radius: var(--roxy-radius-md, 8px); padding: var(--roxy-space-lg, 1.5rem); box-shadow: var(--roxy-shadow-sm); display: grid; gap: var(--roxy-space-md, 1rem); } .head { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: var(--roxy-space-sm, 0.5rem); } .title { margin: 0; font-size: var(--roxy-text-lg, 1.125rem); font-weight: var(--roxy-weight-bold, 600); } .count { color: var(--roxy-accent-ink, #b45309); font-size: var(--roxy-text-sm, 0.875rem); font-weight: var(--roxy-weight-bold, 600); } .stats { display: flex; flex-wrap: wrap; gap: var(--roxy-space-sm, 0.5rem); } .stat { display: inline-flex; align-items: baseline; gap: 0.35rem; padding: 2px 10px; border-radius: var(--roxy-radius-full, 9999px); background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent); font-size: var(--roxy-text-xs, 0.75rem); color: var(--roxy-fg, #0a0a0a); } .stat b { color: var(--roxy-fg, #0a0a0a); font-variant-numeric: tabular-nums; } .section h3 { margin: 0 0 var(--roxy-space-xs, 0.25rem); font-size: var(--roxy-text-xs, 0.75rem); color: var(--roxy-muted, #71717a); text-transform: uppercase; letter-spacing: 0.06em; } .row { display: flex; align-items: center; gap: var(--roxy-space-sm, 0.5rem); padding: var(--roxy-space-sm, 0.5rem) 0; border-bottom: 1px solid var(--roxy-border, #e4e4e7); font-size: var(--roxy-text-sm, 0.875rem); flex-wrap: wrap; } .row:last-child { border-bottom: none; } .gates { font-variant-numeric: tabular-nums; color: var(--roxy-accent-ink, #b45309); font-weight: var(--roxy-weight-bold, 600); min-width: 3.5rem; } .cname { flex: 1; min-width: 8rem; } .badge { display: inline-flex; padding: 1px 8px; border-radius: var(--roxy-radius-full, 9999px); font-size: var(--roxy-text-xs, 0.75rem); font-weight: var(--roxy-weight-bold, 600); } .badge.defined { background: color-mix(in srgb, var(--roxy-success, #16a34a) 16%, transparent); color: var(--roxy-success-fg, #166534); } .badge.open { background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 45%, transparent); color: var(--roxy-fg, #0a0a0a); } .badge.core { background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 18%, transparent); color: var(--roxy-fg, #0a0a0a); } `, ]; protected renderData(d: CalculatePentaResponse) { const channels = d.channels ?? []; const upper = channels.filter((c) => c.position === 'upper'); const lower = channels.filter((c) => c.position === 'lower'); return html`

Penta

${typeof d.memberCount === 'number' ? html`${d.memberCount} members` : nothing}
${this.renderSummary(d.summary)} ${this.renderGroup('Upper (direction)', upper)} ${this.renderGroup('Lower (execution)', lower)}
`; } private renderSummary(s: CalculatePentaResponse['summary'] | undefined) { if (!s) return nothing; const gaps = s.gapGates ?? []; return html`
${typeof s.definedChannels === 'number' ? html`Defined channels ${s.definedChannels}` : nothing} ${typeof s.filledGates === 'number' ? html`Filled gates ${s.filledGates}` : nothing} ${gaps.length > 0 ? html`Gap gates ${gaps.join(', ')}` : nothing} ${typeof s.coreDefined === 'boolean' ? html`${s.coreDefined ? 'Core defined' : 'Core open'}` : nothing}
`; } private renderGroup( label: string, channels: NonNullable, ) { if (channels.length === 0) return nothing; return html`

${label}

${channels.map( (c) => html`
${c.gateA}-${c.gateB} ${c.name ?? ''} ${c.defined ? 'Defined' : 'Open'} ${c.isCore ? html`Core` : nothing}
`, )}
`; } protected renderEmpty() { return html`
No penta data
`; } } declare global { interface HTMLElementTagNameMap { 'roxy-hd-penta': RoxyHdPenta; } }