Privacy & analytics

What we send — and what we don't.

We try to keep this short. The page below describes exactly what the code does, in plain language.

1. The short version

Turning Lines uses Google Analytics 4 for visit counting and Microsoft Clarity for session replay, and only after you explicitly grant consent. Until then, no Google or Clarity tag is loaded and no request is made to googletagmanager.com, google-analytics.com, or clarity.ms. If you decline, nothing about you is sent to either vendor.

The two tools are independent. Google Analytics 4 receives a closed 32-event allow-list (below); Microsoft Clarity records the rendered page DOM, clicks, scrolls, the page URL, and the referrer when its tag is loaded. They share the same consent gate but each vendor sees a different slice of your visit.

The visitor's free-text question, the hexagram they cast, and their saved reading history are stored in this browser's localStorage only — they never reach the GA4 event layer and they are not custom Clarity tags. Clarity does, however, capture the rendered DOM (unless the element carries data-clarity-mask="true") and the page URL, so the placement of an unsaved question on the page can still appear in a Clarity replay. See section 3 for the GA4 allow-list, section 3b for what GA4 adds on top, and section 8 for what Clarity itself sees.

The one exception in the GA4 layer is the three closed-enum answers on the quick check-in form below the reading workbench — intent_category, helpfulness, and would_return. If you have granted consent, those three answers are sent to Google as a single feedback_submitted event when you submit the form.

2. What is sent (only after consent)

After you grant consent, this site may send:

  • 32 whitelisted GA4 events, split into five families: 24 core funnel / page-interaction events (question_input_started, question_committed, question_skipped, reading_started, reading_progress_half, reading_completed, reading_reset_before_complete, results_engaged, calculation_opened, copy_result_clicked, copy_result_succeeded, copy_result_failed, cast_mode_selected, manual_mode_selected, feedback_submitted, hero_primary_cta_clicked, hero_method_cta_clicked, method_section_viewed, supporting_content_viewed, faq_opened, history_section_viewed, history_reading_opened), 2 section-visibility signals (workbench_visible, results_reflection_visible), 2 line-level attribution events (reading_line_recorded, reading_line_undone), 5 learning and content events (feedback_started, content_engaged, guide_to_reading, diagram_interacted, language_switched), and page_view. Anything outside this list is silently dropped.
  • Section-visibility signals: the two events (workbench_visible, results_reflection_visible) tell us whether the visitor actually scrolled the workbench or the reflection sections into the viewport, independent of any click. They carry only the four closed-set enum dimensions below — never question text, hexagram, email, or anything identifying.
  • Four non-identifying parameters on each event: the page's content_language (en or zh-CN), a coarse page_id, the bucketed source_category (direct / search / social / referral / unknown — derived from the referrer host locally), and a coarse device_class (mobile / tablet / desktop — derived from the viewport width locally).
  • Search attribution: for a recognised search engine, page_referrer is set to a fixed homepage for that provider (for example, https://www.google.com/). For other sources it is empty. We never send the original referrer path, search query, fragment, or arbitrary referring website to GA4.
  • Three extra parameters on feedback_submitted: intent_category, helpfulness (1–5), would_return (yes / maybe / no). All three are closed enums.
  • One extra parameter on diagram_interacted: diagram_id from a closed registry (coin_to_hexagram). The six-throw hexagram diagram also exposes its id="six-throw-diagram"; both attributes map to the same diagram_id enum value. Unknown ids are dropped.
  • Content participation signals: the 5 learning events measure whether a visitor stayed with the content, switched language, opened the workbench CTA from a guide page, or interacted with a known diagram region. They record coarse page-level engagement only — never the question text, the real hexagram you cast, the free-text feedback you typed, your email address, or any keystroke / form value.
  • Line-level attribution: the two line-level events (reading_line_recorded, reading_line_undone) and the small closed-set params on reading_started, reading_progress_half, reading_reset_before_complete, and copy_result_failed. The accepted params are exactly: reading_mode (cast / enter), input_method (manual_coins / random_cast / manual_entry), line_position (1…6), progress_bucket (early / half / late / complete), and failure_reason (clipboard_unavailable / clipboard_rejected / unknown). Anything outside these closed sets is silently dropped. We never send the actual coin faces, the line value (6/7/8/9), the hexagram number, the question text, the underlying error message, or the stack trace.

3. What the GA4 event layer never sends

These items stay out of the GA4 event layer (the 32 events above). For Clarity's separate DOM-replay scope, see section 8.

  • The text of your question.
  • The hexagram number, the six line values, the relating hexagram, the moving lines.
  • Your reading history (notes, reviews, dates).
  • Your email address. The feedback form no longer asks for one.
  • Free-text replies. The feedback form no longer asks for any.
  • The full URL you came from, including any search query.
  • The full URL you are on. Even when the analytics layer reports page_location, it is built from a closed whitelist of paths — 64 hexagram detail URLs all collapse to /hexagrams so the specific hexagram number never reaches Google. Query strings and fragments are never sent.
  • The raw viewport width or height.

The feedback form is closed-enum only: three multiple-choice questions. After you grant consent and submit, those three answers are sent as feedback_submitted. Free-text fields are not collected, so there is no free-text reply to send.

Once you grant consent, the Google Analytics 4 tag loads. GA4 itself then collects, by its own design, the things any GA4 property collects on the public web: the visitor's IP address, User-Agent, coarse device and browser class, and a pseudonymous _ga cookie holding a randomly-assigned client ID that can link activity within a single browser across visits. Google documents this on their GA4 cookie usage page.

According to Google, GA4 does not record or store the full IP address on its analytics servers — see Google's own IP-anonymisation reference. We do not describe the resulting data as "completely anonymous": it is pseudonymous, linkable within a single browser, and processed by Google as the data processor for this property.

The first time you visit, a banner at the bottom of the page asks whether analytics + session replay are OK. Decline is the default: if you do nothing, nothing is sent. Your choice is stored in this browser only — clearing your browser storage removes it.

You can re-open the dialog at any time from the footer's Privacy & analytics settings link. The dialog shows the current state for each tool separately, the cookies each vendor sets, and the withdraw path.

Withdrawing consent:

  • flips the Google tag's analytics_storage consent back to denied and tries to clear the _ga, _gid, and related cookies this site can reach;
  • pushes the official Microsoft Clarity consentv2 deny signal (both ad_Storage and analytics_Storage set to denied), AND calls Clarity's documented clarity("consent", false) — the official Microsoft API call for clearing Clarity cookies and stopping further tracking on this page;
  • reloads the page. The reload is THIS site's engineering choice — Microsoft does not document reload as a privacy guarantee, and we do not claim it stops every Clarity capture on its own. The consent(false) + consentv2 deny + reload combination is the strongest contract we can offer: after the reload, the gate re-evaluates the persisted "denied" choice and the Clarity tag is not re-injected. First-party cookies Clarity set under this site's own origin (the _clck / _clsk cookies the loader can address) are within the official consent(false) scope; the separate third-party cookies Clarity sets under clarity.ms / microsoft.com domains (e.g. CLID, ANONCHK, MR, MUID, SM) are outside this site's reach — clearing them is the visitor's responsibility via their browser's site-data controls.

5. What we never use analytics for

  • Advertising personalisation — ad_storage, ad_user_data, and ad_personalization are always denied.
  • Building a profile of you across other websites.
  • Selling your data. We do not sell any of the data this site collects or generates.

Note: Google Analytics itself is a data processor for this property. When you consent, Google receives the events above and processes them under Google's own terms and privacy policy. We do not, in addition, share those events with anyone else.

6. Google and Microsoft as processors

If you grant consent, Google acts as a data processor for the events described above. Microsoft's Clarity acts as a separate data processor for the session replay described in section 8. Each vendor's own terms and privacy policy apply once data reaches their servers. Their docs explain how long each retains its data and how to manage it from their side.

7. Contact

The site is published at ichingonline.site. There is no contact form on this site and the footer does not solicit email addresses.

If you want to give feedback after a reading, the homepage reading workbench has a small quick check-in form underneath it. That form only offers three closed-enum choices — intent_category, helpfulness, and would_return — there is no free-text note field. Those three answers are sent to Google as a single feedback_submitted event, and only after you have granted analytics consent, so please do not pick an answer you would not want Google to receive. The form does not accept messages, comments, or notes of any kind.

If you submit while the analytics script is still loading, the three answers stay in your browser until the script finishes — the form stays visible and shows a short "queued for replay" note instead of pretending your answers have been sent. Nothing leaves the browser until the script is ready and Google actually receives the event.

To change or withdraw analytics consent at any time, open the Privacy & analytics settings dialog from the site footer. Your choice is stored in this browser only.

8. Microsoft Clarity session replay (separate from GA4)

Microsoft Clarity is an independent session-replay tool from Google Analytics. The same consent choice gates both, but each vendor sees a different slice of your visit, and Clarity only loads when a Clarity Project ID is configured on the server. If the project ID is not configured (the default on a fresh checkout), no request is ever made to clarity.ms even after you grant analytics consent — you can confirm the live state from the Privacy & analytics settings dialog in the site footer.

When the Clarity Project ID is configured AND you have granted analytics consent, Clarity itself (not this site) captures:

  • the current page URL (the full location.href, including any path / query / fragment Clarity can read at capture time);
  • the page referrer (the full document.referrer — the URL you came from);
  • mouse clicks, scrolls, and similar UI interactions;
  • the rendered page DOM (subject to the site's data-clarity-mask attributes);
  • pseudonymous first-party cookies (_clck, _clsk) and a pseudonymous browser identifier that can link activity within a single browser across visits — see Clarity's cookies reference; plus separate third-party cookies Clarity sets under theclarity.ms / microsoft.com domains (e.g. CLID, ANONCHK, MR,MUID, SM) that this site cannot reach from its own origin.

Clarity itself may interpret clicks (which target a button is observable via the click position + adjacent masked / unmasked region), so a click next to a masked input can still convey inferable information. The data-clarity-mask attribute suppresses the visible text but it cannot hide the shape or layout of an element.

This site does not push custom Clarity tags. We never call clarity("set", ...) with the visitor's question, the hexagram number, the saved reading notes, the Google Analytics client ID, or any custom user or session identifier. Clarity's own snippet generates its pseudonymous session ID — we do not customise it, and we do not claim it is absent.

This site additionally opts sensitive regions out of the replay's text capture through the data-clarity-mask="true" attribute, which Clarity's masking contract recognises. The masked regions include:

  • The free-text question textarea on the homepage.
  • The echoed question block above the workbench results.
  • The full reading result workbench (hexagram figures, the changing-line summary, the calculation disclosure body, the five reflection sections, the closed-enum feedback card).
  • Every row in the reading history list, the full history detail panel, the echoed saved question, and the three plain-text notes (chose / changed / learned).

The throw-line button, the progress navigation, the consent banner, the Settings dialog, the locale switcher, and the footer links are intentionally not masked so the replay can still show the visitor's click path through the page. Clarity's default input-masking still covers the form inputs themselves; the data-clarity-mask attributes above extend that to the reading-specific DOM.

Withdraw contract. Withdrawing analytics consent does three things in order: (1) pushes the official Clarity consentv2 deny signal (with ad_Storage: "denied" and analytics_Storage: "denied"), (2) calls Clarity's documented clarity("consent", false) — the official Microsoft API call that instructs Clarity to clear its first-party cookies and stop further tracking on this page, and (3) reloads this page so the gate re-reads the persisted "denied" choice and the Clarity tag is not re-injected. We do not claim Microsoft's documentation guarantees the reload alone stops every replay; the combination of consent(false) + consentv2 deny + reload is the strongest contract we can offer. First-party cookies Clarity has set under this site's own origin (the _clck / _clsk cookies) are within the consent(false) scope; the separate third-party cookies Clarity sets under clarity.ms / microsoft.com domains (e.g. CLID, ANONCHK, MR, MUID, SM) are outside this site's reach, so visitors who want to clear them should use their browser's site-data controls for those domains.

Withdraw immediately by re-opening the Privacy & analytics settings dialog from the site footer and choosing Withdraw consent. Your choice is stored in this browser only.

Microsoft acts as a data processor for any Clarity replay that is recorded. Microsoft's own terms and privacy policy apply once data reaches their servers.