{% extends "macros.html" %} {% import "macros/browser-detect.html" as browser_detect with context %} {% from "macros/early_exit.html" import early_exit_modal %} {% from "macros/timeline-shell.html" import template_data_script, timeline_header, timeline_hold_region, timeline_footer with context %} {% block stylesheets %} {# theme.html arrives via psynet_layout.html's stylesheets block. #} {{ super() }} {% if inplace_timeline_transitions %} {% endif %} {% for x in css %} {% endfor %} {% for x in css_links %} {% endfor %} {% endblock %} {% block body %} {% macro timeline_main_body() %}
{% block main_body %} {{ media.media_container() }} {{ progress.trial_progress_display(trial_progress_display_config) }} {% endblock %} {% if partial_mode %} {# Architecture: docs/developer/page_lifecycle.rst #} {# In partial_mode we keep page-local assets and framework template scripts in a hidden container for activation after the fragment swap. #} {% endif %}
{% endmacro %} {% if partial_mode %} {# Fallback when this file is the template root. Child templates that extend timeline-page.html are rewritten to timeline-fragment.html. #}
{{ timeline_header() }} {{ timeline_hold_region() }} {{ timeline_main_body() }} {{ timeline_footer() }} {% if early_exit_offer_id %} {{ early_exit_modal( participant.assignment_id, early_exit_offer_id, early_exit_confirmation ) }} {% endif %} {{ template_data_script() }}
{% else %} {# Full-page render used for initial loads and for the legacy reload path. The browser-detect markup stays outside the fragment so that in-place transitions do not duplicate it. #} {{ browser_detect.body() }}
{{ timeline_header() }} {{ timeline_hold_region() }} {{ timeline_main_body() }} {% block modals %} {% if early_exit_offer_id %} {{ early_exit_modal( participant.assignment_id, early_exit_offer_id, early_exit_confirmation ) }} {% endif %} {% block comment_modal %} {% if config.get("leave_comments_on_every_page") %} {% endif %} {% endblock %} {% block alert_modal %} {% endblock %} {% block resume_modal %} {% endblock %} {% endblock %} {{ timeline_footer() }}
{% endif %} {% endblock %} {% block head %} {{ super() }} {% for src in js_dependencies %} {# Blocking tags preserve first-page script order. Failed loads are marked so the guarded loader can still reject missing js_dependencies. #} {% endfor %} {% if not partial_mode %} {{ template_data_script() }} {% endif %} {# Last in the head block: everything it emits is valid head content. #} {{ browser_detect.head() }} {% endblock %} {% block libs %} {{ super() }} {% endblock %} {% block scripts %} {% if not partial_mode %} {{ super() }} {% endif %} {% endblock %}