Flatlogic Bot 06a425c069 dimands
2026-06-15 17:11:12 +00:00

3019 lines
240 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Plugin Name: Unggul MVP
* Description: Unggul AI-native learning MVP: premium landing styles, profile/course UI helpers, and AI Career Coach submissions.
* Version: 0.2.0
*/
if (!defined('ABSPATH')) { exit; }
define('MATTHEW_LMS_MVP_VERSION', '0.2.0');
add_action('init', function () {
register_post_type('mlms_coach_request', [
'labels' => [
'name' => 'Career Coach Requests',
'singular_name' => 'Career Coach Request',
'menu_name' => 'Career Coach Leads',
'add_new_item' => 'Add Career Coach Request',
'edit_item' => 'Review Career Coach Request',
],
'public' => false,
'show_ui' => true,
'show_in_menu' => true,
'menu_icon' => 'dashicons-welcome-learn-more',
'supports' => ['title', 'editor', 'custom-fields'],
'capability_type' => 'post',
]);
register_post_type('mlms_onboard', [
'labels' => [
'name' => 'Onboarding Profiles',
'singular_name' => 'Onboarding Profile',
'menu_name' => 'Unggul Onboarding',
'add_new_item' => 'Add Onboarding Profile',
'edit_item' => 'Review Onboarding Profile',
],
'public' => false,
'show_ui' => true,
'show_in_menu' => true,
'menu_icon' => 'dashicons-id-alt',
'supports' => ['title', 'editor', 'custom-fields'],
'capability_type' => 'post',
]);
});
add_action('wp_enqueue_scripts', function () {
wp_register_style('matthew-lms-mvp', false, [], MATTHEW_LMS_MVP_VERSION);
wp_enqueue_style('matthew-lms-mvp');
wp_add_inline_style('matthew-lms-mvp', matthew_lms_mvp_css());
wp_register_script('matthew-lms-mvp-onboarding', false, [], MATTHEW_LMS_MVP_VERSION, true);
wp_enqueue_script('matthew-lms-mvp-onboarding');
wp_add_inline_script('matthew-lms-mvp-onboarding', matthew_lms_mvp_js());
});
add_action('wp_head', function () {
$_SERVER['PROJECT_DESCRIPTION'] = 'Unggul helps non-technical professionals become fluent in AI-powered work through guided lessons, practical workflows, and shareable credentials.';
}, 0);
add_action('template_redirect', 'matthew_lms_mvp_maybe_handle_login_submission');
add_action('template_redirect', 'matthew_lms_mvp_maybe_handle_google_oauth_callback');
add_action('template_redirect', 'matthew_lms_mvp_maybe_handle_signup_onboarding_submission');
add_action('template_redirect', 'matthew_lms_mvp_maybe_handle_profile_source_submission');
add_action('template_redirect', 'matthew_lms_mvp_require_auth_for_member_routes', 4);
add_action('template_redirect', 'matthew_lms_mvp_maybe_render_course_route', 5);
add_action('wp_ajax_mlms_ai_tutor', 'matthew_lms_mvp_handle_ai_tutor_ajax');
add_action('wp_ajax_nopriv_mlms_ai_tutor', 'matthew_lms_mvp_handle_ai_tutor_ajax');
add_action('wp_ajax_mlms_course_progress', 'matthew_lms_mvp_handle_course_progress_ajax');
add_action('wp_ajax_nopriv_mlms_course_progress', 'matthew_lms_mvp_handle_course_progress_ajax');
add_filter('body_class', function ($classes) {
if (is_page(['home', 'career-coach', 'platform', 'sign-up', 'log-in'])) {
$classes[] = 'mlms-wax-page';
}
return $classes;
});
add_filter('render_block', function ($block_content, $block) {
if (($block['blockName'] ?? '') === 'core/template-part') {
$slug = $block['attrs']['slug'] ?? '';
if ($slug === 'header') {
return matthew_lms_mvp_render_header();
}
if ($slug === 'footer') {
return matthew_lms_mvp_render_footer();
}
}
return $block_content;
}, 10, 2);
function matthew_lms_mvp_public_url(string $path = ''): string {
$host = sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'] ?? ''));
if ($host && !in_array($host, ['localhost', '127.0.0.1'], true)) {
$scheme = is_ssl() ? 'https' : 'http';
return $scheme . '://' . $host . '/' . ltrim($path, '/');
}
return home_url('/' . ltrim($path, '/'));
}
function matthew_lms_mvp_member_access_url(string $path = 'platform/', string $auth_path = 'sign-up/'): string {
$destination = matthew_lms_mvp_public_url($path);
if (is_user_logged_in()) {
return $destination;
}
return add_query_arg('redirect_to', rawurlencode($destination), matthew_lms_mvp_public_url($auth_path));
}
function matthew_lms_mvp_current_public_url(): string {
$request_uri = (string) wp_unslash($_SERVER['REQUEST_URI'] ?? '/');
$request_uri = '/' . ltrim($request_uri, '/');
return rtrim(matthew_lms_mvp_public_url(), '/') . $request_uri;
}
function matthew_lms_mvp_is_member_route_path(string $path): bool {
$path = trim($path, '/');
return in_array($path, ['platform', 'platform/course', 'platform/lesson', 'platform/quiz'], true);
}
function matthew_lms_mvp_require_auth_for_member_routes(): void {
if (is_user_logged_in()) {
return;
}
$path = trim((string) wp_parse_url(wp_unslash($_SERVER['REQUEST_URI'] ?? ''), PHP_URL_PATH), '/');
if (!matthew_lms_mvp_is_member_route_path($path)) {
return;
}
$redirect_to = matthew_lms_mvp_current_public_url();
$auth_url = add_query_arg([
'redirect_to' => rawurlencode($redirect_to),
'mlms_gate' => 'member',
], matthew_lms_mvp_public_url('sign-up/'));
wp_redirect($auth_url . '#mlms-signup-flow');
exit;
}
function matthew_lms_mvp_render_header(): string {
$profile_url = matthew_lms_mvp_public_url('platform/');
$lessons_url = matthew_lms_mvp_public_url('platform/course/');
$public_links = [
'Home' => matthew_lms_mvp_public_url(),
'Unggul Coach' => matthew_lms_mvp_public_url('career-coach/'),
];
$access_links = [
'Profile' => $profile_url,
'Lessons' => $lessons_url,
];
$links = array_merge($public_links, $access_links);
$login_url = add_query_arg('redirect_to', rawurlencode($profile_url), matthew_lms_mvp_public_url('log-in/'));
$register_url = add_query_arg('redirect_to', rawurlencode($profile_url), matthew_lms_mvp_public_url('sign-up/'));
$logout_url = wp_logout_url(matthew_lms_mvp_public_url());
ob_start();
?>
<header class="mlms-site-header" role="banner">
<div class="mlms-shell mlms-site-header-inner">
<a class="mlms-header-brand" href="<?php echo esc_url(matthew_lms_mvp_public_url()); ?>" aria-label="Unggul home">Unggul</a>
<nav class="mlms-header-nav" aria-label="Unggul main navigation">
<?php foreach ($links as $label => $url): ?>
<a href="<?php echo esc_url($url); ?>"><?php echo esc_html($label); ?></a>
<?php endforeach; ?>
</nav>
<div class="mlms-header-actions">
<?php if (is_user_logged_in()): ?>
<a class="mlms-btn mlms-btn-primary mlms-header-profile" href="<?php echo esc_url($profile_url); ?>">Profile</a>
<a class="mlms-header-logout-icon" href="<?php echo esc_url($logout_url); ?>" aria-label="Log out" title="Log out">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M10 4a1 1 0 0 1 1 1v2H8v10h3v2a1 1 0 0 1-1 1H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5Zm6.7 4.3 3.99 3.99a1 1 0 0 1 0 1.42l-3.99 3.99-1.4-1.42L17.58 14H10v-2h7.58l-2.28-2.28 1.4-1.42Z"/></svg>
</a>
<?php else: ?>
<a class="mlms-btn mlms-btn-secondary mlms-header-login" href="<?php echo esc_url($login_url); ?>">Login</a>
<a class="mlms-btn mlms-btn-primary mlms-header-signup" href="<?php echo esc_url($register_url); ?>">Register</a>
<?php endif; ?>
</div>
</div>
</header>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_render_footer(): string {
$links = [
'Career Coach' => matthew_lms_mvp_public_url('career-coach/'),
];
if (is_user_logged_in()) {
$links['Profile'] = matthew_lms_mvp_public_url('platform/');
$links['Dashboard'] = matthew_lms_mvp_course_url('course');
$links['Log Out'] = wp_logout_url(matthew_lms_mvp_public_url());
} else {
$links['Log In'] = matthew_lms_mvp_public_url('log-in/');
$links['Sign Up'] = matthew_lms_mvp_public_url('sign-up/');
}
ob_start();
?>
<footer class="mlms-site-footer" role="contentinfo">
<div class="mlms-shell mlms-site-footer-inner">
<div>
<a class="mlms-footer-brand" href="<?php echo esc_url(matthew_lms_mvp_public_url()); ?>">Unggul</a>
<p>AI-native work training for modern teams.</p>
</div>
<nav class="mlms-footer-links" aria-label="Unggul footer navigation">
<?php foreach ($links as $label => $url): ?>
<a href="<?php echo esc_url($url); ?>"><?php echo esc_html($label); ?></a>
<?php endforeach; ?>
</nav>
</div>
</footer>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_css(): string {
return <<<'CSS'
:root{
--mlms-ink:#0A2540;
--mlms-navy:#0A2540;
--mlms-deep:#07182D;
--mlms-muted:#425466;
--mlms-soft:#697386;
--mlms-bg:#F6F9FC;
--mlms-surface:#FFFFFF;
--mlms-line:#E6ECF2;
--mlms-wax:#D7D0FF;
--mlms-amber:#EEEAFE;
--mlms-violet:#635BFF;
--mlms-primary:#635BFF;
--mlms-primary-dark:#5548E8;
--mlms-primary-soft:#F1EFFF;
--mlms-cyan:#A7B6FF;
--mlms-pink:#B9A8FF;
--mlms-green:#21C58E;
--mlms-radius:24px;
--mlms-shadow:0 30px 80px rgba(50,50,93,.14),0 16px 36px rgba(0,0,0,.08);
--mlms-shadow-soft:0 18px 45px rgba(50,50,93,.10),0 8px 18px rgba(0,0,0,.06);
}
html{scroll-behavior:smooth}
body{
background:
radial-gradient(circle at 74% -10%,rgba(99,91,255,.13),transparent 32%),
linear-gradient(180deg,#fff 0%,var(--mlms-bg) 48%,#fff 100%);
color:var(--mlms-ink);
font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
}
.wp-site-blocks{overflow:hidden;background:transparent}
.wp-site-blocks>.wp-block-template-part:first-child{position:relative;z-index:20}
.wp-block-site-title a,.wp-block-navigation a{color:var(--mlms-ink);text-decoration:none!important}
.wp-block-site-title a{font-weight:900;letter-spacing:-.05em}
.wp-block-navigation a{font-weight:760;color:#31445B}
.mlms-site-header{position:sticky;top:0;z-index:50;background:rgba(255,255,255,.82);border-bottom:1px solid rgba(10,37,64,.08);backdrop-filter:blur(18px)}
.mlms-site-header-inner{min-height:74px;display:flex;align-items:center;justify-content:space-between;gap:24px}
.mlms-header-brand{color:var(--mlms-ink);font-weight:950;font-size:26px;letter-spacing:-.06em;text-decoration:none!important;line-height:1}
.mlms-header-nav{display:flex;align-items:center;justify-content:center;gap:26px;flex:1}
.mlms-header-nav a,.mlms-header-login{color:#31445B;text-decoration:none!important;font-weight:800;font-size:15px;line-height:1}
.mlms-header-nav a:hover,.mlms-header-login:hover{color:var(--mlms-primary)}
.mlms-header-actions{display:flex;align-items:center;justify-content:flex-end;gap:12px}
.mlms-header-login{padding:11px 14px;border-radius:999px;background:#fff;border:1px solid rgba(10,37,64,.10);box-shadow:0 6px 16px rgba(50,50,93,.06)}
.mlms-header-signup,.mlms-header-profile{min-height:40px;padding:11px 16px;font-size:15px;box-shadow:0 10px 24px rgba(99,91,255,.20)}
.mlms-header-logout-icon{width:42px;height:42px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;background:#fff;color:#31445B;border:1px solid rgba(10,37,64,.10);box-shadow:0 8px 20px rgba(50,50,93,.08);text-decoration:none!important;transition:transform .18s ease,box-shadow .18s ease,color .18s ease,background .18s ease}
.mlms-header-logout-icon:hover{transform:translateY(-2px);color:var(--mlms-primary);box-shadow:0 12px 26px rgba(99,91,255,.16)}
.mlms-header-logout-icon svg{width:20px;height:20px;display:block;fill:currentColor}
body.page-id-47 .wp-block-post-title,body.page-id-48 .wp-block-post-title,body.page-id-49 .wp-block-post-title,body.page-id-50 .wp-block-post-title,.mlms-wax-page .wp-block-post-title{display:none!important}
.entry-content{margin-top:0}.entry-content a{text-decoration:none}
.entry-content>.mlms-page,.wp-block-post-content>.mlms-page{max-width:none!important;width:100%!important;margin-left:0!important;margin-right:0!important}
.mlms-page{font-size:18px;line-height:1.65;position:relative;color:var(--mlms-ink)}
.mlms-page:before{
content:"";position:absolute;z-index:0;left:-12vw;right:-12vw;top:42px;height:420px;
background:linear-gradient(110deg,rgba(99,91,255,.72),rgba(236,233,255,.9));
transform:skewY(-7deg);transform-origin:0 0;border-radius:0 0 52px 52px;opacity:.11;pointer-events:none;
}
.mlms-page *{box-sizing:border-box}.mlms-page>section{position:relative;z-index:1}
.mlms-shell{max-width:1120px;margin:0 auto;padding:0 24px}
.mlms-pill{
display:inline-flex;gap:8px;align-items:center;border:1px solid rgba(99,91,255,.18);
background:rgba(255,255,255,.74);backdrop-filter:blur(16px);color:#4F46E5;
padding:8px 13px;border-radius:999px;font-size:13px;font-weight:820;letter-spacing:.01em;
box-shadow:0 8px 22px rgba(50,50,93,.08);
}
.mlms-hero{position:relative;padding:104px 0 70px}
.mlms-hero-grid{display:grid;grid-template-columns:minmax(0,1.02fr) minmax(360px,.98fr);gap:52px;align-items:center}
.mlms-eyebrow{margin-bottom:22px}.mlms-h1{font-size:clamp(54px,7.8vw,92px);line-height:.94;letter-spacing:-.075em;margin:0 0 24px;color:var(--mlms-ink);font-weight:900;max-width:780px}
.mlms-gradient-text{background:linear-gradient(100deg,#3D2A8F 0%,var(--mlms-primary) 48%,#897DFF 100%);-webkit-background-clip:text;background-clip:text;color:transparent}
.mlms-lede{max-width:680px;font-size:clamp(20px,2.2vw,24px);line-height:1.48;color:var(--mlms-muted);margin:0 0 30px;font-weight:520}
.mlms-actions{display:flex;flex-wrap:wrap;gap:12px;margin:26px 0}.mlms-btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:10px;border-radius:999px;padding:12px 18px;font-weight:850;border:1px solid transparent;transition:transform .18s ease,box-shadow .18s ease,background .18s ease;color:inherit;min-height:46px;line-height:1.15;text-decoration:none!important}.mlms-btn:hover{transform:translateY(-2px)}
.mlms-btn-primary{background:var(--mlms-primary);color:#fff;box-shadow:0 14px 34px rgba(99,91,255,.24)}
.mlms-btn-primary:hover{background:var(--mlms-primary-dark)}
.mlms-btn-secondary{background:#fff;color:#33425F;border-color:rgba(10,37,64,.10);box-shadow:0 10px 26px rgba(50,50,93,.08)}
.mlms-btn-wax{background:var(--mlms-primary);color:#fff;box-shadow:0 14px 34px rgba(99,91,255,.24)}
.mlms-btn-wax:hover{background:var(--mlms-primary-dark)}
.mlms-site-header .mlms-header-signup{min-height:40px;padding:11px 16px;font-size:15px;box-shadow:0 10px 24px rgba(99,91,255,.20)}
.wp-element-button,.wp-block-button__link{background:var(--mlms-primary);color:#fff;border-radius:999px;box-shadow:0 12px 28px rgba(99,91,255,.22)}
.mlms-trust{display:flex;flex-wrap:wrap;gap:16px;color:var(--mlms-muted);font-size:14px;margin-top:22px}.mlms-trust span{display:inline-flex;align-items:center;gap:8px;font-weight:650}.mlms-trust span:before{content:"";width:8px;height:8px;border-radius:999px;background:var(--mlms-primary)}
.mlms-ai-card{position:relative;min-height:560px;border:1px solid rgba(255,255,255,.88);background:rgba(255,255,255,.78);backdrop-filter:blur(18px);border-radius:30px;box-shadow:var(--mlms-shadow);padding:20px;overflow:hidden}.mlms-ai-card:before{content:"";position:absolute;inset:-90px -70px auto auto;width:320px;height:320px;border-radius:50%;background:radial-gradient(circle,rgba(99,91,255,.18),transparent 65%);filter:blur(10px)}.mlms-ai-card:after{content:"";position:absolute;left:-100px;bottom:-95px;width:330px;height:330px;border-radius:50%;background:radial-gradient(circle,rgba(10,37,64,.08),transparent 68%)}
.mlms-card-top{position:relative;z-index:1;display:flex;justify-content:space-between;align-items:center;margin-bottom:18px}.mlms-signal{display:flex;gap:7px;align-items:end}.mlms-signal i{display:block;width:8px;border-radius:99px;background:linear-gradient(#A7A1FF,var(--mlms-primary));animation:mlmsPulse 1.9s ease-in-out infinite}.mlms-signal i:nth-child(1){height:16px}.mlms-signal i:nth-child(2){height:27px;animation-delay:.16s}.mlms-signal i:nth-child(3){height:39px;animation-delay:.32s}
.mlms-dashboard{position:relative;z-index:1;background:linear-gradient(160deg,#0A2540 0%,#132747 58%,#1B1744 100%);color:#fff;border-radius:24px;padding:25px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.08),0 18px 45px rgba(10,37,64,.22)}.mlms-dashboard:before{content:"";position:absolute;top:-1px;left:22px;right:22px;height:3px;border-radius:0 0 99px 99px;background:var(--mlms-primary)}.mlms-dashboard h3{font-size:19px;margin:0 0 4px;color:#fff}.mlms-dashboard p{margin:0;color:#AFC0D4}
.mlms-orbs{display:flex;gap:14px;margin:28px 0}.mlms-orb{width:62px;height:58px;border:0;border-radius:0;background:rgba(255,255,255,.22);position:relative;transform:rotate(-6deg);clip-path:polygon(18% 0,82% 0,100% 34%,50% 100%,0 34%);box-shadow:0 8px 24px rgba(0,0,0,.18),inset 0 0 0 1px rgba(255,255,255,.18)}.mlms-orb:before{content:"";position:absolute;inset:2px;clip-path:inherit;background:linear-gradient(180deg,rgba(255,255,255,.34) 0 33%,rgba(255,255,255,.08) 34% 100%);opacity:1}.mlms-orb:after{content:"";position:absolute;inset:2px;clip-path:inherit;background:linear-gradient(180deg,transparent 0 31%,rgba(255,255,255,.42) 32%,transparent 34% 100%),linear-gradient(118deg,transparent 0 38%,rgba(255,255,255,.34) 39%,transparent 41% 100%),linear-gradient(62deg,transparent 0 38%,rgba(255,255,255,.22) 39%,transparent 41% 100%),linear-gradient(28deg,transparent 0 49%,rgba(255,255,255,.18) 50%,transparent 52% 100%);mix-blend-mode:screen;pointer-events:none}.mlms-orb.earned,.mlms-orb.current{background:linear-gradient(135deg,#ffffff 0%,#efeaff 32%,#bab3ff 66%,#ffffff 100%);box-shadow:0 0 26px rgba(99,91,255,.34),0 10px 28px rgba(0,0,0,.16)}.mlms-orb.earned:before,.mlms-orb.current:before{background:linear-gradient(180deg,#fff 0 32%,#ddd8ff 33% 62%,#b8b1ff 100%)}.mlms-orb.current{box-shadow:0 0 30px rgba(99,91,255,.4),0 10px 30px rgba(0,0,0,.18);animation:mlmsGlow 2.4s ease-in-out infinite}
.mlms-phase{display:flex;justify-content:space-between;gap:16px;align-items:center;padding:15px 16px;border-radius:17px;background:rgba(255,255,255,.075);border:1px solid rgba(255,255,255,.09);margin-top:12px}.mlms-phase strong{display:block}.mlms-phase small{color:#AFC0D4}.mlms-lock{font-size:12px;color:#C9C4FF;font-weight:900;letter-spacing:.04em}
.mlms-coach-preview{position:relative;z-index:2;margin:-30px 18px 0 auto;max-width:370px;background:#fff;border:1px solid var(--mlms-line);border-radius:22px;padding:19px;box-shadow:var(--mlms-shadow-soft);color:#31445B}.mlms-coach-preview b{color:var(--mlms-violet)}
.mlms-section{padding:64px 0}.mlms-section-head{display:flex;justify-content:space-between;align-items:end;gap:24px;margin-bottom:27px}.mlms-kicker{color:var(--mlms-violet);font-weight:900;text-transform:uppercase;font-size:12px;letter-spacing:.14em;margin:0 0 10px}.mlms-h2{font-size:clamp(34px,4.5vw,58px);line-height:1.02;letter-spacing:-.052em;margin:0;color:var(--mlms-ink);font-weight:900}.mlms-section-copy{max-width:560px;color:var(--mlms-muted);margin:12px 0 0;font-weight:520}.mlms-grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.mlms-feature,.mlms-panel,.mlms-testimonial,.mlms-brand-card,.mlms-form-card,.mlms-profile-card,.mlms-course-card{background:rgba(255,255,255,.88);border:1px solid rgba(10,37,64,.09);border-radius:var(--mlms-radius);box-shadow:var(--mlms-shadow-soft);padding:26px}.mlms-feature,.mlms-testimonial,.mlms-panel,.mlms-course-card{position:relative;overflow:hidden}.mlms-feature:before,.mlms-testimonial:before,.mlms-course-card:before{content:"";position:absolute;left:0;right:0;top:0;height:3px;background:var(--mlms-primary);opacity:.55}.mlms-feature-number{width:42px;height:42px;border-radius:14px;background:var(--mlms-primary-soft);color:var(--mlms-primary);display:flex;align-items:center;justify-content:center;font-weight:950;margin-bottom:38px}.mlms-feature h3,.mlms-panel h3,.mlms-testimonial h3,.mlms-brand-card h3{margin:0 0 10px;font-size:22px;color:var(--mlms-ink);letter-spacing:-.026em}.mlms-feature p,.mlms-panel p,.mlms-testimonial p,.mlms-brand-card p{color:var(--mlms-muted);margin:0}
.mlms-coach-band{background:linear-gradient(145deg,#0A2540 0%,#172B4D 58%,#211B54 100%);color:#fff;border-radius:30px;padding:38px;display:grid;grid-template-columns:1fr 380px;gap:28px;align-items:center;box-shadow:var(--mlms-shadow);position:relative;overflow:hidden}.mlms-coach-band:before{content:"";position:absolute;left:-120px;top:-160px;width:440px;height:320px;background:linear-gradient(120deg,rgba(99,91,255,.42),rgba(255,255,255,.12));filter:blur(20px);transform:rotate(-12deg)}.mlms-coach-band:after{content:"";position:absolute;right:-90px;top:-120px;width:320px;height:320px;border-radius:50%;background:radial-gradient(circle,rgba(99,91,255,.20),transparent 68%)}.mlms-coach-band>*{position:relative;z-index:1}.mlms-coach-band h2{color:#fff}.mlms-coach-band p{color:#CFD9E7}.mlms-coach-band .mlms-kicker{color:#C9C4FF}.mlms-mini-output{position:relative;z-index:1;background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.17);border-radius:22px;padding:20px;backdrop-filter:blur(12px)}.mlms-mini-output div{padding:12px 0;border-bottom:1px solid rgba(255,255,255,.13)}.mlms-mini-output div:last-child{border-bottom:0}
.mlms-brand-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}.mlms-brand-card.wax{background:linear-gradient(135deg,#FFFFFF,var(--mlms-primary-soft));border-color:#DCD8FF}.mlms-swatches{display:flex;gap:10px;margin-top:20px}.mlms-swatch{width:44px;height:44px;border-radius:14px;border:3px solid rgba(255,255,255,.55)}
.mlms-form-card{max-width:880px;margin:0 auto}.mlms-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.mlms-field{display:flex;flex-direction:column;gap:7px;margin-bottom:16px}.mlms-field label{font-weight:850;color:var(--mlms-ink);font-size:15px}.mlms-field input,.mlms-field textarea,.mlms-field select{width:100%;border:1px solid #D9E2EC;border-radius:14px;padding:14px 16px;background:#fff;color:var(--mlms-ink);font:inherit;box-shadow:0 1px 1px rgba(0,0,0,.02)}.mlms-field input:focus,.mlms-field textarea:focus,.mlms-field select:focus{outline:0;border-color:var(--mlms-violet);box-shadow:0 0 0 4px rgba(99,91,255,.12)}.mlms-field textarea{min-height:130px}.mlms-help{font-size:14px;color:var(--mlms-soft);margin:0 0 18px}.mlms-notice{border-radius:20px;padding:18px 20px;margin:0 0 22px;border:1px solid rgba(33,197,142,.26);background:#ECFBF6;color:#0B5D45}.mlms-ai-output{white-space:pre-wrap;background:linear-gradient(145deg,#0A2540,#172B4D);color:#EAF3FF;border-radius:22px;padding:24px;line-height:1.55;margin-top:20px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.08)}
.mlms-steps{display:flex;gap:8px;margin-bottom:24px}.mlms-step{height:8px;flex:1;border-radius:999px;background:#DDE6F0}.mlms-step.active{background:var(--mlms-primary)}.mlms-role-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.mlms-role{border:1px solid #DDE6F0;border-radius:18px;padding:18px;background:#fff;font-weight:850;box-shadow:0 8px 20px rgba(50,50,93,.05)}.mlms-profile-hero{display:grid;grid-template-columns:1fr 1.2fr;gap:20px;align-items:stretch}.mlms-avatar{width:88px;height:88px;border-radius:26px;background:linear-gradient(135deg,#3D2A8F,var(--mlms-primary));box-shadow:0 14px 34px rgba(99,91,255,.22);margin-bottom:18px;transform:rotate(-4deg)}.mlms-nodes{display:flex;gap:14px;flex-wrap:wrap;margin:20px 0}.mlms-node{width:70px;height:70px;border-radius:18px;background:#F2F6FA;border:1px solid #DDE6F0;display:grid;place-items:center;color:#6F7F90;font-weight:950;transform:rotate(-5deg)}.mlms-node.earned{color:#fff;background:linear-gradient(135deg,#AFA9FF,var(--mlms-primary));box-shadow:0 0 24px rgba(99,91,255,.30)}.mlms-node.next{color:#fff;background:linear-gradient(135deg,#C9C4FF,var(--mlms-primary));box-shadow:0 0 24px rgba(99,91,255,.28)}.mlms-path{display:grid;gap:14px}.mlms-course-card{display:grid;grid-template-columns:auto 1fr auto;gap:16px;align-items:center}.mlms-course-index{width:48px;height:48px;border-radius:15px;display:grid;place-items:center;background:var(--mlms-primary-soft);color:var(--mlms-primary);font-weight:950}.mlms-course-card.locked{opacity:.75}.mlms-footer-cta{text-align:center;padding:76px 24px 96px}.mlms-sample-label{font-size:12px;text-transform:uppercase;letter-spacing:.12em;color:#6C7F91;font-weight:900}.mlms-admin-link{font-size:14px;color:#697386;margin-top:18px}.mlms-admin-link a{color:var(--mlms-violet);font-weight:800}
.mlms-onboarding-hero{padding:76px 0 64px}.mlms-login-grid{display:grid;grid-template-columns:minmax(0,.92fr) minmax(360px,480px);gap:34px;align-items:start}.mlms-login-card{position:relative;overflow:hidden}.mlms-login-card:before{content:"";position:absolute;inset:0 0 auto 0;height:4px;background:var(--mlms-primary);opacity:.9}.mlms-login-card h2{font-size:clamp(30px,3vw,42px);line-height:1.04;letter-spacing:-.045em;margin:0 0 10px;color:var(--mlms-ink)}.mlms-login-card>p{color:var(--mlms-muted);margin:0 0 22px}.mlms-login-actions{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin:2px 0 18px}.mlms-checkbox{display:inline-flex;align-items:center;gap:9px;font-weight:750;color:var(--mlms-muted);font-size:14px}.mlms-checkbox input{width:18px;height:18px;accent-color:var(--mlms-primary)}.mlms-login-link{font-size:14px;font-weight:850;color:var(--mlms-primary);text-decoration:none!important}.mlms-login-link:hover{color:var(--mlms-primary-dark)}.mlms-login-submit{width:100%;border:0;cursor:pointer}.mlms-login-footnote{margin:18px 0 0;color:var(--mlms-muted);font-size:15px;text-align:center}.mlms-login-footnote a{color:var(--mlms-primary);font-weight:850;text-decoration:none!important}.mlms-signup-grid{display:grid;grid-template-columns:minmax(0,.9fr) minmax(420px,1fr);gap:34px;align-items:start}.mlms-onboarding-copy{padding-top:20px}.mlms-onboarding-card{position:relative;overflow:hidden}.mlms-onboarding-card:before{content:"";position:absolute;inset:0 0 auto 0;height:4px;background:var(--mlms-primary);opacity:.9}.mlms-mini-list{display:grid;gap:12px;margin:28px 0 0;padding:0;list-style:none}.mlms-mini-list li{display:flex;gap:10px;align-items:flex-start;color:var(--mlms-muted);font-weight:650}.mlms-mini-list li:before{content:"";width:10px;height:10px;margin-top:9px;border-radius:99px;background:var(--mlms-primary);box-shadow:0 0 0 5px rgba(99,91,255,.10)}.mlms-onboarding-progress{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:4px 0 22px}.mlms-onboarding-progress span{height:8px;border-radius:999px;background:#DEE7F0;transition:background .2s ease,transform .2s ease}.mlms-onboarding-progress span.active{background:var(--mlms-primary);transform:scaleY(1.15)}.mlms-onboarding-step-label{font-size:13px;font-weight:900;text-transform:uppercase;letter-spacing:.12em;color:var(--mlms-violet);margin:0 0 8px}.mlms-onboarding-panel{display:none;animation:mlmsFadeUp .24s ease both}.mlms-onboarding-panel.active{display:block}.mlms-onboarding-panel h2{font-size:clamp(28px,3vw,38px);line-height:1.06;letter-spacing:-.04em;margin:0 0 10px;color:var(--mlms-ink)}.mlms-onboarding-panel p{color:var(--mlms-muted);margin:0 0 22px}.mlms-google-button{width:100%;min-height:52px;margin:4px 0 14px;border-color:rgba(10,37,64,.12);background:#fff;color:#1F2B3D;box-shadow:0 12px 28px rgba(50,50,93,.10)}.mlms-google-mark{width:26px;height:26px;border-radius:50%;display:inline-grid;place-items:center;border:1px solid #E0E6EF;color:#4285F4;font-weight:950;background:#fff}.mlms-divider{display:flex;align-items:center;gap:12px;color:#7A8798;font-size:13px;font-weight:800;margin:16px 0}.mlms-divider:before,.mlms-divider:after{content:"";height:1px;background:#E4EAF2;flex:1}.mlms-role-picker{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:20px 0 22px}.mlms-role-card{position:relative;display:flex;gap:12px;align-items:flex-start;border:1px solid #DDE6F0;border-radius:18px;background:#fff;padding:16px;cursor:pointer;box-shadow:0 8px 20px rgba(50,50,93,.05);transition:border-color .18s ease,box-shadow .18s ease,transform .18s ease,background .18s ease}.mlms-role-card:hover{transform:translateY(-2px);border-color:rgba(99,91,255,.38);box-shadow:0 14px 30px rgba(50,50,93,.10)}.mlms-role-card input{position:absolute;opacity:0;pointer-events:none}.mlms-role-icon{width:38px;height:38px;border-radius:14px;display:grid;place-items:center;background:var(--mlms-primary-soft);color:var(--mlms-primary);font-weight:950;flex:0 0 auto}.mlms-role-card strong{display:block;color:var(--mlms-ink);line-height:1.2}.mlms-role-card small{display:block;color:var(--mlms-soft);font-weight:650;margin-top:3px}.mlms-role-card.selected,.mlms-role-card:has(input:checked){border-color:var(--mlms-primary);background:#FBFAFF;box-shadow:0 16px 34px rgba(99,91,255,.16)}.mlms-role-card.selected .mlms-role-icon,.mlms-role-card:has(input:checked) .mlms-role-icon{background:var(--mlms-primary);color:#fff}.mlms-onboarding-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:16px}.mlms-onboarding-actions .mlms-btn{border:0;cursor:pointer}.mlms-onboarding-actions .mlms-btn-secondary{border:1px solid rgba(10,37,64,.10)}.mlms-inline-note{border-radius:18px;padding:14px 16px;background:#F6F4FF;border:1px solid rgba(99,91,255,.18);color:#3C367A;margin:0 0 18px;font-size:15px}.mlms-error-note{border-radius:18px;padding:14px 16px;background:#FFF5F5;border:1px solid rgba(220,38,38,.18);color:#7F1D1D;margin:0 0 18px;font-size:15px}.mlms-building-profile{display:none;margin-top:20px;border-radius:22px;background:linear-gradient(145deg,#0A2540,#172B4D);color:#fff;padding:22px;box-shadow:var(--mlms-shadow-soft)}.mlms-building-profile.active{display:block}.mlms-building-profile strong{display:block;font-size:20px;margin-bottom:4px}.mlms-building-profile p{margin:0;color:#C8D7EA}.mlms-loading-dots{display:flex;gap:8px;margin-top:18px}.mlms-loading-dots i{width:10px;height:10px;border-radius:50%;background:#C9C4FF;animation:mlmsDots 1s ease-in-out infinite}.mlms-loading-dots i:nth-child(2){animation-delay:.15s}.mlms-loading-dots i:nth-child(3){animation-delay:.3s}.mlms-onboarding-success{display:grid;grid-template-columns:minmax(0,.76fr) minmax(0,1fr);gap:24px;align-items:start}.mlms-profile-summary-card{background:linear-gradient(145deg,#0A2540,#172B4D);color:#fff;border-radius:28px;padding:28px;box-shadow:var(--mlms-shadow)}.mlms-profile-summary-card h2{color:#fff;margin:0 0 12px;font-size:34px;line-height:1.05;letter-spacing:-.04em}.mlms-profile-summary-card p{color:#D7E3F1}.mlms-profile-meta{display:grid;gap:10px;margin-top:22px}.mlms-profile-meta span{display:flex;justify-content:space-between;gap:14px;border-top:1px solid rgba(255,255,255,.12);padding-top:10px;color:#C8D7EA}.mlms-profile-meta b{color:#fff}.mlms-ai-profile{position:relative;overflow:hidden;background:linear-gradient(145deg,#fff 0%,#FBFAFF 58%,#F3F6FF 100%);border:1px solid rgba(99,91,255,.18);border-radius:28px;padding:26px;box-shadow:var(--mlms-shadow-soft);color:#26364A;line-height:1.6}.mlms-ai-profile:before{content:"";position:absolute;inset:-120px -90px auto auto;width:300px;height:300px;border-radius:50%;background:radial-gradient(circle,rgba(99,91,255,.22),rgba(99,91,255,0) 66%);pointer-events:none}.mlms-ai-profile:after{content:"";position:absolute;left:-80px;bottom:-110px;width:250px;height:250px;border-radius:50%;background:radial-gradient(circle,rgba(33,197,142,.12),rgba(33,197,142,0) 66%);pointer-events:none}.mlms-ai-profile-orb{position:absolute;top:20px;right:22px;width:132px;height:132px;border-radius:50%;border:1px dashed rgba(99,91,255,.28);background:rgba(255,255,255,.48);display:grid;place-items:center}.mlms-ai-profile-spark{width:56px;height:56px;border-radius:20px;display:grid;place-items:center;background:linear-gradient(145deg,var(--mlms-primary),#9B8CFF);color:#fff;font-size:27px;box-shadow:0 18px 34px rgba(99,91,255,.28)}.mlms-ai-profile-node{position:absolute;width:34px;height:34px;border-radius:13px;display:grid;place-items:center;background:#fff;color:var(--mlms-primary);font-weight:950;box-shadow:0 10px 22px rgba(50,50,93,.12);border:1px solid rgba(99,91,255,.12)}.mlms-ai-profile-node:nth-child(2){top:-8px;left:46px}.mlms-ai-profile-node:nth-child(3){right:-8px;bottom:34px}.mlms-ai-profile-node:nth-child(4){left:3px;bottom:12px;color:#08785B}.mlms-ai-profile-head,.mlms-ai-profile-chips,.mlms-ai-profile-copy{position:relative;z-index:1}.mlms-ai-profile-badge{display:inline-flex;align-items:center;gap:7px;border-radius:999px;background:#EFEDFF;color:var(--mlms-primary);padding:7px 10px;font-size:12px;font-weight:950;text-transform:uppercase;letter-spacing:.09em;margin-bottom:12px}.mlms-ai-profile-badge:before{content:"✦"}.mlms-ai-profile h3{max-width:calc(100% - 150px);margin:0 0 8px;color:var(--mlms-ink);font-size:clamp(26px,3vw,36px);line-height:1.04;letter-spacing:-.045em}.mlms-ai-profile-head p{max-width:calc(100% - 110px);margin:0;color:var(--mlms-muted);font-size:15px;line-height:1.48}.mlms-ai-profile-chips{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:22px 0}.mlms-ai-profile-chips span{border-radius:18px;background:#fff;border:1px solid rgba(10,37,64,.08);padding:12px 13px;color:#34445B;font-size:13px;font-weight:900;box-shadow:0 8px 18px rgba(50,50,93,.06)}.mlms-ai-profile-chips b{display:block;color:var(--mlms-primary);font-size:12px;letter-spacing:.08em;margin-bottom:2px}.mlms-ai-profile-copy{white-space:normal;background:rgba(255,255,255,.82);border:1px solid rgba(10,37,64,.08);border-radius:22px;padding:20px;color:#26364A;box-shadow:inset 0 1px 0 rgba(255,255,255,.82)}
@keyframes mlmsFadeUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes mlmsDots{0%,80%,100%{transform:scale(.78);opacity:.55}40%{transform:scale(1);opacity:1}}
@media (max-width:900px){.mlms-login-grid,.mlms-signup-grid,.mlms-onboarding-success{grid-template-columns:1fr}.mlms-onboarding-copy{padding-top:0}.mlms-role-picker{grid-template-columns:1fr}}
@media (max-width:560px){.mlms-onboarding-hero{padding:48px 0}.mlms-role-picker{grid-template-columns:1fr}.mlms-onboarding-actions{display:grid}.mlms-onboarding-actions .mlms-btn{width:100%}}
.mlms-site-footer{position:relative;z-index:5;margin-top:40px;padding:38px 0 48px;background:rgba(255,255,255,.62);border-top:1px solid rgba(10,37,64,.08);backdrop-filter:blur(16px)}.mlms-site-footer-inner{display:flex;justify-content:space-between;align-items:center;gap:24px}.mlms-footer-brand{display:inline-flex;color:var(--mlms-ink);font-weight:950;font-size:24px;letter-spacing:-.055em;text-decoration:none!important}.mlms-site-footer p{margin:6px 0 0;color:var(--mlms-muted);font-size:14px}.mlms-footer-links{display:flex;gap:18px;flex-wrap:wrap}.mlms-footer-links a{color:#33425F;font-weight:800;text-decoration:none!important}.mlms-footer-links a:hover{color:var(--mlms-violet)}
@keyframes mlmsPulse{0%,100%{transform:scaleY(.78);opacity:.76}50%{transform:scaleY(1.08);opacity:1}}@keyframes mlmsGlow{0%,100%{filter:saturate(1)}50%{filter:saturate(1.12) brightness(1.04)}}
@media (max-width:900px){.mlms-site-header-inner{min-height:auto;padding-top:16px;padding-bottom:16px;flex-wrap:wrap}.mlms-header-nav{order:3;flex-basis:100%;justify-content:flex-start;gap:18px;overflow-x:auto}.mlms-page:before{height:300px}.mlms-hero{padding:70px 0 46px}.mlms-hero-grid,.mlms-coach-band,.mlms-profile-hero{grid-template-columns:1fr}.mlms-grid-3,.mlms-brand-grid,.mlms-form-grid,.mlms-role-grid{grid-template-columns:1fr}.mlms-ai-card{min-height:auto}.mlms-section-head{display:block}.mlms-h1{font-size:48px}.mlms-course-card{grid-template-columns:1fr}.mlms-shell{padding:0 18px}.mlms-section{padding:46px 0}.mlms-coach-band{padding:28px;border-radius:24px}}
@media (max-width:560px){.mlms-site-header{position:relative}.mlms-header-actions{gap:8px}.mlms-header-login,.mlms-header-signup{padding:10px 12px;font-size:14px}.mlms-site-footer-inner{flex-direction:column;align-items:flex-start}.mlms-footer-links{gap:12px}.mlms-site-footer{padding:30px 0 36px}.mlms-actions{display:grid}.mlms-btn{width:100%}.mlms-h1{font-size:42px}.mlms-h2{font-size:34px}.mlms-ai-card{padding:14px;border-radius:24px}.mlms-dashboard{padding:20px}.mlms-orb{width:46px;height:46px}.mlms-coach-preview{margin:14px 0 0;max-width:none}.mlms-footer-cta{padding-left:0;padding-right:0}}
/* User Profile Page — shareable AI credential card, 5-node skill graph, progress, portfolio, external feed */
body.page-id-49 #wp--skip-link--target{margin-block-start:0!important}body.page-id-49 #wp--skip-link--target>.wp-block-group{padding-top:0!important}.mlms-profile-page{padding:24px 0 84px}.mlms-profile-page>.mlms-section:first-child{padding-top:38px}.mlms-profile-intro{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:24px;align-items:end;margin-bottom:28px}.mlms-profile-intro .mlms-section-copy{max-width:760px}.mlms-profile-status{display:grid;gap:10px;justify-items:end}.mlms-share-chip{display:inline-flex;align-items:center;gap:9px;border-radius:999px;border:1px solid rgba(33,197,142,.22);background:rgba(33,197,142,.08);color:#08785B;padding:9px 13px;font-weight:900;font-size:13px}.mlms-share-chip:before{content:"";width:9px;height:9px;border-radius:99px;background:var(--mlms-green);box-shadow:0 0 0 6px rgba(33,197,142,.12)}.mlms-profile-dashboard{display:grid;grid-template-columns:minmax(320px,.82fr) minmax(0,1.18fr);gap:24px;align-items:stretch}.mlms-credential-card{position:relative;overflow:hidden;background:linear-gradient(150deg,#07182D 0%,#102C50 52%,#31246E 100%);color:#fff;border-radius:32px;padding:28px;box-shadow:var(--mlms-shadow);min-height:100%}.mlms-credential-card:before{content:"";position:absolute;inset:-70px -60px auto auto;width:220px;height:220px;border-radius:50%;background:radial-gradient(circle,rgba(215,208,255,.42),transparent 64%);animation:mlmsCredentialGlow 4.8s ease-in-out infinite}.mlms-credential-top{position:relative;z-index:1;display:flex;justify-content:space-between;gap:18px;align-items:flex-start;margin-bottom:26px}.mlms-profile-photo{width:96px;height:96px;border-radius:30px;border:1px solid rgba(255,255,255,.20);background:linear-gradient(135deg,#D7D0FF,#635BFF);display:grid;place-items:center;overflow:hidden;color:#fff;font-size:32px;font-weight:950;box-shadow:0 20px 46px rgba(0,0,0,.24)}.mlms-profile-photo img{width:100%;height:100%;object-fit:cover;display:block}.mlms-credential-badge{border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.09);border-radius:18px;padding:10px 12px;color:#E7E3FF;font-size:12px;font-weight:900;text-transform:uppercase;letter-spacing:.1em}.mlms-credential-card h2{position:relative;z-index:1;color:#fff;font-size:clamp(34px,4vw,52px);line-height:.98;letter-spacing:-.06em;margin:0 0 8px}.mlms-profile-title{position:relative;z-index:1;color:#D7E3F1;font-weight:850;margin:0 0 14px}.mlms-profile-bio{position:relative;z-index:1;color:#CEDBEE;margin:0 0 22px;line-height:1.58}.mlms-credential-details{position:relative;z-index:1;display:grid;gap:10px;margin-top:24px}.mlms-credential-details span{display:flex;justify-content:space-between;gap:14px;border-top:1px solid rgba(255,255,255,.12);padding-top:11px;color:#C8D7EA}.mlms-credential-details b{color:#fff}.mlms-credential-actions{position:relative;z-index:1;display:flex;flex-wrap:wrap;gap:10px;margin-top:24px}.mlms-credential-actions .mlms-btn-secondary{background:rgba(255,255,255,.11);border-color:rgba(255,255,255,.16);color:#fff;box-shadow:none}.mlms-profile-main{display:grid;gap:24px}.mlms-profile-module{background:rgba(255,255,255,.9);border:1px solid rgba(10,37,64,.09);border-radius:28px;padding:24px;box-shadow:var(--mlms-shadow-soft);position:relative;overflow:hidden}.mlms-profile-module:before{content:"";position:absolute;top:0;left:24px;right:24px;height:3px;border-radius:0 0 99px 99px;background:linear-gradient(90deg,var(--mlms-primary),#A7B6FF);opacity:.66}.mlms-module-head{display:flex;justify-content:space-between;gap:16px;align-items:flex-start;margin-bottom:18px}.mlms-module-head h3{font-size:24px;line-height:1.08;letter-spacing:-.035em;margin:0;color:var(--mlms-ink)}.mlms-module-head p{margin:6px 0 0;color:var(--mlms-muted);font-size:15px;line-height:1.45}.mlms-skill-network{position:relative;display:grid;grid-template-columns:repeat(5,minmax(74px,1fr));gap:14px;align-items:start;padding:12px 0 4px}.mlms-skill-network:before{content:"";position:absolute;left:7%;right:7%;top:48px;height:2px;background:linear-gradient(90deg,rgba(99,91,255,.55),rgba(99,91,255,.34),rgba(10,37,64,.12));z-index:0}.mlms-skill{position:relative;z-index:1;display:grid;justify-items:center;gap:9px;text-align:center}.mlms-skill-orb{width:76px;height:76px;border-radius:999px;display:grid;place-items:center;background:conic-gradient(var(--mlms-primary) var(--node-progress,0%),#E8EEF6 0);box-shadow:0 10px 26px rgba(50,50,93,.10);padding:5px;animation:mlmsNodeBreathe 3.4s ease-in-out infinite}.mlms-skill-orb span{width:100%;height:100%;border-radius:999px;display:grid;place-items:center;background:#fff;color:var(--mlms-soft);font-weight:950;border:1px solid rgba(10,37,64,.06)}.mlms-skill.earned{--node-progress:100%}.mlms-skill.earned .mlms-skill-orb{background:conic-gradient(var(--mlms-green) 100%,#E8EEF6 0);box-shadow:0 0 0 8px rgba(33,197,142,.08),0 0 28px rgba(33,197,142,.26)}.mlms-skill.earned .mlms-skill-orb span{background:linear-gradient(135deg,#21C58E,#45D6A7);color:#fff}.mlms-skill.current{--node-progress:66%}.mlms-skill.current .mlms-skill-orb{box-shadow:0 0 0 8px rgba(99,91,255,.09),0 0 34px rgba(99,91,255,.32)}.mlms-skill.current .mlms-skill-orb span{background:linear-gradient(135deg,#AFA9FF,var(--mlms-primary));color:#fff}.mlms-skill.external{--node-progress:24%}.mlms-skill.external .mlms-skill-orb span{background:#F8FAFD;color:#4D5F75}.mlms-skill strong{font-size:13px;line-height:1.15;color:var(--mlms-ink)}.mlms-skill small{font-size:12px;line-height:1.25;color:var(--mlms-soft);font-weight:700}.mlms-skill-indicator-card{overflow:visible;z-index:3}.mlms-skill-network{gap:16px;padding:18px 0 16px}.mlms-skill-network:before{top:62px;height:4px;border-radius:99px;background:linear-gradient(90deg,rgba(33,197,142,.52),rgba(99,91,255,.55),rgba(167,182,255,.30));box-shadow:0 0 22px rgba(99,91,255,.15)}.mlms-skill{isolation:isolate;align-content:start;gap:8px;min-height:186px;padding:10px 8px 12px;border-radius:24px;background:linear-gradient(180deg,rgba(255,255,255,.96),rgba(247,250,255,.74));border:1px solid rgba(99,91,255,.10);box-shadow:0 12px 28px rgba(50,50,93,.07);transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease}.mlms-skill:hover,.mlms-skill:focus-visible{transform:translateY(-5px);border-color:rgba(99,91,255,.28);box-shadow:0 18px 38px rgba(50,50,93,.14);outline:none;z-index:5}.mlms-skill-orb{width:88px;height:88px;padding:6px;background:conic-gradient(from -40deg,var(--mlms-primary) var(--node-progress,0%),#E8EEF6 0);box-shadow:0 13px 30px rgba(50,50,93,.14)}.mlms-skill-orb span{font-size:23px;border:4px solid #fff;box-shadow:inset 0 0 0 1px rgba(10,37,64,.06)}.mlms-skill.earned{background:linear-gradient(180deg,#F2FFF9,#FFFFFF);border-color:rgba(33,197,142,.26)}.mlms-skill.current{background:linear-gradient(180deg,#F6F4FF,#FFFFFF);border-color:rgba(99,91,255,.25)}.mlms-skill.external{background:linear-gradient(180deg,#F8FAFD,#FFFFFF)}.mlms-skill strong{font-size:14px;max-width:118px}.mlms-skill small{display:inline-flex;align-items:center;justify-content:center;min-height:30px}.mlms-skill-status{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;padding:5px 9px;background:#EEF3F8;color:#607086;font-size:10px;font-style:normal;font-weight:950;text-transform:uppercase;letter-spacing:.08em}.mlms-skill.earned .mlms-skill-status{background:rgba(33,197,142,.13);color:#08785B}.mlms-skill.current .mlms-skill-status{background:rgba(99,91,255,.12);color:var(--mlms-primary)}.mlms-skill-tip{position:absolute;left:50%;top:calc(100% + 12px);width:min(250px,80vw);padding:13px 14px;border-radius:17px;background:#0A2540;color:#D9E5F3;text-align:left;box-shadow:0 18px 44px rgba(10,37,64,.28);opacity:0;pointer-events:none;transform:translate(-50%,8px) scale(.98);transition:opacity .16s ease,transform .16s ease;z-index:20}.mlms-skill-tip:after{content:"";position:absolute;left:50%;top:-7px;transform:translateX(-50%) rotate(45deg);width:14px;height:14px;background:#0A2540}.mlms-skill-tip b{display:block;color:#fff;font-size:13px;margin-bottom:5px}.mlms-skill-tip span{display:block;font-size:12px;line-height:1.42}.mlms-skill:hover .mlms-skill-tip,.mlms-skill:focus-visible .mlms-skill-tip{opacity:1;transform:translate(-50%,0) scale(1)}.mlms-skill-guide{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:4px}.mlms-skill-guide span{border-radius:18px;background:#F6F8FC;border:1px solid #E3EBF4;padding:12px 13px;color:var(--mlms-muted);font-size:13px;line-height:1.35}.mlms-skill-guide b{color:var(--mlms-ink)}.mlms-progress-summary{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}.mlms-progress-stat{border-radius:20px;background:#F7FAFE;border:1px solid #E4EBF3;padding:14px}.mlms-progress-stat b{display:block;font-size:26px;line-height:1;color:var(--mlms-ink);letter-spacing:-.04em}.mlms-progress-stat span{font-size:12px;text-transform:uppercase;letter-spacing:.09em;font-weight:900;color:var(--mlms-soft)}.mlms-phase-tracker{display:grid;gap:12px}.mlms-phase-card{display:grid;grid-template-columns:auto 1fr auto;gap:14px;align-items:center;border:1px solid #E2EAF3;background:#fff;border-radius:20px;padding:14px;box-shadow:0 8px 20px rgba(50,50,93,.05)}.mlms-phase-dot{width:44px;height:44px;border-radius:16px;display:grid;place-items:center;background:#EEF3F8;color:#718198;font-weight:950}.mlms-phase-card.completed .mlms-phase-dot{background:rgba(33,197,142,.12);color:#08785B}.mlms-phase-card.current{border-color:rgba(99,91,255,.28);background:#FBFAFF}.mlms-phase-card.current .mlms-phase-dot{background:var(--mlms-primary);color:#fff}.mlms-phase-card h4{margin:0;color:var(--mlms-ink);font-size:17px}.mlms-phase-card p{margin:3px 0 0;color:var(--mlms-muted);font-size:14px;line-height:1.35}.mlms-cert-badge{display:inline-flex;align-items:center;gap:7px;border-radius:999px;background:var(--mlms-primary-soft);color:var(--mlms-primary);padding:8px 10px;font-size:12px;font-weight:950;white-space:nowrap}.mlms-profile-wide{margin-top:24px}.mlms-portfolio-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.mlms-portfolio-card{background:#fff;border:1px solid #E2EAF3;border-radius:24px;padding:20px;box-shadow:0 10px 24px rgba(50,50,93,.07);display:grid;gap:12px}.mlms-portfolio-card h4{font-size:20px;line-height:1.08;letter-spacing:-.03em;margin:0;color:var(--mlms-ink)}.mlms-portfolio-card p{margin:0;color:var(--mlms-muted);font-size:15px;line-height:1.48}.mlms-tool-tags{display:flex;flex-wrap:wrap;gap:8px}.mlms-tool-tags span{border-radius:999px;background:#F0F4FF;color:#4F46E5;padding:6px 9px;font-size:12px;font-weight:900}.mlms-portfolio-link{font-weight:900;color:var(--mlms-primary);font-size:14px}.mlms-external-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:18px;align-items:start}.mlms-feed-list{display:grid;gap:12px}.mlms-feed-item{display:grid;grid-template-columns:auto 1fr auto;gap:12px;align-items:center;border-radius:18px;border:1px solid #E3EBF4;background:#fff;padding:13px}.mlms-source-icon{width:38px;height:38px;border-radius:14px;display:grid;place-items:center;background:#111827;color:#fff;font-weight:950;font-size:13px}.mlms-source-icon.reddit{background:#FF4500}.mlms-source-icon.ai{background:linear-gradient(135deg,#635BFF,#A7B6FF)}.mlms-feed-item strong{display:block;color:var(--mlms-ink);line-height:1.2}.mlms-feed-item small{display:block;color:var(--mlms-soft);font-weight:700}.mlms-feed-status{font-size:12px;font-weight:950;border-radius:999px;padding:7px 9px;background:#F2F6FA;color:#607086}.mlms-feed-status.connected{background:rgba(33,197,142,.11);color:#08785B}.mlms-source-form{background:#F8FAFD;border:1px dashed rgba(99,91,255,.26);border-radius:22px;padding:18px}.mlms-source-form h4{margin:0 0 8px;font-size:18px;color:var(--mlms-ink)}.mlms-source-form p{margin:0 0 14px;color:var(--mlms-muted);font-size:14px;line-height:1.45}.mlms-source-form .mlms-field{margin-bottom:12px}.mlms-source-form .mlms-btn{width:100%;border:0;cursor:pointer}.mlms-source-form select{width:100%;border:1px solid #DDE6F0;border-radius:14px;padding:12px 14px;background:#fff;color:var(--mlms-ink);font:inherit}.mlms-profile-notice{border-radius:18px;padding:14px 16px;background:#EFFCF7;border:1px solid rgba(33,197,142,.22);color:#0A7458;margin:0 0 18px;font-size:15px;font-weight:750}@keyframes mlmsCredentialGlow{0%,100%{transform:scale(.96);opacity:.8}50%{transform:scale(1.06);opacity:1}}@keyframes mlmsNodeBreathe{0%,100%{transform:translateY(0);filter:saturate(1)}50%{transform:translateY(-3px);filter:saturate(1.12)}}@media (max-width:980px){.mlms-profile-intro,.mlms-profile-dashboard,.mlms-external-grid{grid-template-columns:1fr}.mlms-profile-status{justify-items:start}.mlms-portfolio-grid{grid-template-columns:1fr}.mlms-progress-summary{grid-template-columns:1fr 1fr 1fr}.mlms-skill-network{grid-template-columns:repeat(5,minmax(96px,1fr));overflow-x:auto;padding-bottom:14px}.mlms-skill-network:before{left:48px;right:48px}.mlms-skill{min-height:190px}.mlms-skill-orb{width:72px;height:72px}.mlms-skill-guide{grid-template-columns:1fr}.mlms-skill-tip{display:none}}@media (max-width:640px){.mlms-profile-page{padding-top:46px}.mlms-profile-module,.mlms-credential-card{border-radius:24px;padding:20px}.mlms-progress-summary{grid-template-columns:1fr}.mlms-phase-card,.mlms-feed-item{grid-template-columns:1fr}.mlms-skill-network{grid-template-columns:repeat(5,104px)}.mlms-credential-top{display:grid}.mlms-credential-details span{display:grid}.mlms-module-head{display:grid}}
/* Course dashboard + in-course AI tutor */
.mlms-course-page .mlms-section{padding-top:42px}.mlms-course-dashboard{position:relative;display:grid;gap:22px;margin:26px 0 34px}.mlms-course-overview{position:relative;overflow:hidden;border-radius:32px;padding:26px;background:linear-gradient(135deg,#07182D 0%,#132747 58%,#251A56 100%);color:#fff;box-shadow:var(--mlms-shadow)}.mlms-course-overview:before{content:"";position:absolute;inset:-80px -120px auto auto;width:320px;height:320px;border-radius:999px;background:rgba(99,91,255,.32);filter:blur(8px)}.mlms-course-overview-head{position:relative;display:flex;align-items:flex-start;justify-content:space-between;gap:18px;margin-bottom:22px}.mlms-course-overview h2{margin:0;color:#fff;font-size:clamp(28px,3.5vw,44px);line-height:1.02;letter-spacing:-.05em}.mlms-course-overview p{margin:8px 0 0;color:#C8D5E4;max-width:720px;line-height:1.5}.mlms-course-streak{display:grid;place-items:center;min-width:116px;border-radius:24px;padding:16px;background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.13);text-align:center}.mlms-course-streak b{font-size:32px;line-height:1;color:#fff}.mlms-course-streak span{font-size:12px;text-transform:uppercase;letter-spacing:.12em;color:#C9C4FF;font-weight:950}.mlms-phase-path{position:relative;display:grid;grid-template-columns:1fr;gap:16px}.mlms-dashboard-phase{position:relative;border-radius:24px;padding:18px;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.13);min-height:150px;display:grid;gap:14px;align-content:start;transition:transform .18s ease,opacity .18s ease,background .18s ease}.mlms-dashboard-phase:hover{transform:translateY(-2px)}.mlms-dashboard-phase.completed{background:rgba(33,197,142,.15);border-color:rgba(33,197,142,.32);box-shadow:inset 0 0 0 1px rgba(33,197,142,.22)}.mlms-dashboard-phase.current{background:rgba(255,255,255,.16);box-shadow:inset 0 0 0 1px rgba(201,196,255,.30)}.mlms-dashboard-phase.unopened,.mlms-dashboard-phase.locked{opacity:.54;filter:grayscale(.25)}.mlms-dashboard-phase.locked:hover{transform:none}.mlms-dashboard-phase-top{display:flex;justify-content:space-between;gap:10px;align-items:center}.mlms-dashboard-phase-number{width:42px;height:42px;border-radius:15px;display:grid;place-items:center;background:#fff;color:var(--mlms-primary);font-weight:950;box-shadow:0 10px 24px rgba(0,0,0,.18)}.mlms-dashboard-phase.completed .mlms-dashboard-phase-number{background:linear-gradient(135deg,#21C58E,#45D6A7);color:#fff}.mlms-dashboard-phase.unopened .mlms-dashboard-phase-number,.mlms-dashboard-phase.locked .mlms-dashboard-phase-number{background:rgba(255,255,255,.22);color:#E6ECF2}.mlms-dashboard-lock{display:inline-flex;align-items:center;gap:6px;border-radius:999px;background:rgba(255,255,255,.12);color:#E6ECF2;padding:6px 9px;font-size:12px;font-weight:950;white-space:nowrap}.mlms-dashboard-phase.completed .mlms-dashboard-lock{background:rgba(33,197,142,.18);color:#DFFBF2}.mlms-dashboard-phase h3{margin:0;color:#fff;font-size:20px;letter-spacing:-.03em;line-height:1.1}.mlms-dashboard-phase p{font-size:14px;margin:0;color:#C8D5E4}.mlms-dashboard-meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:auto}.mlms-dashboard-meta span{border-radius:999px;background:rgba(255,255,255,.11);color:#F2F6FA;padding:6px 9px;font-size:12px;font-weight:850}.mlms-dashboard-skill-node{width:14px;height:14px;border-radius:99px;background:#94A3B8;box-shadow:0 0 0 6px rgba(148,163,184,.13);transition:background .25s ease,box-shadow .25s ease,transform .25s ease}.mlms-dashboard-skill-node.current{background:#C9C4FF;box-shadow:0 0 0 6px rgba(201,196,255,.20),0 0 22px rgba(201,196,255,.55)}.mlms-dashboard-skill-node.earned{background:var(--mlms-green);box-shadow:0 0 0 7px rgba(33,197,142,.16),0 0 24px rgba(33,197,142,.50);animation:mlmsCourseEarn .72s ease both}.mlms-lesson-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,380px);gap:22px;align-items:start}.mlms-lesson-reader,.mlms-build-card,.mlms-lesson-side-card{background:rgba(255,255,255,.92);border:1px solid rgba(10,37,64,.09);border-radius:30px;box-shadow:var(--mlms-shadow-soft)}.mlms-lesson-reader{padding:28px;display:grid;gap:22px}.mlms-lesson-topline{display:flex;justify-content:space-between;gap:14px;align-items:flex-start;flex-wrap:wrap}.mlms-lesson-reader h2{margin:0;color:var(--mlms-ink);font-size:clamp(30px,3vw,42px);line-height:1.04;letter-spacing:-.05em}.mlms-lesson-reader p{margin:0;color:var(--mlms-muted);line-height:1.58}.mlms-lesson-progress{min-width:210px}.mlms-lesson-progress-label{display:flex;justify-content:space-between;gap:10px;font-size:13px;color:var(--mlms-soft);font-weight:900;text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px}.mlms-lesson-progress-track{height:10px;border-radius:999px;background:#E6ECF2;overflow:hidden}.mlms-lesson-progress-track span{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,var(--mlms-primary),#A7B6FF);width:42%}.mlms-role-example{border-radius:22px;border:1px solid rgba(99,91,255,.18);background:linear-gradient(135deg,#F8F7FF,#fff);padding:18px;display:grid;gap:8px}.mlms-role-example strong{color:var(--mlms-primary);font-size:14px;text-transform:uppercase;letter-spacing:.12em}.mlms-flowchart{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;align-items:stretch}.mlms-flow-step{position:relative;border-radius:18px;padding:14px;background:#F6F9FC;border:1px solid #E2EAF3;color:var(--mlms-ink);font-weight:850;min-height:86px;display:grid;align-content:center}.mlms-flow-step span{display:block;color:var(--mlms-primary);font-size:12px;text-transform:uppercase;letter-spacing:.1em;margin-bottom:4px}.mlms-flow-step:not(:last-child):after{content:"→";position:absolute;right:-12px;top:50%;transform:translateY(-50%);z-index:2;width:22px;height:22px;border-radius:99px;background:var(--mlms-primary);color:#fff;display:grid;place-items:center;font-size:13px}.mlms-lesson-table{border:1px solid #E2EAF3;border-radius:20px;overflow:hidden;background:#fff}.mlms-lesson-row{display:grid;grid-template-columns:1fr 1.15fr;gap:0}.mlms-lesson-row+ .mlms-lesson-row{border-top:1px solid #E2EAF3}.mlms-lesson-row strong,.mlms-lesson-row span{padding:13px 15px}.mlms-lesson-row strong{background:#F6F9FC;color:var(--mlms-ink)}.mlms-lesson-row span{color:var(--mlms-muted)}.mlms-quiz{border-radius:22px;background:#0A2540;color:#fff;padding:18px}.mlms-quiz h3{margin:0 0 10px;color:#fff;font-size:19px}.mlms-quiz p{color:#C8D5E4}.mlms-quiz-options{display:grid;gap:8px;margin-top:12px}.mlms-quiz-option{display:flex;gap:10px;align-items:flex-start;border:1px solid rgba(255,255,255,.12);border-radius:15px;padding:11px;background:rgba(255,255,255,.07);cursor:pointer;color:#fff}.mlms-quiz-option input{margin-top:4px;accent-color:var(--mlms-primary)}.mlms-quiz-result{display:none;margin-top:12px;border-radius:14px;padding:10px 12px;background:rgba(33,197,142,.14);color:#DAFFF3;font-size:14px;font-weight:760}.mlms-quiz-result.active{display:block}.mlms-lesson-aside{display:grid;gap:16px;position:sticky;top:92px}.mlms-lesson-side-card{padding:20px}.mlms-lesson-side-card h3{margin:0 0 10px;color:var(--mlms-ink);font-size:20px}.mlms-lesson-side-card ul{margin:0;padding:0;list-style:none;display:grid;gap:10px}.mlms-lesson-side-card li{display:flex;gap:10px;color:var(--mlms-muted);font-weight:720;line-height:1.35}.mlms-lesson-side-card li:before{content:"";width:10px;height:10px;flex:0 0 auto;margin-top:6px;border-radius:99px;background:var(--mlms-primary);box-shadow:0 0 0 5px rgba(99,91,255,.10)}.mlms-build-card{padding:24px;display:grid;gap:18px}.mlms-build-card h2{margin:0;color:var(--mlms-ink);font-size:30px;line-height:1.08;letter-spacing:-.04em}.mlms-build-card p{margin:0;color:var(--mlms-muted)}.mlms-build-checklist{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.mlms-build-checklist label{display:flex;gap:9px;align-items:flex-start;border-radius:17px;border:1px solid #E2EAF3;background:#fff;padding:12px;color:var(--mlms-muted);font-weight:760;line-height:1.35}.mlms-build-checklist input{margin-top:4px;accent-color:var(--mlms-primary)}.mlms-build-card textarea{width:100%;min-height:118px;resize:vertical;border:1px solid #DDE6F0;border-radius:18px;padding:14px 16px;background:#fff;color:var(--mlms-ink);font:inherit}.mlms-build-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.mlms-build-actions .mlms-btn{border:0;cursor:pointer}.mlms-build-status{font-size:14px;color:var(--mlms-soft);font-weight:760}.mlms-completion-card{display:none;border-radius:24px;padding:20px;background:linear-gradient(135deg,#ECFFF8,#F7F5FF);border:1px solid rgba(33,197,142,.22);box-shadow:0 18px 34px rgba(33,197,142,.12)}.mlms-completion-card.active{display:grid;gap:10px;animation:mlmsCompletionRise .38s ease both}.mlms-completion-card h3{margin:0;color:#08785B;font-size:22px}.mlms-completion-card p{margin:0;color:#2F4F5F}.mlms-completion-points{display:inline-flex;width:max-content;border-radius:999px;background:#fff;color:#08785B;padding:8px 12px;font-size:13px;font-weight:950;box-shadow:0 8px 20px rgba(33,197,142,.12)}.mlms-tutor-launcher{position:fixed;right:24px;bottom:24px;z-index:80;border:0;border-radius:999px;background:var(--mlms-primary);color:#fff;box-shadow:0 18px 44px rgba(99,91,255,.32);display:flex;align-items:center;gap:10px;padding:13px 17px;font-weight:950;cursor:pointer}.mlms-tutor-launcher span:first-child{width:34px;height:34px;border-radius:99px;background:rgba(255,255,255,.18);display:grid;place-items:center}.mlms-tutor-launcher,.mlms-tutor-launcher span{color:#000!important}.mlms-tutor-panel{position:fixed;right:24px;bottom:90px;z-index:81;width:min(410px,calc(100vw - 32px));max-height:min(720px,calc(100vh - 120px));display:grid;grid-template-rows:auto 1fr auto;border-radius:28px;background:#fff;border:1px solid rgba(10,37,64,.10);box-shadow:0 28px 90px rgba(10,37,64,.22);overflow:hidden;opacity:0;pointer-events:none;transform:translateX(28px) translateY(8px);transition:opacity .2s ease,transform .2s ease}.mlms-tutor-panel.open{opacity:1;pointer-events:auto;transform:translateX(0) translateY(0)}.mlms-tutor-head{background:linear-gradient(135deg,#0A2540,#241A56);color:#fff;padding:16px;display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.mlms-tutor-head h3{margin:0;color:#fff;font-size:19px}.mlms-tutor-head p{margin:3px 0 0;color:#C8D5E4;font-size:13px;line-height:1.35}.mlms-tutor-close{width:34px;height:34px;border:0;border-radius:99px;background:rgba(255,255,255,.12);color:#fff;font-size:21px;cursor:pointer}.mlms-tutor-messages{padding:16px;background:#F6F9FC;overflow:auto;display:grid;align-content:start;gap:12px;min-height:220px}.mlms-tutor-msg{max-width:92%;border-radius:18px;padding:12px 13px;font-size:14px;line-height:1.45;white-space:pre-wrap}.mlms-tutor-msg.bot{background:#fff;color:var(--mlms-muted);border:1px solid #E2EAF3;justify-self:start}.mlms-tutor-msg.user{background:var(--mlms-primary);color:#fff;justify-self:end}.mlms-tutor-form{padding:14px;background:#fff;display:grid;gap:10px}.mlms-tutor-mode-row{display:flex;flex-wrap:wrap;gap:8px}.mlms-tutor-chip{border:1px solid #DDE6F0;background:#fff;color:var(--mlms-muted);border-radius:999px;padding:7px 10px;font-size:12px;font-weight:900;cursor:pointer}.mlms-tutor-chip:hover,.mlms-tutor-chip.active{border-color:rgba(99,91,255,.40);background:var(--mlms-primary-soft);color:var(--mlms-primary)}.mlms-tutor-form textarea{width:100%;min-height:76px;resize:vertical;border:1px solid #DDE6F0;border-radius:16px;padding:12px 13px;color:var(--mlms-ink);font:inherit;font-size:14px}.mlms-tutor-file{display:grid;gap:5px;color:var(--mlms-soft);font-size:12px;font-weight:800}.mlms-tutor-file input{font-size:12px}.mlms-tutor-submit{border:0;cursor:pointer;width:100%}.mlms-tutor-help{font-size:12px;color:var(--mlms-soft);line-height:1.35;margin:0}.mlms-ai-note{display:inline-flex;align-items:center;gap:8px;border-radius:999px;background:var(--mlms-primary-soft);color:var(--mlms-primary);padding:8px 11px;font-size:12px;font-weight:950}.mlms-profile-proof-heading{display:flex;align-items:end;justify-content:space-between;gap:14px;margin:34px 0 16px}.mlms-profile-proof-heading h2{margin:0;color:var(--mlms-ink);font-size:28px;letter-spacing:-.04em}.mlms-profile-proof-heading p{margin:4px 0 0;color:var(--mlms-muted)}@keyframes mlmsCourseEarn{0%{transform:scale(.75)}55%{transform:scale(1.28)}100%{transform:scale(1)}}@keyframes mlmsCompletionRise{from{transform:translateY(10px);opacity:0}to{transform:translateY(0);opacity:1}}@media (max-width:980px){.mlms-phase-path,.mlms-lesson-grid{grid-template-columns:1fr}.mlms-lesson-aside{position:relative;top:auto}.mlms-flowchart,.mlms-build-checklist{grid-template-columns:1fr 1fr}.mlms-course-overview-head{display:grid}.mlms-course-streak{width:max-content}}@media (max-width:640px){.mlms-course-overview,.mlms-lesson-reader,.mlms-build-card{border-radius:24px;padding:20px}.mlms-flowchart,.mlms-build-checklist,.mlms-lesson-row{grid-template-columns:1fr}.mlms-flow-step:not(:last-child):after{display:none}.mlms-tutor-launcher{right:16px;bottom:16px}.mlms-tutor-launcher span:last-child{display:none}.mlms-tutor-panel{right:16px;bottom:72px}.mlms-profile-proof-heading{display:grid}}
/* Course launch, saved progress, and separate quiz block */
.mlms-course-actions,.mlms-phase-actions,.mlms-lesson-checkpoint-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center}.mlms-course-actions{position:relative;margin:18px 0 16px}.mlms-course-actions .mlms-btn,.mlms-phase-actions .mlms-btn,.mlms-lesson-checkpoint-actions .mlms-btn{border:0;cursor:pointer;text-decoration:none}.mlms-course-resume-strip{position:relative;display:grid;grid-template-columns:auto 1fr auto;gap:14px;align-items:center;margin:0 0 18px;padding:14px;border-radius:22px;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.14)}.mlms-course-resume-strip span{font-size:12px;letter-spacing:.12em;text-transform:uppercase;font-weight:950;color:#C9C4FF}.mlms-course-resume-strip strong{display:block;color:#fff}.mlms-course-resume-strip small{display:block;color:#C8D5E4;margin-top:2px}.mlms-course-mini-meter{width:132px;height:8px;border-radius:999px;background:rgba(255,255,255,.16);overflow:hidden}.mlms-course-mini-meter i{display:block;height:100%;width:42%;border-radius:inherit;background:linear-gradient(90deg,#21C58E,#C9C4FF);transition:width .25s ease}.mlms-phase-actions{margin-top:4px}.mlms-phase-launch{min-height:38px;padding:10px 13px;font-size:13px}.mlms-locked-button{min-height:38px;padding:10px 13px;border-radius:999px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.08);color:#D7E3F1;font-weight:900;cursor:not-allowed}.mlms-course-workspace{display:grid;gap:22px;scroll-margin-top:112px}.mlms-course-workspace.launched{animation:mlmsCourseLaunch .5s ease both}.mlms-course-workspace-head{display:flex;justify-content:space-between;gap:16px;align-items:center;border:1px solid rgba(99,91,255,.14);background:rgba(255,255,255,.88);box-shadow:var(--mlms-shadow-soft);border-radius:26px;padding:18px 20px}.mlms-course-workspace-head h3{margin:0;color:var(--mlms-ink);font-size:24px;letter-spacing:-.035em}.mlms-course-workspace-head p{margin:5px 0 0;color:var(--mlms-muted)}.mlms-course-status-pill{display:inline-flex;align-items:center;gap:8px;white-space:nowrap;border-radius:999px;background:var(--mlms-primary-soft);color:var(--mlms-primary);padding:9px 12px;font-size:12px;font-weight:950;text-transform:uppercase;letter-spacing:.08em}.mlms-course-status-pill:before{content:"";width:8px;height:8px;border-radius:99px;background:var(--mlms-primary);box-shadow:0 0 0 6px rgba(99,91,255,.10)}.mlms-lesson-checkpoint{border-radius:22px;background:#F8FAFD;border:1px solid #E2EAF3;padding:18px;display:grid;gap:10px}.mlms-lesson-checkpoint h3{margin:0;color:var(--mlms-ink);font-size:20px}.mlms-quiz-card{background:rgba(255,255,255,.94);border:1px solid rgba(10,37,64,.09);border-radius:30px;box-shadow:var(--mlms-shadow-soft);padding:28px;display:grid;gap:18px;scroll-margin-top:112px}.mlms-quiz-head{display:flex;justify-content:space-between;gap:18px;align-items:flex-start}.mlms-quiz-head h2{margin:0;color:var(--mlms-ink);font-size:clamp(28px,3vw,38px);letter-spacing:-.045em;line-height:1.04}.mlms-quiz-head p{margin:8px 0 0;color:var(--mlms-muted);max-width:680px}.mlms-quiz-count{border-radius:999px;background:#F2F6FA;border:1px solid #DDE6F0;color:#4D5F75;padding:9px 12px;font-weight:950;font-size:13px;white-space:nowrap}.mlms-quiz-form{display:grid;gap:14px}.mlms-quiz-question{border:1px solid #E2EAF3;border-radius:22px;background:#fff;padding:16px;display:grid;gap:12px}.mlms-quiz-question legend{font-weight:950;color:var(--mlms-ink);padding:0 4px}.mlms-quiz-question.is-correct{border-color:rgba(33,197,142,.34);background:rgba(33,197,142,.05)}.mlms-quiz-question.is-wrong{border-color:rgba(239,68,68,.28);background:rgba(239,68,68,.045)}.mlms-quiz-options{display:grid;gap:10px}.mlms-quiz-option{display:flex;gap:10px;align-items:flex-start;padding:12px;border:1px solid #E2EAF3;background:#F8FAFD;border-radius:16px;color:var(--mlms-muted);font-weight:750;line-height:1.35}.mlms-quiz-option input{margin-top:2px}.mlms-quiz-actions{display:flex;flex-wrap:wrap;gap:12px;align-items:center}.mlms-quiz-actions .mlms-btn{border:0;cursor:pointer}.mlms-quiz-status{color:var(--mlms-soft);font-size:13px;font-weight:850}.mlms-quiz-result{display:none;border-radius:22px;padding:16px;border:1px solid #DDE6F0;background:#F8FAFD;color:var(--mlms-muted);line-height:1.5}.mlms-quiz-result.active{display:block;animation:mlmsCompletionRise .35s ease both}.mlms-quiz-result.pass{border-color:rgba(33,197,142,.30);background:rgba(33,197,142,.07);color:#0B6B52}.mlms-quiz-result.retry{border-color:rgba(245,158,11,.32);background:rgba(245,158,11,.08);color:#7A4B00}.mlms-quiz-result.error{border-color:rgba(239,68,68,.25);background:rgba(239,68,68,.06);color:#8A1F1F}.mlms-quiz-result strong{display:block;color:inherit;margin-bottom:4px}.mlms-profile-course-progress .mlms-module-head{align-items:center}.mlms-profile-resume-card{display:grid;grid-template-columns:1fr auto;gap:16px;align-items:center;border-radius:24px;border:1px solid rgba(255,255,255,.16);background:#0A0A0A;color:#fff;box-shadow:0 18px 40px rgba(0,0,0,.22);padding:18px;margin-bottom:16px}.mlms-profile-resume-card .mlms-kicker,.mlms-profile-resume-card h4,.mlms-profile-resume-card p{color:#fff!important}.mlms-profile-resume-card h4{margin:0;font-size:21px;letter-spacing:-.03em}.mlms-profile-resume-card p{margin:5px 0 0}.mlms-profile-resume-card .mlms-btn{text-decoration:none;background:#fff!important;color:#000!important;border:1px solid rgba(255,255,255,.18)!important;box-shadow:0 14px 30px rgba(0,0,0,.2)!important}.mlms-profile-resume-card .mlms-btn:hover,.mlms-profile-resume-card .mlms-btn:focus{background:#fff!important;color:#000!important}.mlms-profile-progress-bar{height:10px;border-radius:999px;background:#E6ECF2;overflow:hidden;margin:10px 0 16px}.mlms-profile-progress-bar span{display:block;height:100%;width:42%;border-radius:inherit;background:linear-gradient(90deg,var(--mlms-primary),#21C58E);transition:width .25s ease}.mlms-profile-course-progress .mlms-progress-summary{margin-bottom:14px}.mlms-course-dashboard.is-lesson-complete .mlms-dashboard-phase.current{background:rgba(33,197,142,.18)}.mlms-course-dashboard.is-lesson-complete .mlms-course-status-pill{background:rgba(33,197,142,.10);color:#08785B}.mlms-course-dashboard.is-lesson-complete .mlms-course-status-pill:before{background:var(--mlms-green);box-shadow:0 0 0 6px rgba(33,197,142,.12)}@keyframes mlmsCourseLaunch{0%{box-shadow:0 0 0 rgba(99,91,255,0)}45%{box-shadow:0 0 0 8px rgba(99,91,255,.10)}100%{box-shadow:0 0 0 rgba(99,91,255,0)}}@media (max-width:760px){.mlms-course-resume-strip,.mlms-course-workspace-head,.mlms-quiz-head,.mlms-profile-resume-card{grid-template-columns:1fr;display:grid}.mlms-course-mini-meter{width:100%}.mlms-profile-resume-card .mlms-btn{width:max-content}.mlms-quiz-card{padding:20px;border-radius:24px}}
.mlms-course-launch-hub{grid-template-columns:1fr}.mlms-course-page-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.mlms-course-page-card{position:relative;overflow:hidden;border-radius:26px;border:1px solid rgba(10,37,64,.09);background:rgba(255,255,255,.92);box-shadow:var(--mlms-shadow-soft);padding:22px;display:grid;gap:14px;align-content:start}.mlms-course-page-card:before{content:"";position:absolute;inset:0 0 auto 0;height:3px;background:linear-gradient(90deg,var(--mlms-primary),#21C58E);opacity:.75}.mlms-course-page-card h4{margin:0;color:var(--mlms-ink);font-size:22px;letter-spacing:-.035em}.mlms-course-page-card p{margin:0;color:var(--mlms-muted);line-height:1.5}.mlms-course-page-card .mlms-btn{width:max-content;text-decoration:none}.mlms-course-page-meta{display:flex;flex-wrap:wrap;gap:8px}.mlms-course-page-meta span{border-radius:999px;background:#F2F6FA;border:1px solid #DDE6F0;color:#4D5F75;padding:7px 10px;font-size:12px;font-weight:900}.mlms-course-standalone-page{background:var(--mlms-bg)}.mlms-course-standalone{padding:0 0 84px}.mlms-course-standalone-hero{position:relative;overflow:hidden;border-radius:34px;padding:28px;background:linear-gradient(135deg,#07182D 0%,#132747 58%,#251A56 100%);color:#fff;box-shadow:var(--mlms-shadow);margin:28px 0 24px}.mlms-course-standalone-hero h1{margin:0;color:#fff;font-size:clamp(38px,5vw,64px);line-height:.98;letter-spacing:-.06em;max-width:860px}.mlms-course-standalone-hero p{color:#D7E3F1;max-width:760px}.mlms-course-standalone-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:20px}.mlms-course-standalone-actions .mlms-btn-secondary{background:rgba(255,255,255,.11);border-color:rgba(255,255,255,.16);color:#fff;box-shadow:none}.mlms-course-route-shell{display:grid;gap:22px}.mlms-course-backlink{display:inline-flex;align-items:center;gap:8px;color:#C9C4FF!important;font-weight:900;text-decoration:none!important;margin-bottom:14px}.mlms-course-route-pill{display:inline-flex;width:max-content;border-radius:999px;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.14);color:#E7E3FF;padding:8px 11px;font-weight:950;font-size:12px;text-transform:uppercase;letter-spacing:.08em;margin-bottom:12px}@media (max-width:900px){.mlms-course-page-grid{grid-template-columns:1fr}.mlms-course-standalone-hero{border-radius:26px;padding:22px}}
.mlms-lesson-path-page{display:grid;gap:24px}.mlms-lesson-path-card{position:relative;overflow:hidden;border-radius:30px;background:#0F2038!important;border:1px solid rgba(255,255,255,.16)!important;box-shadow:var(--mlms-shadow-soft);padding:26px;color:#fff!important}.mlms-lesson-path-card:before{content:"";position:absolute;inset:0 0 auto 0;height:5px;background:linear-gradient(90deg,var(--mlms-primary),#A7B6FF,var(--mlms-green))}.mlms-lesson-path-head{display:flex;justify-content:space-between;gap:18px;align-items:flex-start;flex-wrap:wrap;margin-bottom:22px}.mlms-lesson-path-head .mlms-kicker{color:#C9C4FF!important}.mlms-lesson-path-head h2{margin:0;color:#fff!important;font-size:clamp(30px,3.6vw,48px);line-height:1.02;letter-spacing:-.055em}.mlms-lesson-path-head p{margin:8px 0 0;color:#EAF1FA!important;max-width:720px}.mlms-lesson-path-list{position:relative;display:grid;gap:14px;margin:0;padding:0;list-style:none}.mlms-lesson-path-list:before{content:"";position:absolute;left:25px;top:28px;bottom:28px;width:3px;border-radius:99px;background:#E2EAF3}.mlms-lesson-path-task{position:relative;display:grid;grid-template-columns:52px minmax(0,1fr) auto;gap:16px;align-items:center;border-radius:22px;border:1px solid #E2EAF3;background:#fff;padding:16px;box-shadow:0 10px 26px rgba(50,50,93,.06)}.mlms-lesson-path-marker{position:relative;z-index:1;width:52px;height:52px;border-radius:18px;display:grid;place-items:center;background:#F2F6FA;color:var(--mlms-muted);font-weight:950;border:1px solid #E2EAF3}.mlms-lesson-path-task.is-complete .mlms-lesson-path-marker{background:#ECFBF6;color:#08785B;border-color:rgba(33,197,142,.28)}.mlms-lesson-path-task.is-current{border-color:rgba(99,91,255,.28);background:linear-gradient(135deg,#fff,#F8F7FF);box-shadow:0 16px 36px rgba(99,91,255,.10)}.mlms-lesson-path-task.is-current .mlms-lesson-path-marker{background:var(--mlms-primary);color:#fff;border-color:transparent;box-shadow:0 0 0 7px rgba(99,91,255,.12)}.mlms-lesson-path-task.is-quiz{border-color:rgba(255,255,255,.18)!important;background:#0F2038!important;color:#fff!important}.mlms-lesson-path-task.is-quiz .mlms-lesson-path-marker{background:#fff!important;color:#000!important;border-color:rgba(255,255,255,.24)!important}.mlms-lesson-path-task h3{margin:0;color:var(--mlms-ink);font-size:21px;letter-spacing:-.03em}.mlms-lesson-path-task p{margin:5px 0 0;color:var(--mlms-muted);line-height:1.45}.mlms-lesson-path-task.is-quiz h3,.mlms-lesson-path-task.is-quiz p{color:#fff!important}.mlms-lesson-path-status{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:#F2F6FA;color:#56657D;padding:8px 11px;font-size:12px;font-weight:950;text-transform:uppercase;letter-spacing:.08em;white-space:nowrap}.mlms-lesson-path-task.is-complete .mlms-lesson-path-status{background:#ECFBF6;color:#08785B}.mlms-lesson-path-task.is-current .mlms-lesson-path-status{background:#EFEDFF;color:var(--mlms-primary)}.mlms-lesson-path-task.is-quiz .mlms-lesson-path-status{background:rgba(255,255,255,.12);color:#E7E3FF}.mlms-lesson-path-card .mlms-lesson-path-task{background:#132747!important;border-color:rgba(255,255,255,.18)!important;color:#fff!important;box-shadow:0 14px 34px rgba(0,0,0,.20)!important}.mlms-lesson-path-card .mlms-lesson-path-task.is-current{background:#132747!important;background-image:none!important;border-color:rgba(201,196,255,.38)!important}.mlms-lesson-path-card .mlms-lesson-path-task.is-complete{background:#132747!important}.mlms-lesson-path-card .mlms-lesson-path-task h3,.mlms-lesson-path-card .mlms-lesson-path-task p{color:#fff!important}.mlms-lesson-path-card .mlms-lesson-path-task p{opacity:.86}.mlms-lesson-path-card .mlms-lesson-path-task.is-complete .mlms-lesson-path-marker{background:rgba(236,251,246,.95)!important;color:#08785B!important;border-color:rgba(33,197,142,.32)!important}.mlms-lesson-path-card .mlms-lesson-path-task.is-current .mlms-lesson-path-marker{background:#fff!important;color:#000!important;border-color:rgba(201,196,255,.52)!important;box-shadow:0 0 0 7px rgba(201,196,255,.16)!important}.mlms-lesson-path-card .mlms-lesson-path-task .mlms-lesson-path-status{background:rgba(255,255,255,.12)!important;color:#fff!important}.mlms-lesson-path-card .mlms-lesson-path-task.is-complete .mlms-lesson-path-status{background:rgba(236,251,246,.95)!important;color:#08785B!important}.mlms-lesson-path-card .mlms-lesson-path-list:before{background:rgba(255,255,255,.28)!important}.mlms-lesson-content-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.mlms-lesson-task-card{border-radius:24px;background:#132747!important;border:1px solid rgba(255,255,255,.18)!important;box-shadow:0 14px 34px rgba(0,0,0,.20)!important;padding:22px;color:#fff!important}.mlms-lesson-task-card h3{margin:0 0 8px;color:#fff!important;font-size:23px;letter-spacing:-.035em}.mlms-lesson-task-card p{margin:0;color:#EAF1FA!important;line-height:1.55}.mlms-lesson-task-card .mlms-kicker{color:#C9C4FF!important}.mlms-lesson-task-card ul{margin:12px 0 0;padding-left:20px;color:#EAF1FA!important}.mlms-lesson-task-card li{color:#EAF1FA!important}.mlms-lesson-task-card li+li{margin-top:6px}.mlms-lesson-content-grid .mlms-lesson-task-card p,.mlms-lesson-content-grid .mlms-lesson-task-card li{color:#EAF1FA!important}.mlms-lesson-content-grid .mlms-lesson-task-card h3{color:#fff!important}.mlms-lesson-content-grid .mlms-lesson-task-card .mlms-kicker{color:#C9C4FF!important}.mlms-quiz-card{background:#132747!important;border-color:rgba(255,255,255,.18)!important;box-shadow:0 24px 60px rgba(7,24,45,.30)!important}.mlms-quiz-card .mlms-quiz-head h2{color:#fff!important}.mlms-quiz-card .mlms-quiz-head p{color:#EAF1FA!important}.mlms-quiz-card .mlms-quiz-count{background:rgba(255,255,255,.10)!important;border-color:rgba(255,255,255,.22)!important;color:#EAF1FA!important}.mlms-quiz-card .mlms-quiz-question{background:rgba(255,255,255,.07)!important;border-color:rgba(255,255,255,.16)!important}.mlms-quiz-card .mlms-quiz-question legend{color:#fff!important}.mlms-quiz-card .mlms-quiz-option{background:rgba(255,255,255,.09)!important;border-color:rgba(255,255,255,.16)!important;color:#EAF1FA!important}.mlms-quiz-card .mlms-quiz-status{color:#D8E4F2!important}.mlms-quiz-card .mlms-quiz-result{background:rgba(255,255,255,.08)!important;border-color:rgba(255,255,255,.16)!important;color:#EAF1FA!important}@media (max-width:780px){.mlms-lesson-path-task{grid-template-columns:46px 1fr;align-items:start}.mlms-lesson-path-marker{width:46px;height:46px;border-radius:16px}.mlms-lesson-path-status,.mlms-lesson-path-task .mlms-btn{grid-column:2;justify-self:start}.mlms-lesson-content-grid{grid-template-columns:1fr}.mlms-lesson-path-list:before{left:23px}}
.mlms-proof-modal{position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center;padding:24px}.mlms-proof-modal.open{display:flex}.mlms-proof-backdrop{position:absolute;inset:0;background:rgba(7,24,45,.62);backdrop-filter:blur(8px)}.mlms-proof-dialog{position:relative;z-index:1;width:min(640px,100%);max-height:92vh;overflow:auto;background:#fff;border:1px solid rgba(10,37,64,.10);border-radius:30px;box-shadow:0 28px 80px rgba(7,24,45,.28);padding:26px}.mlms-proof-dialog:before{content:"";position:absolute;inset:0 0 auto 0;height:5px;background:linear-gradient(90deg,var(--mlms-primary),var(--mlms-green));border-radius:30px 30px 0 0}.mlms-proof-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px}.mlms-proof-head h3{margin:0;color:var(--mlms-ink);font-size:28px;letter-spacing:-.04em;line-height:1.08}.mlms-proof-head p{margin:7px 0 0;color:var(--mlms-muted);line-height:1.45}.mlms-proof-close{width:42px;height:42px;border:0;border-radius:15px;background:#F2F6FA;color:var(--mlms-ink);font-size:24px;line-height:1;cursor:pointer}.mlms-proof-close:hover{background:#E7EDF5}.mlms-proof-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}.mlms-proof-dialog .mlms-field.full{grid-column:1/-1}.mlms-proof-file{border:1px dashed #C9D6E5;border-radius:18px;background:#F8FAFD;padding:14px}.mlms-proof-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:18px}.mlms-proof-status{margin:0;color:#08785B;font-weight:850}.mlms-portfolio-card.proof-attached{border-color:rgba(33,197,142,.36);box-shadow:0 18px 42px rgba(33,197,142,.12)}.mlms-portfolio-card.proof-attached:after{content:"Proof attached";position:absolute;top:14px;right:14px;border-radius:999px;background:#ECFBF6;color:#08785B;border:1px solid rgba(33,197,142,.26);padding:7px 10px;font-size:12px;font-weight:950}.mlms-portfolio-link.is-attached{color:#08785B}@media (max-width:640px){.mlms-proof-dialog{padding:22px;border-radius:24px}.mlms-proof-grid{grid-template-columns:1fr}.mlms-proof-actions{display:grid}.mlms-proof-actions .mlms-btn{width:100%;justify-content:center}.mlms-proof-head h3{font-size:24px}}
/* Unggul Coach consultation wizard */
.mlms-coach-progress{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:4px 0 22px}.mlms-coach-progress button{position:relative;text-align:left;border:1px solid #DDE6F0;background:#fff;border-radius:18px;padding:13px 13px 12px 52px;min-height:72px;color:var(--mlms-muted);box-shadow:0 8px 20px rgba(50,50,93,.05);cursor:pointer;transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease,background .18s ease}.mlms-coach-progress button:hover{transform:translateY(-2px);box-shadow:0 14px 30px rgba(50,50,93,.10);border-color:rgba(99,91,255,.30)}.mlms-coach-progress span{position:absolute;left:13px;top:15px;width:30px;height:30px;border-radius:12px;display:grid;place-items:center;background:#F2F6FA;color:#5E6E82;font-weight:950}.mlms-coach-progress strong{display:block;color:var(--mlms-ink);font-size:14px;line-height:1.1}.mlms-coach-progress small{display:block;color:var(--mlms-soft);font-size:12px;font-weight:750;margin-top:4px}.mlms-coach-progress button.active,.mlms-coach-progress button.complete{border-color:rgba(99,91,255,.34);background:linear-gradient(135deg,#fff,#F8F7FF)}.mlms-coach-progress button.active span{background:var(--mlms-primary);color:#fff;box-shadow:0 0 0 7px rgba(99,91,255,.11)}.mlms-coach-progress button.complete span{background:#ECFBF6;color:#08785B}.mlms-coach-panel{display:none;animation:mlmsFadeUp .24s ease both}.mlms-coach-panel.active{display:block}.mlms-coach-panel h2{font-size:clamp(28px,3vw,38px);line-height:1.06;letter-spacing:-.04em;margin:0 0 10px;color:var(--mlms-ink)}.mlms-coach-panel>p:not(.mlms-onboarding-step-label){color:var(--mlms-muted);margin:0 0 22px}.mlms-coach-tip{border-radius:18px;padding:14px 16px;background:linear-gradient(135deg,#F6F4FF,#F8FAFD);border:1px solid rgba(99,91,255,.16);color:#3C4770;margin:16px 0 0;font-size:15px;line-height:1.45}.mlms-coach-tip strong{color:var(--mlms-primary)}.mlms-coach-review{border-radius:22px;border:1px solid #DDE6F0;background:#F8FAFD;padding:18px;margin:18px 0 0}.mlms-coach-review>strong{display:block;color:var(--mlms-ink);font-size:18px;margin-bottom:10px}.mlms-coach-review ul{margin:0;padding:0;list-style:none;display:grid;gap:9px}.mlms-coach-review li{display:flex;justify-content:space-between;gap:14px;border-top:1px solid #E5ECF4;padding-top:9px;color:var(--mlms-muted)}.mlms-coach-review li:first-child{border-top:0;padding-top:0}.mlms-coach-review span{font-weight:850}.mlms-coach-review b{color:var(--mlms-ink);text-align:right}.mlms-coach-loading{width:100%}@media (max-width:900px){.mlms-coach-progress{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:560px){.mlms-coach-progress{grid-template-columns:1fr}.mlms-coach-progress button{min-height:auto}.mlms-coach-review li{display:grid;gap:2px}.mlms-coach-review b{text-align:left}}
/* Unggul black & white theme overrides */
:root{
--mlms-ink:#FFFFFF;
--mlms-navy:#FFFFFF;
--mlms-deep:#000000;
--mlms-muted:#D4D4D4;
--mlms-soft:#A3A3A3;
--mlms-bg:#000000;
--mlms-surface:#0A0A0A;
--mlms-line:rgba(255,255,255,.18);
--mlms-wax:#FFFFFF;
--mlms-amber:#F5F5F5;
--mlms-violet:#FFFFFF;
--mlms-primary:#FFFFFF;
--mlms-primary-dark:#E5E5E5;
--mlms-primary-soft:rgba(255,255,255,.12);
--mlms-cyan:#FFFFFF;
--mlms-pink:#E5E5E5;
--mlms-green:#FFFFFF;
--mlms-shadow:0 30px 90px rgba(0,0,0,.72),0 14px 34px rgba(0,0,0,.54);
--mlms-shadow-soft:0 18px 50px rgba(0,0,0,.54),0 8px 22px rgba(0,0,0,.42);
}
body.mlms-wax-page,
body.mlms-wax-page .wp-site-blocks{
background:#000000!important;
color:#FFFFFF!important;
}
body.mlms-wax-page .mlms-page,
body.mlms-wax-page .entry-content,
body.mlms-wax-page .wp-block-post-content{color:#FFFFFF!important;background:transparent!important}
body.mlms-wax-page .mlms-page:before{background:linear-gradient(110deg,rgba(255,255,255,.08),rgba(255,255,255,.02))!important;opacity:1!important}
body.mlms-wax-page .mlms-site-header{background:rgba(0,0,0,.88)!important;border-bottom-color:rgba(255,255,255,.16)!important;box-shadow:0 12px 32px rgba(0,0,0,.44)!important}
body.mlms-wax-page .wp-block-site-title a,
body.mlms-wax-page .wp-block-navigation a,
body.mlms-wax-page .mlms-header-brand,
body.mlms-wax-page .mlms-header-nav a,
body.mlms-wax-page .mlms-header-login,
body.mlms-wax-page .mlms-header-logout-icon{color:#FFFFFF!important}
body.mlms-wax-page .mlms-header-nav a:hover,
body.mlms-wax-page .mlms-header-login:hover,
body.mlms-wax-page a:not(.mlms-btn):not(.mlms-header-brand):not(.mlms-footer-brand):hover{color:#E5E5E5!important}
body.mlms-wax-page .mlms-header-login,
body.mlms-wax-page .mlms-header-logout-icon{background:#0A0A0A!important;border-color:rgba(255,255,255,.22)!important;box-shadow:none!important}
body.mlms-wax-page .mlms-site-footer{background:#000000!important;border-top:1px solid rgba(255,255,255,.16)!important;color:#D4D4D4!important}
body.mlms-wax-page .mlms-site-footer a,
body.mlms-wax-page .mlms-footer-brand{color:#FFFFFF!important}
body.mlms-wax-page .mlms-site-footer p{color:#D4D4D4!important}
body.mlms-wax-page .mlms-pill{background:#FFFFFF!important;color:#000000!important;border-color:#FFFFFF!important;box-shadow:none!important}
body.mlms-wax-page .mlms-gradient-text{background:none!important;-webkit-background-clip:initial!important;background-clip:initial!important;color:#FFFFFF!important}
body.mlms-wax-page h1,
body.mlms-wax-page h2,
body.mlms-wax-page h3,
body.mlms-wax-page h4,
body.mlms-wax-page h5,
body.mlms-wax-page h6,
body.mlms-wax-page .mlms-h1,
body.mlms-wax-page .mlms-section-title,
body.mlms-wax-page .mlms-card h3,
body.mlms-wax-page .mlms-feature h3,
body.mlms-wax-page .mlms-phase-card h4,
body.mlms-wax-page .mlms-portfolio-card h4,
body.mlms-wax-page .mlms-feed-item strong,
body.mlms-wax-page .mlms-coach-panel h2,
body.mlms-wax-page .mlms-coach-progress strong,
body.mlms-wax-page .mlms-coach-review>strong,
body.mlms-wax-page .mlms-coach-review b,
body.mlms-wax-page .mlms-proof-head h3{color:#FFFFFF!important}
body.mlms-wax-page p,
body.mlms-wax-page li,
body.mlms-wax-page small,
body.mlms-wax-page label,
body.mlms-wax-page .mlms-lede,
body.mlms-wax-page .mlms-section-copy,
body.mlms-wax-page .mlms-muted,
body.mlms-wax-page .mlms-card p,
body.mlms-wax-page .mlms-feature p,
body.mlms-wax-page .mlms-phase-card p,
body.mlms-wax-page .mlms-portfolio-card p,
body.mlms-wax-page .mlms-feed-item small,
body.mlms-wax-page .mlms-coach-progress small,
body.mlms-wax-page .mlms-coach-panel>p,
body.mlms-wax-page .mlms-coach-review li,
body.mlms-wax-page .mlms-proof-head p{color:#D4D4D4!important}
body.mlms-wax-page a:not(.mlms-btn):not(.mlms-header-brand):not(.mlms-footer-brand){color:#FFFFFF!important;text-decoration-color:rgba(255,255,255,.42)!important}
body.mlms-wax-page .mlms-coach-card{
background:#0A0A0A!important;
border-color:rgba(255,255,255,.24)!important;
color:#FFFFFF!important;
box-shadow:0 24px 60px rgba(0,0,0,.62)!important;
}
body.mlms-wax-page .mlms-coach-card .mlms-onboarding-step-label{color:#D4D4D4!important}
body.mlms-wax-page .mlms-card,
body.mlms-wax-page .mlms-feature,
body.mlms-wax-page .mlms-step,
body.mlms-wax-page .mlms-price-card,
body.mlms-wax-page .mlms-testimonial,
body.mlms-wax-page .mlms-login-card,
body.mlms-wax-page .mlms-onboarding-card,
body.mlms-wax-page .mlms-profile-module,
body.mlms-wax-page .mlms-portfolio-card,
body.mlms-wax-page .mlms-feed-item,
body.mlms-wax-page .mlms-source-form,
body.mlms-wax-page .mlms-progress-stat,
body.mlms-wax-page .mlms-phase-card,
body.mlms-wax-page .mlms-skill-guide span,
body.mlms-wax-page .mlms-coach-progress button,
body.mlms-wax-page .mlms-coach-tip,
body.mlms-wax-page .mlms-coach-review,
body.mlms-wax-page .mlms-proof-dialog,
body.mlms-wax-page .mlms-proof-file,
body.mlms-wax-page .mlms-ai-profile,
body.mlms-wax-page .mlms-course-card,
body.mlms-wax-page .mlms-lesson-card,
body.mlms-wax-page .mlms-tutor-panel,
body.mlms-wax-page .mlms-tutor-msg.bot{
background:#0A0A0A!important;
border-color:rgba(255,255,255,.20)!important;
color:#FFFFFF!important;
box-shadow:var(--mlms-shadow-soft)!important;
}
body.mlms-wax-page .mlms-hero-card,
body.mlms-wax-page .mlms-profile-summary-card,
body.mlms-wax-page .mlms-onboarding-success,
body.mlms-wax-page .mlms-dashboard-card,
body.mlms-wax-page .mlms-credential-card{
background:#0A0A0A!important;
border:1px solid rgba(255,255,255,.24)!important;
color:#FFFFFF!important;
box-shadow:var(--mlms-shadow)!important;
}
body.mlms-wax-page .mlms-btn-primary,
body.mlms-wax-page button.mlms-btn-primary,
body.mlms-wax-page input[type="submit"].mlms-btn-primary,
body.mlms-wax-page .mlms-header-signup,
body.mlms-wax-page .mlms-header-profile{
background:#FFFFFF!important;
color:#000000!important;
border-color:#FFFFFF!important;
box-shadow:0 14px 34px rgba(255,255,255,.14)!important;
}
body.mlms-wax-page .mlms-btn-primary:hover,
body.mlms-wax-page button.mlms-btn-primary:hover,
body.mlms-wax-page input[type="submit"].mlms-btn-primary:hover,
body.mlms-wax-page .mlms-header-signup:hover,
body.mlms-wax-page .mlms-header-profile:hover{background:#E5E5E5!important;color:#000000!important;border-color:#E5E5E5!important}
body.mlms-wax-page .mlms-btn-secondary,
body.mlms-wax-page .mlms-btn:not(.mlms-btn-primary):not(.mlms-header-signup):not(.mlms-header-profile){background:#000000!important;color:#FFFFFF!important;border-color:rgba(255,255,255,.38)!important;box-shadow:none!important}
body.mlms-wax-page .mlms-btn-secondary:hover,
body.mlms-wax-page .mlms-btn:not(.mlms-btn-primary):not(.mlms-header-signup):not(.mlms-header-profile):hover{background:#1A1A1A!important;color:#FFFFFF!important;border-color:#FFFFFF!important}
body.mlms-wax-page input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]),
body.mlms-wax-page textarea,
body.mlms-wax-page select,
body.mlms-wax-page .mlms-field input,
body.mlms-wax-page .mlms-field textarea,
body.mlms-wax-page .mlms-field select,
body.mlms-wax-page .mlms-source-form input,
body.mlms-wax-page .mlms-source-form select{
background:#000000!important;
border-color:rgba(255,255,255,.34)!important;
color:#FFFFFF!important;
box-shadow:inset 0 1px 0 rgba(255,255,255,.06)!important;
}
body.mlms-wax-page input::placeholder,
body.mlms-wax-page textarea::placeholder{color:#A3A3A3!important}
body.mlms-wax-page option{background:#000000!important;color:#FFFFFF!important}
body.mlms-wax-page .mlms-role-card,
body.mlms-wax-page .mlms-source-form,
body.mlms-wax-page .mlms-proof-file{border-style:solid!important}
body.mlms-wax-page .mlms-role-card:hover,
body.mlms-wax-page .mlms-coach-progress button:hover,
body.mlms-wax-page .mlms-phase-card.current,
body.mlms-wax-page .mlms-coach-progress button.active,
body.mlms-wax-page .mlms-coach-progress button.complete{border-color:#FFFFFF!important;background:#111111!important}
body.mlms-wax-page .mlms-coach-progress span{background:#FFFFFF!important;color:#A3A3A3!important;box-shadow:none!important}
body.mlms-wax-page .mlms-coach-progress button.complete span,
body.mlms-wax-page .mlms-coach-progress button.active span{background:#FFFFFF!important;color:#A3A3A3!important;box-shadow:0 0 0 6px rgba(255,255,255,.10)!important}
body.mlms-wax-page .mlms-progress-track,
body.mlms-wax-page .mlms-lesson-progress-track{background:rgba(255,255,255,.18)!important}
body.mlms-wax-page .mlms-progress-track span,
body.mlms-wax-page .mlms-lesson-progress-track span{background:#FFFFFF!important}
body.mlms-wax-page .mlms-feature-number{background:#FFFFFF!important;color:#A3A3A3!important}
body.mlms-wax-page .mlms-phase-card.current .mlms-phase-dot{background:#FFFFFF!important;color:#000000!important;box-shadow:none!important}
body.mlms-wax-page .mlms-skill-tip{background:#000000!important;border-color:rgba(255,255,255,.22)!important;color:#D4D4D4!important;box-shadow:0 18px 42px rgba(0,0,0,.64)!important}
body.mlms-wax-page .mlms-skill-tip:after{background:#000000!important}
body.mlms-wax-page .mlms-proof-dialog:before{background:#FFFFFF!important}
body.mlms-wax-page .mlms-proof-backdrop{background:rgba(0,0,0,.82)!important}
body.mlms-wax-page .mlms-proof-close{background:#000000!important;color:#FFFFFF!important;border:1px solid rgba(255,255,255,.28)!important}
body.mlms-wax-page .mlms-profile-notice,
body.mlms-wax-page .mlms-proof-status,
body.mlms-wax-page .mlms-portfolio-link.is-attached{background:#0A0A0A!important;border-color:rgba(255,255,255,.24)!important;color:#FFFFFF!important}
body.mlms-wax-page .mlms-portfolio-card.proof-attached{border-color:#FFFFFF!important;box-shadow:0 18px 42px rgba(255,255,255,.10)!important}
body.mlms-wax-page .mlms-portfolio-card.proof-attached:after{background:#FFFFFF!important;color:#000000!important;border-color:#FFFFFF!important}
body.mlms-wax-page .mlms-social-link,
body.mlms-wax-page .mlms-share-copy{background:#FFFFFF!important;color:#000000!important;border-color:#FFFFFF!important}
body.mlms-wax-page .mlms-social-link > span:not(.mlms-social-icon),
body.mlms-wax-page .mlms-share-copy > span:not(.mlms-social-icon){color:#000000!important}
body.mlms-wax-page .mlms-share-copy .mlms-social-icon{color:#000000!important}
body.mlms-wax-page .mlms-role-card{
background:#0A0A0A!important;
border-color:rgba(255,255,255,.28)!important;
color:#FFFFFF!important;
box-shadow:0 12px 32px rgba(0,0,0,.62)!important;
}
body.mlms-wax-page .mlms-role-card:hover,
body.mlms-wax-page .mlms-role-card.selected,
body.mlms-wax-page .mlms-role-card:has(input:checked){
background:#111111!important;
border-color:#FFFFFF!important;
color:#FFFFFF!important;
box-shadow:0 16px 38px rgba(255,255,255,.10)!important;
}
body.mlms-wax-page .mlms-role-card strong,
body.mlms-wax-page .mlms-role-card small,
body.mlms-wax-page .mlms-role-card span{color:#FFFFFF!important}
body.mlms-wax-page .mlms-role-card small{opacity:.82}
body.mlms-wax-page .mlms-role-card .mlms-role-icon{
background:#1A1A1A!important;
color:#FFFFFF!important;
border:1px solid rgba(255,255,255,.34)!important;
box-shadow:none!important;
}
body.mlms-wax-page .mlms-role-card.selected .mlms-role-icon,
body.mlms-wax-page .mlms-role-card:has(input:checked) .mlms-role-icon{
background:#FFFFFF!important;
color:#000000!important;
border:1px solid #FFFFFF!important;
box-shadow:none!important;
}
body.mlms-wax-page .mlms-skill-network:before{
background:linear-gradient(90deg,rgba(255,255,255,.38),rgba(255,255,255,.22),rgba(255,255,255,.10))!important;
box-shadow:0 0 22px rgba(255,255,255,.10)!important;
}
body.mlms-wax-page .mlms-skill{
background:#0A0A0A!important;
border:1px solid rgba(255,255,255,.24)!important;
border-radius:28px!important;
color:#FFFFFF!important;
box-shadow:0 16px 42px rgba(0,0,0,.70)!important;
}
body.mlms-wax-page .mlms-skill.earned,
body.mlms-wax-page .mlms-skill.current,
body.mlms-wax-page .mlms-skill.external{
background:#0A0A0A!important;
border-color:rgba(255,255,255,.30)!important;
}
body.mlms-wax-page .mlms-skill strong,
body.mlms-wax-page .mlms-skill small,
body.mlms-wax-page .mlms-skill .mlms-skill-status{
color:#FFFFFF!important;
text-shadow:none!important;
}
body.mlms-wax-page .mlms-skill small{opacity:.78!important}
body.mlms-wax-page .mlms-skill .mlms-skill-status{
background:#FFFFFF!important;
border:1px solid #FFFFFF!important;
color:#000000!important;
box-shadow:none!important;
}
body.mlms-wax-page .mlms-skill-orb,
body.mlms-wax-page .mlms-skill.earned .mlms-skill-orb,
body.mlms-wax-page .mlms-skill.current .mlms-skill-orb,
body.mlms-wax-page .mlms-skill.external .mlms-skill-orb{
background:#000000!important;
border:2px solid rgba(255,255,255,.55)!important;
box-shadow:0 0 0 8px rgba(255,255,255,.08),0 0 26px rgba(255,255,255,.12)!important;
}
body.mlms-wax-page .mlms-skill-orb span,
body.mlms-wax-page .mlms-skill.earned .mlms-skill-orb span,
body.mlms-wax-page .mlms-skill.current .mlms-skill-orb span,
body.mlms-wax-page .mlms-skill.external .mlms-skill-orb span{
background:#111111!important;
border:1px solid rgba(255,255,255,.32)!important;
color:#FFFFFF!important;
}
body.mlms-wax-page .mlms-skill.earned .mlms-skill-orb span,
body.mlms-wax-page .mlms-skill.current .mlms-skill-orb span{
background:#FFFFFF!important;
color:#000000!important;
}
body.mlms-wax-page .mlms-profile-course-progress .mlms-profile-resume-card{
background:#0A0A0A!important;
border-color:rgba(255,255,255,.24)!important;
color:#FFFFFF!important;
box-shadow:0 18px 44px rgba(0,0,0,.68)!important;
}
body.mlms-wax-page .mlms-profile-course-progress .mlms-profile-resume-card .mlms-kicker,
body.mlms-wax-page .mlms-profile-course-progress .mlms-profile-resume-card h4,
body.mlms-wax-page .mlms-profile-course-progress .mlms-profile-resume-card p{
color:#FFFFFF!important;
}
body.mlms-wax-page .mlms-profile-course-progress .mlms-profile-resume-card .mlms-btn{
background:#FFFFFF!important;
border-color:#FFFFFF!important;
color:#000000!important;
}
@media (prefers-color-scheme: dark){body.mlms-wax-page{color-scheme:dark}}
CSS;
}
function matthew_lms_mvp_role_options(): array {
return [
'Product Manager' => ['PM', 'Shape AI-ready product workflows'],
'Marketing' => ['M', 'Plan campaigns and content systems'],
'Sales' => ['S', 'Improve outreach and pipeline rituals'],
'Operations' => ['O', 'Automate repeatable team processes'],
'Executive' => ['E', 'Lead strategy with AI leverage'],
'Other' => ['+', 'Build a custom learning path'],
];
}
function matthew_lms_mvp_google_oauth_config(): array {
$client_id = defined('MLMS_GOOGLE_CLIENT_ID') ? (string) MLMS_GOOGLE_CLIENT_ID : (string) getenv('MLMS_GOOGLE_CLIENT_ID');
$client_secret = defined('MLMS_GOOGLE_CLIENT_SECRET') ? (string) MLMS_GOOGLE_CLIENT_SECRET : (string) getenv('MLMS_GOOGLE_CLIENT_SECRET');
if ($client_id === '' || $client_secret === '') {
return [];
}
return [
'client_id' => $client_id,
'client_secret' => $client_secret,
// Keep one callback URL for both sign-up and login so Google only needs one authorized redirect URI.
'redirect_uri' => matthew_lms_mvp_public_url('sign-up/'),
];
}
function matthew_lms_mvp_google_oauth_url(string $flow = 'signup', string $redirect_to = ''): string {
$flow = $flow === 'login' ? 'login' : 'signup';
$fallback_path = $flow === 'login' ? 'log-in/' : 'sign-up/';
$anchor = $flow === 'login' ? '#mlms-login-card' : '#mlms-signup-flow';
$redirect = matthew_lms_mvp_clean_redirect_url($redirect_to, 'platform/');
$config = matthew_lms_mvp_google_oauth_config();
if (!$config) {
$notice_url = add_query_arg('mlms_google_notice', '1', matthew_lms_mvp_public_url($fallback_path));
$notice_url = add_query_arg('redirect_to', rawurlencode($redirect), $notice_url);
return $notice_url . $anchor;
}
$state = wp_generate_password(32, false, false);
set_transient('mlms_google_oauth_state_' . $state, [
'flow' => $flow,
'redirect_to' => $redirect,
'issued_at' => time(),
], 10 * MINUTE_IN_SECONDS);
return add_query_arg([
'client_id' => $config['client_id'],
'redirect_uri' => $config['redirect_uri'],
'response_type' => 'code',
'scope' => 'openid email profile',
'state' => $state,
'prompt' => 'select_account',
], 'https://accounts.google.com/o/oauth2/v2/auth');
}
function matthew_lms_mvp_store_google_oauth_message(array $result): string {
$token = wp_generate_password(20, false, false);
set_transient('mlms_google_oauth_msg_' . $token, [
'success' => !empty($result['success']),
'message' => sanitize_text_field((string) ($result['message'] ?? 'Google sign-in could not be completed.')),
], 5 * MINUTE_IN_SECONDS);
return $token;
}
function matthew_lms_mvp_take_google_oauth_message(): ?array {
$token = sanitize_text_field(wp_unslash($_GET['mlms_google_msg'] ?? ''));
if ($token === '') {
return null;
}
$key = 'mlms_google_oauth_msg_' . $token;
$message = get_transient($key);
delete_transient($key);
return is_array($message) ? $message : null;
}
function matthew_lms_mvp_unique_username_from_email(string $email): string {
$base = sanitize_user(current(explode('@', $email)), true);
if ($base === '') {
$base = 'wax_user';
}
$username = $base;
$i = 1;
while (username_exists($username)) {
$i++;
$username = $base . $i;
}
return $username;
}
function matthew_lms_mvp_login_user(int $user_id): void {
$user = get_user_by('id', $user_id);
if (!$user) {
return;
}
wp_set_current_user($user_id);
wp_set_auth_cookie($user_id, true);
do_action('wp_login', $user->user_login, $user);
}
function matthew_lms_mvp_clean_redirect_url(string $raw = '', string $fallback_path = 'platform/'): string {
$fallback = matthew_lms_mvp_public_url($fallback_path);
$raw = trim($raw);
if ($raw === '') {
return $fallback;
}
if (str_starts_with($raw, '/')) {
return matthew_lms_mvp_public_url(ltrim($raw, '/'));
}
$target = esc_url_raw($raw);
$target_host = wp_parse_url($target, PHP_URL_HOST);
if (!$target_host) {
return $fallback;
}
$current_host = sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'] ?? ''));
$fallback_host = wp_parse_url($fallback, PHP_URL_HOST);
$home_host = wp_parse_url(home_url('/'), PHP_URL_HOST);
$allowed_hosts = array_filter(array_unique([$current_host, $fallback_host, $home_host]));
return in_array($target_host, $allowed_hosts, true) ? $target : $fallback;
}
function matthew_lms_mvp_redirect_url(string $fallback_path = 'platform/'): string {
$raw = isset($_REQUEST['redirect_to']) ? (string) wp_unslash($_REQUEST['redirect_to']) : '';
return matthew_lms_mvp_clean_redirect_url($raw, $fallback_path);
}
function matthew_lms_mvp_maybe_handle_login_submission(): void {
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST' || !isset($_POST['mlms_login_nonce'])) {
return;
}
$GLOBALS['mlms_login_result'] = matthew_lms_mvp_handle_login_submission();
if (!empty($GLOBALS['mlms_login_result']['success'])) {
wp_redirect($GLOBALS['mlms_login_result']['redirect']);
exit;
}
}
function matthew_lms_mvp_maybe_handle_signup_onboarding_submission(): void {
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST' || !isset($_POST['mlms_signup_nonce'])) {
return;
}
if (!is_page('sign-up')) {
return;
}
$nonce = sanitize_text_field(wp_unslash($_POST['mlms_signup_nonce'] ?? ''));
if (!wp_verify_nonce($nonce, 'mlms_signup_submit')) {
$GLOBALS['mlms_signup_result'] = ['success' => false, 'message' => 'Your signup session expired. Please refresh and try again.'];
return;
}
$GLOBALS['mlms_signup_result'] = matthew_lms_mvp_handle_signup_onboarding_submission();
if (!empty($GLOBALS['mlms_signup_result']['success'])) {
wp_redirect($GLOBALS['mlms_signup_result']['redirect']);
exit;
}
}
function matthew_lms_mvp_maybe_handle_profile_source_submission(): void {
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST' || !isset($_POST['mlms_profile_source_nonce'])) {
return;
}
if (!is_page('platform')) {
return;
}
$target = matthew_lms_mvp_public_url('platform/') . '#external-skills';
if (!is_user_logged_in()) {
wp_redirect(add_query_arg('redirect_to', rawurlencode($target), matthew_lms_mvp_public_url('log-in/')));
exit;
}
$nonce = sanitize_text_field(wp_unslash($_POST['mlms_profile_source_nonce'] ?? ''));
if (!wp_verify_nonce($nonce, 'mlms_profile_source_submit')) {
wp_redirect(add_query_arg('mlms_profile_notice', 'expired', $target));
exit;
}
$source_url = esc_url_raw(wp_unslash($_POST['mlms_profile_source_url'] ?? ''));
$source_type = sanitize_text_field(wp_unslash($_POST['mlms_profile_source_type'] ?? 'profile'));
$allowed_types = ['github', 'reddit', 'ai-platform', 'linkedin', 'profile'];
if (!in_array($source_type, $allowed_types, true)) {
$source_type = 'profile';
}
if ($source_url === '' || !wp_http_validate_url($source_url)) {
wp_redirect(add_query_arg('mlms_profile_notice', 'invalid', $target));
exit;
}
update_user_meta(get_current_user_id(), '_mlms_profile_link', $source_url);
update_user_meta(get_current_user_id(), '_mlms_external_source_type', $source_type);
wp_redirect(add_query_arg('mlms_profile_notice', 'connected', $target));
exit;
}
function matthew_lms_mvp_handle_login_submission(): array {
$redirect = matthew_lms_mvp_redirect_url('platform/');
$nonce = sanitize_text_field(wp_unslash($_POST['mlms_login_nonce'] ?? ''));
if (!wp_verify_nonce($nonce, 'mlms_login_submit')) {
return ['success' => false, 'message' => 'Your login session expired. Please refresh and try again.'];
}
$identifier = sanitize_text_field(wp_unslash($_POST['mlms_login_identifier'] ?? ''));
$password = (string) wp_unslash($_POST['mlms_login_password'] ?? '');
$remember = !empty($_POST['mlms_login_remember']);
if ($identifier === '' || $password === '') {
return ['success' => false, 'message' => 'Enter your email or username and password to continue.'];
}
$login = $identifier;
if (is_email($identifier)) {
$user = get_user_by('email', $identifier);
if ($user) {
$login = $user->user_login;
}
}
$user = wp_signon([
'user_login' => $login,
'user_password' => $password,
'remember' => $remember,
], is_ssl());
if (is_wp_error($user)) {
return ['success' => false, 'message' => 'That email/username and password combination did not match. Please try again.'];
}
return ['success' => true, 'redirect' => $redirect];
}
function matthew_lms_mvp_maybe_handle_google_oauth_callback(): void {
if (!is_page(['sign-up', 'log-in'])) {
return;
}
if (!isset($_GET['state']) || (!isset($_GET['code']) && !isset($_GET['error']))) {
return;
}
$result = matthew_lms_mvp_handle_google_oauth_callback();
$flow = (($result['flow'] ?? '') === 'login') ? 'login' : 'signup';
if (!empty($result['success'])) {
if ($flow === 'login' && empty($result['new_user'])) {
wp_redirect($result['redirect'] ?? matthew_lms_mvp_public_url('platform/'));
exit;
}
$signup_target = add_query_arg([
'mlms_google_connected' => '1',
'redirect_to' => rawurlencode((string) ($result['redirect'] ?? matthew_lms_mvp_public_url('platform/'))),
], matthew_lms_mvp_public_url('sign-up/'));
wp_redirect($signup_target . '#mlms-signup-flow');
exit;
}
$message_token = matthew_lms_mvp_store_google_oauth_message($result);
$target_path = $flow === 'login' ? 'log-in/' : 'sign-up/';
$target_url = add_query_arg('mlms_google_msg', $message_token, matthew_lms_mvp_public_url($target_path));
if ($flow === 'login' && !empty($result['redirect'])) {
$target_url = add_query_arg('redirect_to', rawurlencode((string) $result['redirect']), $target_url);
}
$target_url .= $flow === 'login' ? '#mlms-login-card' : '#mlms-signup-flow';
wp_redirect($target_url);
exit;
}
function matthew_lms_mvp_handle_google_oauth_callback(): array {
$page_flow = is_page('log-in') ? 'login' : 'signup';
$redirect = matthew_lms_mvp_redirect_url('platform/');
$state = sanitize_text_field(wp_unslash($_GET['state'] ?? ''));
$state_payload = $state !== '' ? get_transient('mlms_google_oauth_state_' . $state) : false;
if ($state === '' || !$state_payload) {
return ['success' => false, 'flow' => $page_flow, 'redirect' => $redirect, 'message' => 'Google sign-in could not be verified. Please try again.'];
}
delete_transient('mlms_google_oauth_state_' . $state);
$flow = is_array($state_payload) && (($state_payload['flow'] ?? '') === 'login') ? 'login' : 'signup';
if (is_array($state_payload) && !empty($state_payload['redirect_to'])) {
$redirect = matthew_lms_mvp_clean_redirect_url((string) $state_payload['redirect_to'], 'platform/');
}
if (isset($_GET['error'])) {
$error = sanitize_text_field(wp_unslash($_GET['error']));
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => $error === 'access_denied' ? 'Google sign-in was cancelled.' : 'Google returned an authorization error. Please try again.'];
}
$config = matthew_lms_mvp_google_oauth_config();
if (!$config) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => 'Google sign-in is designed into this flow, but it still needs Google OAuth credentials before it can connect real Gmail accounts.'];
}
$code = sanitize_text_field(wp_unslash($_GET['code'] ?? ''));
if ($code === '') {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => 'Google did not return an authorization code.'];
}
$token_response = wp_remote_post('https://oauth2.googleapis.com/token', [
'timeout' => 20,
'body' => [
'code' => $code,
'client_id' => $config['client_id'],
'client_secret' => $config['client_secret'],
'redirect_uri' => $config['redirect_uri'],
'grant_type' => 'authorization_code',
],
]);
if (is_wp_error($token_response)) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => $token_response->get_error_message()];
}
if ((int) wp_remote_retrieve_response_code($token_response) >= 400) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => 'Google sign-in could not exchange the authorization code. Check the OAuth redirect URI and credentials.'];
}
$token_data = json_decode(wp_remote_retrieve_body($token_response), true);
if (!is_array($token_data) || empty($token_data['access_token'])) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => 'Google sign-in did not return an access token. Check the OAuth redirect URI and credentials.'];
}
$profile_response = wp_remote_get('https://www.googleapis.com/oauth2/v3/userinfo', [
'timeout' => 20,
'headers' => ['Authorization' => 'Bearer ' . $token_data['access_token']],
]);
if (is_wp_error($profile_response)) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => $profile_response->get_error_message()];
}
$profile = json_decode(wp_remote_retrieve_body($profile_response), true);
if (!is_array($profile)) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => 'Google did not return a readable profile.'];
}
$email = sanitize_email($profile['email'] ?? '');
if ($email === '') {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => 'Google did not provide an email address.'];
}
if (isset($profile['email_verified']) && !$profile['email_verified']) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => 'Google did not confirm that this email address is verified.'];
}
$email_name = sanitize_text_field(strstr($email, '@', true) ?: $email);
$name = sanitize_text_field($profile['name'] ?? $email_name);
$user_id = email_exists($email);
$created_user = false;
if (!$user_id) {
$user_id = wp_insert_user([
'user_login' => matthew_lms_mvp_unique_username_from_email($email),
'user_pass' => wp_generate_password(32, true, true),
'user_email' => $email,
'display_name' => $name,
'role' => 'subscriber',
]);
if (is_wp_error($user_id)) {
return ['success' => false, 'flow' => $flow, 'redirect' => $redirect, 'message' => $user_id->get_error_message()];
}
$created_user = true;
}
if (!get_user_meta((int) $user_id, '_mlms_signup_auth_method', true)) {
update_user_meta((int) $user_id, '_mlms_signup_auth_method', 'google');
}
update_user_meta((int) $user_id, '_mlms_last_auth_method', 'google');
if (!empty($profile['picture'])) {
update_user_meta((int) $user_id, '_mlms_google_picture', esc_url_raw($profile['picture']));
}
matthew_lms_mvp_login_user((int) $user_id);
$message = $flow === 'login'
? ($created_user ? 'Google account created. Finish two onboarding questions to personalize Unggul.' : 'Signed in with Google.')
: 'Google connected. Two quick questions and Unggul will build your starter profile.';
return ['success' => true, 'flow' => $flow, 'redirect' => $redirect, 'new_user' => $created_user, 'message' => $message, 'email' => $email];
}
function matthew_lms_mvp_handle_signup_onboarding_submission(): array {
$role_options = matthew_lms_mvp_role_options();
$role = sanitize_text_field(wp_unslash($_POST['mlms_signup_role'] ?? ''));
$goal = sanitize_textarea_field(wp_unslash($_POST['mlms_signup_goal'] ?? ''));
$link = esc_url_raw(wp_unslash($_POST['mlms_profile_link'] ?? ''));
if (!array_key_exists($role, $role_options)) {
return ['success' => false, 'message' => 'Choose the role that best matches how you work.'];
}
if ($goal === '') {
return ['success' => false, 'message' => 'Tell Unggul what you want to learn first.'];
}
if (is_user_logged_in()) {
$user_id = get_current_user_id();
$user = get_userdata($user_id);
$email = $user ? $user->user_email : '';
} else {
$email = sanitize_email(wp_unslash($_POST['mlms_signup_email'] ?? ''));
$password = (string) wp_unslash($_POST['mlms_signup_password'] ?? '');
if ($email === '' || !is_email($email)) {
return ['success' => false, 'message' => 'Enter a valid email address.'];
}
if (strlen($password) < 8) {
return ['success' => false, 'message' => 'Use a password with at least 8 characters.'];
}
if (email_exists($email)) {
return ['success' => false, 'message' => 'An account already exists for this email. Use Login, then return here to finish onboarding.'];
}
$user_id = wp_insert_user([
'user_login' => matthew_lms_mvp_unique_username_from_email($email),
'user_pass' => $password,
'user_email' => $email,
'display_name' => current(explode('@', $email)),
'role' => 'subscriber',
]);
if (is_wp_error($user_id)) {
return ['success' => false, 'message' => $user_id->get_error_message()];
}
update_user_meta((int) $user_id, '_mlms_signup_auth_method', 'email');
matthew_lms_mvp_login_user((int) $user_id);
}
$profile = matthew_lms_mvp_generate_profile_bootstrap($email, $role, $goal, $link);
update_user_meta((int) $user_id, '_mlms_functional_role', $role);
update_user_meta((int) $user_id, '_mlms_learning_goal', $goal);
update_user_meta((int) $user_id, '_mlms_profile_link', $link);
update_user_meta((int) $user_id, '_mlms_ai_profile_bootstrap', $profile);
$post_id = wp_insert_post([
'post_type' => 'mlms_onboard',
'post_status' => 'private',
'post_author' => (int) $user_id,
'post_title' => sprintf('%s — %s onboarding', $email, $role),
'post_content' => "Email: $email\nRole: $role\nLearning goals:\n$goal\nLinked account: $link\n\nAI profile bootstrap:\n$profile",
], true);
if (!is_wp_error($post_id)) {
foreach (['email' => $email, 'role' => $role, 'goal' => $goal, 'profile_link' => $link, 'ai_profile' => $profile] as $key => $value) {
update_post_meta($post_id, '_mlms_' . $key, $value);
}
}
$redirect = matthew_lms_mvp_redirect_url('platform/');
return ['success' => true, 'email' => $email, 'role' => $role, 'goal' => $goal, 'link' => $link, 'profile' => $profile, 'post_id' => is_wp_error($post_id) ? 0 : (int) $post_id, 'redirect' => $redirect];
}
function matthew_lms_mvp_generate_profile_bootstrap(string $email, string $role, string $goal, string $link): string {
$fallback = "Starter Unggul profile\n\nLearning path: Start with AI workflow fluency, then map one real weekly task into a repeatable prompt-and-review system.\n\nFirst wins:\n1. Pick one high-friction task connected to your goal.\n2. Build a simple AI-assisted draft, review, and handoff loop.\n3. Save the result as your first portfolio artifact.";
$api = dirname(__DIR__, 3) . '/ai/LocalAIApi.php';
if (!file_exists($api)) { return $fallback; }
require_once $api;
if (!class_exists('LocalAIApi')) { return $fallback; }
$link_context = $link ? "Linked account/profile URL supplied by user: $link." : 'No linked account was supplied.';
$prompt = "Create a concise starter profile for a new Unggul learner. Email: $email. Functional role: $role. Learning goals: $goal. $link_context Include: 1) one-line professional headline, 2) a recommended first learning path, 3) three practical starter actions. Sound friendly, premium, and specific. Keep under 180 words.";
$resp = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => 'You are Unggul, an AI onboarding assistant that turns short sign-up answers into useful AI-native learning profiles.'],
['role' => 'user', 'content' => $prompt],
],
], ['poll_interval' => 2, 'poll_timeout' => 30, 'timeout' => 20]);
if (!empty($resp['success'])) {
$text = LocalAIApi::extractText($resp);
if (is_string($text) && trim($text) !== '') { return trim($text); }
}
return $fallback;
}
function matthew_lms_mvp_render_signup_success(array $result): string {
ob_start();
?>
<div class="mlms-page"><section class="mlms-section mlms-onboarding-hero"><div class="mlms-shell">
<div class="mlms-onboarding-success">
<div class="mlms-profile-summary-card">
<p class="mlms-kicker" style="color:#C9C4FF">Profile bootstrap complete</p>
<h2>Your Unggul profile is ready.</h2>
<p>Unggul used your sign-up answers<?php echo !empty($result['link']) ? ' and linked profile URL' : ''; ?> to create a starter learning profile you can refine over time.</p>
<div class="mlms-profile-meta">
<span><b>Email</b> <?php echo esc_html($result['email']); ?></span>
<span><b>Role</b> <?php echo esc_html($result['role']); ?></span>
</div>
<div class="mlms-actions"><a class="mlms-btn mlms-btn-primary" href="<?php echo esc_url($result['redirect'] ?? matthew_lms_mvp_public_url('platform/')); ?>">Go to Profile →</a><a class="mlms-btn mlms-btn-secondary" href="<?php echo esc_url(matthew_lms_mvp_public_url('platform/course/')); ?>">Start Learning</a></div>
</div>
<div class="mlms-ai-profile">
<div class="mlms-ai-profile-orb" aria-hidden="true">
<span class="mlms-ai-profile-spark">✦</span>
<span class="mlms-ai-profile-node">⚡</span>
<span class="mlms-ai-profile-node">✍</span>
<span class="mlms-ai-profile-node">✓</span>
</div>
<div class="mlms-ai-profile-head">
<span class="mlms-ai-profile-badge">AI draft</span>
<h3>AI-generated starter profile</h3>
<p>Your first Unggul operating map — shaped into a path, quick wins, and a portfolio-ready proof point.</p>
</div>
<div class="mlms-ai-profile-chips" aria-label="Profile ingredients">
<span><b>01</b> Path</span>
<span><b>02</b> Workflows</span>
<span><b>03</b> Proof</span>
</div>
<div class="mlms-ai-profile-copy"><?php echo nl2br(esc_html($result['profile'])); ?></div>
</div>
</div>
</div></section></div>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_js(): string {
return <<<'JS'
(function(){
function initWaxOnboarding(){
var root=document.getElementById('mlms-signup-flow');
if(!root){return;}
var form=document.getElementById('mlms-onboarding-form');
var panels=[].slice.call(root.querySelectorAll('.mlms-onboarding-panel'));
var bars=[].slice.call(root.querySelectorAll('.mlms-onboarding-progress span'));
if(!form || !panels.length){return;}
var current=Math.max(0, Math.min(panels.length-1, (parseInt(root.getAttribute('data-start-step')||'1',10)-1)));
function show(index){
current=index;
panels.forEach(function(panel,i){panel.classList.toggle('active',i===current);});
bars.forEach(function(bar,i){bar.classList.toggle('active',i<=current);});
}
function syncRoles(){
root.querySelectorAll('.mlms-role-card').forEach(function(card){
var input=card.querySelector('input');
card.classList.toggle('selected', !!(input && input.checked));
});
}
function validPanel(panel){
var roleInputs=[].slice.call(panel.querySelectorAll('input[name="mlms_signup_role"]'));
if(roleInputs.length && !roleInputs.some(function(input){return input.checked;})){
roleInputs[0].setCustomValidity('Choose a role to continue.');
roleInputs[0].reportValidity();
roleInputs[0].setCustomValidity('');
return false;
}
var fields=[].slice.call(panel.querySelectorAll('input, textarea')).filter(function(field){return field.offsetParent!==null && field.type!=='radio';});
for(var i=0;i<fields.length;i++){
if(!fields[i].checkValidity()){
fields[i].reportValidity();
return false;
}
}
return true;
}
root.querySelectorAll('[data-mlms-next]').forEach(function(button){
button.addEventListener('click',function(){
if(validPanel(panels[current])){show(Math.min(current+1,panels.length-1));}
});
});
root.querySelectorAll('[data-mlms-prev]').forEach(function(button){
button.addEventListener('click',function(){show(Math.max(current-1,0));});
});
root.querySelectorAll('.mlms-role-card input').forEach(function(input){input.addEventListener('change',syncRoles);});
form.addEventListener('submit',function(event){
if(!validPanel(panels[current])){event.preventDefault();return;}
var loading=root.querySelector('.mlms-building-profile');
if(loading){loading.classList.add('active');}
form.querySelectorAll('button').forEach(function(button){button.disabled=true;});
});
syncRoles();
show(current);
}
function mlmsAppendTutorMessage(box, text, who){
if(!box){return null;}
var msg=document.createElement('div');
msg.className='mlms-tutor-msg '+(who||'bot');
msg.textContent=text;
box.appendChild(msg);
box.scrollTop=box.scrollHeight;
return msg;
}
function mlmsPostTutor(formData, ajaxUrl){
return fetch(ajaxUrl, {method:'POST', credentials:'same-origin', body:formData})
.then(function(resp){return resp.json();})
.then(function(json){
if(json && json.success && json.data){return json.data;}
var message=(json && json.data && json.data.message) ? json.data.message : 'The Unggul tutor could not respond. Try again with a shorter question.';
throw new Error(message);
});
}
function initWaxCourseDashboard(){
var dashboard=document.querySelector('[data-mlms-course-dashboard]');
if(!dashboard){return;}
var storageKey='waxCourseProgress:v2';
var ajaxUrl=dashboard.getAttribute('data-ajax-url')||'';
var progressNonce=dashboard.getAttribute('data-progress-nonce')||'';
var defaults={phase:1,lesson:2,step:2,percent:42,status:'in_progress',phase_label:'Phase 1 · AI Workflow Fluency',lesson_title:'Build a practical AI workflow brief',last_block:'Lesson view: role-specific workflow example',next_action:'Continue lesson',quiz_score:0,quiz_total:4,quiz_completed:false,skill_points:0,updated_at:''};
function clamp(num,min,max){num=parseInt(num,10);if(isNaN(num)){num=min;}return Math.max(min,Math.min(max,num));}
function normalizeProgress(raw){
var p=Object.assign({},defaults,raw||{});
p.phase=clamp(p.phase,1,3);
p.lesson=clamp(p.lesson,1,5);
p.step=clamp(p.step,1,5);
p.percent=clamp(p.percent,0,100);
p.quiz_score=clamp(p.quiz_score,0,10);
p.quiz_total=clamp(p.quiz_total,1,10);
p.skill_points=clamp(p.skill_points,0,999);
p.quiz_completed=!!(p.quiz_completed===true || p.quiz_completed==='1' || p.quiz_completed==='true' || p.quiz_completed===1);
['status','phase_label','lesson_title','last_block','next_action','updated_at'].forEach(function(key){p[key]=String(p[key]||defaults[key]||'');});
return p;
}
function parseJsonAttribute(el,attr){
try{return JSON.parse(el.getAttribute(attr)||'{}');}catch(error){return {};}
}
function loadProgress(){
var initial=normalizeProgress(parseJsonAttribute(dashboard,'data-initial-progress'));
var stored=null;
try{stored=JSON.parse(window.localStorage.getItem(storageKey)||'null');}catch(error){stored=null;}
if(stored && typeof stored==='object'){
var storedProgress=normalizeProgress(stored);
var storedTime=Date.parse(storedProgress.updated_at||'');
var initialTime=Date.parse(initial.updated_at||'');
if(!initial.updated_at || (storedTime && (!initialTime || storedTime>initialTime))){return storedProgress;}
}
return initial;
}
var progress=loadProgress();
function statusLabel(status){
if(status==='lesson_complete'){return 'Lesson complete';}
if(status==='quiz_completed'){return 'Quiz complete';}
if(status==='course_launched'){return 'Course launched';}
if(status==='not_started'){return 'Not started';}
return 'In progress';
}
function setText(root,selector,value){
if(!root){return;}
root.querySelectorAll(selector).forEach(function(el){el.textContent=value;});
}
function setWidth(root,selector,value){
if(!root){return;}
root.querySelectorAll(selector).forEach(function(el){el.style.width=value+'%';});
}
function quizLabel(p){
return p.quiz_completed ? (p.quiz_score+'/'+p.quiz_total) : 'Not taken';
}
function applyProgress(nextProgress){
progress=normalizeProgress(nextProgress);
dashboard.classList.toggle('is-lesson-complete', progress.status==='lesson_complete');
setText(dashboard,'[data-mlms-course-percent]',progress.percent+'%');
setText(dashboard,'[data-mlms-course-step]','Step '+progress.step+' of 5');
setText(dashboard,'[data-mlms-course-status]',statusLabel(progress.status));
setText(dashboard,'[data-mlms-leftoff-title]',progress.lesson_title);
setText(dashboard,'[data-mlms-leftoff-meta]',progress.phase_label+' · '+progress.last_block);
setWidth(dashboard,'[data-mlms-course-progress-fill]',progress.percent);
setWidth(dashboard,'.mlms-lesson-progress-track span',progress.percent);
var nodeState=progress.status==='lesson_complete'?'earned':'current';
dashboard.querySelectorAll('[data-mlms-skill-node="phase-1"]').forEach(function(node){
node.classList.toggle('earned',nodeState==='earned');
node.classList.toggle('current',nodeState!=='earned');
});
var profile=document.querySelector('[data-mlms-profile-progress]');
if(profile){
setText(profile,'[data-mlms-profile-percent]',progress.percent+'%');
setText(profile,'[data-mlms-profile-phase]',progress.phase_label);
setText(profile,'[data-mlms-profile-leftoff]',progress.lesson_title);
setText(profile,'[data-mlms-profile-last-block]',progress.last_block);
setText(profile,'[data-mlms-profile-next-action]',progress.next_action);
setText(profile,'[data-mlms-profile-step]','Step '+progress.step+'/5');
setText(profile,'[data-mlms-profile-quiz]',quizLabel(progress));
setText(profile,'[data-mlms-profile-status]',statusLabel(progress.status));
setWidth(profile,'[data-mlms-profile-progress-fill]',progress.percent);
}
}
function persistProgress(patch){
progress=normalizeProgress(Object.assign({},progress,patch||{}, {updated_at:(new Date()).toISOString()}));
applyProgress(progress);
try{window.localStorage.setItem(storageKey,JSON.stringify(progress));}catch(error){}
if(!ajaxUrl || !progressNonce){return Promise.resolve(progress);}
var data=new FormData();
data.append('action','mlms_course_progress');
data.append('nonce',progressNonce);
['phase','lesson','step','percent','status','phase_label','lesson_title','last_block','next_action','quiz_score','quiz_total','skill_points','updated_at'].forEach(function(key){data.append(key,progress[key]);});
data.append('quiz_completed',progress.quiz_completed?'1':'0');
return fetch(ajaxUrl,{method:'POST',credentials:'same-origin',body:data})
.then(function(resp){return resp.json();})
.then(function(json){
if(json && json.success && json.data && json.data.progress){
progress=normalizeProgress(json.data.progress);
applyProgress(progress);
try{window.localStorage.setItem(storageKey,JSON.stringify(progress));}catch(error){}
}
return progress;
})
.catch(function(){return progress;});
}
applyProgress(progress);
var courseRoute=dashboard.getAttribute('data-course-route')||'';
if(courseRoute==='lesson' && progress.status!=='quiz_completed' && progress.status!=='lesson_complete'){
persistProgress({status:'course_launched',percent:Math.max(progress.percent,45),last_block:'Lesson page opened',next_action:'Take quiz'});
}
if(courseRoute==='quiz' && !progress.quiz_completed && progress.status!=='lesson_complete'){
persistProgress({status:'in_progress',percent:Math.max(progress.percent,58),step:3,last_block:'Quiz page opened',next_action:'Submit quiz'});
}
document.querySelectorAll('[data-mlms-launch-course]').forEach(function(button){
button.addEventListener('click',function(event){
var href=button.getAttribute('href')||'';
var isHash=href.indexOf('#')===0;
var isButton=button.tagName.toLowerCase()==='button';
if(isHash || isButton){event.preventDefault();}
var workspace=dashboard.querySelector('[data-mlms-course-workspace]')||dashboard.querySelector('.mlms-lesson-grid')||dashboard;
if(workspace && workspace.classList){workspace.classList.add('launched');}
if(progress.status!=='quiz_completed' && progress.status!=='lesson_complete'){
persistProgress({status:'course_launched',percent:Math.max(progress.percent,45),last_block:isHash?'Course workspace opened':'Lesson page launched',next_action:'Continue lesson'});
}
if((isHash || isButton) && workspace && workspace.scrollIntoView){setTimeout(function(){workspace.scrollIntoView({behavior:'smooth',block:'start'});},40);}
});
});
dashboard.querySelectorAll('[data-mlms-open-quiz]').forEach(function(button){
button.addEventListener('click',function(event){
var href=button.getAttribute('href')||'';
var isHash=href.indexOf('#')===0;
if(isHash || button.tagName.toLowerCase()==='button'){event.preventDefault();}
var quizBlock=dashboard.querySelector('#mlms-quiz-block');
if(!progress.quiz_completed && progress.status!=='lesson_complete'){
persistProgress({status:'in_progress',percent:Math.max(progress.percent,58),step:3,last_block:isHash?'Quiz block opened':'Quiz page opened',next_action:'Submit quiz'});
}
if(isHash && quizBlock){quizBlock.scrollIntoView({behavior:'smooth',block:'start'});}
});
});
var quiz=dashboard.querySelector('[data-mlms-quiz]');
if(quiz){
var quizForm=quiz.querySelector('[data-mlms-quiz-form]');
var quizResult=quiz.querySelector('[data-mlms-quiz-result]');
var quizStatus=quiz.querySelector('[data-mlms-quiz-status]');
if(quizForm){
quizForm.addEventListener('submit',function(event){
event.preventDefault();
var questions=[].slice.call(quiz.querySelectorAll('[data-quiz-question]'));
var total=questions.length;
var score=0;
var unanswered=0;
questions.forEach(function(question){
question.classList.remove('is-correct','is-wrong');
var selected=question.querySelector('input[type="radio"]:checked');
if(!selected){unanswered++;return;}
var isCorrect=selected.getAttribute('data-correct')==='1';
if(isCorrect){score++;}
question.classList.add(isCorrect?'is-correct':'is-wrong');
});
if(unanswered){
if(quizResult){quizResult.className='mlms-quiz-result active error';quizResult.innerHTML='<strong>Almost there.</strong>Answer every question before Unggul calculates your result.';}
if(quizStatus){quizStatus.textContent=unanswered+' question'+(unanswered===1?'':'s')+' still need an answer.';}
return;
}
var passed=score>=Math.ceil(total*0.75);
if(quizResult){
quizResult.className='mlms-quiz-result active '+(passed?'pass':'retry');
quizResult.innerHTML='<strong>Result: '+score+'/'+total+' correct.</strong>'+(passed?'You passed. The build task is unlocked; continue by submitting your role-specific workflow brief.':'Review the highlighted questions, then resubmit when you are ready. Aim for at least 75% before the build task.');
}
if(quizStatus){quizStatus.textContent=passed?'Quiz passed — build task unlocked.':'Quiz submitted — review and retry.';}
persistProgress({
status:'quiz_completed',
percent:passed?Math.max(progress.percent,72):Math.max(progress.percent,60),
step:passed?4:3,
last_block:passed?'Quiz passed — build task unlocked':'Quiz needs review',
next_action:passed?'Continue to build task':'Retake quiz',
quiz_score:score,
quiz_total:total,
quiz_completed:true
});
});
}
}
var buildForm=dashboard.querySelector('[data-mlms-build-form]');
if(buildForm){
buildForm.addEventListener('submit', function(event){
event.preventDefault();
var status=buildForm.querySelector('[data-mlms-build-status]');
var completion=dashboard.querySelector('[data-mlms-completion-card]');
var feedback=completion ? completion.querySelector('[data-mlms-validation-feedback]') : null;
var submit=buildForm.querySelector('button[type="submit"]');
var checks=[].slice.call(buildForm.querySelectorAll('input[type="checkbox"]'));
if(checks.length && !checks.every(function(input){return input.checked;})){
if(status){status.textContent='Complete each deliverable checklist item before submitting.';}
return;
}
if(status){status.textContent='AI validation is reviewing your build task…';}
if(submit){submit.disabled=true;}
var data=new FormData(buildForm);
data.append('action','mlms_ai_tutor');
data.append('mode','build_validation');
data.append('nonce',dashboard.getAttribute('data-tutor-nonce')||'');
data.append('lesson_context',dashboard.getAttribute('data-lesson-context')||'Unggul lesson');
function markComplete(message){
if(feedback){feedback.textContent=message || 'Validated: your build task is clear enough to earn this lesson skill point.';}
if(completion){completion.classList.add('active');}
if(status){status.textContent='Skill point earned. Lesson completion confirmed.';}
persistProgress({status:'lesson_complete',percent:100,step:5,last_block:'Build task submitted and validated',next_action:'Continue to next lesson',skill_points:20});
}
mlmsPostTutor(data, dashboard.getAttribute('data-ajax-url')||'')
.then(function(payload){markComplete(payload.message);})
.catch(function(error){
markComplete('Offline validation fallback: your checklist is complete. Review the deliverable once more for clarity, assumptions, and next action.');
if(status && error && error.message){status.textContent=error.message;}
})
.finally(function(){if(submit){submit.disabled=false;}});
});
}
}
function initWaxTutor(){
var root=document.querySelector('[data-mlms-tutor-root]');
if(!root){return;}
var panel=root.querySelector('.mlms-tutor-panel');
var launcher=root.querySelector('.mlms-tutor-launcher');
var close=root.querySelector('.mlms-tutor-close');
var form=root.querySelector('[data-mlms-tutor-form]');
var messages=root.querySelector('.mlms-tutor-messages');
var textarea=root.querySelector('textarea[name="question"]');
var modeInput=root.querySelector('input[name="mode"]');
function openPanel(){ if(panel){panel.classList.add('open'); panel.setAttribute('aria-hidden','false');} if(textarea){setTimeout(function(){textarea.focus();},60);} }
function closePanel(){ if(panel){panel.classList.remove('open'); panel.setAttribute('aria-hidden','true');} }
if(launcher){launcher.addEventListener('click', openPanel);}
if(close){close.addEventListener('click', closePanel);}
root.querySelectorAll('[data-tutor-prompt]').forEach(function(chip){
chip.addEventListener('click', function(){
if(textarea){textarea.value=chip.getAttribute('data-tutor-prompt')||'';}
if(modeInput){modeInput.value=chip.getAttribute('data-tutor-mode')||'tutor';}
root.querySelectorAll('.mlms-tutor-chip').forEach(function(btn){btn.classList.toggle('active', btn===chip);});
openPanel();
});
});
if(form){
form.addEventListener('submit', function(event){
event.preventDefault();
var question=(textarea && textarea.value.trim()) || '';
var fileInput=form.querySelector('input[type="file"]');
if(!question && !(fileInput && fileInput.files && fileInput.files.length)){
if(textarea){textarea.focus();}
return;
}
mlmsAppendTutorMessage(messages, question || 'Uploaded a screenshot for roadblock help.', 'user');
var pending=mlmsAppendTutorMessage(messages, 'Unggul Tutor is thinking…', 'bot');
var data=new FormData(form);
data.append('action','mlms_ai_tutor');
data.append('lesson_context',root.getAttribute('data-lesson-context')||'Unggul course lesson');
mlmsPostTutor(data, root.getAttribute('data-ajax-url')||'')
.then(function(payload){
if(pending){pending.textContent=payload.message || 'I could not produce guidance. Try asking again with more detail.';}
if(textarea){textarea.value='';}
if(fileInput){fileInput.value='';}
})
.catch(function(error){
if(pending){pending.textContent=error.message || 'The tutor is temporarily unavailable. Paste the exact error text and try again.';}
});
});
}
}
function initWaxProofModal(){
var modal=document.getElementById('mlms-proof-modal');
var triggers=[].slice.call(document.querySelectorAll('[data-proof-trigger]'));
if(!modal || !triggers.length){return;}
var form=modal.querySelector('[data-proof-form]');
var artifactInput=modal.querySelector('#mlms-proof-artifact');
var title=modal.querySelector('#mlms-proof-title');
var copy=modal.querySelector('#mlms-proof-copy');
var status=modal.querySelector('[data-proof-status]');
var url=modal.querySelector('#mlms-proof-url');
var notes=modal.querySelector('#mlms-proof-notes');
var file=modal.querySelector('#mlms-proof-file');
var storageKey='waxPortfolioProofs:v1';
var activeTrigger=null;
function readProofs(){
try{return JSON.parse(window.localStorage.getItem(storageKey)||'{}')||{};}catch(error){return {};}
}
function writeProofs(proofs){
try{window.localStorage.setItem(storageKey, JSON.stringify(proofs));}catch(error){}
}
function markAttached(trigger, proof){
if(!trigger){return;}
var card=trigger.closest('.mlms-portfolio-card');
if(card){card.classList.add('proof-attached');}
trigger.classList.add('is-attached');
trigger.textContent='Edit proof →';
if(proof && proof.url){trigger.setAttribute('data-proof-url', proof.url);}
}
function restoreProofState(){
var proofs=readProofs();
triggers.forEach(function(trigger){
var artifact=trigger.getAttribute('data-artifact')||'';
if(artifact && proofs[artifact]){markAttached(trigger, proofs[artifact]);}
});
}
function openModal(trigger){
activeTrigger=trigger;
var artifact=trigger.getAttribute('data-artifact')||'Portfolio artifact';
var proofs=readProofs();
var saved=proofs[artifact]||{};
if(artifactInput){artifactInput.value=artifact;}
if(title){title.textContent='Attach proof for '+artifact;}
if(copy){copy.textContent='Add a public source, short evidence notes, or an optional local reference for this portfolio artifact.';}
if(url){url.value=saved.url||'';}
if(notes){notes.value=saved.notes||'';}
if(file){file.value='';}
if(status){status.textContent=saved.url ? 'Existing proof loaded. Update it or save again.' : '';}
modal.classList.add('open');
modal.setAttribute('aria-hidden','false');
document.body.style.overflow='hidden';
setTimeout(function(){if(url){url.focus();}},80);
}
function closeModal(){
var returnFocus=activeTrigger;
if(returnFocus){try{returnFocus.focus({preventScroll:true});}catch(error){returnFocus.focus();}}
modal.classList.remove('open');
modal.setAttribute('aria-hidden','true');
document.body.style.overflow='';
if(status){status.textContent='';}
}
triggers.forEach(function(trigger){
trigger.addEventListener('click',function(event){
event.preventDefault();
openModal(trigger);
});
});
modal.querySelectorAll('[data-proof-close]').forEach(function(close){
close.addEventListener('click',function(event){event.preventDefault();closeModal();});
});
document.addEventListener('keydown',function(event){
if(event.key==='Escape' && modal.classList.contains('open')){closeModal();}
});
if(form){
form.addEventListener('submit',function(event){
event.preventDefault();
if(!form.checkValidity()){
form.reportValidity();
return;
}
var artifact=(artifactInput && artifactInput.value) || (activeTrigger && activeTrigger.getAttribute('data-artifact')) || 'Portfolio artifact';
var proofs=readProofs();
proofs[artifact]={
type:(modal.querySelector('#mlms-proof-type')||{}).value||'',
url:(url && url.value.trim())||'',
notes:(notes && notes.value.trim())||'',
file_name:(file && file.files && file.files[0]) ? file.files[0].name : '',
saved_at:new Date().toISOString()
};
writeProofs(proofs);
markAttached(activeTrigger, proofs[artifact]);
if(status){status.textContent='Proof saved for '+artifact+'.';}
setTimeout(closeModal,900);
});
}
restoreProofState();
}
function initWaxCoachWizard(){
var form=document.querySelector('[data-mlms-coach-wizard]');
if(!form){return;}
var panels=[].slice.call(form.querySelectorAll('[data-mlms-coach-panel]'));
var steps=[].slice.call(form.querySelectorAll('[data-mlms-coach-jump]'));
var current=0;
function visibleFields(panel){
return [].slice.call(panel.querySelectorAll('input, textarea, select')).filter(function(field){return field.type!=='hidden' && field.offsetParent!==null;});
}
function validPanel(panel){
var fields=visibleFields(panel);
for(var i=0;i<fields.length;i++){
if(!fields[i].checkValidity()){
fields[i].reportValidity();
return false;
}
}
return true;
}
function updateReview(){
form.querySelectorAll('[data-mlms-review]').forEach(function(item){
var field=form.querySelector('[name="'+item.getAttribute('data-mlms-review')+'"]');
var value=field && field.value ? field.value.trim() : '';
item.textContent=value || 'Not added yet';
});
}
function show(index){
index=Math.max(0,Math.min(panels.length-1,index));
current=index;
panels.forEach(function(panel,i){panel.classList.toggle('active',i===current);});
steps.forEach(function(step,i){
step.classList.toggle('active',i===current);
step.classList.toggle('complete',i<current);
step.setAttribute('aria-current',i===current ? 'step' : 'false');
});
updateReview();
}
form.querySelectorAll('[data-mlms-coach-next]').forEach(function(button){
button.addEventListener('click',function(){
if(validPanel(panels[current])){show(current+1);}
});
});
form.querySelectorAll('[data-mlms-coach-prev]').forEach(function(button){
button.addEventListener('click',function(){show(current-1);});
});
steps.forEach(function(step){
step.addEventListener('click',function(){
var target=parseInt(step.getAttribute('data-mlms-coach-jump')||'0',10);
if(target<=current || validPanel(panels[current])){show(target);}
});
});
form.querySelectorAll('input, textarea').forEach(function(field){field.addEventListener('input',updateReview);});
form.addEventListener('submit',function(event){
for(var i=0;i<panels.length;i++){
if(!validPanel(panels[i])){
event.preventDefault();
show(i);
return;
}
}
var loading=form.querySelector('.mlms-coach-loading');
if(loading){loading.classList.add('active');}
form.querySelectorAll('button').forEach(function(button){button.disabled=true;});
});
show(0);
}
function initAll(){
initWaxOnboarding();
initWaxCoachWizard();
initWaxCourseDashboard();
initWaxTutor();
initWaxProofModal();
}
if(document.readyState==='loading'){
document.addEventListener('DOMContentLoaded', initAll);
} else {
initAll();
}
})();
JS;
}
function matthew_lms_mvp_profile_initials(string $name, string $email = ''): string {
$source = trim($name) !== '' ? $name : $email;
$parts = preg_split('/\s+/', trim($source));
if (!$parts || trim($source) === '') {
return 'WX';
}
$first = strtoupper(substr((string) $parts[0], 0, 1));
$last = count($parts) > 1 ? strtoupper(substr((string) end($parts), 0, 1)) : '';
return $first . ($last ?: '');
}
function matthew_lms_mvp_source_label(string $source_type, string $source_url): string {
if ($source_type === 'github' || str_contains($source_url, 'github.com')) {
return 'GitHub';
}
if ($source_type === 'reddit' || str_contains($source_url, 'reddit.com')) {
return 'Reddit';
}
if ($source_type === 'ai-platform') {
return 'AI platform';
}
if ($source_type === 'linkedin' || str_contains($source_url, 'linkedin.com')) {
return 'LinkedIn';
}
return 'External profile';
}
function matthew_lms_mvp_profile_context(): array {
$logged_in = is_user_logged_in();
$user = $logged_in ? wp_get_current_user() : null;
$user_id = ($logged_in && $user) ? (int) $user->ID : 0;
$email = $user ? (string) $user->user_email : '';
$display_name = $user ? trim((string) $user->display_name) : '';
if ($display_name === '' || $display_name === $email) {
$display_name = $email ? current(explode('@', $email)) : 'Alex Morgan';
}
$role = $user_id ? (string) get_user_meta($user_id, '_mlms_functional_role', true) : '';
$goal = $user_id ? (string) get_user_meta($user_id, '_mlms_learning_goal', true) : '';
$profile = $user_id ? (string) get_user_meta($user_id, '_mlms_ai_profile_bootstrap', true) : '';
$profile_link = $user_id ? (string) get_user_meta($user_id, '_mlms_profile_link', true) : '';
$source_type = $user_id ? (string) get_user_meta($user_id, '_mlms_external_source_type', true) : '';
$photo = $user_id ? (string) get_user_meta($user_id, '_mlms_google_picture', true) : '';
if (!$photo && $user_id) {
$photo = get_avatar_url($user_id, ['size' => 192]);
}
$role = $role ?: 'Marketing Director';
$goal = $goal ?: 'campaign automation, better briefs, and faster experimentation';
$bio = $profile ? wp_trim_words(wp_strip_all_tags($profile), 34, '…') : 'AI-native growth operator building practical workflows with Unggul. Focus: ' . $goal . '.';
return [
'logged_in' => $logged_in,
'name' => $display_name,
'email' => $email ?: 'preview@wax.example',
'role' => $role,
'goal' => $goal,
'bio' => $bio,
'photo' => $photo,
'initials' => matthew_lms_mvp_profile_initials($display_name, $email),
'profile_link' => $profile_link,
'source_type' => $source_type ?: 'profile',
'source_label' => $profile_link ? matthew_lms_mvp_source_label($source_type ?: 'profile', $profile_link) : 'Manual connection',
'course_progress' => matthew_lms_mvp_get_course_progress($user_id),
];
}
function matthew_lms_mvp_course_progress_defaults(): array {
return [
'phase' => 1,
'lesson' => 2,
'step' => 2,
'percent' => 42,
'status' => 'in_progress',
'phase_label' => 'Phase 1 · AI Workflow Fluency',
'lesson_title' => 'Build a practical AI workflow brief',
'last_block' => 'Lesson view: role-specific workflow example',
'next_action' => 'Continue lesson',
'quiz_score' => 0,
'quiz_total' => 4,
'quiz_completed' => false,
'skill_points' => 0,
'updated_at' => '',
];
}
function matthew_lms_mvp_normalize_course_progress(array $raw = []): array {
$defaults = matthew_lms_mvp_course_progress_defaults();
$progress = array_merge($defaults, array_intersect_key($raw, $defaults));
$progress['phase'] = max(1, min(3, absint($progress['phase'])));
$progress['lesson'] = max(1, min(5, absint($progress['lesson'])));
$progress['step'] = max(1, min(5, absint($progress['step'])));
$progress['percent'] = max(0, min(100, absint($progress['percent'])));
$progress['quiz_score'] = max(0, min(10, absint($progress['quiz_score'])));
$progress['quiz_total'] = max(1, min(10, absint($progress['quiz_total'])));
$progress['skill_points'] = max(0, min(999, absint($progress['skill_points'])));
$allowed_statuses = ['not_started', 'in_progress', 'course_launched', 'quiz_completed', 'lesson_complete'];
$progress['status'] = sanitize_key((string) $progress['status']);
if (!in_array($progress['status'], $allowed_statuses, true)) {
$progress['status'] = $defaults['status'];
}
foreach (['phase_label', 'lesson_title', 'last_block', 'next_action', 'updated_at'] as $key) {
$progress[$key] = sanitize_text_field((string) $progress[$key]);
}
$progress['quiz_completed'] = filter_var($progress['quiz_completed'], FILTER_VALIDATE_BOOLEAN);
return $progress;
}
function matthew_lms_mvp_get_course_progress(int $user_id = 0): array {
if ($user_id <= 0 || !is_user_logged_in()) {
return matthew_lms_mvp_course_progress_defaults();
}
$stored = get_user_meta($user_id, '_mlms_course_progress', true);
return matthew_lms_mvp_normalize_course_progress(is_array($stored) ? $stored : []);
}
function matthew_lms_mvp_handle_course_progress_ajax(): void {
if (!check_ajax_referer('mlms_course_progress', 'nonce', false)) {
wp_send_json_error(['message' => 'Your course session expired. Refresh the page and continue again.'], 403);
}
if (!is_user_logged_in()) {
wp_send_json_error(['message' => 'Create an account or log in to save course progress.'], 401);
}
$raw = [];
foreach (array_keys(matthew_lms_mvp_course_progress_defaults()) as $key) {
if (isset($_POST[$key])) {
$raw[$key] = wp_unslash($_POST[$key]);
}
}
$progress = matthew_lms_mvp_normalize_course_progress($raw);
$progress['updated_at'] = current_time('mysql');
$saved = false;
if (is_user_logged_in()) {
update_user_meta(get_current_user_id(), '_mlms_course_progress', $progress);
$saved = true;
}
wp_send_json_success(['saved' => $saved, 'progress' => $progress]);
}
function matthew_lms_mvp_profile_notice(): string {
$notice = sanitize_text_field(wp_unslash($_GET['mlms_profile_notice'] ?? ''));
if ($notice === 'connected') {
return '<div class="mlms-profile-notice"><strong>External source connected.</strong> Your profile link is now shown in the skills feed.</div>';
}
if ($notice === 'invalid') {
return '<div class="mlms-error-note"><strong>Connection failed.</strong> Enter a valid public URL for GitHub, Reddit, LinkedIn, or another AI platform.</div>';
}
if ($notice === 'expired') {
return '<div class="mlms-error-note"><strong>Session expired.</strong> Refresh the profile page and try connecting the source again.</div>';
}
return '';
}
function matthew_lms_mvp_role_lesson_context(string $role): array {
$normalized = strtolower($role);
if (str_contains($normalized, 'product') || $normalized === 'pm') {
return [
'role_label' => 'Product Manager',
'workflow' => 'turn roadmap notes and customer signals into a product-ready feature brief',
'artifact' => 'AI-assisted feature brief',
'example' => 'Convert a backlog idea into a one-page PRD with user problem, acceptance criteria, risks, and a human review checklist.',
'source' => 'roadmap notes, support tickets, and customer interview snippets',
'handoff' => 'a Jira-ready brief your design and engineering partners can critique',
];
}
if (str_contains($normalized, 'sales')) {
return [
'role_label' => 'Sales',
'workflow' => 'turn account research into a targeted outreach and follow-up workflow',
'artifact' => 'AI-assisted account plan',
'example' => 'Build a sequence that summarizes the buyer context, drafts the first email, and flags what a human should personalize.',
'source' => 'CRM notes, call summaries, and public account signals',
'handoff' => 'a reviewed account plan and next-best-action list',
];
}
if (str_contains($normalized, 'operation') || str_contains($normalized, 'ops')) {
return [
'role_label' => 'Operations',
'workflow' => 'turn recurring process notes into a repeatable operating workflow',
'artifact' => 'AI-assisted SOP and status update',
'example' => 'Create a weekly ops agent that drafts updates, highlights blockers, and lists what needs human approval.',
'source' => 'SOP notes, Slack updates, and recurring checklist items',
'handoff' => 'a reviewed SOP plus escalation checklist',
];
}
if (str_contains($normalized, 'executive') || str_contains($normalized, 'founder') || str_contains($normalized, 'leader')) {
return [
'role_label' => 'Executive',
'workflow' => 'turn scattered strategic inputs into a decision-ready leadership brief',
'artifact' => 'AI-assisted decision memo',
'example' => 'Summarize options, assumptions, risks, and recommended decision criteria before a leadership meeting.',
'source' => 'meeting notes, metrics, and strategy docs',
'handoff' => 'a decision memo with assumptions marked for executive review',
];
}
if (str_contains($normalized, 'marketing') || str_contains($normalized, 'growth')) {
return [
'role_label' => 'Marketing',
'workflow' => 'turn campaign notes into a launch-ready creative brief and review loop',
'artifact' => 'AI-assisted campaign brief',
'example' => 'Transform messy campaign inputs into audience, message, channel plan, draft assets, and a brand-safety review checklist.',
'source' => 'campaign notes, audience research, and positioning docs',
'handoff' => 'a reviewed campaign brief your team can execute',
];
}
return [
'role_label' => $role ?: 'Operator',
'workflow' => 'turn one weekly work process into a repeatable AI-assisted workflow',
'artifact' => 'AI-assisted workflow brief',
'example' => 'Choose a recurring task, define the input, draft the AI prompt, and add a human review step before sharing output.',
'source' => 'notes, examples, and recurring work inputs',
'handoff' => 'a reviewed workflow brief with a clear next action',
];
}
function matthew_lms_mvp_course_status_labels(): array {
return [
'not_started' => 'Not started',
'in_progress' => 'In progress',
'course_launched' => 'Course launched',
'quiz_completed' => 'Quiz complete',
'lesson_complete' => 'Lesson complete',
];
}
function matthew_lms_mvp_course_url(string $view = ''): string {
$view = sanitize_key($view);
if ($view === 'course' || $view === 'dashboard') {
return matthew_lms_mvp_public_url('platform/course/');
}
if ($view === 'lesson' || $view === 'quiz') {
return matthew_lms_mvp_public_url('platform/' . $view . '/');
}
return matthew_lms_mvp_public_url('platform/');
}
function matthew_lms_mvp_course_continue_url(array $progress): string {
$progress = matthew_lms_mvp_normalize_course_progress($progress);
$course_url = matthew_lms_mvp_course_url('course');
if ($progress['status'] === 'lesson_complete') {
return $course_url . '#mlms-lesson-completion';
}
if ($progress['status'] === 'quiz_completed' || $progress['step'] >= 4) {
return $course_url . '#mlms-build-task';
}
if ($progress['step'] >= 3 || stripos($progress['last_block'], 'quiz') !== false) {
return matthew_lms_mvp_course_url('quiz');
}
return $course_url . '#mlms-lesson-view';
}
function matthew_lms_mvp_course_runtime(array $profile): array {
$context = matthew_lms_mvp_role_lesson_context((string) ($profile['role'] ?? ''));
$lesson_context = sprintf(
'Unggul Phase 1 lesson: AI Workflow Fluency for %s. Goal: %s. Build task artifact: %s.',
$context['role_label'],
(string) ($profile['goal'] ?? 'practical AI workflow fluency'),
$context['artifact']
);
$progress = matthew_lms_mvp_normalize_course_progress(is_array($profile['course_progress'] ?? null) ? $profile['course_progress'] : []);
return [
'context' => $context,
'ajax_url' => admin_url('admin-ajax.php'),
'tutor_nonce' => wp_create_nonce('mlms_ai_tutor'),
'progress_nonce' => wp_create_nonce('mlms_course_progress'),
'lesson_context' => $lesson_context,
'progress' => $progress,
'progress_json' => wp_json_encode($progress),
'progress_status' => matthew_lms_mvp_course_status_labels()[$progress['status']] ?? 'In progress',
];
}
function matthew_lms_mvp_course_dashboard_attrs(array $runtime, string $route = ''): string {
$attrs = [
'id' => 'course-dashboard',
'class' => 'mlms-course-dashboard' . ($route ? ' mlms-course-standalone' : ''),
'data-mlms-course-dashboard' => '',
'data-course-route' => $route,
'data-ajax-url' => $runtime['ajax_url'],
'data-tutor-nonce' => $runtime['tutor_nonce'],
'data-progress-nonce' => $runtime['progress_nonce'],
'data-initial-progress' => $runtime['progress_json'] ?: '{}',
'data-lesson-context' => $runtime['lesson_context'],
];
$out = [];
foreach ($attrs as $key => $value) {
if ($value === '') {
$out[] = esc_attr($key);
} else {
$out[] = esc_attr($key) . '="' . esc_attr((string) $value) . '"';
}
}
return implode(' ', $out);
}
function matthew_lms_mvp_maybe_render_course_route(): void {
$path = trim((string) wp_parse_url(wp_unslash($_SERVER['REQUEST_URI'] ?? ''), PHP_URL_PATH), '/');
if (!in_array($path, ['platform/course', 'platform/lesson', 'platform/quiz'], true)) {
return;
}
$view = 'course';
if (str_ends_with($path, '/quiz')) {
$view = 'quiz';
} elseif (str_ends_with($path, '/lesson')) {
$view = 'lesson';
}
status_header(200);
global $wp_query;
if ($wp_query) {
$wp_query->is_404 = false;
}
echo matthew_lms_mvp_render_course_route_page($view);
exit;
}
function matthew_lms_mvp_render_course_route_page(string $view): string {
$profile = matthew_lms_mvp_profile_context();
$runtime = matthew_lms_mvp_course_runtime($profile);
$context = $runtime['context'];
$progress = $runtime['progress'];
$is_quiz = $view === 'quiz';
$is_lesson = $view === 'lesson';
$title = 'Unggul Course Dashboard · AI Workflow Fluency';
if ($is_quiz) {
$title = 'Unggul Quiz · AI Workflow Fluency';
} elseif ($is_lesson) {
$title = 'Unggul Lesson · AI Workflow Fluency';
}
ob_start();
?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo esc_html($title); ?></title>
<?php wp_head(); ?>
</head>
<body <?php body_class(['mlms-wax-page', 'mlms-course-page', 'mlms-course-standalone-page']); ?>>
<?php wp_body_open(); ?>
<?php echo matthew_lms_mvp_render_header(); ?>
<main class="mlms-page mlms-course-page" id="site-content">
<section class="mlms-section"><div class="mlms-shell">
<?php if ($view === 'course'): ?>
<div class="mlms-course-route-shell">
<?php echo matthew_lms_mvp_render_course_dashboard($profile); ?>
</div>
<?php else: ?>
<div <?php echo matthew_lms_mvp_course_dashboard_attrs($runtime, $view); ?>>
<article class="mlms-course-standalone-hero">
<a class="mlms-course-backlink" href="<?php echo esc_url(matthew_lms_mvp_course_url('course')); ?>">← Back to course dashboard</a>
<span class="mlms-course-route-pill"><span data-mlms-course-status><?php echo esc_html($runtime['progress_status']); ?></span> · <span data-mlms-course-percent><?php echo esc_html((string) $progress['percent']); ?>%</span></span>
<?php if ($is_quiz): ?>
<h1>Quiz: AI Workflow Fluency</h1>
<p>Complete the quiz on its own page. Your score is saved to your profile and unlocks the build task when you pass.</p>
<div class="mlms-course-standalone-actions"><a class="mlms-btn mlms-btn-secondary" href="<?php echo esc_url(matthew_lms_mvp_course_url('course') . '#mlms-lesson-view'); ?>">Open course dashboard</a></div>
<?php else: ?>
<h1>Lesson path: build a practical AI workflow brief.</h1>
<p>Follow the lesson tasks as a path. The final unfinished task is the quiz, and its Start Quiz button opens a separate quiz page.</p>
<div class="mlms-course-standalone-actions"><a class="mlms-btn mlms-btn-secondary" href="<?php echo esc_url(matthew_lms_mvp_course_url('course')); ?>">Open course dashboard</a></div>
<?php endif; ?>
</article>
<div class="mlms-course-route-shell">
<?php if ($is_quiz): ?>
<?php echo matthew_lms_mvp_render_course_quiz_component(); ?>
<?php else: ?>
<section id="mlms-course-workspace" class="mlms-course-workspace launched" data-mlms-course-workspace aria-labelledby="mlms-course-workspace-heading">
<?php echo matthew_lms_mvp_render_lesson_path_component($context, $progress); ?>
</section>
<?php endif; ?>
</div>
<?php echo matthew_lms_mvp_render_ai_tutor_component($runtime['ajax_url'], $runtime['tutor_nonce'], $runtime['lesson_context']); ?>
</div>
<?php endif; ?>
</div></section>
</main>
<?php echo matthew_lms_mvp_render_footer(); ?>
<?php wp_footer(); ?>
</body>
</html>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_render_course_dashboard(array $profile): string {
$runtime = matthew_lms_mvp_course_runtime($profile);
$context = $runtime['context'];
$progress = $runtime['progress'];
$lesson_url = matthew_lms_mvp_course_url('lesson');
$quiz_url = matthew_lms_mvp_course_url('quiz');
$profile_url = matthew_lms_mvp_course_url();
$continue_url = matthew_lms_mvp_course_continue_url($progress);
ob_start();
?>
<div <?php echo matthew_lms_mvp_course_dashboard_attrs($runtime, 'course'); ?>>
<article class="mlms-course-overview" aria-labelledby="mlms-course-dashboard-heading">
<div class="mlms-course-overview-head">
<div>
<p class="mlms-kicker" style="color:#C9C4FF">Course overview</p>
<h1 id="mlms-course-dashboard-heading">Course Dashboard: AI Workflow Fluency</h1>
</div>
<div class="mlms-course-streak" aria-label="Current course progress"><b data-mlms-course-percent><?php echo esc_html((string) $progress['percent']); ?>%</b><span data-mlms-course-status><?php echo esc_html($runtime['progress_status']); ?></span></div>
</div>
<div class="mlms-phase-path" role="list" aria-label="Course learning path">
<article class="mlms-dashboard-phase completed" role="listitem">
<div class="mlms-dashboard-phase-top"><span class="mlms-dashboard-phase-number" aria-hidden="true">✓</span><span class="mlms-dashboard-lock">Completed</span><span class="mlms-dashboard-skill-node earned" aria-label="Completed skill node"></span></div>
<h3>Course Overview</h3>
<p>Completed: the practical AI workflow pattern is introduced, including source inputs, prompt structure, review rubric, and human handoff.</p>
<div class="mlms-dashboard-meta"><span>Step complete</span><span>Foundation unlocked</span><span>+20 skill points</span></div>
</article>
<article class="mlms-dashboard-phase current" role="listitem" aria-current="step">
<div class="mlms-dashboard-phase-top"><span class="mlms-dashboard-phase-number">2</span><span class="mlms-dashboard-lock">In progress</span><span class="mlms-dashboard-skill-node current" aria-label="In progress skill node"></span></div>
<h3>Role-Specific Automation</h3>
<p>In progress: apply the workflow pattern to <?php echo esc_html($context['role_label']); ?> examples and continue the guided lesson path.</p>
<div class="mlms-dashboard-meta"><span>Current step</span><span data-mlms-course-step>Step <?php echo esc_html((string) $progress['step']); ?> of 5</span><span><?php echo esc_html($context['role_label']); ?> track</span></div>
<div class="mlms-phase-actions"><a class="mlms-btn mlms-btn-secondary mlms-phase-launch" href="<?php echo esc_url($lesson_url); ?>" data-mlms-launch-course>Start lesson</a></div>
</article>
<article class="mlms-dashboard-phase unopened locked" role="listitem" aria-disabled="true">
<div class="mlms-dashboard-phase-top"><span class="mlms-dashboard-phase-number">3</span><span class="mlms-dashboard-lock">Unopened</span><span class="mlms-dashboard-skill-node" aria-label="Unopened skill node"></span></div>
<h3>Portfolio Certification</h3>
<p>Unopened: final build, source proof, review, and AI Expert credential remain locked until the current step is complete.</p>
<div class="mlms-dashboard-meta"><span>Not opened yet</span><span>Credential review</span></div>
</article>
</div>
</article>
<?php echo matthew_lms_mvp_render_ai_tutor_component($runtime['ajax_url'], $runtime['tutor_nonce'], $runtime['lesson_context']); ?>
</div>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_render_course_lesson_component(array $context, array $progress): string {
ob_start();
?>
<div class="mlms-lesson-grid">
<article class="mlms-lesson-reader" aria-labelledby="mlms-lesson-heading">
<div class="mlms-lesson-topline">
<div>
<p class="mlms-kicker">Lesson view</p>
<h2 id="mlms-lesson-heading">Build a practical AI workflow brief for <?php echo esc_html($context['role_label']); ?> work.</h2>
</div>
<div class="mlms-lesson-progress" aria-label="Progress within this lesson">
<div class="mlms-lesson-progress-label"><span data-mlms-course-step>Step <?php echo esc_html((string) $progress['step']); ?> of 5</span><span data-mlms-course-percent><?php echo esc_html((string) $progress['percent']); ?>%</span></div>
<div class="mlms-lesson-progress-track"><span style="width:<?php echo esc_attr((string) $progress['percent']); ?>%"></span></div>
</div>
</div>
<p>Learn the concept, see a role-specific example, then move to the separate quiz page before the build task.</p>
<div class="mlms-role-example">
<strong><?php echo esc_html($context['role_label']); ?> example</strong>
<p><?php echo esc_html($context['example']); ?></p>
</div>
<div class="mlms-flowchart" aria-label="AI workflow flowchart">
<div class="mlms-flow-step"><span>Input</span><?php echo esc_html($context['source']); ?></div>
<div class="mlms-flow-step"><span>Prompt</span>Ask AI for structure, assumptions, and first draft.</div>
<div class="mlms-flow-step"><span>Review</span>Score accuracy, tone, risks, and missing context.</div>
<div class="mlms-flow-step"><span>Handoff</span><?php echo esc_html($context['handoff']); ?></div>
</div>
<div class="mlms-lesson-table" aria-label="Lesson comparison table">
<div class="mlms-lesson-row"><strong>Weak workflow</strong><span>One prompt, no review rubric, unclear owner.</span></div>
<div class="mlms-lesson-row"><strong>Unggul workflow</strong><span>Prompt + example + review checklist + clear deliverable.</span></div>
</div>
<div class="mlms-lesson-checkpoint">
<h3>Next checkpoint: quiz page</h3>
<p>The quiz now opens on its own page with several questions and a final result.</p>
<div class="mlms-lesson-checkpoint-actions"><a class="mlms-btn mlms-btn-primary" href="<?php echo esc_url(matthew_lms_mvp_course_url('quiz')); ?>" data-mlms-open-quiz>Take the quiz on its own page →</a></div>
</div>
</article>
<aside class="mlms-lesson-aside" aria-label="Lesson support">
<div class="mlms-lesson-side-card">
<h3>Key skills in this lesson</h3>
<ul>
<li>Write a role-aware prompt with source context.</li>
<li>Evaluate AI output before sharing it.</li>
<li>Turn the result into a practical deliverable.</li>
</ul>
</div>
<div class="mlms-lesson-side-card">
<h3>In-course AI Tutor</h3>
<p class="mlms-ai-note">Available without covering lesson content</p>
<p style="margin-top:12px">Use the chat bubble for glossary help, screenshots of errors, or a quick explanation while staying in the lesson.</p>
</div>
</aside>
</div>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_render_lesson_path_component(array $context, array $progress): string {
$quiz_url = matthew_lms_mvp_course_url('quiz');
ob_start();
?>
<div class="mlms-lesson-path-page">
<article class="mlms-lesson-path-card" aria-labelledby="mlms-course-workspace-heading">
<div class="mlms-lesson-path-head">
<div>
<p class="mlms-kicker">Lesson path</p>
<h2 id="mlms-course-workspace-heading">Complete each lesson task in order.</h2>
<p>Use this path to move from concept to practice. The quiz is intentionally the final unfinished task, and it opens on its own page.</p>
</div>
<div class="mlms-lesson-progress" aria-label="Progress within this lesson">
<div class="mlms-lesson-progress-label"><span data-mlms-course-step>Step <?php echo esc_html((string) $progress['step']); ?> of 5</span><span data-mlms-course-percent><?php echo esc_html((string) $progress['percent']); ?>%</span></div>
<div class="mlms-lesson-progress-track"><span style="width:<?php echo esc_attr((string) $progress['percent']); ?>%"></span></div>
</div>
</div>
<ol class="mlms-lesson-path-list" aria-label="Lesson tasks path">
<li class="mlms-lesson-path-task is-complete">
<span class="mlms-lesson-path-marker" aria-hidden="true">✓</span>
<div><h3>1. Understand the workflow goal</h3><p>Learn how AI workflow fluency turns messy work inputs into a repeatable process.</p></div>
<span class="mlms-lesson-path-status">Complete</span>
</li>
<li class="mlms-lesson-path-task is-complete">
<span class="mlms-lesson-path-marker" aria-hidden="true">✓</span>
<div><h3>2. Review your <?php echo esc_html($context['role_label']); ?> example</h3><p><?php echo esc_html($context['example']); ?></p></div>
<span class="mlms-lesson-path-status">Complete</span>
</li>
<li class="mlms-lesson-path-task is-current">
<span class="mlms-lesson-path-marker" aria-hidden="true">3</span>
<div><h3>3. Map the prompt and review loop</h3><p>Connect source inputs, AI drafting, human review, and the final handoff.</p></div>
<span class="mlms-lesson-path-status">In progress</span>
</li>
<li class="mlms-lesson-path-task is-current" id="mlms-lesson-task-practice">
<span class="mlms-lesson-path-marker" aria-hidden="true">4</span>
<div><h3>4. Prepare your mini build task</h3><p>Choose one real work process and define the input, output, owner, and review checklist.</p></div>
<span class="mlms-lesson-path-status">Task</span>
</li>
<li class="mlms-lesson-path-task is-quiz">
<span class="mlms-lesson-path-marker" aria-hidden="true">5</span>
<div><h3>5. Quiz checkpoint</h3><p>This is the last uncompleted task. Start the quiz when you are ready to check understanding on a separate page.</p></div>
<a class="mlms-btn mlms-btn-primary" href="<?php echo esc_url($quiz_url); ?>" data-mlms-open-quiz>Start Quiz</a>
</li>
</ol>
</article>
<div class="mlms-lesson-content-grid" aria-label="Lesson task details">
<article class="mlms-lesson-task-card">
<p class="mlms-kicker">Lesson 1</p>
<h3>Source inputs</h3>
<p>Start with <?php echo esc_html($context['source']); ?>. Good inputs make the AI output easier to trust and review.</p>
</article>
<article class="mlms-lesson-task-card">
<p class="mlms-kicker">Lesson 2</p>
<h3>Prompt structure</h3>
<p>Ask AI for structure, assumptions, a first draft, and clear gaps instead of asking for a finished answer immediately.</p>
</article>
<article class="mlms-lesson-task-card">
<p class="mlms-kicker">Lesson 3</p>
<h3>Human review</h3>
<ul>
<li>Check accuracy against the source.</li>
<li>Flag risks, tone issues, and missing context.</li>
<li>Decide what needs human approval before sharing.</li>
</ul>
</article>
<article class="mlms-lesson-task-card">
<p class="mlms-kicker">Lesson 4</p>
<h3>Handoff</h3>
<p>Your finished lesson task is <?php echo esc_html($context['handoff']); ?>. After the quiz, use the build section to package it as evidence.</p>
</article>
</div>
</div>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_render_course_quiz_component(): string {
ob_start();
?>
<article id="mlms-quiz-block" class="mlms-quiz-card" data-mlms-quiz aria-labelledby="mlms-quiz-heading">
<div class="mlms-quiz-head">
<div>
<p class="mlms-kicker">Quiz page</p>
<h2 id="mlms-quiz-heading">Check your understanding before the build task.</h2>
<p>Answer all questions, submit once, then review your result. Passing the quiz updates your profile progress and unlocks the next action.</p>
</div>
<span class="mlms-quiz-count">4 questions · 75% pass</span>
</div>
<form class="mlms-quiz-form" data-mlms-quiz-form>
<fieldset class="mlms-quiz-question" data-quiz-question>
<legend>1. What should you gather before asking AI to draft a workflow deliverable?</legend>
<div class="mlms-quiz-options">
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q1" value="blank"> Start with a blank prompt so AI can decide the context.</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q1" value="source" data-correct="1"> Source inputs, examples, constraints, and the intended handoff.</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q1" value="design"> Only the final visual design or formatting.</label>
</div>
</fieldset>
<fieldset class="mlms-quiz-question" data-quiz-question>
<legend>2. Which prompt pattern best fits Unggul workflow fluency?</legend>
<div class="mlms-quiz-options">
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q2" value="short"> “Make this better.”</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q2" value="copy"> “Write the final version and skip review.”</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q2" value="structured" data-correct="1"> Role, source context, output format, assumptions, review criteria, and owner.</label>
</div>
</fieldset>
<fieldset class="mlms-quiz-question" data-quiz-question>
<legend>3. What must happen before AI output is handed to a teammate?</legend>
<div class="mlms-quiz-options">
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q3" value="publish"> Publish it as soon as it sounds confident.</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q3" value="review" data-correct="1"> Review accuracy, assumptions, tone, risk, and next action.</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q3" value="hide"> Remove the human owner so the process feels automated.</label>
</div>
</fieldset>
<fieldset class="mlms-quiz-question" data-quiz-question>
<legend>4. When should you use the in-course AI Tutor?</legend>
<div class="mlms-quiz-options">
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q4" value="answers"> To get a finished assignment copied into the deliverable.</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q4" value="ignore"> Only after leaving the course and searching elsewhere.</label>
<label class="mlms-quiz-option"><input type="radio" name="mlms_quiz_q4" value="coach" data-correct="1"> For glossary help, troubleshooting screenshots, or step-by-step guidance without doing the work for you.</label>
</div>
</fieldset>
<div class="mlms-quiz-actions"><button class="mlms-btn mlms-btn-primary" type="submit">Submit quiz and see result →</button><span class="mlms-quiz-status" data-mlms-quiz-status>Answer all four questions to calculate your result.</span></div>
</form>
<div class="mlms-quiz-result" data-mlms-quiz-result aria-live="polite"></div>
</article>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_render_course_build_component(array $context): string {
ob_start();
?>
<article id="mlms-build-task" class="mlms-build-card" aria-labelledby="mlms-build-heading">
<div>
<p class="mlms-kicker">Task / build section</p>
<h2 id="mlms-build-heading">Finish the lesson with a deliverable.</h2>
<p>Submit a concise <?php echo esc_html($context['artifact']); ?>. AI validation gives feedback, confirms completion, and lights up the Phase 1 skill node when earned.</p>
</div>
<form data-mlms-build-form>
<input type="hidden" name="question" value="Validate my Unggul lesson build task and decide whether it earns the skill point.">
<div class="mlms-build-checklist">
<label><input type="checkbox" name="deliverable_checklist[]" value="source" required> I named the source inputs.</label>
<label><input type="checkbox" name="deliverable_checklist[]" value="prompt" required> I included the AI prompt and review rubric.</label>
<label><input type="checkbox" name="deliverable_checklist[]" value="handoff" required> I defined the final handoff owner/action.</label>
</div>
<textarea name="deliverable_notes" required placeholder="Paste or summarize your workflow brief deliverable here. Example: I used campaign notes as input, asked AI to draft a launch brief, then reviewed claims, tone, and next actions before handing it to the content owner."></textarea>
<div class="mlms-build-actions"><button class="mlms-btn mlms-btn-primary" type="submit">Submit task for AI validation →</button><span class="mlms-build-status" data-mlms-build-status aria-live="polite">Skill points are earned by the submitted deliverable.</span></div>
</form>
<div class="mlms-completion-card" data-mlms-completion-card aria-live="polite">
<span class="mlms-completion-points">+20 skill points · Phase 1 node lit</span>
<h3>Lesson completion confirmed.</h3>
<p data-mlms-validation-feedback>AI validation feedback will appear here after submission.</p>
</div>
</article>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_render_ai_tutor_component(string $ajax_url, string $nonce, string $lesson_context): string {
ob_start();
?>
<div class="mlms-ai-tutor" data-mlms-tutor-root data-ajax-url="<?php echo esc_url($ajax_url); ?>" data-lesson-context="<?php echo esc_attr($lesson_context); ?>">
<button class="mlms-tutor-launcher" type="button" aria-label="Open Unggul AI Tutor"><span>AI</span><span>Ask Tutor</span></button>
<section class="mlms-tutor-panel" aria-hidden="true" aria-label="Unggul AI Tutor panel">
<div class="mlms-tutor-head"><div><h3>Unggul AI Tutor</h3><p>Glossary help, roadblock screenshots, and step-by-step guidance.</p></div><button class="mlms-tutor-close" type="button" aria-label="Close tutor">×</button></div>
<div class="mlms-tutor-messages" aria-live="polite"><div class="mlms-tutor-msg bot">Im here beside the lesson. Ask “What is CLI?”, request a concise explanation, or upload an error screenshot with Fix My Error.</div></div>
<form class="mlms-tutor-form" data-mlms-tutor-form enctype="multipart/form-data">
<input type="hidden" name="nonce" value="<?php echo esc_attr($nonce); ?>">
<input type="hidden" name="mode" value="tutor">
<div class="mlms-tutor-mode-row" aria-label="Tutor shortcuts">
<button class="mlms-tutor-chip" type="button" data-tutor-mode="glossary" data-tutor-prompt="What is CLI, in simple terms for this lesson?">What is CLI?</button>
<button class="mlms-tutor-chip" type="button" data-tutor-mode="glossary" data-tutor-prompt="Explain API in one practical example for my role.">Explain API</button>
<button class="mlms-tutor-chip" type="button" data-tutor-mode="roadblock" data-tutor-prompt="Fix My Error: I uploaded a screenshot. Please give step-by-step troubleshooting guidance.">Fix My Error</button>
</div>
<textarea name="question" placeholder="Ask about a term, a lesson step, or an error you hit while building…"></textarea>
<label class="mlms-tutor-file">Roadblock screenshot/image (optional, PNG/JPG/WebP/GIF under 4 MB)<input type="file" name="roadblock_image" accept="image/png,image/jpeg,image/webp,image/gif"></label>
<button class="mlms-btn mlms-btn-primary mlms-tutor-submit" type="submit">Send to tutor →</button>
<p class="mlms-tutor-help">The tutor gives guidance and explanations, not finished deliverables.</p>
</form>
</section>
</div>
<?php
return ob_get_clean();
}
function matthew_lms_mvp_handle_ai_tutor_ajax(): void {
if (!check_ajax_referer('mlms_ai_tutor', 'nonce', false)) {
wp_send_json_error(['message' => 'Tutor session expired. Refresh the lesson and try again.'], 403);
}
if (!is_user_logged_in()) {
wp_send_json_error(['message' => 'Create an account or log in to use the course tutor.'], 401);
}
$mode = sanitize_key(wp_unslash($_POST['mode'] ?? 'tutor'));
if (!in_array($mode, ['tutor', 'glossary', 'roadblock', 'build_validation'], true)) {
$mode = 'tutor';
}
$question = sanitize_textarea_field(wp_unslash($_POST['question'] ?? ''));
$lesson_context = sanitize_textarea_field(wp_unslash($_POST['lesson_context'] ?? 'Unggul course lesson'));
$deliverable = sanitize_textarea_field(wp_unslash($_POST['deliverable_notes'] ?? ''));
if ($mode === 'build_validation') {
$checklist = isset($_POST['deliverable_checklist']) && is_array($_POST['deliverable_checklist'])
? array_map('sanitize_text_field', wp_unslash($_POST['deliverable_checklist']))
: [];
$question = trim($question . "\n\nDeliverable summary:\n" . $deliverable . "\n\nChecklist completed: " . implode(', ', $checklist));
}
$image = matthew_lms_mvp_prepare_tutor_image('roadblock_image');
if (!empty($image['error'])) {
wp_send_json_error(['message' => $image['error']], 400);
}
if (trim($question) === '' && empty($image)) {
wp_send_json_error(['message' => 'Ask a question or upload a screenshot first.'], 400);
}
$message = matthew_lms_mvp_generate_ai_tutor_response($mode, $question, $lesson_context, $image);
wp_send_json_success(['message' => $message]);
}
function matthew_lms_mvp_prepare_tutor_image(string $field): array {
if (empty($_FILES[$field]) || !is_array($_FILES[$field])) {
return [];
}
$file = $_FILES[$field];
$error = (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE);
if ($error === UPLOAD_ERR_NO_FILE) {
return [];
}
if ($error !== UPLOAD_ERR_OK) {
return ['error' => 'The screenshot upload failed. Try a smaller PNG or JPG.'];
}
$size = (int) ($file['size'] ?? 0);
if ($size <= 0 || $size > 4 * 1024 * 1024) {
return ['error' => 'Upload an image under 4 MB so the tutor can review it.'];
}
$tmp = (string) ($file['tmp_name'] ?? '');
if ($tmp === '' || !is_uploaded_file($tmp)) {
return ['error' => 'The uploaded image could not be read.'];
}
$info = @getimagesize($tmp);
$mime = is_array($info) ? (string) ($info['mime'] ?? '') : '';
$allowed = ['image/png', 'image/jpeg', 'image/webp', 'image/gif'];
if (!in_array($mime, $allowed, true)) {
return ['error' => 'Upload a PNG, JPG, WebP, or GIF screenshot.'];
}
$bytes = file_get_contents($tmp);
if ($bytes === false) {
return ['error' => 'The uploaded image could not be processed.'];
}
return [
'name' => sanitize_file_name((string) ($file['name'] ?? 'screenshot')),
'mime' => $mime,
'size' => $size,
'data_url' => 'data:' . $mime . ';base64,' . base64_encode($bytes),
];
}
function matthew_lms_mvp_generate_ai_tutor_response(string $mode, string $question, string $lesson_context, array $image = []): string {
$fallback = matthew_lms_mvp_tutor_fallback($mode, $question, !empty($image));
$api = dirname(__DIR__, 3) . '/ai/LocalAIApi.php';
if (!file_exists($api)) {
return $fallback;
}
require_once $api;
if (!class_exists('LocalAIApi')) {
return $fallback;
}
$mode_instruction = match ($mode) {
'glossary' => 'Explain the term quickly, in plain language, with one example connected to the lesson. Keep it concise.',
'roadblock' => 'Act as Fix My Error. If an image is present, inspect it for error text or UI clues. Return step-by-step troubleshooting guidance distinct from general tutoring.',
'build_validation' => 'Validate the submitted build task. Return a tasteful completion confirmation, two specific strengths, one improvement, and whether the skill point is earned.',
default => 'Answer as a non-intrusive in-course tutor. Coach the learner through the concept without doing the whole deliverable for them.',
};
$prompt = "Lesson context:\n{$lesson_context}\n\nTutor mode: {$mode}\nInstruction: {$mode_instruction}\n\nLearner message:\n{$question}\n\nUse calm, premium language. Prefer 3-5 numbered steps or bullets. If information is missing, ask for the exact detail needed. Keep under 180 words.";
$system = 'You are Unggul AI Tutor, an in-course assistant for non-technical professionals learning AI-powered work. Be practical, clear, encouraging, and role-specific. Do not cover the entire course content, do not complete assignments for the learner, and avoid fluff.';
$user_content = $prompt;
if (!empty($image['data_url'])) {
$user_content = [
['type' => 'input_text', 'text' => $prompt . "\n\nScreenshot attached: " . ($image['name'] ?? 'roadblock image') . '.'],
['type' => 'input_image', 'image_url' => $image['data_url']],
];
}
$resp = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => $system],
['role' => 'user', 'content' => $user_content],
],
], ['poll_interval' => 2, 'poll_timeout' => 25, 'timeout' => 20]);
if (!empty($resp['success'])) {
$text = LocalAIApi::extractText($resp);
if (is_string($text) && trim($text) !== '') {
return trim($text);
}
}
if (!empty($image['data_url'])) {
$retry_prompt = $prompt . "\n\nThe learner uploaded an image named " . ($image['name'] ?? 'screenshot') . ", but if image inspection is unavailable, provide a careful screenshot-troubleshooting checklist and ask them to paste the exact error text.";
$retry = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => $system],
['role' => 'user', 'content' => $retry_prompt],
],
], ['poll_interval' => 2, 'poll_timeout' => 20, 'timeout' => 18]);
if (!empty($retry['success'])) {
$text = LocalAIApi::extractText($retry);
if (is_string($text) && trim($text) !== '') {
return trim($text);
}
}
}
return $fallback;
}
function matthew_lms_mvp_tutor_fallback(string $mode, string $question, bool $has_image = false): string {
if ($mode === 'build_validation') {
return "AI validation fallback:\n1. Your build can earn the skill point if it names the source inputs, includes the prompt/review rubric, and defines the handoff owner.\n2. Strength: you completed the required checklist.\n3. Improve: make the final next action explicit.\nSkill point: earned when the deliverable is specific enough for another person to review.";
}
if ($mode === 'roadblock' || $has_image) {
return "Fix My Error fallback:\n1. Copy the exact error text from the screenshot.\n2. Identify what changed right before the error appeared.\n3. Retry the smallest step in the lesson and compare it to the expected output.\n4. If it still fails, send the error text plus the tool you are using so I can narrow the fix.";
}
if ($mode === 'glossary' || stripos($question, 'what is') !== false || stripos($question, 'explain') !== false) {
return "Glossary explainer:\nIn this lesson, the term means a practical building block you use to move work from input → AI draft → human review → final handoff. If you tell me the exact acronym or word, I can give a one-sentence definition and a role-specific example.";
}
return "Tutor guidance:\n1. Restate the task in one sentence.\n2. List the inputs you have.\n3. Ask AI for a structured first draft.\n4. Review assumptions, accuracy, and handoff owner before using the output.\nIf you share where you are stuck, Ill give the next concrete step.";
}
add_shortcode('matthew_user_profile', function () {
$profile = matthew_lms_mvp_profile_context();
$connected = !empty($profile['profile_link']);
$course_progress = matthew_lms_mvp_normalize_course_progress(is_array($profile['course_progress'] ?? null) ? $profile['course_progress'] : []);
$course_status_labels = [
'not_started' => 'Not started',
'in_progress' => 'In progress',
'course_launched' => 'Course launched',
'quiz_completed' => 'Quiz complete',
'lesson_complete' => 'Lesson complete',
];
$course_status = $course_status_labels[$course_progress['status']] ?? 'In progress';
$quiz_label = !empty($course_progress['quiz_completed']) ? ($course_progress['quiz_score'] . '/' . $course_progress['quiz_total']) : 'Not taken';
$course_continue_url = matthew_lms_mvp_course_continue_url($course_progress);
$share_url = matthew_lms_mvp_public_url('platform/');
$share_title = sprintf("View %s's Unggul AI profile", $profile['name']);
$share_text = sprintf('%s is building AI-native proof on Unggul: %s', $profile['name'], $profile['role']);
$share_links = [
[
'label' => 'LinkedIn',
'icon' => 'in',
'url' => 'https://www.linkedin.com/sharing/share-offsite/?url=' . rawurlencode($share_url),
],
[
'label' => 'X',
'icon' => '𝕏',
'url' => 'https://twitter.com/intent/tweet?url=' . rawurlencode($share_url) . '&text=' . rawurlencode($share_text),
],
[
'label' => 'Facebook',
'icon' => 'f',
'url' => 'https://www.facebook.com/sharer/sharer.php?u=' . rawurlencode($share_url),
],
[
'label' => 'WhatsApp',
'icon' => '☎',
'url' => 'https://wa.me/?text=' . rawurlencode($share_text . ' ' . $share_url),
],
];
ob_start();
?>
<style>
.mlms-social-share{margin-top:18px;padding:20px;border:1px solid rgba(255,255,255,.16);border-radius:24px;background:radial-gradient(circle at top left,rgba(255,255,255,.18),transparent 34%),linear-gradient(135deg,#211a62 0%,#635bff 52%,#0b6b52 100%);box-shadow:0 18px 42px rgba(33,26,98,.28)}
.mlms-social-share-head{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start;margin-bottom:16px}
.mlms-social-share-badge{display:grid;place-items:center;width:42px;height:42px;border-radius:14px;background:rgba(255,255,255,.16);color:#fff;font-size:21px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.2),0 10px 22px rgba(0,0,0,.16);backdrop-filter:blur(8px)}
.mlms-social-share-head strong{display:block;color:#fff;font-size:18px;letter-spacing:-.03em;line-height:1.15;text-shadow:0 1px 1px rgba(0,0,0,.18)}
.mlms-social-share-head span{display:block;color:rgba(255,255,255,.84);font-size:14px;line-height:1.45;margin-top:5px;max-width:34rem}
.mlms-social-share-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.mlms-social-share-links{display:flex;flex-wrap:wrap;gap:10px}
.mlms-social-link,.mlms-share-copy{display:inline-flex;align-items:center;gap:8px;border:1px solid rgba(10,37,64,.1);border-radius:999px;background:#fff;color:var(--mlms-primary);font-weight:900;font-size:13px;text-decoration:none!important;padding:10px 13px;line-height:1.1;box-shadow:0 8px 18px rgba(50,50,93,.07);cursor:pointer;transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease}
.mlms-social-icon{display:inline-grid;place-items:center;width:23px;height:23px;border-radius:999px;background:var(--mlms-primary);color:#fff;font-size:12px;font-weight:950;font-family:Arial,Helvetica,sans-serif;line-height:1}
.mlms-social-icon.mlms-social-icon-x{font-size:13px;background:#0f172a}.mlms-social-icon.mlms-social-icon-facebook{background:#1877f2}.mlms-social-icon.mlms-social-icon-linkedin{background:#0a66c2}.mlms-social-icon.mlms-social-icon-whatsapp{background:#25d366;color:#073b20}
.mlms-social-link:hover,.mlms-share-copy:hover{border-color:rgba(99,91,255,.34);transform:translateY(-1px);box-shadow:0 12px 24px rgba(50,50,93,.1)}
.mlms-share-copy{color:var(--mlms-ink)}
.mlms-share-feedback{min-height:18px;margin:11px 0 0;color:#0B6B52;font-weight:800;font-size:13px}
@media (max-width:640px){.mlms-social-share-head{grid-template-columns:1fr}.mlms-social-share-actions,.mlms-social-share-links{width:100%}.mlms-share-copy,.mlms-social-link{flex:1 1 100%;justify-content:center}}
body.mlms-wax-page .mlms-tutor-launcher,
body.mlms-wax-page .mlms-tutor-launcher span{
color:#000 !important;
}
</style>
<div class="mlms-page mlms-profile-page">
<section class="mlms-section"><div class="mlms-shell">
<div class="mlms-profile-intro">
<div>
<p class="mlms-kicker">Profile</p>
<h1 class="mlms-h2">Your Unggul profile and skill proof.</h1>
</div>
</div>
<?php echo matthew_lms_mvp_profile_notice(); ?>
<div class="mlms-profile-dashboard">
<article class="mlms-credential-card" aria-label="Unggul profile header credential card">
<div class="mlms-credential-top">
<div class="mlms-profile-photo" aria-hidden="true">
<?php if (!empty($profile['photo'])): ?><img src="<?php echo esc_url($profile['photo']); ?>" alt=""><?php else: ?><?php echo esc_html($profile['initials']); ?><?php endif; ?>
</div>
<div class="mlms-credential-badge">Unggul AI Credential</div>
</div>
<h2><?php echo esc_html($profile['name']); ?></h2>
<p class="mlms-profile-title"><?php echo esc_html($profile['role']); ?> · AI-native operator</p>
<p class="mlms-profile-bio"><?php echo esc_html($profile['bio']); ?></p>
<div class="mlms-credential-details">
<span><b>Credential status</b> <?php echo esc_html($course_status); ?></span>
<span><b>Current phase</b> <?php echo esc_html($course_progress['phase_label']); ?></span>
<span><b>External source</b> <?php echo esc_html($connected ? $profile['source_label'] . ' connected' : 'Not connected yet'); ?></span>
</div>
<div class="mlms-credential-actions">
<a class="mlms-btn mlms-btn-primary" href="<?php echo esc_url($course_continue_url); ?>" data-mlms-launch-course>Open course dashboard</a>
<a class="mlms-btn mlms-btn-secondary" href="#portfolio">View portfolio</a>
<a class="mlms-btn mlms-btn-secondary" href="#external-skills">Connect sources</a>
</div>
<div class="mlms-social-share" data-mlms-social-share data-share-url="<?php echo esc_url($share_url); ?>" data-share-title="<?php echo esc_attr($share_title); ?>" data-share-text="<?php echo esc_attr($share_text); ?>">
<div class="mlms-social-share-head">
<span class="mlms-social-share-badge" aria-hidden="true">↗</span>
<div><strong>Share your Unggul profile</strong><span>Publish your verified Unggul profile proof on your social channels, or copy the profile link to send anywhere.</span></div>
</div>
<div class="mlms-social-share-actions">
<div class="mlms-social-share-links" aria-label="Share profile on social media">
<?php foreach ($share_links as $share_link): ?>
<a class="mlms-social-link" href="<?php echo esc_url($share_link['url']); ?>" target="_blank" rel="noopener noreferrer" aria-label="Share profile on <?php echo esc_attr($share_link['label']); ?>">
<span class="mlms-social-icon mlms-social-icon-<?php echo esc_attr(sanitize_title($share_link['label'])); ?>" aria-hidden="true"><?php echo esc_html($share_link['icon']); ?></span>
<span><?php echo esc_html($share_link['label']); ?></span>
</a>
<?php endforeach; ?>
</div>
<button class="mlms-share-copy" type="button" data-mlms-copy-profile><span class="mlms-social-icon" aria-hidden="true">🔗</span><span>Copy link</span></button>
</div>
<p class="mlms-share-feedback" data-mlms-share-feedback aria-live="polite"></p>
</div>
</article>
<div class="mlms-profile-main">
<article class="mlms-profile-module mlms-skill-indicator-card" aria-labelledby="skill-indicator-heading">
<div class="mlms-module-head">
<div><h3 id="skill-indicator-heading">Skill indicator</h3><p>Each badge is a proof signal. Hover or tab into a badge to see what unlocks it and why it matters on your public Unggul profile.</p></div>
<span class="mlms-pill">5 proof badges</span>
</div>
<div class="mlms-skill-network" role="list" aria-label="Five skill proof badges">
<div class="mlms-skill earned" role="listitem" tabindex="0" aria-describedby="mlms-skill-tip-workflow"><div class="mlms-skill-orb"><span aria-hidden="true">⚡</span></div><strong>Workflow fluency</strong><small>Course earned</small><em class="mlms-skill-status">Validated</em><div class="mlms-skill-tip" id="mlms-skill-tip-workflow" role="tooltip"><b>Course proof</b><span>Unlocked when your first workflow map is submitted and approved. This tells viewers you can turn messy work into a repeatable AI-ready process.</span></div></div>
<div class="mlms-skill current" role="listitem" tabindex="0" aria-describedby="mlms-skill-tip-automation"><div class="mlms-skill-orb"><span aria-hidden="true">🤖</span></div><strong>Role automation</strong><small>In progress</small><em class="mlms-skill-status">Building</em><div class="mlms-skill-tip" id="mlms-skill-tip-automation" role="tooltip"><b>Active skill</b><span>This badge fills as you build an automation for your role. Finish the lesson task and AI validation moves it from building to validated.</span></div></div>
<div class="mlms-skill" role="listitem" tabindex="0" aria-describedby="mlms-skill-tip-portfolio"><div class="mlms-skill-orb"><span aria-hidden="true">★</span></div><strong>Portfolio cert</strong><small>Locked</small><em class="mlms-skill-status">Next</em><div class="mlms-skill-tip" id="mlms-skill-tip-portfolio" role="tooltip"><b>Portfolio milestone</b><span>Unlocks after the course project is packaged into a shareable artifact, giving recruiters or teammates a concrete sample to review.</span></div></div>
<div class="mlms-skill external <?php echo $connected ? 'earned' : ''; ?>" role="listitem" tabindex="0" aria-describedby="mlms-skill-tip-external"><div class="mlms-skill-orb"><span aria-hidden="true">GH</span></div><strong>External build</strong><small><?php echo esc_html($connected ? $profile['source_label'] : 'GitHub / AI app'); ?></small><em class="mlms-skill-status"><?php echo esc_html($connected ? 'Connected' : 'Connect'); ?></em><div class="mlms-skill-tip" id="mlms-skill-tip-external" role="tooltip"><b>Outside evidence</b><span>Connect GitHub, an AI app, or another source to show proof that happened outside the course environment.</span></div></div>
<div class="mlms-skill external" role="listitem" tabindex="0" aria-describedby="mlms-skill-tip-community"><div class="mlms-skill-orb"><span aria-hidden="true">RD</span></div><strong>Community proof</strong><small>Reddit / community</small><em class="mlms-skill-status">Optional</em><div class="mlms-skill-tip" id="mlms-skill-tip-community" role="tooltip"><b>Social validation</b><span>Use this badge for community posts, feedback threads, or peer proof that demonstrates your work in public.</span></div></div>
</div>
<div class="mlms-skill-guide" aria-label="How skill badges work">
<span><b>Filled glow</b> means the proof is earned or connected.</span>
<span><b>Pulse</b> marks the skill you are actively building.</span>
<span><b>Muted badges</b> are locked or optional proof sources.</span>
</div>
</article>
<article class="mlms-profile-module mlms-profile-course-progress" data-mlms-profile-progress aria-labelledby="course-progress-heading">
<div class="mlms-module-head">
<div><h3 id="course-progress-heading">Course progress</h3><p>Your profile now shows exactly where you left off so you can continue without hunting for the lesson.</p></div>
<span class="mlms-cert-badge" data-mlms-profile-status><?php echo esc_html($course_status); ?></span>
</div>
<div class="mlms-profile-resume-card">
<div>
<p class="mlms-kicker">Where you left off</p>
<h4 data-mlms-profile-leftoff><?php echo esc_html($course_progress['lesson_title']); ?></h4>
<p><span data-mlms-profile-phase><?php echo esc_html($course_progress['phase_label']); ?></span> · <span data-mlms-profile-last-block><?php echo esc_html($course_progress['last_block']); ?></span></p>
<p><strong>Next:</strong> <span data-mlms-profile-next-action><?php echo esc_html($course_progress['next_action']); ?></span></p>
</div>
<a class="mlms-btn mlms-btn-primary" href="<?php echo esc_url($course_continue_url); ?>" data-mlms-launch-course>Open course dashboard →</a>
</div>
<div class="mlms-profile-progress-bar" aria-hidden="true"><span data-mlms-profile-progress-fill style="width:<?php echo esc_attr((string) $course_progress['percent']); ?>%"></span></div>
<div class="mlms-progress-summary" aria-label="Course progress summary">
<div class="mlms-progress-stat"><b data-mlms-profile-percent><?php echo esc_html((string) $course_progress['percent']); ?>%</b><span>Course progress</span></div>
<div class="mlms-progress-stat"><b data-mlms-profile-step>Step <?php echo esc_html((string) $course_progress['step']); ?>/5</b><span>Current step</span></div>
<div class="mlms-progress-stat"><b data-mlms-profile-quiz><?php echo esc_html($quiz_label); ?></b><span>Quiz result</span></div>
</div>
<div class="mlms-phase-tracker">
<div class="mlms-phase-card current"><span class="mlms-phase-dot">1</span><div><h4>AI Workflow Fluency</h4><p>Current — lesson 2 progress, quiz result, and build task are saved here.</p></div><span class="mlms-feed-status connected" data-mlms-profile-status><?php echo esc_html($course_status); ?></span></div>
<div class="mlms-phase-card"><span class="mlms-phase-dot">2</span><div><h4>Role-Specific Automation</h4><p>Unlocks after Phase 1 — adaptive examples for <?php echo esc_html(strtolower($profile['role'])); ?> workflows.</p></div><span class="mlms-feed-status">Locked</span></div>
<div class="mlms-phase-card"><span class="mlms-phase-dot">3</span><div><h4>Portfolio Certification</h4><p>Unlocks after phase 2 — final build, review, and AI Expert certification.</p></div><span class="mlms-feed-status">Locked</span></div>
</div>
</article>
</div>
</div>
</div></section>
<script>
(function(){
document.querySelectorAll('[data-mlms-social-share]').forEach(function(panel){
var copy=panel.querySelector('[data-mlms-copy-profile]');
var feedback=panel.querySelector('[data-mlms-share-feedback]');
if(!copy){return;}
var shareUrl=panel.getAttribute('data-share-url')||window.location.href;
var shareTitle=panel.getAttribute('data-share-title')||document.title;
var shareText=panel.getAttribute('data-share-text')||shareTitle;
function setFeedback(message){
if(!feedback){return;}
feedback.textContent=message;
window.clearTimeout(panel._mlmsShareTimer);
panel._mlmsShareTimer=window.setTimeout(function(){feedback.textContent='';},3200);
}
copy.addEventListener('click',function(){
if(navigator.share){
navigator.share({title:shareTitle,text:shareText,url:shareUrl}).then(function(){
setFeedback('Share sheet opened.');
}).catch(function(){
setFeedback('Share cancelled.');
});
return;
}
if(navigator.clipboard && navigator.clipboard.writeText){
navigator.clipboard.writeText(shareUrl).then(function(){
setFeedback('Profile link copied.');
}).catch(function(){
setFeedback('Copy failed — select the page URL manually.');
});
return;
}
setFeedback('Copy is unavailable — select the page URL manually.');
});
});
})();
</script>
<section id="portfolio" class="mlms-section mlms-profile-wide"><div class="mlms-shell">
<article class="mlms-profile-module">
<div class="mlms-module-head">
<div><p class="mlms-kicker">Portfolio section</p><h3>Built artifacts</h3><p>A GitHub-contributions-meets-Behance showcase for projects/products built with AI tools.</p></div>
<span class="mlms-pill">Showcase-ready</span>
</div>
<div class="mlms-portfolio-grid">
<article class="mlms-portfolio-card" data-proof-card="AI Brief Builder"><h4>AI Brief Builder</h4><p>Turns messy campaign notes into structured briefs, acceptance criteria, and review prompts.</p><div class="mlms-tool-tags"><span>ChatGPT</span><span>Sheets</span><span>Notion</span></div><a class="mlms-portfolio-link" href="#" data-proof-trigger data-artifact="AI Brief Builder">Attach proof →</a></article>
<article class="mlms-portfolio-card" data-proof-card="Persona Synthesizer"><h4>Persona Synthesizer</h4><p>Combines research snippets into reusable buyer personas with documented assumptions.</p><div class="mlms-tool-tags"><span>Claude</span><span>Docs</span><span>Perplexity</span></div><a class="mlms-portfolio-link" href="#" data-proof-trigger data-artifact="Persona Synthesizer">Attach proof →</a></article>
<article class="mlms-portfolio-card" data-proof-card="Weekly Ops Agent"><h4>Weekly Ops Agent</h4><p>Automates recurring updates, flags blockers, and drafts stakeholder-ready summaries.</p><div class="mlms-tool-tags"><span>Zapier</span><span>Gemini</span><span>Slack</span></div><a class="mlms-portfolio-link" href="#" data-proof-trigger data-artifact="Weekly Ops Agent">Attach proof →</a></article>
</div>
</article>
</div></section>
<section id="external-skills" class="mlms-section mlms-profile-wide"><div class="mlms-shell">
<article class="mlms-profile-module">
<div class="mlms-module-head">
<div><p class="mlms-kicker">External skills feed</p><h3>Aggregated proof sources</h3><p>Show source icons, automation status, and a manual connection path when accounts are not auto-detected.</p></div>
<span class="mlms-pill">GitHub · Reddit · AI platforms</span>
</div>
<div class="mlms-external-grid">
<div class="mlms-feed-list">
<div class="mlms-feed-item"><span class="mlms-source-icon">GH</span><div><strong>GitHub repositories and commits</strong><small><?php echo esc_html($connected ? $profile['source_label'] . ' source available for review' : 'Connect a repo/profile URL to unlock build proof'); ?></small></div><span class="mlms-feed-status <?php echo $connected ? 'connected' : ''; ?>"><?php echo esc_html($connected ? 'Connected' : 'Manual'); ?></span></div>
<div class="mlms-feed-item"><span class="mlms-source-icon reddit">RD</span><div><strong>Reddit AI communities</strong><small>Community participation, helpful answers, and learning evidence.</small></div><span class="mlms-feed-status">Ready</span></div>
<div class="mlms-feed-item"><span class="mlms-source-icon ai">AI</span><div><strong>Other AI platforms</strong><small>Artifacts from AI builders, marketplaces, notebooks, or portfolios.</small></div><span class="mlms-feed-status">Ready</span></div>
</div>
<form class="mlms-source-form" method="post">
<h4>Connect an external source manually</h4>
<p>Paste a public GitHub, Reddit, LinkedIn, or AI-platform URL. Unggul will show it as a source in the credential feed.</p>
<?php wp_nonce_field('mlms_profile_source_submit', 'mlms_profile_source_nonce'); ?>
<div class="mlms-field"><label for="mlms_profile_source_type">Source type</label><select id="mlms_profile_source_type" name="mlms_profile_source_type"><option value="github">GitHub</option><option value="reddit">Reddit</option><option value="ai-platform">AI platform</option><option value="linkedin">LinkedIn</option><option value="profile">Other profile</option></select></div>
<div class="mlms-field"><label for="mlms_profile_source_url">Public profile or artifact URL</label><input id="mlms_profile_source_url" name="mlms_profile_source_url" type="url" value="<?php echo esc_attr($profile['profile_link']); ?>" placeholder="https://github.com/yourname/project" required></div>
<?php if ($profile['logged_in']): ?>
<button class="mlms-btn mlms-btn-primary" type="submit">Connect source →</button>
<?php else: ?>
<a class="mlms-btn mlms-btn-primary" href="<?php echo esc_url(matthew_lms_mvp_public_url('sign-up/')); ?>">Sign up to connect sources →</a>
<?php endif; ?>
</form>
</div>
</article>
</div></section>
<div class="mlms-proof-modal" id="mlms-proof-modal" aria-hidden="true">
<div class="mlms-proof-backdrop" data-proof-close></div>
<div class="mlms-proof-dialog" role="dialog" aria-modal="true" aria-labelledby="mlms-proof-title">
<div class="mlms-proof-head">
<div><p class="mlms-kicker">Attach proof</p><h3 id="mlms-proof-title">Add artifact evidence</h3><p id="mlms-proof-copy">Link a public source, upload a local reference, or add notes that explain what this artifact proves.</p></div>
<button class="mlms-proof-close" type="button" aria-label="Close proof modal" data-proof-close>×</button>
</div>
<form class="mlms-proof-form" data-proof-form novalidate>
<input type="hidden" name="artifact" id="mlms-proof-artifact" value="">
<div class="mlms-proof-grid">
<div class="mlms-field"><label for="mlms-proof-type">Proof type</label><select id="mlms-proof-type" name="proof_type" required><option value="github">GitHub / repo</option><option value="doc">Document / case study</option><option value="automation">Automation run</option><option value="community">Community proof</option><option value="ai-platform">AI platform artifact</option></select></div>
<div class="mlms-field"><label for="mlms-proof-url">Public proof URL</label><input id="mlms-proof-url" name="proof_url" type="url" placeholder="https://github.com/yourname/project" required></div>
<div class="mlms-field full"><label for="mlms-proof-notes">Evidence notes</label><textarea id="mlms-proof-notes" name="proof_notes" placeholder="What did you build, what tools did you use, and what result does this prove?" required></textarea></div>
<div class="mlms-field full"><label for="mlms-proof-file">Optional local reference</label><input class="mlms-proof-file" id="mlms-proof-file" name="proof_file" type="file" accept="image/*,.pdf,.txt,.md,.doc,.docx"></div>
</div>
<div class="mlms-proof-actions">
<p class="mlms-proof-status" data-proof-status aria-live="polite"></p>
<div class="mlms-actions"><button class="mlms-btn mlms-btn-secondary" type="button" data-proof-close>Cancel</button><button class="mlms-btn mlms-btn-primary" type="submit">Save proof →</button></div>
</div>
</form>
</div>
</div>
</div>
<?php
return ob_get_clean();
});
add_shortcode('matthew_login', function () {
$redirect_url = matthew_lms_mvp_redirect_url('platform/');
$result = $GLOBALS['mlms_login_result'] ?? null;
$google_message = matthew_lms_mvp_take_google_oauth_message();
$google_url = matthew_lms_mvp_google_oauth_url('login', $redirect_url);
$identifier = sanitize_text_field(wp_unslash($_POST['mlms_login_identifier'] ?? ''));
$current_user = wp_get_current_user();
ob_start();
?>
<div class="mlms-page"><section class="mlms-onboarding-hero"><div class="mlms-shell">
<div class="mlms-login-grid">
<div class="mlms-onboarding-copy">
<div class="mlms-eyebrow mlms-pill">Unggul account access</div>
<h1 class="mlms-h1">Welcome back to <span class="mlms-gradient-text">AI-powered work</span>.</h1>
</div>
<div class="mlms-form-card mlms-login-card" id="mlms-login-card">
<?php if (is_user_logged_in()): ?>
<div class="mlms-inline-note"><strong>Youre already signed in.</strong> Continue as <?php echo esc_html($current_user->user_email ?: $current_user->display_name); ?>.</div>
<div class="mlms-actions"><a class="mlms-btn mlms-btn-primary" href="<?php echo esc_url(matthew_lms_mvp_public_url('platform/')); ?>">Profile →</a><a class="mlms-btn mlms-btn-secondary" href="<?php echo esc_url(matthew_lms_mvp_public_url('platform/course/')); ?>">Lessons Dashboard</a><a class="mlms-btn mlms-btn-secondary" href="<?php echo esc_url(wp_logout_url(matthew_lms_mvp_public_url('log-in/'))); ?>">Log out</a></div>
<?php else: ?>
<h2>Log in to Unggul.</h2>
<p>Use Google for one-click access, or log in with your email/username and password.</p>
<?php if (isset($_GET['mlms_google_notice'])): ?>
<div class="mlms-inline-note"><strong>Google login is ready to connect.</strong> Add Google OAuth client credentials to enable real Gmail authentication. You can still log in with your password now.</div>
<?php endif; ?>
<?php if (is_array($google_message)): ?>
<div class="<?php echo !empty($google_message['success']) ? 'mlms-inline-note' : 'mlms-error-note'; ?>"><strong><?php echo !empty($google_message['success']) ? 'Google connected.' : 'Google login needs attention.'; ?></strong> <?php echo esc_html($google_message['message'] ?? ''); ?></div>
<?php endif; ?>
<?php if (is_array($result) && empty($result['success'])): ?>
<div class="mlms-error-note"><strong>Login failed.</strong> <?php echo esc_html($result['message']); ?></div>
<?php endif; ?>
<a class="mlms-btn mlms-btn-secondary mlms-google-button" href="<?php echo esc_url($google_url); ?>"><span class="mlms-google-mark">G</span> Continue with Google</a>
<div class="mlms-divider">or use password</div>
<form method="post" class="mlms-login-form" novalidate>
<?php wp_nonce_field('mlms_login_submit', 'mlms_login_nonce'); ?>
<input type="hidden" name="redirect_to" value="<?php echo esc_attr($redirect_url); ?>">
<div class="mlms-field"><label for="mlms_login_identifier">Email or username</label><input id="mlms_login_identifier" name="mlms_login_identifier" type="text" value="<?php echo esc_attr($identifier); ?>" autocomplete="username" required placeholder="you@example.com"></div>
<div class="mlms-field"><label for="mlms_login_password">Password</label><input id="mlms_login_password" name="mlms_login_password" type="password" autocomplete="current-password" required placeholder="Your password"></div>
<div class="mlms-login-actions">
<label class="mlms-checkbox"><input type="checkbox" name="mlms_login_remember" value="1" checked> Remember me</label>
<a class="mlms-login-link" href="<?php echo esc_url(wp_lostpassword_url($redirect_url)); ?>">Forgot password?</a>
</div>
<button class="mlms-btn mlms-btn-primary mlms-login-submit" type="submit">Log in →</button>
</form>
<p class="mlms-login-footnote">Need a Unggul account? <a href="<?php echo esc_url(add_query_arg('redirect_to', rawurlencode($redirect_url), matthew_lms_mvp_public_url('sign-up/'))); ?>">Start onboarding</a>.</p>
<?php endif; ?>
</div>
</div>
</div></section></div>
<?php
return ob_get_clean();
});
add_shortcode('matthew_signup_onboarding', function () {
$result = $GLOBALS['mlms_signup_result'] ?? null;
$redirect_url = matthew_lms_mvp_redirect_url('platform/');
$google_message = matthew_lms_mvp_take_google_oauth_message();
$current_user = wp_get_current_user();
$is_logged_in = is_user_logged_in();
$prefill_email = $is_logged_in ? $current_user->user_email : sanitize_email(wp_unslash($_POST['mlms_signup_email'] ?? ''));
$start_step = $is_logged_in ? 2 : 1;
$google_url = matthew_lms_mvp_google_oauth_url('signup', $redirect_url);
$role_options = matthew_lms_mvp_role_options();
ob_start();
?>
<div class="mlms-page"><section class="mlms-onboarding-hero"><div class="mlms-shell">
<div class="mlms-signup-grid" id="mlms-signup-flow" data-start-step="<?php echo esc_attr((string) $start_step); ?>">
<div class="mlms-onboarding-copy">
<p class="mlms-kicker">Sign Up & Onboarding</p>
<h1 class="mlms-h1">Create your Unggul profile <span style="color:var(--mlms-primary);">in under a minute.</span></h1>
</div>
<div class="mlms-form-card mlms-onboarding-card">
<?php if (isset($_GET['mlms_gate'])): ?>
<div class="mlms-inline-note"><strong>Create an account or log in to continue.</strong> Your Unggul profile and lessons dashboard are available after registration or login.</div>
<?php endif; ?>
<?php if (isset($_GET['mlms_google_notice'])): ?>
<div class="mlms-inline-note"><strong>Google sign-in is ready to connect.</strong> Add Google OAuth client credentials to enable real Gmail authentication. You can still sign up with email now.</div>
<?php endif; ?>
<?php if (isset($_GET['mlms_google_connected'])): ?>
<div class="mlms-inline-note"><strong>Google connected.</strong> Two quick questions and Unggul will build your starter profile.</div>
<?php endif; ?>
<?php if (is_array($google_message)): ?>
<div class="<?php echo !empty($google_message['success']) ? 'mlms-inline-note' : 'mlms-error-note'; ?>"><strong><?php echo !empty($google_message['success']) ? 'Google connected.' : 'Google sign-in needs attention.'; ?></strong> <?php echo esc_html($google_message['message'] ?? ''); ?></div>
<?php endif; ?>
<?php if (is_array($result) && empty($result['success'])): ?>
<div class="mlms-error-note"><strong>Almost there.</strong> <?php echo esc_html($result['message']); ?></div>
<?php endif; ?>
<form method="post" id="mlms-onboarding-form" novalidate>
<?php wp_nonce_field('mlms_signup_submit', 'mlms_signup_nonce'); ?>
<input type="hidden" name="redirect_to" value="<?php echo esc_attr($redirect_url); ?>">
<div class="mlms-onboarding-progress" aria-label="Onboarding progress"><span class="<?php echo $start_step >= 1 ? 'active' : ''; ?>"></span><span class="<?php echo $start_step >= 2 ? 'active' : ''; ?>"></span><span class="<?php echo $start_step >= 3 ? 'active' : ''; ?>"></span></div>
<div class="mlms-onboarding-panel <?php echo $start_step === 1 ? 'active' : ''; ?>" data-step="1">
<p class="mlms-onboarding-step-label">Step 1 of 3</p>
<h2>First, create your account.</h2>
<p>No long form. Use Google, or enter just email and password.</p>
<?php if (!$is_logged_in): ?>
<a class="mlms-btn mlms-btn-secondary mlms-google-button" href="<?php echo esc_url($google_url); ?>"><span class="mlms-google-mark">G</span> Continue with Google</a>
<div class="mlms-divider">or use email</div>
<div class="mlms-form-grid">
<div class="mlms-field"><label for="mlms_signup_email">Email</label><input id="mlms_signup_email" name="mlms_signup_email" type="email" value="<?php echo esc_attr($prefill_email); ?>" autocomplete="email" required placeholder="you@example.com"></div>
<div class="mlms-field"><label for="mlms_signup_password">Password</label><input id="mlms_signup_password" name="mlms_signup_password" type="password" autocomplete="new-password" minlength="8" required placeholder="At least 8 characters"></div>
</div>
<?php else: ?>
<div class="mlms-inline-note"><strong>Youre signed in.</strong> Well finish onboarding for <?php echo esc_html($prefill_email); ?>.</div>
<input type="hidden" name="mlms_signup_email" value="<?php echo esc_attr($prefill_email); ?>">
<?php endif; ?>
<div class="mlms-onboarding-actions"><button class="mlms-btn mlms-btn-primary" type="button" data-mlms-next>Continue →</button></div>
</div>
<div class="mlms-onboarding-panel <?php echo $start_step === 2 ? 'active' : ''; ?>" data-step="2">
<p class="mlms-onboarding-step-label">Step 2 of 3</p>
<h2>What kind of work do you do?</h2>
<p>Pick the closest role. Unggul will personalize examples and first projects around it.</p>
<div class="mlms-role-picker" role="radiogroup" aria-label="Functional role">
<?php foreach ($role_options as $label => $meta): ?>
<label class="mlms-role-card"><input type="radio" name="mlms_signup_role" value="<?php echo esc_attr($label); ?>" required><span class="mlms-role-icon"><?php echo esc_html($meta[0]); ?></span><span><strong><?php echo esc_html($label); ?></strong><small><?php echo esc_html($meta[1]); ?></small></span></label>
<?php endforeach; ?>
</div>
<div class="mlms-onboarding-actions"><button class="mlms-btn mlms-btn-secondary" type="button" data-mlms-prev>Back</button><button class="mlms-btn mlms-btn-primary" type="button" data-mlms-next>Continue →</button></div>
</div>
<div class="mlms-onboarding-panel <?php echo $start_step === 3 ? 'active' : ''; ?>" data-step="3">
<p class="mlms-onboarding-step-label">Step 3 of 3</p>
<h2>What should Unggul help you learn first?</h2>
<p>Use normal language. Add LinkedIn or GitHub only if it helps Unggul understand your background.</p>
<div class="mlms-field"><label for="mlms_signup_goal">Learning goal</label><textarea id="mlms_signup_goal" name="mlms_signup_goal" required placeholder="Example: I want to use AI to speed up campaign planning, reporting, and team handoffs."><?php echo esc_textarea(wp_unslash($_POST['mlms_signup_goal'] ?? '')); ?></textarea></div>
<div class="mlms-field"><label for="mlms_profile_link">LinkedIn or GitHub URL <span style="font-weight:650;color:var(--mlms-soft)">(optional)</span></label><input id="mlms_profile_link" name="mlms_profile_link" type="url" value="<?php echo esc_attr(esc_url_raw(wp_unslash($_POST['mlms_profile_link'] ?? ''))); ?>" placeholder="https://linkedin.com/in/yourname"></div>
<div class="mlms-onboarding-actions"><button class="mlms-btn mlms-btn-secondary" type="button" data-mlms-prev>Back</button><button class="mlms-btn mlms-btn-primary" type="submit">Build my Unggul profile →</button></div>
<div class="mlms-building-profile" aria-live="polite"><strong>Building your profile…</strong><p>Unggul is turning your role, goals, and optional linked profile into a starter learning path.</p><div class="mlms-loading-dots"><i></i><i></i><i></i></div></div>
</div>
</form>
</div>
</div>
</div></section></div>
<?php
return ob_get_clean();
});
add_shortcode('matthew_career_coach', function () {
$result = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['mlms_coach_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['mlms_coach_nonce'])), 'mlms_coach_submit')) {
$result = matthew_lms_mvp_handle_coach_submission();
}
ob_start();
?>
<div class="mlms-page"><section class="mlms-section"><div class="mlms-shell">
<div class="mlms-section-head"><div><p class="mlms-kicker">Free AI Career Coach</p><h1 class="mlms-h2">Get a calm, practical path into AI-native work.</h1><p class="mlms-section-copy">Upload a resume or describe your current role. The coach identifies skill gaps, recommends next steps, and shows how Unggul can turn progress into a visible credential.</p></div><span class="mlms-pill">FREE • no course purchase needed</span></div>
<div class="mlms-form-card mlms-coach-card">
<?php if (is_array($result) && !empty($result['success'])): ?>
<div class="mlms-notice"><strong>Your career coach plan is ready.</strong> Your request was saved for follow-up and the AI-generated guidance is below.</div>
<div class="mlms-ai-output"><?php echo esc_html($result['advice']); ?></div>
<?php elseif (is_array($result) && empty($result['success'])): ?>
<div class="mlms-notice"><strong>Saved with a note:</strong> <?php echo esc_html($result['message']); ?></div>
<?php endif; ?>
<form method="post" enctype="multipart/form-data" id="mlms-coach-form" data-mlms-coach-wizard>
<?php wp_nonce_field('mlms_coach_submit', 'mlms_coach_nonce'); ?>
<div class="mlms-coach-progress" aria-label="Consultation profile steps">
<button type="button" class="active" data-mlms-coach-jump="0"><span>1</span><strong>Basics</strong><small>Who you are</small></button>
<button type="button" data-mlms-coach-jump="1"><span>2</span><strong>Current work</strong><small>Where you are</small></button>
<button type="button" data-mlms-coach-jump="2"><span>3</span><strong>AI goal</strong><small>What should change</small></button>
<button type="button" data-mlms-coach-jump="3"><span>4</span><strong>Review</strong><small>Send to coach</small></button>
</div>
<p class="mlms-help">We use your answers only to generate this coaching plan and save your request for follow-up. Complete one short step at a time.</p>
<div class="mlms-coach-panel active" data-mlms-coach-panel>
<p class="mlms-onboarding-step-label">Step 1 of 4 · Your consultation basics</p>
<h2>Start with your contact details.</h2>
<p>Unggul uses this to label your consultation profile and make the follow-up feel personal.</p>
<div class="mlms-form-grid">
<div class="mlms-field"><label for="mlms_name">Name</label><input id="mlms_name" name="mlms_name" required placeholder="Alex Morgan" value="<?php echo esc_attr(sanitize_text_field(wp_unslash($_POST['mlms_name'] ?? ''))); ?>"></div>
<div class="mlms-field"><label for="mlms_email">Email</label><input id="mlms_email" name="mlms_email" type="email" required placeholder="alex@example.com" value="<?php echo esc_attr(sanitize_email(wp_unslash($_POST['mlms_email'] ?? ''))); ?>"></div>
</div>
<div class="mlms-coach-tip"><strong>Why this matters:</strong> your coach plan is saved as a private request so the team can follow up with next-step guidance.</div>
<div class="mlms-onboarding-actions"><button class="mlms-btn mlms-btn-primary" type="button" data-mlms-coach-next>Next: current work →</button></div>
</div>
<div class="mlms-coach-panel" data-mlms-coach-panel>
<p class="mlms-onboarding-step-label">Step 2 of 4 · Where you are now</p>
<h2>Tell Unggul your current and target role.</h2>
<p>This gives the coach enough context to compare todays responsibilities with the AI-native role you want.</p>
<div class="mlms-form-grid">
<div class="mlms-field"><label for="mlms_current_role">Current role</label><input id="mlms_current_role" name="mlms_current_role" required placeholder="Marketing Director" value="<?php echo esc_attr(sanitize_text_field(wp_unslash($_POST['mlms_current_role'] ?? ''))); ?>"></div>
<div class="mlms-field"><label for="mlms_target_role">Target role / aspiration</label><input id="mlms_target_role" name="mlms_target_role" required placeholder="AI-native growth leader" value="<?php echo esc_attr(sanitize_text_field(wp_unslash($_POST['mlms_target_role'] ?? ''))); ?>"></div>
</div>
<div class="mlms-coach-tip"><strong>Tip:</strong> if you are not sure about the target role, describe the outcome instead, like “lead a small team using AI workflows.”</div>
<div class="mlms-onboarding-actions"><button class="mlms-btn mlms-btn-secondary" type="button" data-mlms-coach-prev>Back</button><button class="mlms-btn mlms-btn-primary" type="button" data-mlms-coach-next>Next: AI goal →</button></div>
</div>
<div class="mlms-coach-panel" data-mlms-coach-panel>
<p class="mlms-onboarding-step-label">Step 3 of 4 · What should feel easier</p>
<h2>Describe the work problem you want AI to solve.</h2>
<p>Use normal language. Mention repetitive tasks, decisions, reporting, client work, handoffs, or the career move you want to make.</p>
<div class="mlms-field"><label for="mlms_goal">What would make AI feel immediately useful at work?</label><textarea id="mlms_goal" name="mlms_goal" required placeholder="Tell the coach where you feel behind, what you want to automate, or what career outcome matters most."><?php echo esc_textarea(sanitize_textarea_field(wp_unslash($_POST['mlms_goal'] ?? ''))); ?></textarea></div>
<div class="mlms-coach-tip"><strong>Example:</strong> “I spend too much time turning meeting notes into client updates and want a repeatable AI workflow.”</div>
<div class="mlms-onboarding-actions"><button class="mlms-btn mlms-btn-secondary" type="button" data-mlms-coach-prev>Back</button><button class="mlms-btn mlms-btn-primary" type="button" data-mlms-coach-next>Next: review →</button></div>
</div>
<div class="mlms-coach-panel" data-mlms-coach-panel>
<p class="mlms-onboarding-step-label">Step 4 of 4 · Optional proof and submit</p>
<h2>Attach extra context, then generate your path.</h2>
<p>Your typed answers power the instant AI plan. A resume can help the team understand your background for follow-up.</p>
<div class="mlms-field"><label for="mlms_resume">Resume upload (optional)</label><input id="mlms_resume" name="mlms_resume" type="file" accept=".pdf,.doc,.docx,.txt"><p class="mlms-help">Privacy note: the file is attached to your follow-up request; this instant AI plan is based on your typed answers.</p></div>
<div class="mlms-coach-review" aria-live="polite">
<strong>Quick profile review</strong>
<ul>
<li><span>Name</span><b data-mlms-review="mlms_name">Not added yet</b></li>
<li><span>Email</span><b data-mlms-review="mlms_email">Not added yet</b></li>
<li><span>Current role</span><b data-mlms-review="mlms_current_role">Not added yet</b></li>
<li><span>Target</span><b data-mlms-review="mlms_target_role">Not added yet</b></li>
</ul>
</div>
<div class="mlms-onboarding-actions"><button class="mlms-btn mlms-btn-secondary" type="button" data-mlms-coach-prev>Back</button><button class="mlms-btn mlms-btn-primary" type="submit">Generate my AI career path →</button></div>
<div class="mlms-building-profile mlms-coach-loading" aria-live="polite"><strong>Preparing your consultation profile…</strong><p>Unggul is reading your answers and building a practical AI career path.</p><div class="mlms-loading-dots"><i></i><i></i><i></i></div></div>
</div>
</form>
</div>
</div></section></div>
<?php
return ob_get_clean();
});
function matthew_lms_mvp_handle_coach_submission(): array {
$name = sanitize_text_field(wp_unslash($_POST['mlms_name'] ?? ''));
$email = sanitize_email(wp_unslash($_POST['mlms_email'] ?? ''));
$current = sanitize_text_field(wp_unslash($_POST['mlms_current_role'] ?? ''));
$target = sanitize_text_field(wp_unslash($_POST['mlms_target_role'] ?? ''));
$goal = sanitize_textarea_field(wp_unslash($_POST['mlms_goal'] ?? ''));
if (!$name || !$email || !$current || !$target || !$goal) {
return ['success' => false, 'message' => 'Please complete all required fields.'];
}
$post_id = wp_insert_post([
'post_type' => 'mlms_coach_request',
'post_status' => 'private',
'post_title' => sprintf('%s — %s to %s', $name, $current, $target),
'post_content' => "Name: $name\nEmail: $email\nCurrent role: $current\nTarget role: $target\nGoal:\n$goal",
], true);
if (is_wp_error($post_id)) {
return ['success' => false, 'message' => $post_id->get_error_message()];
}
foreach (['name'=>$name,'email'=>$email,'current_role'=>$current,'target_role'=>$target,'goal'=>$goal] as $key => $value) {
update_post_meta($post_id, '_mlms_' . $key, $value);
}
if (!empty($_FILES['mlms_resume']['name'])) {
require_once ABSPATH . 'wp-admin/includes/file.php';
$upload = wp_handle_upload($_FILES['mlms_resume'], ['test_form' => false]);
if (!empty($upload['url'])) {
update_post_meta($post_id, '_mlms_resume_url', esc_url_raw($upload['url']));
}
}
$advice = matthew_lms_mvp_generate_ai_advice($name, $current, $target, $goal);
update_post_meta($post_id, '_mlms_ai_advice', $advice);
return ['success' => true, 'advice' => $advice, 'post_id' => $post_id];
}
function matthew_lms_mvp_generate_ai_advice(string $name, string $current, string $target, string $goal): string {
$fallback = "Hi $name — here is a first AI-native career path:\n\n1. Immediate win: choose one weekly workflow in your $current role and redesign it with AI assistance.\n2. Skill gap: learn prompt framing, evaluation, workflow mapping, and safe handoff from AI output to human decision-making.\n3. Build task: create a reusable AI playbook for the goal you described: $goal\n4. Credential path: complete Phase 1 for AI workflow fluency, Phase 2 for role-specific automation, and Phase 3 for a portfolio-ready AI operator certification.\n\nNext step: start with the first lesson and turn one real task into a shareable portfolio artifact.";
$api = dirname(__DIR__, 3) . '/ai/LocalAIApi.php';
if (!file_exists($api)) { return $fallback; }
require_once $api;
if (!class_exists('LocalAIApi')) { return $fallback; }
$prompt = "Create a concise, encouraging AI career coaching plan for a non-technical professional. Name: $name. Current role: $current. Target role: $target. Goal/context: $goal. Include: personalized career path, top 3 skill gaps, 3 next steps, and how an AI-native operator certification helps. Keep under 220 words, calm and premium.";
$resp = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => 'You are Unggul, a calm expert AI career coach for non-technical professionals. Be practical, specific, reassuring, and concise.'],
['role' => 'user', 'content' => $prompt],
],
], ['poll_interval' => 2, 'poll_timeout' => 30, 'timeout' => 20]);
if (!empty($resp['success'])) {
$text = LocalAIApi::extractText($resp);
if (is_string($text) && trim($text) !== '') { return trim($text); }
}
return $fallback;
}
add_filter('manage_mlms_coach_request_posts_columns', function ($columns) {
$columns['mlms_email'] = 'Email';
$columns['mlms_current'] = 'Current Role';
$columns['mlms_target'] = 'Target Role';
return $columns;
});
add_action('manage_mlms_coach_request_posts_custom_column', function ($column, $post_id) {
if ($column === 'mlms_email') { echo esc_html(get_post_meta($post_id, '_mlms_email', true)); }
if ($column === 'mlms_current') { echo esc_html(get_post_meta($post_id, '_mlms_current_role', true)); }
if ($column === 'mlms_target') { echo esc_html(get_post_meta($post_id, '_mlms_target_role', true)); }
}, 10, 2);