Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <link rel="icon" type="image/svg+xml" href="/ccr-logo.svg"> | |
| <title>CCR Platform - Guide</title> | |
| <style> | |
| :root { | |
| --bg: #ffffff; --fg: #1a1a1a; --muted: #666; --border: #ddd; | |
| --accent: #26736f; --card: #f7f7f8; --amber-bg: #fff7e0; --amber-border: #e0b84d; | |
| --code-bg: #f0f0f2; --brand: #151515; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --bg: #16181c; --fg: #e6e6e6; --muted: #9a9a9a; --border: #3a3d44; | |
| --accent: #6cc9c6; --card: #1f2228; --amber-bg: #2e2810; --amber-border: #8a6d1f; | |
| --code-bg: #24272e; | |
| } | |
| } | |
| * { box-sizing: border-box; } | |
| body { | |
| margin: 0; background: var(--bg); color: var(--fg); | |
| font: 16px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | |
| } | |
| /* Topbar mirrors the app header EXACTLY (gradient, height, logo size, | |
| tagline, link styles) so navigating app <-> docs doesn't jump. Kept | |
| identical to guide.html and styles.css .header. */ | |
| .topbar { | |
| background: linear-gradient(180deg, #1b1b1c, var(--brand)); color: #fff; | |
| padding: 14px 28px; | |
| display: flex; align-items: center; gap: 14px; flex-wrap: wrap; | |
| position: sticky; top: 0; z-index: 30; | |
| border-bottom: 1px solid rgba(255,255,255,.07); | |
| } | |
| .topbar .brand { display: inline-flex; align-items: center; text-decoration: none; } | |
| .brand-logo { height: 28px; width: auto; display: block; } | |
| .topbar .brand:hover { opacity: .88; } | |
| .topbar .lab { | |
| flex: 1 1 280px; min-width: 0; font-size: 12.5px; line-height: 1.5; | |
| color: #fff; opacity: .85; | |
| } | |
| .topbar nav { margin-left: auto; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; } | |
| .topbar nav a { color: rgba(255,255,255,.82); text-decoration: none; font-size: 13px; } | |
| .topbar nav a:hover { color: #fff; text-decoration: underline; } | |
| .topbar nav a.current { color: #fff; font-weight: 600; } | |
| .topbar .topbar-btn { | |
| background: rgba(255,255,255,.12); color: #fff; | |
| border: 1px solid rgba(255,255,255,.45); | |
| padding: 5px 14px; border-radius: 9px; font-size: 13px; text-decoration: none; | |
| } | |
| .topbar .topbar-btn:hover { background: rgba(255,255,255,.22); text-decoration: none; } | |
| main { max-width: 860px; margin: 0 auto; padding: 2rem 1.25rem 4rem; } | |
| h1 { font-size: 1.7rem; margin: 0 0 .25rem; } | |
| h2 { font-size: 1.25rem; margin: 2.2rem 0 .6rem; padding-top: .6rem; border-top: 1px solid var(--border); } | |
| h3 { font-size: 1.02rem; margin: 1.2rem 0 .4rem; } | |
| p, li { color: var(--fg); } | |
| .sub { color: var(--muted); margin: 0 0 1.2rem; } | |
| a { color: var(--accent); } | |
| code { background: var(--code-bg); padding: .1em .35em; border-radius: 4px; font-size: .9em; } | |
| .note { | |
| background: var(--amber-bg); border: 1px solid var(--amber-border); | |
| border-radius: 8px; padding: .8rem 1rem; margin: 1rem 0; | |
| } | |
| .note b { display: block; margin-bottom: .25rem; } | |
| .tablewrap { overflow-x: auto; } | |
| table { border-collapse: collapse; width: 100%; margin: .8rem 0; font-size: .93rem; } | |
| th, td { border: 1px solid var(--border); padding: .45rem .6rem; text-align: left; vertical-align: top; } | |
| th { background: var(--card); } | |
| ol li, ul li { margin: .3rem 0; } | |
| /* Numbered step cards for the walk-through. */ | |
| .steps { list-style: none; counter-reset: step; padding: 0; margin: 1rem 0; } | |
| .steps > li { | |
| counter-increment: step; position: relative; | |
| padding: .1rem 0 1rem 3rem; margin: 0; | |
| } | |
| .steps > li::before { | |
| content: counter(step); position: absolute; left: 0; top: .1rem; | |
| width: 2rem; height: 2rem; border-radius: 50%; | |
| background: var(--accent); color: #fff; font-weight: 700; font-size: .95rem; | |
| display: flex; align-items: center; justify-content: center; | |
| } | |
| .steps > li h3 { margin: .15rem 0 .3rem; } | |
| .top { position: fixed; right: 1rem; bottom: 1rem; background: var(--card); | |
| border: 1px solid var(--border); border-radius: 8px; padding: .4rem .7rem; | |
| text-decoration: none; font-size: .85rem; } | |
| /* Shareable section links: a "#" appears on hover; click a heading to copy | |
| a direct link to that section. */ | |
| h2[id], h3[id] { scroll-margin-top: 1rem; } | |
| .anchor { | |
| opacity: 0; text-decoration: none; color: var(--muted); font-weight: 400; | |
| margin-left: .4rem; cursor: pointer; transition: opacity .12s; | |
| } | |
| h2:hover .anchor, h3:hover .anchor, .anchor:focus { opacity: .7; } | |
| .anchor:hover { opacity: 1; color: var(--accent); } | |
| :target { animation: flash 1.4s ease-out; } | |
| @keyframes flash { | |
| from { background: var(--amber-bg); } | |
| to { background: transparent; } | |
| } | |
| .toast { | |
| position: fixed; left: 50%; bottom: 1.5rem; transform: translateX(-50%); | |
| background: var(--brand); color: #fff; padding: .5rem .9rem; border-radius: 8px; | |
| font-size: .85rem; opacity: 0; pointer-events: none; transition: opacity .2s; | |
| } | |
| .toast.show { opacity: 1; } | |
| @media (prefers-reduced-motion: reduce) { | |
| :target { animation: none; } | |
| .anchor { transition: none; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="topbar"> | |
| <a class="brand" href="/"><svg class="brand-logo" viewBox="188 455 880 338" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> | |
| <g fill="none" stroke="#fff" stroke-width="32"> | |
| <path d="M 458.884 538.509 A 133 133 0 1 0 458.884 709.491"/> | |
| <path d="M 563.116 538.509 A 133 133 0 0 1 766.884 538.509"/> | |
| <path d="M 563.116 709.491 A 133 133 0 0 0 766.884 709.491"/> | |
| <path d="M 846 754 V 496 H 950 A 73 62 0 0 1 950 620 H 846"/> | |
| <path d="M 930 620 L 1035 759"/> | |
| </g> | |
| <circle cx="481" cy="624" r="15" fill="#339B98"/> | |
| <circle cx="538" cy="624" r="15" fill="#339B98"/> | |
| </svg></a> | |
| <span class="lab">Psychological Text Analysis with Contextualized Construct Representation</span> | |
| <nav> | |
| <a href="/welcome">About</a> | |
| <a href="/guide" class="current">Guide</a> | |
| <a href="/testing" id="nav-testing" hidden>Testing</a> | |
| <a href="/product" id="nav-product" hidden>How it works</a> | |
| <a class="topbar-btn" href="/">Open dashboard</a> | |
| </nav> | |
| </header> | |
| <main> | |
| <h1>How to use the CCR Platform</h1> | |
| <p class="sub">A guide to running a Contextualized Construct Representation (CCR) analysis: | |
| upload your texts, choose a psychological construct, and get per-text scores you can | |
| inspect, export, and reproduce. No coding required.</p> | |
| <h2 id="what">What the platform does</h2> | |
| <p>CCR measures how strongly a piece of text reflects a psychological construct (for | |
| example individualism, moral concern, depression, or gratitude) without relying on | |
| word counts or keyword lists. You provide a set of questionnaire-style items that define | |
| the construct - either from a validated scale in the built-in library, or your own. The | |
| platform embeds each item and each of your texts with a sentence-embedding language | |
| model, then scores every text by its semantic similarity (cosine) to the items. Higher | |
| similarity means the text more strongly reflects the construct.</p> | |
| <p>The pipeline is based on Atari et al. (2023) and follows the reference | |
| <code>ccr_wrapper</code> implementation, so scores are comparable to the published method. | |
| Everything runs on this server: your text is never sent to a third-party AI service for | |
| embedding.</p> | |
| <h2 id="accounts">Accounts (optional, but recommended)</h2> | |
| <p>You can try a small analysis without signing in. A free account lifts the anonymous | |
| limits and, importantly, <b>keeps your datasets and runs</b> instead of deleting the | |
| upload after each analysis. Sign in from the top right (email and password, or Google | |
| where enabled).</p> | |
| <div class="tablewrap"> | |
| <table> | |
| <tr><th></th><th>Signed out</th><th>Free account</th></tr> | |
| <tr><td>Upload size</td><td>5 MB</td><td>50 MB</td></tr> | |
| <tr><td>Rows per file</td><td>200</td><td>up to 50,000</td></tr> | |
| <tr><td>Runs per day</td><td>3, then sign in</td><td>unlimited</td></tr> | |
| <tr><td>Runs kept</td><td>none (upload deleted after each run)</td><td>saved</td></tr> | |
| <tr><td>AI-drafted items</td><td>not available</td><td>available (daily cap)</td></tr> | |
| </table> | |
| </div> | |
| <h2 id="sample-data">Sample datasets: try it without your own data</h2> | |
| <p>New to CCR and don't have a corpus handy? Download one of these ready-to-run example | |
| files, upload it in Step 1, and pick the suggested construct. They are small synthetic | |
| demo texts made by the lab for trying the platform, not research data.</p> | |
| <div class="tablewrap"> | |
| <table> | |
| <tr><th>Dataset</th><th>What is in it</th><th>Good to pair with</th></tr> | |
| <tr> | |
| <td><a href="/samples/sample_corpus.csv" download>sample_corpus.csv</a></td> | |
| <td>About 60 short first-person texts about life satisfaction and wellbeing</td> | |
| <td>Search the library for <b>Satisfaction With Life</b></td> | |
| </tr> | |
| <tr> | |
| <td><a href="/samples/moral_foundations_demo.csv" download>moral_foundations_demo.csv</a></td> | |
| <td>About 40 texts expressing moral concern (mostly care and harm)</td> | |
| <td>Search for <b>Care (MFQ-2)</b></td> | |
| </tr> | |
| <tr> | |
| <td><a href="/samples/large_demo.csv" download>large_demo.csv</a></td> | |
| <td>About 800 short everyday texts, a bigger corpus to feel real run times</td> | |
| <td>Any construct; try <b>PANAS</b> positive or negative affect</td> | |
| </tr> | |
| <tr> | |
| <td><a href="/samples/long_documents_demo.csv" download>long_documents_demo.csv</a></td> | |
| <td>About 10 long multi-paragraph texts</td> | |
| <td>Any construct; useful for seeing the truncation warning</td> | |
| </tr> | |
| </table> | |
| </div> | |
| <p>Each file has one text per row (a <code>text</code> column) and the platform detects | |
| the text column for you. When you choose a construct, prefer a validated scale from the | |
| library whenever one exists.</p> | |
| <h2 id="steps">Running an analysis, step by step</h2> | |
| <ol class="steps"> | |
| <li> | |
| <h3>Create a project</h3> | |
| <p>A project holds your datasets and runs. Name it after your study. Use the sidebar | |
| to switch between projects or create a new one.</p> | |
| </li> | |
| <li> | |
| <h3>Upload your corpus (Step 1)</h3> | |
| <p>Upload a CSV or Excel file with <b>one text per row</b> - tweets, essays, | |
| open-ended survey answers, transcripts. Extra columns (IDs, conditions, demographics) | |
| are fine; they pass through untouched into the results file. The platform suggests | |
| which column holds the text; confirm or change it. No corpus of your own yet? Start | |
| with a <a href="#sample-data">sample dataset</a>.</p> | |
| </li> | |
| <li> | |
| <h3>Choose your construct(s) (Step 2)</h3> | |
| <p>Pick from the library of validated scales, or add your own. You can select several | |
| constructs (up to 10) to score them together in one run and see how they correlate in | |
| your texts. See <a href="#constructs">Constructs</a> below for all four ways to add | |
| one, including drafting items with AI.</p> | |
| </li> | |
| <li> | |
| <h3>Pick a model and language (Step 3)</h3> | |
| <p>Choose the embedding model and the corpus language. Start with <b>MiniLM</b> (the | |
| CCR reference model) for English. Scores are only comparable <i>within</i> the same | |
| model, never across model families. See <a href="#models">Models</a>.</p> | |
| </li> | |
| <li> | |
| <h3>Run, and read the warnings first</h3> | |
| <p>Press Run. When it finishes, read the amber <b>warnings panel</b> before anything | |
| else: it flags things like empty or duplicate rows, texts that were too short, texts | |
| longer than the model's window (truncated), or a mismatch between your corpus language | |
| and the model. Warnings never change your scores - they tell you how to read them.</p> | |
| </li> | |
| <li> | |
| <h3>Inspect the results</h3> | |
| <p>Each run shows a score distribution (histogram plus mean, SD, min, max), the | |
| per-item loadings (how each questionnaire item contributed), and the top- and | |
| bottom-scoring texts. If the top texts do not look like your construct, trust that | |
| instinct. Multi-construct runs add a <b>correlation card</b> showing the Pearson | |
| correlation between constructs across your texts.</p> | |
| </li> | |
| <li> | |
| <h3>Export and reproduce</h3> | |
| <p>Download the results CSV (your columns plus per-item similarities and the CCR | |
| score), the run metadata, and a script that reproduces the numbers on your own | |
| machine. See <a href="#repro">Exports and reproducibility</a>.</p> | |
| </li> | |
| </ol> | |
| <h2 id="constructs">Constructs: four ways to add one</h2> | |
| <p>A construct is a set of short questionnaire-style items. There are four ways to provide | |
| them:</p> | |
| <ul> | |
| <li><b>From the library.</b> Search the built-in collection of validated psychological | |
| scales by name or category (for example "GAD-7" or "empathy"). This is the preferred | |
| option whenever a validated scale exists.</li> | |
| <li><b>Type or paste.</b> Enter a scale's items yourself, one per line. Append | |
| <code>(R)</code> to a line to mark it reverse-scored.</li> | |
| <li><b>Upload a file.</b> A CSV/XLSX with an item column (and optionally a reverse | |
| column). You review the parsed items before saving. Uploaded item files are never | |
| retained on the server.</li> | |
| <li><b>Draft with AI.</b> When no validated scale exists, the platform can draft | |
| candidate items from the construct's name and a short description. Signed-in users | |
| only. See below.</li> | |
| </ul> | |
| <h3 id="ai">Drafting construct items with AI</h3> | |
| <p>For a construct with no validated questionnaire, describe it (name plus a few | |
| sentences) and the platform drafts candidate items for you to review. This is a starting | |
| point, <b>not</b> a validated scale: you review, edit, and delete items before saving, and | |
| everything the construct touches is labeled <b>"AI-generated - not validated"</b> in the | |
| picker, in the results, and in the exported metadata.</p> | |
| <div class="tablewrap"> | |
| <table> | |
| <tr><th>Detail</th><th>Value</th></tr> | |
| <tr><td>Model</td><td><span id="ai-model">Claude Haiku 4.5</span> <span id="ai-provider" class="sub" style="font-size:.9em"></span></td></tr> | |
| <tr><td>Prompt version</td><td><span id="ai-prompt-version">v1</span> (recorded on every draft, so a run's provenance names the exact instructions used)</td></tr> | |
| <tr><td>Item style</td><td>First-person, positively-keyed statements only (no reverse-scored items in this version), plain language, one idea per item</td></tr> | |
| <tr><td>Items per draft</td><td><span id="ai-nitems">5 to 20 (default 10)</span></td></tr> | |
| <tr><td>Daily limit</td><td><span id="ai-cap">a per-account daily cap</span></td></tr> | |
| <tr><td>Where it runs</td><td>The drafting model is a hosted language model (the construct name and description you type are sent to it). This is the <b>only</b> step that uses an external model; your uploaded corpus is embedded locally and is never sent anywhere.</td></tr> | |
| </table> | |
| </div> | |
| <p>The exact model name, provider, and prompt version shown above are read live from this | |
| instance, so they always match what actually drafts your items. The same details are | |
| stamped onto every saved AI construct and carried into run metadata and the reproduction | |
| script.</p> | |
| <div class="note"> | |
| <b>Read AI-drafted scores with caution.</b> | |
| These items are drafted by a language model and have not been psychometrically | |
| validated. Review every item, remove weak ones, and prefer a validated scale from the | |
| library whenever one exists. A validation study (AI-drafted vs. validated scale items on | |
| the same texts) is planned before these are recommended for real analyses. | |
| </div> | |
| <h2 id="repro">Exports and reproducibility</h2> | |
| <p>Every run is designed to be auditable and reproducible outside the platform. From the | |
| results page you can download:</p> | |
| <ul> | |
| <li><b>Results CSV.</b> One row per text: all of your original columns, plus a similarity | |
| column per construct item (<code>sim_item_1</code>, <code>sim_item_2</code>, ...) and | |
| the overall <code>ccr_score</code>. Multi-construct runs prefix these columns per | |
| construct (for example <code>mfq_care_sim_item_1</code>, <code>mfq_care_ccr_score</code>), | |
| so every number - including the between-construct correlations - is recomputable from | |
| the file.</li> | |
| <li><b>Metadata (JSON).</b> The exact model revision, the construct snapshot with an item | |
| hash, the language block, environment/package versions, and any warnings. For AI | |
| constructs it also records the drafting model, prompt version, and date, plus a | |
| machine-readable cautionary note.</li> | |
| <li><b>Reproduction script + requirements.</b> A standalone Python script and a | |
| <code>requirements</code> file, both stamped with your run's id.</li> | |
| </ul> | |
| <h3 id="repro-run">Running the reproduction script</h3> | |
| <p>The script re-embeds your items and texts and recomputes the scores with the same model | |
| and settings, on any machine, with no access to this platform. It embeds the construct | |
| items verbatim from the run, so the numbers reproduce offline. From the script's header | |
| (which lists the exact commands with your run id and file name):</p> | |
| <pre><code>pip install -r requirements-repro_<run-id>.txt | |
| python reproduce_analysis_<run-id>.py your_corpus.csv</code></pre> | |
| <p>The reproduced values match the exported CSV to within about 1e-5 with the real models. | |
| Because the script carries the model revision and the construct snapshot, it is also a | |
| durable record of exactly how a result was produced - useful for a methods section or a | |
| reviewer. If the run used AI-drafted items, the script and metadata carry that provenance | |
| too, so the caveat travels with the numbers.</p> | |
| <h2 id="models">Models available</h2> | |
| <p>Chosen on the Step 3 card. Larger models are slower and, the first time anyone selects | |
| one on this instance, take a couple of extra minutes to download before the run starts; | |
| after that they stay warm. Scores are comparable only within the same model.</p> | |
| <div class="tablewrap"> | |
| <table> | |
| <tr><th>Model</th><th>Best for</th><th>Notes</th></tr> | |
| <tr><td>MiniLM L6 v2 <b>(default)</b></td><td>English, general use</td><td>Fast; the CCR reference model. Start here.</td></tr> | |
| <tr><td>E5 Large v2</td><td>English, higher quality</td><td>Stronger but noticeably slower.</td></tr> | |
| <tr><td>Multilingual E5 Base</td><td>50+ languages</td><td>Use for non-English corpora.</td></tr> | |
| <tr><td>PsyEmbedding BERT / RoBERTa / GTE / E5 Large</td><td>Psychological text</td><td>Lab fine-tunes for psychology research. English; heavier, so the first run downloads a larger model.</td></tr> | |
| </table> | |
| </div> | |
| <h2 id="privacy">Your data and privacy</h2> | |
| <p>The embedding models run on this server - your uploaded text is never sent to a | |
| third-party AI service. The one exception is the optional AI item-drafting feature, where | |
| only the construct <i>name and description you type</i> (never your corpus) are sent to the | |
| drafting model. Signed-out uploads are deleted after each analysis; signed-in datasets and | |
| runs are kept until you delete them. Please avoid uploading sensitive or identifiable data.</p> | |
| <div class="note"> | |
| <b>Open-source disclaimer.</b> | |
| This is open-source software: use at your own risk. It is provided "as is", without | |
| warranties or guarantees of accuracy, reliability, security, fitness for a particular | |
| purpose, or continued support. Users are responsible for validating outputs, protecting | |
| their data, and determining whether the software is appropriate for their intended use. | |
| The authors and contributors are not liable for losses or damages resulting from its use. | |
| </div> | |
| <h2 id="help">Questions or feedback</h2> | |
| <p>This platform is built and maintained by the | |
| <a href="https://websites.umass.edu/matari/" target="_blank" rel="noopener noreferrer">Culture & Morality Lab</a> | |
| (PI: <a href="https://mohammadatari.com/" target="_blank" rel="noopener noreferrer">Mohammad Atari</a>) | |
| at the University of Massachusetts Amherst. For suggestions, questions, bugs, and ideas, | |
| reach the maintainer at <a href="mailto:devaanand@umass.edu">devaanand@umass.edu</a>.</p> | |
| </main> | |
| <a class="top" href="#">↑ Top</a> | |
| <div class="toast" id="toast" role="status" aria-live="polite">Link copied</div> | |
| <script> | |
| (function () { | |
| var toast = document.getElementById('toast'), timer; | |
| function ping(msg) { | |
| toast.textContent = msg; | |
| toast.classList.add('show'); | |
| clearTimeout(timer); | |
| timer = setTimeout(function () { toast.classList.remove('show'); }, 1600); | |
| } | |
| function share(id) { | |
| if (history.replaceState) history.replaceState(null, '', '#' + id); | |
| location.hash = id; | |
| var url = location.href; | |
| if (navigator.clipboard && navigator.clipboard.writeText) { | |
| navigator.clipboard.writeText(url).then( | |
| function () { ping('Section link copied'); }, | |
| function () { ping('Link in address bar'); } | |
| ); | |
| } else { | |
| ping('Link in address bar'); | |
| } | |
| } | |
| var heads = document.querySelectorAll('h2[id], h3[id]'); | |
| Array.prototype.forEach.call(heads, function (h) { | |
| var a = document.createElement('a'); | |
| a.className = 'anchor'; | |
| a.href = '#' + h.id; | |
| a.textContent = '#'; | |
| a.setAttribute('aria-label', 'Copy link to this section'); | |
| a.addEventListener('click', function (e) { e.preventDefault(); share(h.id); }); | |
| h.appendChild(a); | |
| h.style.cursor = 'pointer'; | |
| h.addEventListener('click', function (e) { | |
| if (e.target.tagName === 'A') return; | |
| share(h.id); | |
| }); | |
| }); | |
| })(); | |
| </script> | |
| <script> | |
| // Fill in the live AI model details and reveal the lab-only nav links. | |
| // The model name/provider/prompt version come straight from the running | |
| // instance so the guide never states a stale model (falls back to the | |
| // static defaults already in the table if the call fails or is absent). | |
| fetch("/api/auth/me").then(function (r) { return r.json(); }).then(function (me) { | |
| if (["lab", "maintainer", "pi"].indexOf(me.role) !== -1) { | |
| var t = document.getElementById("nav-testing"); if (t) t.hidden = false; | |
| var p = document.getElementById("nav-product"); if (p) p.hidden = false; | |
| } | |
| var g = me.generation; | |
| if (g && g.available) { | |
| var setText = function (id, val) { | |
| var el = document.getElementById(id); | |
| if (el && val) el.textContent = val; | |
| }; | |
| setText("ai-model", g.model_label || g.model); | |
| if (g.provider_label) setText("ai-provider", "(" + g.provider_label + ")"); | |
| if (g.prompt_version) setText("ai-prompt-version", "v" + g.prompt_version); | |
| if (g.n_items_min && g.n_items_max) { | |
| setText("ai-nitems", g.n_items_min + " to " + g.n_items_max + | |
| (g.n_items_default ? " (default " + g.n_items_default + ")" : "")); | |
| } | |
| if (g.max_generations_per_day) { | |
| setText("ai-cap", g.max_generations_per_day + " drafts per day"); | |
| } | |
| } | |
| }).catch(function () {}); | |
| </script> | |
| </body> | |
| </html> | |