<?php
// st-install.php — ONE-FILE, ONE-BUTTON auto-installer for SELF-HOSTED Soft-Transport (David 2026-06-13:
// "they press a button, the autoverificator checks, if it has everything it auto does the 3 files plus
// everything, if not it waits and tells the user that it needs those"). The customer uploads THIS single
// file to their own server's web root and opens it. It auto-checks the server; if ready, ONE button:
//   1) downloads the app (transport.php + transport.zip + transport.error.php) from soft-transport.com,
//   2) runs the whole install by driving the transport.php wizard automatically — the SAME 7-stage contract
//      the auto-provisioning engine already proved (admin/provision/_provisioner.class.php::step_wizard),
//   3) activates the license (calls soft-transport.com/activate.php with this server's IP, writes inc/license.txt).
// If ionCube or any requirement is missing it STOPS and tells the customer exactly what to enable. On any
// hiccup it falls back to the manual checklist (instalare.php). No IT knowledge, no clicking through.
//
// ?token=<64hex> (carried from the purchase link) → auto-activates at the end.

// [cc] SAFETY: this installer is meant to run on the CUSTOMER's own server. If someone opens it on
// soft-transport.com itself, refuse — it must be downloaded and placed on the customer's hosting.
if ( stripos( (string) ( $_SERVER['HTTP_HOST'] ?? '' ), 'soft-transport.com' ) !== false ) {
	header( 'Content-Type: text/html; charset=utf-8' );
	exit( '<p style="font:18px system-ui;max-width:560px;margin:60px auto;color:#333">Acest fișier se rulează pe <b>serverul tău</b>, nu aici. Descarcă-l din pagina de instalare și pune-l în folderul site-ului tău, apoi deschide-l de pe domeniul tău.</p>' );
}

@set_time_limit( 0 );
@ini_set( 'memory_limit', '512M' );

// [cc 2026-09-07] instalare.php rescrie linia de mai jos la descărcare când linkul poartă un token; ?token= din URL are prioritate.
$ST_TOKEN = ''; /*ST_TOKEN*/

$scheme    = ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ? 'https' : 'http';
$selfdir   = rtrim( str_replace( '\\', '/', dirname( (string) ( $_SERVER['SCRIPT_NAME'] ?? '/' ) ) ), '/' );
$SELF      = $scheme . '://' . ( $_SERVER['HTTP_HOST'] ?? 'localhost' ) . $selfdir;   // [cc] this script's OWN folder (transport.php is a sibling) — works at web root AND in a subfolder
$token     = (string) ( $_GET['token'] ?? '' );
if ( $token === '' ) { $token = (string) $ST_TOKEN; }
if ( ! preg_match( '/^[a-f0-9]{64}$/', $token ) ) { $token = ''; }

// [cc 2026-09-07] The installer talks ONLY to soft-transport.com. The one exception is a developer's
// sibling file st-install.local.php returning a base URL — gitignored, never shipped, never linked.
function st_central(): string {
	$loc = __DIR__ . '/st-install.local.php';
	if ( is_file( $loc ) ) { $u = @include $loc; if ( is_string( $u ) && preg_match( '#^https?://#', $u ) ) { return rtrim( $u, '/' ); } }
	return 'https://www.soft-transport.com';
}
function st_manifest(): ?array {
	[ $code, , $body ] = st_http( st_central() . '/update.manifest.php', null, null );
	$m = ( $code === 200 ) ? json_decode( (string) $body, true ) : null;
	return ( is_array( $m ) && ! empty( $m['ok'] ) && isset( $m['files'] ) ) ? $m : null;
}

/* ST-CHECKS-BEGIN */
// [cc 2026-09-07] Shared requirements block, byte-identical copy in st-install.php. Shape: [ label, ok, detail, hint, blocker ]; blockers stop install, rest are amber.
function st_bytes( $v ) { $v = trim( (string) $v ); if ( $v === '' ) { return 0; } if ( $v === '-1' ) { return PHP_INT_MAX; } $n = (float) $v; $u = strtoupper( substr( $v, -1 ) ); return (int) ( $n * ( $u === 'G' ? 1073741824 : ( $u === 'M' ? 1048576 : ( $u === 'K' ? 1024 : 1 ) ) ) ); }
function st_https_ok( $url ) { if ( ! function_exists( 'curl_init' ) ) { return [ false, 'curl lipsă' ]; } $ch = curl_init( $url ); curl_setopt_array( $ch, [ CURLOPT_NOBODY => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2 ] ); curl_exec( $ch ); $code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE ); $err = curl_error( $ch ); curl_close( $ch ); return [ $code >= 200 && $code < 500, $err !== '' ? $err : ( 'HTTP ' . $code ) ]; }
function st_self_get( $url ) {
	// [cc] fetch our own URL; if the server cannot reach its own name, pin the host to 127.0.0.1 then to SERVER_ADDR
	if ( ! function_exists( 'curl_init' ) ) { return [ 0, '' ]; }
	$host = (string) parse_url( $url, PHP_URL_HOST ); $port = (int) ( parse_url( $url, PHP_URL_PORT ) ?: ( parse_url( $url, PHP_URL_SCHEME ) === 'https' ? 443 : 80 ) );
	foreach ( [ null, '127.0.0.1', (string) ( $_SERVER['SERVER_ADDR'] ?? '' ) ] as $pin ) {
		if ( $pin === '' ) { continue; }
		$ch = curl_init( $url ); curl_setopt_array( $ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8, CURLOPT_CONNECTTIMEOUT => 4, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0 ] );
		if ( $pin !== null ) { curl_setopt( $ch, CURLOPT_RESOLVE, [ $host . ':' . $port . ':' . $pin ] ); }
		$b = curl_exec( $ch ); $code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE ); curl_close( $ch );
		if ( $code > 0 ) { return [ $code, (string) $b ]; }
	}
	return [ 0, '' ];
}
function st_requirements( array $opt = [] ): array {
	$c = [];
	// [cc 2026-09-07] 7.2 e pragul REAL (kitul e criptat pentru ținta 7.2 și o parte din flotă rulează 7.2.34); 7.4+ rămâne recomandarea, ca rând galben separat
	$c[] = [ 'PHP ' . PHP_VERSION, version_compare( PHP_VERSION, '7.2.0', '>=' ), 'Necesar: PHP 7.2 sau mai nou — pe serverul tău rulează PHP ' . PHP_VERSION, 'Cere furnizorului de hosting PHP 7.2+ (ideal 8.1).', true ];
	$c[] = [ 'PHP recomandat 7.4+', version_compare( PHP_VERSION, '7.4.0', '>=' ), 'Pe serverul tău rulează PHP ' . PHP_VERSION . ' — merge, dar 7.4 sau 8.1 e recomandat', 'Cere trecerea la PHP 7.4 sau 8.1 când poți — e mai rapid și mai sigur.', false ];
	$icOk = extension_loaded( 'ionCube Loader' ); $icVer = ( $icOk && function_exists( 'ioncube_loader_version' ) ) ? (float) ioncube_loader_version() : 0;
	$c[] = [ 'ionCube Loader' . ( $icOk ? ' v' . ( $icVer ?: '?' ) : '' ), $icOk && ( $icVer >= 10 || $icVer === 0.0 ), 'OBLIGATORIU — codul Soft-Transport este criptat ionCube', 'Cere furnizorului să activeze extensia „ionCube Loader" (v10+) pentru PHP. Pe Plesk/cPanel e o bifă.', true ];
	$c[] = [ 'MySQL (mysqli)', extension_loaded( 'mysqli' ), 'Necesar pentru baza de date', 'Cere activarea extensiei „mysqli".', true ];
	$c[] = [ 'Arhivare ZIP', class_exists( 'ZipArchive' ) || extension_loaded( 'zip' ), 'Necesar pentru despachetarea aplicației', 'Cere activarea extensiei PHP „zip".', true ];
	$c[] = [ 'cURL', extension_loaded( 'curl' ), 'Hărți HERE, Google Drive, actualizări', 'Cere activarea extensiei „curl".', true ];
	$c[] = [ 'openssl', extension_loaded( 'openssl' ), 'Conexiuni securizate', 'Cere activarea extensiei „openssl".', true ];
	$c[] = [ 'json', extension_loaded( 'json' ), 'Date interne', 'Cere activarea extensiei „json".', true ];
	$c[] = [ 'mbstring', extension_loaded( 'mbstring' ), 'Text cu diacritice', 'Cere activarea extensiei „mbstring".', false ];
	$c[] = [ 'gd', extension_loaded( 'gd' ), 'Imagini și PDF', 'Cere activarea extensiei „gd".', false ];
	$probe = __DIR__ . '/.st_write_probe_' . substr( md5( (string) getmypid() ), 0, 8 ); $wr = @file_put_contents( $probe, 'ok' ) !== false; if ( $wr ) { @unlink( $probe ); }
	$c[] = [ 'Scriere în folder', $wr, 'Necesar pentru instalare și configurare', 'Folderul în care ai pus acest fișier trebuie să fie scriibil (permisiuni 755/775).', true ];
	$ht = __DIR__ . '/.htaccess'; $htOk = ! file_exists( $ht ) || is_writable( $ht );
	$c[] = [ '.htaccess din rădăcină', $htOk, $htOk ? ( file_exists( $ht ) ? 'există și se poate rescrie' : 'nu există încă (se creează la instalare)' ) : 'există dar NU se poate rescrie', 'Un singur fișier neinscriptibil oprește toată despachetarea. Cere drept de scriere pe .htaccess.', true ];
	$free = @disk_free_space( __DIR__ ); $freeMb = $free !== false ? (int) ( $free / 1048576 ) : -1;
	$c[] = [ 'Spațiu liber: ' . ( $freeMb < 0 ? '?' : $freeMb . ' MB' ), $freeMb < 0 || $freeMb >= 600, 'Necesar: 600 MB (arhivă + copie de siguranță + despachetare)', 'Eliberează spațiu sau cere mai mult de la hosting.', true ];
	// [cc 2026-09-07] instalatorul ridică singur limita la 512M; dacă ini_set e permis, un 128M pornit nu mai e blocant — doar galben
	$mem = (string) ini_get( 'memory_limit' ); $memOk = st_bytes( $mem ) >= 256 * 1048576 || @ini_set( 'memory_limit', '256M' ) !== false;
	$c[] = [ 'Memorie PHP: ' . $mem, $memOk, $memOk ? 'Recomandat: 256M+ (limita se poate ridica din instalator)' : 'Recomandat: 256M+, iar limita NU se poate ridica din PHP', 'Cere ridicarea „memory_limit" la 256M.', false ];
	$met = (int) ini_get( 'max_execution_time' ); $canSet = function_exists( 'set_time_limit' ) && ! in_array( 'set_time_limit', array_map( 'trim', explode( ',', (string) ini_get( 'disable_functions' ) ) ), true );
	$c[] = [ 'Timp de execuție: ' . ( $met === 0 ? 'nelimitat' : $met . 's' ), $met === 0 || $met >= 300 || $canSet, 'Necesar: 300s sau set_time_limit permis', 'Cere „max_execution_time = 300".', true ];
	$up = st_bytes( ini_get( 'upload_max_filesize' ) ); $post = st_bytes( ini_get( 'post_max_size' ) );
	$c[] = [ 'Upload: ' . ini_get( 'upload_max_filesize' ) . ' / ' . ini_get( 'post_max_size' ), $up >= 32 * 1048576 && $post >= 32 * 1048576, 'Recomandat: 32M pentru documente scanate', 'Cere „upload_max_filesize = 32M" și „post_max_size = 32M".', false ];
	$miv = (int) ini_get( 'max_input_vars' ); $c[] = [ 'max_input_vars: ' . $miv, $miv >= 3000, 'Recomandat: 3000 (formulare mari)', 'Cere „max_input_vars = 3000".', false ];
	// [cc 2026-09-07] spec §3.3: open_basedir restrânge căile, iar /tmp e folosit la dezarhivare și la copiile de siguranță
	$obd = trim( (string) ini_get( 'open_basedir' ) );
	$c[] = [ 'open_basedir: ' . ( $obd !== '' ? $obd : 'nesetat' ), $obd === '', $obd === '' ? 'fără restricții de cale' : 'căi restricționate — /tmp și copiile de siguranță pot eșua', 'Cere adăugarea folderului temporar (/tmp) în „open_basedir", sau dezactivarea lui.', false ];
	[ $h1, $h1w ] = st_https_ok( 'https://www.soft-transport.com/update.manifest.php' );
	$c[] = [ 'HTTPS spre soft-transport.com', $h1, $h1 ? 'merge, cu verificarea certificatului' : $h1w, 'Serverul trebuie să iasă pe internet prin HTTPS cu certificate valide (curl.cainfo / ca-certificates).', true ];
	[ $h2, $h2w ] = st_https_ok( 'https://www.googleapis.com/discovery/v1/apis' ); [ $h3, $h3w ] = st_https_ok( 'https://router.hereapi.com/' );
	$c[] = [ 'HTTPS spre Google și HERE', $h2 && $h3, ( $h2 ? 'Google ok' : 'Google: ' . $h2w ) . ' · ' . ( $h3 ? 'HERE ok' : 'HERE: ' . $h3w ), 'Firewall-ul trebuie să permită ieșirea spre googleapis.com și hereapi.com.', false ];
	$rwOk = false; $rwDetail = 'neverificat (nu pot apela propriul server)';
	$rwDir = __DIR__ . '/.st_rw_probe'; if ( ! is_dir( $rwDir ) ) { @mkdir( $rwDir, 0755 ); }
	if ( is_dir( $rwDir ) ) {
		$w1 = @file_put_contents( $rwDir . '/.htaccess', "RewriteEngine On\nRewriteRule ^x$ ok.txt [L]\n" ); $w2 = @file_put_contents( $rwDir . '/ok.txt', 'ok' ); // [cc 2026-09-07] cleanup below now runs even if a write fails partway — no leaked .st_rw_probe/
		if ( $w1 !== false && $w2 !== false ) {
			$self = isset( $opt['self_url'] ) ? (string) $opt['self_url'] : ( ( ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ? 'https' : 'http' ) . '://' . ( $_SERVER['HTTP_HOST'] ?? 'localhost' ) . rtrim( str_replace( '\\', '/', dirname( (string) ( $_SERVER['SCRIPT_NAME'] ?? '/' ) ) ), '/' ) );
			[ $code, $body ] = st_self_get( $self . '/.st_rw_probe/x' );
			if ( $code === 200 && trim( $body ) === 'ok' ) { $rwOk = true; $rwDetail = 'mod_rewrite și .htaccess funcționează'; } elseif ( $code > 0 ) { $rwDetail = 'regulile .htaccess NU se aplică (HTTP ' . $code . ')'; }
		}
		@unlink( $rwDir . '/.htaccess' ); @unlink( $rwDir . '/ok.txt' ); @rmdir( $rwDir );
	}
	$c[] = [ 'Rescriere URL (mod_rewrite)', $rwOk, $rwDetail, 'Aplicația șoferilor folosește /api/ prin .htaccess. Cere „AllowOverride All" și mod_rewrite.', false ];
	$sub = rtrim( str_replace( '\\', '/', dirname( (string) ( $_SERVER['SCRIPT_NAME'] ?? '/' ) ) ), '/' );
	$c[] = [ 'Locație: ' . ( $sub === '' ? 'rădăcina domeniului' : 'subfolder ' . $sub ), true, 'Aplicația va fi la ' . ( $sub === '' ? '/' : $sub . '/' ), '', false ];
	$mailOk = function_exists( 'mail' ) && trim( (string) ini_get( 'sendmail_path' ) ) !== '';
	$c[] = [ 'Email din PHP', $mailOk, $mailOk ? 'sendmail configurat' : 'sendmail_path lipsă', 'Notificările pleacă prin mail(). Cere configurarea sendmail/postfix.', false ];
	$tz = (string) ini_get( 'date.timezone' ); $c[] = [ 'Fus orar: ' . ( $tz !== '' ? $tz : 'nesetat' ), $tz !== '', 'Orele din comenzi depind de el', 'Cere „date.timezone = Europe/Bucharest".', false ];
	if ( ! empty( $opt['db_name'] ) && extension_loaded( 'mysqli' ) ) {
		if ( function_exists( 'mysqli_report' ) ) { mysqli_report( MYSQLI_REPORT_OFF ); } // [cc 2026-09-07] PHP 8.1+ defaults to MYSQLI_REPORT_STRICT (throws on connect failure) — keep the connect_errno branch working
		$mysqli = @new mysqli( 'localhost', (string) $opt['db_user'], (string) $opt['db_pass'], (string) $opt['db_name'] );
		if ( $mysqli->connect_errno ) { $c[] = [ 'Baza de date', false, $mysqli->connect_error, 'Verifică numele bazei, utilizatorul și parola în panoul hostingului.', true ]; }
		else { $ver = $mysqli->server_info; $q = $mysqli->query( 'SELECT @@sql_mode' ); $row = $q ? $q->fetch_row() : null; $mode = (string) ( $row[0] ?? '' ); $mysqli->close(); // [cc 2026-09-07] query() returns false on failure — guard before fetch_row() to avoid a fatal
			// [cc 2026-09-07] spec §3.3: prag real de versiune (MySQL 5.7 / MariaDB 10.2) — era hardcodat true; MariaDB raportează „5.5.5-10.x-MariaDB", deci tăiem prefixul înainte de comparație
			$vraw = preg_replace( '/^5\.5\.5-/', '', (string) $ver ); $isMaria = stripos( $vraw, 'mariadb' ) !== false;
			$vnum = preg_match( '/^(\d+\.\d+(?:\.\d+)?)/', $vraw, $vm ) ? $vm[1] : '0';
			$verOk = $isMaria ? version_compare( $vnum, '10.2', '>=' ) : version_compare( $vnum, '5.7', '>=' );
			$c[] = [ 'Baza de date: MySQL ' . $ver, $verOk, $verOk ? 'conectare reușită' : 'versiune prea veche — necesar MySQL 5.7+ sau MariaDB 10.2+', 'Cere furnizorului MySQL 5.7 sau mai nou (MariaDB 10.2+).', true ];
			$c[] = [ 'sql_mode', stripos( $mode, 'ONLY_FULL_GROUP_BY' ) === false, $mode === '' ? 'implicit' : $mode, 'Cere scoaterea ONLY_FULL_GROUP_BY din sql_mode (SET GLOBAL sql_mode = \'\').', false ]; }
	}
	$blockers = [];
	foreach ( $c as $x ) { if ( ! $x[1] && $x[4] ) { $blockers[] = $x[0]; } }
	return [ $c, count( $blockers ) === 0, $blockers ];
}
/* ST-CHECKS-END */

// ── HTTP with loopback fallback: a server that cannot resolve its own name (NUMBER1) still installs ──
// [cc 2026-09-07] added the 127.0.0.1/SERVER_ADDR pin + $verify flag so central calls stay TLS-verified while self-calls skip it
function st_http( string $url, $post, ?string $cookie, bool $verify = true, array $hdrs = [], bool $multipart = false ): array {
	$host = (string) parse_url( $url, PHP_URL_HOST ); $port = (int) ( parse_url( $url, PHP_URL_PORT ) ?: ( parse_url( $url, PHP_URL_SCHEME ) === 'https' ? 443 : 80 ) );
	$isSelf = ( $host === (string) ( $_SERVER['HTTP_HOST'] ?? '' ) ) || ( $host === (string) parse_url( $GLOBALS['SELF'] ?? '', PHP_URL_HOST ) );
	$pins = $isSelf ? [ null, '127.0.0.1', (string) ( $_SERVER['SERVER_ADDR'] ?? '' ) ] : [ null ];
	foreach ( $pins as $pin ) {
		if ( $pin === '' ) { continue; }
		$ch = curl_init( $url );
		curl_setopt_array( $ch, [
			CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => false, CURLOPT_HEADER => true,
			CURLOPT_SSL_VERIFYPEER => ( $verify && ! $isSelf ), CURLOPT_SSL_VERIFYHOST => ( $verify && ! $isSelf ) ? 2 : 0,
			CURLOPT_TIMEOUT => 600, CURLOPT_CONNECTTIMEOUT => 20, CURLOPT_USERAGENT => 'Soft-Transport self-installer',
		] );
		if ( $hdrs ) { curl_setopt( $ch, CURLOPT_HTTPHEADER, $hdrs ); }   // [cc 2026-09-07] instalatorul v33 raspunde '1' DOAR cererii de tip AJAX (Referer/Origin/X-Requested-With) — vezi _provisioner.class.php::wizardCurl, contractul dovedit pe zzztest7
		if ( $pin !== null ) { curl_setopt( $ch, CURLOPT_RESOLVE, [ $host . ':' . $port . ':' . $pin ] ); }
		if ( $cookie ) { curl_setopt( $ch, CURLOPT_COOKIEJAR, $cookie ); curl_setopt( $ch, CURLOPT_COOKIEFILE, $cookie ); }
		if ( $post !== null ) { curl_setopt( $ch, CURLOPT_POST, true ); curl_setopt( $ch, CURLOPT_POSTFIELDS, ( is_array( $post ) && ! $multipart ) ? http_build_query( $post ) : $post ); }   // [cc 2026-09-07] multipart = exact ce trimite motorul de provizionare
		$resp = curl_exec( $ch ); $code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE ); $hsz = (int) curl_getinfo( $ch, CURLINFO_HEADER_SIZE ); curl_close( $ch );
		if ( $code > 0 ) { $resp = (string) $resp; return [ $code, substr( $resp, 0, $hsz ), substr( $resp, $hsz ) ]; }
	}
	return [ 0, '', '' ];
}

// ── stream a file to disk and verify the manifest sha1; nothing is left behind on mismatch ──
// [cc 2026-09-07] new: replaces the old unverified st_download(); manifest-driven release resolution needs a sha1 gate
function st_download_verified( string $url, string $dest, string $sha1 ): bool {
	$ch = curl_init( $url ); $out = @fopen( $dest, 'wb' ); if ( ! $out ) { return false; }
	curl_setopt_array( $ch, [ CURLOPT_FILE => $out, CURLOPT_TIMEOUT => 1800, CURLOPT_CONNECTTIMEOUT => 20, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_FOLLOWLOCATION => true ] );
	$okc = curl_exec( $ch ); $code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE ); curl_close( $ch ); fclose( $out );
	if ( $okc === false || $code !== 200 || ! is_file( $dest ) || filesize( $dest ) < 64 || ( $sha1 !== '' && strtolower( sha1_file( $dest ) ) !== strtolower( $sha1 ) ) ) { @unlink( $dest ); return false; }
	return true;
}

// ── progress to soft admin (only with a token; silent failure never blocks the install) ──
// [cc 2026-09-07] new: install.report.php doesn't exist yet (Task 6) — a non-200 or missing token just returns null, never throws
function st_report( string $token, string $stage, bool $ok, array $details ): ?array {
	if ( $token === '' ) { return null; }
	[ $code, , $body ] = st_http( st_central() . '/install.report.php', [ 't' => $token, 'stage' => $stage, 'ok' => $ok ? '1' : '0', 'details' => json_encode( $details ) ], null );
	$j = ( $code === 200 ) ? json_decode( (string) $body, true ) : null;
	return is_array( $j ) ? $j : null;
}

// [cc 2026-09-07] Contractul instalatorului, copiat din motorul de provizionare (_provisioner.class.php::step_wizard,
// [cc] dovedit live pe zzztest7): pasii POST raspund '1' (sau '-12' = deja instalat) DOAR daca cererea are
// [cc] anteturile de AJAX, iar paginile 3/4/5 trebuie sa contina titlul „N/5". O verificare mai ingaduitoare
// [cc] a lasat o instalare sa raporteze succes fara ca inc/configuration.ini sa fie scris — nu o repetam.
function st_wizard_hdrs( string $base, bool $post ): array {
	$o = 'https://' . (string) parse_url( $base, PHP_URL_HOST );
	$h = [ 'Referer: ' . $o . '/transport.php', 'Origin: ' . $o ];
	if ( $post ) { $h[] = 'X-Requested-With: XMLHttpRequest'; }
	return $h;
}
function st_wizard_say( int $code, string $body ): string {
	return 'HTTP ' . $code . ' · ' . substr( trim( preg_replace( '/\s+/', ' ', strip_tags( $body ) ) ), 0, 120 );
}

// ── identity marker read by the tenant's hotfix agent (Plan 2); written once, never overwritten ──
// [cc 2026-09-07] .st_identity.php with an exiting first line + company_raw 'X<id>' — exact shape hf_identity() reads in the tenant's sys/_hotfix.php
function st_write_identity( int $companyId ): void {
	$dir = __DIR__ . '/temporary'; if ( ! is_dir( $dir ) ) { @mkdir( $dir, 0775, true ); }
	$f = $dir . '/.st_identity.php'; if ( is_file( $f ) || is_file( $dir . '/.st_identity.json' ) || $companyId <= 0 ) { return; }
	st_guard_dir( $dir );
	$row = [ 'company_id' => $companyId, 'company_raw' => 'X' . $companyId, 'install_id' => bin2hex( random_bytes( 32 ) ), 'created' => date( 'c' ) ];
	if ( @file_put_contents( $f, '<?php exit; ?>' . "\n" . json_encode( $row ) ) !== false ) { @chmod( $f, 0600 ); }
}

// [cc 2026-09-07] the two guards hf_guard_dir() drops on the tenant — temporary/ is web-served and the root .htaccess denies by extension only
function st_guard_dir( string $d ): void {
	if ( ! is_dir( $d ) ) { return; }
	$h = $d . '/.htaccess';
	if ( ! is_file( $h ) ) { @file_put_contents( $h, "<IfModule mod_authz_core.c>\n\tRequire all denied\n</IfModule>\n<IfModule !mod_authz_core.c>\n\tOrder allow,deny\n\tDeny from all\n</IfModule>\n" ); }
	$i = $d . '/index.php';
	if ( ! is_file( $i ) ) { @file_put_contents( $i, "<?php http_response_code( 403 ); exit; ?>\n" ); }
}

// [cc 2026-09-07] api/.htaccess lipsea din kit pe ~115 tenanți → aplicația șoferilor dă 404; conținut copiat octet cu octet dintr-o instalare care merge
function st_api_htaccess_body(): string {
	$lines = [
		'# API URL rewrite — converts /api/<path> into /api/index.php?url=<path>',
		'# and forwards the Authorization header to PHP (Plesk/Apache strip it',
		'# by default when running PHP-FPM behind the front-end server).',
		'',
		'RewriteEngine On',
		'',
		'# Pass the Authorization header through to PHP. PHP exposes it via the',
		'# HTTP_AUTHORIZATION env var, which $general->getBasicAuthorizationData()',
		'# reads. Without this rewrite, Plesk\'s Apache→PHP-FPM proxy drops it',
		'# and every auth call returns "Authorization not found".',
		'RewriteCond %{HTTP:Authorization} ^(.*)',
		'RewriteRule .* - [E=HTTP_AUTHORIZATION:%1]',
		'',
		'# Skip the rewrite for actual files and directories so existing assets',
		'# (system/, etc.) keep serving directly.',
		'RewriteCond %{REQUEST_FILENAME} !-f',
		'RewriteCond %{REQUEST_FILENAME} !-d',
		'',
		'# Route everything else through index.php. QSA preserves any extra',
		'# query parameters; L stops further rewriting.',
		'RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]',
	];
	return implode( "\r\n", $lines ) . "\r\n";
}

// [cc 2026-09-07] se scrie DOAR dacă api/ există și fișierul lipsește — niciodată peste unul existent
function st_write_api_htaccess( array &$log ): void {
	$dir = __DIR__ . '/api';
	if ( ! is_dir( $dir ) ) { $log[] = 'Fără folder api/ — sar peste api/.htaccess'; return; }
	$f = $dir . '/.htaccess';
	if ( is_file( $f ) ) { $log[] = 'api/.htaccess exista deja'; return; }
	if ( @file_put_contents( $f, st_api_htaccess_body() ) !== false ) { $log[] = 'Am creat api/.htaccess (aplicația șoferilor)'; }
	else { $log[] = 'Nu am putut scrie api/.htaccess — aplicația șoferilor va da 404 până îl creezi manual'; }
}

// [cc 2026-09-07] apply install.seed.php's SQL statement-by-statement on the tenant DB — Task 7
function st_apply_seed( string $db, string $user, string $pass, string $sql ): array {
	if ( function_exists( 'mysqli_report' ) ) { mysqli_report( MYSQLI_REPORT_OFF ); } // [cc 2026-09-07] PHP 8.1+ defaults to MYSQLI_REPORT_STRICT (throws on query error) — same fix as st_requirements()
	$m = @new mysqli( 'localhost', $user, $pass, $db ); if ( $m->connect_errno ) { return [ 0, 1, $m->connect_error ]; }
	$m->set_charset( 'utf8mb4' ); $m->query( "SET NAMES utf8mb4" ); $m->query( "SET foreign_key_checks = 0" ); $m->query( "SET sql_mode = ''" );
	// [cc 2026-09-07] ONE transaction — the seed bundle is DML only, so a partial failure must never leave a half-applied password reset — fix round 1
	if ( method_exists( $m, 'begin_transaction' ) ) { $m->begin_transaction(); } else { $m->query( 'START TRANSACTION' ); }
	$applied = 0; $failed = 0; $first = '';
	foreach ( preg_split( '/;\s*\n/', $sql ) as $stmt ) {
		$stmt = trim( $stmt, "; \t\r\n" ); if ( $stmt === '' ) { continue; }
		if ( $m->query( $stmt ) ) { $applied++; } else { $failed = 1; $first = $m->error; break; } // [cc 2026-09-07] stop at the first failure — fix round 1
	}
	if ( $failed > 0 ) { $m->rollback(); $applied = 0; } else { $m->commit(); } // [cc 2026-09-07] rollback on any failure, commit only on full success — fix round 1
	$m->close(); return [ $applied, $failed, $first ];
}

if ( defined( 'ST_INSTALL_LIB' ) ) { return; }

// [cc 2026-09-07] now resolved via st_central() instead of a hardcoded soft-transport.com (so the dev override applies here too)
$ACTIVATE = st_central() . '/activate.php';
$MANUAL   = st_central() . '/instalare.php';

// ════════════════════════════════════════════════════════════════════════════════════════════════════════
// POST — run the whole install
// ════════════════════════════════════════════════════════════════════════════════════════════════════════
$result = null;
if ( $_SERVER['REQUEST_METHOD'] === 'POST' && ( $_POST['op'] ?? '' ) === 'install' ) {
	$db   = preg_replace( '/[^a-z0-9_]/i', '', (string) ( $_POST['db'] ?? '' ) );        // DB name == user (wizard requires identical)
	$pass = (string) ( $_POST['dbpass'] ?? '' );
	$tm = []; preg_match( '/[a-f0-9]{64}/', (string) ( $_POST['actlink'] ?? '' ), $tm ); $tok = $tm[0] ?? '';
	if ( $tok === '' ) { $tok = $token; }   // [cc 2026-09-07] tokenul copt în fișier la descărcare, dacă clientul nu lipește linkul
	$adminPw = substr( bin2hex( random_bytes( 6 ) ), 0, 10 );  // app login password (shown at the end)
	$log = [];
	$ok  = true;
	try {
		[ , $allOk, $blk ] = st_requirements();
		// [cc 2026-09-07] „check" se raportează ÎNAINTE de aruncare — altfel un server roșu nu producea niciun raport
		st_report( $tok, 'check', $allOk, [ 'php' => PHP_VERSION, 'loader' => function_exists( 'ioncube_loader_version' ) ? (string) ioncube_loader_version() : '', 'host' => (string) ( $_SERVER['HTTP_HOST'] ?? '' ), 'dir' => $GLOBALS['selfdir'], 'blockers' => $blk, 'reason' => $allOk ? '' : implode( ', ', $blk ) ] );
		if ( ! $allOk ) { throw new Exception( 'Serverul nu îndeplinește încă cerințele: ' . implode( ', ', $blk ) ); }
		if ( $db === '' || $pass === '' ) { throw new Exception( 'Completează numele bazei de date și parola.' ); }

		// [cc 2026-09-07] 1) resolve + download the current release (sha1-verified) — never a hardcoded folder
		$rel = st_manifest();
		if ( $rel === null ) { throw new Exception( 'Nu am putut citi lista versiunii curente de pe soft-transport.com. Verifică ieșirea HTTPS a serverului.' ); }
		// [cc 2026-09-07] transport.php din canalul de actualizare e MOTORUL DE ACTUALIZARE: pe un server gol el
		// [cc] doar despachetează codul și sare peste baza de date, deci instalarea rămânea fără configuration.ini.
		// [cc] Instalatorul complet în 5 pași se publică separat (releases.json → 'installer') și îl ia pe locul lui.
		$ins = ( isset( $rel['installer'] ) && is_array( $rel['installer'] ) && ! empty( $rel['installer']['url'] ) ) ? $rel['installer'] : null;
		if ( $ins === null ) { throw new Exception( 'Versiunea publicată nu conține instalatorul complet, doar actualizarea. Scrie-ne la contact@soft-transport.ro — nu pornim o instalare pe jumătate.' ); }
		foreach ( $rel['files'] as $f ) {
			$name = basename( (string) $f['path'] );
			$url  = (string) $f['url']; $sha = (string) ( $f['sha1'] ?? '' );
			if ( $name === 'transport.php' ) { $url = (string) $ins['url']; $sha = (string) ( $ins['sha1'] ?? '' ); }
			$dest = __DIR__ . '/' . $name;
			if ( ! st_download_verified( $url, $dest, $sha ) ) { st_report( $tok, 'install', false, [ 'step' => 'download', 'file' => $f['path'] ] ); throw new Exception( 'Nu am putut descărca sau verifica ' . $name . '. Reîncearcă sau folosește instalarea manuală.' ); }
			$log[] = 'Descărcat ' . $name . ( $name === 'transport.php' ? ' (instalator complet)' : '' ) . ' (v' . $rel['version'] . ')';
		}

		// [cc 2026-09-07] LICENȚA SE SCRIE ÎNAINTE DE INSTALATOR. transport.php citește inc/license.txt ca să decidă
		// [cc] ce este: fără el rulează doar în „mod actualizare" (despachetează codul și SARE peste baza de date —
		// [cc] „instalare noua - se configureaza separat"), deci instalarea părea reușită dar inc/configuration.ini
		// [cc] nu se scria niciodată. Cu licența pe disc pornește instalatorul complet în 5 pași. Raportul „license"
		// [cc] rămâne DUPĂ „install", pentru că mașina de stări din central impune ordinea pașilor.
		$activated = false; $passwords = []; $seedOk = false; $seedError = ''; // [cc 2026-09-07] Task 7 — seed bundle state (seedError added fix round 1)
		$ip = (string) ( $_SERVER['SERVER_ADDR'] ?? '' );
		if ( $tok !== '' ) {
			[ $ac, , $ab ] = st_http( $GLOBALS['ACTIVATE'] . '?t=' . rawurlencode( $tok ), [ 'server_ip' => $ip ], null );
			// [cc 2026-09-07] la o reluare tokenul e deja folosit, deci descărcăm licența emisă atunci în loc să eșuăm
			if ( $ac !== 200 || strpos( $ab, 'LICENSE' ) === false ) { [ $ac, , $ab ] = st_http( $GLOBALS['ACTIVATE'] . '?t=' . rawurlencode( $tok ) . '&download=1', null, null ); }
			if ( $ac === 200 && strpos( $ab, 'LICENSE' ) !== false ) {
				if ( ! is_dir( __DIR__ . '/inc' ) ) { @mkdir( __DIR__ . '/inc', 0775, true ); }
				if ( @file_put_contents( __DIR__ . '/inc/license.txt', $ab ) !== false ) { $activated = true; $log[] = 'Licența a fost activată pentru ' . $ip; }
			}
		}
		if ( ! $activated && ! is_file( __DIR__ . '/inc/license.txt' ) ) { throw new Exception( 'Licența nu a putut fi activată, iar fără ea instalatorul nu poate porni. Deschide linkul „Activează licența" din emailul primit la achiziție.' ); }   // [cc 2026-09-07] fără licență instalatorul rulează în mod actualizare și lasă instalarea pe jumătate — mai bine ne oprim aici, cu un mesaj clar

		// 3) drive the transport.php wizard (the engine's proven 7-stage contract; same server → no DNS pin)
		$cookie = sys_get_temp_dir() . '/_stiw_' . bin2hex( random_bytes( 4 ) ) . '.txt';
		$base   = $GLOBALS['SELF'] . '/transport.php';
		$hG = st_wizard_hdrs( $base, false ); $hP = st_wizard_hdrs( $base, true );
		[ $c1, , $b1 ] = st_http( $base, null, $cookie, true, $hG );
		$log[] = 'Pasul 1 (pornire): ' . st_wizard_say( $c1, $b1 );
		if ( $c1 !== 200 || strpos( $b1, '1/5' ) === false ) { throw new Exception( 'Instalatorul nu a pornit (pasul 1). Asigură-te că ionCube e activ.' ); }
		[ $c2, , $b2 ] = st_http( $base, [ 'action' => '1', 'select_language' => '2' ], $cookie, true, $hP, true );
		$r2 = trim( strip_tags( $b2 ) );
		$log[] = 'Pasul 2 (limba): ' . st_wizard_say( $c2, $b2 );
		if ( $c2 !== 200 || $r2 !== '1' ) { throw new Exception( 'Selectarea limbii a eșuat (pasul 2): ' . st_wizard_say( $c2, $b2 ) ); }
		[ $c3, , $b3 ] = st_http( $base, [ 'action' => '2', 'input_database_host' => 'localhost', 'input_database_name' => $db, 'input_database_username' => $db, 'input_database_password' => $pass, 'input_user_password' => $adminPw ], $cookie, true, $hP, true );
		$r3 = trim( strip_tags( $b3 ) );
		$log[] = 'Pasul 3 (baza de date): ' . st_wizard_say( $c3, $b3 );   // [cc 2026-09-07] răspunsul instalatorului intră în jurnal: fără el, un eșec pe serverul clientului nu se poate diagnostica de la distanță
		if ( $c3 !== 200 || ( $r3 !== '1' && $r3 !== '-12' ) ) { throw new Exception( 'Conectarea la baza de date a eșuat. Verifică: numele bazei și utilizatorul trebuie să fie IDENTICE, iar parola corectă. (' . st_wizard_say( $c3, $b3 ) . ')' ); }
		if ( $r3 === '-12' ) { $adminPw = ''; $log[] = 'Aplicația era deja instalată — rămâne valabilă parola de la prima instalare'; }   // [cc 2026-09-07] parola proaspăt generată NU se aplică peste o instalare existentă, deci nu o mai afișăm
		else {
			foreach ( [ 3, 4, 5 ] as $pg ) {
				[ $cc, , $bb ] = st_http( $base . '?page=' . $pg, null, $cookie, true, $hG );
				$log[] = 'Pasul ' . ( $pg + 1 ) . ': ' . st_wizard_say( $cc, $bb );
				if ( $cc !== 200 || strpos( $bb, $pg . '/5' ) === false ) { throw new Exception( 'Instalarea s-a oprit la pagina ' . $pg . ': ' . st_wizard_say( $cc, $bb ) ); }
			}
		}
		@unlink( $cookie );
		// [cc 2026-09-07] proba finală: aplicația chiar pornește? (302 → user.login.php). Fără ea, un pas mut trece drept succes.
		[ $cb, $hb, $bb2 ] = st_http( $GLOBALS['SELF'] . '/', null, null, false );
		if ( ! ( $cb === 302 && stripos( $hb, 'user.login.php' ) !== false ) && stripos( $bb2, 'user.login.php' ) === false ) { throw new Exception( 'Aplicația nu pornește după instalare (' . st_wizard_say( $cb, $bb2 ) . ').' ); }
		$log[] = 'Aplicația a fost instalată și pornește';
		st_write_api_htaccess( $log );   // [cc 2026-09-07] reparație de flotă: fără api/.htaccess aplicația șoferilor dă 404

		// [cc 2026-09-07] raportăm instalarea, curățăm arhiva de 130 MB, apoi raportăm licența (central impune ordinea etapelor)
		st_report( $tok, 'install', true, [ 'app_url' => $GLOBALS['SELF'] . '/', 'version' => $rel['version'], 'server_ip' => $ip ] );
		foreach ( [ 'transport.zip', 'transport.php', 'transport.error.php' ] as $left ) { if ( is_file( __DIR__ . '/' . $left ) ) { @unlink( __DIR__ . '/' . $left ); } }   // [cc] the wizard cleans up on success; make sure nothing 130 MB stays public
		if ( $activated ) { $rep = st_report( $tok, 'license', true, [ 'ip' => $ip ] ); st_write_identity( (int) ( $rep['company_id'] ?? 0 ) ); }

		// [cc 2026-09-07] 4) apply the signed seed bundle (partner #1, per-instance passwords, variable resets) — Task 7
		if ( $activated ) {
			[ $sc, , $sb ] = st_http( st_central() . '/install.seed.php?t=' . rawurlencode( $tok ), null, null );
			$seed = ( $sc === 200 ) ? json_decode( (string) $sb, true ) : null;
			if ( is_array( $seed ) && ! empty( $seed['ok'] ) && hash_equals( hash_hmac( 'sha256', (string) $seed['sql'], $tok ), (string) $seed['sig'] ) ) {
				[ $ap, $fl, $fe ] = st_apply_seed( $db, $db, $pass, (string) $seed['sql'] );
				$seedOk = ( $fl === 0 ); $seedError = $fe; // [cc 2026-09-07] seedError captured for the success-screen amber note — fix round 1
				$log[] = $seedOk ? 'Datele firmei au fost pregătite' : ( 'Pregătirea datelor: ' . $fl . ' erori (' . $fe . ')' );
				st_report( $tok, 'seed', $seedOk, [ 'applied' => $ap, 'failed' => $fl, 'first_error' => $fe ] );
				if ( $seedOk && is_array( $seed['passwords'] ) ) { $passwords = $seed['passwords']; }
			} else { $seedError = 'nu am putut verifica pachetul'; $log[] = 'Pregătirea datelor a fost sărită (' . $seedError . ')'; st_report( $tok, 'seed', false, [ 'reason' => 'verify' ] ); } // [cc 2026-09-07] seedError captured here too — fix round 1
		}

		$result = [ 'ok' => true, 'log' => $log, 'adminPw' => $adminPw, 'activated' => $activated, 'has_token' => ( $tok !== '' ), 'passwords' => $passwords, 'seed_ok' => $seedOk, 'seed_error' => $seedError, 'app' => $GLOBALS['SELF'] . '/' ]; // [cc 2026-09-07] seed_ok/seed_error surfaced for the success screen; has_token drives the „licența nu s-a activat" mesaj
	} catch ( Throwable $e ) {
		$result = [ 'ok' => false, 'log' => $log, 'error' => $e->getMessage() ];
	}
}

// ════════════════════════════════════════════════════════════════════════════════════════════════════════
// Render
// ════════════════════════════════════════════════════════════════════════════════════════════════════════
[ $checks, $allOk, $blockers ] = st_requirements();
header( 'Content-Type: text/html; charset=utf-8' );
function _h( $s ) { return htmlspecialchars( (string) $s, ENT_QUOTES, 'UTF-8' ); }
?>
<!doctype html><html lang="ro"><head><meta charset="utf-8"><title>Instalare Soft-Transport</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
:root{--bg:#14110E;--card:#1A1815;--line:#36332c;--ink:#F1ECDF;--muted:#C9C2B2;--cyan:#46B5E0;--green:#7ed957;--red:#ff6b6b}
*{box-sizing:border-box}body{font:17px/1.6 system-ui,-apple-system,Segoe UI,sans-serif;background:var(--bg);color:var(--ink);margin:0;padding:24px}
.wrap{max-width:600px;margin:0 auto}h1{font-size:25px;margin:0 0 6px}.lead{color:var(--muted);margin:0 0 22px}
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:24px}
.row{display:flex;gap:10px;padding:8px 0;align-items:center}.row .i{width:22px;font-size:18px}.row .i.y{color:var(--green)}.row .i.n{color:var(--red)}.i.w{color:#e0a33a}
.lbl{font-weight:600}.muted{color:var(--muted);font-size:14px}
label{display:block;margin:14px 0 4px;font-size:14px;color:var(--muted)}input{width:100%;padding:13px 14px;font-size:16px;background:#0e0d0b;border:1px solid var(--line);border-radius:9px;color:var(--ink)}
.btn{display:block;width:100%;text-align:center;border:0;font-weight:800;font-size:19px;padding:18px;border-radius:12px;margin-top:20px;cursor:pointer;text-decoration:none;color:#06303d;background:var(--cyan)}
.btn[disabled]{opacity:.4;cursor:not-allowed}.note{background:#0e0d0b;border:1px solid var(--line);border-radius:9px;padding:14px;color:var(--muted);font-size:14px;margin-top:14px}
.banner{padding:16px 18px;border-radius:11px;font-weight:700;margin-bottom:18px}.banner.ok{background:#1f3a1a;border:1px solid #3c763d;color:#bfe9a0}.banner.no{background:#3a1c1c;border:1px solid #a94442;color:#f0b9b9}
code{background:#0e0d0b;padding:2px 6px;border-radius:4px;color:#cfe8ff}a{color:var(--cyan)}.big{font-size:21px}
</style></head><body><div class="wrap">
<h1>Instalare Soft-Transport</h1>
<p class="lead">Pe serverul tău. Un singur buton.</p>

<?php if ( $result && $result['ok'] ) { ?>
	<?php /* [cc 2026-09-07] fără licență activată nu mai spunem „Gata!" — instalarea nu e utilizabilă */ ?>
	<?php if ( $result['activated'] ) { ?>
	<div class="banner ok big">✓ Gata! Soft-Transport rulează pe serverul tău.</div>
	<?php } else { ?>
	<div class="banner big" style="background:#3a2e1c;border:1px solid #a9794d;color:#f0d9b9">⚠ Aplicația s-a instalat, dar <b>licența NU a fost activată</b>. Fără licență aplicația nu pornește — vezi mai jos ce ai de făcut.</div>
	<?php } ?>
	<div class="card">
		<?php /* [cc 2026-09-07] administrator@st.ro e cont de NIVEL 1: aplicația îl trimite înapoi la login (sys/inc.user.0.auth.php,
		       regula lui David din 16.06.2026 — administratorii nu au meniu în aplicație). Butonul lui duce la PANOUL DE ADMINISTRARE;
		       conturile firmei de mai jos sunt cele pentru aplicația propriu-zisă. */ ?>
		<p>Intră în panoul de administrare cu contul de mai jos:</p>
		<a class="btn" href="<?php echo _h( $result['app'] ); ?>admin/">Deschide panoul de administrare →</a>
		<a class="btn" style="background:transparent;border:2px solid var(--cyan);color:var(--cyan)" href="<?php echo _h( $result['app'] ); ?>">Deschide aplicația →</a>
		<?php /* [cc 2026-09-07] fallback ternary: administrator@st.ro's real password is the seed's reset value once the seed applies (buildAccountPasswordResetSql resets it too) — fix round 1 */ ?>
		<?php $pwShown = ! empty( $result['passwords']['admin'] ) ? (string) $result['passwords']['admin'] : (string) $result['adminPw']; ?>
		<div class="note">Utilizator: <code>administrator@st.ro</code> · Parolă: <?php echo $pwShown !== '' ? '<code>' . _h( $pwShown ) . '</code> (notează-o!)' : '<b>cea de la prima instalare</b> — aplicația era deja instalată, nu am schimbat-o'; ?>.<br>
		Licență: <?php echo $result['activated'] ? '<b style="color:#9fd86f">activată ✓</b>' : '<b style="color:#f0b9b9">neactivată</b>'; ?></div>
		<?php if ( ! $result['activated'] ) { ?>
		<div class="note" style="background:#3a2e1c;border-color:#a9794d;color:#f0d9b9"><b>Pasul următor:</b> <?php echo $result['has_token'] ? 'nu am putut activa licența automat. Deschide linkul „Activează licența" din emailul primit la achiziție, descarcă <code>license.txt</code> și pune-l în folderul <code>inc/</code> al instalării.' : 'instalatorul nu a primit niciun cod de activare, deci licența nu a fost cerută. Deschide linkul „Activează licența" din emailul primit la achiziție, descarcă <code>license.txt</code> și pune-l în folderul <code>inc/</code> al instalării.'; ?> Nu găsești emailul? Scrie-ne la contact@soft-transport.ro.</div>
		<?php } ?>
		<?php /* [cc 2026-09-07] Conturile firmei — per-role standard-account passwords from the seed bundle — Task 7 */ ?>
		<?php if ( ! empty( $result['passwords'] ) ) { echo '<div class="note"><b>Conturile firmei</b> — acestea intră în <b>aplicație</b> (parola de mai jos e pentru toate conturile standard, notează-o):<br>'; foreach ( $result['passwords'] as $acc => $pw ) { echo '<code>' . _h( $acc ) . '</code> · <code>' . _h( $pw ) . '</code><br>'; } echo '</div>'; } ?>
		<?php /* [cc 2026-09-07] amber warning — seed didn't apply; the transaction in st_apply_seed() guarantees the admin password above stays valid — fix round 1 */ ?>
		<?php if ( $result['activated'] && empty( $result['seed_ok'] ) ) { ?>
		<div class="note" style="background:#3a2e1c;border-color:#a9794d;color:#f0d9b9">Pregătirea datelor firmei nu s-a putut face automat (<?php echo _h( $result['seed_error'] ); ?>). Parola de administrator de mai sus rămâne valabilă. Scrie-ne la contact@soft-transport.ro și o facem noi.</div>
		<?php } ?>
		<?php /* [cc 2026-09-07] jurnalul se vede si la reusita: fara el o instalare „reusita" dar mutilata nu se poate diagnostica de la distanta */ ?>
		<?php if ( $result['log'] ) { echo '<details class="note"><summary style="cursor:pointer">Detalii tehnice ale instalarii</summary><div class="muted" style="margin-top:6px">' . _h( implode( ' · ', $result['log'] ) ) . '</div></details>'; } ?>
	</div>
<?php } elseif ( $result && ! $result['ok'] ) { ?>
	<div class="banner no">Instalarea automată s-a oprit: <?php echo _h( $result['error'] ); ?></div>
	<div class="card">
		<?php if ( $result['log'] ) { echo '<p class="muted">Ce s-a făcut: ' . _h( implode( ' · ', $result['log'] ) ) . '</p>'; } ?>
		<p>Poți rezolva ce lipsește și reîncerca, sau folosi instalarea pas cu pas:</p>
		<a class="btn" href="?<?php echo $token ? 'token=' . _h( $token ) : ''; ?>">Reîncearcă</a>
		<a class="btn" style="background:transparent;border:2px solid var(--cyan);color:var(--cyan)" href="<?php echo _h( $MANUAL ); ?>">Instalare manuală pas cu pas →</a>
	</div>
<?php } else { ?>
	<div class="banner <?php echo $allOk ? 'ok' : 'no'; ?>"><?php echo $allOk ? '✓ Serverul tău este pregătit.' : '✗ Mai lipsește: ' . _h( implode( ', ', $blockers ) ); ?></div>
	<div class="card">
		<?php /* [cc 2026-09-07] un rând care NU blochează instalarea se arată galben cu „!", exact ca în st-check.php — un ✗ roșu sub bannerul verde „serverul e pregătit" se citea ca defect */ ?>
		<?php foreach ( $checks as $c ) { $cls = $c[1] ? 'y' : ( $c[4] ? 'n' : 'w' ); $ic = $c[1] ? '✓' : ( $c[4] ? '✗' : '!' ); echo '<div class="row"><div class="i ' . $cls . '">' . $ic . '</div><div><span class="lbl">' . _h( $c[0] ) . '</span>' . ( ! $c[1] ? ' <span class="muted">— ' . _h( $c[2] ) . '</span>' : '' ) . '</div></div>'; } ?>
		<?php if ( $allOk ) { ?>
		<form method="post">
			<input type="hidden" name="op" value="install">
			<label>Numele bazei de date <b>și</b> al utilizatorului (trebuie să fie <b>identice</b> — le creezi în panoul hostingului)</label>
			<input type="text" name="db" placeholder="ex. softtransport" required>
			<label>Parola bazei de date</label>
			<input type="text" name="dbpass" placeholder="parola pe care ai pus-o la baza de date" required>
			<label>Linkul de activare din emailul primit la achiziție</label>
			<input type="text" name="actlink" value="<?php echo $token ? _h( $ACTIVATE . '?t=' . $token ) : ''; ?>" placeholder="https://soft-transport.com/activate.php?t=...">
			<button class="btn" type="submit">⚡ Instalează automat</button>
			<div class="note">Apasă o dată. Descărcăm aplicația, o instalăm și activăm licența — durează 1–2 minute. Nu închide pagina.</div>
		</form>
		<?php } else { ?>
		<div class="note">Cere furnizorului tău de hosting să activeze ce e roșu mai sus (mai ales <b>ionCube Loader</b> — e gratuit, o bifă). Apoi reîncarcă pagina.</div>
		<a class="btn" href="?<?php echo $token ? 'token=' . _h( $token ) : ''; ?>">Am activat — verifică din nou</a>
		<?php } ?>
	</div>
<?php } ?>
</div></body></html>
