exec(open('ui_test.py').read().split("with sync_playwright() as pw:")[0])
def mouse_drag(p, src, dst_sel):
    a = src.bounding_box(); d = p.query_selector(dst_sel).bounding_box()
    p.mouse.move(a['x'] + 40, a['y'] + 14); p.mouse.down()
    for i in range(1, 16): p.mouse.move(a['x'] + 40 + (d['x'] + 60 - a['x'] - 40) * i / 15, a['y'] + 14 + (d['y'] + 40 - a['y'] - 14) * i / 15); p.wait_for_timeout(16)
    p.mouse.up(); p.wait_for_timeout(1500)
with sync_playwright() as pw:
    b = pw.chromium.launch()
    for W in (1905, 1440):
        ctx = b.new_context(viewport={'width': W, 'height': 900}); p, errs = login(ctx)
        p.goto(BASE + '#/agents/' + aid + '/pipeline?p=recruits'); p.wait_for_selector('.pcard.crm'); p.wait_for_timeout(800)
        ok(p.evaluate('!!window.Sortable'), f'{W}: Sortable loaded')
        card = p.query_selector('.col[data-stage]:nth-child(3) .pcard.crm') or p.query_selector('.col[data-stage] .pcard.crm')
        cid = card.get_attribute('data-card'); before = sql(f"select stage_id from cards where id={cid}")
        target = p.eval_on_selector_all('.col[data-stage]', "(cs, b) => cs.map(c => c.dataset.stage).filter(s => s !== b)[1]", before)
        mouse_drag(p, card, f'.col[data-stage="{target}"] .col-b')
        ok(sql(f"select stage_id from cards where id={cid}") == target, f'{W}: mouse drag moves the lead to another stage (saved)', (before, target, sql(f"select stage_id from cards where id={cid}")))
        ok(not p.query_selector('.rec2-h'), f'{W}: a drag does not open the record')
        c2 = p.query_selector('.pcard.crm'); c2.click(); p.wait_for_selector('.rec2-h', timeout=5000); ok(True, f'{W}: a plain click still opens the record'); p.keyboard.press('Escape'); p.wait_for_timeout(300)
        bb = p.eval_on_selector('#board', 'b => { const r = b.getBoundingClientRect(); return [r.right, innerWidth, r.bottom, innerHeight] }')
        ok(bb[1] - bb[0] < 40 and bb[3] - bb[2] < 40, f'{W}: board fills the width and height', bb)
        gap = p.eval_on_selector('#board', 'b => getComputedStyle(b).columnGap'); ok(gap == '6px', f'{W}: tight column gap', gap)
        ok(not p.query_selector('#sbWs') and not p.query_selector('#wsMenu'), f'{W}: no workspace switcher in the sidebar')
        p.screenshot(path=f'/tmp/drag-{W}.png'); ok(not errs, f'{W}: no script errors', errs[:2])
        ctx.close()
    b.close()
print(f"\n{sum(R)} passed, {len(R) - sum(R)} failed")
