<?php

namespace CaptureRelay;

final class Documentation
{
    public function render(): void
    {
        $sections = $this->sections();

        echo '<header class="capture-relay-tab-intro"><h2>' . esc_html__('Documentation', 'capture-relay') . '</h2><p>'
            . esc_html__('A complete guide for website administrators, editors, and the people using the CaptureRelay mobile app.', 'capture-relay')
            . '</p></header>';
        echo '<div class="capture-relay-docs">';
        echo '<aside class="capture-relay-docs-toc" aria-label="' . esc_attr__('Documentation contents', 'capture-relay') . '"><strong>'
            . esc_html__('On this page', 'capture-relay') . '</strong><ol>';
        foreach ($sections as $section) {
            echo '<li><a href="#' . esc_attr($section['id']) . '">' . esc_html($section['title']) . '</a></li>';
        }
        echo '</ol></aside>';

        echo '<div class="capture-relay-docs-content">';
        foreach ($sections as $section) {
            $this->renderSection($section);
        }

        echo '<section id="support" class="capture-relay-docs-section capture-relay-docs-support"><h2>'
            . esc_html__('Questions and support', 'capture-relay') . '</h2><p>'
            . esc_html__('If this guide and the Diagnostics tab do not resolve a question, contact the CaptureRelay team. Include the WordPress version, plugin version, phone platform, app version, the exact message shown, and the approximate time of the event. Never send a device credential, authorization header, signing secret, WordPress password, or private server key.', 'capture-relay')
            . '</p><p><a class="button button-primary" href="' . esc_url(Config::CONTACT_URL) . '" target="_blank" rel="noopener noreferrer">'
            . esc_html__('Open the CaptureRelay contact form', 'capture-relay') . '</a></p></section>';
        echo '</div></div>';
    }

    private function sections(): array
    {
        return [
            [
                'id' => 'overview',
                'title' => __('How CaptureRelay works', 'capture-relay'),
                'paragraphs' => [
                    __('CaptureRelay sends camera photos or specifically selected device-library images directly from an approved iPhone or Android phone to this WordPress Media Library. Skyrocket Radio does not operate a central image relay or CaptureRelay account service; the app communicates with each selected WordPress website.', 'capture-relay'),
                    __('The mobile app prepares a separate copy for every selected destination. Each website controls its required pixel dimensions, final Media Library format, image quality, and maximum file size; the app user may supply a separate image title, descriptive alt text, and Photo Credit. The plugin verifies, sanitizes, and records each accepted delivery as a normal WordPress attachment. It never creates or publishes a post.', 'capture-relay'),
                ],
                'definitions' => [
                    __('App user', 'capture-relay') => __('The person taking or selecting images and choosing where they should be delivered.', 'capture-relay'),
                    __('Website administrator', 'capture-relay') => __('The WordPress administrator who chooses settings and approves, rejects, revokes, or blocks devices.', 'capture-relay'),
                    __('Destination', 'capture-relay') => __('An approved WordPress website selected in the app. One image may be sent to several destinations at once.', 'capture-relay'),
                    __('Media Library attachment', 'capture-relay') => __('The WordPress image record created after a successful upload, including its processed JPEG, title, caption, alt text, and CaptureRelay metadata.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'initial-setup',
                'title' => __('Initial website setup', 'capture-relay'),
                'paragraphs' => [
                    __('The first-activation notice remains visible until an administrator saves the Settings tab. Saving confirms that this website has reviewed its delivery, notification, security, and retention choices.', 'capture-relay'),
                ],
                'steps' => [
                    __('Open the Settings tab and confirm that mobile uploads and new requests are enabled.', 'capture-relay'),
                    __('Set the required image width and height, final Media Library format, image quality, and maximum upload size in KB. JPEG is the default. WebP is available when the site’s WordPress image engine supports it. A quality range of 75–85 is normally appropriate for fast-loading website images; 75 is the default.', 'capture-relay'),
                    __('Enter every notification recipient and choose whether the site should email for approval requests, successful uploads, or repeated requests.', 'capture-relay'),
                    __('Review the connection, authentication, upload, and pending-request limits. The defaults are designed to be practical while resisting automated abuse.', 'capture-relay'),
                    __('Review record-retention periods and decide whether deleting the plugin should also erase its operational and security data.', 'capture-relay'),
                    __('Save Settings. Then open Diagnostics and confirm HTTPS, the upload directory, image processing, REST availability, and approved credential health.', 'capture-relay'),
                ],
                'note' => __('Do not approve a device until the administrator recognizes its name, platform, and request source. CaptureRelay never requires a wp-config.php change.', 'capture-relay'),
            ],
            [
                'id' => 'app-experience',
                'title' => __('What app users can expect', 'capture-relay'),
                'paragraphs' => [
                    __('The app opens to a camera designed around the active destination’s exact aspect ratio. Users can capture a new image in portrait or landscape orientation, or choose one or many existing images through the phone’s system photo picker.', 'capture-relay'),
                    __('The crop guide represents the image that will be delivered. Portrait captures and source images with a different aspect ratio are cropped to fill each destination website’s required dimensions. The app then resizes and compresses a separate copy for each destination.', 'capture-relay'),
                    __('Phone and tablet layouts adapt to portrait, landscape, safe areas, camera cutouts, and system controls. The Websites screen uses a compact accordion: tap a website summary to reveal its local name, Photo Credit, connection controls, and status details.', 'capture-relay'),
                ],
                'bullets' => [
                    __('Camera permission is used only when taking a photo inside CaptureRelay.', 'capture-relay'),
                    __('The system photo picker gives CaptureRelay only the existing images the user explicitly selects. Selecting an existing image never creates another copy in the device library.', 'capture-relay'),
                    __('Save photos taken with CaptureRelay is enabled by default. When enabled, the app requests add-only library access and saves one original, uncropped camera photo to Apple Photos or the Android gallery before preparing destination copies. It does not scan the library.', 'capture-relay'),
                    __('If the device cannot save a local original, the app displays a warning but continues preparing the image for delivery. Users should resolve the permission or storage issue before relying on future local backups.', 'capture-relay'),
                    __('CaptureRelay does not request location permission and does not send GPS metadata.', 'capture-relay'),
                    __('The image title and alt text are blank by default and may remain blank. On supported devices, the app may offer an optional on-device suggestion that remains editable before delivery. A default or per-website Photo Credit may also be applied.', 'capture-relay'),
                    __('Users can enable several approved destinations and deliver one image to all of them. Success or failure is tracked independently for every destination.', 'capture-relay'),
                    __('Interrupted or rejected deliveries remain in the Upload Queue with a readable status so the user can retry or remove them.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'connect-device',
                'title' => __('Connect a device', 'capture-relay'),
                'steps' => [
                    __('In the mobile app, open Settings, choose Websites, and select Add Website.', 'capture-relay'),
                    __('Enter the complete HTTPS address of the WordPress website and a recognizable device name, such as a person’s name and phone model. The app remembers the last valid device name for the next request; administrators should still compare the platform, model, app version, and request source because display names are not security identifiers.', 'capture-relay'),
                    __('Tap Request Access. The app creates a protected pending request and checks periodically for the administrator’s decision.', 'capture-relay'),
                    __('In WordPress, open Tools, CaptureRelay, and the Connections tab. A menu count and administrator notice appear while requests await review. Email is also sent when approval-request email is enabled.', 'capture-relay'),
                    __('Compare the device name, platform, model, app version, request time, expiration, and source. Choose Approve only when the request is expected.', 'capture-relay'),
                    __('After approval, the app receives a unique credential for this website and stores it in iOS Keychain or Android Keystore-backed storage. The website becomes available as a delivery destination.', 'capture-relay'),
                    __('In the app, set the website’s local display name and Photo Credit if desired. Use Test to verify the connection. Required image dimensions are controlled by this website and are refreshed by the app automatically.', 'capture-relay'),
                ],
                'note' => __('A pending request cannot upload an image. Approval is required before the website issues an upload credential. Cancelling a pending request asks for confirmation, cancels it on the website, and removes that website entry from the app.', 'capture-relay'),
            ],
            [
                'id' => 'approval-decisions',
                'title' => __('Approve, reject, revoke, and block devices', 'capture-relay'),
                'definitions' => [
                    __('Approve', 'capture-relay') => __('Creates a new independent credential and permits the device to upload. Reconnecting the same device rotates and invalidates its prior credential.', 'capture-relay'),
                    __('Reject', 'capture-relay') => __('Closes the current request without approving it. The device may submit a new request later, subject to rate limits.', 'capture-relay'),
                    __('Reject & Block', 'capture-relay') => __('Rejects the request and stores a one-way device identity block so repeat requests from that device are refused until an administrator unblocks it.', 'capture-relay'),
                    __('Rename', 'capture-relay') => __('Changes only the administrator-facing display name. It does not change the phone or credential.', 'capture-relay'),
                    __('Revoke', 'capture-relay') => __('Immediately invalidates that device credential. Future tests and uploads fail until the device requests access and is approved again.', 'capture-relay'),
                    __('Revoke & Block', 'capture-relay') => __('Immediately invalidates the credential and also prevents that device from creating another request.', 'capture-relay'),
                    __('Unblock', 'capture-relay') => __('Removes the permanent block. It does not restore a revoked credential; the app user must request access again.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'delivery',
                'title' => __('Capture, prepare, and deliver images', 'capture-relay'),
                'steps' => [
                    __('Select one or more destinations in the camera’s destination selector. Destination selection remains usable in portrait and landscape orientation.', 'capture-relay'),
                    __('Take a photo with the in-app camera or choose one or many images with Photos. Flash and camera flip controls are available when supported by the phone.', 'capture-relay'),
                    __('When Save photos taken with CaptureRelay is enabled, the app first saves one original camera photo to the device library. It does this once per capture, not once per destination. Imported library images are not duplicated.', 'capture-relay'),
                    __('Review the crop and image details. The app creates a separate crop, resize, and compression job using each destination website’s current requirements.', 'capture-relay'),
                    __('Optionally enter a concise editorial title, descriptive alt text, and confirm the Photo Credit. Both text fields may be blank. On a supported device, Suggest with AI prepares editable title and alt-text suggestions locally; it never submits the image automatically.', 'capture-relay'),
                    __('Submit the image. Completed destinations show success; offline, rejected, oversized, or interrupted destinations stay in the queue for review.', 'capture-relay'),
                    __('Open the WordPress Media Library or Upload Activity tab to confirm the attachment and its delivery record.', 'capture-relay'),
                ],
                'bullets' => [
                    __('Required dimensions are controlled in this plugin’s Settings tab. Updated apps retrieve them automatically; older apps continue using their locally stored dimensions for backward compatibility.', 'capture-relay'),
                    __('The plugin receives horizontal JPEG output from the app, then safely rewrites it as the website’s selected JPEG or WebP Media Library format. It remains subject to the configured dimension, pixel, memory, and KB limits. This preserves compatibility with older released apps.', 'capture-relay'),
                    __('Server-side rewriting strips EXIF, GPS, XMP, IPTC, and other embedded image metadata before the image enters the Media Library.', 'capture-relay'),
                    __('Automatic retries use the same upload identifier, so an already completed delivery returns the original attachment instead of creating a duplicate.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'media-library',
                'title' => __('What appears in WordPress', 'capture-relay'),
                'bullets' => [
                    __('A successful delivery creates a normal JPEG or WebP attachment in the Media Library, as selected by this website administrator.', 'capture-relay'),
                    __('A compatible app may supply the attachment title and alt text separately. The title may remain blank; the processed JPEG still receives a sanitized server filename for storage. Older app versions continue using their supplied title as both the WordPress title and alt text.', 'capture-relay'),
                    __('Photo Credit is formatted using the configured caption format. Include the {credit} token wherever the supplied credit should appear.', 'capture-relay'),
                    __('The configured default copyright value is stored as attachment metadata when provided.', 'capture-relay'),
                    __('CaptureRelay also stores internal attachment metadata needed for idempotency and auditing. It does not publish a page or post and does not select a featured image automatically.', 'capture-relay'),
                    __('Deleting an attachment uses normal WordPress behavior. Removing an Upload Activity record through retention does not delete its Media Library image.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'settings-reference',
                'title' => __('Settings reference', 'capture-relay'),
                'paragraphs' => [
                    __('Save Settings after making changes. Approved apps receive current website delivery settings during authenticated configuration checks.', 'capture-relay'),
                ],
                'definitions' => [
                    __('Enable mobile uploads', 'capture-relay') => __('Controls image delivery from approved devices. Disabling it leaves approvals, settings, and queued images intact so uploads can resume later.', 'capture-relay'),
                    __('Accept new connection requests', 'capture-relay') => __('Controls whether new devices may enter the approval queue. Disabling it does not interrupt uploads from devices that are already approved.', 'capture-relay'),
                    __('Send approval-request emails', 'capture-relay') => __('Emails configured recipients when a new device requests access.', 'capture-relay'),
                    __('Send an email after each image upload', 'capture-relay') => __('Emails configured recipients after a new attachment is successfully created. Idempotent retries do not send duplicates.', 'capture-relay'),
                    __('Email again for repeated device requests', 'capture-relay') => __('When disabled, a request from the same device within 24 hours is still recorded under Connections but its repeat approval email is suppressed. Enable this when every resubmission should notify recipients.', 'capture-relay'),
                    __('Notification recipients', 'capture-relay') => __('One or more valid email addresses separated by commas or spaces. WordPress sends through the website’s configured mail system.', 'capture-relay'),
                    __('Request expiration', 'capture-relay') => __('How long an unapproved request remains actionable. Expired requests cannot be approved.', 'capture-relay'),
                    __('Closed-request retention', 'capture-relay') => __('How long approved, rejected, cancelled, and expired request records remain before scheduled cleanup.', 'capture-relay'),
                    __('Maximum pending requests', 'capture-relay') => __('The site-wide size of the active approval queue.', 'capture-relay'),
                    __('Connection requests per IP, device, and site', 'capture-relay') => __('Separate hourly ceilings that reduce repeated and distributed request abuse.', 'capture-relay'),
                    __('Approval checks per IP', 'capture-relay') => __('Limits pre-approval status polling, credential completion, and cancellation traffic.', 'capture-relay'),
                    __('Required image width and height', 'capture-relay') => __('The authoritative output dimensions for this website. Updated apps refresh these values automatically and do not expose them as contributor-editable settings.', 'capture-relay'),
                    __('Media Library image format', 'capture-relay') => __('Chooses the final file type saved in WordPress. JPEG is the broadly compatible default. WebP usually produces smaller files but requires Imagick or GD WebP support on this website.', 'capture-relay'),
                    __('Image quality', 'capture-relay') => __('The compression target used while preparing the website’s final JPEG or WebP file. Lower is smaller; higher retains more detail and increases file size.', 'capture-relay'),
                    __('Maximum upload size', 'capture-relay') => __('The final compressed-file ceiling in KB. Images that cannot fit remain rejected for this destination.', 'capture-relay'),
                    __('Photo Credit caption format', 'capture-relay') => __('The Media Library caption template. It must contain {credit}; an invalid template returns to Photo by {credit}.', 'capture-relay'),
                    __('Default copyright value', 'capture-relay') => __('Optional website-wide copyright text stored with every accepted attachment.', 'capture-relay'),
                    __('Uploads per device per hour', 'capture-relay') => __('Limits valid uploads from one approved credential.', 'capture-relay'),
                    __('Rejected authentication attempts per IP', 'capture-relay') => __('Temporarily limits invalid signed traffic without charging successful approved-device requests to the same failure bucket.', 'capture-relay'),
                    __('Activity and upload-record retention', 'capture-relay') => __('Controls scheduled cleanup of audit events and delivery records. Media Library attachments remain.', 'capture-relay'),
                    __('Erase plugin data when uninstalled', 'capture-relay') => __('When enabled, deleting the plugin removes its settings, credentials, requests, blocks, counters, and activity tables. Media Library attachments remain.', 'capture-relay'),
                    __('App: Default Photo Credit', 'capture-relay') => __('The contributor’s fallback credit. A per-website Photo Credit overrides it for that destination.', 'capture-relay'),
                    __('App: Save photos taken with CaptureRelay', 'capture-relay') => __('Enabled by default. Saves one original in-app camera capture to Apple Photos or the Android gallery before destination cropping and compression. Turning it off affects future captures only; selected library images are never duplicated.', 'capture-relay'),
                    __('App: Camera grid', 'capture-relay') => __('Shows or hides the rule-of-thirds grid inside the destination crop guide. It does not change the delivered crop.', 'capture-relay'),
                    __('App: Suggest with AI', 'capture-relay') => __('Appears only when the phone reports compatible on-device image intelligence. It proposes separate editable title and alt text, does not submit automatically, and is absent on unsupported devices.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'notifications',
                'title' => __('Notifications and administrator visibility', 'capture-relay'),
                'bullets' => [
                    __('A pending-request count appears beside CaptureRelay under Tools and on the Connections tab.', 'capture-relay'),
                    __('Administrators with the configured capability see a dashboard notice linking directly to Connections while requests are pending.', 'capture-relay'),
                    __('Approval-request and successful-upload notifications use a responsive CaptureRelay-branded HTML template with the website, device, time, and direct review action appropriate to the event. Upload emails also show a linked image preview; the preview and Review Image button open that attachment in the WordPress Media Library.', 'capture-relay'),
                    __('Approval and upload emails use wp_mail and therefore depend on the website’s normal mail or SMTP configuration.', 'capture-relay'),
                    __('Email recipients do not grant WordPress access. Only administrators with the configured capability can approve or manage devices.', 'capture-relay'),
                    __('If email is unreliable, use the pending count and Connections tab as the authoritative source.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'activity-diagnostics',
                'title' => __('Upload Activity and Diagnostics', 'capture-relay'),
                'definitions' => [
                    __('Upload Activity', 'capture-relay') => __('Shows recent deliveries, Photo Credit, originating device, time, status, attachment link, and a sanitized error message when a delivery failed.', 'capture-relay'),
                    __('REST API route', 'capture-relay') => __('The discovery endpoint used by the app to recognize this website.', 'capture-relay'),
                    __('Mobile uploads and new connection requests', 'capture-relay') => __('Shows the independent availability state for approved-device uploads and new approval requests.', 'capture-relay'),
                    __('HTTPS', 'capture-relay') => __('Must be active on a production website. Local development may behave differently.', 'capture-relay'),
                    __('Upload directory and image processing', 'capture-relay') => __('Confirm that WordPress can write Media Library files and has Imagick or GD support.', 'capture-relay'),
                    __('Official app verification', 'capture-relay') => __('Shows whether an external Apple App Attest or Google Play Integrity verifier is connected and enforced. Prepared but not enforced means approval and signed-device security are active, but official-store app attestation is not yet required.', 'capture-relay'),
                    __('Approved credential health', 'capture-relay') => __('Every approved credential should be valid. An invalid result means that device should be revoked and approved again after the site keys are confirmed.', 'capture-relay'),
                    __('Recent Errors', 'capture-relay') => __('Shows the latest warning and error events without exposing credentials or authorization secrets.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'security-privacy',
                'title' => __('Security and privacy behavior', 'capture-relay'),
                'bullets' => [
                    __('Production requests require HTTPS. Approved-device requests are signed with HMAC-SHA256, time-limited, bound to the request body, and protected by one-time nonce replay detection.', 'capture-relay'),
                    __('Each website and device has an independent credential. Revoking one destination does not expose or disable another destination.', 'capture-relay'),
                    __('Credentials are stored in protected phone storage and encrypted at rest in WordPress using site-specific key material.', 'capture-relay'),
                    __('Raw request IP addresses are cleared when a request is approved, rejected, cancelled, or expires. Keyed IP hashes and short-lived counters support abuse prevention.', 'capture-relay'),
                    __('CaptureRelay enforces a fixed forty-million-pixel decoded-image ceiling to protect PHP memory from decompression-bomb uploads. This internal safeguard is not administrator-configurable.', 'capture-relay'),
                    __('The plugin does not send telemetry, Media Library images, credentials, or stored WordPress data to Skyrocket Radio.', 'capture-relay'),
                    __('Optional title and alt-text suggestions are generated on the mobile device. CaptureRelay does not send an image to Skyrocket Radio or a paid cloud AI service for this feature; the plugin receives only the final image and text the app user chooses to deliver.', 'capture-relay'),
                    __('Destination administrators control their device records, delivery records, blocks, and Media Library attachments. Skyrocket Radio normally cannot access or delete those records.', 'capture-relay'),
                    __('Only install plugin updates obtained from the official CaptureRelay website and verify the published checksum when one is supplied.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'troubleshooting',
                'title' => __('Common questions and troubleshooting', 'capture-relay'),
                'definitions' => [
                    __('The app says it could not connect', 'capture-relay') => __('CaptureRelay intentionally uses a generic connection message. Confirm the exact HTTPS website address, plugin activation, REST API availability, and that a firewall or security plugin is not blocking the capture-relay/v1 namespace. Check Diagnostics.', 'capture-relay'),
                    __('This website is not accepting new connections', 'capture-relay') => __('Enable Accept new connection requests in Settings. This option is independent of Enable mobile uploads, so approved devices may continue delivering while new requests are paused.', 'capture-relay'),
                    __('A request never appears', 'capture-relay') => __('Confirm Accept new connection requests is enabled, the phone has internet access, the site is reachable publicly, and request limits or the maximum pending queue have not been reached.', 'capture-relay'),
                    __('The request says it is already pending', 'capture-relay') => __('Review or reject the existing Connections entry. One device cannot create multiple simultaneous pending requests.', 'capture-relay'),
                    __('The device is blocked', 'capture-relay') => __('An administrator previously used Reject & Block or Revoke & Block. Remove the entry under Blocked Devices, then have the user request access again.', 'capture-relay'),
                    __('The app remains pending after approval', 'capture-relay') => __('Keep the app open briefly so it can complete the protected credential exchange. Verify the website remains reachable and check Diagnostics and Recent Errors.', 'capture-relay'),
                    __('Test fails after previously working', 'capture-relay') => __('The device may have been revoked, the website URL or HTTPS certificate may have changed, WordPress security rules may be blocking REST, or the credential may have rotated after a new approval.', 'capture-relay'),
                    __('Image rejected for size or pixels', 'capture-relay') => __('Review the required dimensions, Media Library image format, image quality, and KB limit in Settings. The fixed forty-million-pixel and memory protections are intentionally not configurable.', 'capture-relay'),
                    __('Image remains in the queue', 'capture-relay') => __('Open the queue item for its destination-specific status. Restore connectivity or correct the reported setting, then retry. Removing a queued item deletes the app’s queued copy, not a completed WordPress attachment.', 'capture-relay'),
                    __('A camera photo is not in Apple Photos or the Android gallery', 'capture-relay') => __('Confirm Save photos taken with CaptureRelay is enabled and allow CaptureRelay to add photos in the device’s system settings. A save warning does not stop website delivery. Existing images selected through the system picker are intentionally not copied.', 'capture-relay'),
                    __('No approval or upload email arrives', 'capture-relay') => __('Confirm the corresponding option and recipients, then test the website’s wp_mail or SMTP setup. A repeated request from the same device within 24 hours does not send another approval email unless Email again for repeated device requests is enabled. Connections and Upload Activity remain authoritative.', 'capture-relay'),
                    __('The same image appears twice', 'capture-relay') => __('Normal automatic retries are idempotent. Separate manual submissions create separate upload identifiers and therefore separate attachments.', 'capture-relay'),
                ],
            ],
            [
                'id' => 'ongoing-management',
                'title' => __('Ongoing management and handoff', 'capture-relay'),
                'bullets' => [
                    __('Review pending requests promptly and reject unknown devices. Use blocking when repeated requests should never return.', 'capture-relay'),
                    __('Revoke a device immediately when a phone is lost, replaced, reassigned, or no longer needs access.', 'capture-relay'),
                    __('Use clear device names so future administrators can identify owners without inspecting technical identifiers.', 'capture-relay'),
                    __('Review Upload Activity and Diagnostics after hosting, HTTPS, caching, firewall, SMTP, WordPress, PHP, or security-plugin changes.', 'capture-relay'),
                    __('Keep the plugin and mobile apps current. Record the chosen notification and retention policy in the website’s internal operational documentation.', 'capture-relay'),
                    __('Before uninstalling, decide whether operational and security records should be preserved or erased. Export or retain any audit information required by the organization before deletion.', 'capture-relay'),
                ],
            ],
        ];
    }

    private function renderSection(array $section): void
    {
        echo '<section id="' . esc_attr($section['id']) . '" class="capture-relay-docs-section"><h2>'
            . esc_html($section['title']) . '</h2>';
        foreach ($section['paragraphs'] ?? [] as $paragraph) {
            echo '<p>' . esc_html($paragraph) . '</p>';
        }
        if (!empty($section['steps'])) {
            echo '<ol class="capture-relay-docs-steps">';
            foreach ($section['steps'] as $step) {
                echo '<li>' . esc_html($step) . '</li>';
            }
            echo '</ol>';
        }
        if (!empty($section['bullets'])) {
            echo '<ul>';
            foreach ($section['bullets'] as $bullet) {
                echo '<li>' . esc_html($bullet) . '</li>';
            }
            echo '</ul>';
        }
        if (!empty($section['definitions'])) {
            echo '<dl class="capture-relay-docs-definitions">';
            foreach ($section['definitions'] as $term => $definition) {
                echo '<div><dt>' . esc_html($term) . '</dt><dd>' . esc_html($definition) . '</dd></div>';
            }
            echo '</dl>';
        }
        if (!empty($section['note'])) {
            echo '<p class="capture-relay-docs-note"><strong>' . esc_html__('Important:', 'capture-relay') . '</strong> '
                . esc_html($section['note']) . '</p>';
        }
        echo '</section>';
    }
}
