function BldSetup({ lettering, onLetteringChange, calibratedSolvedFacelets, cubeState, scramble, liveMoves, onCalibrateSolved, onClearCalibration }) {
  const inverse = invertAlg(scramble);
  const hasCalibration = !!calibratedSolvedFacelets;
  const matchesCalibratedSolved = hasCalibration && cubeState?.facelets === calibratedSolvedFacelets;
  const liveAlg = liveMoves.map(m => m.move).filter(Boolean).join(' ');

  function updateField(field, value) {
    onLetteringChange({ ...lettering, [field]: value.toUpperCase().replace(/[^A-Z]/g, '').slice(0, 24) });
  }

  return (
    <div style={bldStyles.wrap}>
      <div style={bldStyles.header}>
        <span style={bldStyles.title}>3BLD setup POC</span>
        <span style={{...bldStyles.badge, ...(matchesCalibratedSolved ? bldStyles.badgeOk : {})}}>
          {hasCalibration ? (matchesCalibratedSolved ? 'Solved' : 'Not solved') : 'Not calibrated'}
        </span>
      </div>
      <div style={bldStyles.grid}>
        <label style={bldStyles.field}>
          <span style={bldStyles.label}>Corner lettering</span>
          <input style={bldStyles.input} value={lettering.corners} onChange={e => updateField('corners', e.target.value)} />
        </label>
        <label style={bldStyles.field}>
          <span style={bldStyles.label}>Edge lettering</span>
          <input style={bldStyles.input} value={lettering.edges} onChange={e => updateField('edges', e.target.value)} />
        </label>
      </div>
      <div style={bldStyles.actions}>
        <button style={bldStyles.btn} onClick={onCalibrateSolved} disabled={!cubeState?.facelets}>Set current cube as solved</button>
        <button style={bldStyles.btnGhost} onClick={onClearCalibration} disabled={!hasCalibration}>Clear calibration</button>
      </div>
      <div style={bldStyles.infoGrid}>
        <div>
          <span style={bldStyles.label}>Reference inverse scramble</span>
          <div style={bldStyles.alg}>{inverse || 'No scramble yet'}</div>
        </div>
        <div>
          <span style={bldStyles.label}>Live execution moves</span>
          <div style={bldStyles.alg}>{liveAlg || 'No moves yet'}</div>
        </div>
      </div>
      <p style={bldStyles.note}>
        This is a first calibration layer: set the solved facelets after physically solving the cube. During attempts, the badge reports whether live facelets match that calibrated solved state. Future BLD analysis will use the lettering scheme to derive memo targets and detect the first wrong case/move.
      </p>
    </div>
  );
}

const bldStyles = {
  wrap: {
    background: 'oklch(14% 0.01 250)',
    border: '1px solid oklch(20% 0.01 250)',
    borderRadius: '8px',
    padding: '14px 16px',
    display: 'flex',
    flexDirection: 'column',
    gap: '10px',
  },
  header: { display: 'flex', alignItems: 'center', gap: '10px' },
  title: {
    fontSize: '12px',
    fontWeight: '700',
    letterSpacing: '0.08em',
    textTransform: 'uppercase',
    color: 'oklch(60% 0.01 250)',
  },
  badge: {
    marginLeft: 'auto',
    padding: '3px 8px',
    borderRadius: '999px',
    background: 'oklch(20% 0.08 25)',
    color: 'oklch(65% 0.14 25)',
    fontSize: '11px',
    fontWeight: '700',
  },
  badgeOk: {
    background: 'oklch(18% 0.10 165)',
    color: 'oklch(65% 0.18 165)',
  },
  grid: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' },
  field: { display: 'flex', flexDirection: 'column', gap: '5px' },
  label: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '10px',
    letterSpacing: '0.10em',
    color: 'oklch(42% 0.01 250)',
    textTransform: 'uppercase',
  },
  input: {
    padding: '8px 10px',
    borderRadius: '6px',
    border: '1px solid oklch(24% 0.01 250)',
    background: 'oklch(10% 0.01 250)',
    color: 'oklch(85% 0.01 250)',
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '12px',
  },
  actions: { display: 'flex', gap: '8px' },
  btn: {
    padding: '6px 10px',
    borderRadius: '6px',
    border: 'none',
    background: 'oklch(65% 0.18 165)',
    color: 'oklch(10% 0.01 250)',
    cursor: 'pointer',
    fontWeight: '700',
    fontSize: '12px',
  },
  btnGhost: {
    padding: '6px 10px',
    borderRadius: '6px',
    border: '1px solid oklch(25% 0.01 250)',
    background: 'transparent',
    color: 'oklch(55% 0.01 250)',
    cursor: 'pointer',
    fontSize: '12px',
  },
  infoGrid: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' },
  alg: {
    marginTop: '5px',
    minHeight: '32px',
    padding: '8px',
    borderRadius: '6px',
    background: 'oklch(10% 0.01 250)',
    color: 'oklch(70% 0.01 250)',
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '12px',
    lineHeight: 1.4,
  },
  note: {
    margin: 0,
    color: 'oklch(38% 0.01 250)',
    fontSize: '11px',
    lineHeight: 1.4,
  },
};

Object.assign(window, { BldSetup });
