{
  "_comment": "GC Points earning + spending rates. MIRROR of what is authoritatively implemented in scripts/edge-functions/earn-points.ts (deployed as the earn-points Edge Function) and assets/js/gamification_v2.js. Surfaced on Admin -> Configuration -> Site data (/admin/config) via admin-config-viewer.js. CORRECTED 2026-09-21: the earning block previously documented a streak ladder of 100/500/2000/5000 that was never implemented anywhere -- the live server pays 3/5/10/20/50 and no streak_day_* award had ever been written. A 2000-point milestone is also unpayable, because the Edge Function awards min(points, 400 - earnedToday). The SPENDING block below is NOT yet verified against spend-points and may carry the same drift -- treat it as unconfirmed. Compliance disclosure copy lives in docs/plans/points-system-disclosure.md; the user-facing legal agreement in docs/legal/gc-points-program-terms.md.",
  "_lastUpdated": "2026-10-26",
  "_authority": "MasonByte Board (per gc-points-program-terms.md §8) — rates modifiable without notice",
  "earning": [
    {
      "action": "quiz_answer_correct",
      "points": "+5 to +10",
      "scope": "per question",
      "note": "Variable based on difficulty band"
    },
    {
      "action": "quiz_answer_first_time",
      "points": "+5",
      "scope": "bonus, once per Q",
      "note": "First correct answer to a previously-untouched question"
    },
    {
      "action": "section_complete",
      "points": "+100",
      "scope": "per section",
      "note": "Crossing the completion threshold of any state's section"
    },
    {
      "action": "section_flawless",
      "points": "+200",
      "scope": "per section",
      "note": "100% on all questions in a section, single attempt"
    },
    {
      "action": "practice_exam_finish",
      "points": "+50",
      "scope": "per exam",
      "note": "Completing any practice exam regardless of score"
    },
    {
      "action": "daily_challenge_complete",
      "points": "+50",
      "scope": "per day",
      "note": "Cap: 1 daily challenge per UTC day"
    },
    {
      "action": "streak_day_1",
      "points": "+3",
      "scope": "milestone, once per 100-day cycle",
      "note": "1-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "streak_day_3",
      "points": "+5",
      "scope": "milestone, once per 100-day cycle",
      "note": "3-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "streak_day_7",
      "points": "+10",
      "scope": "milestone, once per 100-day cycle",
      "note": "7-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "streak_day_14",
      "points": "+20",
      "scope": "milestone, once per 100-day cycle",
      "note": "14-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "streak_day_30",
      "points": "+50",
      "scope": "milestone, once per 100-day cycle",
      "note": "30-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "streak_day_60",
      "points": "+75",
      "scope": "milestone, once per 100-day cycle",
      "note": "60-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "streak_day_90",
      "points": "+100",
      "scope": "milestone, once per 100-day cycle",
      "note": "90-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "streak_day_100",
      "points": "+150",
      "scope": "milestone, once per 100-day cycle",
      "note": "100-day study streak. Ladder CYCLES: reward day = ((streak-1)%100)+1, so day 101 restarts at 1 while the displayed streak keeps climbing."
    },
    {
      "action": "review_missed_question",
      "points": "+5",
      "scope": "per question reviewed",
      "note": "Returning to a previously-incorrect question and re-attempting"
    },
    {
      "action": "share_result",
      "points": "+25",
      "scope": "per share, daily cap",
      "note": "Sharing a completion or score to social. Daily cap to prevent farming."
    },
    {
      "action": "seasonal_mark_claim",
      "points": "+10",
      "scope": "once per holiday mark per year",
      "note": "Signed-in click on the cornucopia (Nov 2 - Thanksgiving) or the candy cane (day after Thanksgiving - Dec 31) beside the home page logo. Server-side, seasonal-points.js; behind the seasonal_points switch (2026-10-26)."
    }
  ],
  "spending": [
    {
      "feature": "ui_theme_unlock",
      "cost": "500 to 2000",
      "type": "cosmetic",
      "note": "Per theme; pack of 5 themes Phase 1"
    },
    {
      "feature": "exam_replay",
      "cost": "1000",
      "type": "convenience",
      "note": "Practice Exam Variation Mode (new question pool, same section)"
    },
    {
      "feature": "cram_sheet_preview",
      "cost": "5000",
      "type": "convenience",
      "note": "Time-limited preview of a paid state's cram sheet"
    },
    {
      "feature": "streak_freeze",
      "cost": "1000",
      "type": "convenience",
      "note": "Prevents a streak from breaking on a missed day; 1-use"
    },
    {
      "feature": "tier_up_nudge",
      "cost": "2000",
      "type": "informational",
      "note": "Push notification + email when next tier crosses"
    }
  ],
  "limits": {
    "max_earn_calls_per_day_per_user": 100,
    "daily_points_cap": 400,
    "idle_timeout_ms": 300000,
    "heartbeat_interval_ms": 30000,
    "_note": "Rate limits enforced server-side via the earn-points Edge Function. Source: assets/js/gamification_v2.js CONFIG block."
  },
  "tiersToReach": {
    "_comment": "Approximate GC Point thresholds for each tier per docs/plans/gamification-decisions-2026-05-16.md §3. Updated 2026-05-16 with Matthew-locked answers (bronze/silver/gold/diamond-builder ladder w/ 3 sublevels per tier). For exact thresholds in code see gamification_v2.js TIERS array (post PR-1 of §10).",
    "Apprentice": {
      "points": 0,
      "note": "Default — signup only"
    },
    "Bronze I": {
      "points": 25,
      "note": "First practice (3-5 correct answers)"
    },
    "Bronze II": {
      "points": 75,
      "note": ""
    },
    "Bronze III": {
      "points": 200,
      "note": ""
    },
    "Silver I": {
      "points": 500,
      "note": ""
    },
    "Silver II": {
      "points": 1000,
      "note": ""
    },
    "Silver III": {
      "points": 1750,
      "note": ""
    },
    "Gold I": {
      "points": 2750,
      "note": ""
    },
    "Gold II": {
      "points": 4000,
      "note": ""
    },
    "Gold III": {
      "points": 5500,
      "note": ""
    },
    "Diamond Builder I": {
      "points": 8000,
      "note": "Multi-state engagement / exam pass — Matthew 2026-05-16: avoids 'Master' nomenclature"
    },
    "Diamond Builder II": {
      "points": 12000,
      "note": ""
    },
    "Diamond Builder III": {
      "points": 17500,
      "note": "Top — power-user / champion flex"
    }
  }
}
