#!/usr/bin/env python3
"""
rowjobs.site
rowjobs
Dependencies:
    • jinja2 (install via pip install jinja2)
    • BeautifulSoup (install via pip install beautifulsoup4)
    • Python built-ins: re, random, datetime, argparse, os, json
"""

import os
import json
import re
import random
import datetime
import argparse
from jinja2 import Template
from bs4 import BeautifulSoup

# Define the main domain for constructing job URLs.
MAIN_DOMAIN = "https://rowjobs.site.com"

# Global set to track used random suffixes.
used_suffixes = set()

def generate_random_suffix():
    """Generate a unique 6-digit random number as a string."""
    global used_suffixes
    candidate = ''.join(random.choices('0123456789', k=6))
    while candidate in used_suffixes:
        candidate = ''.join(random.choices('0123456789', k=6))
    used_suffixes.add(candidate)
    return candidate

def slugify(title):
    """
    Create a URL-friendly slug from the job title.
    Converts to lower-case, replaces spaces with hyphens, and removes unwanted characters.
    """
    slug = title.lower()
    slug = re.sub(r'[^a-z0-9\s-]', '', slug)
    slug = re.sub(r'\s+', '-', slug).strip('-')
    return slug

def clean_visible_body(html):
    """
    Clean the job body HTML by:
      • Removing all hyperlink tags (<a> and </a>) while preserving their inner text.
      • Unwrapping all <span> tags (i.e. remove the span tags but keep their inner content).
      • Removing any class attributes from all remaining tags.
    """
    soup = BeautifulSoup(html, "html.parser")
    # Remove hyperlink tags but preserve their inner text.
    for a_tag in soup.find_all('a'):
        a_tag.unwrap()
    # Unwrap all span tags (do not decompose, so inner text remains)
    for span in soup.find_all('span'):
        span.unwrap()
    # Remove class attributes from all tags.
    for tag in soup.find_all(True):
        tag.attrs.pop('class', None)
    return str(soup)

def ensure_html_linebreaks(text):
    """
    If the cleaned text does not contain HTML tags,
    replace newline characters with <br> tags and also convert \u0095 to an HTML bullet.
    """
    if "<" not in text:  # No HTML tags found, assume plain text.
        text = text.replace("\u0095", "&bull;")
        text = text.replace("\n", "<br>")
    return text

def remove_html_tags(html):
    """
    Remove all HTML tags from a string, but first convert <br> and </p> tags to newline characters.
    """
    # Convert <br> tags to newline characters.
    html = re.sub(r'(?i)<br\s*/?>', '\n', html)
    # Convert closing </p> tags to newline characters.
    html = re.sub(r'(?i)</p>', '\n', html)
    # Remove all remaining HTML tags.
    return re.sub(r'<[^>]+>', '', html)

def extract_salary(detail):
    """
    Extract a single numeric salary value from a string (e.g., '$25 per hour' -> '25').
    """
    match = re.search(r'\$([\d,\.]+)', detail)
    if match:
        return match.group(1).replace(',', '')
    return None

def extract_salary_range(detail):
    """
    Extract a salary range from a string.
    Expected formats: "$18-$25 per hour" or "$360K–$920K a year".
    Returns a tuple: (min_salary, max_salary, unit) or None.
    """
    pattern = r'\$([\d,\.]+)\s*(K?)[-–]\s*\$([\d,\.]+)\s*(K?)\s*(?:per\s+([\w/]+))?'
    m = re.search(pattern, detail, re.IGNORECASE)
    if m:
        lower_str, lower_mult, upper_str, upper_mult, unit = m.groups()
        def convert(num_str, multiplier):
            num_str = num_str.replace(',', '')
            try:
                value = float(num_str)
            except ValueError:
                value = 0
            if multiplier.upper() == 'K':
                value *= 1000
            return int(value)
        lower_val = convert(lower_str, lower_mult)
        upper_val = convert(upper_str, upper_mult)
        unit = unit.upper() if unit else ""
        return (lower_val, upper_val, unit)
    return None

def format_employment_type(emp_type):
    """
    Convert an employment type string like "FULL_TIME, PART_TIME" into a more human-friendly version.
    """
    parts = [p.strip() for p in emp_type.split(",")]
    return ", ".join(part.replace("_", " ").title() for part in parts)

# HTML template.
template_html = r"""<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ job_title }}</title>
        
        <meta name="description" content="{{ job_title | lower }}, Work At Home Jobs, USA Careers">
        <meta name="keywords" content="remote job, {{ job_title | lower }}, work from home, Part-Time remote jobs, Data Entry Remote Jobs, USA jobs, USA careers">
        <meta name="robots" content="index, follow">
        <meta name="author" content="rowjobs">
                            
        <meta property="og:title" content="{{ job_title }} | Remote Jobs USA">
        <meta property="og:description" content="{{ og_description | escape }}">
        <meta property="og:type" content="article">
        <meta property="og:url" content="{{ job_url }}">
        <meta property="article:published_time" content="{{ date_posted }}">
        <meta property="article:modified_time" content="{{ date_posted }}">
                            
        <link rel="canonical" href="{{ job_url }}" />
        <link rel="icon" type="image/x-icon" href="/favicon.ico">
        
        <style>
            /* Base Styles */
            :root {
                --blue-500: #3B82F6;
                --blue-600: #2563EB;
                --blue-700: #1D4ED8;
                --blue-800: #1E40AF;
                --gray-50: #F9FAFB;
                --gray-100: #F3F4F6;
                --gray-200: #E5E7EB;
                --gray-300: #D1D5DB;
                --gray-400: #9CA3AF;
                --gray-500: #6B7280;
                --gray-600: #4B5563;
                --gray-700: #374151;
                --gray-800: #1F2937;
                --gray-900: #111827;
            }
            /* Reset */
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }
            body {
                margin: 0;
                font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
                background-color: var(--gray-50);
                color: var(--gray-900);
                line-height: 1.5;
                min-height: 100vh;
                display: flex;
                flex-direction: column;
            }
            /* Typography */
            h1 {
                font-size: 2.5rem;
                font-weight: 700;
                margin-bottom: 1rem;
            }
            h2 {
                font-size: 1.5rem;
                font-weight: 600;
                margin-bottom: 0.5rem;
            }
            p {
                margin-bottom: 1rem;
            }
            /* Links and Buttons */
            a {
                color: inherit;
                text-decoration: none;
            }
            a:hover {
                text-decoration: none;
            }
            button {
                text-decoration: none;
            }
            .no-results a {
                color: var(--blue-600);
                text-decoration: none;
            }
            .no-results a:hover {
                color: var(--blue-700);
                text-decoration: none;
            }
            /* Layout */
            .max-w-4xl { max-width: 56rem; }
            .max-w-7xl { max-width: 80rem; }
            .mx-auto { margin-left: auto; margin-right: auto; }
            .px-4 { padding-left: 1rem; padding-right: 1rem; }
            .py-8 { padding-top: 2rem; padding-bottom: 2rem; }
            .mt-16 { margin-top: 4rem; }
            .mb-8 { margin-bottom: 2rem; }
            .mb-6 { margin-bottom: 1.5rem; }
            .mb-4 { margin-bottom: 1rem; }
            .mr-2 { margin-right: 0.5rem; }
            .ml-2 { margin-left: 0.5rem; }
            .p-6 { padding: 1.5rem; }
            /* Typography */
            .text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
            .text-2xl { font-size: 1.5rem; line-height: 2rem; }
            .text-xl { font-size: 1.25rem; line-height: 1.75rem; }
            .text-lg { font-size: 1.125rem; line-height: 1.75rem; }
            .text-base { font-size: 1rem; line-height: 1.5rem; }
            .text-sm { font-size: 0.875rem; line-height: 1.25rem; }
            .font-bold { font-weight: 700; }
            .font-semibold { font-weight: 600; }
            .font-medium { font-weight: 500; }
            .leading-relaxed { line-height: 1.625; }
            .tracking-wide { letter-spacing: 0.025em; }
            /* Colors */
            .text-gray-900 { color: var(--gray-900); }
            .text-gray-800 { color: var(--gray-800); }
            .text-gray-700 { color: var(--gray-700); }
            .text-gray-600 { color: var(--gray-600); }
            .text-gray-500 { color: var(--gray-500); }
            .text-blue-600 { color: var(--blue-600); }
            .text-white { color: white; }
            .bg-white { background-color: white; }
            .bg-blue-600 { background-color: var(--blue-600); }
            .hover:bg-blue-700:hover { background-color: var(--blue-700); }
            .hover:text-blue-600:hover { color: var(--blue-600); }
            .hover:bg-gray-50:hover { background-color: var(--gray-50); }
            /* Components */
            .rounded-xl { border-radius: 0.75rem; }
            .rounded-lg { border-radius: 0.5rem; }
            .rounded-full { border-radius: 9999px; }
            .shadow-lg { box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05); }
            .shadow-sm { box-shadow: 0 1px 2px 0 rgba(0,0,0,0.05); }
            .border { border: 1px solid var(--gray-300); }
            .border-t { border-top: 1px solid var(--gray-300); }
            /* Flex and Grid */
            .flex { display: flex; }
            .inline-flex { display: inline-flex; }
            .grid { display: grid; }
            .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            .items-center { align-items: center; }
            .justify-between { justify-content: space-between; }
            .gap-4 { gap: 1rem; }
            .gap-2 { gap: 0.5rem; }
            .flex-wrap { flex-wrap: wrap; }
            .flex-1 { flex: 1 1 0%; }
            /* Header */
            .fixed { position: fixed; }
            .w-full { width: 100%; }
            .top-0 { top: 0; }
            .z-50 { z-index: 50; }
            /* Job Content Specific */
            .job-content {
                color: var(--gray-800);
                font-size: 1rem;
                line-height: 1.75;
            }
            .job-content h2 {
                font-size: 1.5rem;
                font-weight: 600;
                color: var(--gray-900);
                margin-top: 2rem;
                margin-bottom: 1rem;
                scroll-margin-top: 80px;
            }
            .job-content p {
                margin-bottom: 1.5rem;
                color: var(--gray-700);
            }
            .job-content ul {
                margin: 1.5rem 0;
                padding-left: 1.5rem;
                list-style-type: disc;
            }
            .job-content li {
                margin: 0.5rem 0;
                color: var(--gray-700);
            }
            /* Similar Jobs Section */
            .job-card {
                background: white;
                padding: 1rem;
                border: 1px solid var(--gray-200);
                border-radius: 0.5rem;
                transition: all 0.2s ease;
            }
            .job-card:hover {
                border-color: var(--blue-500);
                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
                transform: translateY(-1px);
            }
            .job-card a {
                color: var(--gray-800);
                font-weight: 500;
                line-height: 1.4;
                display: block;
            }
            .job-card a:hover {
                color: var(--blue-600);
            }
            /* Sticky Apply Button */
            .sticky-apply {
                position: sticky;
                bottom: 2rem;
                text-align: center;
                z-index: 10;
                padding-top: 2rem;
                margin-top: 2rem;
                background: linear-gradient(
                    to top,
                    rgba(255,255,255,1) 0%,
                    rgba(255,255,255,0.9) 50%,
                    rgba(255,255,255,0) 100%
                );
            }
            /* Animations */
            .animate-fade {
                animation: fadeIn 0.5s ease-in;
            }
            @keyframes fadeIn {
                from { opacity: 0; transform: translateY(10px); }
                to { opacity: 1; transform: translateY(0); }
            }
            /* Transitions */
            .transition-all {
                transition: all 0.3s ease;
            }
            .transform {
                transform-origin: center;
            }
            .hover:scale-105:hover {
                transform: scale(1.05);
            }
            /* Responsive Design */
            @media (max-width: 640px) {
                .hidden { display: none; }
                .grid-cols-2 { grid-template-columns: 1fr; }
                .text-3xl { font-size: 1.5rem; }
                .px-8 { padding-left: 1rem; padding-right: 1rem; }
                .py-4 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
            }
            @media (min-width: 640px) {
                .sm:inline { display: inline; }
                .sm:flex { display: flex; }
            }
            @media (min-width: 768px) {
                .md:flex { display: flex; }
                .md:hidden { display: none; }
            }
            /* Icons */
            .icon {
                display: inline-block;
                width: 1.25em;
                height: 1.25em;
                vertical-align: -0.125em;
                fill: currentColor;
            }
            /* Apply Button Styles */
            .bg-gradient-primary {
                background: linear-gradient(135deg, var(--blue-500) 0%, var(--blue-700) 100%);
                box-shadow: 0 20px 40px -10px rgba(37, 99, 235, 0.5);
            }
            .btn-apply {
                transform-origin: center;
                will-change: transform;
                animation: float 3s ease-in-out infinite;
            }
            @keyframes float {
                0% { transform: translateY(0px); }
                50% { transform: translateY(-10px); }
                100% { transform: translateY(0px); }
            }
            .btn-apply:hover {
                transform: translateY(-2px) scale(1.05);
                box-shadow: 0 25px 50px -12px rgba(37, 99, 235, 0.6);
            }
            .btn-apply:active {
                transform: translateY(1px) scale(0.98);
            }
            /* Responsive Apply Button */
            @media (min-width: 768px) {
                .sticky-apply a {
                    padding: 1.25rem 3rem;
                    font-size: 1.5rem;
                }
                .sticky-apply .icon {
                    width: 1.5em;
                    height: 1.5em;
                    margin-right: 0.75rem;
                }
            }
            /* Similar Jobs Card Styles */
            .space-y-4 > * + * {
                margin-top: 1rem;
            }
            .space-y-4 {
                display: block;
            }
            .grid {
                display: grid;
                gap: 1rem;
            }
            /* Navigation Header */
            .nav-header {
                background: white;
                box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
                position: fixed;
                width: 100%;
                top: 0;
                z-index: 50;
            }
            .nav-header a {
                display: flex;
                align-items: center;
                gap: 0.5rem;
                color: var(--blue-600);
                text-decoration: none;
                font-weight: 600;
            }
            .nav-header a:hover {
                color: var(--blue-700);
            }
            .nav-header .icon {
                width: 1.5em;
                height: 1.5em;
                fill: currentColor;
            }
            .nav-header span {
                font-size: 1.125rem;
            }
            .nav-header .container {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 1rem;
                max-width: 80rem;
                margin: 0 auto;
            }
            .nav-header form {
                display: none;
                flex: 1;
                max-width: 32rem;
                margin: 0 1rem;
            }
            @media (min-width: 768px) {
                .nav-header form {
                    display: flex;
                    gap: 0.5rem;
                }
            }
            .nav-header input {
                flex: 1;
                padding: 0.5rem 1rem;
                border: 1px solid var(--gray-300);
                border-radius: 0.5rem;
                outline: none;
            }
            .nav-header input:focus {
                border-color: var(--blue-500);
                box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
            }
            .nav-header button {
                padding: 0.5rem 1rem;
                background: var(--blue-600);
                color: white;
                border: none;
                border-radius: 0.5rem;
                cursor: pointer;
                display: flex;
                align-items: center;
            }
            .nav-header button:hover {
                background: var(--blue-700);
            }
            /* Main Header */
            .header {
                background: linear-gradient(to right, var(--blue-600), var(--blue-800));
                color: white;
                padding: 4rem 1rem;
                text-align: center;
            }
            .header a {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                gap: 0.5rem;
                color: white;
                text-decoration: none;
                margin-bottom: 1rem;
            }
            .header a:hover {
                opacity: 0.9;
            }
            .header .icon {
                width: 2em;
                height: 2em;
                fill: currentColor;
            }
            .header span {
                font-size: 1.5rem;
                font-weight: 700;
            }
        </style>
        
        <svg style="display: none;">
            <symbol id="icon-location" viewBox="0 0 24 24">
                <path d="M12 0C7.802 0 4 3.403 4 7.602C4 11.8 7.469 16.812 12 24C16.531 16.812 20 11.8 20 7.602C20 3.403 16.199 0 12 0ZM12 11C10.343 11 9 9.657 9 8C9 6.343 10.343 5 12 5C13.657 5 15 6.343 15 8C15 9.657 13.657 11 12 11Z"/>
            </symbol>
            <symbol id="icon-clock" viewBox="0 0 24 24">
                <path d="M12 0C5.383 0 0 5.383 0 12C0 18.617 5.383 24 12 24C18.617 24 24 18.617 24 12C24 5.383 18.617 0 12 0ZM12 21C7.037 21 3 16.963 3 12C3 7.037 7.037 3 12 3C16.963 3 21 7.037 21 12C21 16.963 16.963 21 12 21ZM13 6H11V13L16.5 16.5L17.5 14.85L13 12V6Z"/>
            </symbol>
            <symbol id="icon-calendar" viewBox="0 0 24 24">
                <path d="M20 3H19V1H17V3H7V1H5V3H4C2.9 3 2 3.9 2 5V21C2 22.1 2.9 23 4 23H20C21.1 23 22 22.1 22 21V5C22 3.9 21.1 3 20 3ZM20 21H4V8H20V21Z"/>
            </symbol>
            <symbol id="icon-laptop-house" viewBox="0 0 640 512">
                <path d="M272 288h-64c-8.8 0-16 7.2-16 16v64c0 8.8 7.2 16 16 16h64c8.8 0 16-7.2 16-16v-64c0-8.8-7.2-16-16-16zm-32 64h-32v-32h32v32zm376-80v-32c0-13.3-10.7-24-24-24h-72v-40c0-13.3-10.7-24-24-24h-48V88c0-13.3-10.7-24-24-24h-24V24c0-13.3-10.7-24-24-24H168c-13.3 0-24 10.7-24 24v40h-24c-13.3 0-24 10.7-24 24v64H48c-13.3 0-24 10.7-24 24v40H24c-13.3 0-24 10.7-24 24v32c0 13.3 10.7 24 24 24h72v-40c0-13.3 10.7-24 24-24h48v40c0 13.3 10.7 24 24 24h48v40c0 13.3 10.7 24 24 24h24v40c0 13.3 10.7 24 24 24h304c13.3 0 24-10.7 24-24v-40h72c13.3 0 24-10.7 24-24zm-124 88c0 4.4-3.6 8-8 8H348c-4.4 0-8-3.6-8-8v-56c0-4.4 3.6-8 8-8h136c4.4 0 8 3.6 8 8v56zm0-88c0 4.4-3.6 8-8 8H348c-4.4 0-8-3.6-8-8v-56c0-4.4 3.6-8 8-8h136c4.4 0 8 3.6 8 8v56z"/>
            </symbol>
            <symbol id="icon-search" viewBox="0 0 512 512">
                <path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/>
            </symbol>
            <symbol id="icon-paper-plane" viewBox="0 0 512 512">
                <path d="M476 3.2L12.5 270.6c-18.1 10.4-15.8 35.6 2.2 43.2L121 358.4l287.3-253.2c5.5-4.9 13.3 2.6 8.6 8.3L176 407v80.5c0 23.6 28.5 32.9 42.5 15.8L282 426l124.6 52.2c14.2 6 30.4-2.9 33-18.2l72-432C515 7.8 493.3-6.8 476 3.2z"/>
            </symbol>
            <symbol id="icon-arrow-left" viewBox="0 0 448 512">
                <path d="M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z"/>
            </symbol>
        </svg>
        
        <script type="application/ld+json">
        {
            "@context": "https://schema.org",
            "@type": "JobPosting",
            "title": "{{ job_title }}",
            "datePosted": "{{ date_posted }}",
            "validThrough": "{{ valid_through }}",
            "description": "{{ job_description | escape }}",
            "url": "{{ job_url }}",
            "jobLocationType": "{{ job_location_type }}",
            "applicantLocationRequirements": [{"@type": "Country", "name": "{{ applicant_country }}"}],
            "employmentType": "{{ employment_type }}",
            "baseSalary": {
                "@type": "MonetaryAmount",
                "currency": "{{ currency }}",
                "value": {
                    {%- if salary_min is defined and salary_max is defined -%}
                        "minValue": "{{ salary_min }}",
                        "maxValue": "{{ salary_max }}",
                        "unitText": "{{ salary_unit }}"
                    {%- else -%}
                        "value": "{{ salary_value }}",
                        "unitText": "{{ salary_unit }}"
                    {%- endif -%}
                }
            },
            "hiringOrganization": {
                "@type": "Organization",
                "name": "{{ company_name }}",
                "sameAs": "{{ company_website }}"
            },
            "jobLocation": {
                "@type": "Place",
                "address": {
                    "@type": "PostalAddress",
                    "addressCountry": "{{ applicant_country }}"
                }
            }
        }
        </script>
        
        <script type="application/ld+json">
        {
            "@context": "https://schema.org",
            "@type": "BreadcrumbList",
            "itemListElement": [
                {
                    "@type": "ListItem",
                    "position": 1,
                    "name": "Home",
                    "item": "https://rowjobs.site"
                },
                {
                    "@type": "ListItem",
                    "position": 2,
                    "name": "{{ job_title }}",
                    "item": "{{ job_url }}"
                }
            ]
        }
        </script>
    </head>
    <body class="bg-gray-50">
        <header class="nav-header">
            <nav class="container">
                <a href="/" aria-label="Remote Jobs USA - Home">
                    <svg class="icon" aria-hidden="true">
                        <use href="#icon-laptop-house"/>
                    </svg>
                    <span>Remote Jobs USA</span>
                </a>
                <form action="/" method="GET" class="hidden md:flex gap-2 flex-1 max-w-md mx-4">
                    <input 
                        type="search" 
                        name="search" 
                        placeholder="Search jobs..." 
                        class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
                        aria-label="Search remote jobs"
                    >
                    <button type="submit" 
                            class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
                            aria-label="Submit search">
                        <svg class="icon" aria-hidden="true">
                            <use href="#icon-search"/>
                        </svg>
                    </button>
                </form>
            </nav>
        </header>
        
        <main class="max-w-4xl mx-auto px-4 py-8 mt-16 animate-fade">
            <div class="bg-white rounded-xl shadow-lg p-6 mb-8">
                <div class="flex items-center justify-between mb-6">
                    <h1 class="text-3xl font-bold text-gray-900">{{ job_title }}</h1>
                    <span class="text-gray-500 text-sm">Posted {{ date_posted }}</span>
                </div>
                
                <div class="flex flex-wrap gap-4 mb-6 text-sm text-gray-600">
                    <span class="flex items-center">
                        <span class="icon-container">
                            <svg class="icon"><use href="#icon-location"/></svg>
                        </span>
                        {{ location }}
                    </span>
                    <span class="flex items-center">
                        <span class="icon-container">
                            <svg class="icon"><use href="#icon-clock"/></svg>
                        </span>
                        {{ employment_type_display }}
                    </span>
                    <span class="flex items-center">
                        <span class="icon-container">
                            <svg class="icon"><use href="#icon-calendar"/></svg>
                        </span>
                        Immediate Start
                    </span>
                </div>

                <div class="prose max-w-none mb-8">
                  {{ job_body | safe }}
                </div>
                
                <div class="sticky-apply">
                    <a href="{{ apply_url }}" 
                       class="inline-flex items-center px-8 py-4 md:px-12 md:py-5 text-lg md:text-2xl font-semibold rounded-full shadow-lg text-white bg-blue-600 hover:bg-blue-700 transform hover:scale-105 transition-all"
                       aria-label="Apply now for {{ job_title }} position">
                        <svg class="icon mr-2 md:mr-3" style="width: 1.25em; height: 1.25em;" aria-hidden="true">
                            <use href="#icon-paper-plane"/>
                        </svg>
                        Apply Now
                    </a>
                </div>
                
                <div class="bg-white rounded-xl shadow-lg p-6 mb-8">
                    <h2 class="text-2xl font-bold text-gray-900 mb-4">Similar Jobs</h2>
                    <div class="grid grid-cols-2 gap-4">
                        <div class="space-y-4">
                            <h3 class="text-xl font-semibold text-gray-800 mb-4">Recent Jobs</h3>
                            {% for job in recent_jobs %}
                            <div class="job-card">
                                <a href="{{ job.job_url }}" class="block hover:text-blue-600 transition-colors">
                                    {{ job.job_title }}
                                </a>
                            </div>
                            {% endfor %}
                        </div>
                        <div class="space-y-4">
                            <h3 class="text-xl font-semibold text-gray-800 mb-4">You May Also Like</h3>
                            {% for job in suggested_jobs %}
                            <div class="job-card">
                                <a href="{{ job.job_url }}" class="block hover:text-blue-600 transition-colors">
                                    {{ job.job_title }}
                                </a>
                            </div>
                            {% endfor %}
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="text-center">
                <a href="/" class="inline-flex items-center px-6 py-3 border border-gray-300 shadow-sm text-base font-medium rounded-full text-gray-700 bg-white hover:bg-gray-50 hover:text-blue-600 transition-colors">
                    <svg class="icon mr-2">
                        <use href="#icon-arrow-left"/>
                    </svg>
                    Back to Job Board
                </a>
            </div>
        </main>
        
        <footer class="bg-white border-t mt-12 py-8">
            <div class="max-w-7xl mx-auto px-4 text-center text-gray-600">
                <p>Find the best remote jobs in USA - rowjobs</p>
            </div>
        </footer>
    </body>
</html>
"""

def process_job(job_record, force_remote=False, force_company=None, force_apply_url=None):
    """
    Process a single job record:
      • Generate an initial job URL from the job title.
      • Join the body parts preserving the original HTML.
      • Clean the job body by removing <a> tags (and unwrapping <span> tags and removing class attributes).
      • Create a plain text version for the schema description (converting <br> and </p> tags to newlines).
      • Extract salary data.
      • Apply any forced overrides (location, company, apply URL).
      • Create an OG description limited to the first 120 words.
    """
    job_title = job_record.get("job_title", "No Title")
    new_job_url = f"{MAIN_DOMAIN}/job/{slugify(job_title)}"
    
    body_parts = job_record.get("body_parts", [])
    raw_body = "".join(body_parts)
    visible_body = clean_visible_body(raw_body)
    # Ensure that if the visible body is plain text, we convert newlines to <br> tags
    visible_body = ensure_html_linebreaks(visible_body)
    
    plain_description = remove_html_tags(visible_body)
    truncated_og_description = " ".join(plain_description.split()[:120])
    
    company_name = job_record.get("company_name", "Unknown Company")
    if force_company:
        company_name = force_company
    
    location = job_record.get("location", "Unknown location")
    if force_remote:
        location = "Remote, USA"
    
    job_details = job_record.get("job_details", [])
    today = datetime.date.today()
    date_posted = today.isoformat()
    valid_through = (today + datetime.timedelta(days=300)).isoformat()
    
    full_time_pattern = re.compile(r'full[-_\s]*time', re.IGNORECASE)
    part_time_pattern = re.compile(r'part[-_\s]*time', re.IGNORECASE)
    employment_set = set()
    for detail in job_details:
        if full_time_pattern.search(detail):
            employment_set.add("FULL_TIME")
        if part_time_pattern.search(detail):
            employment_set.add("PART_TIME")
    if not employment_set:
        employment_set.add("FULL_TIME")
    employment_type = ", ".join(sorted(employment_set))
    employment_type_display = format_employment_type(employment_type)
    
    job_location_type = "ONSITE"
    if "remote" in job_record.get("location", "").lower():
        job_location_type = "TELECOMMUTE"
    if force_remote:
        job_location_type = "TELECOMMUTE"
    
    applicant_country = "US"
    company_website = MAIN_DOMAIN
    apply_url = force_apply_url if force_apply_url else new_job_url
    
    salary_min = None
    salary_max = None
    salary_value = None
    salary_unit = None
    for detail in job_details:
        if "$" in detail and ('-' in detail or '–' in detail):
            salary_range = extract_salary_range(detail)
            if salary_range:
                salary_min, salary_max, unit_extracted = salary_range
                salary_unit = unit_extracted if unit_extracted else ("HOUR" if "hour" in detail.lower() else "YEAR")
                break
    if salary_min is None or salary_max is None:
        for detail in job_details:
            if "$" in detail:
                extracted = extract_salary(detail)
                if extracted:
                    salary_value = extracted
                    if "hour" in detail.lower():
                        salary_unit = "HOUR"
                    elif "year" in detail.lower():
                        salary_unit = "YEAR"
                    else:
                        salary_unit = "MONTHLY"
                    break
    salary_data = {}
    if salary_min is not None and salary_max is not None:
        salary_data["salary_min"] = salary_min
        salary_data["salary_max"] = salary_max
        salary_data["salary_unit"] = salary_unit
    elif salary_value is not None:
        salary_data["salary_value"] = salary_value
        salary_data["salary_unit"] = salary_unit
    else:
        salary_data["salary_value"] = "Not Disclosed by Recruiter"
        salary_data["salary_unit"] = "MONTHLY"
    
    return {
        "job_title": job_title,
        "job_url": new_job_url,
        "apply_url": apply_url,
        "job_body": visible_body,
        "job_description": plain_description,
        "og_description": truncated_og_description,
        "company_name": company_name,
        "location": location,
        "date_posted": date_posted,
        "valid_through": valid_through,
        "employment_type": employment_type,
        "employment_type_display": employment_type_display,
        "currency": "USD",
        "applicant_country": applicant_country,
        "job_location_type": job_location_type,
        "company_website": company_website,
        **salary_data
    }

def generate_html(job_data, template_str):
    """Render the HTML content using the provided template and job data."""
    template = Template(template_str)
    return template.render(**job_data)

def main(ndjson_file, output_dir, force_company, force_apply_filepath):
    force_apply_url = None
    if force_apply_filepath:
        with open(force_apply_filepath, "r", encoding="utf-8") as f:
            force_apply_url = f.read().strip()
    
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    all_job_data = []
    with open(ndjson_file, "r", encoding="utf-8") as f:
        for idx, line in enumerate(f, start=1):
            line = line.strip()
            if not line:
                continue
            try:
                job_record = json.loads(line)
                # Set force_remote to True by default
                job_data = process_job(job_record, force_remote=True, force_company=force_company, force_apply_url=force_apply_url)
                all_job_data.append(job_data)
            except Exception as e:
                print(f"Error processing line {idx}: {e}")
    
    for job in all_job_data:
        file_identifier = generate_random_suffix()
        job["file_identifier"] = file_identifier
        job["job_url"] = f"{MAIN_DOMAIN}/job/{file_identifier}"
        if not force_apply_url:
            job["apply_url"] = job["job_url"]
    
    for current_job in all_job_data:
        other_jobs = [job for job in all_job_data if job is not current_job]
        recent_jobs = random.sample(other_jobs, min(10, len(other_jobs))) if other_jobs else []
        remaining_candidates = [job for job in other_jobs if job not in recent_jobs]
        suggested_jobs = random.sample(remaining_candidates, min(10, len(remaining_candidates))) if remaining_candidates else []
        current_job["recent_jobs"] = recent_jobs
        current_job["suggested_jobs"] = suggested_jobs
        
        file_name = f"{current_job['file_identifier']}.html"
        output_path = os.path.join(output_dir, file_name)
        html_content = generate_html(current_job, template_html)
        with open(output_path, "w", encoding="utf-8") as out_f:
            out_f.write(html_content)
        print(f"Generated: {output_path}")
    
    total_jobs = len(all_job_data)
    full_time_only = sum(1 for job in all_job_data if job["employment_type"] == "FULL_TIME")
    part_time_only = sum(1 for job in all_job_data if job["employment_type"] == "PART_TIME")
    both = sum(1 for job in all_job_data if job["employment_type"] == "FULL_TIME, PART_TIME")
    
    stats_output_path = os.path.join(output_dir, "job_statistics.txt")
    with open(stats_output_path, "w", encoding="utf-8") as stat_file:
         stat_file.write(f"Total jobs processed: {total_jobs}\n")
         stat_file.write(f"Full Time only: {full_time_only}\n")
         stat_file.write(f"Part Time only: {part_time_only}\n")
         stat_file.write(f"Both Full Time and Part Time: {both}\n")
    print(f"Statistics file generated: {stats_output_path}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Generate HTML files with job posting schema from NDJSON data using a refined template. All jobs are marked as TELECOMMUTE by default. Use --force-company to override the company name, and --force-apply to use a forced apply URL from a file."
    )
    parser.add_argument("ndjson_file", help="Path to the NDJSON file containing job records")
    parser.add_argument("output_dir", help="Directory for the generated HTML files")
    parser.add_argument("--force-company", type=str, default=None, help="Force the company name in the schema to this value")
    parser.add_argument("--force-apply", type=str, default=None, help="Path to a text file containing the forced apply URL")
    args = parser.parse_args()
    main(args.ndjson_file, args.output_dir, args.force_company, args.force_apply)