declare(strict_types=1);session_start();require __DIR__.'/lib.php';if(setting('setup_complete')!=='1'){header('Location: setup.php');exit;}if(!isset($_SESSION['captcha_a']))captcha_new(); $error='';$action=$_POST['action']??''; if($_SERVER['REQUEST_METHOD']==='POST')try{ check_csrf(); if($action==='register'){$name=trim($_POST['name']??'');$email=strtolower(trim($_POST['email']??''));$pw=$_POST['password']??'';if(!captcha_ok($_POST['captcha']??''))throw new RuntimeException('CAPTCHA answer was incorrect.');if(!$name||!filter_var($email,FILTER_VALIDATE_EMAIL)||strlen($pw)<10)throw new RuntimeException('Enter your name, a valid email, and a password of at least 10 characters.');$st=db()->prepare('INSERT INTO users(name,email,company,password_hash,role,verified,created_at) VALUES(?,?,?,?,?,?,?)');$st->execute([$name,$email,trim($_POST['company']??''),password_hash($pw,PASSWORD_DEFAULT),'buyer',1,now()]);$_SESSION['uid']=(int)db()->lastInsertId();captcha_new();activity(null,$_SESSION['uid'],'buyer_registered');flash('ok','Registration complete. You may now place a private offer.');header('Location: ?page=offer');exit;} if($action==='login'){$st=db()->prepare('SELECT * FROM users WHERE email=?');$st->execute([strtolower(trim($_POST['email']??''))]);$u=$st->fetch();if(!$u||!password_verify($_POST['password']??'',$u['password_hash']))throw new RuntimeException('Invalid email or password.');$_SESSION['uid']=(int)$u['id'];header('Location: ?page='.($u['role']==='admin'?'admin':'offer'));exit;} if($action==='logout'){session_destroy();header('Location: ?');exit;} if($action==='submit_offer'){$u=require_login();if($u['role']==='admin')throw new RuntimeException('Seller administrators cannot submit buyer offers.');$amount=(float)str_replace([',','$'],'',$_POST['amount']??'0');if($amount<(float)setting('minimum_bid','0'))throw new RuntimeException('Offer must be at least '.money((float)setting('minimum_bid','0')).'.');if(empty($_POST['ack']))throw new RuntimeException('You must acknowledge the private-offer disclaimer.');$st=db()->prepare('INSERT INTO offers(user_id,amount,message,status,created_at,updated_at) VALUES(?,?,?,?,?,?)');$st->execute([(int)$u['id'],$amount,trim($_POST['message']??''),'submitted',now(),now()]);$oid=(int)db()->lastInsertId();activity($oid,(int)$u['id'],'offer_submitted',money($amount));flash('ok','Your private offer has been submitted.');header('Location: ?page=offer');exit;} if($action==='settings'){require_admin();$keys=['listing_type','asset_title','primary_domain','bundle_domains','tagline','headline','subheadline','description','specifications','provenance','condition_notes','location','included_items','delivery_terms','hero_note','minimum_bid','contact_email','escrow_provider','docusign_account_id','docusign_template_id','seo_title','seo_description','seo_canonical','seo_keywords','seo_social_title','seo_social_description','seo_social_image','hero_fit'];foreach($keys as $k)save_setting($k,trim($_POST[$k]??''));foreach(['thumbnails_enabled','master_image_click','docusign_enabled','seo_index','hero_autoplay','hero_loop','hero_controls'] as $k)save_setting($k,isset($_POST[$k])?'1':'0');flash('ok','Settings saved.');header('Location: ?page=admin&tab=settings');exit;} if($action==='design_save'){require_admin();foreach(['design_heading_font','design_heading_size','design_heading_weight','design_heading_align','design_heading_x','design_heading_y','design_heading_w','design_tagline_font','design_tagline_size','design_tagline_weight','design_tagline_align','design_tagline_x','design_tagline_y','design_tagline_w','design_desc_font','design_desc_size','design_desc_weight','design_desc_align','design_desc_x','design_desc_y','design_desc_w','design_mobile_heading_size','design_mobile_tagline_size','design_mobile_desc_size'] as $k)save_setting($k,trim($_POST[$k]??''));flash('ok','Layout saved.');header('Location: ?page=admin&tab=design');exit;} if($action==='hero_upload'){require_admin();if(empty($_FILES['hero']['tmp_name']))throw new RuntimeException('Choose a media file.');$m=upload_media($_FILES['hero']);$old=setting('hero_media');if($old)@unlink(__DIR__.'/uploads/'.$old);save_setting('hero_media',$m['filename']);save_setting('hero_media_type',$m['type']);flash('ok','Hero media updated.');header('Location: ?page=admin&tab=design');exit;} if($action==='hero_remove'){require_admin();$old=setting('hero_media');if($old)@unlink(__DIR__.'/uploads/'.$old);save_setting('hero_media','');save_setting('hero_media_type','');flash('ok','Hero media removed.');header('Location: ?page=admin&tab=design');exit;} if($action==='gallery_upload'){require_admin();if(empty($_FILES['media']['tmp_name']))throw new RuntimeException('Choose media.');$m=upload_media($_FILES['media']);$st=db()->prepare('INSERT INTO gallery(filename,alt_text,sort_order,created_at,media_type,mime_type) VALUES(?,?,?,?,?,?)');$st->execute([$m['filename'],trim($_POST['alt_text']??''),(int)($_POST['sort_order']??0),now(),$m['type'],$m['mime']]);flash('ok','Gallery media added.');header('Location: ?page=admin&tab=gallery');exit;} if($action==='gallery_delete'){require_admin();$id=(int)($_POST['id']??0);$st=db()->prepare('SELECT * FROM gallery WHERE id=?');$st->execute([$id]);if($g=$st->fetch()){@unlink(__DIR__.'/uploads/'.$g['filename']);db()->prepare('DELETE FROM gallery WHERE id=?')->execute([$id]);}flash('ok','Gallery media removed.');header('Location: ?page=admin&tab=gallery');exit;} if($action==='faq_save'){require_admin();$items=[];foreach($_POST['faq_q']??[] as $i=>$q){$q=trim((string)$q);$a=trim((string)(($_POST['faq_a']??[])[$i]??''));if($q&&$a)$items[]=['q'=>$q,'a'=>$a];}save_setting('faq_json',json_encode($items,JSON_UNESCAPED_SLASHES));flash('ok','FAQ saved.');header('Location: ?page=admin&tab=faq');exit;} if($action==='admin_offer'){$a=require_admin();$id=(int)($_POST['offer_id']??0);$decision=$_POST['decision']??'';$map=['accept'=>'accepted','reject'=>'rejected','counter'=>'countered'];if(!isset($map[$decision]))throw new RuntimeException('Invalid decision.');$counter=null;if($decision==='counter'){$counter=(float)str_replace([',','$'],'',$_POST['counter_amount']??'0');if($counter<=0)throw new RuntimeException('Enter a valid counter amount.');}db()->prepare('UPDATE offers SET status=?,counter_amount=?,updated_at=? WHERE id=?')->execute([$map[$decision],$counter,now(),$id]);activity($id,(int)$a['id'],'seller_'.$decision,$counter?money($counter):'');if($decision==='accept')db()->prepare('INSERT OR IGNORE INTO transactions(offer_id,agreement_status,escrow_status,created_at) VALUES(?,?,?,?)')->execute([$id,'pending_signature','not_started',now()]);flash('ok','Offer updated.');header('Location: ?page=admin&tab=offers');exit;} if($action==='transaction_update'){$a=require_admin();$oid=(int)($_POST['offer_id']??0);$st=db()->prepare('SELECT * FROM transactions WHERE offer_id=?');$st->execute([$oid]);$tx=$st->fetch();if(!$tx)throw new RuntimeException('Transaction not found.');$escrow=$_POST['escrow_status']??$tx['escrow_status'];$funds=$tx['funds_confirmed_at'];$deadline=$tx['transfer_deadline'];if($escrow==='funds_secured'&&!$funds){$funds=now();$deadline=add_business_days($funds,3);}db()->prepare('UPDATE transactions SET agreement_status=?,docusign_envelope_id=?,escrow_status=?,escrow_reference=?,funds_confirmed_at=?,transfer_deadline=?,transfer_started_at=?,transfer_completed_at=? WHERE offer_id=?')->execute([$_POST['agreement_status']??$tx['agreement_status'],trim($_POST['docusign_envelope_id']??''),$escrow,trim($_POST['escrow_reference']??''),$funds,$deadline,!empty($_POST['transfer_started'])?($tx['transfer_started_at']?:now()):$tx['transfer_started_at'],!empty($_POST['transfer_completed'])?($tx['transfer_completed_at']?:now()):$tx['transfer_completed_at'],$oid]);flash('ok','Closing workflow updated.');header('Location: ?page=admin&tab=offers');exit;} }catch(Throwable $ex){$error=$ex->getMessage();} $page=$_GET['page']??'home';$u=current_user();$flash=pull_flash();$gallery=gallery_items();$seoTitle=setting('seo_title',setting('asset_title'));$seoDesc=setting('seo_description');$canonical=setting('seo_canonical')?:site_url();$isItem=setting('listing_type')==='item'; ?>