Skip to content
FieldnotesInteractive preview

Fieldnotes

A qualitative research workspace connecting original fictional interviews, manual highlights, themes and evidence-backed insight notes.

Published by build.host

Editorial research workspace

Download source + assets

8,304 words · Complete build specification

BUILD SPECIFICATIONDownload .md

Fieldnotes — keep the voice, find the meaning

Build a complete, original qualitative research workspace named Fieldnotes. The slug was checked against the existing source and generated-demo directories and is unused. The site must include a distinctive marketing page, a searchable research library, three complete study pages with fictional interview transcripts, a working highlight and theme-coding flow, an evidence-backed insight notebook, and a substantial method guide. This is a local research demonstration, not an analytics dashboard, support queue, task board, or chat composer.

The product’s purpose is to keep interpretation connected to evidence. A visitor reads a researcher’s question and a participant’s response, highlights a passage, assigns a theme manually, and writes an insight supported by one or more source passages. The notebook must preserve that chain. A saved insight should never become a detached card containing a confident statement with no way to inspect the words behind it. The source link is part of the design, not an optional footnote.

Live research foundation

The live product reference is Dovetail at https://dovetail.com/. The homepage was opened and inspected at a desktop viewport of 1471 by 847. Its current composition has a very large centered proposition, a supporting paragraph, paired actions, a wide product screenshot below the hero, and a long sequence that moves through centralizing, analyzing, querying, and acting on customer evidence. The headline occupied roughly 833 pixels of width and 220 pixels of height, beginning around x=319 and y=304. The product image below measured approximately 1264 by 640 pixels and began around y=851.

The reference uses a dark presentation, restrained grid decoration, bold oversized typography, and an image that shows a real research interface rather than an abstract illustration. The product screenshot includes source material, analysis surfaces, and a question-and-evidence relationship. Later sections repeatedly connect conclusions to customer context. This is the transferable pattern: the marketing composition should introduce a research workflow that remains recognizable when the visitor enters the actual application.

The live reference makes claims about automated analysis and large-scale customer intelligence. Fieldnotes must not reproduce those claims. The local implementation does not call a model, classify sentiment, infer themes, summarize interviews, or verify conclusions. It offers manual selection, explicit labels, text filtering, and a place to write interpretations. Make this distinction clear in the landing copy, study notice, notebook editor, guide, and export. Avoid calling a deterministic search or checkbox interaction artificial intelligence.

A promotional modal appeared during the reference inspection. Do not reproduce that interruption. Fieldnotes should open directly into a complete, readable marketing page and let visitors choose when to enter the workspace. Do not copy the reference’s customer logos, statistics, testimonials, compliance claims, imagery, source code, or proprietary typography. The original site uses a different warm visual direction and entirely fictional research content. Exact reference animation durations were not measured, so Fieldnotes’ transitions are independent design decisions.

Original visual direction

Fieldnotes uses a warm paper foundation, a terracotta-peach hero, charcoal-green ink, and soft research-theme colors. The main background is approximately #f6f3eb. The hero is around #e7a287. Main text is near #292c28, and supporting text is a readable green-gray around #59624e. The strongest action color is dark green around #30392d. Theme colors are pale yellow, green, lavender, and peach, with dark text. These colors should feel like intentional annotations on paper, not a generic rainbow dashboard.

Use the locally licensed Geist family for all interface and body text. The brand is a compact lowercase wordmark paired with a small original notebook-like shape. Headings use medium-to-semibold weights, tight tracking, and compact line height. Body paragraphs remain around sixteen to eighteen pixels with comfortable leading. Primary controls and navigation are at least fourteen pixels; secondary labels are at least twelve. The application must reflow on mobile instead of scaling down a miniature desktop screenshot.

The page should be visually distinct from both a quantitative analytics product and a support inbox. Avoid metric tiles, trend charts, issue-status columns, live message composers, or a central search-chat box. The dominant objects are interview passages, highlight colors, theme labels, evidence citations, and written interpretations. A quote can use Georgia in an illustrative card to emphasize the human voice, but the working transcript remains in a readable sans-serif. Do not turn every quote into a decorative oversized pull quote at the expense of context.

Use moderate radii, generally four to seven pixels on controls and cards, with a slightly larger radius on the broad hero preview. Borders are subtle green-gray rules. Shadows are used sparingly in the marketing preview. The real workspace should feel calm and practical, with enough separation to identify its panels without nesting cards inside cards unnecessarily. Highlighted passages use a soft yellow surface and a visible stateful button. Theme labels provide a separate layer of organization.

Landing page composition

The header is a warm-paper bar approximately eighty-three pixels tall. Put the Fieldnotes identity at the left and Research library, Notebook, The method, and Explore a study at the right. The final link is a dark filled action. Every destination is a complete page. On mobile, retain the three useful text links and allow the header to wrap naturally. Do not squeeze navigation into tiny text or hide the notebook behind an unexplained icon.

The hero uses a centered proposition on the peach surface. The headline is two lines: Keep the voice, then Find the meaning. Add a pale yellow, slightly angled highlight behind the word meaning. The highlight is an original code-native graphic and should remain behind the letterforms. At a large desktop width, the headline can approach ninety-six pixels. On a narrow phone, it reduces to roughly forty-eight pixels while retaining a controlled line count. The supporting paragraph explains the relationship between interviews, highlights, and interpretations.

Place two actions below the paragraph: Explore the research library and See how it works. A small note states that the demo uses fictional interviews and local manual work. Below the hero copy, show a broad, detailed workspace preview that meets the bottom edge of the peach section. The preview has a thin top bar and three columns: a small study navigation area, a transcript excerpt, and an example interpretation linked to its source. It must look like a plausible product surface and use readable text, not a rasterized screenshot full of illegible labels.

The preview excerpt uses the actual fictional passage about an example project helping more than a checklist. Highlight that phrase and attach a Clarity label. The neighboring interpretation says that a concrete example may help a new user understand the workspace’s intended outcome. Label it Example interpretation so it is not mistaken for a note already saved in the visitor’s notebook. Its View source link opens the real corresponding passage on the first study page. This makes the marketing example directly inspectable.

After the preview, include a restrained row of capability statements: original transcripts, manual highlights and themes, source-linked insights, and portable Markdown export. These are implemented capabilities, not customer logos or commercial proof. The next section explains the three-step workflow with a real quote, a set of theme labels, and a small source-linked note illustration. Follow with a dark green section that contrasts evidence with interpretation through two fictional quotes. End with concise native FAQ disclosures and a closing invitation to the library.

Fictional study data

Create three complete studies with one fictional participant each and three source passages per study. Use participant numbers instead of invented realistic identities. Each study has a title, research stage, subject, question, context, participant perspective, scenario date, and stable passage identifiers. The dates are part of the fictional scenario, not a claim that an actual interview occurred. The stable identifiers allow highlights and insight citations to survive page navigation and browser reloads.

The first study, Finding the first useful step, concerns onboarding and is labeled Discovery. Its participant is a new workspace member. The passages discuss uncertainty about where to begin, the usefulness of a concrete example project, and hesitation before inviting someone without knowing what they would see. Each passage includes the researcher’s question and a timestamp. The interview should sound natural and specific without being presented as a real user’s testimony.

The second study, When a handoff becomes a question, concerns collaboration and is labeled Exploration. Its participant is a project coordinator. The passages discuss checking who changed a ready status, receiving a file without the decision behind it, and wanting one named owner and one clear request. The wording should give a researcher several plausible interpretations while leaving room for uncertainty. Do not attach a predetermined correct insight to the study.

The third study, Returning without starting over, concerns navigation and is labeled Evaluation. Its participant is an occasional contributor. The passages discuss wanting to know what changed, searching for a remembered phrase rather than a document title, and wanting a dated summary with links to the original discussion. The study demonstrates that concise summaries and source access can serve different needs. It does not claim that this pattern applies to all returning users.

All nine passages are original demonstration content. The product should state this in context and in the export. Do not import real customer conversations, private research files, external transcripts, or actual participant information. No consent, privacy, or research-validity claim should be fabricated. The goal is to demonstrate a useful method with safe, clearly fictional material.

Research library

The library begins with a clear page heading and a short explanation of the three conversations. A compact workspace navigation row shows Studies, Highlights, and Insights, with counts derived from actual local state. The study count is three because three records exist. Highlight and insight counts update from the visitor’s stored work. Avoid decorative analytics numbers or implied business outcomes.

Provide a search input and a research-stage select. Search matches the study title, subject, and description using ordinary case-insensitive text matching. The stage filter and search must combine with AND semantics. A search for onboarding while Evaluation is selected should produce no result, not ignore one filter. Show the result count and a useful empty state. Reset clears both controls and restores all studies. The filter does not call an external service or infer semantic similarity.

Each study card has a simple original paper-note illustration, stage and subject metadata, a readable title, description, source count, and a real link to the study. The illustrations are code-drawn and share a coherent visual language without pretending to be participant portraits. On desktop, use three columns. On mobile, stack cards at full readable width. Keep the titles and descriptions large enough to scan comfortably.

Study and transcript experience

A study page uses the shared header, a substantial title, workspace navigation, and an explicit fictional-source notice. The content layout has a compact research-context column and a larger transcript column. The sidebar holds the research question and participant metadata. The transcript begins with a heading, context paragraph, and a passage-view control. On mobile, the context becomes an ordinary block above the transcript, with no miniature fixed sidebar.

Each passage is a semantic article with a stable anchor. Show the researcher’s question first, then the participant label, timestamp, and response. The response is a readable blockquote on a white surface. Place the Highlight passage button and theme select beneath it. Do not require drag-selection to use the demonstration. The action selects a complete predefined passage, and the guide must say so. This keeps keyboard and touch behavior clear and consistent.

A new highlight begins as Uncoded. Do not silently assign a thematic interpretation merely because the visitor clicked Highlight. After highlighting, enable the theme select and let the visitor choose Clarity, Confidence, Collaboration, or Friction. Uncoded remains available as the neutral state. The button changes to Highlighted with a pressed state, and the passage receives a warm highlight surface. Clicking it again removes the highlight while leaving the original source passage intact.

The Highlighted only view hides passages that are not currently highlighted and reports the count. If the view becomes empty, explain how to return to all passages. Theme changes save locally and report success. The site does not say that a model classified the passage, detected sentiment, or discovered a theme. The theme represents the visitor’s chosen organizing lens.

Notebook and evidence organization

The notebook has a highlights area and an insight editor, followed by saved interpretations. At desktop widths, the highlights and editor sit side by side. At narrow widths, they stack. The highlights area can be filtered by theme, including Uncoded. Each card contains the complete source response, a theme label, and a link identifying the participant and timestamp. The source link opens the exact passage anchor in its study page.

An empty highlight view should say that there are no highlights in the current view and offer a route to the library. It must not imply that all stored highlights are gone when a theme filter simply excludes them. The notebook count comes from stored highlights, while the filtered card count can be narrower. Keep that distinction understandable through the labels and empty-state text.

The editor asks for an insight title and the researcher’s interpretation. The title is required and limited to one hundred characters. The interpretation requires at least twenty meaningful characters and is limited to a reasonable length. Visible labels and native validation remain in place. If custom validity is used to reject whitespace-only text, clear that validity on input so the visitor can correct the field without becoming stuck behind a stale error.

Supporting evidence is selected through real labeled checkboxes. Each choice displays the source passage and participant/timestamp. A new insight can draw from current highlights. At least one source passage is required before saving. If the visitor has no highlights, explain how to obtain evidence instead of rendering an empty unexplained box. If they enter a title and interpretation but select no evidence, show a clear error and focus an available evidence control.

Saved insights and editing

A saved insight is explicitly labeled Researcher interpretation. It displays the title, body, and supporting source passages. Each citation includes the full quote and a direct link back to the original study location. This is the central product behavior: evidence and interpretation remain separate but connected. Do not replace the evidence with a generic source-count badge that cannot be opened.

Provide an Edit insight action. Editing fills the existing form, checks the original evidence, changes the editor heading and save action, and offers Cancel editing. Saving changes updates that insight rather than appending a duplicate. Cancel resets the editor to a new-note state. Preserve the existing evidence when an insight is edited, even if a source passage is no longer in the current highlight collection. The original transcript is still available and the citation should remain valid.

Removing a highlight does not erase an existing insight’s citation. A highlight is a working selection; an insight’s evidence is a saved reference to the immutable fictional source. Explain this distinction in the guide and the removal feedback. It prevents a casual cleanup of highlights from silently stripping support from a saved interpretation. All stored evidence identifiers must be validated against the known source map when the workspace loads.

Storage and export

Store highlights and insights under a dedicated local-storage key. Parse stored values defensively. Accept only known passage identifiers and known theme identifiers. Insight records need a string identifier, title, body, and an evidence array. Discard unknown evidence references and do not load a saved insight with no valid evidence. Limit restored text lengths and deduplicate evidence identifiers. If storage is malformed, fall back to an empty workspace rather than crashing the page.

Persisting can fail when browser storage is blocked or full. Keep the current in-memory work available and show a useful message telling the visitor to export before leaving. Do not claim that a cloud save occurred. The product has no account, synchronization, or external research backend. Source transcripts are static, and the visitor’s selections and notes remain in this browser unless they explicitly export them.

Prepare export creates a Markdown representation of highlights, theme assignments, insight notes, and source references. It should include a clear statement that the interviews are fictional and the organization is manual. The export repeats the complete quoted evidence so the file remains understandable outside the site. Include participant labels, timestamps, and study/anchor references. Do not export only internal IDs with no human-readable context.

Show the prepared Markdown in a read-only text area and provide a real download anchor with a Blob URL and a descriptive filename. Copy Markdown should attempt the Clipboard API and report its actual result. If copying fails, focus and select the visible text so manual copying remains possible. Revoke an older Blob URL before creating a replacement. Preparing, copying, or downloading the notebook does not send it to another person or service.

Method guide

The guide is a complete editorial page with sections on reading in context, highlighting deliberately, choosing themes, writing interpretations, and local storage/export. Explain the actual workflow rather than hypothetical enterprise features. It should help someone understand the difference between a response, a code, an interpretation, and a validated research finding. Use the fictional example about a concrete project helping more than a checklist to demonstrate the distinction.

A useful example includes three parts: the source quote, a possible interpretation, and an open question about whether other participants would respond similarly. This avoids teaching that one quote proves a general conclusion. The guide should say that theme frequency is not automatically a measure of prevalence or severity. Fieldnotes does not calculate those claims. Its contribution is traceability and a place for deliberate reasoning.

Describe the four thematic lenses in plain language. Clarity concerns understanding and next steps. Confidence concerns feeling ready to act or verify. Collaboration concerns shared context and ownership. Friction concerns hesitation or extra work. These are examples, not a universal coding taxonomy. A new highlight stays Uncoded until the visitor chooses a lens.

Responsive and accessibility quality

All real controls and navigation should be at least fourteen pixels, secondary labels at least twelve, and editorial body text around sixteen to eighteen. On mobile, stack the product preview’s transcript and interpretation rather than scaling the whole desktop preview down. Hide only the decorative preview sidebar, not the meaningful source quote or its link. The live study sidebar becomes a readable context block, and passage controls wrap naturally.

Use native form controls, buttons for state changes, links for navigation, and logical headings. The main page has one h1, sections have h2, and card or insight headings follow the appropriate hierarchy. Add a skip link and one main landmark. Pressed-state highlight buttons and visible theme labels should not rely on color alone. Source links must identify enough context to be useful outside their visual card.

No automatic scrolling, infinite animation, or pretend analysis spinner is needed. Highlight transitions can be brief and subtle. Respect reduced-motion preferences by removing transitions and smooth scrolling. Maintain sufficient contrast on peach, cream, white, and dark green surfaces. A selected yellow passage must retain dark readable text. Do not hide overflow globally to conceal an oversized panel; fix the intrinsic layout instead.

Delivery and verification

Export buildFieldnotes from scripts/fieldnotes/build.mjs and return an object containing prompt. Generate seven complete pages: marketing home, library, notebook, guide, and three study pages. Keep all work in scripts/fieldnotes, public/demos/fieldnotes, and public/prompts/fieldnotes.md. Do not edit shared catalogs, application routes, validators, or deployment configuration. The parent task owns integration.

Deliver a code-drawn original workflow thumbnail as hero.webp, its source SVG if useful, a local Geist font with its license, and clear provider-neutral credits. The detailed prompt must contain at least three thousand substantive words and append the exact fictional data, responsive CSS, and real JavaScript beneath Reference interaction handler. Do not include a favicon or unrelated platform branding.

Verify the complete path in the browser: combine library filters, open a study, highlight a passage, assign a theme, reload and confirm restoration, filter highlighted passages, open the notebook, write an interpretation with selected evidence, save it, edit it without duplication, follow a source citation, and prepare/copy the export. Test the missing-evidence error and correction path. Inspect desktop and mobile layouts, measure document overflow, and check readable control sizes. Report only what was observed, distinguishing a prepared download from a completed file transfer.

The finished product should make qualitative reasoning tangible without pretending to automate it. Its visual character comes from warm annotation surfaces and specific source material. Its practical value comes from keeping the visitor’s conclusions connected to the words that prompted them.

Exact fictional research data

export const themes = [
  { id: 'uncoded', name: 'Uncoded', color: '#e4e4dc' },
  { id: 'clarity', name: 'Clarity', color: '#f1d77f' },
  { id: 'confidence', name: 'Confidence', color: '#c4dcca' },
  { id: 'collaboration', name: 'Collaboration', color: '#d1cef0' },
  { id: 'friction', name: 'Friction', color: '#f0c4b4' },
];
export const studies = [
  {
    id: 'first-steps',
    title: 'Finding the first useful step',
    stage: 'Discovery',
    subject: 'Onboarding',
    count: 3,
    description: 'How people decide where to begin in an unfamiliar workspace.',
    question: 'What helps a new user feel ready to take the first action?',
    context:
      'A fictional interview with a new member of a project workspace. The discussion explores orientation, terminology, and the moment someone decides they understand enough to begin.',
    participant: 'Participant 01',
    role: 'New workspace member',
    date: '12 June 2026',
    color: '#e9c9ad',
    quotes: [
      {
        id: 'first-01',
        time: '02:14',
        prompt: 'What did you notice when you first opened the workspace?',
        text: 'There were a lot of places I could go, but I could not tell which one was meant for me. I wanted one small thing to do first, just to know I was in the right place.',
      },
      {
        id: 'first-02',
        time: '06:32',
        prompt: 'What helped you understand what would happen next?',
        text: 'The example project helped more than the checklist. I could see what a finished version looked like, and then the labels started to make sense.',
      },
      {
        id: 'first-03',
        time: '10:08',
        prompt: 'Was there a point where you paused?',
        text: 'I hesitated before inviting someone because I did not know what they would see. A preview of the invitation would have made that feel much safer.',
      },
    ],
  },
  {
    id: 'shared-work',
    title: 'When a handoff becomes a question',
    stage: 'Exploration',
    subject: 'Collaboration',
    count: 3,
    description:
      'What makes ownership and next steps clear across a small team.',
    question: 'How do teammates know when something is ready for them?',
    context:
      'A fictional interview with a coordinator who moves work between a designer and a reviewer. The discussion focuses on ownership, changing status, and what a useful handoff contains.',
    participant: 'Participant 02',
    role: 'Project coordinator',
    date: '18 June 2026',
    color: '#cdd7d4',
    quotes: [
      {
        id: 'shared-01',
        time: '01:46',
        prompt: 'How do you know a piece of work is ready for review?',
        text: 'Usually someone tells me in a message. The status might say ready, but I still check who changed it and whether there is anything specific they want me to look at.',
      },
      {
        id: 'shared-02',
        time: '05:19',
        prompt: 'Where does a handoff tend to lose context?',
        text: 'The file arrives without the decision behind it. I can see what changed, but not why. A short note about the trade-off would save another conversation.',
      },
      {
        id: 'shared-03',
        time: '09:05',
        prompt: 'What would make the next step clearer?',
        text: 'I would like one named owner and one clear request. If three people are tagged, everyone can assume someone else is taking care of it.',
      },
    ],
  },
  {
    id: 'looking-back',
    title: 'Returning without starting over',
    stage: 'Evaluation',
    subject: 'Navigation',
    count: 3,
    description: 'How people find the context they need after time away.',
    question: 'What helps a returning user recover their place in a project?',
    context:
      'A fictional interview with an occasional contributor returning to a workspace after two weeks. The discussion explores memory, finding recent changes, and deciding which information still matters.',
    participant: 'Participant 03',
    role: 'Occasional contributor',
    date: '24 June 2026',
    color: '#d6d0e6',
    quotes: [
      {
        id: 'return-01',
        time: '03:02',
        prompt: 'What is the first thing you look for when you return?',
        text: 'I want to know what changed since I was last here. The project looks familiar, but that does not tell me whether the decision I remember is still the current one.',
      },
      {
        id: 'return-02',
        time: '07:11',
        prompt: 'How do you find the information you need?',
        text: 'I search for a phrase I remember from the conversation. I do not usually remember the document title, especially if someone renamed it later.',
      },
      {
        id: 'return-03',
        time: '11:27',
        prompt: 'What makes you confident that you have the right context?',
        text: 'A dated summary with links to the original discussion would help. I want the short version first, but I need a way to check the source if something feels unclear.',
      },
    ],
  },
];

Exact responsive CSS

@font-face {
  font-family: Notes;
  src: url('assets/geist-variable.woff2') format('woff2');
  font-display: swap;
  font-weight: 100 900;
}
* {
  box-sizing: border-box;
}
html {
  scroll-behavior: smooth;
  scroll-padding-top: 90px;
}
body {
  margin: 0;
  background: #f6f3eb;
  color: #292c28;
  font:
    16px/1.65 Notes,
    Arial,
    sans-serif;
  -webkit-font-smoothing: antialiased;
}
h1,
h2,
h3,
h4,
p,
blockquote {
  margin: 0;
}
button,
input,
select,
textarea {
  font: inherit;
}
button {
  cursor: pointer;
  color: inherit;
}
a {
  color: inherit;
  text-decoration: none;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid #354fc2;
  outline-offset: 4px;
}
h1,
h2,
h3 {
  font-weight: 550;
  line-height: 1.1;
  letter-spacing: -0.045em;
}
::selection {
  background: #f1d77f;
}
.skip {
  position: fixed;
  top: -100px;
  left: 20px;
  padding: 14px;
  background: #fff;
  z-index: 50;
}
.skip:focus {
  top: 10px;
}
.header {
  height: 83px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 5%;
  background: #f6f3eb;
  border-bottom: 1px solid #d0d1c5;
  gap: 30px;
}
.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -1.4px;
}
.brand-mark {
  width: 25px;
  height: 29px;
  background: #30392d;
  position: relative;
  border-radius: 2px 8px 2px 2px;
}
.brand-mark:before,
.brand-mark:after {
  content: '';
  position: absolute;
  left: 6px;
  right: 6px;
  height: 2px;
  background: #f4eedc;
  top: 10px;
}
.brand-mark:after {
  top: 17px;
}
.nav {
  display: flex;
  align-items: center;
  gap: 30px;
  font-size: 14px;
}
.nav a:hover,
.footer a:hover {
  text-decoration: underline;
  text-underline-offset: 5px;
}
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 25px;
  background: #30392d;
  border: 1px solid #30392d;
  color: #fff;
  padding: 12px 20px;
  min-height: 46px;
  border-radius: 5px;
  font-size: 14px;
  font-weight: 500;
}
.button.secondary {
  background: transparent;
  color: #30392d;
  border-color: #979e8d;
}
.button:hover {
  background: #46523e;
  color: #fff;
}
.eyebrow {
  font-size: 12px;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: #59624e;
  display: block;
}
.hero {
  background: #e7a287;
  padding: 72px 5% 0;
  overflow: hidden;
}
.hero-copy {
  text-align: center;
  max-width: 1050px;
  margin: 0 auto 48px;
}
.hero-copy h1 {
  font-size: clamp(54px, 6.7vw, 96px);
  line-height: 1.04;
  letter-spacing: -0.06em;
  margin: 24px 0;
}
.hero-copy h1 span {
  position: relative;
  display: inline-block;
  z-index: 0;
}
.hero-copy h1 span:after {
  content: '';
  position: absolute;
  left: -6px;
  right: -6px;
  bottom: 4px;
  height: 38%;
  background: #f5d97b;
  z-index: -1;
  transform: rotate(-2deg);
}
.hero-copy p {
  max-width: 650px;
  margin: 0 auto 27px;
  font-size: 18px;
  line-height: 1.6;
  color: #4e443c;
}
.hero-actions {
  display: flex;
  justify-content: center;
  gap: 15px;
  flex-wrap: wrap;
}
.hero-note {
  font-size: 12px !important;
  margin: 18px auto 0 !important;
}
.preview {
  max-width: 1210px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid #aaa18f;
  border-radius: 12px 12px 0 0;
  box-shadow: 0 20px 65px #693d2520;
  overflow: hidden;
  transform: translateY(1px);
}
.preview-bar {
  padding: 16px 22px;
  border-bottom: 1px solid #e0dfd5;
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #61685b;
  gap: 20px;
}
.preview-grid {
  display: grid;
  grid-template-columns: 205px 1.5fr 1fr;
  min-height: 400px;
}
.preview-sidebar {
  background: #f4f4ec;
  padding: 25px 20px;
  border-right: 1px solid #e0dfd5;
}
.preview-sidebar h2 {
  font-size: 16px;
  letter-spacing: -0.3px;
  line-height: 1.4;
  margin-bottom: 20px;
}
.preview-sidebar span {
  display: block;
  font-size: 14px;
  padding: 10px 12px;
  margin: 5px 0;
  border-radius: 4px;
}
.preview-sidebar .active {
  background: #e0e5d5;
}
.preview-transcript {
  padding: 28px;
  border-right: 1px solid #e0dfd5;
  min-width: 0;
}
.preview-transcript h2 {
  font-size: 25px;
  margin: 10px 0 25px;
}
.preview-transcript p {
  font-size: 16px;
  line-height: 1.8;
  color: #4c5446;
}
.preview-transcript mark {
  background: #f6df91;
  color: #30382a;
  padding: 3px 0;
  box-shadow:
    3px 0 #f6df91,
    -3px 0 #f6df91;
}
.quote-person {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-bottom: 17px;
  font-size: 12px;
  color: #606954;
}
.avatar {
  width: 32px;
  height: 32px;
  display: inline-grid;
  place-items: center;
  background: #dce4d3;
  border-radius: 50%;
  font-size: 12px;
  color: #405134;
  flex-shrink: 0;
}
.preview-insight {
  padding: 28px;
  background: #fbfaf5;
}
.preview-insight h2 {
  font-size: 20px;
  margin: 10px 0 18px;
  line-height: 1.2;
}
.preview-insight p {
  font-size: 16px;
  line-height: 1.8;
}
.theme-label {
  display: inline-block;
  padding: 4px 9px;
  border-radius: 4px;
  font-size: 12px;
  color: #333b2d;
  line-height: 1.5;
  background: #f1d77f;
}
.evidence-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-top: 1px solid #d8ddd0;
  margin-top: 28px;
  padding-top: 15px;
  font-size: 12px;
  color: #657052;
}
.trust-row {
  display: flex;
  justify-content: space-between;
  gap: 25px;
  padding: 30px 5%;
  border-bottom: 1px solid #d0d1c5;
  flex-wrap: wrap;
  font-size: 14px;
  color: #58614f;
}
.section {
  padding: 85px 5%;
}
.section-heading {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: 10%;
  align-items: end;
  margin-bottom: 45px;
}
.section-heading h2 {
  font-size: clamp(38px, 4.3vw, 62px);
}
.section-heading p {
  font-size: 17px;
  line-height: 1.8;
  color: #59624e;
  max-width: 510px;
}
.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 25px;
}
.step {
  border-top: 1px solid #afb8a2;
  padding: 25px 0;
}
.step .number {
  font-size: 12px;
  color: #626e57;
  margin-bottom: 30px;
  display: block;
}
.step h3 {
  font-size: 27px;
  margin-bottom: 17px;
}
.step p {
  font-size: 16px;
  line-height: 1.8;
  color: #59624e;
}
.step-example {
  background: #fff;
  border: 1px solid #d5dace;
  margin-top: 26px;
  padding: 22px;
  min-height: 185px;
}
.step-example blockquote {
  font:
    20px/1.5 Georgia,
    serif;
  color: #49523e;
}
.step-example .theme-label {
  margin-top: 15px;
}
.theme-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 20px;
}
.step-example .line {
  height: 9px;
  background: #dfe5d4;
  margin: 13px 0;
}
.step-example .line.short {
  width: 65%;
}
.feature-band {
  background: #303d35;
  color: #f4f2e8;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9%;
  align-items: center;
}
.feature-band h2 {
  font-size: clamp(38px, 4.8vw, 67px);
  margin: 20px 0 25px;
}
.feature-band .eyebrow {
  color: #c6d4bd;
}
.feature-band p {
  font-size: 17px;
  line-height: 1.8;
  color: #ced8c7;
}
.evidence-stack {
  display: grid;
  gap: 18px;
}
.stack-card {
  background: #f2eddf;
  color: #35402e;
  padding: 25px;
  transform: rotate(-2deg);
}
.stack-card:nth-child(2) {
  transform: rotate(2deg);
  margin-left: 25px;
}
.stack-card blockquote {
  font:
    22px/1.5 Georgia,
    serif;
  margin: 15px 0;
}
.stack-card small {
  font-size: 12px;
}
.faq {
  max-width: 900px;
  margin: auto;
}
.faq h2 {
  font-size: 48px;
  margin-bottom: 35px;
}
.faq details {
  padding: 21px 0;
  border-top: 1px solid #bdc5b1;
}
.faq summary {
  font-size: 17px;
  cursor: pointer;
}
.faq p {
  font-size: 16px;
  line-height: 1.8;
  margin-top: 15px;
  color: #59624e;
}
.closing {
  background: #e6aa90;
  text-align: center;
  padding: 65px 20px;
}
.closing h2 {
  font-size: clamp(40px, 4.9vw, 70px);
  max-width: 850px;
  margin: 0 auto 25px;
}
.footer {
  padding: 32px 5%;
  display: flex;
  justify-content: space-between;
  gap: 30px;
  align-items: center;
  font-size: 12px;
  color: #59624e;
}
.footer nav {
  display: flex;
  gap: 25px;
  font-size: 14px;
}
.app-top {
  padding: 35px 5% 25px;
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 35px;
}
.app-top h1 {
  font-size: clamp(38px, 4.5vw, 62px);
  margin: 10px 0 17px;
}
.app-top p {
  font-size: 16px;
  color: #59624e;
  max-width: 670px;
}
.workspace-nav {
  display: flex;
  gap: 23px;
  padding: 17px 5%;
  border-bottom: 1px solid #d0d1c5;
  border-top: 1px solid #d0d1c5;
  background: #eeeee3;
  font-size: 14px;
}
.workspace-nav a {
  padding: 6px 0;
}
.workspace-nav a[aria-current='page'] {
  border-bottom: 2px solid #354b2f;
  font-weight: 600;
}
.count {
  display: inline-grid;
  place-items: center;
  background: #d8e0ca;
  min-width: 22px;
  height: 22px;
  border-radius: 4px;
  font-size: 12px;
  margin-left: 5px;
}
.local-note {
  background: #eee8dc;
  border: 1px solid #ded7c9;
  padding: 12px 17px;
  margin: 25px 5% 0;
  border-radius: 5px;
  font-size: 12px;
  color: #62634f;
}
.library {
  padding: 30px 5% 70px;
}
.filters {
  display: flex;
  gap: 17px;
  align-items: end;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
.filters label {
  display: grid;
  gap: 7px;
  font-size: 14px;
}
.filters input,
.filters select {
  min-height: 47px;
  background: #fff;
  border: 1px solid #acb49f;
  border-radius: 4px;
  padding: 10px 13px;
  max-width: 100%;
  color: #303a28;
}
.filters input {
  width: 330px;
}
.library-count {
  font-size: 14px;
  color: #59624e;
  margin: 20px 0;
}
.study-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 25px;
}
.study-card {
  background: #fff;
  border: 1px solid #d5dace;
  border-radius: 7px;
  overflow: hidden;
}
.study-art {
  height: 175px;
  padding: 24px;
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: center;
  position: relative;
}
.study-art .paper {
  width: 90px;
  height: 115px;
  background: #fffaf0;
  box-shadow: 4px 5px 0 #272c2514;
  padding: 17px 12px;
  transform: rotate(-9deg);
}
.study-art .paper:nth-child(2) {
  transform: rotate(9deg);
  margin-left: -30px;
  margin-top: 16px;
}
.paper i {
  height: 5px;
  display: block;
  background: #a5b398;
  margin-bottom: 8px;
}
.paper i:nth-child(2) {
  background: #f0cc71;
  width: 80%;
}
.study-info {
  padding: 25px;
}
.study-info h2 {
  font-size: 26px;
  line-height: 1.15;
  margin: 12px 0 18px;
}
.study-info p {
  font-size: 16px;
  line-height: 1.7;
  color: #59624e;
  margin-bottom: 18px;
}
.study-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #626e57;
  border-top: 1px solid #dce1d5;
  padding-top: 15px;
  gap: 15px;
}
.text-button,
.source-link {
  display: inline-block;
  background: none;
  border: 0;
  border-bottom: 1px solid #74816a;
  padding: 7px 0;
  font-size: 14px;
  color: #394d2e;
  text-align: left;
}
.study-info .text-button {
  margin-top: 20px;
}
.empty {
  background: #e9eddf;
  padding: 30px;
  border: 1px solid #d0d9c0;
  font-size: 16px;
  line-height: 1.8;
}
.empty h2,
.empty h3 {
  font-size: 26px;
  margin-bottom: 15px;
}
.study-layout {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  gap: 6%;
  padding: 35px 5% 75px;
}
.study-sidebar {
  position: sticky;
  top: 25px;
  align-self: start;
}
.study-sidebar h2 {
  font-size: 18px;
  margin-bottom: 18px;
  letter-spacing: -0.4px;
}
.study-sidebar p {
  font-size: 14px;
  color: #59624e;
  line-height: 1.8;
  margin-bottom: 22px;
}
.study-sidebar dl {
  font-size: 12px;
  line-height: 1.8;
}
.study-sidebar dt {
  color: #647257;
  margin-top: 15px;
}
.study-sidebar dd {
  margin: 0;
  font-size: 14px;
}
.study-sidebar .button {
  margin-top: 25px;
  width: 100%;
}
.transcript {
  min-width: 0;
}
.transcript-top {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 25px;
  border-bottom: 1px solid #c2cbb8;
  padding-bottom: 22px;
  margin-bottom: 10px;
}
.transcript-top h2 {
  font-size: 27px;
  letter-spacing: -0.8px;
}
.transcript-top label {
  font-size: 12px;
  display: grid;
  gap: 6px;
}
.transcript-top select {
  font-size: 14px;
  padding: 8px;
  border: 1px solid #acb79f;
  border-radius: 4px;
  background: #fff;
}
.transcript-context {
  padding: 25px 0;
  font-size: 16px;
  line-height: 1.9;
  color: #59624e;
}
.passage {
  padding: 30px 0;
  border-top: 1px solid #d2dac8;
  scroll-margin-top: 35px;
}
.passage-question {
  display: flex;
  gap: 15px;
  align-items: start;
  color: #5c6653;
  font-size: 16px;
  margin-bottom: 22px;
}
.speaker {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 15px;
  font-size: 12px;
  color: #657457;
}
.speaker strong {
  font-size: 14px;
  color: #35462b;
  font-weight: 500;
}
.passage-text {
  font-size: 18px;
  line-height: 1.85;
  padding: 18px 20px;
  background: #fff;
  border: 1px solid #d9dece;
  border-radius: 6px;
  transition: background-color 0.25s;
}
.highlighted .passage-text {
  background: #fff0b7;
  border-color: #e3c769;
}
.passage-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-top: 16px;
}
.highlight-button {
  background: #fff;
  border: 1px solid #a5b197;
  border-radius: 4px;
  padding: 10px 14px;
  font-size: 14px;
  min-height: 43px;
}
.highlight-button[aria-pressed='true'] {
  background: #e3ebd8;
  border-color: #9eaf8c;
}
.theme-control {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
}
.theme-control select {
  padding: 9px;
  background: #fff;
  border: 1px solid #b2bda4;
  border-radius: 4px;
  font-size: 14px;
  color: #36452d;
}
.theme-control select:disabled {
  color: #71796b;
  background: #e9ece2;
  cursor: not-allowed;
}
.passage-count {
  font-size: 12px;
  color: #626e57;
  margin-top: 10px;
}
.status {
  display: block;
  font-size: 14px;
  line-height: 1.7;
  color: #435a35;
  margin: 20px 0;
  min-height: 24px;
}
.notebook-layout {
  padding: 35px 5% 70px;
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 40px;
  align-items: start;
}
.notebook-section h2 {
  font-size: 31px;
  margin-bottom: 20px;
}
.notebook-section .filters {
  margin-bottom: 22px;
}
.notebook-section .filters select {
  font-size: 14px;
}
.highlight-list {
  display: grid;
  gap: 18px;
}
.evidence-card {
  background: #fff;
  border: 1px solid #d1d9c6;
  border-radius: 6px;
  padding: 23px;
}
.evidence-card blockquote {
  font:
    20px/1.65 Georgia,
    serif;
  margin: 17px 0;
}
.insight-editor {
  background: #eef0e4;
  border: 1px solid #c6d0b7;
  padding: 27px;
  border-radius: 7px;
  min-width: 0;
}
.insight-editor h2 {
  font-size: 28px;
  margin-bottom: 15px;
}
.insight-editor > p {
  font-size: 14px;
  color: #59624e;
  line-height: 1.8;
}
.insight-form label {
  display: block;
  font-size: 14px;
  margin-top: 22px;
}
.insight-form input[type='text'],
.insight-form textarea {
  display: block;
  width: 100%;
  background: #fff;
  border: 1px solid #aebba0;
  border-radius: 4px;
  padding: 12px;
  margin-top: 8px;
  font-size: 16px;
  color: #303d29;
}
.insight-form textarea {
  min-height: 150px;
  resize: vertical;
}
.insight-form fieldset {
  border: 0;
  padding: 0;
  margin: 25px 0;
}
.insight-form legend {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 8px;
}
.evidence-choice {
  display: flex !important;
  align-items: start;
  gap: 12px;
  padding: 13px;
  background: #fff;
  border: 1px solid #d0dac2;
  border-radius: 4px;
  font-size: 14px !important;
  line-height: 1.7;
  margin-top: 12px !important;
}
.evidence-choice input {
  margin-top: 5px;
  accent-color: #466335;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.evidence-choice small {
  display: block;
  font-size: 12px;
  color: #5b6c4f;
  margin-top: 7px;
}
.evidence-choices {
  max-height: 350px;
  overflow: auto;
  padding-right: 4px;
}
.error {
  font-size: 14px;
  color: #9d351e;
  margin: 12px 0;
}
.editor-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.saved-insights {
  padding: 0 5% 65px;
}
.saved-insights > h2 {
  font-size: 35px;
  margin-bottom: 30px;
}
.insight-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 25px;
}
.insight-card {
  background: #fff;
  border: 1px solid #cbd5bd;
  padding: 28px;
  border-radius: 7px;
}
.insight-card h3 {
  font-size: 28px;
  margin: 15px 0 18px;
}
.insight-body {
  font-size: 16px;
  line-height: 1.9;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.insight-evidence {
  border-top: 1px solid #d4ddca;
  margin-top: 25px;
  padding-top: 20px;
}
.insight-evidence h4 {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 18px;
}
.insight-evidence blockquote {
  font-size: 14px;
  line-height: 1.8;
  border-left: 3px solid #e6ce73;
  padding-left: 15px;
  margin: 15px 0;
}
.insight-evidence .source-link {
  display: block;
  width: max-content;
  font-size: 14px;
  margin-top: 9px;
}
.export-panel {
  padding: 30px;
  background: #e6ebdc;
  border: 1px solid #c9d4ba;
  border-radius: 7px;
  margin-top: 35px;
}
.export-panel h2 {
  font-size: 26px;
  margin-bottom: 20px;
}
.export-panel textarea {
  display: block;
  width: 100%;
  min-height: 260px;
  font:
    14px/1.8 Notes,
    sans-serif;
  padding: 20px;
  border: 1px solid #acb99c;
  margin-bottom: 20px;
  resize: vertical;
}
.guide {
  padding: 30px 5% 70px;
  display: grid;
  grid-template-columns: 220px minmax(0, 800px);
  gap: 8%;
  justify-content: space-between;
}
.guide-nav {
  display: flex;
  flex-direction: column;
  gap: 16px;
  position: sticky;
  top: 30px;
  align-self: start;
  font-size: 14px;
}
.guide article section {
  padding: 25px 0 35px;
  border-top: 1px solid #bdc8af;
}
.guide h2 {
  font-size: 31px;
  margin-bottom: 20px;
}
.guide p,
.guide li {
  font-size: 17px;
  line-height: 1.9;
  color: #56634b;
  margin-bottom: 18px;
}
.guide ol,
.guide ul {
  padding-left: 23px;
}
.guide-callout {
  padding: 22px;
  background: #e4eacc;
  border-left: 3px solid #81995f;
  font-size: 16px;
  line-height: 1.8;
}
.sample-themes {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin: 20px 0;
}
[hidden] {
  display: none !important;
}
@media (max-width: 1000px) {
  .hero {
    padding-top: 55px;
  }
  .hero-copy h1 {
    font-size: 77px;
  }
  .preview-grid {
    grid-template-columns: 165px 1.3fr 1fr;
  }
  .preview-sidebar {
    padding: 22px 13px;
  }
  .preview-transcript,
  .preview-insight {
    padding: 23px;
  }
  .preview-transcript h2 {
    font-size: 23px;
  }
  .study-layout {
    grid-template-columns: 210px 1fr;
    gap: 4%;
  }
  .study-grid {
    gap: 17px;
  }
  .study-info {
    padding: 20px;
  }
  .study-info h2 {
    font-size: 24px;
  }
  .notebook-layout {
    gap: 25px;
  }
  .insight-editor {
    padding: 23px;
  }
  .header {
    gap: 20px;
  }
  .nav {
    gap: 20px;
  }
  .steps {
    gap: 22px;
  }
  .step h3 {
    font-size: 25px;
  }
}
@media (max-width: 760px) {
  .header {
    height: auto;
    min-height: 78px;
    padding: 19px 5%;
    flex-wrap: wrap;
    gap: 18px;
  }
  .brand {
    font-size: 26px;
  }
  .nav {
    gap: 20px;
    flex-wrap: wrap;
  }
  .header .nav .button {
    display: none;
  }
  .hero {
    padding: 42px 5% 0;
  }
  .hero-copy {
    margin-bottom: 35px;
  }
  .hero-copy h1 {
    font-size: clamp(48px, 10vw, 68px);
    letter-spacing: -0.055em;
    margin: 22px 0;
  }
  .hero-copy p {
    font-size: 17px;
  }
  .preview-grid {
    grid-template-columns: 1fr;
  }
  .preview-sidebar {
    display: none;
  }
  .preview-transcript {
    border-right: 0;
    border-bottom: 1px solid #dedfd3;
    padding: 25px;
  }
  .preview-insight {
    padding: 25px;
  }
  .preview-bar {
    padding: 14px 18px;
    flex-wrap: wrap;
    gap: 8px;
  }
  .preview-transcript h2 {
    font-size: 27px;
  }
  .preview-insight h2 {
    font-size: 24px;
  }
  .preview-transcript p,
  .preview-insight p {
    font-size: 16px;
  }
  .section {
    padding: 55px 5%;
  }
  .section-heading {
    grid-template-columns: 1fr;
    gap: 25px;
    margin-bottom: 35px;
  }
  .section-heading h2 {
    font-size: 41px;
  }
  .section-heading p {
    font-size: 16px;
  }
  .steps {
    grid-template-columns: 1fr;
    gap: 25px;
  }
  .step {
    padding-top: 22px;
  }
  .step .number {
    margin-bottom: 20px;
  }
  .step h3 {
    font-size: 29px;
  }
  .step-example {
    min-height: 0;
  }
  .feature-band {
    grid-template-columns: 1fr;
    gap: 35px;
  }
  .feature-band h2 {
    font-size: 43px;
  }
  .feature-band p {
    font-size: 16px;
  }
  .stack-card {
    transform: none;
  }
  .stack-card:nth-child(2) {
    transform: none;
    margin-left: 15px;
  }
  .faq h2 {
    font-size: 39px;
  }
  .closing h2 {
    font-size: 43px;
  }
  .footer {
    flex-direction: column;
    align-items: start;
    gap: 20px;
  }
  .footer nav {
    gap: 20px;
    flex-wrap: wrap;
  }
  .app-top {
    padding: 32px 5% 25px;
    align-items: start;
    flex-direction: column;
    gap: 20px;
  }
  .app-top h1 {
    font-size: 42px;
  }
  .workspace-nav {
    gap: 18px;
    flex-wrap: wrap;
    padding: 14px 5%;
  }
  .workspace-nav a {
    font-size: 14px;
  }
  .filters {
    align-items: stretch;
    gap: 14px;
  }
  .filters label {
    width: 100%;
  }
  .filters input {
    width: 100%;
  }
  .study-grid {
    grid-template-columns: 1fr;
    gap: 25px;
  }
  .study-art {
    height: 160px;
  }
  .study-info h2 {
    font-size: 28px;
  }
  .study-layout {
    grid-template-columns: 1fr;
    padding-top: 25px;
    gap: 30px;
  }
  .study-sidebar {
    position: static;
    border-bottom: 1px solid #c6d1b9;
    padding-bottom: 25px;
  }
  .study-sidebar h2 {
    font-size: 22px;
  }
  .study-sidebar p {
    font-size: 16px;
  }
  .study-sidebar dl {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 20px;
  }
  .study-sidebar dt {
    margin: 0;
  }
  .study-sidebar dd {
    margin-bottom: 15px;
  }
  .study-sidebar .button {
    width: auto;
  }
  .transcript-top {
    align-items: start;
    gap: 20px;
    flex-direction: column;
  }
  .transcript-top label {
    width: 100%;
    font-size: 14px;
  }
  .transcript-top select {
    min-height: 44px;
  }
  .passage {
    padding: 25px 0;
  }
  .passage-text {
    font-size: 17px;
    padding: 18px;
  }
  .passage-actions {
    align-items: start;
    flex-wrap: wrap;
    gap: 15px;
  }
  .theme-control {
    font-size: 14px;
  }
  .highlight-button {
    font-size: 14px;
  }
  .notebook-layout {
    grid-template-columns: 1fr;
    gap: 35px;
    padding-top: 28px;
  }
  .insight-editor {
    padding: 23px;
  }
  .insight-list {
    grid-template-columns: 1fr;
  }
  .insight-card {
    padding: 23px;
  }
  .saved-insights > h2 {
    font-size: 31px;
  }
  .export-panel {
    padding: 22px;
  }
  .export-panel textarea {
    padding: 15px;
    font-size: 14px;
  }
  .guide {
    grid-template-columns: 1fr;
    gap: 28px;
  }
  .guide-nav {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 15px;
  }
  .guide p,
  .guide li {
    font-size: 16px;
  }
  .guide h2 {
    font-size: 29px;
  }
}
@media (max-width: 420px) {
  .nav {
    width: 100%;
    justify-content: space-between;
    gap: 14px;
  }
  .hero-copy h1 {
    font-size: 48px;
  }
  .hero-copy h1 span:after {
    height: 35%;
  }
  .hero-actions {
    gap: 12px;
  }
  .hero-actions .button {
    padding: 12px 16px;
  }
  .workspace-nav {
    gap: 14px;
  }
  .workspace-nav .count {
    margin-left: 3px;
  }
  .study-meta {
    font-size: 12px;
  }
  .speaker {
    flex-wrap: wrap;
    gap: 8px;
  }
  .insight-evidence .source-link {
    width: auto;
  }
  .app-top h1 {
    font-size: 39px;
  }
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *:before,
  *:after {
    animation: none !important;
    transition: none !important;
  }
}
.hero .eyebrow {
  color: #383e2f;
}
.hero-copy p {
  color: #413b32;
}
.hero .button.secondary {
  border-color: #5b5143;
}

Reference interaction handler

const data = JSON.parse(document.getElementById('fieldnotes-data').textContent);
const sourceQuotes = new Map(
  data.studies.flatMap((study) =>
    study.quotes.map((quote) => [quote.id, { ...quote, study }]),
  ),
);
const themeIds = new Set(data.themes.map((theme) => theme.id));
const storageKey = 'fieldnotes-workspace-v1';
function loadWorkspace() {
  try {
    const raw = JSON.parse(localStorage.getItem(storageKey) || '{}');
    const highlights = {};
    for (const [id, theme] of Object.entries(raw.highlights || {}))
      if (sourceQuotes.has(id) && themeIds.has(theme)) highlights[id] = theme;
    const insights = Array.isArray(raw.insights)
      ? raw.insights
          .filter(
            (note) =>
              note &&
              typeof note.id === 'string' &&
              typeof note.title === 'string' &&
              typeof note.body === 'string' &&
              Array.isArray(note.evidence),
          )
          .map((note) => ({
            ...note,
            title: note.title.slice(0, 100),
            body: note.body.slice(0, 2500),
            evidence: [
              ...new Set(note.evidence.filter((id) => sourceQuotes.has(id))),
            ],
          }))
          .filter((note) => note.evidence.length)
      : [];
    return { highlights, insights };
  } catch {
    return { highlights: {}, insights: [] };
  }
}
const workspace = loadWorkspace();
let editingId = null;
let exportUrl = '';
function announce(text) {
  document
    .querySelectorAll('[data-status]')
    .forEach((el) => (el.textContent = text));
}
function invalidateExport() {
  const panel = document.querySelector('#export-panel');
  if (!panel) return;
  panel.hidden = true;
  document.querySelector('#download-notebook').hidden = true;
  if (exportUrl) {
    URL.revokeObjectURL(exportUrl);
    exportUrl = '';
  }
}
function persist() {
  invalidateExport();
  try {
    localStorage.setItem(storageKey, JSON.stringify(workspace));
    return true;
  } catch {
    announce(
      'Browser storage is unavailable. Export this workspace before leaving the page.',
    );
    return false;
  }
}
function refreshBadges() {
  document
    .querySelectorAll('[data-highlight-count]')
    .forEach(
      (el) =>
        (el.textContent = String(Object.keys(workspace.highlights).length)),
    );
  document
    .querySelectorAll('[data-insight-count]')
    .forEach((el) => (el.textContent = String(workspace.insights.length)));
}
function reflectPassages() {
  document.querySelectorAll('[data-passage]').forEach((article) => {
    const id = article.dataset.passage;
    const selected = Object.hasOwn(workspace.highlights, id);
    article.classList.toggle('highlighted', selected);
    const button = article.querySelector('[data-highlight]');
    button.setAttribute('aria-pressed', String(selected));
    button.textContent = selected ? 'Highlighted ✓' : 'Highlight passage';
    const select = article.querySelector('[data-theme]');
    select.disabled = !selected;
    select.value = workspace.highlights[id] || 'uncoded';
  });
  refreshBadges();
  applyPassageFilter();
}
function applyPassageFilter() {
  const filter = document.querySelector('#passage-filter');
  if (!filter) return;
  let count = 0;
  document.querySelectorAll('[data-passage]').forEach((article) => {
    article.hidden =
      filter.value === 'highlighted' &&
      !Object.hasOwn(workspace.highlights, article.dataset.passage);
    if (!article.hidden) count++;
  });
  document.querySelector('#passage-count').textContent =
    `${count} ${count === 1 ? 'passage' : 'passages'} shown`;
  document.querySelector('#no-passages').hidden = count > 0;
}
document.querySelectorAll('[data-highlight]').forEach((button) =>
  button.addEventListener('click', () => {
    const id = button.dataset.highlight;
    if (Object.hasOwn(workspace.highlights, id))
      delete workspace.highlights[id];
    else workspace.highlights[id] = 'uncoded';
    const saved = persist();
    reflectPassages();
    if (saved)
      announce(
        Object.hasOwn(workspace.highlights, id)
          ? 'Passage highlighted. Choose a theme to describe it.'
          : 'Highlight removed. Existing insight citations are preserved.',
      );
  }),
);
document.querySelectorAll('[data-theme]').forEach((select) =>
  select.addEventListener('change', () => {
    workspace.highlights[select.dataset.theme] = select.value;
    if (persist()) announce('Theme updated in this browser.');
  }),
);
document
  .querySelector('#passage-filter')
  ?.addEventListener('change', applyPassageFilter);
reflectPassages();
const search = document.querySelector('#study-search');
function filterStudies() {
  const query = search.value.trim().toLowerCase();
  const stage = document.querySelector('#stage-filter').value;
  let count = 0;
  document.querySelectorAll('[data-study]').forEach((article) => {
    const study = data.studies.find(
      (item) => item.id === article.dataset.study,
    );
    const visible =
      (!stage || study.stage === stage) &&
      `${study.title} ${study.subject} ${study.description}`
        .toLowerCase()
        .includes(query);
    article.hidden = !visible;
    if (visible) count++;
  });
  document.querySelector('#study-count').textContent =
    `${count} ${count === 1 ? 'study' : 'studies'}`;
  document.querySelector('#empty-library').hidden = count > 0;
}
if (search) {
  search.addEventListener('input', filterStudies);
  document
    .querySelector('#stage-filter')
    .addEventListener('change', filterStudies);
  document.querySelector('#reset-filters').addEventListener('click', () => {
    search.value = '';
    document.querySelector('#stage-filter').value = '';
    filterStudies();
  });
  filterStudies();
}
function element(tag, className, text) {
  const el = document.createElement(tag);
  if (className) el.className = className;
  if (text !== undefined) el.textContent = text;
  return el;
}
function sourceLink(id) {
  const quote = sourceQuotes.get(id);
  const link = element(
    'a',
    'source-link',
    `${quote.study.participant} · ${quote.time} ↗`,
  );
  link.href = quote.study.id + '.html#' + id;
  return link;
}
function renderHighlights() {
  const list = document.querySelector('#highlight-list');
  if (!list) return;
  list.replaceChildren();
  const selectedTheme = document.querySelector('#notebook-theme').value;
  const ids = Object.keys(workspace.highlights).filter(
    (id) => !selectedTheme || workspace.highlights[id] === selectedTheme,
  );
  document.querySelector('#empty-highlights').hidden = ids.length > 0;
  for (const id of ids) {
    const quote = sourceQuotes.get(id);
    const theme = data.themes.find(
      (item) => item.id === workspace.highlights[id],
    );
    const card = element('article', 'evidence-card');
    const badge = element('span', 'theme-label', theme.name);
    badge.style.backgroundColor = theme.color;
    card.append(badge, element('blockquote', '', quote.text), sourceLink(id));
    list.append(card);
  }
}
function renderEvidenceChoices() {
  const container = document.querySelector('#evidence-choices');
  if (!container) return;
  container.replaceChildren();
  const existing = editingId
    ? workspace.insights.find((note) => note.id === editingId)?.evidence || []
    : [];
  const ids = [...new Set([...Object.keys(workspace.highlights), ...existing])];
  document.querySelector('#no-evidence').hidden = ids.length > 0;
  for (const id of ids) {
    const quote = sourceQuotes.get(id);
    const label = element('label', 'evidence-choice');
    const input = document.createElement('input');
    input.type = 'checkbox';
    input.name = 'evidence';
    input.value = id;
    input.checked = existing.includes(id);
    const text = element('span', '', quote.text);
    text.append(
      element('small', '', `${quote.study.participant} · ${quote.time}`),
    );
    label.append(input, text);
    container.append(label);
  }
}
function renderInsights() {
  const list = document.querySelector('#insight-list');
  if (!list) return;
  list.replaceChildren();
  document.querySelector('#empty-insights').hidden =
    workspace.insights.length > 0;
  for (const note of workspace.insights) {
    const card = element('article', 'insight-card');
    card.append(
      element('span', 'eyebrow', 'RESEARCHER INTERPRETATION'),
      element('h3', '', note.title),
      element('p', 'insight-body', note.body),
    );
    const evidence = element('div', 'insight-evidence');
    evidence.append(
      element(
        'h4',
        '',
        `${note.evidence.length} source ${note.evidence.length === 1 ? 'passage' : 'passages'}`,
      ),
    );
    for (const id of note.evidence) {
      const quote = sourceQuotes.get(id);
      const quoteElement = element('blockquote', '', quote.text);
      quoteElement.append(sourceLink(id));
      evidence.append(quoteElement);
    }
    card.append(evidence);
    const edit = element('button', 'text-button', 'Edit insight');
    edit.type = 'button';
    edit.addEventListener('click', () => editInsight(note.id));
    card.append(edit);
    list.append(card);
  }
}
function editInsight(id) {
  const note = workspace.insights.find((item) => item.id === id);
  editingId = id;
  document.querySelector('#insight-title').value = note.title;
  document.querySelector('#insight-body').value = note.body;
  document.querySelector('#editor-heading').textContent =
    'Refine the interpretation';
  document.querySelector('#save-insight').textContent = 'Save changes';
  document.querySelector('#cancel-edit').hidden = false;
  renderEvidenceChoices();
  document.querySelector('#insight-title').focus();
}
function resetEditor() {
  editingId = null;
  document.querySelector('#insight-form').reset();
  document.querySelector('#editor-heading').textContent = 'Write an insight';
  document.querySelector('#save-insight').textContent = 'Save insight';
  document.querySelector('#cancel-edit').hidden = true;
  document.querySelector('#evidence-error').textContent = '';
  renderEvidenceChoices();
}
function readText(formData, name) {
  const value = formData.get(name);
  return typeof value === 'string' ? value.trim() : '';
}
const insightForm = document.querySelector('#insight-form');
if (insightForm) {
  insightForm.addEventListener('input', () => {
    document.querySelector('#insight-title').setCustomValidity('');
    document.querySelector('#insight-body').setCustomValidity('');
    document.querySelector('#evidence-error').textContent = '';
  });
  renderHighlights();
  renderInsights();
  renderEvidenceChoices();
  document
    .querySelector('#notebook-theme')
    .addEventListener('change', renderHighlights);
  document.querySelector('#cancel-edit').addEventListener('click', resetEditor);
  insightForm.addEventListener('submit', (event) => {
    event.preventDefault();
    const title = document.querySelector('#insight-title'),
      body = document.querySelector('#insight-body');
    title.setCustomValidity(title.value.trim() ? '' : 'Add an insight title.');
    body.setCustomValidity(
      body.value.trim().length >= 20
        ? ''
        : 'Write at least 20 characters of interpretation.',
    );
    if (!insightForm.reportValidity()) return;
    const values = new FormData(insightForm);
    const evidence = values
      .getAll('evidence')
      .filter((id) => typeof id === 'string' && sourceQuotes.has(id));
    if (!evidence.length) {
      document.querySelector('#evidence-error').textContent =
        'Choose at least one source passage to support this insight.';
      document.querySelector('#evidence-choices input')?.focus();
      return;
    }
    const note = {
      id: editingId || crypto.randomUUID(),
      title: readText(values, 'title'),
      body: readText(values, 'body'),
      evidence,
    };
    if (editingId)
      workspace.insights = workspace.insights.map((item) =>
        item.id === editingId ? note : item,
      );
    else workspace.insights.push(note);
    const saved = persist();
    renderInsights();
    refreshBadges();
    resetEditor();
    if (saved)
      announce(
        'Insight saved with its source passages. This is your interpretation, not automated analysis.',
      );
  });
}
function workspaceMarkdown() {
  const highlights = Object.entries(workspace.highlights).map(
    ([id, themeId]) => {
      const quote = sourceQuotes.get(id);
      const theme = data.themes.find((item) => item.id === themeId);
      return `### ${theme.name} — ${quote.study.title}\n\n> ${quote.text}\n\nSource: ${quote.study.participant}, ${quote.time} (${quote.study.id}.html#${id})`;
    },
  );
  const insights = workspace.insights.map(
    (note) =>
      `### ${note.title}\n\n${note.body}\n\nEvidence:\n${note.evidence
        .map((id) => {
          const quote = sourceQuotes.get(id);
          return `> ${quote.text}\n\n${quote.study.participant}, ${quote.time} (${quote.study.id}.html#${id})`;
        })
        .join('\n\n')}`,
  );
  return `# Fieldnotes research notebook\n\nFictional interviews. Manual highlights, theme assignments, and researcher interpretations. No automated analysis or real participant data.\n\n## Highlights\n\n${highlights.join('\n\n') || 'No highlights saved.'}\n\n## Insights\n\n${insights.join('\n\n') || 'No insights saved.'}\n`;
}
document.querySelector('#prepare-export')?.addEventListener('click', () => {
  const markdown = workspaceMarkdown();
  if (exportUrl) URL.revokeObjectURL(exportUrl);
  exportUrl = URL.createObjectURL(
    new Blob([markdown], { type: 'text/markdown;charset=utf-8' }),
  );
  const link = document.querySelector('#download-notebook');
  link.href = exportUrl;
  link.hidden = false;
  document.querySelector('#export-text').value = markdown;
  document.querySelector('#export-panel').hidden = false;
  document.querySelector('#export-panel').focus();
  announce('Notebook export prepared. Review or download the Markdown below.');
});
document.querySelector('#copy-export')?.addEventListener('click', async () => {
  try {
    await navigator.clipboard.writeText(
      document.querySelector('#export-text').value,
    );
    announce('Notebook Markdown copied.');
  } catch {
    const text = document.querySelector('#export-text');
    text.focus();
    text.select();
    announce(
      'Clipboard unavailable. Select and copy the export text, or use the download link.',
    );
  }
});